vmainwindow.cpp 101 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017
  1. #include <QtWidgets>
  2. #include <QList>
  3. #include <QPrinter>
  4. #include <QPrintDialog>
  5. #include <QPainter>
  6. #include "vmainwindow.h"
  7. #include "vdirectorytree.h"
  8. #include "vnote.h"
  9. #include "vfilelist.h"
  10. #include "vconfigmanager.h"
  11. #include "utils/vutils.h"
  12. #include "veditarea.h"
  13. #include "voutline.h"
  14. #include "vnotebookselector.h"
  15. #include "dialog/vfindreplacedialog.h"
  16. #include "dialog/vsettingsdialog.h"
  17. #include "vcaptain.h"
  18. #include "vedittab.h"
  19. #include "vwebview.h"
  20. #include "vexporter.h"
  21. #include "vmdtab.h"
  22. #include "vvimindicator.h"
  23. #include "vvimcmdlineedit.h"
  24. #include "vtabindicator.h"
  25. #include "dialog/vupdater.h"
  26. #include "vorphanfile.h"
  27. #include "dialog/vorphanfileinfodialog.h"
  28. #include "vsingleinstanceguard.h"
  29. #include "vnotefile.h"
  30. #include "vbuttonwithwidget.h"
  31. #include "vattachmentlist.h"
  32. #include "vfilesessioninfo.h"
  33. #include "vsnippetlist.h"
  34. #include "vtoolbox.h"
  35. #include "vbuttonmenuitem.h"
  36. #include "vpalette.h"
  37. #include "utils/viconutils.h"
  38. #include "dialog/vtipsdialog.h"
  39. #include "vcart.h"
  40. #include "dialog/vexportdialog.h"
  41. extern VConfigManager *g_config;
  42. extern VPalette *g_palette;
  43. VMainWindow *g_mainWin;
  44. VNote *g_vnote;
  45. VWebUtils *g_webUtils;
  46. const int VMainWindow::c_sharedMemTimerInterval = 1000;
  47. #if defined(QT_NO_DEBUG)
  48. extern QFile g_logFile;
  49. #endif
  50. #define COLOR_PIXMAP_ICON_SIZE 64
  51. VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent)
  52. : QMainWindow(p_parent),
  53. m_guard(p_guard),
  54. m_windowOldState(Qt::WindowNoState),
  55. m_requestQuit(false),
  56. m_printer(NULL)
  57. {
  58. qsrand(QDateTime::currentDateTime().toTime_t());
  59. g_mainWin = this;
  60. setWindowIcon(QIcon(":/resources/icons/vnote.ico"));
  61. vnote = new VNote(this);
  62. g_vnote = vnote;
  63. m_webUtils.init();
  64. g_webUtils = &m_webUtils;
  65. if (g_config->getEnableCompactMode()) {
  66. m_panelViewState = PanelViewState::CompactMode;
  67. } else {
  68. m_panelViewState = PanelViewState::TwoPanels;
  69. }
  70. initCaptain();
  71. setupUI();
  72. initMenuBar();
  73. initToolBar();
  74. initShortcuts();
  75. initDockWindows();
  76. changePanelView(m_panelViewState);
  77. restoreStateAndGeometry();
  78. setContextMenuPolicy(Qt::NoContextMenu);
  79. notebookSelector->update();
  80. initSharedMemoryWatcher();
  81. registerCaptainAndNavigationTargets();
  82. }
  83. void VMainWindow::initSharedMemoryWatcher()
  84. {
  85. m_sharedMemTimer = new QTimer(this);
  86. m_sharedMemTimer->setSingleShot(false);
  87. m_sharedMemTimer->setInterval(c_sharedMemTimerInterval);
  88. connect(m_sharedMemTimer, &QTimer::timeout,
  89. this, &VMainWindow::checkSharedMemory);
  90. m_sharedMemTimer->start();
  91. }
  92. void VMainWindow::initCaptain()
  93. {
  94. // VCaptain should be visible to accpet key focus. But VCaptain
  95. // may hide other widgets.
  96. m_captain = new VCaptain(this);
  97. connect(m_captain, &VCaptain::captainModeChanged,
  98. this, [this](bool p_captainMode) {
  99. static QString normalStyle = m_avatarBtn->styleSheet();
  100. static QString captainStyle = QString("color: %1; background: %2;")
  101. .arg(g_palette->color("avatar_captain_mode_fg"))
  102. .arg(g_palette->color("avatar_captain_mode_bg"));
  103. if (p_captainMode) {
  104. m_avatarBtn->setStyleSheet(captainStyle);
  105. } else {
  106. m_avatarBtn->setStyleSheet(normalStyle);
  107. }
  108. });
  109. }
  110. void VMainWindow::registerCaptainAndNavigationTargets()
  111. {
  112. m_captain->registerNavigationTarget(notebookSelector);
  113. m_captain->registerNavigationTarget(directoryTree);
  114. m_captain->registerNavigationTarget(m_fileList);
  115. m_captain->registerNavigationTarget(editArea);
  116. m_captain->registerNavigationTarget(m_toolBox);
  117. m_captain->registerNavigationTarget(outline);
  118. m_captain->registerNavigationTarget(m_snippetList);
  119. // Register Captain mode targets.
  120. m_captain->registerCaptainTarget(tr("AttachmentList"),
  121. g_config->getCaptainShortcutKeySequence("AttachmentList"),
  122. this,
  123. showAttachmentListByCaptain);
  124. m_captain->registerCaptainTarget(tr("LocateCurrentFile"),
  125. g_config->getCaptainShortcutKeySequence("LocateCurrentFile"),
  126. this,
  127. locateCurrentFileByCaptain);
  128. m_captain->registerCaptainTarget(tr("ExpandMode"),
  129. g_config->getCaptainShortcutKeySequence("ExpandMode"),
  130. this,
  131. toggleExpandModeByCaptain);
  132. m_captain->registerCaptainTarget(tr("OnePanelView"),
  133. g_config->getCaptainShortcutKeySequence("OnePanelView"),
  134. this,
  135. toggleOnePanelViewByCaptain);
  136. m_captain->registerCaptainTarget(tr("DiscardAndRead"),
  137. g_config->getCaptainShortcutKeySequence("DiscardAndRead"),
  138. this,
  139. discardAndReadByCaptain);
  140. m_captain->registerCaptainTarget(tr("ToolsDock"),
  141. g_config->getCaptainShortcutKeySequence("ToolsDock"),
  142. this,
  143. toggleToolsDockByCaptain);
  144. m_captain->registerCaptainTarget(tr("CloseNote"),
  145. g_config->getCaptainShortcutKeySequence("CloseNote"),
  146. this,
  147. closeFileByCaptain);
  148. m_captain->registerCaptainTarget(tr("ShortcutsHelp"),
  149. g_config->getCaptainShortcutKeySequence("ShortcutsHelp"),
  150. this,
  151. shortcutsHelpByCaptain);
  152. m_captain->registerCaptainTarget(tr("FlushLogFile"),
  153. g_config->getCaptainShortcutKeySequence("FlushLogFile"),
  154. this,
  155. flushLogFileByCaptain);
  156. }
  157. void VMainWindow::setupUI()
  158. {
  159. QWidget *directoryPanel = setupDirectoryPanel();
  160. m_fileList = new VFileList();
  161. m_fileList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
  162. editArea = new VEditArea();
  163. editArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  164. m_findReplaceDialog = editArea->getFindReplaceDialog();
  165. m_fileList->setEditArea(editArea);
  166. directoryTree->setEditArea(editArea);
  167. // Main Splitter
  168. m_mainSplitter = new QSplitter();
  169. m_mainSplitter->setObjectName("MainSplitter");
  170. m_mainSplitter->addWidget(directoryPanel);
  171. m_mainSplitter->addWidget(m_fileList);
  172. setTabOrder(directoryTree, m_fileList->getContentWidget());
  173. m_mainSplitter->addWidget(editArea);
  174. m_mainSplitter->setStretchFactor(0, 0);
  175. m_mainSplitter->setStretchFactor(1, 0);
  176. m_mainSplitter->setStretchFactor(2, 1);
  177. // Signals
  178. connect(directoryTree, &VDirectoryTree::currentDirectoryChanged,
  179. m_fileList, &VFileList::setDirectory);
  180. connect(directoryTree, &VDirectoryTree::directoryUpdated,
  181. editArea, &VEditArea::handleDirectoryUpdated);
  182. connect(notebookSelector, &VNotebookSelector::notebookUpdated,
  183. editArea, &VEditArea::handleNotebookUpdated);
  184. connect(notebookSelector, &VNotebookSelector::notebookCreated,
  185. directoryTree, [this](const QString &p_name, bool p_import) {
  186. Q_UNUSED(p_name);
  187. if (!p_import) {
  188. directoryTree->newRootDirectory();
  189. }
  190. });
  191. connect(m_fileList, &VFileList::fileClicked,
  192. editArea, &VEditArea::openFile);
  193. connect(m_fileList, &VFileList::fileCreated,
  194. editArea, &VEditArea::openFile);
  195. connect(m_fileList, &VFileList::fileUpdated,
  196. editArea, &VEditArea::handleFileUpdated);
  197. connect(editArea, &VEditArea::tabStatusUpdated,
  198. this, &VMainWindow::handleAreaTabStatusUpdated);
  199. connect(editArea, &VEditArea::statusMessage,
  200. this, &VMainWindow::showStatusMessage);
  201. connect(editArea, &VEditArea::vimStatusUpdated,
  202. this, &VMainWindow::handleVimStatusUpdated);
  203. connect(m_findReplaceDialog, &VFindReplaceDialog::findTextChanged,
  204. this, &VMainWindow::handleFindDialogTextChanged);
  205. setCentralWidget(m_mainSplitter);
  206. initVimCmd();
  207. m_vimIndicator = new VVimIndicator(this);
  208. m_vimIndicator->hide();
  209. m_tabIndicator = new VTabIndicator(this);
  210. m_tabIndicator->hide();
  211. // Create and show the status bar
  212. statusBar()->addPermanentWidget(m_vimCmd);
  213. statusBar()->addPermanentWidget(m_vimIndicator);
  214. statusBar()->addPermanentWidget(m_tabIndicator);
  215. initTrayIcon();
  216. }
  217. QWidget *VMainWindow::setupDirectoryPanel()
  218. {
  219. // Notebook selector.
  220. notebookLabel = new QLabel(tr("Notebooks"));
  221. notebookLabel->setProperty("TitleLabel", true);
  222. notebookLabel->setProperty("NotebookPanel", true);
  223. notebookSelector = new VNotebookSelector();
  224. notebookSelector->setObjectName("NotebookSelector");
  225. notebookSelector->setProperty("NotebookPanel", true);
  226. notebookSelector->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
  227. // Navigation panel.
  228. directoryLabel = new QLabel(tr("Folders"));
  229. directoryLabel->setProperty("TitleLabel", true);
  230. directoryLabel->setProperty("NotebookPanel", true);
  231. directoryTree = new VDirectoryTree;
  232. directoryTree->setProperty("NotebookPanel", true);
  233. QVBoxLayout *naviLayout = new QVBoxLayout;
  234. naviLayout->addWidget(directoryLabel);
  235. naviLayout->addWidget(directoryTree);
  236. naviLayout->setContentsMargins(0, 0, 0, 0);
  237. naviLayout->setSpacing(0);
  238. QWidget *naviWidget = new QWidget();
  239. naviWidget->setLayout(naviLayout);
  240. QWidget *tmpWidget = new QWidget();
  241. // Compact splitter.
  242. m_naviSplitter = new QSplitter();
  243. m_naviSplitter->setOrientation(Qt::Vertical);
  244. m_naviSplitter->setObjectName("NaviSplitter");
  245. m_naviSplitter->addWidget(naviWidget);
  246. m_naviSplitter->addWidget(tmpWidget);
  247. m_naviSplitter->setStretchFactor(0, 0);
  248. m_naviSplitter->setStretchFactor(1, 1);
  249. tmpWidget->hide();
  250. QVBoxLayout *nbLayout = new QVBoxLayout;
  251. nbLayout->addWidget(notebookLabel);
  252. nbLayout->addWidget(notebookSelector);
  253. nbLayout->addWidget(m_naviSplitter);
  254. nbLayout->setContentsMargins(0, 0, 0, 0);
  255. nbLayout->setSpacing(0);
  256. QWidget *nbContainer = new QWidget();
  257. nbContainer->setObjectName("NotebookPanel");
  258. nbContainer->setLayout(nbLayout);
  259. nbContainer->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
  260. connect(notebookSelector, &VNotebookSelector::curNotebookChanged,
  261. this, [this](VNotebook *p_notebook) {
  262. directoryTree->setNotebook(p_notebook);
  263. directoryTree->setFocus();
  264. });
  265. connect(notebookSelector, &VNotebookSelector::curNotebookChanged,
  266. this, &VMainWindow::handleCurrentNotebookChanged);
  267. connect(directoryTree, &VDirectoryTree::currentDirectoryChanged,
  268. this, &VMainWindow::handleCurrentDirectoryChanged);
  269. return nbContainer;
  270. }
  271. void VMainWindow::initToolBar()
  272. {
  273. const int tbIconSize = g_config->getToolBarIconSize() * VUtils::calculateScaleFactor();
  274. QSize iconSize(tbIconSize, tbIconSize);
  275. initFileToolBar(iconSize);
  276. initViewToolBar(iconSize);
  277. initEditToolBar(iconSize);
  278. initNoteToolBar(iconSize);
  279. }
  280. void VMainWindow::initViewToolBar(QSize p_iconSize)
  281. {
  282. QToolBar *viewToolBar = addToolBar(tr("View"));
  283. viewToolBar->setObjectName("ViewToolBar");
  284. viewToolBar->setMovable(false);
  285. if (p_iconSize.isValid()) {
  286. viewToolBar->setIconSize(p_iconSize);
  287. }
  288. m_viewActGroup = new QActionGroup(this);
  289. QAction *onePanelViewAct = new QAction(VIconUtils::menuIcon(":/resources/icons/one_panel.svg"),
  290. tr("Single Panel"),
  291. m_viewActGroup);
  292. QString keyText = VUtils::getCaptainShortcutSequenceText("OnePanelView");
  293. if (!keyText.isEmpty()) {
  294. onePanelViewAct->setText(tr("Single Panel\t%1").arg(keyText));
  295. }
  296. onePanelViewAct->setStatusTip(tr("Display only the notes list panel"));
  297. onePanelViewAct->setCheckable(true);
  298. onePanelViewAct->setData((int)PanelViewState::SinglePanel);
  299. QAction *twoPanelViewAct = new QAction(VIconUtils::menuIcon(":/resources/icons/two_panels.svg"),
  300. tr("Two Panels"),
  301. m_viewActGroup);
  302. keyText = VUtils::getCaptainShortcutSequenceText("OnePanelView");
  303. if (!keyText.isEmpty()) {
  304. twoPanelViewAct->setText(tr("Two Panels\t%1").arg(keyText));
  305. }
  306. twoPanelViewAct->setStatusTip(tr("Display both the folders and notes list panel"));
  307. twoPanelViewAct->setCheckable(true);
  308. twoPanelViewAct->setData((int)PanelViewState::TwoPanels);
  309. QAction *compactViewAct = new QAction(VIconUtils::menuIcon(":/resources/icons/compact_mode.svg"),
  310. tr("Compact Mode"),
  311. m_viewActGroup);
  312. compactViewAct->setStatusTip(tr("Integrate the folders and notes list panel in one column"));
  313. compactViewAct->setCheckable(true);
  314. compactViewAct->setData((int)PanelViewState::CompactMode);
  315. connect(m_viewActGroup, &QActionGroup::triggered,
  316. this, [this](QAction *p_action) {
  317. if (!p_action) {
  318. return;
  319. }
  320. int act = p_action->data().toInt();
  321. switch (act) {
  322. case (int)PanelViewState::SinglePanel:
  323. onePanelView();
  324. break;
  325. case (int)PanelViewState::TwoPanels:
  326. twoPanelView();
  327. break;
  328. case (int)PanelViewState::CompactMode:
  329. compactModeView();
  330. break;
  331. default:
  332. break;
  333. }
  334. });
  335. QMenu *panelMenu = new QMenu(this);
  336. panelMenu->setToolTipsVisible(true);
  337. panelMenu->addAction(onePanelViewAct);
  338. panelMenu->addAction(twoPanelViewAct);
  339. panelMenu->addAction(compactViewAct);
  340. expandViewAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/expand.svg"),
  341. tr("Expand"), this);
  342. keyText = VUtils::getCaptainShortcutSequenceText("ExpandMode");
  343. if (!keyText.isEmpty()) {
  344. expandViewAct->setText(tr("Expand\t%1").arg(keyText));
  345. }
  346. expandViewAct->setStatusTip(tr("Expand the edit area"));
  347. expandViewAct->setCheckable(true);
  348. expandViewAct->setMenu(panelMenu);
  349. connect(expandViewAct, &QAction::triggered,
  350. this, [this](bool p_checked) {
  351. // Recover m_panelViewState or change to expand mode.
  352. changePanelView(p_checked ? PanelViewState::ExpandMode
  353. : m_panelViewState);
  354. });
  355. viewToolBar->addAction(expandViewAct);
  356. QAction *menuBarAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/menubar.svg"),
  357. tr("Menu Bar"),
  358. this);
  359. menuBarAct->setStatusTip(tr("Toggle menu bar"));
  360. menuBarAct->setCheckable(true);
  361. menuBarAct->setChecked(g_config->getMenuBarChecked());
  362. connect(menuBarAct, &QAction::triggered,
  363. this, [this](bool p_checked) {
  364. menuBar()->setVisible(p_checked);
  365. g_config->setMenuBarChecked(p_checked);
  366. });
  367. QMenu *screenMenu = new QMenu(this);
  368. screenMenu->setToolTipsVisible(true);
  369. screenMenu->addAction(menuBarAct);
  370. QAction *fullScreenAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/fullscreen.svg"),
  371. tr("Full Screen"),
  372. this);
  373. QString keySeq = g_config->getShortcutKeySequence("FullScreen");
  374. QKeySequence seq(keySeq);
  375. if (!seq.isEmpty()) {
  376. fullScreenAct->setText(tr("Full Screen\t%1").arg(VUtils::getShortcutText(keySeq)));
  377. fullScreenAct->setShortcut(seq);
  378. }
  379. fullScreenAct->setStatusTip(tr("Toggle full screen"));
  380. fullScreenAct->setMenu(screenMenu);
  381. connect(fullScreenAct, &QAction::triggered,
  382. this, [this]() {
  383. if (windowState() & Qt::WindowFullScreen) {
  384. if (m_windowOldState & Qt::WindowMaximized) {
  385. showMaximized();
  386. } else {
  387. showNormal();
  388. }
  389. } else {
  390. showFullScreen();
  391. }
  392. });
  393. viewToolBar->addAction(fullScreenAct);
  394. }
  395. // Enable/disable all actions of @p_widget.
  396. static void setActionsEnabled(QWidget *p_widget, bool p_enabled)
  397. {
  398. Q_ASSERT(p_widget);
  399. QList<QAction *> actions = p_widget->actions();
  400. for (auto const & act : actions) {
  401. act->setEnabled(p_enabled);
  402. }
  403. }
  404. void VMainWindow::initEditToolBar(QSize p_iconSize)
  405. {
  406. m_editToolBar = addToolBar(tr("Edit Toolbar"));
  407. m_editToolBar->setObjectName("EditToolBar");
  408. m_editToolBar->setMovable(false);
  409. if (p_iconSize.isValid()) {
  410. m_editToolBar->setIconSize(p_iconSize);
  411. }
  412. m_editToolBar->addSeparator();
  413. m_headingSequenceAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/heading_sequence.svg"),
  414. tr("Heading Sequence"),
  415. this);
  416. m_headingSequenceAct->setStatusTip(tr("Enable heading sequence in current note in edit mode"));
  417. m_headingSequenceAct->setCheckable(true);
  418. connect(m_headingSequenceAct, &QAction::triggered,
  419. this, [this](bool p_checked){
  420. if (isHeadingSequenceApplicable()) {
  421. VMdTab *tab = dynamic_cast<VMdTab *>(m_curTab.data());
  422. Q_ASSERT(tab);
  423. tab->enableHeadingSequence(p_checked);
  424. }
  425. });
  426. m_editToolBar->addAction(m_headingSequenceAct);
  427. initHeadingButton(m_editToolBar);
  428. QAction *boldAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/bold.svg"),
  429. tr("Bold\t%1").arg(VUtils::getShortcutText("Ctrl+B")),
  430. this);
  431. boldAct->setStatusTip(tr("Insert bold text or change selected text to bold"));
  432. connect(boldAct, &QAction::triggered,
  433. this, [this](){
  434. if (m_curTab) {
  435. m_curTab->decorateText(TextDecoration::Bold);
  436. }
  437. });
  438. m_editToolBar->addAction(boldAct);
  439. QAction *italicAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/italic.svg"),
  440. tr("Italic\t%1").arg(VUtils::getShortcutText("Ctrl+I")),
  441. this);
  442. italicAct->setStatusTip(tr("Insert italic text or change selected text to italic"));
  443. connect(italicAct, &QAction::triggered,
  444. this, [this](){
  445. if (m_curTab) {
  446. m_curTab->decorateText(TextDecoration::Italic);
  447. }
  448. });
  449. m_editToolBar->addAction(italicAct);
  450. QAction *strikethroughAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/strikethrough.svg"),
  451. tr("Strikethrough\t%1").arg(VUtils::getShortcutText("Ctrl+D")),
  452. this);
  453. strikethroughAct->setStatusTip(tr("Insert strikethrough text or change selected text to strikethroughed"));
  454. connect(strikethroughAct, &QAction::triggered,
  455. this, [this](){
  456. if (m_curTab) {
  457. m_curTab->decorateText(TextDecoration::Strikethrough);
  458. }
  459. });
  460. m_editToolBar->addAction(strikethroughAct);
  461. QAction *inlineCodeAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/inline_code.svg"),
  462. tr("Inline Code\t%1").arg(VUtils::getShortcutText("Ctrl+K")),
  463. this);
  464. inlineCodeAct->setStatusTip(tr("Insert inline-code text or change selected text to inline-coded"));
  465. connect(inlineCodeAct, &QAction::triggered,
  466. this, [this](){
  467. if (m_curTab) {
  468. m_curTab->decorateText(TextDecoration::InlineCode);
  469. }
  470. });
  471. m_editToolBar->addAction(inlineCodeAct);
  472. QAction *codeBlockAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/code_block.svg"),
  473. tr("Code Block\t%1").arg(VUtils::getShortcutText("Ctrl+M")),
  474. this);
  475. codeBlockAct->setStatusTip(tr("Insert fenced code block text or wrap selected text into a fenced code block"));
  476. connect(codeBlockAct, &QAction::triggered,
  477. this, [this](){
  478. if (m_curTab) {
  479. m_curTab->decorateText(TextDecoration::CodeBlock);
  480. }
  481. });
  482. m_editToolBar->addAction(codeBlockAct);
  483. // Insert link.
  484. QAction *insetLinkAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/link.svg"),
  485. tr("Insert Link\t%1").arg(VUtils::getShortcutText("Ctrl+L")),
  486. this);
  487. insetLinkAct->setStatusTip(tr("Insert a link"));
  488. connect(insetLinkAct, &QAction::triggered,
  489. this, [this]() {
  490. if (m_curTab) {
  491. m_curTab->insertLink();
  492. }
  493. });
  494. m_editToolBar->addAction(insetLinkAct);
  495. // Insert image.
  496. QAction *insertImageAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/insert_image.svg"),
  497. tr("Insert Image\t%1").arg(VUtils::getShortcutText("Ctrl+'")),
  498. this);
  499. insertImageAct->setStatusTip(tr("Insert an image from file or URL"));
  500. connect(insertImageAct, &QAction::triggered,
  501. this, [this]() {
  502. if (m_curTab) {
  503. m_curTab->insertImage();
  504. }
  505. });
  506. m_editToolBar->addAction(insertImageAct);
  507. QAction *toggleAct = m_editToolBar->toggleViewAction();
  508. toggleAct->setToolTip(tr("Toggle the edit toolbar"));
  509. viewMenu->addAction(toggleAct);
  510. setActionsEnabled(m_editToolBar, false);
  511. }
  512. void VMainWindow::initNoteToolBar(QSize p_iconSize)
  513. {
  514. QToolBar *noteToolBar = addToolBar(tr("Note Toolbar"));
  515. noteToolBar->setObjectName("NoteToolBar");
  516. noteToolBar->setMovable(false);
  517. if (p_iconSize.isValid()) {
  518. noteToolBar->setIconSize(p_iconSize);
  519. }
  520. noteToolBar->addSeparator();
  521. // Attachment.
  522. m_attachmentList = new VAttachmentList(this);
  523. m_attachmentBtn = new VButtonWithWidget(VIconUtils::toolButtonIcon(":/resources/icons/attachment.svg"),
  524. "",
  525. m_attachmentList,
  526. this);
  527. m_attachmentBtn->setBubbleColor(g_palette->color("bubble_fg"),
  528. g_palette->color("bubble_bg"));
  529. m_attachmentBtn->setToolTip(tr("Attachments (drag files here to add attachments)"));
  530. m_attachmentBtn->setProperty("CornerBtn", true);
  531. m_attachmentBtn->setFocusPolicy(Qt::NoFocus);
  532. m_attachmentBtn->setEnabled(false);
  533. QAction *flashPageAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/flash_page.svg"),
  534. tr("Flash Page"),
  535. this);
  536. flashPageAct->setStatusTip(tr("Open the Flash Page to edit"));
  537. QString keySeq = g_config->getShortcutKeySequence("FlashPage");
  538. QKeySequence seq(keySeq);
  539. if (!seq.isEmpty()) {
  540. flashPageAct->setText(tr("Flash Page\t%1").arg(VUtils::getShortcutText(keySeq)));
  541. flashPageAct->setShortcut(seq);
  542. }
  543. connect(flashPageAct, &QAction::triggered,
  544. this, &VMainWindow::openFlashPage);
  545. noteToolBar->addWidget(m_attachmentBtn);
  546. noteToolBar->addAction(flashPageAct);
  547. }
  548. void VMainWindow::initFileToolBar(QSize p_iconSize)
  549. {
  550. QToolBar *fileToolBar = addToolBar(tr("Note"));
  551. fileToolBar->setObjectName("NoteToolBar");
  552. fileToolBar->setMovable(false);
  553. if (p_iconSize.isValid()) {
  554. fileToolBar->setIconSize(p_iconSize);
  555. }
  556. m_avatarBtn = new QPushButton("VNote", this);
  557. m_avatarBtn->setProperty("AvatarBtn", true);
  558. m_avatarBtn->setFocusPolicy(Qt::NoFocus);
  559. m_avatarBtn->setToolTip(tr("Log In (Not Implemented Yet)"));
  560. newRootDirAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/create_rootdir_tb.svg"),
  561. tr("New Root Folder"),
  562. this);
  563. newRootDirAct->setStatusTip(tr("Create a root folder in current notebook"));
  564. connect(newRootDirAct, &QAction::triggered,
  565. directoryTree, &VDirectoryTree::newRootDirectory);
  566. newNoteAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/create_note_tb.svg"),
  567. tr("New Note"), this);
  568. newNoteAct->setStatusTip(tr("Create a note in current folder"));
  569. QString keySeq = g_config->getShortcutKeySequence("NewNote");
  570. QKeySequence seq(keySeq);
  571. if (!seq.isEmpty()) {
  572. newNoteAct->setText(tr("New Note\t%1").arg(VUtils::getShortcutText(keySeq)));
  573. newNoteAct->setShortcut(seq);
  574. }
  575. connect(newNoteAct, &QAction::triggered,
  576. m_fileList, &VFileList::newFile);
  577. noteInfoAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/note_info_tb.svg"),
  578. tr("Note Info"), this);
  579. noteInfoAct->setStatusTip(tr("View and edit current note's information"));
  580. connect(noteInfoAct, &QAction::triggered,
  581. this, &VMainWindow::curEditFileInfo);
  582. deleteNoteAct = new QAction(VIconUtils::toolButtonDangerIcon(":/resources/icons/delete_note_tb.svg"),
  583. tr("Delete Note"), this);
  584. deleteNoteAct->setStatusTip(tr("Delete current note"));
  585. connect(deleteNoteAct, &QAction::triggered,
  586. this, &VMainWindow::deleteCurNote);
  587. m_editReadAct = new QAction(this);
  588. connect(m_editReadAct, &QAction::triggered,
  589. this, &VMainWindow::toggleEditReadMode);
  590. m_discardExitAct = new QAction(VIconUtils::menuIcon(":/resources/icons/discard_exit.svg"),
  591. tr("Discard Changes And Read"),
  592. this);
  593. keySeq = VUtils::getCaptainShortcutSequenceText("DiscardAndRead");
  594. if (!keySeq.isEmpty()) {
  595. m_discardExitAct->setText(tr("Discard Changes And Read\t%1").arg(keySeq));
  596. }
  597. m_discardExitAct->setStatusTip(tr("Discard changes and exit edit mode"));
  598. connect(m_discardExitAct, &QAction::triggered,
  599. editArea, &VEditArea::readFile);
  600. updateEditReadAct(NULL);
  601. saveNoteAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/save_note.svg"),
  602. tr("Save"), this);
  603. saveNoteAct->setStatusTip(tr("Save changes to current note"));
  604. keySeq = g_config->getShortcutKeySequence("SaveNote");
  605. seq = QKeySequence(keySeq);
  606. if (!seq.isEmpty()) {
  607. saveNoteAct->setText(tr("Save\t%1").arg(VUtils::getShortcutText(keySeq)));
  608. saveNoteAct->setShortcut(seq);
  609. }
  610. connect(saveNoteAct, &QAction::triggered,
  611. editArea, &VEditArea::saveFile);
  612. newRootDirAct->setEnabled(false);
  613. newNoteAct->setEnabled(false);
  614. noteInfoAct->setEnabled(false);
  615. deleteNoteAct->setEnabled(false);
  616. m_editReadAct->setEnabled(false);
  617. m_discardExitAct->setEnabled(false);
  618. saveNoteAct->setEnabled(false);
  619. fileToolBar->addWidget(m_avatarBtn);
  620. fileToolBar->addAction(newRootDirAct);
  621. fileToolBar->addAction(newNoteAct);
  622. fileToolBar->addAction(deleteNoteAct);
  623. fileToolBar->addAction(noteInfoAct);
  624. fileToolBar->addAction(m_editReadAct);
  625. fileToolBar->addAction(m_discardExitAct);
  626. fileToolBar->addAction(saveNoteAct);
  627. }
  628. void VMainWindow::initMenuBar()
  629. {
  630. initFileMenu();
  631. initEditMenu();
  632. initViewMenu();
  633. initMarkdownMenu();
  634. initHelpMenu();
  635. menuBar()->setVisible(g_config->getMenuBarChecked());
  636. }
  637. void VMainWindow::initHelpMenu()
  638. {
  639. QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
  640. helpMenu->setToolTipsVisible(true);
  641. #if defined(QT_NO_DEBUG)
  642. QAction *logAct = new QAction(tr("View &Log"), this);
  643. logAct->setToolTip(tr("View VNote's debug log (%1)").arg(g_config->getLogFilePath()));
  644. connect(logAct, &QAction::triggered,
  645. this, [](){
  646. QUrl url = QUrl::fromLocalFile(g_config->getLogFilePath());
  647. QDesktopServices::openUrl(url);
  648. });
  649. #endif
  650. QAction *shortcutAct = new QAction(tr("&Shortcuts Help"), this);
  651. shortcutAct->setToolTip(tr("View information about shortcut keys"));
  652. connect(shortcutAct, &QAction::triggered,
  653. this, &VMainWindow::shortcutsHelp);
  654. QAction *mdGuideAct = new QAction(tr("&Markdown Guide"), this);
  655. mdGuideAct->setToolTip(tr("A quick guide of Markdown syntax"));
  656. connect(mdGuideAct, &QAction::triggered,
  657. this, [this](){
  658. QString docFile = VUtils::getDocFile(VNote::c_markdownGuideDocFile);
  659. VFile *file = vnote->getOrphanFile(docFile, false, true);
  660. editArea->openFile(file, OpenFileMode::Read);
  661. });
  662. QAction *wikiAct = new QAction(tr("&Wiki"), this);
  663. wikiAct->setToolTip(tr("View VNote's wiki on Github"));
  664. connect(wikiAct, &QAction::triggered,
  665. this, [this]() {
  666. QString url("https://github.com/tamlok/vnote/wiki");
  667. QDesktopServices::openUrl(url);
  668. });
  669. QAction *updateAct = new QAction(tr("Check For &Updates"), this);
  670. updateAct->setToolTip(tr("Check for updates of VNote"));
  671. connect(updateAct, &QAction::triggered,
  672. this, [this](){
  673. VUpdater updater(this);
  674. updater.exec();
  675. });
  676. QAction *starAct = new QAction(tr("Star VNote on &Github"), this);
  677. starAct->setToolTip(tr("Give a star to VNote on Github project"));
  678. connect(starAct, &QAction::triggered,
  679. this, [this]() {
  680. QString url("https://github.com/tamlok/vnote");
  681. QDesktopServices::openUrl(url);
  682. });
  683. QAction *feedbackAct = new QAction(tr("&Feedback"), this);
  684. feedbackAct->setToolTip(tr("Open an issue on Github"));
  685. connect(feedbackAct, &QAction::triggered,
  686. this, [this]() {
  687. QString url("https://github.com/tamlok/vnote/issues");
  688. QDesktopServices::openUrl(url);
  689. });
  690. QAction *aboutAct = new QAction(tr("&About VNote"), this);
  691. aboutAct->setToolTip(tr("View information about VNote"));
  692. aboutAct->setMenuRole(QAction::AboutRole);
  693. connect(aboutAct, &QAction::triggered,
  694. this, &VMainWindow::aboutMessage);
  695. QAction *aboutQtAct = new QAction(tr("About &Qt"), this);
  696. aboutQtAct->setToolTip(tr("View information about Qt"));
  697. aboutQtAct->setMenuRole(QAction::AboutQtRole);
  698. connect(aboutQtAct, &QAction::triggered,
  699. qApp, &QApplication::aboutQt);
  700. helpMenu->addAction(shortcutAct);
  701. helpMenu->addAction(mdGuideAct);
  702. helpMenu->addAction(wikiAct);
  703. helpMenu->addAction(updateAct);
  704. helpMenu->addAction(starAct);
  705. helpMenu->addAction(feedbackAct);
  706. #if defined(QT_NO_DEBUG)
  707. helpMenu->addAction(logAct);
  708. #endif
  709. helpMenu->addAction(aboutQtAct);
  710. helpMenu->addAction(aboutAct);
  711. }
  712. void VMainWindow::initMarkdownMenu()
  713. {
  714. QMenu *markdownMenu = menuBar()->addMenu(tr("&Markdown"));
  715. markdownMenu->setToolTipsVisible(true);
  716. initConverterMenu(markdownMenu);
  717. initMarkdownitOptionMenu(markdownMenu);
  718. markdownMenu->addSeparator();
  719. initRenderStyleMenu(markdownMenu);
  720. initRenderBackgroundMenu(markdownMenu);
  721. initCodeBlockStyleMenu(markdownMenu);
  722. QAction *constrainImageAct = new QAction(tr("Constrain The Width Of Images"), this);
  723. constrainImageAct->setToolTip(tr("Constrain the width of images to the window in read mode (re-open current tabs to make it work)"));
  724. constrainImageAct->setCheckable(true);
  725. connect(constrainImageAct, &QAction::triggered,
  726. this, &VMainWindow::enableImageConstraint);
  727. markdownMenu->addAction(constrainImageAct);
  728. constrainImageAct->setChecked(g_config->getEnableImageConstraint());
  729. QAction *imageCaptionAct = new QAction(tr("Enable Image Caption"), this);
  730. imageCaptionAct->setToolTip(tr("Center the images and display the alt text as caption (re-open current tabs to make it work)"));
  731. imageCaptionAct->setCheckable(true);
  732. connect(imageCaptionAct, &QAction::triggered,
  733. this, &VMainWindow::enableImageCaption);
  734. markdownMenu->addAction(imageCaptionAct);
  735. imageCaptionAct->setChecked(g_config->getEnableImageCaption());
  736. markdownMenu->addSeparator();
  737. QAction *mermaidAct = new QAction(tr("&Mermaid Diagram"), this);
  738. mermaidAct->setToolTip(tr("Enable Mermaid for graph and diagram"));
  739. mermaidAct->setCheckable(true);
  740. connect(mermaidAct, &QAction::triggered,
  741. this, &VMainWindow::enableMermaid);
  742. markdownMenu->addAction(mermaidAct);
  743. mermaidAct->setChecked(g_config->getEnableMermaid());
  744. QAction *flowchartAct = new QAction(tr("&Flowchart.js"), this);
  745. flowchartAct->setToolTip(tr("Enable Flowchart.js for flowchart diagram"));
  746. flowchartAct->setCheckable(true);
  747. connect(flowchartAct, &QAction::triggered,
  748. this, [this](bool p_enabled){
  749. g_config->setEnableFlowchart(p_enabled);
  750. });
  751. markdownMenu->addAction(flowchartAct);
  752. flowchartAct->setChecked(g_config->getEnableFlowchart());
  753. QAction *mathjaxAct = new QAction(tr("Math&Jax"), this);
  754. mathjaxAct->setToolTip(tr("Enable MathJax for math support in Markdown"));
  755. mathjaxAct->setCheckable(true);
  756. connect(mathjaxAct, &QAction::triggered,
  757. this, &VMainWindow::enableMathjax);
  758. markdownMenu->addAction(mathjaxAct);
  759. mathjaxAct->setChecked(g_config->getEnableMathjax());
  760. markdownMenu->addSeparator();
  761. QAction *codeBlockAct = new QAction(tr("Highlight Code Blocks In Edit Mode"), this);
  762. codeBlockAct->setToolTip(tr("Enable syntax highlight within code blocks in edit mode"));
  763. codeBlockAct->setCheckable(true);
  764. connect(codeBlockAct, &QAction::triggered,
  765. this, &VMainWindow::enableCodeBlockHighlight);
  766. markdownMenu->addAction(codeBlockAct);
  767. codeBlockAct->setChecked(g_config->getEnableCodeBlockHighlight());
  768. QAction *lineNumberAct = new QAction(tr("Display Line Number In Code Blocks"), this);
  769. lineNumberAct->setToolTip(tr("Enable line number in code blocks in read mode"));
  770. lineNumberAct->setCheckable(true);
  771. connect(lineNumberAct, &QAction::triggered,
  772. this, [this](bool p_checked){
  773. g_config->setEnableCodeBlockLineNumber(p_checked);
  774. });
  775. markdownMenu->addAction(lineNumberAct);
  776. lineNumberAct->setChecked(g_config->getEnableCodeBlockLineNumber());
  777. QAction *previewImageAct = new QAction(tr("Preview Images In Edit Mode"), this);
  778. previewImageAct->setToolTip(tr("Enable image preview in edit mode (re-open current tabs to make it work)"));
  779. previewImageAct->setCheckable(true);
  780. connect(previewImageAct, &QAction::triggered,
  781. this, &VMainWindow::enableImagePreview);
  782. markdownMenu->addAction(previewImageAct);
  783. previewImageAct->setChecked(g_config->getEnablePreviewImages());
  784. QAction *previewWidthAct = new QAction(tr("Constrain The Width Of Previewed Images"), this);
  785. previewWidthAct->setToolTip(tr("Constrain the width of previewed images to the edit window in edit mode"));
  786. previewWidthAct->setCheckable(true);
  787. connect(previewWidthAct, &QAction::triggered,
  788. this, &VMainWindow::enableImagePreviewConstraint);
  789. markdownMenu->addAction(previewWidthAct);
  790. previewWidthAct->setChecked(g_config->getEnablePreviewImageConstraint());
  791. }
  792. void VMainWindow::initViewMenu()
  793. {
  794. viewMenu = menuBar()->addMenu(tr("&View"));
  795. viewMenu->setToolTipsVisible(true);
  796. }
  797. void VMainWindow::initFileMenu()
  798. {
  799. QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
  800. fileMenu->setToolTipsVisible(true);
  801. // Open external files.
  802. QAction *openAct = new QAction(tr("&Open"), this);
  803. openAct->setToolTip(tr("Open external file to edit"));
  804. connect(openAct, &QAction::triggered,
  805. this, [this](){
  806. static QString lastPath = QDir::homePath();
  807. QStringList files = QFileDialog::getOpenFileNames(this,
  808. tr("Select External Files To Open"),
  809. lastPath);
  810. if (files.isEmpty()) {
  811. return;
  812. }
  813. // Update lastPath
  814. lastPath = QFileInfo(files[0]).path();
  815. openFiles(VUtils::filterFilePathsToOpen(files));
  816. });
  817. fileMenu->addAction(openAct);
  818. // Import notes from files.
  819. m_importNoteAct = newAction(VIconUtils::menuIcon(":/resources/icons/import_note.svg"),
  820. tr("&New Notes From Files"), this);
  821. m_importNoteAct->setToolTip(tr("Create notes from external files in current folder by copy"));
  822. connect(m_importNoteAct, &QAction::triggered,
  823. this, &VMainWindow::importNoteFromFile);
  824. m_importNoteAct->setEnabled(false);
  825. fileMenu->addAction(m_importNoteAct);
  826. fileMenu->addSeparator();
  827. // Export as PDF.
  828. m_exportAct = new QAction(tr("E&xport"), this);
  829. m_exportAct->setToolTip(tr("Export notes"));
  830. connect(m_exportAct, &QAction::triggered,
  831. this, &VMainWindow::handleExportAct);
  832. fileMenu->addAction(m_exportAct);
  833. // Print.
  834. m_printAct = new QAction(VIconUtils::menuIcon(":/resources/icons/print.svg"),
  835. tr("&Print"), this);
  836. m_printAct->setToolTip(tr("Print current note"));
  837. connect(m_printAct, &QAction::triggered,
  838. this, &VMainWindow::printNote);
  839. m_printAct->setEnabled(false);
  840. fileMenu->addAction(m_printAct);
  841. fileMenu->addSeparator();
  842. // Themes.
  843. initThemeMenu(fileMenu);
  844. // Settings.
  845. QAction *settingsAct = newAction(VIconUtils::menuIcon(":/resources/icons/settings.svg"),
  846. tr("&Settings"), this);
  847. settingsAct->setToolTip(tr("View and change settings for VNote"));
  848. settingsAct->setMenuRole(QAction::PreferencesRole);
  849. connect(settingsAct, &QAction::triggered,
  850. this, &VMainWindow::viewSettings);
  851. fileMenu->addAction(settingsAct);
  852. QAction *openConfigAct = new QAction(tr("Open Configuration Folder"), this);
  853. openConfigAct->setToolTip(tr("Open configuration folder of VNote"));
  854. connect(openConfigAct, &QAction::triggered,
  855. this, [this](){
  856. QUrl url = QUrl::fromLocalFile(g_config->getConfigFolder());
  857. QDesktopServices::openUrl(url);
  858. });
  859. fileMenu->addAction(openConfigAct);
  860. QAction *customShortcutAct = new QAction(tr("Custom Shortcuts"), this);
  861. customShortcutAct->setToolTip(tr("Custom some standard shortcuts"));
  862. connect(customShortcutAct, &QAction::triggered,
  863. this, &VMainWindow::customShortcut);
  864. fileMenu->addAction(customShortcutAct);
  865. fileMenu->addSeparator();
  866. // Exit.
  867. QAction *exitAct = new QAction(tr("&Quit"), this);
  868. exitAct->setToolTip(tr("Quit VNote"));
  869. exitAct->setShortcut(QKeySequence("Ctrl+Q"));
  870. exitAct->setMenuRole(QAction::QuitRole);
  871. connect(exitAct, &QAction::triggered,
  872. this, &VMainWindow::quitApp);
  873. fileMenu->addAction(exitAct);
  874. }
  875. void VMainWindow::quitApp()
  876. {
  877. m_requestQuit = true;
  878. close();
  879. }
  880. void VMainWindow::initEditMenu()
  881. {
  882. QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));
  883. editMenu->setToolTipsVisible(true);
  884. // Find/Replace.
  885. m_findReplaceAct = newAction(VIconUtils::menuIcon(":/resources/icons/find_replace.svg"),
  886. tr("Find/Replace"), this);
  887. m_findReplaceAct->setToolTip(tr("Open Find/Replace dialog to search in current note"));
  888. QString keySeq = g_config->getShortcutKeySequence("Find");
  889. qDebug() << "set Find shortcut to" << keySeq;
  890. m_findReplaceAct->setShortcut(QKeySequence(keySeq));
  891. connect(m_findReplaceAct, &QAction::triggered,
  892. this, &VMainWindow::openFindDialog);
  893. m_findNextAct = new QAction(tr("Find Next"), this);
  894. m_findNextAct->setToolTip(tr("Find next occurence"));
  895. keySeq = g_config->getShortcutKeySequence("FindNext");
  896. qDebug() << "set FindNext shortcut to" << keySeq;
  897. m_findNextAct->setShortcut(QKeySequence(keySeq));
  898. connect(m_findNextAct, SIGNAL(triggered(bool)),
  899. m_findReplaceDialog, SLOT(findNext()));
  900. m_findPreviousAct = new QAction(tr("Find Previous"), this);
  901. m_findPreviousAct->setToolTip(tr("Find previous occurence"));
  902. keySeq = g_config->getShortcutKeySequence("FindPrevious");
  903. qDebug() << "set FindPrevious shortcut to" << keySeq;
  904. m_findPreviousAct->setShortcut(QKeySequence(keySeq));
  905. connect(m_findPreviousAct, SIGNAL(triggered(bool)),
  906. m_findReplaceDialog, SLOT(findPrevious()));
  907. m_replaceAct = new QAction(tr("Replace"), this);
  908. m_replaceAct->setToolTip(tr("Replace current occurence"));
  909. connect(m_replaceAct, SIGNAL(triggered(bool)),
  910. m_findReplaceDialog, SLOT(replace()));
  911. m_replaceFindAct = new QAction(tr("Replace && Find"), this);
  912. m_replaceFindAct->setToolTip(tr("Replace current occurence and find the next one"));
  913. connect(m_replaceFindAct, SIGNAL(triggered(bool)),
  914. m_findReplaceDialog, SLOT(replaceFind()));
  915. m_replaceAllAct = new QAction(tr("Replace All"), this);
  916. m_replaceAllAct->setToolTip(tr("Replace all occurences in current note"));
  917. connect(m_replaceAllAct, SIGNAL(triggered(bool)),
  918. m_findReplaceDialog, SLOT(replaceAll()));
  919. QAction *searchedWordAct = new QAction(tr("Highlight Searched Pattern"), this);
  920. searchedWordAct->setToolTip(tr("Highlight all occurences of searched pattern"));
  921. searchedWordAct->setCheckable(true);
  922. connect(searchedWordAct, &QAction::triggered,
  923. this, &VMainWindow::changeHighlightSearchedWord);
  924. // Expand Tab into spaces.
  925. QAction *expandTabAct = new QAction(tr("&Expand Tab"), this);
  926. expandTabAct->setToolTip(tr("Expand entered Tab to spaces"));
  927. expandTabAct->setCheckable(true);
  928. connect(expandTabAct, &QAction::triggered,
  929. this, &VMainWindow::changeExpandTab);
  930. // Tab stop width.
  931. QActionGroup *tabStopWidthAct = new QActionGroup(this);
  932. QAction *twoSpaceTabAct = new QAction(tr("2 Spaces"), tabStopWidthAct);
  933. twoSpaceTabAct->setToolTip(tr("Expand Tab to 2 spaces"));
  934. twoSpaceTabAct->setCheckable(true);
  935. twoSpaceTabAct->setData(2);
  936. QAction *fourSpaceTabAct = new QAction(tr("4 Spaces"), tabStopWidthAct);
  937. fourSpaceTabAct->setToolTip(tr("Expand Tab to 4 spaces"));
  938. fourSpaceTabAct->setCheckable(true);
  939. fourSpaceTabAct->setData(4);
  940. QAction *eightSpaceTabAct = new QAction(tr("8 Spaces"), tabStopWidthAct);
  941. eightSpaceTabAct->setToolTip(tr("Expand Tab to 8 spaces"));
  942. eightSpaceTabAct->setCheckable(true);
  943. eightSpaceTabAct->setData(8);
  944. connect(tabStopWidthAct, &QActionGroup::triggered,
  945. this, &VMainWindow::setTabStopWidth);
  946. // Auto Indent.
  947. m_autoIndentAct = new QAction(tr("Auto Indent"), this);
  948. m_autoIndentAct->setToolTip(tr("Indent automatically when inserting a new line"));
  949. m_autoIndentAct->setCheckable(true);
  950. connect(m_autoIndentAct, &QAction::triggered,
  951. this, &VMainWindow::changeAutoIndent);
  952. // Auto List.
  953. QAction *autoListAct = new QAction(tr("Auto List"), this);
  954. autoListAct->setToolTip(tr("Continue the list automatically when inserting a new line"));
  955. autoListAct->setCheckable(true);
  956. connect(autoListAct, &QAction::triggered,
  957. this, &VMainWindow::changeAutoList);
  958. // Vim Mode.
  959. QAction *vimAct = new QAction(tr("Vim Mode"), this);
  960. vimAct->setToolTip(tr("Enable Vim mode for editing (re-open current tabs to make it work)"));
  961. vimAct->setCheckable(true);
  962. connect(vimAct, &QAction::triggered,
  963. this, &VMainWindow::changeVimMode);
  964. // Smart input method in Vim mode.
  965. QAction *smartImAct = new QAction(tr("Smart Input Method In Vim Mode"), this);
  966. smartImAct->setToolTip(tr("Disable input method when leaving Insert mode in Vim mode"));
  967. smartImAct->setCheckable(true);
  968. connect(smartImAct, &QAction::triggered,
  969. this, [this](bool p_checked){
  970. g_config->setEnableSmartImInVimMode(p_checked);
  971. });
  972. // Highlight current cursor line.
  973. QAction *cursorLineAct = new QAction(tr("Highlight Cursor Line"), this);
  974. cursorLineAct->setToolTip(tr("Highlight current cursor line"));
  975. cursorLineAct->setCheckable(true);
  976. connect(cursorLineAct, &QAction::triggered,
  977. this, &VMainWindow::changeHighlightCursorLine);
  978. // Highlight selected word.
  979. QAction *selectedWordAct = new QAction(tr("Highlight Selected Words"), this);
  980. selectedWordAct->setToolTip(tr("Highlight all occurences of selected words"));
  981. selectedWordAct->setCheckable(true);
  982. connect(selectedWordAct, &QAction::triggered,
  983. this, &VMainWindow::changeHighlightSelectedWord);
  984. // Highlight trailing space.
  985. QAction *trailingSapceAct = new QAction(tr("Highlight Trailing Spaces"), this);
  986. trailingSapceAct->setToolTip(tr("Highlight all the spaces at the end of a line"));
  987. trailingSapceAct->setCheckable(true);
  988. connect(trailingSapceAct, &QAction::triggered,
  989. this, &VMainWindow::changeHighlightTrailingSapce);
  990. QMenu *findReplaceMenu = editMenu->addMenu(tr("Find/Replace"));
  991. findReplaceMenu->setToolTipsVisible(true);
  992. findReplaceMenu->addAction(m_findReplaceAct);
  993. findReplaceMenu->addAction(m_findNextAct);
  994. findReplaceMenu->addAction(m_findPreviousAct);
  995. findReplaceMenu->addAction(m_replaceAct);
  996. findReplaceMenu->addAction(m_replaceFindAct);
  997. findReplaceMenu->addAction(m_replaceAllAct);
  998. findReplaceMenu->addSeparator();
  999. findReplaceMenu->addAction(searchedWordAct);
  1000. searchedWordAct->setChecked(g_config->getHighlightSearchedWord());
  1001. m_findReplaceAct->setEnabled(false);
  1002. m_findNextAct->setEnabled(false);
  1003. m_findPreviousAct->setEnabled(false);
  1004. m_replaceAct->setEnabled(false);
  1005. m_replaceFindAct->setEnabled(false);
  1006. m_replaceAllAct->setEnabled(false);
  1007. editMenu->addSeparator();
  1008. editMenu->addAction(expandTabAct);
  1009. if (g_config->getIsExpandTab()) {
  1010. expandTabAct->setChecked(true);
  1011. } else {
  1012. expandTabAct->setChecked(false);
  1013. }
  1014. QMenu *tabStopWidthMenu = editMenu->addMenu(tr("Tab Stop Width"));
  1015. tabStopWidthMenu->setToolTipsVisible(true);
  1016. tabStopWidthMenu->addAction(twoSpaceTabAct);
  1017. tabStopWidthMenu->addAction(fourSpaceTabAct);
  1018. tabStopWidthMenu->addAction(eightSpaceTabAct);
  1019. int tabStopWidth = g_config->getTabStopWidth();
  1020. switch (tabStopWidth) {
  1021. case 2:
  1022. twoSpaceTabAct->setChecked(true);
  1023. break;
  1024. case 4:
  1025. fourSpaceTabAct->setChecked(true);
  1026. break;
  1027. case 8:
  1028. eightSpaceTabAct->setChecked(true);
  1029. break;
  1030. default:
  1031. qWarning() << "unsupported tab stop width" << tabStopWidth << "in config";
  1032. }
  1033. editMenu->addAction(m_autoIndentAct);
  1034. m_autoIndentAct->setChecked(g_config->getAutoIndent());
  1035. editMenu->addAction(autoListAct);
  1036. if (g_config->getAutoList()) {
  1037. // Let the trigger handler to trigger m_autoIndentAct, too.
  1038. autoListAct->trigger();
  1039. }
  1040. Q_ASSERT(!(autoListAct->isChecked() && !m_autoIndentAct->isChecked()));
  1041. editMenu->addAction(vimAct);
  1042. vimAct->setChecked(g_config->getEnableVimMode());
  1043. editMenu->addAction(smartImAct);
  1044. smartImAct->setChecked(g_config->getEnableSmartImInVimMode());
  1045. editMenu->addSeparator();
  1046. initEditorStyleMenu(editMenu);
  1047. initEditorBackgroundMenu(editMenu);
  1048. initEditorLineNumberMenu(editMenu);
  1049. editMenu->addAction(cursorLineAct);
  1050. cursorLineAct->setChecked(g_config->getHighlightCursorLine());
  1051. editMenu->addAction(selectedWordAct);
  1052. selectedWordAct->setChecked(g_config->getHighlightSelectedWord());
  1053. editMenu->addAction(trailingSapceAct);
  1054. trailingSapceAct->setChecked(g_config->getEnableTrailingSpaceHighlight());
  1055. }
  1056. void VMainWindow::initDockWindows()
  1057. {
  1058. toolDock = new QDockWidget(tr("Tools"), this);
  1059. toolDock->setObjectName("ToolsDock");
  1060. toolDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
  1061. // Outline tree.
  1062. outline = new VOutline(this);
  1063. connect(editArea, &VEditArea::outlineChanged,
  1064. outline, &VOutline::updateOutline);
  1065. connect(editArea, &VEditArea::currentHeaderChanged,
  1066. outline, &VOutline::updateCurrentHeader);
  1067. connect(outline, &VOutline::outlineItemActivated,
  1068. editArea, &VEditArea::scrollToHeader);
  1069. // Snippets.
  1070. m_snippetList = new VSnippetList(this);
  1071. // Cart.
  1072. m_cart = new VCart(this);
  1073. m_toolBox = new VToolBox(this);
  1074. m_toolBox->addItem(outline,
  1075. ":/resources/icons/outline.svg",
  1076. tr("Outline"));
  1077. m_toolBox->addItem(m_snippetList,
  1078. ":/resources/icons/snippets.svg",
  1079. tr("Snippets"));
  1080. m_toolBox->addItem(m_cart,
  1081. ":/resources/icons/cart.svg",
  1082. tr("Cart"));
  1083. toolDock->setWidget(m_toolBox);
  1084. addDockWidget(Qt::RightDockWidgetArea, toolDock);
  1085. QAction *toggleAct = toolDock->toggleViewAction();
  1086. toggleAct->setToolTip(tr("Toggle the tools dock widget"));
  1087. viewMenu->addAction(toggleAct);
  1088. }
  1089. void VMainWindow::importNoteFromFile()
  1090. {
  1091. static QString lastPath = QDir::homePath();
  1092. QStringList files = QFileDialog::getOpenFileNames(this,
  1093. tr("Select Files To Create Notes"),
  1094. lastPath);
  1095. if (files.isEmpty()) {
  1096. return;
  1097. }
  1098. // Update lastPath
  1099. lastPath = QFileInfo(files[0]).path();
  1100. QString msg;
  1101. if (!m_fileList->importFiles(files, &msg)) {
  1102. VUtils::showMessage(QMessageBox::Warning,
  1103. tr("Warning"),
  1104. tr("Fail to create notes for all the files."),
  1105. msg,
  1106. QMessageBox::Ok,
  1107. QMessageBox::Ok,
  1108. this);
  1109. } else {
  1110. int cnt = files.size();
  1111. showStatusMessage(tr("%1 %2 created from external files")
  1112. .arg(cnt)
  1113. .arg(cnt > 1 ? tr("notes") : tr("note")));
  1114. }
  1115. }
  1116. void VMainWindow::changeMarkdownConverter(QAction *action)
  1117. {
  1118. if (!action) {
  1119. return;
  1120. }
  1121. MarkdownConverterType type = (MarkdownConverterType)action->data().toInt();
  1122. g_config->setMarkdownConverterType(type);
  1123. }
  1124. void VMainWindow::aboutMessage()
  1125. {
  1126. QString info = tr("<span style=\"font-weight: bold;\">v%1</span>").arg(VConfigManager::c_version);
  1127. info += "<br/><br/>";
  1128. info += tr("VNote is a Vim-inspired note-taking application for Markdown.");
  1129. info += "<br/>";
  1130. info += tr("Please visit <a href=\"https://github.com/tamlok/vnote.git\">Github</a> for more information.");
  1131. QMessageBox::about(this, tr("About VNote"), info);
  1132. }
  1133. void VMainWindow::changeExpandTab(bool checked)
  1134. {
  1135. g_config->setIsExpandTab(checked);
  1136. }
  1137. void VMainWindow::enableMermaid(bool p_checked)
  1138. {
  1139. g_config->setEnableMermaid(p_checked);
  1140. }
  1141. void VMainWindow::enableMathjax(bool p_checked)
  1142. {
  1143. g_config->setEnableMathjax(p_checked);
  1144. }
  1145. void VMainWindow::changeHighlightCursorLine(bool p_checked)
  1146. {
  1147. g_config->setHighlightCursorLine(p_checked);
  1148. }
  1149. void VMainWindow::changeHighlightSelectedWord(bool p_checked)
  1150. {
  1151. g_config->setHighlightSelectedWord(p_checked);
  1152. }
  1153. void VMainWindow::changeHighlightSearchedWord(bool p_checked)
  1154. {
  1155. g_config->setHighlightSearchedWord(p_checked);
  1156. }
  1157. void VMainWindow::changeHighlightTrailingSapce(bool p_checked)
  1158. {
  1159. g_config->setEnableTrailingSapceHighlight(p_checked);
  1160. }
  1161. void VMainWindow::setTabStopWidth(QAction *action)
  1162. {
  1163. if (!action) {
  1164. return;
  1165. }
  1166. g_config->setTabStopWidth(action->data().toInt());
  1167. }
  1168. void VMainWindow::setEditorBackgroundColor(QAction *action)
  1169. {
  1170. if (!action) {
  1171. return;
  1172. }
  1173. g_config->setCurBackgroundColor(action->data().toString());
  1174. }
  1175. void VMainWindow::initConverterMenu(QMenu *p_menu)
  1176. {
  1177. QMenu *converterMenu = p_menu->addMenu(tr("&Renderer"));
  1178. converterMenu->setToolTipsVisible(true);
  1179. QActionGroup *converterAct = new QActionGroup(this);
  1180. QAction *markedAct = new QAction(tr("Marked"), converterAct);
  1181. markedAct->setToolTip(tr("Use Marked to convert Markdown to HTML (re-open current tabs to make it work)"));
  1182. markedAct->setCheckable(true);
  1183. markedAct->setData(int(MarkdownConverterType::Marked));
  1184. QAction *hoedownAct = new QAction(tr("Hoedown"), converterAct);
  1185. hoedownAct->setToolTip(tr("Use Hoedown to convert Markdown to HTML (re-open current tabs to make it work)"));
  1186. hoedownAct->setCheckable(true);
  1187. hoedownAct->setData(int(MarkdownConverterType::Hoedown));
  1188. QAction *markdownitAct = new QAction(tr("Markdown-it"), converterAct);
  1189. markdownitAct->setToolTip(tr("Use Markdown-it to convert Markdown to HTML (re-open current tabs to make it work)"));
  1190. markdownitAct->setCheckable(true);
  1191. markdownitAct->setData(int(MarkdownConverterType::MarkdownIt));
  1192. QAction *showdownAct = new QAction(tr("Showdown"), converterAct);
  1193. showdownAct->setToolTip(tr("Use Showdown to convert Markdown to HTML (re-open current tabs to make it work)"));
  1194. showdownAct->setCheckable(true);
  1195. showdownAct->setData(int(MarkdownConverterType::Showdown));
  1196. connect(converterAct, &QActionGroup::triggered,
  1197. this, &VMainWindow::changeMarkdownConverter);
  1198. converterMenu->addAction(hoedownAct);
  1199. converterMenu->addAction(markedAct);
  1200. converterMenu->addAction(markdownitAct);
  1201. converterMenu->addAction(showdownAct);
  1202. MarkdownConverterType converterType = g_config->getMdConverterType();
  1203. switch (converterType) {
  1204. case MarkdownConverterType::Marked:
  1205. markedAct->setChecked(true);
  1206. break;
  1207. case MarkdownConverterType::Hoedown:
  1208. hoedownAct->setChecked(true);
  1209. break;
  1210. case MarkdownConverterType::MarkdownIt:
  1211. markdownitAct->setChecked(true);
  1212. break;
  1213. case MarkdownConverterType::Showdown:
  1214. showdownAct->setChecked(true);
  1215. break;
  1216. default:
  1217. Q_ASSERT(false);
  1218. }
  1219. }
  1220. void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu)
  1221. {
  1222. QMenu *optMenu = p_menu->addMenu(tr("Markdown-it Options"));
  1223. optMenu->setToolTipsVisible(true);
  1224. MarkdownitOption opt = g_config->getMarkdownitOption();
  1225. QAction *htmlAct = new QAction(tr("HTML"), this);
  1226. htmlAct->setToolTip(tr("Enable HTML tags in source"));
  1227. htmlAct->setCheckable(true);
  1228. htmlAct->setChecked(opt.m_html);
  1229. connect(htmlAct, &QAction::triggered,
  1230. this, [this](bool p_checked) {
  1231. MarkdownitOption opt = g_config->getMarkdownitOption();
  1232. opt.m_html = p_checked;
  1233. g_config->setMarkdownitOption(opt);
  1234. });
  1235. QAction *breaksAct = new QAction(tr("Line Break"), this);
  1236. breaksAct->setToolTip(tr("Convert '\\n' in paragraphs into line break"));
  1237. breaksAct->setCheckable(true);
  1238. breaksAct->setChecked(opt.m_breaks);
  1239. connect(breaksAct, &QAction::triggered,
  1240. this, [this](bool p_checked) {
  1241. MarkdownitOption opt = g_config->getMarkdownitOption();
  1242. opt.m_breaks = p_checked;
  1243. g_config->setMarkdownitOption(opt);
  1244. });
  1245. QAction *linkifyAct = new QAction(tr("Linkify"), this);
  1246. linkifyAct->setToolTip(tr("Convert URL-like text into links"));
  1247. linkifyAct->setCheckable(true);
  1248. linkifyAct->setChecked(opt.m_linkify);
  1249. connect(linkifyAct, &QAction::triggered,
  1250. this, [this](bool p_checked) {
  1251. MarkdownitOption opt = g_config->getMarkdownitOption();
  1252. opt.m_linkify = p_checked;
  1253. g_config->setMarkdownitOption(opt);
  1254. });
  1255. optMenu->addAction(htmlAct);
  1256. optMenu->addAction(breaksAct);
  1257. optMenu->addAction(linkifyAct);
  1258. }
  1259. void VMainWindow::initRenderBackgroundMenu(QMenu *menu)
  1260. {
  1261. QActionGroup *renderBackgroundAct = new QActionGroup(this);
  1262. connect(renderBackgroundAct, &QActionGroup::triggered,
  1263. this, &VMainWindow::setRenderBackgroundColor);
  1264. QMenu *renderBgMenu = menu->addMenu(tr("&Rendering Background"));
  1265. renderBgMenu->setToolTipsVisible(true);
  1266. const QString &curBgColor = g_config->getCurRenderBackgroundColor();
  1267. QAction *tmpAct = new QAction(tr("System"), renderBackgroundAct);
  1268. tmpAct->setToolTip(tr("Use system's background color configuration for Markdown rendering"));
  1269. tmpAct->setCheckable(true);
  1270. tmpAct->setData("System");
  1271. if (curBgColor == "System") {
  1272. tmpAct->setChecked(true);
  1273. }
  1274. renderBgMenu->addAction(tmpAct);
  1275. const QVector<VColor> &bgColors = g_config->getCustomColors();
  1276. for (int i = 0; i < bgColors.size(); ++i) {
  1277. tmpAct = new QAction(bgColors[i].m_name, renderBackgroundAct);
  1278. tmpAct->setToolTip(tr("Set as the background color for Markdown rendering"));
  1279. tmpAct->setCheckable(true);
  1280. tmpAct->setData(bgColors[i].m_name);
  1281. #if !defined(Q_OS_MACOS) && !defined(Q_OS_MAC)
  1282. QColor color(bgColors[i].m_color);
  1283. QPixmap pixmap(COLOR_PIXMAP_ICON_SIZE, COLOR_PIXMAP_ICON_SIZE);
  1284. pixmap.fill(color);
  1285. tmpAct->setIcon(QIcon(pixmap));
  1286. #endif
  1287. if (curBgColor == bgColors[i].m_name) {
  1288. tmpAct->setChecked(true);
  1289. }
  1290. renderBgMenu->addAction(tmpAct);
  1291. }
  1292. }
  1293. void VMainWindow::initRenderStyleMenu(QMenu *p_menu)
  1294. {
  1295. QMenu *styleMenu = p_menu->addMenu(tr("Rendering &Style"));
  1296. styleMenu->setToolTipsVisible(true);
  1297. QAction *addAct = newAction(VIconUtils::menuIcon(":/resources/icons/add_style.svg"),
  1298. tr("Add Style"),
  1299. styleMenu);
  1300. addAct->setToolTip(tr("Add custom style of read mode"));
  1301. connect(addAct, &QAction::triggered,
  1302. this, [this]() {
  1303. VTipsDialog dialog(VUtils::getDocFile("tips_add_style.md"),
  1304. tr("Add Style"),
  1305. []() {
  1306. QUrl url = QUrl::fromLocalFile(g_config->getStyleConfigFolder());
  1307. QDesktopServices::openUrl(url);
  1308. },
  1309. this);
  1310. dialog.exec();
  1311. });
  1312. styleMenu->addAction(addAct);
  1313. QActionGroup *ag = new QActionGroup(this);
  1314. connect(ag, &QActionGroup::triggered,
  1315. this, [this](QAction *p_action) {
  1316. if (!p_action) {
  1317. return;
  1318. }
  1319. QString data = p_action->data().toString();
  1320. g_config->setCssStyle(data);
  1321. vnote->updateTemplate();
  1322. });
  1323. QList<QString> styles = g_config->getCssStyles();
  1324. QString curStyle = g_config->getCssStyle();
  1325. for (auto const &style : styles) {
  1326. QAction *act = new QAction(style, ag);
  1327. act->setToolTip(tr("Set as the CSS style for Markdown rendering "
  1328. "(re-open current tabs to make it work)"));
  1329. act->setCheckable(true);
  1330. act->setData(style);
  1331. // Add it to the menu.
  1332. styleMenu->addAction(act);
  1333. if (curStyle == style) {
  1334. act->setChecked(true);
  1335. }
  1336. }
  1337. }
  1338. void VMainWindow::initCodeBlockStyleMenu(QMenu *p_menu)
  1339. {
  1340. QMenu *styleMenu = p_menu->addMenu(tr("Code Block Style"));
  1341. styleMenu->setToolTipsVisible(true);
  1342. QAction *addAct = newAction(VIconUtils::menuIcon(":/resources/icons/add_style.svg"),
  1343. tr("Add Style"),
  1344. styleMenu);
  1345. addAct->setToolTip(tr("Add custom style of code block in read mode"));
  1346. connect(addAct, &QAction::triggered,
  1347. this, [this]() {
  1348. VTipsDialog dialog(VUtils::getDocFile("tips_add_style.md"),
  1349. tr("Add Style"),
  1350. []() {
  1351. QUrl url = QUrl::fromLocalFile(g_config->getCodeBlockStyleConfigFolder());
  1352. QDesktopServices::openUrl(url);
  1353. },
  1354. this);
  1355. dialog.exec();
  1356. });
  1357. styleMenu->addAction(addAct);
  1358. QActionGroup *ag = new QActionGroup(this);
  1359. connect(ag, &QActionGroup::triggered,
  1360. this, [this](QAction *p_action) {
  1361. if (!p_action) {
  1362. return;
  1363. }
  1364. QString data = p_action->data().toString();
  1365. g_config->setCodeBlockCssStyle(data);
  1366. vnote->updateTemplate();
  1367. });
  1368. QList<QString> styles = g_config->getCodeBlockCssStyles();
  1369. QString curStyle = g_config->getCodeBlockCssStyle();
  1370. for (auto const &style : styles) {
  1371. QAction *act = new QAction(style, ag);
  1372. act->setToolTip(tr("Set as the code block CSS style for Markdown rendering "
  1373. "(re-open current tabs to make it work)"));
  1374. act->setCheckable(true);
  1375. act->setData(style);
  1376. // Add it to the menu.
  1377. styleMenu->addAction(act);
  1378. if (curStyle == style) {
  1379. act->setChecked(true);
  1380. }
  1381. }
  1382. }
  1383. void VMainWindow::initEditorBackgroundMenu(QMenu *menu)
  1384. {
  1385. QMenu *backgroundColorMenu = menu->addMenu(tr("&Background Color"));
  1386. backgroundColorMenu->setToolTipsVisible(true);
  1387. QActionGroup *backgroundColorAct = new QActionGroup(this);
  1388. connect(backgroundColorAct, &QActionGroup::triggered,
  1389. this, &VMainWindow::setEditorBackgroundColor);
  1390. // System background color
  1391. const QString &curBgColor = g_config->getCurBackgroundColor();
  1392. QAction *tmpAct = new QAction(tr("System"), backgroundColorAct);
  1393. tmpAct->setToolTip(tr("Use system's background color configuration for editor"));
  1394. tmpAct->setCheckable(true);
  1395. tmpAct->setData("System");
  1396. if (curBgColor == "System") {
  1397. tmpAct->setChecked(true);
  1398. }
  1399. backgroundColorMenu->addAction(tmpAct);
  1400. const QVector<VColor> &bgColors = g_config->getCustomColors();
  1401. for (int i = 0; i < bgColors.size(); ++i) {
  1402. tmpAct = new QAction(bgColors[i].m_name, backgroundColorAct);
  1403. tmpAct->setToolTip(tr("Set as the background color for editor"));
  1404. tmpAct->setCheckable(true);
  1405. tmpAct->setData(bgColors[i].m_name);
  1406. #if !defined(Q_OS_MACOS) && !defined(Q_OS_MAC)
  1407. QColor color(bgColors[i].m_color);
  1408. QPixmap pixmap(COLOR_PIXMAP_ICON_SIZE, COLOR_PIXMAP_ICON_SIZE);
  1409. pixmap.fill(color);
  1410. tmpAct->setIcon(QIcon(pixmap));
  1411. #endif
  1412. if (curBgColor == bgColors[i].m_name) {
  1413. tmpAct->setChecked(true);
  1414. }
  1415. backgroundColorMenu->addAction(tmpAct);
  1416. }
  1417. }
  1418. void VMainWindow::initEditorLineNumberMenu(QMenu *p_menu)
  1419. {
  1420. QMenu *lineNumMenu = p_menu->addMenu(tr("Line Number"));
  1421. lineNumMenu->setToolTipsVisible(true);
  1422. QActionGroup *lineNumAct = new QActionGroup(lineNumMenu);
  1423. connect(lineNumAct, &QActionGroup::triggered,
  1424. this, [this](QAction *p_action){
  1425. if (!p_action) {
  1426. return;
  1427. }
  1428. g_config->setEditorLineNumber(p_action->data().toInt());
  1429. emit editorConfigUpdated();
  1430. });
  1431. int lineNumberMode = g_config->getEditorLineNumber();
  1432. QAction *act = lineNumAct->addAction(tr("None"));
  1433. act->setToolTip(tr("Do not display line number in edit mode"));
  1434. act->setCheckable(true);
  1435. act->setData(0);
  1436. lineNumMenu->addAction(act);
  1437. if (lineNumberMode == 0) {
  1438. act->setChecked(true);
  1439. }
  1440. act = lineNumAct->addAction(tr("Absolute"));
  1441. act->setToolTip(tr("Display absolute line number in edit mode"));
  1442. act->setCheckable(true);
  1443. act->setData(1);
  1444. lineNumMenu->addAction(act);
  1445. if (lineNumberMode == 1) {
  1446. act->setChecked(true);
  1447. }
  1448. act = lineNumAct->addAction(tr("Relative"));
  1449. act->setToolTip(tr("Display line number relative to current cursor line in edit mode"));
  1450. act->setCheckable(true);
  1451. act->setData(2);
  1452. lineNumMenu->addAction(act);
  1453. if (lineNumberMode == 2) {
  1454. act->setChecked(true);
  1455. }
  1456. act = lineNumAct->addAction(tr("CodeBlock"));
  1457. act->setToolTip(tr("Display line number in code block in edit mode (for Markdown only)"));
  1458. act->setCheckable(true);
  1459. act->setData(3);
  1460. lineNumMenu->addAction(act);
  1461. if (lineNumberMode == 3) {
  1462. act->setChecked(true);
  1463. }
  1464. }
  1465. void VMainWindow::initEditorStyleMenu(QMenu *p_menu)
  1466. {
  1467. QMenu *styleMenu = p_menu->addMenu(tr("Editor &Style"));
  1468. styleMenu->setToolTipsVisible(true);
  1469. QAction *addAct = newAction(VIconUtils::menuIcon(":/resources/icons/add_style.svg"),
  1470. tr("Add Style"),
  1471. styleMenu);
  1472. addAct->setToolTip(tr("Add custom style of editor"));
  1473. connect(addAct, &QAction::triggered,
  1474. this, [this]() {
  1475. VTipsDialog dialog(VUtils::getDocFile("tips_add_style.md"),
  1476. tr("Add Style"),
  1477. []() {
  1478. QUrl url = QUrl::fromLocalFile(g_config->getStyleConfigFolder());
  1479. QDesktopServices::openUrl(url);
  1480. },
  1481. this);
  1482. dialog.exec();
  1483. });
  1484. styleMenu->addAction(addAct);
  1485. QActionGroup *ag = new QActionGroup(this);
  1486. connect(ag, &QActionGroup::triggered,
  1487. this, [this](QAction *p_action) {
  1488. if (!p_action) {
  1489. return;
  1490. }
  1491. QString data = p_action->data().toString();
  1492. g_config->setEditorStyle(data);
  1493. });
  1494. QList<QString> styles = g_config->getEditorStyles();
  1495. QString style = g_config->getEditorStyle();
  1496. for (auto const &item : styles) {
  1497. QAction *act = new QAction(item, ag);
  1498. act->setToolTip(tr("Set as the editor style (re-open current tabs to make it work)"));
  1499. act->setCheckable(true);
  1500. act->setData(item);
  1501. // Add it to the menu.
  1502. styleMenu->addAction(act);
  1503. if (style == item) {
  1504. act->setChecked(true);
  1505. }
  1506. }
  1507. }
  1508. void VMainWindow::setRenderBackgroundColor(QAction *action)
  1509. {
  1510. if (!action) {
  1511. return;
  1512. }
  1513. g_config->setCurRenderBackgroundColor(action->data().toString());
  1514. vnote->updateTemplate();
  1515. }
  1516. void VMainWindow::updateActionsStateFromTab(const VEditTab *p_tab)
  1517. {
  1518. const VFile *file = p_tab ? p_tab->getFile() : NULL;
  1519. bool editMode = p_tab ? p_tab->isEditMode() : false;
  1520. bool systemFile = file
  1521. && file->getType() == FileType::Orphan
  1522. && dynamic_cast<const VOrphanFile *>(file)->isSystemFile();
  1523. m_printAct->setEnabled(file && file->getDocType() == DocType::Markdown);
  1524. updateEditReadAct(p_tab);
  1525. saveNoteAct->setEnabled(file && editMode && file->isModifiable());
  1526. deleteNoteAct->setEnabled(file && file->getType() == FileType::Note);
  1527. noteInfoAct->setEnabled(file && !systemFile);
  1528. m_attachmentBtn->setEnabled(file && file->getType() == FileType::Note);
  1529. m_headingBtn->setEnabled(file && editMode);
  1530. setActionsEnabled(m_editToolBar, file && editMode);
  1531. // Handle heading sequence act independently.
  1532. m_headingSequenceAct->setEnabled(isHeadingSequenceApplicable());
  1533. const VMdTab *mdTab = dynamic_cast<const VMdTab *>(p_tab);
  1534. m_headingSequenceAct->setChecked(mdTab && mdTab->isHeadingSequenceEnabled());
  1535. // Find/Replace
  1536. m_findReplaceAct->setEnabled(file);
  1537. m_findNextAct->setEnabled(file);
  1538. m_findPreviousAct->setEnabled(file);
  1539. m_replaceAct->setEnabled(file && editMode);
  1540. m_replaceFindAct->setEnabled(file && editMode);
  1541. m_replaceAllAct->setEnabled(file && editMode);
  1542. if (!file) {
  1543. m_findReplaceDialog->closeDialog();
  1544. }
  1545. }
  1546. void VMainWindow::handleAreaTabStatusUpdated(const VEditTabInfo &p_info)
  1547. {
  1548. if (m_curTab != p_info.m_editTab) {
  1549. if (m_curTab) {
  1550. if (m_vimCmd->isVisible()) {
  1551. m_curTab->handleVimCmdCommandCancelled();
  1552. }
  1553. // Disconnect the trigger signal from edit tab.
  1554. disconnect((VEditTab *)m_curTab, 0, m_vimCmd, 0);
  1555. }
  1556. m_curTab = p_info.m_editTab;
  1557. if (m_curTab) {
  1558. connect((VEditTab *)m_curTab, &VEditTab::triggerVimCmd,
  1559. m_vimCmd, &VVimCmdLineEdit::reset);
  1560. }
  1561. m_vimCmd->hide();
  1562. }
  1563. if (m_curTab) {
  1564. m_curFile = m_curTab->getFile();
  1565. } else {
  1566. m_curFile = NULL;
  1567. }
  1568. if (p_info.m_type == VEditTabInfo::InfoType::All) {
  1569. updateActionsStateFromTab(m_curTab);
  1570. m_attachmentList->setFile(dynamic_cast<VNoteFile *>(m_curFile.data()));
  1571. QString title;
  1572. if (m_curFile) {
  1573. m_findReplaceDialog->updateState(m_curFile->getDocType(),
  1574. m_curTab->isEditMode());
  1575. if (m_curFile->getType() == FileType::Note) {
  1576. const VNoteFile *tmpFile = dynamic_cast<const VNoteFile *>((VFile *)m_curFile);
  1577. title = QString("[%1] %2").arg(tmpFile->getNotebookName()).arg(tmpFile->fetchPath());
  1578. } else {
  1579. title = QString("%1").arg(m_curFile->fetchPath());
  1580. }
  1581. if (!m_curFile->isModifiable()) {
  1582. title.append('#');
  1583. }
  1584. if (m_curTab->isModified()) {
  1585. title.append('*');
  1586. }
  1587. }
  1588. updateWindowTitle(title);
  1589. }
  1590. updateStatusInfo(p_info);
  1591. }
  1592. void VMainWindow::onePanelView()
  1593. {
  1594. m_panelViewState = PanelViewState::SinglePanel;
  1595. g_config->setEnableCompactMode(false);
  1596. changePanelView(m_panelViewState);
  1597. }
  1598. void VMainWindow::twoPanelView()
  1599. {
  1600. m_panelViewState = PanelViewState::TwoPanels;
  1601. g_config->setEnableCompactMode(false);
  1602. changePanelView(m_panelViewState);
  1603. }
  1604. void VMainWindow::compactModeView()
  1605. {
  1606. m_panelViewState = PanelViewState::CompactMode;
  1607. g_config->setEnableCompactMode(true);
  1608. changePanelView(m_panelViewState);
  1609. }
  1610. void VMainWindow::enableCompactMode(bool p_enabled)
  1611. {
  1612. const int fileListIdx = 1;
  1613. bool isCompactMode = m_naviSplitter->indexOf(m_fileList) != -1;
  1614. if (p_enabled) {
  1615. // Change to compact mode.
  1616. if (isCompactMode) {
  1617. return;
  1618. }
  1619. // Take m_fileList out of m_mainSplitter.
  1620. QWidget *tmpWidget = new QWidget(this);
  1621. Q_ASSERT(fileListIdx == m_mainSplitter->indexOf(m_fileList));
  1622. m_fileList->hide();
  1623. m_mainSplitter->replaceWidget(fileListIdx, tmpWidget);
  1624. tmpWidget->hide();
  1625. // Insert m_fileList into m_naviSplitter.
  1626. QWidget *wid = m_naviSplitter->replaceWidget(fileListIdx, m_fileList);
  1627. delete wid;
  1628. m_fileList->show();
  1629. } else {
  1630. // Disable compact mode and go back to two panels view.
  1631. if (!isCompactMode) {
  1632. return;
  1633. }
  1634. // Take m_fileList out of m_naviSplitter.
  1635. Q_ASSERT(fileListIdx == m_naviSplitter->indexOf(m_fileList));
  1636. QWidget *tmpWidget = new QWidget(this);
  1637. m_fileList->hide();
  1638. m_naviSplitter->replaceWidget(fileListIdx, tmpWidget);
  1639. tmpWidget->hide();
  1640. // Insert m_fileList into m_mainSplitter.
  1641. QWidget *wid = m_mainSplitter->replaceWidget(fileListIdx, m_fileList);
  1642. delete wid;
  1643. m_fileList->show();
  1644. }
  1645. // Set Tab order.
  1646. setTabOrder(directoryTree, m_fileList->getContentWidget());
  1647. }
  1648. void VMainWindow::changePanelView(PanelViewState p_state)
  1649. {
  1650. switch (p_state) {
  1651. case PanelViewState::ExpandMode:
  1652. m_mainSplitter->widget(0)->hide();
  1653. m_mainSplitter->widget(1)->hide();
  1654. m_mainSplitter->widget(2)->show();
  1655. break;
  1656. case PanelViewState::SinglePanel:
  1657. enableCompactMode(false);
  1658. m_mainSplitter->widget(0)->hide();
  1659. m_mainSplitter->widget(1)->show();
  1660. m_mainSplitter->widget(2)->show();
  1661. break;
  1662. case PanelViewState::TwoPanels:
  1663. enableCompactMode(false);
  1664. m_mainSplitter->widget(0)->show();
  1665. m_mainSplitter->widget(1)->show();
  1666. m_mainSplitter->widget(2)->show();
  1667. break;
  1668. case PanelViewState::CompactMode:
  1669. m_mainSplitter->widget(0)->show();
  1670. m_mainSplitter->widget(1)->hide();
  1671. m_mainSplitter->widget(2)->show();
  1672. enableCompactMode(true);
  1673. break;
  1674. default:
  1675. break;
  1676. }
  1677. // Change the action state.
  1678. QList<QAction *> acts = m_viewActGroup->actions();
  1679. for (auto & act : acts) {
  1680. if (act->data().toInt() == (int)p_state) {
  1681. act->setChecked(true);
  1682. } else {
  1683. act->setChecked(false);
  1684. }
  1685. }
  1686. if (p_state != PanelViewState::ExpandMode) {
  1687. expandViewAct->setChecked(false);
  1688. }
  1689. }
  1690. void VMainWindow::updateWindowTitle(const QString &str)
  1691. {
  1692. QString title = "VNote";
  1693. if (!str.isEmpty()) {
  1694. title = title + " - " + str;
  1695. }
  1696. setWindowTitle(title);
  1697. }
  1698. void VMainWindow::curEditFileInfo()
  1699. {
  1700. Q_ASSERT(m_curFile);
  1701. if (m_curFile->getType() == FileType::Note) {
  1702. VNoteFile *file = dynamic_cast<VNoteFile *>((VFile *)m_curFile);
  1703. Q_ASSERT(file);
  1704. m_fileList->fileInfo(file);
  1705. } else if (m_curFile->getType() == FileType::Orphan) {
  1706. VOrphanFile *file = dynamic_cast<VOrphanFile *>((VFile *)m_curFile);
  1707. Q_ASSERT(file);
  1708. if (!file->isSystemFile()) {
  1709. editOrphanFileInfo(m_curFile);
  1710. }
  1711. }
  1712. }
  1713. void VMainWindow::deleteCurNote()
  1714. {
  1715. if (!m_curFile || m_curFile->getType() != FileType::Note) {
  1716. return;
  1717. }
  1718. VNoteFile *file = dynamic_cast<VNoteFile *>((VFile *)m_curFile);
  1719. m_fileList->deleteFile(file);
  1720. }
  1721. void VMainWindow::closeEvent(QCloseEvent *event)
  1722. {
  1723. bool isExit = m_requestQuit || !g_config->getMinimizeToStystemTray();
  1724. m_requestQuit = false;
  1725. #if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
  1726. // Do not support minimized to tray on macOS.
  1727. isExit = true;
  1728. #endif
  1729. if (!isExit && g_config->getMinimizeToStystemTray() == -1) {
  1730. // Not initialized yet. Prompt for user.
  1731. int ret = VUtils::showMessage(QMessageBox::Information,
  1732. tr("Close VNote"),
  1733. tr("Do you want to minimize VNote to system tray "
  1734. "instead of quitting it when closing VNote?"),
  1735. tr("You could change the option in Settings later."),
  1736. QMessageBox::Ok | QMessageBox::No | QMessageBox::Cancel,
  1737. QMessageBox::Ok,
  1738. this);
  1739. if (ret == QMessageBox::Ok) {
  1740. g_config->setMinimizeToSystemTray(1);
  1741. } else if (ret == QMessageBox::No) {
  1742. g_config->setMinimizeToSystemTray(0);
  1743. isExit = true;
  1744. } else {
  1745. event->ignore();
  1746. return;
  1747. }
  1748. }
  1749. if (isVisible()) {
  1750. saveStateAndGeometry();
  1751. }
  1752. if (isExit || !m_trayIcon->isVisible()) {
  1753. // Get all the opened tabs.
  1754. bool saveOpenedNotes = g_config->getStartupPageType() == StartupPageType::ContinueLeftOff;
  1755. QVector<VFileSessionInfo> fileInfos;
  1756. QVector<VEditTabInfo> tabs;
  1757. if (saveOpenedNotes) {
  1758. tabs = editArea->getAllTabsInfo();
  1759. fileInfos.reserve(tabs.size());
  1760. for (auto const & tab : tabs) {
  1761. // Skip system file.
  1762. VFile *file = tab.m_editTab->getFile();
  1763. if (file->getType() == FileType::Orphan
  1764. && dynamic_cast<VOrphanFile *>(file)->isSystemFile()) {
  1765. continue;
  1766. }
  1767. VFileSessionInfo info = VFileSessionInfo::fromEditTabInfo(&tab);
  1768. fileInfos.push_back(info);
  1769. qDebug() << "file session:" << info.m_file << (info.m_mode == OpenFileMode::Edit);
  1770. }
  1771. }
  1772. if (!editArea->closeAllFiles(false)) {
  1773. // Fail to close all the opened files, cancel closing app.
  1774. event->ignore();
  1775. return;
  1776. }
  1777. if (saveOpenedNotes) {
  1778. g_config->setLastOpenedFiles(fileInfos);
  1779. }
  1780. QMainWindow::closeEvent(event);
  1781. } else {
  1782. hide();
  1783. event->ignore();
  1784. }
  1785. }
  1786. void VMainWindow::saveStateAndGeometry()
  1787. {
  1788. g_config->setMainWindowGeometry(saveGeometry());
  1789. g_config->setMainWindowState(saveState());
  1790. g_config->setToolsDockChecked(toolDock->isVisible());
  1791. if (m_panelViewState == PanelViewState::CompactMode) {
  1792. g_config->setNaviSplitterState(m_naviSplitter->saveState());
  1793. g_config->setMainSplitterState(m_mainSplitter->saveState());
  1794. } else {
  1795. // In one panel view, it will save the wrong state that the directory tree
  1796. // panel has a width of zero.
  1797. changePanelView(PanelViewState::TwoPanels);
  1798. g_config->setMainSplitterState(m_mainSplitter->saveState());
  1799. }
  1800. }
  1801. void VMainWindow::restoreStateAndGeometry()
  1802. {
  1803. const QByteArray &geometry = g_config->getMainWindowGeometry();
  1804. if (!geometry.isEmpty()) {
  1805. restoreGeometry(geometry);
  1806. }
  1807. const QByteArray &state = g_config->getMainWindowState();
  1808. if (!state.isEmpty()) {
  1809. restoreState(state);
  1810. }
  1811. toolDock->setVisible(g_config->getToolsDockChecked());
  1812. const QByteArray &splitterState = g_config->getMainSplitterState();
  1813. if (!splitterState.isEmpty()) {
  1814. m_mainSplitter->restoreState(splitterState);
  1815. }
  1816. const QByteArray &naviSplitterState = g_config->getNaviSplitterState();
  1817. if (!naviSplitterState.isEmpty()) {
  1818. m_naviSplitter->restoreState(naviSplitterState);
  1819. }
  1820. }
  1821. void VMainWindow::handleCurrentDirectoryChanged(const VDirectory *p_dir)
  1822. {
  1823. newNoteAct->setEnabled(p_dir);
  1824. m_importNoteAct->setEnabled(p_dir);
  1825. }
  1826. void VMainWindow::handleCurrentNotebookChanged(const VNotebook *p_notebook)
  1827. {
  1828. newRootDirAct->setEnabled(p_notebook);
  1829. }
  1830. void VMainWindow::keyPressEvent(QKeyEvent *event)
  1831. {
  1832. int key = event->key();
  1833. Qt::KeyboardModifiers modifiers = event->modifiers();
  1834. if (key == Qt::Key_Escape
  1835. || (key == Qt::Key_BracketLeft
  1836. && modifiers == Qt::ControlModifier)) {
  1837. m_findReplaceDialog->closeDialog();
  1838. event->accept();
  1839. return;
  1840. }
  1841. QMainWindow::keyPressEvent(event);
  1842. }
  1843. bool VMainWindow::locateFile(VFile *p_file)
  1844. {
  1845. bool ret = false;
  1846. if (!p_file || p_file->getType() != FileType::Note) {
  1847. return ret;
  1848. }
  1849. VNoteFile *file = dynamic_cast<VNoteFile *>(p_file);
  1850. VNotebook *notebook = file->getNotebook();
  1851. if (notebookSelector->locateNotebook(notebook)) {
  1852. while (directoryTree->currentNotebook() != notebook) {
  1853. QCoreApplication::sendPostedEvents();
  1854. }
  1855. VDirectory *dir = file->getDirectory();
  1856. if (directoryTree->locateDirectory(dir)) {
  1857. while (m_fileList->currentDirectory() != dir) {
  1858. QCoreApplication::sendPostedEvents();
  1859. }
  1860. if (m_fileList->locateFile(file)) {
  1861. ret = true;
  1862. m_fileList->setFocus();
  1863. }
  1864. }
  1865. }
  1866. // Open the directory and file panels after location.
  1867. if (m_panelViewState == PanelViewState::CompactMode) {
  1868. compactModeView();
  1869. } else {
  1870. twoPanelView();
  1871. }
  1872. return ret;
  1873. }
  1874. void VMainWindow::handleFindDialogTextChanged(const QString &p_text, uint /* p_options */)
  1875. {
  1876. bool enabled = true;
  1877. if (p_text.isEmpty()) {
  1878. enabled = false;
  1879. }
  1880. m_findNextAct->setEnabled(enabled);
  1881. m_findPreviousAct->setEnabled(enabled);
  1882. m_replaceAct->setEnabled(enabled);
  1883. m_replaceFindAct->setEnabled(enabled);
  1884. m_replaceAllAct->setEnabled(enabled);
  1885. }
  1886. void VMainWindow::openFindDialog()
  1887. {
  1888. m_findReplaceDialog->openDialog(editArea->getSelectedText());
  1889. }
  1890. void VMainWindow::viewSettings()
  1891. {
  1892. VSettingsDialog settingsDialog(this);
  1893. settingsDialog.exec();
  1894. }
  1895. void VMainWindow::closeCurrentFile()
  1896. {
  1897. if (m_curFile) {
  1898. editArea->closeFile(m_curFile, false);
  1899. }
  1900. }
  1901. void VMainWindow::changeAutoIndent(bool p_checked)
  1902. {
  1903. g_config->setAutoIndent(p_checked);
  1904. }
  1905. void VMainWindow::changeAutoList(bool p_checked)
  1906. {
  1907. g_config->setAutoList(p_checked);
  1908. if (p_checked) {
  1909. if (!m_autoIndentAct->isChecked()) {
  1910. m_autoIndentAct->trigger();
  1911. }
  1912. m_autoIndentAct->setEnabled(false);
  1913. } else {
  1914. m_autoIndentAct->setEnabled(true);
  1915. }
  1916. }
  1917. void VMainWindow::changeVimMode(bool p_checked)
  1918. {
  1919. g_config->setEnableVimMode(p_checked);
  1920. }
  1921. void VMainWindow::enableCodeBlockHighlight(bool p_checked)
  1922. {
  1923. g_config->setEnableCodeBlockHighlight(p_checked);
  1924. }
  1925. void VMainWindow::enableImagePreview(bool p_checked)
  1926. {
  1927. g_config->setEnablePreviewImages(p_checked);
  1928. emit editorConfigUpdated();
  1929. }
  1930. void VMainWindow::enableImagePreviewConstraint(bool p_checked)
  1931. {
  1932. g_config->setEnablePreviewImageConstraint(p_checked);
  1933. emit editorConfigUpdated();
  1934. }
  1935. void VMainWindow::enableImageConstraint(bool p_checked)
  1936. {
  1937. g_config->setEnableImageConstraint(p_checked);
  1938. vnote->updateTemplate();
  1939. }
  1940. void VMainWindow::enableImageCaption(bool p_checked)
  1941. {
  1942. g_config->setEnableImageCaption(p_checked);
  1943. }
  1944. void VMainWindow::shortcutsHelp()
  1945. {
  1946. QString docFile = VUtils::getDocFile(VNote::c_shortcutsDocFile);
  1947. VFile *file = vnote->getOrphanFile(docFile, false, true);
  1948. editArea->openFile(file, OpenFileMode::Read);
  1949. }
  1950. void VMainWindow::printNote()
  1951. {
  1952. if (m_printer
  1953. || !m_curFile
  1954. || m_curFile->getDocType() != DocType::Markdown) {
  1955. return;
  1956. }
  1957. m_printer = new QPrinter();
  1958. QPrintDialog dialog(m_printer, this);
  1959. dialog.setWindowTitle(tr("Print Note"));
  1960. V_ASSERT(m_curTab);
  1961. VMdTab *mdTab = dynamic_cast<VMdTab *>((VEditTab *)m_curTab);
  1962. VWebView *webView = mdTab->getWebViewer();
  1963. V_ASSERT(webView);
  1964. if (webView->hasSelection()) {
  1965. dialog.addEnabledOption(QAbstractPrintDialog::PrintSelection);
  1966. }
  1967. if (dialog.exec() == QDialog::Accepted) {
  1968. webView->page()->print(m_printer, [this](bool p_succ) {
  1969. qDebug() << "print web page callback" << p_succ;
  1970. delete m_printer;
  1971. m_printer = NULL;
  1972. });
  1973. } else {
  1974. delete m_printer;
  1975. m_printer = NULL;
  1976. }
  1977. }
  1978. QAction *VMainWindow::newAction(const QIcon &p_icon,
  1979. const QString &p_text,
  1980. QObject *p_parent)
  1981. {
  1982. #if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
  1983. Q_UNUSED(p_icon);
  1984. return new QAction(p_text, p_parent);
  1985. #else
  1986. return new QAction(p_icon, p_text, p_parent);
  1987. #endif
  1988. }
  1989. void VMainWindow::showStatusMessage(const QString &p_msg)
  1990. {
  1991. const int timeout = 3000;
  1992. statusBar()->showMessage(p_msg, timeout);
  1993. }
  1994. void VMainWindow::updateStatusInfo(const VEditTabInfo &p_info)
  1995. {
  1996. if (m_curTab) {
  1997. m_tabIndicator->update(p_info);
  1998. m_tabIndicator->show();
  1999. if (m_curTab->isEditMode()) {
  2000. if (p_info.m_type == VEditTabInfo::InfoType::All) {
  2001. m_curTab->requestUpdateVimStatus();
  2002. }
  2003. } else {
  2004. m_vimIndicator->hide();
  2005. }
  2006. } else {
  2007. m_tabIndicator->hide();
  2008. m_vimIndicator->hide();
  2009. }
  2010. }
  2011. void VMainWindow::handleVimStatusUpdated(const VVim *p_vim)
  2012. {
  2013. m_vimIndicator->update(p_vim);
  2014. if (!p_vim || !m_curTab || !m_curTab->isEditMode()) {
  2015. m_vimIndicator->hide();
  2016. } else {
  2017. m_vimIndicator->show();
  2018. }
  2019. }
  2020. bool VMainWindow::tryOpenInternalFile(const QString &p_filePath)
  2021. {
  2022. if (p_filePath.isEmpty()) {
  2023. return false;
  2024. }
  2025. if (QFileInfo::exists(p_filePath)) {
  2026. VFile *file = vnote->getInternalFile(p_filePath);
  2027. if (file) {
  2028. editArea->openFile(file, OpenFileMode::Read);
  2029. return true;
  2030. }
  2031. }
  2032. return false;
  2033. }
  2034. void VMainWindow::openFiles(const QStringList &p_files,
  2035. bool p_forceOrphan,
  2036. OpenFileMode p_mode,
  2037. bool p_forceMode)
  2038. {
  2039. for (int i = 0; i < p_files.size(); ++i) {
  2040. VFile *file = NULL;
  2041. if (!p_forceOrphan) {
  2042. file = vnote->getInternalFile(p_files[i]);
  2043. }
  2044. if (!file) {
  2045. file = vnote->getOrphanFile(p_files[i], true);
  2046. }
  2047. editArea->openFile(file, p_mode, p_forceMode);
  2048. }
  2049. }
  2050. void VMainWindow::editOrphanFileInfo(VFile *p_file)
  2051. {
  2052. VOrphanFile *file = dynamic_cast<VOrphanFile *>(p_file);
  2053. Q_ASSERT(file);
  2054. VOrphanFileInfoDialog dialog(file, this);
  2055. if (dialog.exec() == QDialog::Accepted) {
  2056. QString imgFolder = dialog.getImageFolder();
  2057. file->setImageFolder(imgFolder);
  2058. }
  2059. }
  2060. void VMainWindow::checkSharedMemory()
  2061. {
  2062. QStringList files = m_guard->fetchFilesToOpen();
  2063. if (!files.isEmpty()) {
  2064. qDebug() << "shared memory fetch files" << files;
  2065. openFiles(files);
  2066. // Eliminate the signal.
  2067. m_guard->fetchAskedToShow();
  2068. showMainWindow();
  2069. } else if (m_guard->fetchAskedToShow()) {
  2070. qDebug() << "shared memory asked to show up";
  2071. showMainWindow();
  2072. }
  2073. }
  2074. void VMainWindow::initTrayIcon()
  2075. {
  2076. QMenu *menu = new QMenu(this);
  2077. QAction *showMainWindowAct = menu->addAction(tr("Show VNote"));
  2078. connect(showMainWindowAct, &QAction::triggered,
  2079. this, &VMainWindow::showMainWindow);
  2080. QAction *exitAct = menu->addAction(tr("Quit"));
  2081. connect(exitAct, &QAction::triggered,
  2082. this, &VMainWindow::quitApp);
  2083. m_trayIcon = new QSystemTrayIcon(QIcon(":/resources/icons/32x32/vnote.png"), this);
  2084. m_trayIcon->setToolTip(tr("VNote"));
  2085. m_trayIcon->setContextMenu(menu);
  2086. connect(m_trayIcon, &QSystemTrayIcon::activated,
  2087. this, [this](QSystemTrayIcon::ActivationReason p_reason){
  2088. if (p_reason == QSystemTrayIcon::Trigger) {
  2089. this->showMainWindow();
  2090. }
  2091. });
  2092. m_trayIcon->show();
  2093. }
  2094. void VMainWindow::changeEvent(QEvent *p_event)
  2095. {
  2096. if (p_event->type() == QEvent::WindowStateChange) {
  2097. QWindowStateChangeEvent *eve = dynamic_cast<QWindowStateChangeEvent *>(p_event);
  2098. m_windowOldState = eve->oldState();
  2099. }
  2100. QMainWindow::changeEvent(p_event);
  2101. }
  2102. void VMainWindow::showMainWindow()
  2103. {
  2104. if (this->isMinimized()) {
  2105. if (m_windowOldState & Qt::WindowMaximized) {
  2106. this->showMaximized();
  2107. } else if (m_windowOldState & Qt::WindowFullScreen) {
  2108. this->showFullScreen();
  2109. } else {
  2110. this->showNormal();
  2111. }
  2112. } else {
  2113. this->show();
  2114. // Need to call raise() in macOS.
  2115. this->raise();
  2116. }
  2117. this->activateWindow();
  2118. }
  2119. void VMainWindow::openStartupPages()
  2120. {
  2121. StartupPageType type = g_config->getStartupPageType();
  2122. switch (type) {
  2123. case StartupPageType::ContinueLeftOff:
  2124. {
  2125. QVector<VFileSessionInfo> files = g_config->getLastOpenedFiles();
  2126. qDebug() << "open" << files.size() << "last opened files";
  2127. editArea->openFiles(files);
  2128. break;
  2129. }
  2130. case StartupPageType::SpecificPages:
  2131. {
  2132. QStringList pagesToOpen = VUtils::filterFilePathsToOpen(g_config->getStartupPages());
  2133. qDebug() << "open startup pages" << pagesToOpen;
  2134. openFiles(pagesToOpen);
  2135. break;
  2136. }
  2137. default:
  2138. break;
  2139. }
  2140. }
  2141. bool VMainWindow::isHeadingSequenceApplicable() const
  2142. {
  2143. if (!m_curTab) {
  2144. return false;
  2145. }
  2146. Q_ASSERT(m_curFile);
  2147. if (!m_curFile->isModifiable()
  2148. || m_curFile->getDocType() != DocType::Markdown) {
  2149. return false;
  2150. }
  2151. return true;
  2152. }
  2153. // Popup the attachment list if it is enabled.
  2154. bool VMainWindow::showAttachmentListByCaptain(void *p_target, void *p_data)
  2155. {
  2156. Q_UNUSED(p_data);
  2157. VMainWindow *obj = static_cast<VMainWindow *>(p_target);
  2158. if (obj->m_attachmentBtn->isEnabled()) {
  2159. obj->m_attachmentBtn->showPopupWidget();
  2160. }
  2161. return true;
  2162. }
  2163. bool VMainWindow::locateCurrentFileByCaptain(void *p_target, void *p_data)
  2164. {
  2165. Q_UNUSED(p_data);
  2166. VMainWindow *obj = static_cast<VMainWindow *>(p_target);
  2167. if (obj->m_curFile) {
  2168. if (obj->locateFile(obj->m_curFile)) {
  2169. return false;
  2170. }
  2171. }
  2172. return true;
  2173. }
  2174. bool VMainWindow::toggleExpandModeByCaptain(void *p_target, void *p_data)
  2175. {
  2176. Q_UNUSED(p_data);
  2177. VMainWindow *obj = static_cast<VMainWindow *>(p_target);
  2178. obj->expandViewAct->trigger();
  2179. return true;
  2180. }
  2181. bool VMainWindow::toggleOnePanelViewByCaptain(void *p_target, void *p_data)
  2182. {
  2183. Q_UNUSED(p_data);
  2184. VMainWindow *obj = static_cast<VMainWindow *>(p_target);
  2185. if (obj->m_panelViewState == PanelViewState::TwoPanels) {
  2186. obj->onePanelView();
  2187. } else {
  2188. obj->twoPanelView();
  2189. }
  2190. return true;
  2191. }
  2192. bool VMainWindow::discardAndReadByCaptain(void *p_target, void *p_data)
  2193. {
  2194. Q_UNUSED(p_data);
  2195. VMainWindow *obj = static_cast<VMainWindow *>(p_target);
  2196. if (obj->m_curTab) {
  2197. obj->m_discardExitAct->trigger();
  2198. obj->m_curTab->setFocus();
  2199. return false;
  2200. }
  2201. return true;
  2202. }
  2203. bool VMainWindow::toggleToolsDockByCaptain(void *p_target, void *p_data)
  2204. {
  2205. Q_UNUSED(p_data);
  2206. VMainWindow *obj = static_cast<VMainWindow *>(p_target);
  2207. obj->toolDock->setVisible(!obj->toolDock->isVisible());
  2208. return true;
  2209. }
  2210. bool VMainWindow::closeFileByCaptain(void *p_target, void *p_data)
  2211. {
  2212. Q_UNUSED(p_data);
  2213. VMainWindow *obj = static_cast<VMainWindow *>(p_target);
  2214. obj->closeCurrentFile();
  2215. QWidget *nextFocus = obj->editArea->getCurrentTab();
  2216. if (nextFocus) {
  2217. nextFocus->setFocus();
  2218. } else {
  2219. obj->m_fileList->setFocus();
  2220. }
  2221. return false;
  2222. }
  2223. bool VMainWindow::shortcutsHelpByCaptain(void *p_target, void *p_data)
  2224. {
  2225. Q_UNUSED(p_data);
  2226. VMainWindow *obj = static_cast<VMainWindow *>(p_target);
  2227. obj->shortcutsHelp();
  2228. return false;
  2229. }
  2230. bool VMainWindow::flushLogFileByCaptain(void *p_target, void *p_data)
  2231. {
  2232. Q_UNUSED(p_target);
  2233. Q_UNUSED(p_data);
  2234. #if defined(QT_NO_DEBUG)
  2235. // Flush g_logFile.
  2236. g_logFile.flush();
  2237. #endif
  2238. return true;
  2239. }
  2240. void VMainWindow::promptNewNotebookIfEmpty()
  2241. {
  2242. if (vnote->getNotebooks().isEmpty()) {
  2243. notebookSelector->newNotebook();
  2244. }
  2245. }
  2246. void VMainWindow::initShortcuts()
  2247. {
  2248. QString keySeq = g_config->getShortcutKeySequence("CloseNote");
  2249. qDebug() << "set CloseNote shortcut to" << keySeq;
  2250. if (!keySeq.isEmpty()) {
  2251. QShortcut *closeNoteShortcut = new QShortcut(QKeySequence(keySeq), this);
  2252. closeNoteShortcut->setContext(Qt::WidgetWithChildrenShortcut);
  2253. connect(closeNoteShortcut, &QShortcut::activated,
  2254. this, &VMainWindow::closeCurrentFile);
  2255. }
  2256. }
  2257. void VMainWindow::openFlashPage()
  2258. {
  2259. openFiles(QStringList() << g_config->getFlashPage(),
  2260. false,
  2261. OpenFileMode::Edit,
  2262. true);
  2263. }
  2264. void VMainWindow::initHeadingButton(QToolBar *p_tb)
  2265. {
  2266. m_headingBtn = new QPushButton(VIconUtils::toolButtonIcon(":/resources/icons/heading.svg"),
  2267. "",
  2268. this);
  2269. m_headingBtn->setToolTip(tr("Headings"));
  2270. m_headingBtn->setProperty("CornerBtn", true);
  2271. m_headingBtn->setFocusPolicy(Qt::NoFocus);
  2272. m_headingBtn->setEnabled(false);
  2273. QMenu *menu = new QMenu(this);
  2274. QString text(tr("Heading %1"));
  2275. QString tooltip(tr("Heading %1\t%2"));
  2276. QWidgetAction *wact = new QWidgetAction(menu);
  2277. wact->setData(1);
  2278. VButtonMenuItem *w = new VButtonMenuItem(wact, text.arg(1), this);
  2279. w->setToolTip(tooltip.arg(1).arg(VUtils::getShortcutText("Ctrl+1")));
  2280. w->setProperty("Heading1", true);
  2281. wact->setDefaultWidget(w);
  2282. menu->addAction(wact);
  2283. wact = new QWidgetAction(menu);
  2284. wact->setData(2);
  2285. w = new VButtonMenuItem(wact, text.arg(2), this);
  2286. w->setToolTip(tooltip.arg(2).arg(VUtils::getShortcutText("Ctrl+2")));
  2287. w->setProperty("Heading2", true);
  2288. wact->setDefaultWidget(w);
  2289. menu->addAction(wact);
  2290. wact = new QWidgetAction(menu);
  2291. wact->setData(3);
  2292. w = new VButtonMenuItem(wact, text.arg(3), this);
  2293. w->setToolTip(tooltip.arg(3).arg(VUtils::getShortcutText("Ctrl+3")));
  2294. w->setProperty("Heading3", true);
  2295. wact->setDefaultWidget(w);
  2296. menu->addAction(wact);
  2297. wact = new QWidgetAction(menu);
  2298. wact->setData(4);
  2299. w = new VButtonMenuItem(wact, text.arg(4), this);
  2300. w->setToolTip(tooltip.arg(4).arg(VUtils::getShortcutText("Ctrl+4")));
  2301. w->setProperty("Heading4", true);
  2302. wact->setDefaultWidget(w);
  2303. menu->addAction(wact);
  2304. wact = new QWidgetAction(menu);
  2305. wact->setData(5);
  2306. w = new VButtonMenuItem(wact, text.arg(5), this);
  2307. w->setToolTip(tooltip.arg(5).arg(VUtils::getShortcutText("Ctrl+5")));
  2308. w->setProperty("Heading5", true);
  2309. wact->setDefaultWidget(w);
  2310. menu->addAction(wact);
  2311. wact = new QWidgetAction(menu);
  2312. wact->setData(6);
  2313. w = new VButtonMenuItem(wact, text.arg(6), this);
  2314. w->setToolTip(tooltip.arg(6).arg(VUtils::getShortcutText("Ctrl+6")));
  2315. w->setProperty("Heading6", true);
  2316. wact->setDefaultWidget(w);
  2317. menu->addAction(wact);
  2318. wact = new QWidgetAction(menu);
  2319. wact->setData(0);
  2320. w = new VButtonMenuItem(wact, tr("Clear"), this);
  2321. w->setToolTip(tr("Clear\t%1").arg(VUtils::getShortcutText("Ctrl+7")));
  2322. wact->setDefaultWidget(w);
  2323. menu->addAction(wact);
  2324. connect(menu, &QMenu::triggered,
  2325. this, [this, menu](QAction *p_action) {
  2326. if (m_curTab) {
  2327. int level = p_action->data().toInt();
  2328. m_curTab->decorateText(TextDecoration::Heading, level);
  2329. }
  2330. menu->hide();
  2331. });
  2332. m_headingBtn->setMenu(menu);
  2333. p_tb->addWidget(m_headingBtn);
  2334. }
  2335. void VMainWindow::initThemeMenu(QMenu *p_menu)
  2336. {
  2337. QMenu *themeMenu = p_menu->addMenu(tr("Theme"));
  2338. themeMenu->setToolTipsVisible(true);
  2339. QAction *addAct = newAction(VIconUtils::menuIcon(":/resources/icons/add_style.svg"),
  2340. tr("Add Theme"),
  2341. themeMenu);
  2342. addAct->setToolTip(tr("Add custom theme"));
  2343. connect(addAct, &QAction::triggered,
  2344. this, [this]() {
  2345. VTipsDialog dialog(VUtils::getDocFile("tips_add_theme.md"),
  2346. tr("Add Theme"),
  2347. []() {
  2348. QUrl url = QUrl::fromLocalFile(g_config->getThemeConfigFolder());
  2349. QDesktopServices::openUrl(url);
  2350. },
  2351. this);
  2352. dialog.exec();
  2353. });
  2354. themeMenu->addAction(addAct);
  2355. QActionGroup *ag = new QActionGroup(this);
  2356. connect(ag, &QActionGroup::triggered,
  2357. this, [this](QAction *p_action) {
  2358. if (!p_action) {
  2359. return;
  2360. }
  2361. QString data = p_action->data().toString();
  2362. g_config->setTheme(data);
  2363. });
  2364. QList<QString> themes = g_config->getThemes();
  2365. QString theme = g_config->getTheme();
  2366. for (auto const &item : themes) {
  2367. QAction *act = new QAction(item, ag);
  2368. act->setToolTip(tr("Set as the theme of VNote (restart VNote to make it work)"));
  2369. act->setCheckable(true);
  2370. act->setData(item);
  2371. // Add it to the menu.
  2372. themeMenu->addAction(act);
  2373. if (theme == item) {
  2374. act->setChecked(true);
  2375. }
  2376. }
  2377. }
  2378. void VMainWindow::customShortcut()
  2379. {
  2380. VTipsDialog dialog(VUtils::getDocFile("tips_custom_shortcut.md"),
  2381. tr("Custom Shortcuts"),
  2382. []() {
  2383. #if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
  2384. // On macOS, it seems that we could not open that ini file directly.
  2385. QUrl url = QUrl::fromLocalFile(g_config->getConfigFolder());
  2386. #else
  2387. QUrl url = QUrl::fromLocalFile(g_config->getConfigFilePath());
  2388. #endif
  2389. QDesktopServices::openUrl(url);
  2390. },
  2391. this);
  2392. dialog.exec();
  2393. }
  2394. void VMainWindow::initVimCmd()
  2395. {
  2396. m_vimCmd = new VVimCmdLineEdit(this);
  2397. m_vimCmd->setProperty("VimCommandLine", true);
  2398. connect(m_vimCmd, &VVimCmdLineEdit::commandCancelled,
  2399. this, [this]() {
  2400. if (m_curTab) {
  2401. m_curTab->focusTab();
  2402. }
  2403. // NOTICE: should not hide before setting focus to edit tab.
  2404. m_vimCmd->hide();
  2405. if (m_curTab) {
  2406. m_curTab->handleVimCmdCommandCancelled();
  2407. }
  2408. });
  2409. connect(m_vimCmd, &VVimCmdLineEdit::commandFinished,
  2410. this, [this](VVim::CommandLineType p_type, const QString &p_cmd) {
  2411. if (m_curTab) {
  2412. m_curTab->focusTab();
  2413. }
  2414. m_vimCmd->hide();
  2415. // Hide the cmd line edit before execute the command.
  2416. // If we execute the command first, we will get Chinese input
  2417. // method enabled after returning to read mode.
  2418. if (m_curTab) {
  2419. m_curTab->handleVimCmdCommandFinished(p_type, p_cmd);
  2420. }
  2421. });
  2422. connect(m_vimCmd, &VVimCmdLineEdit::commandChanged,
  2423. this, [this](VVim::CommandLineType p_type, const QString &p_cmd) {
  2424. if (m_curTab) {
  2425. m_curTab->handleVimCmdCommandChanged(p_type, p_cmd);
  2426. }
  2427. });
  2428. connect(m_vimCmd, &VVimCmdLineEdit::requestNextCommand,
  2429. this, [this](VVim::CommandLineType p_type, const QString &p_cmd) {
  2430. if (m_curTab) {
  2431. QString cmd = m_curTab->handleVimCmdRequestNextCommand(p_type, p_cmd);
  2432. if (!cmd.isNull()) {
  2433. m_vimCmd->setCommand(cmd);
  2434. } else {
  2435. m_vimCmd->restoreUserLastInput();
  2436. }
  2437. }
  2438. });
  2439. connect(m_vimCmd, &VVimCmdLineEdit::requestPreviousCommand,
  2440. this, [this](VVim::CommandLineType p_type, const QString &p_cmd) {
  2441. if (m_curTab) {
  2442. QString cmd = m_curTab->handleVimCmdRequestPreviousCommand(p_type, p_cmd);
  2443. if (!cmd.isNull()) {
  2444. m_vimCmd->setCommand(cmd);
  2445. }
  2446. }
  2447. });
  2448. connect(m_vimCmd, &VVimCmdLineEdit::requestRegister,
  2449. this, [this](int p_key, int p_modifiers){
  2450. if (m_curTab) {
  2451. QString val = m_curTab->handleVimCmdRequestRegister(p_key, p_modifiers);
  2452. if (!val.isEmpty()) {
  2453. m_vimCmd->setText(m_vimCmd->text() + val);
  2454. }
  2455. }
  2456. });
  2457. m_vimCmd->hide();
  2458. }
  2459. void VMainWindow::toggleEditReadMode()
  2460. {
  2461. if (!m_curTab) {
  2462. return;
  2463. }
  2464. if (m_curTab->isEditMode()) {
  2465. // Save changes and read.
  2466. editArea->saveAndReadFile();
  2467. } else {
  2468. // Edit.
  2469. editArea->editFile();
  2470. }
  2471. }
  2472. void VMainWindow::updateEditReadAct(const VEditTab *p_tab)
  2473. {
  2474. static QIcon editIcon = VIconUtils::toolButtonIcon(":/resources/icons/edit_note.svg");
  2475. static QString editText;
  2476. static QIcon readIcon = VIconUtils::toolButtonIcon(":/resources/icons/save_exit.svg");
  2477. static QString readText;
  2478. if (editText.isEmpty()) {
  2479. QString keySeq = g_config->getShortcutKeySequence("EditReadNote");
  2480. QKeySequence seq(keySeq);
  2481. if (!seq.isEmpty()) {
  2482. QString shortcutText = VUtils::getShortcutText(keySeq);
  2483. editText = tr("Edit\t%1").arg(shortcutText);
  2484. readText = tr("Save Changes And Read\t%1").arg(shortcutText);
  2485. m_editReadAct->setShortcut(seq);
  2486. } else {
  2487. editText = tr("Edit");
  2488. readText = tr("Save Changes And Read");
  2489. }
  2490. }
  2491. if (!p_tab || !p_tab->isEditMode()) {
  2492. // Edit.
  2493. m_editReadAct->setIcon(editIcon);
  2494. m_editReadAct->setText(editText);
  2495. m_editReadAct->setStatusTip(tr("Edit current note"));
  2496. m_discardExitAct->setEnabled(false);
  2497. } else {
  2498. // Read.
  2499. m_editReadAct->setIcon(readIcon);
  2500. m_editReadAct->setText(readText);
  2501. m_editReadAct->setStatusTip(tr("Save changes and exit edit mode"));
  2502. m_discardExitAct->setEnabled(true);
  2503. }
  2504. m_editReadAct->setEnabled(p_tab);
  2505. }
  2506. void VMainWindow::handleExportAct()
  2507. {
  2508. VExportDialog dialog(notebookSelector->currentNotebook(),
  2509. directoryTree->currentDirectory(),
  2510. m_curFile,
  2511. m_cart,
  2512. g_config->getMdConverterType(),
  2513. this);
  2514. dialog.exec();
  2515. }
  2516. VNotebook *VMainWindow::getCurrentNotebook() const
  2517. {
  2518. return notebookSelector->currentNotebook();
  2519. }