Merge branch 'dev' into projsettings_update

This commit is contained in:
Veronica K. B. Olsen
2021-02-27 17:02:23 +01:00
17 changed files with 5282 additions and 418 deletions
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
ko_fi: jadzia626
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
# liberapay: # Replace with a single Liberapay username
# issuehunt: # Replace with a single IssueHunt username
# otechie: # Replace with a single Otechie username
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Python Setup - name: Python Setup
uses: actions/setup-python@v1 uses: actions/setup-python@v2
with: with:
python-version: 3 python-version: 3
architecture: x64 architecture: x64
+4853
View File
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -350,7 +350,7 @@
</message> </message>
<message> <message>
<location filename="../nw/gui/about.py" line="95"/> <location filename="../nw/gui/about.py" line="95"/>
<source>License</source> <source>Licence</source>
<translation>Licence</translation> <translation>Licence</translation>
</message> </message>
<message> <message>
@@ -380,7 +380,7 @@
</message> </message>
<message> <message>
<location filename="../nw/gui/about.py" line="146"/> <location filename="../nw/gui/about.py" line="146"/>
<source>See the License tab for the full license text, or visit the GNU website at {0} for more details.</source> <source>See the Licence tab for the full licence text, or visit the GNU website at {0} for more details.</source>
<translation>Ouvrez l&apos;onglet Licence pour voir le texte complet de la licence (en anglais), ou visitez le site de GNU à l&apos;adresse {0} pour en lire une traduction ou pour obtenir plus de détails.</translation> <translation>Ouvrez l&apos;onglet Licence pour voir le texte complet de la licence (en anglais), ou visitez le site de GNU à l&apos;adresse {0} pour en lire une traduction ou pour obtenir plus de détails.</translation>
</message> </message>
<message> <message>
@@ -400,7 +400,7 @@
</message> </message>
<message> <message>
<location filename="../nw/gui/about.py" line="198"/> <location filename="../nw/gui/about.py" line="198"/>
<source>License: {0}</source> <source>Licence: {0}</source>
<translation>Licence: {0}</translation> <translation>Licence: {0}</translation>
</message> </message>
<message> <message>
@@ -678,7 +678,7 @@
</message> </message>
<message> <message>
<location filename="../nw/gui/build.py" line="471"/> <location filename="../nw/gui/build.py" line="471"/>
<source>JSON + novelWriters Markdown (.json)</source> <source>JSON + novelWriter Markdown (.json)</source>
<translation>JSON + Markdown novelWriter (.json)</translation> <translation>JSON + Markdown novelWriter (.json)</translation>
</message> </message>
<message> <message>
@@ -1008,7 +1008,7 @@
</message> </message>
<message> <message>
<location filename="../nw/gui/doceditor.py" line="1332"/> <location filename="../nw/gui/doceditor.py" line="1332"/>
<source>Please selection some text before calling replace quotes.</source> <source>Please select some text before calling replace quotes.</source>
<translation>Veuillez sélectionner du texte avant de demander le remplacement des guillemets.</translation> <translation>Veuillez sélectionner du texte avant de demander le remplacement des guillemets.</translation>
</message> </message>
</context> </context>
@@ -2521,11 +2521,6 @@
<source>Writing Statistics</source> <source>Writing Statistics</source>
<translation>Statistiques d&apos;écriture</translation> <translation>Statistiques d&apos;écriture</translation>
</message> </message>
<message>
<location filename="../nw/gui/mainmenu.py" line="992"/>
<source>Show the writing statistics dialog</source>
<translation>Afficher le dialogue des statistiques d&apos;écriture</translation>
</message>
<message> <message>
<location filename="../nw/gui/mainmenu.py" line="999"/> <location filename="../nw/gui/mainmenu.py" line="999"/>
<source>Preferences</source> <source>Preferences</source>
@@ -2606,6 +2601,11 @@
<source>Open the novelWriter website at {0}</source> <source>Open the novelWriter website at {0}</source>
<translation>Ouvrir le site web de novelWriter à {0}</translation> <translation>Ouvrir le site web de novelWriter à {0}</translation>
</message> </message>
<message>
<location filename="../nw/gui/mainmenu.py" line="992"/>
<source>Show the writing statistics dialogue</source>
<translation>Afficher le dialogue des statistiques d&apos;écriture</translation>
</message>
</context> </context>
<context> <context>
<name>GuiMainStatus</name> <name>GuiMainStatus</name>
@@ -3283,7 +3283,7 @@
</message> </message>
<message> <message>
<location filename="../nw/gui/preferences.py" line="380"/> <location filename="../nw/gui/preferences.py" line="380"/>
<source>Can be overridden for individual projects in project settings.</source> <source>Can be overridden for individual projects in Project Settings.</source>
<translation>Peut être invalidé pour des projets spécifiques dans leurs paramètres.</translation> <translation>Peut être invalidé pour des projets spécifiques dans leurs paramètres.</translation>
</message> </message>
<message> <message>
@@ -4411,12 +4411,12 @@
</message> </message>
<message> <message>
<location filename="../nw/core/project.py" line="989"/> <location filename="../nw/core/project.py" line="989"/>
<source>You must set a valid backup path in preferences to use the automatic project backup feature.</source> <source>You must set a valid backup path in Preferences to use the automatic project backup feature.</source>
<translation>Vous devez spécifier un répertoire de sauvegarde valide dans les préférences du projet avant d&apos;utiliser la fonction de sauvegarde automatique.</translation> <translation>Vous devez spécifier un répertoire de sauvegarde valide dans les préférences du projet avant d&apos;utiliser la fonction de sauvegarde automatique.</translation>
</message> </message>
<message> <message>
<location filename="../nw/core/project.py" line="998"/> <location filename="../nw/core/project.py" line="998"/>
<source>You must set a valid project name in project settings to use the automatic project backup feature.</source> <source>You must set a valid project name in Project Settings to use the automatic project backup feature.</source>
<translation>Vous devez spécifier un titre de travail valide dans les préférences du projet avant d&apos;utiliser la fonction de sauvegarde automatique.</translation> <translation>Vous devez spécifier un titre de travail valide dans les préférences du projet avant d&apos;utiliser la fonction de sauvegarde automatique.</translation>
</message> </message>
<message> <message>
+179 -180
View File
File diff suppressed because it is too large Load Diff
+177 -178
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -32,6 +32,7 @@ SOURCES += \
nw/guimain.py nw/guimain.py
TRANSLATIONS += \ TRANSLATIONS += \
i18n/nw_en_US.ts \
i18n/nw_fr.ts \ i18n/nw_fr.ts \
i18n/nw_nb_NO.ts \ i18n/nw_nb_NO.ts \
i18n/nw_pt.ts i18n/nw_pt.ts
+1 -1
View File
@@ -137,7 +137,7 @@ def main(sysArgs=None):
"This program is distributed in the hope that it will be useful,\n" "This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n" "GNU General Public Licence for more details.\n"
"\n" "\n"
"Usage:\n" "Usage:\n"
" -h, --help Print this message.\n" " -h, --help Print this message.\n"
+14 -28
View File
@@ -318,8 +318,7 @@ class NWIndex():
nTitle = nLine nTitle = nLine
elif aLine.startswith("@"): elif aLine.startswith("@"):
self._indexNoteRef(tHandle, aLine, nLine, nTitle) self._indexKeyword(tHandle, aLine, nLine, nTitle, itemClass)
self._indexTag(tHandle, aLine, nLine, nTitle, itemClass)
elif aLine.startswith("%"): elif aLine.startswith("%"):
if nTitle > 0: if nTitle > 0:
@@ -460,41 +459,28 @@ class NWIndex():
self._noteIndex[tHandle][sTitle]["updated"] = round(time()) self._noteIndex[tHandle][sTitle]["updated"] = round(time())
return return
def _indexNoteRef(self, tHandle, aLine, nLine, nTitle): def _indexKeyword(self, tHandle, aLine, nLine, nTitle, itemClass):
"""Validate and save the information about a reference to a tag """Validate and save the information about a reference to a tag
in another file. in another file.
""" """
isValid, theBits, _ = self.scanThis(aLine) isValid, theBits, _ = self.scanThis(aLine)
if not isValid or len(theBits) == 0: if not isValid or len(theBits) < 2:
return False logger.warning("Skipping keyword with %d value(s) in %s" % (len(theBits), tHandle))
return
sTitle = "T%06d" % nTitle
if sTitle not in self._refIndex[tHandle]:
return False
if theBits[0] == nwKeyWords.TAG_KEY:
return False
if theBits[0] not in nwKeyWords.VALID_KEYS: if theBits[0] not in nwKeyWords.VALID_KEYS:
return False logger.warning("Skipping invalid keyword '%s' in %s" % (theBits[0], tHandle))
return
for aVal in theBits[1:]:
self._refIndex[tHandle][sTitle]["tags"].append([nLine, theBits[0], aVal])
return True
def _indexTag(self, tHandle, aLine, nLine, nTitle, itemClass):
"""Validate and save the information from a tag.
"""
isValid, theBits, thePos = self.scanThis(aLine)
if not isValid or len(theBits) != 2:
return False
sTitle = "T%06d" % nTitle
if theBits[0] == nwKeyWords.TAG_KEY: if theBits[0] == nwKeyWords.TAG_KEY:
sTitle = "T%06d" % nTitle
self._tagIndex[theBits[1]] = [nLine, tHandle, itemClass.name, sTitle] self._tagIndex[theBits[1]] = [nLine, tHandle, itemClass.name, sTitle]
return True elif sTitle in self._refIndex[tHandle]:
for aVal in theBits[1:]:
self._refIndex[tHandle][sTitle]["tags"].append([nLine, theBits[0], aVal])
return
## ##
# Check @ Lines # Check @ Lines
@@ -714,7 +700,7 @@ class NWIndex():
for refTitle in self._refIndex[tHandle]: for refTitle in self._refIndex[tHandle]:
for aTag in self._refIndex[tHandle][refTitle].get("tags", []): for aTag in self._refIndex[tHandle][refTitle].get("tags", []):
if len(aTag) == 3 and (sTitle is None or sTitle == refTitle): if len(aTag) == 3 and (sTitle is None or sTitle == refTitle):
if aTag[1] in theRefs: # Future-compatible. Check can be removed in 1.2. if aTag[1] in theRefs:
theRefs[aTag[1]].append(aTag[2]) theRefs[aTag[1]].append(aTag[2])
return theRefs return theRefs
+2 -2
View File
@@ -988,7 +988,7 @@ class NWProject():
if not os.path.isdir(self.mainConf.backupPath): if not os.path.isdir(self.mainConf.backupPath):
self.theParent.makeAlert( self.theParent.makeAlert(
self.tr( self.tr(
"You must set a valid backup path in preferences to use " "You must set a valid backup path in Preferences to use "
"the automatic project backup feature." "the automatic project backup feature."
), nwAlert.WARN ), nwAlert.WARN
) )
@@ -997,7 +997,7 @@ class NWProject():
if self.projName == "": if self.projName == "":
self.theParent.makeAlert( self.theParent.makeAlert(
self.tr( self.tr(
"You must set a valid project name in project settings to " "You must set a valid project name in Project Settings to "
"use the automatic project backup feature." "use the automatic project backup feature."
), nwAlert.WARN ), nwAlert.WARN
) )
+7 -7
View File
@@ -92,7 +92,7 @@ class GuiAbout(QDialog):
self.tabBox = QTabWidget() self.tabBox = QTabWidget()
self.tabBox.addTab(self.pageAbout, self.tr("About")) self.tabBox.addTab(self.pageAbout, self.tr("About"))
self.tabBox.addTab(self.pageNotes, self.tr("Release")) self.tabBox.addTab(self.pageNotes, self.tr("Release"))
self.tabBox.addTab(self.pageLicense, self.tr("License")) self.tabBox.addTab(self.pageLicense, self.tr("Licence"))
self.innerBox.addWidget(self.tabBox) self.innerBox.addWidget(self.tabBox)
# OK Button # OK Button
@@ -165,7 +165,7 @@ class GuiAbout(QDialog):
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
), ),
license3 = self.tr( license3 = self.tr(
"See the License tab for the full license text, or visit the " "See the Licence tab for the full licence text, or visit the "
"GNU website at {0} for more details." "GNU website at {0} for more details."
).format( ).format(
"<a href='https://www.gnu.org/licenses/gpl-3.0.html'>GPL v3.0</a>" "<a href='https://www.gnu.org/licenses/gpl-3.0.html'>GPL v3.0</a>"
@@ -179,7 +179,7 @@ class GuiAbout(QDialog):
self.tr("Theme: {0}").format(theTheme.themeName), self.tr("Theme: {0}").format(theTheme.themeName),
self.tr("Author: {0}").format(theTheme.themeAuthor), self.tr("Author: {0}").format(theTheme.themeAuthor),
self.tr("Credit: {0}").format(theTheme.themeCredit), self.tr("Credit: {0}").format(theTheme.themeCredit),
self.tr("License: {0}").format( self.tr("Licence: {0}").format(
f"<a href='{theTheme.themeLicenseUrl}'>{theTheme.themeLicense}</a>" f"<a href='{theTheme.themeLicenseUrl}'>{theTheme.themeLicense}</a>"
) )
) )
@@ -189,7 +189,7 @@ class GuiAbout(QDialog):
self.tr("Icons: {0}").format(theIcons.themeName), self.tr("Icons: {0}").format(theIcons.themeName),
self.tr("Author: {0}").format(theIcons.themeAuthor), self.tr("Author: {0}").format(theIcons.themeAuthor),
self.tr("Credit: {0}").format(theIcons.themeCredit), self.tr("Credit: {0}").format(theIcons.themeCredit),
self.tr("License: {0}").format( self.tr("Licence: {0}").format(
f"<a href='{theIcons.themeLicenseUrl}'>{theIcons.themeLicense}</a>" f"<a href='{theIcons.themeLicenseUrl}'>{theIcons.themeLicense}</a>"
) )
) )
@@ -199,7 +199,7 @@ class GuiAbout(QDialog):
self.tr("Syntax: {0}").format(theTheme.syntaxName), self.tr("Syntax: {0}").format(theTheme.syntaxName),
self.tr("Author: {0}").format(theTheme.syntaxAuthor), self.tr("Author: {0}").format(theTheme.syntaxAuthor),
self.tr("Credit: {0}").format(theTheme.syntaxCredit), self.tr("Credit: {0}").format(theTheme.syntaxCredit),
self.tr("License: {0}").format( self.tr("Licence: {0}").format(
f"<a href='{theTheme.syntaxLicenseUrl}'>{theTheme.syntaxLicense}</a>" f"<a href='{theTheme.syntaxLicenseUrl}'>{theTheme.syntaxLicense}</a>"
) )
) )
@@ -221,7 +221,7 @@ class GuiAbout(QDialog):
return return
def _fillLicensePage(self): def _fillLicensePage(self):
"""Load the content for the License page. """Load the content for the Licence page.
""" """
docPath = os.path.join(self.mainConf.assetPath, "text", "gplv3_en.htm") docPath = os.path.join(self.mainConf.assetPath, "text", "gplv3_en.htm")
if os.path.isfile(docPath): if os.path.isfile(docPath):
@@ -229,7 +229,7 @@ class GuiAbout(QDialog):
helpText = inFile.read() helpText = inFile.read()
self.pageLicense.setHtml(helpText) self.pageLicense.setHtml(helpText)
else: else:
self.pageLicense.setHtml("Error loading license text ...") self.pageLicense.setHtml("Error loading licence text ...")
return return
def _setStyleSheet(self): def _setStyleSheet(self):
+1 -1
View File
@@ -468,7 +468,7 @@ class GuiBuildNovel(QDialog):
self.saveJsonH.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_H)) self.saveJsonH.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_H))
self.saveMenu.addAction(self.saveJsonH) self.saveMenu.addAction(self.saveJsonH)
self.saveJsonM = QAction(self.tr("JSON + novelWriters Markdown (.json)"), self) self.saveJsonM = QAction(self.tr("JSON + novelWriter Markdown (.json)"), self)
self.saveJsonM.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_M)) self.saveJsonM.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_M))
self.saveMenu.addAction(self.saveJsonM) self.saveMenu.addAction(self.saveJsonM)
+1 -1
View File
@@ -1330,7 +1330,7 @@ class GuiDocEditor(QTextEdit):
else: else:
self.theParent.makeAlert( self.theParent.makeAlert(
self.tr("Please selection some text before calling replace quotes."), self.tr("Please select some text before calling replace quotes."),
nwAlert.ERROR nwAlert.ERROR
) )
+1 -1
View File
@@ -989,7 +989,7 @@ class GuiMainMenu(QMenuBar):
# Tools > Writing Stats # Tools > Writing Stats
self.aWritingStats = QAction(self.tr("Writing Statistics"), self) self.aWritingStats = QAction(self.tr("Writing Statistics"), self)
self.aWritingStats.setStatusTip(self.tr("Show the writing statistics dialog")) self.aWritingStats.setStatusTip(self.tr("Show the writing statistics dialogue"))
self.aWritingStats.setShortcut("F6") self.aWritingStats.setShortcut("F6")
self.aWritingStats.triggered.connect(lambda: self.theParent.showWritingStatsDialog()) self.aWritingStats.triggered.connect(lambda: self.theParent.showWritingStatsDialog())
self.toolsMenu.addAction(self.aWritingStats) self.toolsMenu.addAction(self.aWritingStats)
+1 -1
View File
@@ -380,7 +380,7 @@ class GuiPreferencesProjects(QWidget):
self.mainForm.addRow( self.mainForm.addRow(
self.tr("Run backup when the project is closed"), self.tr("Run backup when the project is closed"),
self.backupOnClose, self.backupOnClose,
self.tr("Can be overridden for individual projects in project settings.") self.tr("Can be overridden for individual projects in Project Settings.")
) )
## Ask before backup ## Ask before backup
+17 -3
View File
@@ -195,14 +195,28 @@ def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
assert theIndex.scanText(cHandle, ( assert theIndex.scanText(cHandle, (
"# Jane Smith\n" "# Jane Smith\n"
"@tag: Jane" "@tag: Jane\n"
"@tag:\n"
"@:\n"
)) ))
assert theIndex.scanText(nHandle, ( assert theIndex.scanText(nHandle, (
"# Hello World!\n" "# Hello World!\n"
"@pov: Jane" "@pov: Jane\n"
"@invalid: John\n" # Checks for issue #688
)) ))
assert theIndex._tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]} assert theIndex._tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]}
assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!" assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!"
assert theIndex.getReferences(nHandle, "T000001") == {
"@char": [],
"@custom": [],
"@entity": [],
"@focus": [],
"@location": [],
"@object": [],
"@plot": [],
"@pov": ["Jane"],
"@time": []
}
assert theIndex.novelChangedSince(0) assert theIndex.novelChangedSince(0)
assert theIndex.notesChangedSince(0) assert theIndex.notesChangedSince(0)
@@ -278,7 +292,7 @@ def testCoreIndex_ScanText(nwMinimal, dummyGUI):
"This is a story about Jane Smith.\n\n" "This is a story about Jane Smith.\n\n"
"Well, not really.\n" "Well, not really.\n"
)) ))
assert str(theIndex._tagIndex) == "{'Jane': [2, '%s', 'CHARACTER', 'T000001']}" % cHandle assert theIndex._tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]}
assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!" assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!"
# Check that title sections are indexed properly # Check that title sections are indexed properly
+1 -1
View File
@@ -55,7 +55,7 @@ def testGuiAbout_Dialog(qtbot, monkeypatch, nwGUI):
assert msgAbout.pageNotes.toPlainText() == "Error loading release notes text ..." assert msgAbout.pageNotes.toPlainText() == "Error loading release notes text ..."
msgAbout._fillLicensePage() msgAbout._fillLicensePage()
assert msgAbout.pageLicense.toPlainText() == "Error loading license text ..." assert msgAbout.pageLicense.toPlainText() == "Error loading licence text ..."
msgAbout.showReleaseNotes() msgAbout.showReleaseNotes()
assert msgAbout.tabBox.currentWidget() == msgAbout.pageNotes assert msgAbout.tabBox.currentWidget() == msgAbout.pageNotes