vmainwindow.cpp 109 KB

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