vmainwindow.cpp 111 KB

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