From 1d9cd3ee49cfee756476610a8ddd0c82786829bb Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Mon, 17 Aug 2020 22:39:38 +0200
Subject: [PATCH 1/7] Add a workaround for getting tabs through the html
parsing and setHtml function in QTextBrowser
---
nw/core/tohtml.py | 2 +-
nw/gui/docviewer.py | 12 ++++++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py
index 74c5a676..0c6199c2 100644
--- a/nw/core/tohtml.py
+++ b/nw/core/tohtml.py
@@ -77,7 +77,7 @@ class ToHtml(Tokenizer):
self.doKeywords = True
self.doComments = doComments
self.doSynopsis = doSynopsis
- self.repDict["\t"] = " "*8
+ self.repDict["\t"] = r"!!tab!!" # The viewer replaces these
self._buildRegEx()
return
diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py
index 13707ae9..3c7c9795 100644
--- a/nw/gui/docviewer.py
+++ b/nw/gui/docviewer.py
@@ -167,6 +167,18 @@ class GuiDocViewer(QTextBrowser):
# Make sure the main GUI knows we changed the content
self.theParent.viewMeta.refreshReferences(tHandle)
+ # 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")
+
+ # Refresh the tab stops
+ if self.mainConf.verQtValue >= 51000:
+ self.setTabStopDistance(self.mainConf.getTabWidth())
+ else:
+ self.setTabStopWidth(self.mainConf.getTabWidth())
+
return True
def reloadText(self):
From 10936b69102f6a62528e1adc03b3f4fa633d3f3b Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Mon, 17 Aug 2020 22:40:04 +0200
Subject: [PATCH 2/7] Implement the same tab stop code in the editor as for the
viewer
---
nw/gui/doceditor.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
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):
From 2f177c50fcdf107bb72eaa98e3d2988af78fd3ee Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Tue, 18 Aug 2020 00:21:25 +0200
Subject: [PATCH 3/7] Improved the tab handling and added the same fix to the
build tool
---
nw/core/tohtml.py | 4 ++--
nw/gui/build.py | 27 +++++++++++++++++++++++++--
nw/gui/docviewer.py | 34 ++++++++++++++++++++--------------
3 files changed, 47 insertions(+), 18 deletions(-)
diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py
index 0c6199c2..bfb60984 100644
--- a/nw/core/tohtml.py
+++ b/nw/core/tohtml.py
@@ -48,7 +48,7 @@ class ToHtml(Tokenizer):
"<" : "<",
">" : ">",
"&" : "&",
- "\t" : " "*2,
+ # "\t" : "!!tab!!", # " "*2,
nwUnicode.U_ENDASH : nwUnicode.H_ENDASH,
nwUnicode.U_EMDASH : nwUnicode.H_EMDASH,
nwUnicode.U_HELLIP : nwUnicode.H_HELLIP,
@@ -77,7 +77,7 @@ class ToHtml(Tokenizer):
self.doKeywords = True
self.doComments = doComments
self.doSynopsis = doSynopsis
- self.repDict["\t"] = r"!!tab!!" # The viewer replaces these
+ # self.repDict["\t"] = r"!!tab!!" # The viewer replaces these
self._buildRegEx()
return
diff --git a/nw/gui/build.py b/nw/gui/build.py
index 81af3fa4..c13a3911 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,33 @@ 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()
+ # This forces a repaint of the text, It's a hack to fix an occational
+ # issue where the find/replace above interfers with the rendering and
+ # leaves parts of the document blank.
+ textWidth = self.qDocument.textWidth()
+ self.qDocument.setTextWidth(textWidth - 5)
+ self.qDocument.setTextWidth(textWidth)
+
return
def setStyleSheet(self, theStyles=[]):
diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py
index 3c7c9795..0f7e81cd 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,17 +180,12 @@ class GuiDocViewer(QTextBrowser):
# Make sure the main GUI knows we changed the content
self.theParent.viewMeta.refreshReferences(tHandle)
- # 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")
-
- # Refresh the tab stops
- if self.mainConf.verQtValue >= 51000:
- self.setTabStopDistance(self.mainConf.getTabWidth())
- else:
- self.setTabStopWidth(self.mainConf.getTabWidth())
+ # This forces a repaint of the text, It's a hack to fix an occational
+ # issue where the find/replace above interfers with the rendering and
+ # leaves parts of the document blank.
+ textWidth = self.qDocument.textWidth()
+ self.qDocument.setTextWidth(textWidth - 5)
+ self.qDocument.setTextWidth(textWidth)
return True
@@ -369,8 +377,6 @@ class GuiDocViewer(QTextBrowser):
"}}\n"
".comment {{"
" color: rgb({cColR},{cColG},{cColB});"
- " margin-left: 1em;"
- " margin-right: 1em;"
"}}\n"
".synopsis {{"
" color: rgb({mColR},{mColG},{mColB});"
From a9ac3a2aa1cd6733d5ca0b6e7d5f4d8a8cedc935 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Tue, 18 Aug 2020 00:29:18 +0200
Subject: [PATCH 4/7] Removed commented out code
---
nw/core/tohtml.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py
index bfb60984..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" : "!!tab!!", # " "*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"] = r"!!tab!!" # The viewer replaces these
self._buildRegEx()
return
From b80ff750fbcde2009557b2fc246ddcdd9c8fca2d Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Tue, 18 Aug 2020 00:33:15 +0200
Subject: [PATCH 5/7] A bit less aggressive version off the redraw text hack
---
nw/gui/build.py | 4 +---
nw/gui/docviewer.py | 4 +---
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/nw/gui/build.py b/nw/gui/build.py
index c13a3911..0370e23c 100644
--- a/nw/gui/build.py
+++ b/nw/gui/build.py
@@ -1040,9 +1040,7 @@ class GuiBuildNovelDocView(QTextBrowser):
# This forces a repaint of the text, It's a hack to fix an occational
# issue where the find/replace above interfers with the rendering and
# leaves parts of the document blank.
- textWidth = self.qDocument.textWidth()
- self.qDocument.setTextWidth(textWidth - 5)
- self.qDocument.setTextWidth(textWidth)
+ self.qDocument.setTextWidth(self.qDocument.textWidth())
return
diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py
index 0f7e81cd..63a41e9b 100644
--- a/nw/gui/docviewer.py
+++ b/nw/gui/docviewer.py
@@ -183,9 +183,7 @@ class GuiDocViewer(QTextBrowser):
# This forces a repaint of the text, It's a hack to fix an occational
# issue where the find/replace above interfers with the rendering and
# leaves parts of the document blank.
- textWidth = self.qDocument.textWidth()
- self.qDocument.setTextWidth(textWidth - 5)
- self.qDocument.setTextWidth(textWidth)
+ self.qDocument.setTextWidth(self.qDocument.textWidth())
return True
From 4a20a0eb017493b862be95cdebb2a701fb0f1971 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Tue, 18 Aug 2020 08:50:08 +0200
Subject: [PATCH 6/7] Use markContentsDirty on the QTextDocument instead of the
textWidth hack
---
nw/gui/build.py | 7 +++----
nw/gui/docviewer.py | 7 +++----
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/nw/gui/build.py b/nw/gui/build.py
index 0370e23c..b61dfd0f 100644
--- a/nw/gui/build.py
+++ b/nw/gui/build.py
@@ -1037,10 +1037,9 @@ class GuiBuildNovelDocView(QTextBrowser):
self.verticalScrollBar().setValue(sPos)
self._updateBuildAge()
- # This forces a repaint of the text, It's a hack to fix an occational
- # issue where the find/replace above interfers with the rendering and
- # leaves parts of the document blank.
- self.qDocument.setTextWidth(self.qDocument.textWidth())
+ # 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
diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py
index 63a41e9b..b613ac2f 100644
--- a/nw/gui/docviewer.py
+++ b/nw/gui/docviewer.py
@@ -180,10 +180,9 @@ class GuiDocViewer(QTextBrowser):
# Make sure the main GUI knows we changed the content
self.theParent.viewMeta.refreshReferences(tHandle)
- # This forces a repaint of the text, It's a hack to fix an occational
- # issue where the find/replace above interfers with the rendering and
- # leaves parts of the document blank.
- self.qDocument.setTextWidth(self.qDocument.textWidth())
+ # 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
From 95dd867d47e661f44b9c58e6a5c02f22d8dc524e Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Tue, 18 Aug 2020 20:11:57 +0200
Subject: [PATCH 7/7] Bring the readme file up to date
---
README.md | 33 ++++++++++++++++++---------------
nw/gui/docviewer.py | 2 +-
2 files changed, 19 insertions(+), 16 deletions(-)
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/gui/docviewer.py b/nw/gui/docviewer.py
index b613ac2f..f06eff22 100644
--- a/nw/gui/docviewer.py
+++ b/nw/gui/docviewer.py
@@ -205,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