# -*- coding: utf-8 -*- """novelWriter GUI Main Menu novelWriter – GUI Main Menu ============================= Class holding the main window File History: Created: 2019-04-27 [0.0.1] (Split from winmain) This file is a part of novelWriter Copyright 2020, Veronica Berglyd Olsen This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ import logging import nw from os import path from PyQt5.QtCore import QUrl, QProcess from PyQt5.QtGui import QDesktopServices from PyQt5.QtWidgets import QMenuBar, QAction, QMessageBox from nw.gui.about import GuiAbout from nw.constants import nwItemType, nwItemClass, nwDocAction, nwDocInsert logger = logging.getLogger(__name__) class GuiMainMenu(QMenuBar): def __init__(self, theParent): QMenuBar.__init__(self, theParent) logger.debug("Initialising GuiMainMenu ...") self.mainConf = nw.CONFIG self.theParent = theParent self.theProject = theParent.theProject self.assistProc = None self.helpPath = path.join(self.mainConf.assetPath, "help", "novelWriter.qhc") self.hasHelp = path.isfile(self.helpPath) self._buildProjectMenu() self._buildDocumentMenu() self._buildEditMenu() self._buildViewMenu() self._buildInsertMenu() self._buildFormatMenu() self._buildSearchMenu() self._buildToolsMenu() self._buildHelpMenu() # Function Pointers self._docAction = self.theParent.passDocumentAction self._moveTreeItem = self.theParent.treeView.moveTreeItem self._newTreeItem = self.theParent.treeView.newTreeItem self._docInsert = self.theParent.docEditor.insertText logger.debug("GuiMainMenu initialisation complete") return ## # Methods ## def setAvailableRoot(self): for itemClass in nwItemClass: if itemClass == nwItemClass.NO_CLASS: continue if itemClass == nwItemClass.TRASH: continue self.rootItems[itemClass].setEnabled( self.theProject.projTree.checkRootUnique(itemClass) ) return def closeHelp(self): """Close the process used for the Qt Assistant, if it is open. """ if self.assistProc is None: return if self.assistProc.state() == QProcess.Starting: if self.assistProc.waitForStarted(10000): self.assistProc.terminate() else: self.assistProc.kill() elif self.assistProc.state() == QProcess.Running: self.assistProc.terminate() if not self.assistProc.waitForFinished(10000): self.assistProc.kill() return ## # Update Menu on Settings Changed ## def setSpellCheck(self, theMode): """Set the spell check check box to theMode. This is controlled by the document editor class, which holds the master spell check flag. """ self.aSpellCheck.setChecked(theMode) return def setAutoOutline(self, theMode): """Set the auto outline check box to theMode. Used during initialisation. """ self.aAutoOutline.setChecked(theMode) return ## # Menu Action ## def _menuExit(self): """Exit novelWriter. """ self.theParent.closeMain() return def _toggleSpellCheck(self): """Toggle spell checking. The active status of the spell check flag is handled by the document editor class, so we make no decision, just pass a None to the function and let it decide. """ self.theParent.docEditor.setSpellCheck(None) return True def _toggleAutoOutline(self, theMode): """Toggle auto outline when the menu entry is checked. """ self.theProject.setAutoOutline(theMode) return True def _showAbout(self): """Show the about dialog. """ if self.mainConf.showGUI: msgAbout = GuiAbout(self.theParent) msgAbout.exec_() return True def _showAboutQt(self): """Show Qt's own About dialog. """ msgBox = QMessageBox() msgBox.aboutQt(self.theParent,"About Qt") return True def _openAssistant(self): """Open the documentation in Qt Assistant. """ self.assistProc = QProcess(self) self.assistProc.start("assistant", [ "-collectionFile", self.helpPath, "-enableRemoteControl" ]) if not self.assistProc.waitForStarted(20000): return False return True def _openWebsite(self, theUrl): """Open an URL in the system's default browser. """ QDesktopServices.openUrl(QUrl(theUrl)) return True def _openIssue(self): """Open the issue tracker URL in the system's default browser. """ QDesktopServices.openUrl(QUrl(nw.__issuesurl__)) return True def _showDocumentLocation(self): """Open the dialog showing the location of the editor document. """ self.theParent.docEditor.revealLocation() return True def _doBackup(self): """Call the backup function for the project. """ self.theProject.zipIt(True) return True ## # Menu Builders ## def _buildProjectMenu(self): # Project self.projMenu = self.addMenu("&Project") # Project > New Project self.aNewProject = QAction("New Project", self) self.aNewProject.setStatusTip("Create new project") self.aNewProject.triggered.connect(lambda : self.theParent.newProject(None)) self.projMenu.addAction(self.aNewProject) # Project > Open Project self.aOpenProject = QAction("Open Project", self) self.aOpenProject.setStatusTip("Open project") self.aOpenProject.setShortcut("Ctrl+Shift+O") self.aOpenProject.triggered.connect(self.theParent.manageProjects) self.projMenu.addAction(self.aOpenProject) # Project > Save Project self.aSaveProject = QAction("Save Project", self) self.aSaveProject.setStatusTip("Save project") self.aSaveProject.setShortcut("Ctrl+Shift+S") self.aSaveProject.triggered.connect(self.theParent.saveProject) self.projMenu.addAction(self.aSaveProject) # Project > Close Project self.aCloseProject = QAction("Close Project", self) self.aCloseProject.setStatusTip("Close project") self.aCloseProject.setShortcut("Ctrl+Shift+W") self.aCloseProject.triggered.connect(lambda : self.theParent.closeProject(False)) self.projMenu.addAction(self.aCloseProject) # Project > Project Settings self.aProjectSettings = QAction("Project Settings", self) self.aProjectSettings.setStatusTip("Project settings") self.aProjectSettings.setShortcut("Ctrl+Shift+,") self.aProjectSettings.triggered.connect(self.theParent.editProjectDialog) self.projMenu.addAction(self.aProjectSettings) # Project > Separator self.projMenu.addSeparator() # Project > New Root self.rootMenu = self.projMenu.addMenu("Create Root Folder") self.rootItems = {} self.rootItems[nwItemClass.NOVEL] = QAction("Novel Root", self.rootMenu) self.rootItems[nwItemClass.PLOT] = QAction("Plot Root", self.rootMenu) self.rootItems[nwItemClass.CHARACTER] = QAction("Character Root", self.rootMenu) self.rootItems[nwItemClass.WORLD] = QAction("Location Root", self.rootMenu) self.rootItems[nwItemClass.TIMELINE] = QAction("Timeline Root", self.rootMenu) self.rootItems[nwItemClass.OBJECT] = QAction("Object Root", self.rootMenu) self.rootItems[nwItemClass.ENTITY] = QAction("Entity Root", self.rootMenu) self.rootItems[nwItemClass.CUSTOM] = QAction("Custom Root", self.rootMenu) nCount = 0 for itemClass in self.rootItems.keys(): nCount += 1 # This forces the lambdas to be unique self.rootItems[itemClass].triggered.connect( lambda nCount, itemClass=itemClass : self._newTreeItem(nwItemType.ROOT, itemClass) ) self.rootMenu.addAction(self.rootItems[itemClass]) # Project > New Folder self.aCreateFolder = QAction("Create Folder", self) self.aCreateFolder.setStatusTip("Create folder") self.aCreateFolder.setShortcut("Ctrl+Shift+N") self.aCreateFolder.triggered.connect(lambda : self._newTreeItem(nwItemType.FOLDER, None)) self.projMenu.addAction(self.aCreateFolder) # Project > Separator self.projMenu.addSeparator() # Project > Edit self.aEditItem = QAction("Edit Project Item", self) self.aEditItem.setStatusTip("Change item settings") self.aEditItem.setShortcuts(["Ctrl+E", "F2"]) self.aEditItem.triggered.connect(self.theParent.editItem) self.projMenu.addAction(self.aEditItem) # Project > Delete self.aDeleteItem = QAction("Delete Project Item", self) self.aDeleteItem.setStatusTip("Delete selected item") self.aDeleteItem.setShortcut("Ctrl+Del") self.aDeleteItem.triggered.connect(lambda : self.theParent.treeView.deleteItem(None)) self.projMenu.addAction(self.aDeleteItem) # Project > Empty Trash self.aEmptyTrash = QAction("Empty Trash", self) self.aEmptyTrash.setStatusTip("Permanently delete all files in the Trash folder") self.aEmptyTrash.triggered.connect(self.theParent.treeView.emptyTrash) self.projMenu.addAction(self.aEmptyTrash) # Project > Separator self.projMenu.addSeparator() # Project > Exit self.aExitNW = QAction("Exit", self) self.aExitNW.setStatusTip("Exit %s" % self.mainConf.appName) self.aExitNW.setShortcut("Ctrl+Q") self.aExitNW.triggered.connect(self._menuExit) self.projMenu.addAction(self.aExitNW) return def _buildDocumentMenu(self): # Document self.docuMenu = self.addMenu("&Document") # Document > New self.aNewDoc = QAction("New Document", self) self.aNewDoc.setStatusTip("Create new document") self.aNewDoc.setShortcut("Ctrl+N") self.aNewDoc.triggered.connect(lambda : self._newTreeItem(nwItemType.FILE, None)) self.docuMenu.addAction(self.aNewDoc) # Document > Open self.aOpenDoc = QAction("Open Document", self) self.aOpenDoc.setStatusTip("Open selected document") self.aOpenDoc.setShortcut("Ctrl+O") self.aOpenDoc.triggered.connect(self.theParent.openSelectedItem) self.docuMenu.addAction(self.aOpenDoc) # Document > Save self.aSaveDoc = QAction("Save Document", self) self.aSaveDoc.setStatusTip("Save current document") self.aSaveDoc.setShortcut("Ctrl+S") self.aSaveDoc.triggered.connect(self.theParent.saveDocument) self.docuMenu.addAction(self.aSaveDoc) # Document > Close self.aCloseDoc = QAction("Close Document", self) self.aCloseDoc.setStatusTip("Close current document") self.aCloseDoc.setShortcut("Ctrl+W") self.aCloseDoc.triggered.connect(self.theParent.closeDocEditor) self.docuMenu.addAction(self.aCloseDoc) # Document > Separator self.docuMenu.addSeparator() # Document > Preview self.aViewDoc = QAction("View Document", self) self.aViewDoc.setStatusTip("View document as HTML") self.aViewDoc.setShortcut("Ctrl+R") self.aViewDoc.triggered.connect(lambda : self.theParent.viewDocument(None)) self.docuMenu.addAction(self.aViewDoc) # Document > Close Preview self.aCloseView = QAction("Close Document View", self) self.aCloseView.setStatusTip("Close document view pane") self.aCloseView.setShortcut("Ctrl+Shift+R") self.aCloseView.triggered.connect(self.theParent.closeDocViewer) self.docuMenu.addAction(self.aCloseView) # Document > Separator self.docuMenu.addSeparator() # Document > Show File Details self.aFileDetails = QAction("Show File Details", self) self.aFileDetails.setStatusTip( "Shows a message box with the document location in the project folder" ) self.aFileDetails.triggered.connect(self._showDocumentLocation) self.docuMenu.addAction(self.aFileDetails) # Document > Import From File self.aImportFile = QAction("Import from File", self) self.aImportFile.setStatusTip("Import document from a text or markdown file") self.aImportFile.setShortcut("Ctrl+Shift+I") self.aImportFile.triggered.connect(self.theParent.importDocument) self.docuMenu.addAction(self.aImportFile) # Document > Merge Documents self.aMergeDocs = QAction("Merge Folder to Document", self) self.aMergeDocs.setStatusTip("Merge a folder of documents to a single document") self.aMergeDocs.triggered.connect(self.theParent.mergeDocuments) self.docuMenu.addAction(self.aMergeDocs) # Document > Split Document self.aSplitDoc = QAction("Split Document to Folder", self) self.aSplitDoc.setStatusTip("Split a document into a folder of multiple documents") self.aSplitDoc.triggered.connect(self.theParent.splitDocument) self.docuMenu.addAction(self.aSplitDoc) return def _buildEditMenu(self): # Edit self.editMenu = self.addMenu("&Edit") # Edit > Undo self.aEditUndo = QAction("Undo", self) self.aEditUndo.setStatusTip("Undo last change") self.aEditUndo.setShortcut("Ctrl+Z") self.aEditUndo.triggered.connect(lambda: self._docAction(nwDocAction.UNDO)) self.editMenu.addAction(self.aEditUndo) # Edit > Redo self.aEditRedo = QAction("Redo", self) self.aEditRedo.setStatusTip("Redo last change") self.aEditRedo.setShortcut("Ctrl+Y") self.aEditRedo.triggered.connect(lambda: self._docAction(nwDocAction.REDO)) self.editMenu.addAction(self.aEditRedo) # Edit > Separator self.editMenu.addSeparator() # Edit > Cut self.aEditCut = QAction("Cut", self) self.aEditCut.setStatusTip("Cut selected text") self.aEditCut.setShortcut("Ctrl+X") self.aEditCut.triggered.connect(lambda: self._docAction(nwDocAction.CUT)) self.editMenu.addAction(self.aEditCut) # Edit > Copy self.aEditCopy = QAction("Copy", self) self.aEditCopy.setStatusTip("Copy selected text") self.aEditCopy.setShortcut("Ctrl+C") self.aEditCopy.triggered.connect(lambda: self._docAction(nwDocAction.COPY)) self.editMenu.addAction(self.aEditCopy) # Edit > Paste self.aEditPaste = QAction("Paste", self) self.aEditPaste.setStatusTip("Paste text from clipboard") self.aEditPaste.setShortcut("Ctrl+V") self.aEditPaste.triggered.connect(lambda: self._docAction(nwDocAction.PASTE)) self.editMenu.addAction(self.aEditPaste) # Edit > Separator self.editMenu.addSeparator() # Edit > Select All self.aSelectAll = QAction("Select All", self) self.aSelectAll.setStatusTip("Select all text in document") self.aSelectAll.setShortcut("Ctrl+A") self.aSelectAll.triggered.connect(lambda: self._docAction(nwDocAction.SEL_ALL)) self.editMenu.addAction(self.aSelectAll) # Edit > Select Paragraph self.aSelectPar = QAction("Select Paragraph", self) self.aSelectPar.setStatusTip("Select all text in paragraph") self.aSelectPar.setShortcut("Ctrl+Shift+A") self.aSelectPar.triggered.connect(lambda: self._docAction(nwDocAction.SEL_PARA)) self.editMenu.addAction(self.aSelectPar) return def _buildViewMenu(self): # View self.viewMenu = self.addMenu("&View") # View > TreeView self.aFocusTree = QAction("Focus Project Tree", self) self.aFocusTree.setStatusTip("Move focus to project tree") self.aFocusTree.setShortcut("Alt+1") self.aFocusTree.triggered.connect(lambda : self.theParent.setFocus(1)) self.viewMenu.addAction(self.aFocusTree) # View > Document Pane 1 self.aFocusEditor = QAction("Focus Document Editor", self) self.aFocusEditor.setStatusTip("Move focus to left document pane") self.aFocusEditor.setShortcut("Alt+2") self.aFocusEditor.triggered.connect(lambda : self.theParent.setFocus(2)) self.viewMenu.addAction(self.aFocusEditor) # View > Document Pane 2 self.aFocusView = QAction("Focus Document Viewer", self) self.aFocusView.setStatusTip("Move focus to right document pane") self.aFocusView.setShortcut("Alt+3") self.aFocusView.triggered.connect(lambda : self.theParent.setFocus(3)) self.viewMenu.addAction(self.aFocusView) # View > Separator self.viewMenu.addSeparator() # View > Toggle Distraction Free Mode self.aFocusMode = QAction("Distraction Free Mode", self) self.aFocusMode.setStatusTip("Toggles distraction free mode, only showing text editor") self.aFocusMode.setShortcut("F8") self.aFocusMode.setCheckable(True) self.aFocusMode.setChecked(self.theParent.isFocusMode) self.aFocusMode.toggled.connect(self.theParent.toggleFocusMode) self.viewMenu.addAction(self.aFocusMode) # View > Toggle Full Screen self.aFullScreen = QAction("Full Screen Mode", self) self.aFullScreen.setStatusTip("Maximises the main window") self.aFullScreen.setShortcut("F11") self.aFullScreen.triggered.connect(self.theParent.toggleFullScreenMode) self.viewMenu.addAction(self.aFullScreen) return def _buildInsertMenu(self): # Insert self.insertMenu = self.addMenu("&Insert") # Insert > Short Dash self.aInsENDash = QAction("Short Dash", self) self.aInsENDash.setStatusTip("Insert short dash") self.aInsENDash.setShortcut("Ctrl+K, -") self.aInsENDash.triggered.connect(lambda: self._docInsert(nwDocInsert.SHORT_DASH)) self.insertMenu.addAction(self.aInsENDash) # Insert > Long Dash self.aInsEMDash = QAction("Long Dash", self) self.aInsEMDash.setStatusTip("Insert long dash") self.aInsEMDash.setShortcut("Ctrl+K, _") self.aInsEMDash.triggered.connect(lambda: self._docInsert(nwDocInsert.LONG_DASH)) self.insertMenu.addAction(self.aInsEMDash) # Insert > Ellipsis self.aInsEllipsis = QAction("Ellipsis", self) self.aInsEllipsis.setStatusTip("Insert ellipsis") self.aInsEllipsis.setShortcut("Ctrl+K, .") self.aInsEllipsis.triggered.connect(lambda: self._docInsert(nwDocInsert.ELLIPSIS)) self.insertMenu.addAction(self.aInsEllipsis) # Insert > Separator self.insertMenu.addSeparator() # Insert > Left Single Quote self.aInsQuoteLS = QAction("Left Single Quote", self) self.aInsQuoteLS.setStatusTip("Insert left single quote") self.aInsQuoteLS.setShortcut("Ctrl+K, 1") self.aInsQuoteLS.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_LS)) self.insertMenu.addAction(self.aInsQuoteLS) # Insert > Right Single Quote self.aInsQuoteRS = QAction("Right Single Quote", self) self.aInsQuoteRS.setStatusTip("Insert right single quote") self.aInsQuoteRS.setShortcut("Ctrl+K, 2") self.aInsQuoteRS.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_RS)) self.insertMenu.addAction(self.aInsQuoteRS) # Insert > Left Double Quote self.aInsQuoteLD = QAction("Left Double Quote", self) self.aInsQuoteLD.setStatusTip("Insert left double quote") self.aInsQuoteLD.setShortcut("Ctrl+K, 3") self.aInsQuoteLD.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_LD)) self.insertMenu.addAction(self.aInsQuoteLD) # Insert > Right Double Quote self.aInsQuoteRD = QAction("Right Double Quote", self) self.aInsQuoteRD.setStatusTip("Insert right double quote") self.aInsQuoteRD.setShortcut("Ctrl+K, 4") self.aInsQuoteRD.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_RD)) self.insertMenu.addAction(self.aInsQuoteRD) # Insert > Separator self.insertMenu.addSeparator() # Insert > Hard Line Break self.aInsHardBreak = QAction("Hard Line Break", self) self.aInsHardBreak.setStatusTip("Insert a hard line break") self.aInsHardBreak.setShortcut("Ctrl+K, Return") self.aInsHardBreak.triggered.connect(lambda: self._docInsert(nwDocInsert.HARD_BREAK)) self.insertMenu.addAction(self.aInsHardBreak) # Insert > Non-Breaking Space self.aInsNBSpace = QAction("Non-Breaking Space", self) self.aInsNBSpace.setStatusTip("Insert a non-breaking space") self.aInsNBSpace.setShortcut("Ctrl+K, Space") self.aInsNBSpace.triggered.connect(lambda: self._docInsert(nwDocInsert.NB_SPACE)) self.insertMenu.addAction(self.aInsNBSpace) # Insert > Thin Space self.aInsThinSpace = QAction("Thin Space", self) self.aInsThinSpace.setStatusTip("Insert a thin space") self.aInsThinSpace.setShortcut("Ctrl+K, Shift+Space") self.aInsThinSpace.triggered.connect(lambda: self._docInsert(nwDocInsert.THIN_SPACE)) self.insertMenu.addAction(self.aInsThinSpace) # Insert > Thin Non-Breaking Space self.aInsThinNBSpace = QAction("Thin Non-Breaking Space", self) self.aInsThinNBSpace.setStatusTip("Insert a thin non-breaking space") self.aInsThinNBSpace.setShortcut("Ctrl+K, Ctrl+Space") self.aInsThinNBSpace.triggered.connect(lambda: self._docInsert(nwDocInsert.THIN_NB_SPACE)) self.insertMenu.addAction(self.aInsThinNBSpace) return def _buildSearchMenu(self): # Search self.srcMenu = self.addMenu("&Search") # Search > Find self.aFind = QAction("Find", self) self.aFind.setStatusTip("Find text in document") self.aFind.setShortcut("Ctrl+F") self.aFind.triggered.connect(lambda: self._docAction(nwDocAction.FIND)) self.srcMenu.addAction(self.aFind) # Search > Replace self.aReplace = QAction("Replace", self) self.aReplace.setStatusTip("Replace text in document") if self.mainConf.osDarwin: self.aReplace.setShortcut("Ctrl+=") else: self.aReplace.setShortcut("Ctrl+H") self.aReplace.triggered.connect(lambda: self._docAction(nwDocAction.REPLACE)) self.srcMenu.addAction(self.aReplace) # Search > Find Next self.aFindNext = QAction("Find Next", self) self.aFindNext.setStatusTip("Find next occurrence text in document") if self.mainConf.osDarwin: self.aFindNext.setShortcuts(["Ctrl+G","F3"]) else: self.aFindNext.setShortcuts(["F3","Ctrl+G"]) self.aFindNext.triggered.connect(lambda: self._docAction(nwDocAction.GO_NEXT)) self.srcMenu.addAction(self.aFindNext) # Search > Find Prev self.aFindPrev = QAction("Find Previous", self) self.aFindPrev.setStatusTip("Find previous occurrence text in document") if self.mainConf.osDarwin: self.aFindPrev.setShortcuts(["Ctrl+Shift+G","Shift+F3"]) else: self.aFindPrev.setShortcuts(["Shift+F3","Ctrl+Shift+G"]) self.aFindPrev.triggered.connect(lambda: self._docAction(nwDocAction.GO_PREV)) self.srcMenu.addAction(self.aFindPrev) # Search > Replace Next self.aReplaceNext = QAction("Replace Next", self) self.aReplaceNext.setStatusTip("Find and replace next occurrence text in document") self.aReplaceNext.setShortcut("Ctrl+Shift+1") self.aReplaceNext.triggered.connect(lambda: self._docAction(nwDocAction.REPL_NEXT)) self.srcMenu.addAction(self.aReplaceNext) return def _buildFormatMenu(self): # Format self.fmtMenu = self.addMenu("&Format") # Format > Emphasis self.aFmtEmph = QAction("Emphasis", self) self.aFmtEmph.setStatusTip("Add emphasis to selected text (italic)") self.aFmtEmph.setShortcut("Ctrl+I") self.aFmtEmph.triggered.connect(lambda: self._docAction(nwDocAction.EMPH)) self.fmtMenu.addAction(self.aFmtEmph) # Format > Strong Emphasis self.aFmtStrong = QAction("Strong Emphasis", self) self.aFmtStrong.setStatusTip("Add strong emphasis to selected text (bold)") self.aFmtStrong.setShortcut("Ctrl+B") self.aFmtStrong.triggered.connect(lambda: self._docAction(nwDocAction.STRONG)) self.fmtMenu.addAction(self.aFmtStrong) # Format > Strikethrough self.aFmtStrike = QAction("Strikethrough", self) self.aFmtStrike.setStatusTip("Add strikethrough to selected text") self.aFmtStrike.setShortcut("Ctrl+-") self.aFmtStrike.triggered.connect(lambda: self._docAction(nwDocAction.STRIKE)) self.fmtMenu.addAction(self.aFmtStrike) # Edit > Separator self.fmtMenu.addSeparator() # Format > Double Quotes self.aFmtDQuote = QAction("Wrap Double Quotes", self) self.aFmtDQuote.setStatusTip("Wrap selected text in double quotes") self.aFmtDQuote.setShortcut("Ctrl+D") self.aFmtDQuote.triggered.connect(lambda: self._docAction(nwDocAction.D_QUOTE)) self.fmtMenu.addAction(self.aFmtDQuote) # Format > Single Quotes self.aFmtSQuote = QAction("Wrap Single Quotes", self) self.aFmtSQuote.setStatusTip("Wrap selected text in single quotes") self.aFmtSQuote.setShortcut("Ctrl+Shift+D") self.aFmtSQuote.triggered.connect(lambda: self._docAction(nwDocAction.S_QUOTE)) self.fmtMenu.addAction(self.aFmtSQuote) # Format > Separator self.fmtMenu.addSeparator() # Format > Header 1 self.aFmtHead1 = QAction("Header 1", self) self.aFmtHead1.setStatusTip("Change the block format to Header 1") self.aFmtHead1.setShortcut("Ctrl+1") self.aFmtHead1.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H1)) self.fmtMenu.addAction(self.aFmtHead1) # Format > Header 2 self.aFmtHead2 = QAction("Header 2", self) self.aFmtHead2.setStatusTip("Change the block format to Header 2") self.aFmtHead2.setShortcut("Ctrl+2") self.aFmtHead2.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H2)) self.fmtMenu.addAction(self.aFmtHead2) # Format > Header 3 self.aFmtHead3 = QAction("Header 3", self) self.aFmtHead3.setStatusTip("Change the block format to Header 3") self.aFmtHead3.setShortcut("Ctrl+3") self.aFmtHead3.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H3)) self.fmtMenu.addAction(self.aFmtHead3) # Format > Header 4 self.aFmtHead4 = QAction("Header 4", self) self.aFmtHead4.setStatusTip("Change the block format to Header 4") self.aFmtHead4.setShortcut("Ctrl+4") self.aFmtHead4.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H4)) self.fmtMenu.addAction(self.aFmtHead4) # Format > Comment self.aFmtComment = QAction("Comment", self) self.aFmtComment.setStatusTip("Change the block format to comment") self.aFmtComment.setShortcut("Ctrl+/") self.aFmtComment.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_COM)) self.fmtMenu.addAction(self.aFmtComment) # Format > Remove Block Format self.aFmtNoFormat = QAction("Remove Block Format", self) self.aFmtNoFormat.setStatusTip("Strips block format") self.aFmtNoFormat.setShortcuts(["Ctrl+0","Ctrl+Shift+/"]) self.aFmtNoFormat.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_TXT)) self.fmtMenu.addAction(self.aFmtNoFormat) # Format > Separator self.fmtMenu.addSeparator() # Format > Replace Single Quotes self.aFmtReplSng = QAction("Replace Single Quotes", self) self.aFmtReplSng.setStatusTip("Replace all straight single quotes in selected text") self.aFmtReplSng.triggered.connect(lambda: self._docAction(nwDocAction.REPL_SNG)) self.fmtMenu.addAction(self.aFmtReplSng) # Format > Replace Double Quotes self.aFmtReplDbl = QAction("Replace Double Quotes", self) self.aFmtReplDbl.setStatusTip("Replace all straight double quotes in selected text") self.aFmtReplDbl.triggered.connect(lambda: self._docAction(nwDocAction.REPL_DBL)) self.fmtMenu.addAction(self.aFmtReplDbl) return def _buildToolsMenu(self): # Tools self.toolsMenu = self.addMenu("&Tools") # Tools > Move Up self.aMoveUp = QAction("Move Tree Item Up", self) self.aMoveUp.setStatusTip("Move item up") self.aMoveUp.setShortcut("Ctrl+Shift+Up") self.aMoveUp.triggered.connect(lambda : self._moveTreeItem(-1)) self.toolsMenu.addAction(self.aMoveUp) # Tools > Move Down self.aMoveDown = QAction("Move Tree Item Down", self) self.aMoveDown.setStatusTip("Move item down") self.aMoveDown.setShortcut("Ctrl+Shift+Down") self.aMoveDown.triggered.connect(lambda : self._moveTreeItem(1)) self.toolsMenu.addAction(self.aMoveDown) # Tools > Separator self.toolsMenu.addSeparator() # Tools > Toggle Spell Check self.aSpellCheck = QAction("Check Spelling", self) self.aSpellCheck.setStatusTip("Toggle check spelling") self.aSpellCheck.setCheckable(True) self.aSpellCheck.setChecked(self.theProject.spellCheck) # Here we must used triggered, not toggled, to avoid recursion self.aSpellCheck.triggered.connect(self._toggleSpellCheck) self.aSpellCheck.setShortcut("Ctrl+F7") self.toolsMenu.addAction(self.aSpellCheck) # Tools > Update Spell Check self.aReRunSpell = QAction("Re-Run Spell Check", self) self.aReRunSpell.setStatusTip("Run the spell checker on current document") self.aReRunSpell.setShortcut("F7") self.aReRunSpell.triggered.connect(self.theParent.docEditor.spellCheckDocument) self.toolsMenu.addAction(self.aReRunSpell) # Tools > Separator self.toolsMenu.addSeparator() # Tools > Rebuild Indices self.aRebuildIndex = QAction("Rebuild Index", self) self.aRebuildIndex.setStatusTip("Rebuild the tag indices and word counts") self.aRebuildIndex.setShortcut("F9") self.aRebuildIndex.triggered.connect(self.theParent.rebuildIndex) self.toolsMenu.addAction(self.aRebuildIndex) # Tools > Rebuild Outline self.aRebuildOutline = QAction("Rebuild Outline", self) self.aRebuildOutline.setStatusTip("Rebuild the novel outline tree") self.aRebuildOutline.setShortcut("F10") self.aRebuildOutline.triggered.connect(self.theParent.rebuildOutline) self.toolsMenu.addAction(self.aRebuildOutline) # Tools > Toggle Auto Build Outline self.aAutoOutline = QAction("Auto-Update Outline", self) self.aAutoOutline.setStatusTip("Update project outline when a novel file is changed") self.aAutoOutline.setCheckable(True) self.aAutoOutline.toggled.connect(self._toggleAutoOutline) self.aAutoOutline.setShortcut("Ctrl+F10") self.toolsMenu.addAction(self.aAutoOutline) # Tools > Separator self.toolsMenu.addSeparator() # Tools > Backup self.aBackupProject = QAction("Backup Project Folder", self) self.aBackupProject.setStatusTip("Backup Project") self.aBackupProject.triggered.connect(self._doBackup) self.toolsMenu.addAction(self.aBackupProject) # Tools > Export Project self.aBuildProject = QAction("Build Novel Project", self) self.aBuildProject.setStatusTip("Launch the Build novel project tool") self.aBuildProject.setShortcut("F5") self.aBuildProject.triggered.connect(self.theParent.buildProjectDialog) self.toolsMenu.addAction(self.aBuildProject) # Tools > Writing Stats self.aWritingStats = QAction("Writing Statistics", self) self.aWritingStats.setStatusTip("Show the writing statistics dialog") self.aWritingStats.setShortcut("F6") self.aWritingStats.triggered.connect(self.theParent.showWritingStatsDialog) self.toolsMenu.addAction(self.aWritingStats) # Tools > Settings self.aPreferences = QAction("Preferences", self) self.aPreferences.setStatusTip("Preferences") self.aPreferences.setShortcut("Ctrl+,") self.aPreferences.triggered.connect(self.theParent.editConfigDialog) self.toolsMenu.addAction(self.aPreferences) return def _buildHelpMenu(self): # Help self.helpMenu = self.addMenu("&Help") # Help > About self.aAboutNW = QAction("About %s" % self.mainConf.appName, self) self.aAboutNW.setStatusTip("About %s" % self.mainConf.appName) self.aAboutNW.triggered.connect(self._showAbout) self.helpMenu.addAction(self.aAboutNW) # Help > About Qt5 self.aAboutQt = QAction("About Qt5", self) self.aAboutQt.setStatusTip("About Qt5") self.aAboutQt.triggered.connect(self._showAboutQt) self.helpMenu.addAction(self.aAboutQt) # Help > Separator self.helpMenu.addSeparator() # Document > Documentation self.aHelp = QAction("Documentation", self) if self.hasHelp: self.aHelp.setStatusTip("View local documentation with Qt Assistant") self.aHelp.triggered.connect(self._openAssistant) else: self.aHelp.setStatusTip("View online documentation") self.aHelp.triggered.connect(lambda: self._openWebsite(nw.__docurl__)) self.aHelp.setShortcut("F1") self.helpMenu.addAction(self.aHelp) # Document > Go to Website self.aWebsite = QAction("Open the novelWriter Website", self) self.aWebsite.setStatusTip("View the main website") self.aWebsite.triggered.connect(lambda: self._openWebsite(nw.__url__)) self.helpMenu.addAction(self.aWebsite) # Document > Report Issue self.aIssue = QAction("Report an Issue", self) self.aIssue.setStatusTip("Report a bug or issue on GitHub") self.aIssue.triggered.connect(lambda: self._openWebsite(nw.__issuesurl__)) self.helpMenu.addAction(self.aIssue) return # END Class GuiMainMenu