diff --git a/README.md b/README.md index a56418a3..3c60e951 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ -novelWriter is a markdown-like text editor designed for writing novels and larger projects of many -smaller plain text documents. It uses its own flavour of markdown that supports a meta data syntax +novelWriter is a Markdown-like text editor designed for writing novels and larger projects of many +smaller plain text documents. It uses its own flavour of Markdown that supports a meta data syntax for comments, synopsis and cross-referencing between files. It's designed to be a simple text editor -which allows for easy organisation of text files and notes, built on plain text files for +that allows for easy organisation of text files and notes, built on plain text files for robustness. The plain text storage is suitable for version control software, and also well suited for file @@ -29,7 +29,7 @@ The contributing guide is available in [CONTRIBUTING](CONTRIBUTING.md). The default branch on this repository switched to `main` on 6. August 2020. If you are running novelWriter from a git clone, you need to clone the repository again. -Alternatively, you can run the following to get back on the main branch: +Alternatively, you can run the following to get back on the new default branch: ```bash git remote update @@ -48,8 +48,8 @@ novelWriter is in a _beta_ state. Please report any issues you may encounter in tracker. You should be able to use novelWriter for real projects, but as with all software, please make -regular backups. There is a built in backup feature that can pack the entire project into a zip file -on close. Please check the documentation for further details. +regular backups of your data. There is a built in backup feature that can pack the entire project +into a zip file on close. Please check the documentation for further details. ## License @@ -80,7 +80,7 @@ novelWriter is _not_ a full-feature Markdown editor. It allows for a minimal set needed for writing text documents for novels. These are currently limited to: * Headings level 1 to 4 using the `#` syntax only. -* Emphasised, strong text. These are rendered as italicised and bold. +* Emphasised and strong text. These are rendered as italicised and bold. * Strikethrough text. * Hard line breaks using two or more spaces at the end of a line. @@ -98,21 +98,24 @@ In addition, novelWriter adds the following, which is otherwise not supported by version, non-breaking spaces are converted to normal spaces when saving the document. This is done by the Qt library. * Thin spaces are also supported, as well as non-breaking thin spaces. -* Tabs may be rendered, depending on export format. With Qt 5.10 or higher, the width of a tab in - pixels can be changed in Preferences. +* Tabs can be used in the text, and should be properly aligned. The width of a tab in pixels can be + changed in Preferences. Note that tabs are exported as-is, also to HTML format. However, most + browsers will treat a tab as a space, so it may not show up like expected if you view the exported + HTML file. The core export format of novelWriter is HTML5. You can also export the entire project as a single -novelWriter flavour document. In addition, other exports to Open Document, PDF, and plain text is -offered through the Qt library, although with limitations to formatting. +novelWriter Markdown-flavour document. In addition, other exports to Open Document, PDF, and plain +text is offered through the Qt library, although with limitations to formatting. ## Implementation The application is written in Python3 using Qt5 via PyQt5. It is developed on Linux, but it should -in principle work fine on other operating systems as well as long as dependencies are met. It is +in principle work fine on other operating systems as well, as long as dependencies are met. It is regularly tested on Windows 10. -The application can be started from the source folder with one of the commands: +The application can be started from the source folder with one of the commands, depending on your +Python configuration: ``` ./novelWriter.py python novelWriter.py @@ -158,7 +161,7 @@ pip install pyenchant ``` PyQt/Qt should be at least 5.3, but ideally 5.10 or higher for nearly all features to work. -Exporting to markdown requires PyQt/Qt 5.14. There are no known minimum for `lxml`, but the code +Exporting to Markdown requires PyQt/Qt 5.14. There are no known minimum for `lxml`, but the code was originally written with 4.2. The optional spell check library must be at least 3.0.0 to work with Windows 64 bit systems. On Linux, 2.0.0 also works fine. @@ -237,7 +240,7 @@ clickable in the document view pane, and control-clickable in the editor. They m quickly navigate between the documents while editing. -## Contribution +## Contributing If you want to contribute to novelWriter, please follow the coding convention laid out in the [Style Guide](markdown/style.md). They broadly follow Python PEP8, but there are a few diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index 74c5a676..dadf55bd 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -45,10 +45,9 @@ class ToHtml(Tokenizer): self.cssStyles = True self.repDict = { - "<" : "<", - ">" : ">", - "&" : "&", - "\t" : " "*2, + "<" : "<", + ">" : ">", + "&" : "&", nwUnicode.U_ENDASH : nwUnicode.H_ENDASH, nwUnicode.U_EMDASH : nwUnicode.H_EMDASH, nwUnicode.U_HELLIP : nwUnicode.H_HELLIP, @@ -77,7 +76,6 @@ class ToHtml(Tokenizer): self.doKeywords = True self.doComments = doComments self.doSynopsis = doSynopsis - self.repDict["\t"] = " "*8 self._buildRegEx() return diff --git a/nw/gui/build.py b/nw/gui/build.py index 81af3fa4..b61dfd0f 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -551,7 +551,6 @@ class GuiBuildNovel(QDialog): isNone |= theItem.itemClass == nwItemClass.NO_CLASS isNone |= theItem.itemClass == nwItemClass.TRASH isNone |= theItem.parHandle == self.theProject.projTree.trashRoot() - isNone |= theItem.parHandle == self.theProject.projTree.archiveRoot() isNone |= theItem.parHandle is None isNote = theItem.itemLayout == nwItemLayout.NOTE isNovel = not isNone and not isNote @@ -563,6 +562,10 @@ class GuiBuildNovel(QDialog): if isNovel and not novelFiles: return False + rootItem = self.theProject.projTree.getRootItem(theItem.itemHandle) + if rootItem.itemClass == nwItemClass.ARCHIVE: + return False + return True def _saveDocument(self, theFormat): @@ -1014,13 +1017,30 @@ class GuiBuildNovelDocView(QTextBrowser): theText = "".join(theText) self.buildTime = timeStamp + sPos = self.verticalScrollBar().value() - theText = theText.replace(" ", " "*4) + # Refresh the tab stops + if self.mainConf.verQtValue >= 51000: + self.setTabStopDistance(self.mainConf.getTabWidth()) + else: + self.setTabStopWidth(self.mainConf.getTabWidth()) + + theText = theText.replace("\t", "!!tab!!") theText = theText.replace("", "") theText = theText.replace("", "") self.setHtml(theText) + + while self.find("!!tab!!"): + theCursor = self.textCursor() + theCursor.insertText("\t") + + self.verticalScrollBar().setValue(sPos) self._updateBuildAge() + # Since we change the content while it may still be rendering, we mark + # the document dirty again to make sure it's re-rendered properly. + self.qDocument.markContentsDirty(0, self.qDocument.characterCount()) + return def setStyleSheet(self, theStyles=[]): diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 737758bf..4cecfc7a 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -206,8 +206,6 @@ class GuiDocEditor(QTextEdit): # Also set the document text options for the document text flow theOpt = QTextOption() - if self.mainConf.verQtValue >= 51000: - theOpt.setTabStopDistance(self.mainConf.getTabWidth()) if self.mainConf.doJustify: theOpt.setAlignment(Qt.AlignJustify) if self.mainConf.showTabsNSpaces: @@ -288,6 +286,12 @@ class GuiDocEditor(QTextEdit): self.hLight.spellCheck = spTemp qApp.restoreOverrideCursor() + # Refresh the tab stops + if self.mainConf.verQtValue >= 51000: + self.setTabStopDistance(self.mainConf.getTabWidth()) + else: + self.setTabStopWidth(self.mainConf.getTabWidth()) + return True def replaceText(self, theText): diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index 13707ae9..f06eff22 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -156,7 +156,20 @@ class GuiDocViewer(QTextBrowser): self.setText("An error occurred while generating the preview.") return False - self.setHtml(aDoc.theResult) + # Refresh the tab stops + if self.mainConf.verQtValue >= 51000: + self.setTabStopDistance(self.mainConf.getTabWidth()) + else: + self.setTabStopWidth(self.mainConf.getTabWidth()) + + self.setHtml(aDoc.theResult.replace("\t", "!!tab!!")) + + # Loop through the text and put back in the tabs. Tabs are removed by + # the setHtml function, so the ToHtml class puts in a placeholder. + while self.find("!!tab!!"): + theCursor = self.textCursor() + theCursor.insertText("\t") + if self.theHandle == tHandle: self.verticalScrollBar().setValue(sPos) self.theHandle = tHandle @@ -167,6 +180,10 @@ class GuiDocViewer(QTextBrowser): # Make sure the main GUI knows we changed the content self.theParent.viewMeta.refreshReferences(tHandle) + # Since we change the content while it may still be rendering, we mark + # the document dirty again to make sure it's re-rendered properly. + self.qDocument.markContentsDirty(0, self.qDocument.characterCount()) + return True def reloadText(self): @@ -188,7 +205,7 @@ class GuiDocViewer(QTextBrowser): "exist, or the index is out of date. The index can be updated " "from the Tools menu, or by pressing F9." ) % theTag, nwAlert.ERROR) - return + return False else: # Let the parent handle the opening as it also ensures that # the doc view panel is visible in case this request comes @@ -357,8 +374,6 @@ class GuiDocViewer(QTextBrowser): "}}\n" ".comment {{" " color: rgb({cColR},{cColG},{cColB});" - " margin-left: 1em;" - " margin-right: 1em;" "}}\n" ".synopsis {{" " color: rgb({mColR},{mColG},{mColB});"