diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index 6123ce21..1fd6f00d 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -54,6 +54,8 @@ class ToHtml(Tokenizer): nwUnicode.U_EMDASH : nwUnicode.H_EMDASH, nwUnicode.U_HELLIP : nwUnicode.H_HELLIP, nwUnicode.U_NBSP : nwUnicode.H_NBSP, + nwUnicode.U_THNSP : nwUnicode.H_THNSP, + nwUnicode.U_THNBSP : nwUnicode.H_THNBSP, } self.revDict = {} self.reReplace = [] diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index eadce6ff..35ac09d8 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -48,7 +48,7 @@ from PyQt5.QtWidgets import ( from nw.core import NWDoc from nw.gui.dochighlight import GuiDocHighlighter from nw.core import NWSpellSimple, countWords -from nw.constants import nwUnicode, nwDocAction +from nw.constants import nwUnicode, nwDocAction, nwDocInsert, nwInsertSymbols from nw.common import transferCase logger = logging.getLogger(__name__) @@ -570,6 +570,21 @@ class GuiDocEditor(QTextEdit): )) return + def insertText(self, theInsert): + """Insert a specific type of text at the cursor position. + """ + if isinstance(theInsert, str): + theText = theInsert + elif theInsert in nwInsertSymbols.SYMBOLS: + theText = nwInsertSymbols.SYMBOLS[theInsert] + else: + return False + theCursor = self.textCursor() + theCursor.beginEditBlock() + theCursor.insertText(theText) + theCursor.endEditBlock() + return True + def closeSearch(self): """Close the search box. """ @@ -598,18 +613,7 @@ class GuiDocEditor(QTextEdit): # seems to be capturing the return key. return - if keyEvent.modifiers() == Qt.ShiftModifier: - theKey = keyEvent.key() - if theKey == Qt.Key_Return: - self._insertHardBreak() - elif theKey == Qt.Key_Enter: - self._insertHardBreak() - elif theKey == Qt.Key_Space: - self._insertNonBreakingSpace() - else: - QTextEdit.keyPressEvent(self, keyEvent) - else: - QTextEdit.keyPressEvent(self, keyEvent) + QTextEdit.keyPressEvent(self, keyEvent) return @@ -797,24 +801,6 @@ class GuiDocEditor(QTextEdit): return True - def _insertHardBreak(self): - """Inserts a hard line break at the cursor position. - """ - theCursor = self.textCursor() - theCursor.beginEditBlock() - theCursor.insertText(" \n") - theCursor.endEditBlock() - return - - def _insertNonBreakingSpace(self): - """Inserts a non-breaking space at the cursor position. - """ - theCursor = self.textCursor() - theCursor.beginEditBlock() - theCursor.insertText(nwUnicode.U_NBSP) - theCursor.endEditBlock() - return - def _openSpellContext(self): """Opens the spell check context menu at the current point of the cursor. diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py index a76c9646..4acc0e7a 100644 --- a/nw/gui/dochighlight.py +++ b/nw/gui/dochighlight.py @@ -132,7 +132,7 @@ class GuiDocHighlighter(QSyntaxHighlighter): # Non-Breaking Spaces self.hRules.append(( - "[%s]+" % nwUnicode.U_NBSP, { + "[%s%s]+" % (nwUnicode.U_NBSP, nwUnicode.U_THNBSP), { 0 : self.hStyles["nobreak"], } )) diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 720eefae..7f7c9164 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -28,12 +28,12 @@ import logging import nw -from PyQt5.QtCore import QUrl +from PyQt5.QtCore import Qt, QUrl 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 +from nw.constants import nwItemType, nwItemClass, nwDocAction, nwDocInsert logger = logging.getLogger(__name__) @@ -50,8 +50,9 @@ class GuiMainMenu(QMenuBar): self._buildProjectMenu() self._buildDocumentMenu() self._buildEditMenu() - self._buildViewMenu() + self._buildInsertMenu() self._buildFormatMenu() + self._buildViewMenu() self._buildToolsMenu() self._buildHelpMenu() @@ -59,6 +60,7 @@ class GuiMainMenu(QMenuBar): 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") @@ -341,53 +343,6 @@ class GuiMainMenu(QMenuBar): 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.aZenMode = QAction("Zen Mode", self) - self.aZenMode.setStatusTip("Toggles distraction free mode, only showing text editor") - self.aZenMode.setShortcut("F8") - self.aZenMode.setCheckable(True) - self.aZenMode.setChecked(self.theParent.isZenMode) - self.aZenMode.toggled.connect(self.theParent.toggleZenMode) - self.viewMenu.addAction(self.aZenMode) - - # 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 _buildEditMenu(self): # Edit @@ -497,6 +452,65 @@ class GuiMainMenu(QMenuBar): 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 > 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 _buildFormatMenu(self): # Format @@ -594,6 +608,53 @@ class GuiMainMenu(QMenuBar): 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.aZenMode = QAction("Zen Mode", self) + self.aZenMode.setStatusTip("Toggles distraction free mode, only showing text editor") + self.aZenMode.setShortcut("F8") + self.aZenMode.setCheckable(True) + self.aZenMode.setChecked(self.theParent.isZenMode) + self.aZenMode.toggled.connect(self.theParent.toggleZenMode) + self.viewMenu.addAction(self.aZenMode) + + # 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 _buildToolsMenu(self): # Tools diff --git a/nw/guimain.py b/nw/guimain.py index 70a6ba0b..a82d5a99 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -966,12 +966,15 @@ class GuiMain(QMainWindow): """Connect to the main window all menu actions that need to be available also when the main menu is hidden. """ + # Project self.addAction(self.mainMenu.aSaveProject) self.addAction(self.mainMenu.aExitNW) + + # Document self.addAction(self.mainMenu.aSaveDoc) self.addAction(self.mainMenu.aFileDetails) - self.addAction(self.mainMenu.aZenMode) - self.addAction(self.mainMenu.aFullScreen) + + # Edit self.addAction(self.mainMenu.aEditUndo) self.addAction(self.mainMenu.aEditRedo) self.addAction(self.mainMenu.aEditCut) @@ -979,6 +982,17 @@ class GuiMain(QMainWindow): self.addAction(self.mainMenu.aEditPaste) self.addAction(self.mainMenu.aSelectAll) self.addAction(self.mainMenu.aSelectPar) + + # Insert + self.addAction(self.mainMenu.aInsENDash) + self.addAction(self.mainMenu.aInsEMDash) + self.addAction(self.mainMenu.aInsEllipsis) + self.addAction(self.mainMenu.aInsHardBreak) + self.addAction(self.mainMenu.aInsNBSpace) + self.addAction(self.mainMenu.aInsThinSpace) + self.addAction(self.mainMenu.aInsThinNBSpace) + + # Format self.addAction(self.mainMenu.aFmtItalic) self.addAction(self.mainMenu.aFmtBold) self.addAction(self.mainMenu.aFmtBoldIt) @@ -990,10 +1004,19 @@ class GuiMain(QMainWindow): self.addAction(self.mainMenu.aFmtHead4) self.addAction(self.mainMenu.aFmtComment) self.addAction(self.mainMenu.aFmtNoFormat) + + # View + self.addAction(self.mainMenu.aZenMode) + self.addAction(self.mainMenu.aFullScreen) + + # Tools self.addAction(self.mainMenu.aSpellCheck) self.addAction(self.mainMenu.aReRunSpell) self.addAction(self.mainMenu.aPreferences) + + # Help self.addAction(self.mainMenu.aHelp) + return True def _setWindowTitle(self, projName=None):