Rewrap self.tr and fix a few bugs

This commit is contained in:
Veronica K. B. Olsen
2021-02-15 16:47:57 +01:00
parent dba8865d75
commit df19ced50c
28 changed files with 682 additions and 516 deletions
+3 -2
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from nw.constants.constants import (
nwConst, nwLists, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes,
nwUnicode, nwHtmlUnicode
trConst, nwConst, nwLists, nwRegEx, nwFiles, nwKeyWords, nwLabels,
nwQuotes, nwUnicode, nwHtmlUnicode
)
from nw.constants.enum import (
nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline,
@@ -9,6 +9,7 @@ from nw.constants.enum import (
)
__all__ = [
"trConst",
"nwConst",
"nwLists",
"nwRegEx",
+66 -60
View File
@@ -24,12 +24,18 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from PyQt5.QtCore import QT_TRANSLATE_NOOP
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtCore import QT_TRANSLATE_NOOP as QT_TRN
from nw.constants.enum import (
nwItemClass, nwItemLayout, nwItemType, nwOutline
)
def trConst(tString):
"""Wrapper function for locally translating constants.
"""
return QCoreApplication.translate("Constant", tString)
class nwConst():
# Date and Time Formats
@@ -121,17 +127,17 @@ class nwKeyWords:
class nwLabels():
CLASS_NAME = {
nwItemClass.NO_CLASS : QT_TRANSLATE_NOOP("Constant", "None"),
nwItemClass.NOVEL : QT_TRANSLATE_NOOP("Constant", "Novel"),
nwItemClass.PLOT : QT_TRANSLATE_NOOP("Constant", "Plot"),
nwItemClass.CHARACTER : QT_TRANSLATE_NOOP("Constant", "Characters"),
nwItemClass.WORLD : QT_TRANSLATE_NOOP("Constant", "Locations"),
nwItemClass.TIMELINE : QT_TRANSLATE_NOOP("Constant", "Timeline"),
nwItemClass.OBJECT : QT_TRANSLATE_NOOP("Constant", "Objects"),
nwItemClass.ENTITY : QT_TRANSLATE_NOOP("Constant", "Entity"),
nwItemClass.CUSTOM : QT_TRANSLATE_NOOP("Constant", "Custom"),
nwItemClass.ARCHIVE : QT_TRANSLATE_NOOP("Constant", "Outtakes"),
nwItemClass.TRASH : QT_TRANSLATE_NOOP("Constant", "Trash"),
nwItemClass.NO_CLASS : QT_TRN("Constant", "None"),
nwItemClass.NOVEL : QT_TRN("Constant", "Novel"),
nwItemClass.PLOT : QT_TRN("Constant", "Plot"),
nwItemClass.CHARACTER : QT_TRN("Constant", "Characters"),
nwItemClass.WORLD : QT_TRN("Constant", "Locations"),
nwItemClass.TIMELINE : QT_TRN("Constant", "Timeline"),
nwItemClass.OBJECT : QT_TRN("Constant", "Objects"),
nwItemClass.ENTITY : QT_TRN("Constant", "Entity"),
nwItemClass.CUSTOM : QT_TRN("Constant", "Custom"),
nwItemClass.ARCHIVE : QT_TRN("Constant", "Outtakes"),
nwItemClass.TRASH : QT_TRN("Constant", "Trash"),
}
CLASS_FLAG = {
nwItemClass.NO_CLASS : "0",
@@ -160,15 +166,15 @@ class nwLabels():
nwItemClass.TRASH : "cls_trash",
}
LAYOUT_NAME = {
nwItemLayout.NO_LAYOUT : QT_TRANSLATE_NOOP("Constant", "None"),
nwItemLayout.TITLE : QT_TRANSLATE_NOOP("Constant", "Title Page"),
nwItemLayout.BOOK : QT_TRANSLATE_NOOP("Constant", "Book"),
nwItemLayout.PAGE : QT_TRANSLATE_NOOP("Constant", "Plain Page"),
nwItemLayout.PARTITION : QT_TRANSLATE_NOOP("Constant", "Partition"),
nwItemLayout.UNNUMBERED : QT_TRANSLATE_NOOP("Constant", "Unnumbered"),
nwItemLayout.CHAPTER : QT_TRANSLATE_NOOP("Constant", "Chapter"),
nwItemLayout.SCENE : QT_TRANSLATE_NOOP("Constant", "Scene"),
nwItemLayout.NOTE : QT_TRANSLATE_NOOP("Constant", "Note"),
nwItemLayout.NO_LAYOUT : QT_TRN("Constant", "None"),
nwItemLayout.TITLE : QT_TRN("Constant", "Title Page"),
nwItemLayout.BOOK : QT_TRN("Constant", "Book"),
nwItemLayout.PAGE : QT_TRN("Constant", "Plain Page"),
nwItemLayout.PARTITION : QT_TRN("Constant", "Partition"),
nwItemLayout.UNNUMBERED : QT_TRN("Constant", "Unnumbered"),
nwItemLayout.CHAPTER : QT_TRN("Constant", "Chapter"),
nwItemLayout.SCENE : QT_TRN("Constant", "Scene"),
nwItemLayout.NOTE : QT_TRN("Constant", "Note"),
}
LAYOUT_FLAG = {
nwItemLayout.NO_LAYOUT : "Xo",
@@ -182,27 +188,27 @@ class nwLabels():
nwItemLayout.NOTE : "Nt",
}
KEY_NAME = {
nwKeyWords.TAG_KEY : QT_TRANSLATE_NOOP("Constant", "Tag"),
nwKeyWords.POV_KEY : QT_TRANSLATE_NOOP("Constant", "Point of View"),
nwKeyWords.FOCUS_KEY : QT_TRANSLATE_NOOP("Constant", "Focus"),
nwKeyWords.CHAR_KEY : QT_TRANSLATE_NOOP("Constant", "Characters"),
nwKeyWords.PLOT_KEY : QT_TRANSLATE_NOOP("Constant", "Plot"),
nwKeyWords.TIME_KEY : QT_TRANSLATE_NOOP("Constant", "Timeline"),
nwKeyWords.WORLD_KEY : QT_TRANSLATE_NOOP("Constant", "Locations"),
nwKeyWords.OBJECT_KEY : QT_TRANSLATE_NOOP("Constant", "Objects"),
nwKeyWords.ENTITY_KEY : QT_TRANSLATE_NOOP("Constant", "Entities"),
nwKeyWords.CUSTOM_KEY : QT_TRANSLATE_NOOP("Constant", "Custom"),
nwKeyWords.TAG_KEY : QT_TRN("Constant", "Tag"),
nwKeyWords.POV_KEY : QT_TRN("Constant", "Point of View"),
nwKeyWords.FOCUS_KEY : QT_TRN("Constant", "Focus"),
nwKeyWords.CHAR_KEY : QT_TRN("Constant", "Characters"),
nwKeyWords.PLOT_KEY : QT_TRN("Constant", "Plot"),
nwKeyWords.TIME_KEY : QT_TRN("Constant", "Timeline"),
nwKeyWords.WORLD_KEY : QT_TRN("Constant", "Locations"),
nwKeyWords.OBJECT_KEY : QT_TRN("Constant", "Objects"),
nwKeyWords.ENTITY_KEY : QT_TRN("Constant", "Entities"),
nwKeyWords.CUSTOM_KEY : QT_TRN("Constant", "Custom"),
}
OUTLINE_COLS = {
nwOutline.TITLE : QT_TRANSLATE_NOOP("Constant", "Title"),
nwOutline.LEVEL : QT_TRANSLATE_NOOP("Constant", "Level"),
nwOutline.LABEL : QT_TRANSLATE_NOOP("Constant", "Document"),
nwOutline.LINE : QT_TRANSLATE_NOOP("Constant", "Line"),
nwOutline.CCOUNT : QT_TRANSLATE_NOOP("Constant", "Chars"),
nwOutline.WCOUNT : QT_TRANSLATE_NOOP("Constant", "Words"),
nwOutline.PCOUNT : QT_TRANSLATE_NOOP("Constant", "Pars"),
nwOutline.POV : QT_TRANSLATE_NOOP("Constant", "POV"),
nwOutline.FOCUS : QT_TRANSLATE_NOOP("Constant", "Focus"),
nwOutline.TITLE : QT_TRN("Constant", "Title"),
nwOutline.LEVEL : QT_TRN("Constant", "Level"),
nwOutline.LABEL : QT_TRN("Constant", "Document"),
nwOutline.LINE : QT_TRN("Constant", "Line"),
nwOutline.CCOUNT : QT_TRN("Constant", "Chars"),
nwOutline.WCOUNT : QT_TRN("Constant", "Words"),
nwOutline.PCOUNT : QT_TRN("Constant", "Pars"),
nwOutline.POV : QT_TRN("Constant", "POV"),
nwOutline.FOCUS : QT_TRN("Constant", "Focus"),
nwOutline.CHAR : KEY_NAME[nwKeyWords.CHAR_KEY],
nwOutline.PLOT : KEY_NAME[nwKeyWords.PLOT_KEY],
nwOutline.TIME : KEY_NAME[nwKeyWords.TIME_KEY],
@@ -210,7 +216,7 @@ class nwLabels():
nwOutline.OBJECT : KEY_NAME[nwKeyWords.OBJECT_KEY],
nwOutline.ENTITY : KEY_NAME[nwKeyWords.ENTITY_KEY],
nwOutline.CUSTOM : KEY_NAME[nwKeyWords.CUSTOM_KEY],
nwOutline.SYNOP : QT_TRANSLATE_NOOP("Constant", "Synopsis"),
nwOutline.SYNOP : QT_TRN("Constant", "Synopsis"),
}
# END Class nwLabels
@@ -220,28 +226,28 @@ class nwQuotes():
Source: https://en.wikipedia.org/wiki/Quotation_mark
"""
SYMBOLS = {
"\u0027" : QT_TRANSLATE_NOOP("Constant", "Straight single quotation mark"),
"\u0022" : QT_TRANSLATE_NOOP("Constant", "Straight double quotation mark"),
"\u0027" : QT_TRN("Constant", "Straight single quotation mark"),
"\u0022" : QT_TRN("Constant", "Straight double quotation mark"),
"\u2018" : QT_TRANSLATE_NOOP("Constant", "Left single quotation mark"),
"\u2019" : QT_TRANSLATE_NOOP("Constant", "Right single quotation mark"),
"\u201a" : QT_TRANSLATE_NOOP("Constant", "Single low-9 quotation mark"),
"\u201b" : QT_TRANSLATE_NOOP("Constant", "Single high-reversed-9 quotation mark"),
"\u201c" : QT_TRANSLATE_NOOP("Constant", "Left double quotation mark"),
"\u201d" : QT_TRANSLATE_NOOP("Constant", "Right double quotation mark"),
"\u201e" : QT_TRANSLATE_NOOP("Constant", "Double low-9 quotation mark"),
"\u201f" : QT_TRANSLATE_NOOP("Constant", "Double high-reversed-9 quotation mark"),
"\u2e42" : QT_TRANSLATE_NOOP("Constant", "Double low-reversed-9 quotation mark"),
"\u2018" : QT_TRN("Constant", "Left single quotation mark"),
"\u2019" : QT_TRN("Constant", "Right single quotation mark"),
"\u201a" : QT_TRN("Constant", "Single low-9 quotation mark"),
"\u201b" : QT_TRN("Constant", "Single high-reversed-9 quotation mark"),
"\u201c" : QT_TRN("Constant", "Left double quotation mark"),
"\u201d" : QT_TRN("Constant", "Right double quotation mark"),
"\u201e" : QT_TRN("Constant", "Double low-9 quotation mark"),
"\u201f" : QT_TRN("Constant", "Double high-reversed-9 quotation mark"),
"\u2e42" : QT_TRN("Constant", "Double low-reversed-9 quotation mark"),
"\u2039" : QT_TRANSLATE_NOOP("Constant", "Single left-pointing angle quotation mark"),
"\u203a" : QT_TRANSLATE_NOOP("Constant", "Single right-pointing angle quotation mark"),
"\u00ab" : QT_TRANSLATE_NOOP("Constant", "Left-pointing double angle quotation mark"),
"\u00bb" : QT_TRANSLATE_NOOP("Constant", "Right-pointing double angle quotation mark"),
"\u2039" : QT_TRN("Constant", "Single left-pointing angle quotation mark"),
"\u203a" : QT_TRN("Constant", "Single right-pointing angle quotation mark"),
"\u00ab" : QT_TRN("Constant", "Left-pointing double angle quotation mark"),
"\u00bb" : QT_TRN("Constant", "Right-pointing double angle quotation mark"),
"\u300c" : QT_TRANSLATE_NOOP("Constant", "Left corner bracket"),
"\u300d" : QT_TRANSLATE_NOOP("Constant", "Right corner bracket"),
"\u300e" : QT_TRANSLATE_NOOP("Constant", "Left white corner bracket"),
"\u300f" : QT_TRANSLATE_NOOP("Constant", "Right white corner bracket"),
"\u300c" : QT_TRN("Constant", "Left corner bracket"),
"\u300d" : QT_TRN("Constant", "Right corner bracket"),
"\u300e" : QT_TRN("Constant", "Left white corner bracket"),
"\u300f" : QT_TRN("Constant", "Right white corner bracket"),
}
# END Class nwQuotes
+12 -6
View File
@@ -24,10 +24,11 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from functools import partial
import logging
import os
from functools import partial
from PyQt5.QtCore import QCoreApplication
from nw.constants import nwAlert
@@ -51,7 +52,7 @@ class NWDoc():
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
self.tr = partial(QCoreApplication.translate, self.__class__.__name__)
self.tr = partial(QCoreApplication.translate, "NWDoc")
return
@@ -131,7 +132,9 @@ class NWDoc():
self.theParent.setStatus(
self.tr("{0}: {1}").format(
self.tr("Opened Document"),
self._theItem.itemName))
self._theItem.itemName
)
)
return theText
@@ -178,7 +181,9 @@ class NWDoc():
self.theParent.setStatus(
self.tr("{0}: {1}").format(
self.tr("Saved Document"),
self._theItem.itemName))
self._theItem.itemName
)
)
return True
@@ -201,8 +206,9 @@ class NWDoc():
os.unlink(chkFile)
logger.debug("Deleted: %s" % chkFile)
except Exception as e:
self.makeAlert([self.tr("Could not delete document file."), str(e)],
nwAlert.ERROR)
self.makeAlert(
[self.tr("Could not delete document file."), str(e)], nwAlert.ERROR
)
return False
return True
+4 -3
View File
@@ -24,13 +24,13 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from functools import partial
import nw
import logging
import json
import os
from time import time
from functools import partial
from PyQt5.QtCore import QCoreApplication
@@ -68,10 +68,11 @@ class NWIndex():
self._timeNotes = 0
self._timeIndex = 0
self.tr = partial(QCoreApplication.translate, self.__class__.__name__)
self.clearIndex()
# Internal Mappings
self.tr = partial(QCoreApplication.translate, "NWIndex")
return
##
+130 -96
View File
@@ -24,9 +24,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from functools import partial
from PyQt5.QtCore import QCoreApplication
import nw
import logging
import os
@@ -34,6 +31,9 @@ import shutil
from lxml import etree
from time import time
from functools import partial
from PyQt5.QtCore import QCoreApplication
from nw.core.tree import NWTree
from nw.core.item import NWItem
@@ -45,7 +45,7 @@ from nw.common import (
makeFileNameSafe, hexToInt
)
from nw.constants import (
nwFiles, nwItemType, nwItemClass, nwItemLayout, nwLabels, nwAlert
trConst, nwFiles, nwItemType, nwItemClass, nwItemLayout, nwLabels, nwAlert
)
logger = logging.getLogger(__name__)
@@ -102,7 +102,7 @@ class NWProject():
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
self.tr = partial(QCoreApplication.translate, self.__class__.__name__)
self.tr = partial(QCoreApplication.translate, "NWProject")
# Set Defaults
self.clearProject()
@@ -302,8 +302,8 @@ class NWProject():
nHandle = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL)
for newRoot in projData.get("addRoots", []):
if newRoot in nwItemClass:
self.newRoot(QCoreApplication.translate(
"Constant", nwLabels.CLASS_NAME[newRoot]), newRoot)
self.newRoot(trConst(nwLabels.CLASS_NAME[newRoot]), newRoot
)
# Create a title page
tHandle = self.newFile(self.tr("Title Page"), nwItemClass.NOVEL, nHandle)
@@ -424,13 +424,15 @@ class NWProject():
# Trying to open backup file instead
backFile = fileName[:-3]+"bak"
if os.path.isfile(backFile):
self.makeAlert(self.tr("Attempting to open backup project file instead."),
nwAlert.INFO)
self.makeAlert(
self.tr("Attempting to open backup project file instead."), nwAlert.INFO
)
try:
nwXML = etree.parse(backFile)
except Exception as e:
self.makeAlert([self.tr("Failed to parse project xml."), str(e)],
nwAlert.ERROR)
self.makeAlert(
[self.tr("Failed to parse project xml."), str(e)], nwAlert.ERROR
)
self.clearProject()
return False
else:
@@ -477,23 +479,31 @@ class NWProject():
# read the file. Introduced in version 0.10.
if fileVersion == "1.0":
msgYes = self.theParent.askQuestion(self.tr("Old Project Version"), (
msgYes = self.theParent.askQuestion(
self.tr("Old Project Version"),
"%s<br><br>%s" % (
self.tr("The project file and data is created by a novelWriter version "
"lower than 0.7. Do you want to upgrade the project to the "
"most recent format?"),
self.tr("Note that after the upgrade, you "
"cannot open the project with an older version of novelWriter "
"any more, so make sure you have a recent backup."))))
self.tr(
"The project file and data is created by a novelWriter version "
"lower than 0.7. Do you want to upgrade the project to the "
"most recent format?"
),
self.tr(
"Note that after the upgrade, you cannot open the project with "
"an older version of novelWriter any more, so make sure you "
"have a recent backup.")
)
)
if not msgYes:
self.clearProject()
return False
elif fileVersion != "1.1" and fileVersion != "1.2":
self.makeAlert((
self.tr("Unknown or unsupported novelWriter project file format. "
"The project cannot be opened by this version of novelWriter. "
"The file was saved with novelWriter version {0}.").format(appVersion)
self.tr(
"Unknown or unsupported novelWriter project file format. "
"The project cannot be opened by this version of novelWriter. "
"The file was saved with novelWriter version {0}."
).format(appVersion)
), nwAlert.ERROR)
self.clearProject()
return False
@@ -502,15 +512,18 @@ class NWProject():
# =========================
if hexToInt(hexVersion) > hexToInt(nw.__hexversion__):
msgYes = self.theParent.askQuestion(self.tr("Version Conflict"), (
self.tr("This project was saved by a newer version of novelWriter, version "
"{new_version}. This is version {version}. If you continue to open the "
"project, some attributes and settings may not be preserved, but the "
"overall project should be fine. Continue opening the project?")
).format(
new_version = appVersion,
version = nw.__version__
))
msgYes = self.theParent.askQuestion(
self.tr("Version Conflict"),
self.tr(
"This project was saved by a newer version of novelWriter, version "
"{new_version}. This is version {version}. If you continue to open the "
"project, some attributes and settings may not be preserved, but the "
"overall project should be fine. Continue opening the project?"
).format(
new_version = appVersion,
version = nw.__version__
)
)
if not msgYes:
self.clearProject()
return False
@@ -610,8 +623,8 @@ class NWProject():
self.mainConf.saveRecentCache()
self.theParent.setStatus(self.tr("{0}: {1}").format(
self.tr("Opened Project"),
self.projName))
self.tr("Opened Project"), self.projName)
)
self._scanProjectFolder()
@@ -731,8 +744,8 @@ class NWProject():
self._writeLockFile()
self.theParent.setStatus(self.tr("{0}: {1}").format(
self.tr("Saved Project"),
self.projName))
self.tr("Saved Project"), self.projName)
)
self.setProjectChanged(False)
return True
@@ -787,24 +800,30 @@ class NWProject():
self.theParent.setStatus(self.tr("Backing up project ..."))
if self.mainConf.backupPath is None or self.mainConf.backupPath == "":
self.theParent.makeAlert((
self.tr("Cannot backup project because no backup path is set. "
"Please set a valid backup location in Tools > Preferences.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Cannot backup project because no backup path is set. "
"Please set a valid backup location in Tools > Preferences."
), nwAlert.ERROR
)
return False
if self.projName is None or self.projName == "":
self.theParent.makeAlert((
self.tr("Cannot backup project because no project name is set. "
"Please set a Working Title in Project > Project Settings.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Cannot backup project because no project name is set. "
"Please set a Working Title in Project > Project Settings."
), nwAlert.ERROR
)
return False
if not os.path.isdir(self.mainConf.backupPath):
self.theParent.makeAlert((
self.tr("Cannot backup project because the backup path does not exist. "
"Please set a valid backup location in Tools > Preferences.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Cannot backup project because the backup path does not exist. "
"Please set a valid backup location in Tools > Preferences."
), nwAlert.ERROR
)
return False
cleanName = makeFileNameSafe(self.projName)
@@ -821,11 +840,13 @@ class NWProject():
return False
if os.path.commonpath([self.projPath, baseDir]) == self.projPath:
self.theParent.makeAlert((
self.tr("Cannot backup project because the backup path is within the "
"project folder to be backed up. Please choose a different "
"backup path in Tools > Preferences.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Cannot backup project because the backup path is within the "
"project folder to be backed up. Please choose a different "
"backup path in Tools > Preferences."
), nwAlert.ERROR
)
return False
archName = self.tr("Backup from {0}").format(formatTimeStamp(time(), fileSafe=True))
@@ -838,9 +859,11 @@ class NWProject():
logger.info("Backup written to: %s" % archName)
if doNotify:
self.theParent.makeAlert(
self.tr("Backup archive file written to: {0}").format(
f"{os.path.join(cleanName, archName)}.zip"),
nwAlert.INFO
self.tr(
"Backup archive file written to: {0}"
).format(
f"{os.path.join(cleanName, archName)}.zip"
), nwAlert.INFO
)
except Exception as e:
@@ -865,9 +888,8 @@ class NWProject():
logger.error("No project path set for the example project")
return False
srcSample = os.path.abspath(os.path.join(self.mainConf.appRoot,
self.tr("sample")))
pkgSample = os.path.join(self.mainConf.assetPath, "%s.zip" % self.tr("sample"))
srcSample = os.path.abspath(os.path.join(self.mainConf.appRoot, "sample"))
pkgSample = os.path.join(self.mainConf.assetPath, "sample.zip")
isSuccess = False
if os.path.isfile(pkgSample):
@@ -889,8 +911,8 @@ class NWProject():
dstProj = os.path.join(projPath, nwFiles.PROJ_FILE)
shutil.copyfile(srcProj, dstProj)
srcContent = os.path.join(srcSample, self.tr("content"))
dstContent = os.path.join(projPath, self.tr("content"))
srcContent = os.path.join(srcSample, "content")
dstContent = os.path.join(projPath, "content")
for srcFile in os.listdir(srcContent):
srcDoc = os.path.join(srcContent, srcFile)
dstDoc = os.path.join(dstContent, srcFile)
@@ -904,10 +926,12 @@ class NWProject():
)
else:
self.makeAlert((
self.tr("Failed to create a new example project. Could not find the "
"necessary files. They seem to be missing from this installation.")
), nwAlert.ERROR)
self.makeAlert(
self.tr(
"Failed to create a new example project. Could not find the "
"necessary files. They seem to be missing from this installation."
), nwAlert.ERROR
)
if isSuccess:
self.clearProject()
@@ -944,10 +968,12 @@ class NWProject():
if os.path.isdir(projPath):
if os.listdir(self.projPath):
self.theParent.makeAlert((
self.tr("New project folder is not empty. "
"Each project requires a dedicated project folder.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"New project folder is not empty. "
"Each project requires a dedicated project folder."
), nwAlert.ERROR
)
return False
self.ensureFolderStructure()
@@ -994,17 +1020,21 @@ class NWProject():
self.doBackup = doBackup
if doBackup:
if not os.path.isdir(self.mainConf.backupPath):
self.theParent.makeAlert((
self.tr("You must set a valid backup path in preferences to use "
"the automatic project backup feature.")
), nwAlert.WARN)
self.theParent.makeAlert(
self.tr(
"You must set a valid backup path in preferences to use "
"the automatic project backup feature."
), nwAlert.WARN
)
return False
if self.projName == "":
self.theParent.makeAlert((
self.tr("You must set a valid project name in project settings to "
"use the automatic project backup feature.")
), nwAlert.WARN)
self.theParent.makeAlert(
self.tr(
"You must set a valid project name in project settings to "
"use the automatic project backup feature."
), nwAlert.WARN
)
return False
return True
@@ -1354,6 +1384,7 @@ class NWProject():
aDoc = NWDoc(self, self.theParent)
nOrph = 0
noWhere = False
oPrefix = self.tr("Recovered")
for oHandle in orphanFiles:
# Look for meta data
@@ -1365,9 +1396,9 @@ class NWProject():
oName, oParent, oClass, oLayout = aDoc.getMeta()
if oName:
oName = self.tr("{0}: {1}").format(
self.tr("Recovered"),
oName.lstrip(self.tr("{0}: ").format(self.tr("Recovered"))))
oName = self.tr("[{0}] {1}").format(
oPrefix, oName.strip("[%s]" % oPrefix).strip()
)
else:
nOrph += 1
oName = self.tr("Recovered File {0}").format(nOrph)
@@ -1397,10 +1428,12 @@ class NWProject():
self.projTree.append(oHandle, oParent, orphItem)
if noWhere:
self.makeAlert((
self.tr("One or more orphaned files could not be added back into the "
"project. Make sure at least a Novel root folder exists.")
), nwAlert.WARN)
self.makeAlert(
self.tr(
"One or more orphaned files could not be added back into the "
"project. Make sure at least a Novel root folder exists."
), nwAlert.WARN
)
return True
@@ -1418,13 +1451,9 @@ class NWProject():
if not isFile:
# It's a new file, so add a header
if self.lastWCount > 0:
outFile.write("# %s\n" % self.tr("Offset {0}").format(self.lastWCount))
outFile.write("# Offset %d\n" % self.lastWCount)
outFile.write("# %-17s %-19s %8s %8s %8s\n" % (
self.tr("Start Time"),
self.tr("End Time"),
self.tr("Novel"),
self.tr("Notes"),
self.tr("Idle"),
"Start Time", "End Time", "Novel", "Notes", "Idle"
))
outFile.write("%-19s %-19s %8d %8d %8d\n" % (
@@ -1471,10 +1500,12 @@ class NWProject():
newPath = os.path.join(self.projContent, tHandle+".nwd")
try:
os.rename(theFile, newPath)
logger.info(self.tr("{0}: {1}").format(self.tr("Moved file"), theFile))
logger.info(self.tr("{0}: {1}").format(self.tr("New location"), newPath))
logger.info("Moved file: %s" % theFile)
logger.info("New location: %s" % newPath)
except Exception:
errList.append(self.tr("{0}: {1}").format(self.tr("Could not move"), theFile))
errList.append(
self.tr("{0}: {1}").format(self.tr("Could not move"), theFile)
)
logger.error("Could not move: %s" % theFile)
nw.logException()
@@ -1483,8 +1514,9 @@ class NWProject():
os.unlink(theFile)
logger.info("Deleted file: %s" % theFile)
except Exception:
errList.append(self.tr("{0}: {1}").format(
self.tr("Could not delete"), theFile))
errList.append(
self.tr("{0}: {1}").format(self.tr("Could not delete"), theFile)
)
logger.error("Could not delete: %s" % theFile)
nw.logException()
@@ -1499,7 +1531,9 @@ class NWProject():
os.rmdir(theData)
logger.info("Removed folder: %s" % theFolder)
except Exception:
errList.append(self.tr("{0}: {1}").format(self.tr("Failed to remove"), theFolder))
errList.append(
self.tr("{0}: {1}").format(self.tr("Failed to remove"), theFolder)
)
logger.error("Failed to remove: %s" % theFolder)
nw.logException()
@@ -1509,7 +1543,7 @@ class NWProject():
"""Move an item that doesn't belong in the project folder to
a junk folder.
"""
theJunk = os.path.join(self.projPath, self.tr("junk"))
theJunk = os.path.join(self.projPath, "junk")
if not self._checkFolder(theJunk):
return self.tr("{0}: {1}").format(self.tr("Could not make folder"), theJunk)
@@ -1522,7 +1556,7 @@ class NWProject():
except Exception:
logger.error("Could not move item %s to junk." % theSrc)
nw.logException()
return self.tr("Could not move item {0} to junk.").format(theSrc)
return self.tr("Could not move item {0} to {1}.").format(theSrc, theJunk)
return ""
+5 -5
View File
@@ -407,13 +407,11 @@ class ToHtml(Tokenizer):
"""
if self.genMode == self.M_PREVIEW:
return "<p class='comment'><span class='synopsis'>%s:</span> %s</p>\n" % (
self.tr("Synopsis"),
tText
self._trSynopsis, tText
)
else:
return "<p class='synopsis'><strong>%s:</strong> %s</p>\n" % (
self.tr("Synopsis"),
tText
self._trSynopsis, tText
)
def _formatComments(self, tText):
@@ -422,7 +420,9 @@ class ToHtml(Tokenizer):
if self.genMode == self.M_PREVIEW:
return "<p class='comment'>%s</p>\n" % tText
else:
return "<p class='comment'><strong>%s:</strong> %s</p>\n" % (self.tr("Comment"), tText)
return "<p class='comment'><strong>%s:</strong> %s</p>\n" % (
self._trComment, tText
)
def _formatKeywords(self, tText):
"""Apply HTML formatting to keywords.
+10 -3
View File
@@ -24,11 +24,12 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from functools import partial
import logging
import re
from operator import itemgetter
from functools import partial
from PyQt5.QtCore import QCoreApplication, QRegularExpression
from nw.core.document import NWDoc
@@ -142,7 +143,13 @@ class Tokenizer():
# Error Handling
self.errData = []
self.tr = partial(QCoreApplication.translate, self.__class__.__name__)
# Internal Mappings
self.tr = partial(QCoreApplication.translate, "Tokenizer")
# Localisation
self._trSynopsis = self.tr("Synopsis")
self._trComment = self.tr("Comment")
self._trNotes = self.tr("Notes")
return
@@ -252,7 +259,7 @@ class Tokenizer():
if theItem.itemType != nwItemType.ROOT:
return False
theTitle = self.tr("{0}: {1}").format(self.tr("Notes"), theItem.itemName)
theTitle = self.tr("{0}: {1}").format(self._trNotes, theItem.itemName)
self.theTokens = []
self.theTokens.append((
self.T_TITLE, 0, theTitle, None, self.A_PBB | self.A_CENTRE
+4 -3
View File
@@ -24,14 +24,14 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from functools import partial
import nw
import logging
import os
from time import time
from lxml import etree
from hashlib import sha256
from time import time
from functools import partial
from PyQt5.QtCore import QCoreApplication
@@ -82,7 +82,8 @@ class NWTree():
self._handleSeed = None # Used for generating handles for testing
self.tr = partial(QCoreApplication.translate, self.__class__.__name__)
# Internal Mappings
self.tr = partial(QCoreApplication.translate, "NWTree")
return
+70 -51
View File
@@ -62,7 +62,7 @@ class GuiAbout(QDialog):
nPx = self.mainConf.pxInt(96)
self.nwIcon = QLabel()
self.nwIcon.setPixmap(self.theParent.theTheme.getPixmap("novelwriter", (nPx, nPx)))
self.lblName = QLabel("<b>%s</b>" % self.tr("novelWriter"))
self.lblName = QLabel("<b>novelWriter</b>")
self.lblVers = QLabel("v%s" % nw.__version__)
self.lblDate = QLabel(datetime.strptime(nw.__date__, "%Y-%m-%d").strftime("%x"))
@@ -136,24 +136,31 @@ class GuiAbout(QDialog):
aboutMsg = "".join([
"<h2>%s</h2>" % self.tr("About novelWriter"),
"<p>{copyright:s}.</p>",
"<p>%s</p>" % (self.tr("{0}: {1}").format(
self.tr("Website"),
"<a href=\"{website:s}\">{domain:s}</a>"
)),
"<p>%s</p>" % self.tr("novelWriter is a markdown-like text editor designed for "
"organising and writing novels. It is written in Python 3 with "
"a Qt5 GUI, using PyQt5."),
"<p>%s</p>" % self.tr("novelWriter 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."),
"<p>%s</p>" % self.tr("novelWriter 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."),
"<p>%s</p>" % (self.tr("See the License tab for the full license text, or visit the "
"GNU website at {0} for more details.").format(
"<a href=\"https://www.gnu.org/licenses/gpl-3.0.html\">GPL v3.0</a>")),
"<p>%s</p>" % self.tr("{0}: {1}").format(
self.tr("Website"), "<a href=\"{website:s}\">{domain:s}</a>"
),
"<p>%s</p>" % self.tr(
"novelWriter is a markdown-like text editor designed for organising and "
"writing novels. It is written in Python 3 with a Qt5 GUI, using PyQt5."
),
"<p>%s</p>" % self.tr(
"novelWriter 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."
),
"<p>%s</p>" % self.tr(
"novelWriter 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."
),
"<p>%s</p>" % (
self.tr(
"See the License tab for the full license text, or visit the "
"GNU website at {0} for more details.").format(
"<a href=\"https://www.gnu.org/licenses/gpl-3.0.html\">GPL v3.0</a>"
)
),
"<h3>%s</h3>" % self.tr("Credits"),
"<p>{credits:s}</p>",
]).format(
@@ -167,49 +174,61 @@ class GuiAbout(QDialog):
theIcons = self.theParent.theTheme.theIcons
if theTheme.themeName:
aboutMsg += "".join([
("<h4>%s</h4>" % self.tr("{0}: {1}").format(self.tr("Theme"), theTheme.themeName)),
"<h4>%s</h4>" % self.tr("{0}: {1}").format(
self.tr("Theme"), theTheme.themeName
),
"<p>",
("%s<br/>" % self.tr("<b>{0}:</b> {1}").format(
self.tr("Author"), theTheme.themeAuthor)),
("%s<br/>" % self.tr("<b>{0}:</b> {1}").format(
self.tr("Credit"), theTheme.themeCredit)),
(self.tr("<b>{0}:</b> {1}").format(
self.tr("License"),
"<a href=\"{0}\">{1}</a>".format(
theTheme.themeLicenseUrl, theTheme.themeLicense)
)),
"%s<br/>" % self.tr("<b>{0}:</b> {1}").format(
self.tr("Author"), theTheme.themeAuthor
),
"%s<br/>" % self.tr("<b>{0}:</b> {1}").format(
self.tr("Credit"), theTheme.themeCredit
),
self.tr("<b>{0}:</b> {1}").format(
self.tr("License"), "<a href='{0}'>{1}</a>".format(
theTheme.themeLicenseUrl, theTheme.themeLicense
)
),
"</p>"
])
if theIcons.themeName:
aboutMsg += "".join([
("<h4>%s</h4>" % self.tr("{0}: {1}").format(self.tr("Icons"), theIcons.themeName)),
"<h4>%s</h4>" % self.tr("{0}: {1}").format(
self.tr("Icons"), theIcons.themeName
),
"<p>",
("%s<br/>" % self.tr("<b>{0}:</b> {1}").format(
self.tr("Author"), theIcons.themeAuthor)),
("%s<br/>" % self.tr("<b>{0}:</b> {1}").format(
self.tr("Credit"), theIcons.themeCredit)),
(self.tr("<b>{0}:</b> {1}").format(
self.tr("License"),
"<a href=\"{0}\">{1}</a>".format(
theIcons.themeLicenseUrl, theIcons.themeLicense)
)),
"%s<br/>" % self.tr("<b>{0}:</b> {1}").format(
self.tr("Author"), theIcons.themeAuthor
),
"%s<br/>" % self.tr("<b>{0}:</b> {1}").format(
self.tr("Credit"), theIcons.themeCredit
),
self.tr("<b>{0}:</b> {1}").format(
self.tr("License"), "<a href='{0}'>{1}</a>".format(
theIcons.themeLicenseUrl, theIcons.themeLicense
)
),
"</p>"
])
if theTheme.syntaxName:
aboutMsg += "".join([
("<h4>%s</h4>" % self.tr("{0}: {1}").format(
self.tr("Syntax"),
theTheme.syntaxName)),
"<h4>%s</h4>" % self.tr("{0}: {1}").format(
self.tr("Syntax"), theTheme.syntaxName
),
"<p>",
("%s<br/>" % self.tr("<b>{0}:</b> {1}").format(
self.tr("Author"), theTheme.syntaxAuthor)),
("%s<br/>" % self.tr("<b>{0}</b> {1}").format(
self.tr("Credit"), theTheme.syntaxCredit)),
(self.tr("<b>{0}:</b> {1}").format(
self.tr("License"),
"<a href=\"{0}\">{1}</a>".format(
theTheme.syntaxLicenseUrl, theTheme.syntaxLicense)
)),
"%s<br/>" % self.tr("<b>{0}:</b> {1}").format(
self.tr("Author"), theTheme.syntaxAuthor
),
"%s<br/>" % self.tr("<b>{0}</b> {1}").format(
self.tr("Credit"), theTheme.syntaxCredit
),
self.tr("<b>{0}:</b> {1}").format(
self.tr("License"), "<a href='{0}'>{1}</a>".format(
theTheme.syntaxLicenseUrl, theTheme.syntaxLicense
)
),
"</p>"
])
+56 -40
View File
@@ -105,7 +105,7 @@ class GuiBuildNovel(QDialog):
self.titleForm = QGridLayout(self)
self.titleGroup.setLayout(self.titleForm)
fmtHelp = "<br>".join(
fmtHelp = "<br>".join([
"<b>%s</b>" % self.tr("{0}:").format("Formatting Codes"),
self.tr("{0} for the title as set in the document").format(r"%title%"),
self.tr("{0} for chapter number (1, 2, 3)").format(r"%ch%"),
@@ -114,14 +114,13 @@ class GuiBuildNovel(QDialog):
self.tr("{0} for chapter number in lower case Roman").format(r"%chi%"),
self.tr("{0} for scene number within chapter").format(r"%sc%"),
self.tr("{0} for scene number within novel").format(r"%sca%"),
)
fmtScHelp = (
"<br><br>%s" %
self.tr("Leave blank to skip this heading, or set to a static text, like "
"for instance '{0}', to make a separator. The separator will "
"be centred automatically and only appear between sections of "
"the same type.").format("* * *")
)
])
fmtScHelp = "<br><br>%s" % self.tr(
"Leave blank to skip this heading, or set to a static text, like "
"for instance '{0}', to make a separator. The separator will "
"be centred automatically and only appear between sections of "
"the same type."
).format("* * *")
xFmt = self.mainConf.pxInt(100)
self.fmtTitle = QLineEdit()
@@ -332,10 +331,10 @@ class GuiBuildNovel(QDialog):
self.fileGroup.setLayout(self.fileForm)
self.novelFiles = QSwitch(width=wS, height=hS)
self.novelFiles.setToolTip(
self.tr("Include files with layouts 'Book', 'Page', 'Partition', "
"'Chapter', 'Unnumbered', and 'Scene'.")
)
self.novelFiles.setToolTip(self.tr(
"Include files with layouts 'Book', 'Page', 'Partition', "
"'Chapter', 'Unnumbered', and 'Scene'."
))
self.novelFiles.setChecked(
self.optState.getBool("GuiBuildNovel", "addNovel", True)
)
@@ -347,10 +346,10 @@ class GuiBuildNovel(QDialog):
)
self.ignoreFlag = QSwitch(width=wS, height=hS)
self.ignoreFlag.setToolTip(
self.tr("Ignore the 'Include when building project' setting and include "
"all files in the output.")
)
self.ignoreFlag.setToolTip(self.tr(
"Ignore the 'Include when building project' setting and include "
"all files in the output."
))
self.ignoreFlag.setChecked(
self.optState.getBool("GuiBuildNovel", "ignoreFlag", False)
)
@@ -430,42 +429,51 @@ class GuiBuildNovel(QDialog):
self.btnSave = QPushButton("Save As")
self.btnSave.setMenu(self.saveMenu)
self.saveODT = QAction(self.tr("{0} ({1})").format(self.tr("Open Document"), ".odt"), self)
self.saveODT = QAction(
self.tr("{0} ({1})").format(self.tr("Open Document"), ".odt"), self
)
self.saveODT.triggered.connect(lambda: self._saveDocument(self.FMT_ODT))
self.saveMenu.addAction(self.saveODT)
self.saveFODT = QAction(
self.tr("{0} ({1})").format(self.tr("Flat Open Document"), ".fodt"), self)
self.tr("{0} ({1})").format(self.tr("Flat Open Document"), ".fodt"), self
)
self.saveFODT.triggered.connect(lambda: self._saveDocument(self.FMT_FODT))
self.saveMenu.addAction(self.saveFODT)
self.saveHTM = QAction(self.tr("{0} ({1})").format(
self.tr("novelWriter HTML"), ".htm"), self)
self.saveHTM = QAction(
self.tr("{0} ({1})").format(self.tr("novelWriter HTML"), ".htm"), self
)
self.saveHTM.triggered.connect(lambda: self._saveDocument(self.FMT_HTM))
self.saveMenu.addAction(self.saveHTM)
self.saveNWD = QAction(self.tr("{0} ({1})").format(
self.tr("novelWriter Markdown"), ".nwd"), self)
self.saveNWD = QAction(
self.tr("{0} ({1})").format(self.tr("novelWriter Markdown"), ".nwd"), self
)
self.saveNWD.triggered.connect(lambda: self._saveDocument(self.FMT_NWD))
self.saveMenu.addAction(self.saveNWD)
self.saveMD = QAction(
self.tr("{0} ({1})").format(self.tr("Standard Markdown"), ".md"), self)
self.tr("{0} ({1})").format(self.tr("Standard Markdown"), ".md"), self
)
self.saveMD.triggered.connect(lambda: self._saveDocument(self.FMT_MD))
self.saveMenu.addAction(self.saveMD)
self.saveGH = QAction(
self.tr("{0} ({1})").format(self.tr("GitHub Markdown"), ".md"), self)
self.tr("{0} ({1})").format(self.tr("GitHub Markdown"), ".md"), self
)
self.saveGH.triggered.connect(lambda: self._saveDocument(self.FMT_GH))
self.saveMenu.addAction(self.saveGH)
self.saveJsonH = QAction(self.tr("{0} ({1})").format(
self.tr("JSON + novelWriter HTML"), ".json"), self)
self.saveJsonH = QAction(
self.tr("{0} ({1})").format(self.tr("JSON + novelWriter HTML"), ".json"), self
)
self.saveJsonH.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_H))
self.saveMenu.addAction(self.saveJsonH)
self.saveJsonM = QAction(self.tr("{0} ({1})").format(
self.tr("JSON + novelWriters Markdown"), ".json"), self)
self.saveJsonM = QAction(
self.tr("{0} ({1})").format(self.tr("JSON + novelWriters Markdown"), ".json"), self
)
self.saveJsonM.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_M))
self.saveMenu.addAction(self.saveJsonM)
@@ -745,7 +753,8 @@ class GuiBuildNovel(QDialog):
if bldObj.errData:
self.theParent.makeAlert("%s:<br>-&nbsp;%s" % (
self.tr("There were problems when building the project"),
"<br>-&nbsp;".join(bldObj.errData)), nwAlert.ERROR)
"<br>-&nbsp;".join(bldObj.errData)), nwAlert.ERROR
)
return
@@ -997,14 +1006,14 @@ class GuiBuildNovel(QDialog):
if wSuccess:
self.theParent.makeAlert(
"%s<br> %s" % (
self.tr("{0} file successfully written to:").format(textFmt),
savePath
), nwAlert.INFO
self.tr("{0} file successfully written to:").format(textFmt), savePath
),
nwAlert.INFO
)
else:
self.theParent.makeAlert(
self.tr("Failed to write {0} file. {1}").format(
textFmt, errMsg), nwAlert.ERROR
self.tr("Failed to write {0} file. {1}").format(textFmt, errMsg),
nwAlert.ERROR
)
return wSuccess
@@ -1200,9 +1209,11 @@ class GuiBuildNovelDocView(QTextBrowser):
self.qDocument = self.document()
self.qDocument.setDocumentMargin(self.mainConf.getTextMargin())
self.setPlaceholderText(
self.tr("This area will show the content of the document to be "
"exported or printed. Press the \"Build Preview\" button "
"to generate content.")
self.tr(
"This area will show the content of the document to be "
"exported or printed. Press the \"Build Preview\" button "
"to generate content."
)
)
theFont = QFont()
@@ -1234,7 +1245,8 @@ class GuiBuildNovelDocView(QTextBrowser):
fPx = int(1.1*self.theTheme.fontPixelSize)
self.theTitle = QLabel(self.tr("<b>{0}:</b> {1}".format(
self.tr("Build Time"), self.tr("Unknown"))), self)
self.tr("Build Time"), self.tr("Unknown"))), self
)
self.theTitle.setIndent(0)
self.theTitle.setAutoFillBackground(True)
self.theTitle.setAlignment(Qt.AlignCenter)
@@ -1349,8 +1361,12 @@ class GuiBuildNovelDocView(QTextBrowser):
)
else:
strBuildTime = self.tr("Unknown")
self.theTitle.setText(self.tr("<b>{0}:</b> {1}").format(
self.tr("Build Time"), strBuildTime))
self.tr("Build Time"), strBuildTime)
)
return
def _updateDocMargins(self):
"""Automatically adjust the header to fill the top of the
+49 -40
View File
@@ -36,7 +36,7 @@ import logging
from time import time
from PyQt5.QtCore import (
QCoreApplication, Qt, QSize, QTimer, pyqtSlot, pyqtSignal, QRegExp, QRegularExpression,
Qt, QSize, QTimer, pyqtSlot, pyqtSignal, QRegExp, QRegularExpression,
QPointF, QObject, QRunnable, QPropertyAnimation
)
from PyQt5.QtGui import (
@@ -53,8 +53,8 @@ from nw.core import NWDoc, NWSpellSimple, countWords
from nw.gui.dochighlight import GuiDocHighlighter
from nw.common import transferCase
from nw.constants import (
nwConst, nwAlert, nwUnicode, nwDocAction, nwDocInsert, nwItemClass,
nwKeyWords, nwLabels
trConst, nwConst, nwAlert, nwUnicode, nwDocAction, nwDocInsert,
nwItemClass, nwKeyWords, nwLabels
)
logger = logging.getLogger(__name__)
@@ -293,15 +293,17 @@ class GuiDocEditor(QTextEdit):
docSize = len(theDoc)
if docSize > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert((
self.tr("The document you are trying to open is too big. "
"The document size is {doc_size}. "
"The maximum size allowed is {max_size}.").
format(
self.theParent.makeAlert(
self.tr(
"The document you are trying to open is too big. "
"The document size is {doc_size}. "
"The maximum size allowed is {max_size}."
).format(
doc_size=self.tr("{0}\u202fMB").format(f"{docSize/1.0e6:.2f}"),
max_size=self.tr("{0}\u202fMB").format(f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}")
)
), nwAlert.ERROR)
),
nwAlert.ERROR
)
self.clearEditor()
return False
@@ -384,15 +386,17 @@ class GuiDocEditor(QTextEdit):
"""
docSize = len(theText)
if docSize > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert((
self.tr("The text you are trying to add is too big. "
"The text size is {text_size}. "
"The maximum size allowed is {max_size}.").
format(
self.theParent.makeAlert(
self.tr(
"The text you are trying to add is too big. "
"The text size is {text_size}. "
"The maximum size allowed is {max_size}."
).format(
text_size=self.tr("{0}\u202fMB").format(f"{docSize/1.0e6:.2f}"),
max_size=self.tr("{0}\u202fMB").format(f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}")
)
), nwAlert.ERROR)
),
nwAlert.ERROR
)
return False
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
@@ -747,15 +751,14 @@ class GuiDocEditor(QTextEdit):
return False
msgBox = QMessageBox()
msgBox.information(self, self.tr("File Location"), "".join([
(self.tr("{0}<br>").format(self.tr("File details for the currently open file"))),
(self.tr("{0}<br>").format(
self.tr("{0}: {1}").format(self.tr("Handle"), "{handle:s}"))),
(self.tr("{0}: {1}").format(self.tr("Location"), "{fileLoc:s}"))
]).format(
handle = self.theHandle,
fileLoc = str(self.nwDocument.getFileLocation())
))
msgBox.information(
self,
self.tr("File Location"),
"%s<br>%s" % (
self.tr("The currently open file is saved in:"),
self.nwDocument.getFileLocation()
),
)
return
@@ -938,11 +941,15 @@ class GuiDocEditor(QTextEdit):
self.lastFind = None
if self.qDocument.characterCount() > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert((
self.tr("The document has grown too big and you cannot add more text to it. "
"The maximum size of a single novelWriter document is {max_size}.").
format(max_size=self.tr("{0}\u202fMB").format(f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"))
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"The document has grown too big and you cannot add more text to it. "
"The maximum size of a single novelWriter document is {max_size}."
).format(
max_size=self.tr("{0}\u202fMB").format(f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}")
),
nwAlert.ERROR
)
self.undo()
return
@@ -1860,8 +1867,7 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addAction(self.toggleWord)
self.toggleRegEx = QAction(self.tr("RegEx Mode"), self)
self.toggleRegEx.setToolTip(self.tr("Use regular expressions (requires Qt {0})").format(
"5.3"))
self.toggleRegEx.setToolTip(self.tr("Search using regular expressions"))
self.toggleRegEx.setIcon(self.theTheme.getIcon("search_regex"))
self.toggleRegEx.setCheckable(True)
self.toggleRegEx.setChecked(self.isRegEx)
@@ -2541,10 +2547,8 @@ class GuiDocEditFooter(QWidget):
theIcon = self.theParent.importIcons[iStatus]
sIcon = theIcon.pixmap(self.sPx, self.sPx)
sClass = QCoreApplication.translate(
"Constant", nwLabels.CLASS_NAME[self.theItem.itemClass])
sLayout = QCoreApplication.translate(
"Constant", nwLabels.LAYOUT_NAME[self.theItem.itemLayout])
sClass = trConst(nwLabels.CLASS_NAME[self.theItem.itemClass])
sLayout = trConst(nwLabels.LAYOUT_NAME[self.theItem.itemLayout])
sText = f"{self.theItem.itemStatus} / {sClass} / {sLayout}"
self.statusIcon.setPixmap(sIcon)
@@ -2565,7 +2569,9 @@ class GuiDocEditFooter(QWidget):
self.linesText.setText(
self.tr("{0}: {1} ({2}\u202f%%)".format(
self.tr("Line"), f"{iLine:n}", f"{iDist:.0f}")))
self.tr("Line"), f"{iLine:n}", f"{iDist:.0f}")
)
)
return
@@ -2581,11 +2587,14 @@ class GuiDocEditFooter(QWidget):
self.wordsText.setText(
self.tr("{0}: {1} ({2})".format(
self.tr("Words"), f"{wCount:n}", f"{wDiff:+n}")))
self.tr("Words"), f"{wCount:n}", f"{wDiff:+n}")
)
)
byteSize = self.docEditor.qDocument.characterCount()
self.wordsText.setToolTip(
(self.tr("Document size is {0} bytes").format(f"{byteSize:n}")))
self.tr("Document size is {0} bytes").format(f"{byteSize:n}")
)
return
+12 -12
View File
@@ -104,9 +104,9 @@ class GuiDocMerge(QDialog):
finalOrder.append(self.listBox.item(i).data(Qt.UserRole))
if len(finalOrder) == 0:
self.theParent.makeAlert((
self.tr("No source documents found. Nothing to do.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("No source documents found. Nothing to do."), nwAlert.ERROR
)
return
theDoc = NWDoc(self.theProject, self.theParent)
@@ -116,16 +116,16 @@ class GuiDocMerge(QDialog):
theText += "\n\n"
if self.sourceItem is None:
self.theParent.makeAlert((
self.tr("No source document selected. Nothing to do.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("No source document selected. Nothing to do."), nwAlert.ERROR
)
return
srcItem = self.theProject.projTree[self.sourceItem]
if srcItem is None:
self.theParent.makeAlert((
self.tr("Could not parse source document.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("Could not parse source document."), nwAlert.ERROR
)
return
nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.itemParent)
@@ -166,9 +166,9 @@ class GuiDocMerge(QDialog):
if nwItem is None:
return
if nwItem.itemType is not nwItemType.FOLDER:
self.theParent.makeAlert((
self.tr("Element selected in the project tree must be a folder.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("Element selected in the project tree must be a folder."), nwAlert.ERROR
)
return
for sHandle in self.theParent.treeView.getTreeFromHandle(tHandle):
+30 -22
View File
@@ -118,16 +118,16 @@ class GuiDocSplit(QDialog):
logger.verbose("GuiDocSplit split button clicked")
if self.sourceItem is None:
self.theParent.makeAlert((
self.tr("No source document selected. Nothing to do.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("No source document selected. Nothing to do."), nwAlert.ERROR
)
return
srcItem = self.theProject.projTree[self.sourceItem]
if srcItem is None:
self.theParent.makeAlert((
self.tr("Could not parse source document.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("Could not parse source document."), nwAlert.ERROR
)
return
theDoc = NWDoc(self.theProject, self.theParent)
@@ -150,26 +150,34 @@ class GuiDocSplit(QDialog):
nFiles = len(finalOrder)
if nFiles == 0:
self.theParent.makeAlert((
self.tr("No headers found. Nothing to do.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("No headers found. Nothing to do."), nwAlert.ERROR
)
return
# Check that another folder can be created
parTree = self.theProject.projTree.getItemPath(srcItem.itemParent)
if len(parTree) >= nwConst.MAX_DEPTH - 1:
self.theParent.makeAlert((
self.tr("Cannot add new folder for the document split. "
"Maximum folder depth has been reached. "
"Please move the file to another level in the project tree.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Cannot add new folder for the document split. "
"Maximum folder depth has been reached. "
"Please move the file to another level in the project tree."
), nwAlert.ERROR
)
return
msgYes = self.theParent.askQuestion(self.tr("Split Document"), "%s<br><br>%s" % (
self.tr("The document will be split into {0} file(s) in a new folder. "
"The original document will remain intact.", n=nFiles).format(nFiles),
self.tr("Continue with the splitting process?")
))
msgYes = self.theParent.askQuestion(
self.tr("Split Document"),
"%s<br><br>%s" % (
self.tr(
"The document will be split into {0} file(s) in a new folder. "
"The original document will remain intact.").format(nFiles),
self.tr(
"Continue with the splitting process?"
)
)
)
if not msgYes:
return
@@ -245,9 +253,9 @@ class GuiDocSplit(QDialog):
if nwItem is None:
return
if nwItem.itemType is not nwItemType.FILE:
self.theParent.makeAlert((
self.tr("Element selected in the project tree must be a file.")
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("Element selected in the project tree must be a file."), nwAlert.ERROR
)
return
self.listBox.clear()
+10 -6
View File
@@ -244,10 +244,13 @@ class GuiDocViewer(QTextBrowser):
tHandle, _, sTitle = self.theParent.theIndex.getTagSource(theTag)
if tHandle is None:
self.theParent.makeAlert(
self.tr("Could not find the reference for tag '{0}'. It either doesn't "
"exist, or the index is out of date. The index can be updated "
"from the Tools menu, or by pressing {1}.").
format(theTag, "F9"), nwAlert.ERROR)
self.tr(
"Could not find the reference for tag '{0}'. It either doesn't "
"exist, or the index is out of date. The index can be updated "
"from the Tools menu, or by pressing {1}."
).format(theTag, "F9"),
nwAlert.ERROR
)
return False
else:
# Let the parent handle the opening as it also ensures that
@@ -956,8 +959,9 @@ class GuiDocViewFooter(QWidget):
self.stickyRefs.setFixedSize(QSize(fPx, fPx))
self.stickyRefs.toggled.connect(self._doToggleSticky)
self.stickyRefs.setToolTip(
self.tr("Activate to freeze the content of the references panel when "
"changing document")
self.tr(
"Activate to freeze the content of the references panel when changing document"
)
)
# Show Comments
+7 -9
View File
@@ -27,12 +27,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import nw
import logging
from PyQt5.QtCore import QCoreApplication, Qt
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
from nw.constants import (
nwLabels, nwItemClass, nwItemType, nwItemLayout
trConst, nwLabels, nwItemClass, nwItemType, nwItemLayout
)
logger = logging.getLogger(__name__)
@@ -118,7 +118,7 @@ class GuiItemDetails(QWidget):
self.layoutData.setAlignment(Qt.AlignLeft)
# Character Count
self.cCountName = QLabel(self.tr(" Characters"))
self.cCountName = QLabel(" "+self.tr("Characters"))
self.cCountName.setFont(self.fntLabel)
self.cCountName.setAlignment(Qt.AlignRight)
@@ -127,7 +127,7 @@ class GuiItemDetails(QWidget):
self.cCountData.setAlignment(Qt.AlignRight)
# Word Count
self.wCountName = QLabel(self.tr(" Words"))
self.wCountName = QLabel(" "+self.tr("Words"))
self.wCountName.setFont(self.fntLabel)
self.wCountName.setAlignment(Qt.AlignRight)
@@ -136,7 +136,7 @@ class GuiItemDetails(QWidget):
self.wCountData.setAlignment(Qt.AlignRight)
# Paragraph Count
self.pCountName = QLabel(self.tr(" Paragraphs"))
self.pCountName = QLabel(" "+self.tr("Paragraphs"))
self.pCountName.setFont(self.fntLabel)
self.pCountName.setAlignment(Qt.AlignRight)
@@ -268,10 +268,8 @@ class GuiItemDetails(QWidget):
self.labelData.setText(theLabel)
self.statusData.setText(nwItem.itemStatus)
self.classData.setText(QCoreApplication.translate(
"Constant", nwLabels.CLASS_NAME[nwItem.itemClass]))
self.layoutData.setText(QCoreApplication.translate(
"Constant", nwLabels.LAYOUT_NAME[nwItem.itemLayout]))
self.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass]))
self.layoutData.setText(trConst(nwLabels.LAYOUT_NAME[nwItem.itemLayout]))
if nwItem.itemType == nwItemType.FILE:
self.cCountData.setText(f"{nwItem.charCount:n}")
+15 -12
View File
@@ -27,14 +27,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import nw
import logging
from PyQt5.QtCore import QCoreApplication, pyqtSlot
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QGridLayout, QLineEdit, QComboBox, QLabel,
QDialogButtonBox
)
from nw.gui.custom import QSwitch
from nw.constants import nwLabels, nwItemLayout, nwItemType, nwLists
from nw.constants import trConst, nwLabels, nwItemLayout, nwItemType, nwLists
logger = logging.getLogger(__name__)
@@ -103,8 +103,7 @@ class GuiItemEditor(QDialog):
for itemLayout in nwItemLayout:
if itemLayout in validLayouts:
self.editLayout.addItem(QCoreApplication.translate(
"Constant", nwLabels.LAYOUT_NAME[itemLayout]), itemLayout)
self.editLayout.addItem(trConst(nwLabels.LAYOUT_NAME[itemLayout]), itemLayout)
# Export Switch
self.textExport = QLabel(self.tr("Include when building project"))
@@ -139,17 +138,21 @@ class GuiItemEditor(QDialog):
# Assemble
##
nameLabel = QLabel(self.tr("Label"))
statusLabel = QLabel(self.tr("Status"))
layoutLabel = QLabel(self.tr("Layout"))
self.mainForm = QGridLayout()
self.mainForm.setVerticalSpacing(vSp)
self.mainForm.setHorizontalSpacing(mSp)
self.mainForm.addWidget(QLabel(self.tr("Label")), 0, 0, 1, 1)
self.mainForm.addWidget(self.editName, 0, 1, 1, 2)
self.mainForm.addWidget(QLabel(self.tr("Status")), 1, 0, 1, 1)
self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2)
self.mainForm.addWidget(QLabel(self.tr("Layout")), 2, 0, 1, 1)
self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2)
self.mainForm.addWidget(self.textExport, 3, 0, 1, 2)
self.mainForm.addWidget(self.editExport, 3, 2, 1, 1)
self.mainForm.addWidget(nameLabel, 0, 0, 1, 1)
self.mainForm.addWidget(self.editName, 0, 1, 1, 2)
self.mainForm.addWidget(statusLabel, 1, 0, 1, 1)
self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2)
self.mainForm.addWidget(layoutLabel, 2, 0, 1, 1)
self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2)
self.mainForm.addWidget(self.textExport, 3, 0, 1, 2)
self.mainForm.addWidget(self.editExport, 3, 2, 1, 1)
self.mainForm.setColumnStretch(0, 0)
self.mainForm.setColumnStretch(1, 1)
self.mainForm.setColumnStretch(2, 0)
+22 -14
View File
@@ -27,13 +27,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import nw
import logging
from PyQt5.QtCore import QCoreApplication, QUrl, QProcess
from PyQt5.QtCore import QUrl, QProcess
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QMenuBar, QAction
from nw.constants import (
nwItemType, nwItemClass, nwDocAction, nwDocInsert, nwKeyWords, nwLabels,
nwUnicode
trConst, nwItemType, nwItemClass, nwDocAction, nwDocInsert, nwKeyWords,
nwLabels, nwUnicode
)
logger = logging.getLogger(__name__)
@@ -566,7 +566,8 @@ class GuiMainMenu(QMenuBar):
# Insert > Figure Dash
self.aInsFigDash = QAction(self.tr("Figure Dash"), self)
self.aInsFigDash.setStatusTip(
self.tr("Insert figure dash (same width as a number character)"))
self.tr("Insert figure dash (same width as a number character)")
)
self.aInsFigDash.setShortcut("Ctrl+K, ~")
self.aInsFigDash.triggered.connect(lambda: self._docInsert(nwUnicode.U_FGDASH))
self.mInsDashes.addAction(self.aInsFigDash)
@@ -740,8 +741,7 @@ class GuiMainMenu(QMenuBar):
self.mInsKWItems[nwKeyWords.ENTITY_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, E")
self.mInsKWItems[nwKeyWords.CUSTOM_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, X")
for n, keyWord in enumerate(self.mInsKWItems):
self.mInsKWItems[keyWord][0].setText(
QCoreApplication.translate("Constant", nwLabels.KEY_NAME[keyWord]))
self.mInsKWItems[keyWord][0].setText(trConst(nwLabels.KEY_NAME[keyWord]))
self.mInsKWItems[keyWord][0].setShortcut(self.mInsKWItems[keyWord][1])
self.mInsKWItems[keyWord][0].triggered.connect(
lambda n, keyWord=keyWord: self._insertKeyWord(keyWord)
@@ -796,7 +796,8 @@ class GuiMainMenu(QMenuBar):
# Search > Replace Next
self.aReplaceNext = QAction(self.tr("Replace Next"), self)
self.aReplaceNext.setStatusTip(
self.tr("Find and replace next occurrence text in document"))
self.tr("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)
@@ -898,14 +899,16 @@ class GuiMainMenu(QMenuBar):
# Format > Replace Single Quotes
self.aFmtReplSng = QAction(self.tr("Replace Single Quotes"), self)
self.aFmtReplSng.setStatusTip(
self.tr("Replace all straight single quotes in selected text"))
self.tr("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(self.tr("Replace Double Quotes"), self)
self.aFmtReplDbl.setStatusTip(
self.tr("Replace all straight double quotes in selected text"))
self.tr("Replace all straight double quotes in selected text")
)
self.aFmtReplDbl.triggered.connect(lambda: self._docAction(nwDocAction.REPL_DBL))
self.fmtMenu.addAction(self.aFmtReplDbl)
@@ -1031,7 +1034,8 @@ class GuiMainMenu(QMenuBar):
self.aHelpWeb = QAction(self.tr("Documentation (Online)"), self)
self.aHelpWeb.setStatusTip(
self.tr("View online documentation at {0}").format(nw.__docurl__))
self.tr("View online documentation at {0}").format(nw.__docurl__)
)
self.aHelpWeb.triggered.connect(lambda: self._openWebsite(nw.__docurl__))
if self.mainConf.hasHelp and self.mainConf.hasAssistant:
self.aHelpWeb.setShortcut("Shift+F1")
@@ -1045,28 +1049,32 @@ class GuiMainMenu(QMenuBar):
# Document > Report an Issue
self.aIssue = QAction(self.tr("Report an Issue (GitHub)"), self)
self.aIssue.setStatusTip(
self.tr("Report a bug or issue on GitHub at {0}").format(nw.__issuesurl__))
self.tr("Report a bug or issue on GitHub at {0}").format(nw.__issuesurl__)
)
self.aIssue.triggered.connect(lambda: self._openWebsite(nw.__issuesurl__))
self.helpMenu.addAction(self.aIssue)
# Document > Ask a Question
self.aQuestion = QAction(self.tr("Ask a Question (GitHub)"), self)
self.aQuestion.setStatusTip(
self.tr("Ask a question on GitHub at {0}").format(nw.__helpurl__))
self.tr("Ask a question on GitHub at {0}").format(nw.__helpurl__)
)
self.aQuestion.triggered.connect(lambda: self._openWebsite(nw.__helpurl__))
self.helpMenu.addAction(self.aQuestion)
# Document > Latest Release
self.aRelease = QAction(self.tr("Latest Release (GitHub)"), self)
self.aRelease.setStatusTip(
self.tr("Open the Releases page on GitHub at {0}").format(nw.__releaseurl__))
self.tr("Open the Releases page on GitHub at {0}").format(nw.__releaseurl__)
)
self.aRelease.triggered.connect(lambda: self._openWebsite(nw.__releaseurl__))
self.helpMenu.addAction(self.aRelease)
# Document > Main Website
self.aWebsite = QAction(self.tr("The novelWriter Website"), self)
self.aWebsite.setStatusTip(
self.tr("Open the novelWriter website at {0}").format(nw.__url__))
self.tr("Open the novelWriter website at {0}").format(nw.__url__)
)
self.aWebsite.triggered.connect(lambda: self._openWebsite(nw.__url__))
self.helpMenu.addAction(self.aWebsite)
+5 -8
View File
@@ -29,12 +29,12 @@ import logging
from time import time
from PyQt5.QtCore import QCoreApplication, Qt, QSize
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QMenu, QAction, QAbstractItemView
)
from nw.constants import nwKeyWords, nwLabels, nwOutline
from nw.constants import trConst, nwKeyWords, nwLabels, nwOutline
logger = logging.getLogger(__name__)
@@ -149,8 +149,7 @@ class GuiOutline(QTreeWidget):
"""
self.clear()
self.setColumnCount(1)
self.setHeaderLabel(
QCoreApplication.translate("Constant", nwLabels.OUTLINE_COLS[nwOutline.TITLE]))
self.setHeaderLabel(trConst(nwLabels.OUTLINE_COLS[nwOutline.TITLE]))
self.treeOrder = []
self.colWidth = {}
@@ -356,8 +355,7 @@ class GuiOutline(QTreeWidget):
if self.firstView:
theLabels = []
for i, hItem in enumerate(self.treeOrder):
theLabels.append(
QCoreApplication.translate("Constant", nwLabels.OUTLINE_COLS[hItem]))
theLabels.append(trConst(nwLabels.OUTLINE_COLS[hItem]))
self.colIndex[hItem] = i
self.setHeaderLabels(theLabels)
@@ -484,8 +482,7 @@ class GuiOutlineHeaderMenu(QMenu):
for hItem in nwOutline:
if hItem == nwOutline.TITLE:
continue
self.actionMap[hItem] = QAction(
QCoreApplication.translate("Constant", nwLabels.OUTLINE_COLS[hItem]), self)
self.actionMap[hItem] = QAction(trConst(nwLabels.OUTLINE_COLS[hItem]), self)
self.actionMap[hItem].setCheckable(True)
self.actionMap[hItem].toggled.connect(
lambda isChecked, tItem=hItem : self._columnToggled(isChecked, tItem)
+16 -24
View File
@@ -27,12 +27,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import nw
import logging
from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP, Qt
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QT_TRANSLATE_NOOP as QT_TRN
from PyQt5.QtWidgets import (
QScrollArea, QWidget, QGridLayout, QHBoxLayout, QGroupBox, QLabel
)
from nw.constants import nwLabels, nwKeyWords
from nw.constants import trConst, nwLabels, nwKeyWords
from nw.common import checkInt
logger = logging.getLogger(__name__)
@@ -40,10 +41,10 @@ logger = logging.getLogger(__name__)
class GuiOutlineDetails(QScrollArea):
LVL_MAP = {
"H1" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"),
"H2" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Chapter"),
"H3" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Scene"),
"H4" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Section"),
"H1" : QT_TRN("GuiOutlineDetails", "Title"),
"H2" : QT_TRN("GuiOutlineDetails", "Chapter"),
"H3" : QT_TRN("GuiOutlineDetails", "Scene"),
"H4" : QT_TRN("GuiOutlineDetails", "Section"),
}
def __init__(self, theParent):
@@ -104,24 +105,15 @@ class GuiOutlineDetails(QScrollArea):
self.synopLWrap.addWidget(self.synopValue, 1)
# Tags
self.povKeyLabel = QLabel("<b>%s</b>" % QCoreApplication.translate(
"Constant", nwLabels.KEY_NAME[nwKeyWords.POV_KEY]))
self.focKeyLabel = QLabel("<b>%s</b>" % QCoreApplication.translate(
"Constant", nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY]))
self.chrKeyLabel = QLabel("<b>%s</b>" % QCoreApplication.translate(
"Constant", nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY]))
self.pltKeyLabel = QLabel("<b>%s</b>" % QCoreApplication.translate(
"Constant", nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY]))
self.timKeyLabel = QLabel("<b>%s</b>" % QCoreApplication.translate(
"Constant", nwLabels.KEY_NAME[nwKeyWords.TIME_KEY]))
self.wldKeyLabel = QLabel("<b>%s</b>" % QCoreApplication.translate(
"Constant", nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY]))
self.objKeyLabel = QLabel("<b>%s</b>" % QCoreApplication.translate(
"Constant", nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY]))
self.entKeyLabel = QLabel("<b>%s</b>" % QCoreApplication.translate(
"Constant", nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY]))
self.cstKeyLabel = QLabel("<b>%s</b>" % QCoreApplication.translate(
"Constant", nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY]))
self.povKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY]))
self.focKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY]))
self.chrKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY]))
self.pltKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY]))
self.timKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.TIME_KEY]))
self.wldKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY]))
self.objKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY]))
self.entKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY]))
self.cstKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY]))
self.povKeyLWrap = QHBoxLayout()
self.focKeyLWrap = QHBoxLayout()
+13 -9
View File
@@ -155,8 +155,11 @@ class GuiProjectDetailsMain(QWidget):
self.bookTitle.setAlignment(Qt.AlignHCenter)
self.bookTitle.setWordWrap(True)
self.projName = QLabel(self.tr("{0}: {1}").format(
self.tr("Working Title"), self.theProject.projName))
self.projName = QLabel(
self.tr("{0}: {1}").format(
self.tr("Working Title"), self.theProject.projName
)
)
workFont = self.projName.font()
workFont.setPointSizeF(0.8*fPt)
workFont.setItalic(True)
@@ -273,13 +276,14 @@ class GuiProjectDetailsContents(QWidget):
self.tocTree.setIndentation(0)
self.tocTree.setColumnCount(6)
self.tocTree.setSelectionMode(QAbstractItemView.NoSelection)
self.tocTree.setHeaderLabels(
[self.tr("Title"),
self.tr("Words"),
self.tr("Pages"),
self.tr("Page"),
self.tr("Progress"),
""])
self.tocTree.setHeaderLabels([
self.tr("Title"),
self.tr("Words"),
self.tr("Pages"),
self.tr("Page"),
self.tr("Progress"),
""
])
treeHeadItem = self.tocTree.headerItem()
treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
+6 -3
View File
@@ -192,7 +192,8 @@ class GuiProjectLoad(QDialog):
self, self.tr("Open novelWriter Project"), "",
";;".join([
self.tr("{0} ({1})").format(
self.tr("novelWriter Project File"), nwFiles.PROJ_FILE),
self.tr("novelWriter Project File"), nwFiles.PROJ_FILE
),
self.tr("{0} ({1})").format(self.tr("All Files"), "*")
]),
options=dlgOpt
@@ -233,8 +234,10 @@ class GuiProjectLoad(QDialog):
projName = selList[0].text(self.C_NAME)
msgYes = self.theParent.askQuestion(
self.tr("Remove Entry"),
self.tr("Remove '{0}' from the recent projects list? "
"The project files will not be deleted.").format(projName)
self.tr(
"Remove '{0}' from the recent projects list? "
"The project files will not be deleted."
).format(projName)
)
if msgYes:
self.mainConf.removeFromRecentCache(
+5 -3
View File
@@ -378,7 +378,8 @@ class GuiProjectEditStatus(QWidget):
self.colData[selIdx][4]
)
selItem.setText(self.tr("{0} [{1}]").format(
self.colData[selIdx][0], self.colCounts[selIdx]))
self.colData[selIdx][0], self.colCounts[selIdx])
)
selItem.setIcon(self.colButton.icon())
self.editName.setEnabled(False)
self.colChanged = True
@@ -496,7 +497,8 @@ class GuiProjectEditReplace(QWidget):
self.bottomBox.addWidget(self.delButton)
self.outerBox.addWidget(
QLabel("<b>%s</b>" % self.tr("Text Replace List for Preview and Export")))
QLabel("<b>%s</b>" % self.tr("Text Replace List for Preview and Export"))
)
self.outerBox.addWidget(self.listBox)
self.outerBox.addLayout(self.bottomBox)
self.setLayout(self.outerBox)
@@ -548,7 +550,7 @@ class GuiProjectEditReplace(QWidget):
saveKey = self._stripNotAllowed(newKey)
if len(saveKey) > 0 and len(newVal) > 0:
selItem.setText(0, self.tr("<{0}>").format(saveKey))
selItem.setText(0, "<%s>" % saveKey)
selItem.setText(1, newVal)
self.editKey.clear()
self.editValue.clear()
+16 -17
View File
@@ -30,7 +30,7 @@ import logging
from time import time
from PyQt5.QtCore import QCoreApplication, Qt, QSize, pyqtSignal
from PyQt5.QtCore import Qt, QSize, pyqtSignal
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMenu, QAction
@@ -38,7 +38,8 @@ from PyQt5.QtWidgets import (
from nw.core import NWDoc
from nw.constants import (
nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert, nwConst, nwLists
trConst, nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert,
nwConst, nwLists
)
logger = logging.getLogger(__name__)
@@ -214,8 +215,7 @@ class GuiProjectTree(QTreeWidget):
)
if itemType == nwItemType.ROOT:
tHandle = self.theProject.newRoot(
QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[itemClass]), itemClass)
tHandle = self.theProject.newRoot(trConst(nwLabels.CLASS_NAME[itemClass]), itemClass)
if tHandle is None:
logger.error("No root item added")
return False
@@ -250,8 +250,7 @@ class GuiProjectTree(QTreeWidget):
if self.theProject.projTree.isTrashRoot(pHandle):
self.makeAlert(
self.tr("Cannot add new files or folders to the {0} folder.").format(
QCoreApplication.translate(
"Constant", nwLabels.CLASS_NAME[nwItemClass.TRASH])
trConst(nwLabels.CLASS_NAME[nwItemClass.TRASH])
), nwAlert.ERROR
)
return False
@@ -573,10 +572,10 @@ class GuiProjectTree(QTreeWidget):
self._deleteTreeItem(tHandle)
self._setTreeChanged(True)
else:
self.makeAlert((
self.tr("Cannot delete folder. It is not empty."),
self.tr("Recursive deletion is not supported."),
self.tr("Please delete the content first."),
self.makeAlert(self.tr(
"Cannot delete folder. It is not empty. "
"Recursive deletion is not supported. "
"Please delete the content first."
), nwAlert.ERROR)
return False
@@ -589,10 +588,10 @@ class GuiProjectTree(QTreeWidget):
self.theParent.mainMenu.setAvailableRoot()
self._setTreeChanged(True)
else:
self.makeAlert((
self.tr("Cannot delete root folder. It is not empty."),
self.tr("Recursive deletion is not supported."),
self.tr("Please delete the content first."),
self.makeAlert(self.tr(
"Cannot delete root folder. It is not empty. "
"Recursive deletion is not supported. "
"Please delete the content first."
), nwAlert.ERROR)
return False
@@ -974,9 +973,9 @@ class GuiProjectTree(QTreeWidget):
self.addTopLevelItem(newItem)
else:
self.makeAlert(
self.tr("There is nowhere to add item with name '{0}'").format(
nwItem.itemName),
nwAlert.ERROR
self.tr(
"There is nowhere to add item with name '{0}'").format(nwItem.itemName
), nwAlert.ERROR
)
del self._treeMap[tHandle]
return None
+42 -27
View File
@@ -28,7 +28,7 @@ import nw
import logging
import os
from PyQt5.QtCore import QCoreApplication, Qt
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QWizard, QWizardPage, QLabel, QVBoxLayout, QLineEdit, QPlainTextEdit,
QPushButton, QFileDialog, QHBoxLayout, QRadioButton, QFormLayout,
@@ -36,7 +36,7 @@ from PyQt5.QtWidgets import (
)
from nw.common import makeFileNameSafe
from nw.constants import nwLabels, nwItemClass
from nw.constants import trConst, nwLabels, nwItemClass
from nw.gui.custom import QSwitch
logger = logging.getLogger(__name__)
@@ -96,10 +96,12 @@ class ProjWizardIntroPage(QWizardPage):
self.setTitle(self.tr("Create New Project"))
self.theText = QLabel(
self.tr("Provide at least a working title. The working title should not "
"be change beyond this point as it is used by the application for "
"generating file names for for instance backups. The other fields "
"are optional and can be changed at any time in Project Settings.")
self.tr(
"Provide at least a working title. The working title should not "
"be change beyond this point as it is used by the application for "
"generating file names for for instance backups. The other fields "
"are optional and can be changed at any time in Project Settings."
)
)
self.theText.setWordWrap(True)
@@ -166,8 +168,10 @@ class ProjWizardFolderPage(QWizardPage):
self.setTitle(self.tr("Select Project Folder"))
self.theText = QLabel(
self.tr("Select a location to store the project. A new project folder "
"will be created in the selected location.")
self.tr(
"Select a location to store the project. A new project folder "
"will be created in the selected location."
)
)
self.theText.setWordWrap(True)
@@ -240,9 +244,11 @@ class ProjWizardPopulatePage(QWizardPage):
self.setTitle(self.tr("Populate Project"))
self.theText = QLabel(
self.tr("Choose how to pre-fill the project. Either with a minimal set of "
"starter items, an example project explaining and showing many of "
"the features, or show further custom options on the next page.")
self.tr(
"Choose how to pre-fill the project. Either with a minimal set of "
"starter items, an example project explaining and showing many of "
"the features, or show further custom options on the next page."
)
)
self.theText.setWordWrap(True)
@@ -295,9 +301,11 @@ class ProjWizardCustomPage(QWizardPage):
self.setTitle(self.tr("Custom Project Options"))
self.theText = QLabel(
self.tr("Select which additional root folders to make, and how to populate "
"the Novel folder. If you don't want to add chapters or scenes, set "
"the values to 0. You can add scenes without chapters.")
self.tr(
"Select which additional root folders to make, and how to populate "
"the Novel folder. If you don't want to add chapters or scenes, set "
"the values to 0. You can add scenes without chapters."
)
)
self.theText.setWordWrap(True)
@@ -308,18 +316,24 @@ class ProjWizardCustomPage(QWizardPage):
self.rootForm = QGridLayout()
self.rootGroup.setLayout(self.rootForm)
self.lblPlot = QLabel(self.tr("{0} folder").format(
QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.PLOT])))
self.lblChar = QLabel(self.tr("{0} folder").format(
QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.CHARACTER])))
self.lblWorld = QLabel(self.tr("{0} folder").format(
QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.WORLD])))
self.lblTime = QLabel(self.tr("{0} folder").format(
QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.TIMELINE])))
self.lblPlot = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.PLOT]))
)
self.lblChar = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.CHARACTER]))
)
self.lblWorld = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.WORLD]))
)
self.lblTime = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.TIMELINE]))
)
self.lblObject = QLabel(self.tr("{0} folder").format(
QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.OBJECT])))
trConst(nwLabels.CLASS_NAME[nwItemClass.OBJECT]))
)
self.lblEntity = QLabel(self.tr("{0} folder").format(
QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.ENTITY])))
trConst(nwLabels.CLASS_NAME[nwItemClass.ENTITY]))
)
self.addPlot = QSwitch()
self.addChar = QSwitch()
@@ -407,9 +421,10 @@ class ProjWizardFinalPage(QWizardPage):
self.setTitle(self.tr("Finished"))
self.theText = QLabel("".join([
("<p>%s</p>" % self.tr("All done.")),
("<p>%s</p>" % self.tr("Press '{0}' to create the new project.").format(
self.tr("Done") if self.mainConf.osDarwin else self.tr("Finish")))
"<p>%s</p>" % self.tr("All done."),
"<p>%s</p>" % self.tr("Press '{0}' to create the new project.").format(
self.tr("Done") if self.mainConf.osDarwin else self.tr("Finish")
)
]))
self.theText.setWordWrap(True)
+9 -4
View File
@@ -31,8 +31,9 @@ import configparser
import os
from math import ceil
from functools import partial
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QCoreApplication, Qt
from PyQt5.QtWidgets import QStyle, qApp
from PyQt5.QtGui import (
QPalette, QColor, QIcon, QFont, QFontMetrics, QFontDatabase, QPixmap
@@ -157,6 +158,10 @@ class GuiTheme:
logger.verbose("Text 'N' Height: %d" % self.textNHeight)
logger.verbose("Text 'N' Width: %d" % self.textNWidth)
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
self.tr = partial(QCoreApplication.translate, "GuiTheme")
return
##
@@ -392,7 +397,7 @@ class GuiTheme:
with open(themeConf, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
self.makeAlert(
[self.tr("Could not load theme config file."), str(e)], nwAlert.ERROR
)
continue
@@ -425,7 +430,7 @@ class GuiTheme:
with open(syntaxPath, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
self.makeAlert(
[self.tr("Could not load syntax file."), str(e)], nwAlert.ERROR
)
return []
@@ -740,7 +745,7 @@ class GuiIcons:
with open(themeConf, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
self.makeAlert(
[self.tr("Could not load theme config file."), str(e)], nwAlert.ERROR
)
continue
+15 -6
View File
@@ -159,18 +159,27 @@ class GuiWritingStats(QDialog):
self.totalWords.setFont(self.theTheme.guiFontFixed)
self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Total Time"))), 0, 0)
self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Idle Time"))), 1, 0)
self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Filtered Time"))), 2, 0)
self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Novel Word Count"))), 3, 0)
self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Notes Word Count"))), 4, 0)
self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Total Word Count"))), 5, 0)
lblTTime = QLabel(self.tr("{0}:").format(self.tr("Total Time")))
lblITime = QLabel(self.tr("{0}:").format(self.tr("Idle Time")))
lblFTime = QLabel(self.tr("{0}:").format(self.tr("Filtered Time")))
lblNvCount = QLabel(self.tr("{0}:").format(self.tr("Novel Word Count")))
lblNtCount = QLabel(self.tr("{0}:").format(self.tr("Notes Word Count")))
lblTtCount = QLabel(self.tr("{0}:").format(self.tr("Total Word Count")))
self.infoForm.addWidget(lblTTime, 0, 0)
self.infoForm.addWidget(lblITime, 1, 0)
self.infoForm.addWidget(lblFTime, 2, 0)
self.infoForm.addWidget(lblNvCount, 3, 0)
self.infoForm.addWidget(lblNtCount, 4, 0)
self.infoForm.addWidget(lblTtCount, 5, 0)
self.infoForm.addWidget(self.labelTotal, 0, 1)
self.infoForm.addWidget(self.labelIdleT, 1, 1)
self.infoForm.addWidget(self.labelFilter, 2, 1)
self.infoForm.addWidget(self.novelWords, 3, 1)
self.infoForm.addWidget(self.notesWords, 4, 1)
self.infoForm.addWidget(self.totalWords, 5, 1)
self.infoForm.setRowStretch(6, 1)
# Filter Options
+46 -27
View File
@@ -358,9 +358,10 @@ class GuiMain(QMainWindow):
if os.path.isfile(os.path.join(projPath, self.theProject.projFile)):
self.makeAlert(
self.tr("A project already exists in that location. "
"Please choose another folder."),
nwAlert.ERROR
self.tr(
"A project already exists in that location. "
"Please choose another folder."
), nwAlert.ERROR
)
return False
@@ -394,8 +395,10 @@ class GuiMain(QMainWindow):
if not isYes:
msgYes = self.askQuestion(
self.tr("Close Project"),
"%s<br>%s" % (self.tr("Close the current project?"),
self.tr("Changes are saved automatically."))
"%s<br>%s" % (
self.tr("Close the current project?"),
self.tr("Changes are saved automatically.")
)
)
if not msgYes:
return False
@@ -461,9 +464,11 @@ class GuiMain(QMainWindow):
try:
lockDetails = (
"<br>%s" % self.tr("The project was locked by the computer "
"'{computer_name}' ({os_name} {os_version}), "
"last active on {time}")
"<br>%s" % self.tr(
"The project was locked by the computer "
"'{computer_name}' ({os_name} {os_version}), "
"last active on {time}"
)
).format(
computer_name = self.theProject.lockedBy[0],
os_name = self.theProject.lockedBy[1],
@@ -479,12 +484,16 @@ class GuiMain(QMainWindow):
msgRes = msgBox.warning(
self, self.tr("Project Locked"),
"%s<br><br>%s<br>%s" % (
self.tr("The project is already open by another instance of novelWriter, and "
"is therefore locked. Override lock and continue anyway?"),
self.tr("Note: If the program or the computer previously crashed, the lock "
"can safely be overridden. If, however, another instance of "
"novelWriter has the project open, overriding the lock may corrupt "
"the project, and is not recommended."),
self.tr(
"The project is already open by another instance of novelWriter, and "
"is therefore locked. Override lock and continue anyway?"
),
self.tr(
"Note: If the program or the computer previously crashed, the lock "
"can safely be overridden. If, however, another instance of "
"novelWriter has the project open, overriding the lock may corrupt "
"the project, and is not recommended."
),
lockDetails
),
QMessageBox.Yes | QMessageBox.No, QMessageBox.No
@@ -732,10 +741,13 @@ class GuiMain(QMainWindow):
return False
if not self.docEditor.isEmpty():
msgYes = self.askQuestion(self.tr("Import Document"), (
self.tr("Importing the file will overwrite the current content of the document. "
"Do you want to proceed?")
))
msgYes = self.askQuestion(
self.tr("Import Document"),
self.tr(
"Importing the file will overwrite the current content of the document. "
"Do you want to proceed?"
)
)
if not msgYes:
return False
@@ -874,9 +886,12 @@ class GuiMain(QMainWindow):
if tItem is not None:
self.setStatus(self.tr("{0}: '{1}'").format(self.tr("Indexing"), tItem.itemName))
else:
self.setStatus(self.tr("{0}: {1}").format(
self.tr("Indexing"),
self.tr("Unknown item")))
self.setStatus(
self.tr("{0}: {1}").format(
self.tr("Indexing"),
self.tr("Unknown item")
)
)
if tItem is not None and tItem.itemType == nwItemType.FILE:
logger.verbose("Scanning: %s" % tItem.itemName)
@@ -894,14 +909,16 @@ class GuiMain(QMainWindow):
self.treeView.projectWordCount()
tEnd = time()
self.setStatus(self.tr("Indexing completed in {0} ms").
format(f"{(tEnd - tStart)*1000.0:.1f}"))
self.setStatus(
self.tr("Indexing completed in {0} ms").format(f"{(tEnd - tStart)*1000.0:.1f}")
)
self.docEditor.updateTagHighLighting()
qApp.restoreOverrideCursor()
if not beQuiet:
self.makeAlert(self.tr("The project index has been successfully rebuilt."),
nwAlert.INFO)
self.makeAlert(
self.tr("The project index has been successfully rebuilt."), nwAlert.INFO
)
return True
@@ -1153,8 +1170,10 @@ class GuiMain(QMainWindow):
if self.hasProject:
msgYes = self.askQuestion(
self.tr("Exit"),
"%s<br>%s" % (self.tr("Do you want to exit novelWriter?"),
self.tr("Changes are saved automatically."))
"%s<br>%s" % (
self.tr("Do you want to exit novelWriter?"),
self.tr("Changes are saved automatically.")
)
)
if not msgYes:
return False
+4 -4
View File
@@ -1,13 +1,13 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.2b1" hexVersion="0x010200b1" fileVersion="1.2" timeStamp="2021-02-11 17:04:25">
<novelWriterXML appVersion="1.3a0" hexVersion="0x010300a0" fileVersion="1.2" timeStamp="2021-02-15 16:31:16">
<project>
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
<author>Jay Doh</author>
<saveCount>936</saveCount>
<saveCount>1019</saveCount>
<autoCount>161</autoCount>
<editTime>46791</editTime>
<editTime>48272</editTime>
</project>
<settings>
<doBackup>False</doBackup>
@@ -120,7 +120,7 @@
<charCount>1810</charCount>
<wordCount>318</wordCount>
<paraCount>8</paraCount>
<cursorPos>3</cursorPos>
<cursorPos>1112</cursorPos>
</item>
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
<name>Another Scene</name>