Merge branch 'main' into i18n-de_DE-created

This commit is contained in:
Veronica Berglyd Olsen
2022-11-13 10:52:13 +01:00
committed by GitHub
214 changed files with 23012 additions and 14496 deletions
+3 -4
View File
@@ -23,7 +23,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/>.
"""
import os
import logging
import novelwriter
@@ -44,7 +43,7 @@ logger = logging.getLogger(__name__)
class GuiAbout(QDialog):
def __init__(self, mainGui):
QDialog.__init__(self, mainGui)
super().__init__(parent=mainGui)
logger.debug("Initialising GuiAbout ...")
self.setObjectName("GuiAbout")
@@ -234,7 +233,7 @@ class GuiAbout(QDialog):
def _fillNotesPage(self):
"""Load the content for the Release Notes page.
"""
docPath = os.path.join(self.mainConf.assetPath, "text", "release_notes.htm")
docPath = self.mainConf.assetPath("text") / "release_notes.htm"
docText = readTextFile(docPath)
if docText:
self.pageNotes.setHtml(docText)
@@ -245,7 +244,7 @@ class GuiAbout(QDialog):
def _fillLicensePage(self):
"""Load the content for the Licence page.
"""
docPath = os.path.join(self.mainConf.assetPath, "text", "gplv3_en.htm")
docPath = self.mainConf.assetPath("text") / "gplv3_en.htm"
docText = readTextFile(docPath)
if docText:
self.pageLicense.setHtml(docText)
+88 -110
View File
@@ -1,10 +1,11 @@
"""
novelWriter GUI Doc Merge Tool
================================
GUI class for merging multiple documents to one document
novelWriter GUI Doc Merge Dialog
==================================
Custom dialog class for merging documents.
File History:
Created: 2020-01-23 [0.4.3]
Created: 2020-01-23 [0.4.3]
Rewritten: 2022-10-06 [2.0b1]
This file is a part of novelWriter
Copyright 20182022, Veronica Berglyd Olsen
@@ -26,169 +27,146 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import logging
import novelwriter
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QLabel, QListWidget, QAbstractItemView,
QListWidgetItem, QDialogButtonBox
QAbstractItemView, QDialog, QDialogButtonBox, QGridLayout, QLabel,
QListWidget, QListWidgetItem, QVBoxLayout,
)
from novelwriter.core import NWDoc
from novelwriter.enum import nwAlert, nwItemType
from novelwriter.gui.custom import QHelpLabel
from novelwriter.custom import QHelpLabel, QSwitch
logger = logging.getLogger(__name__)
class GuiDocMerge(QDialog):
def __init__(self, mainGui):
QDialog.__init__(self, mainGui)
def __init__(self, mainGui, sHandle, itemList):
super().__init__(parent=mainGui)
logger.debug("Initialising GuiDocMerge ...")
self.setObjectName("GuiDocMerge")
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.sourceItem = None
self.outerBox = QVBoxLayout()
self._data = {}
self.setWindowTitle(self.tr("Merge Documents"))
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge")))
self.helpLabel = QHelpLabel(
self.tr("Drag and drop items to change the order."), self.mainGui.mainTheme.helpText
)
self.helpLabel = QHelpLabel(self.tr(
"Drag and drop items to change the order, or uncheck to exclude."
), self.mainTheme.helpText)
iPx = self.mainTheme.baseIconSize
hSp = self.mainConf.pxInt(12)
vSp = self.mainConf.pxInt(8)
bSp = self.mainConf.pxInt(12)
self.listBox = QListWidget()
self.listBox.setDragDropMode(QAbstractItemView.InternalMove)
self.listBox.setIconSize(QSize(iPx, iPx))
self.listBox.setMinimumWidth(self.mainConf.pxInt(400))
self.listBox.setMinimumHeight(self.mainConf.pxInt(180))
self.listBox.setSelectionBehavior(QAbstractItemView.SelectRows)
self.listBox.setSelectionMode(QAbstractItemView.SingleSelection)
self.listBox.setDragDropMode(QAbstractItemView.InternalMove)
# Merge Options
self.trashLabel = QLabel(self.tr("Move merged items to Trash"))
self.trashSwitch = QSwitch(width=2*iPx, height=iPx)
self.optBox = QGridLayout()
self.optBox.addWidget(self.trashLabel, 0, 0)
self.optBox.addWidget(self.trashSwitch, 0, 1)
self.optBox.setHorizontalSpacing(hSp)
self.optBox.setColumnStretch(2, 1)
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self._doMerge)
self.buttonBox.rejected.connect(self._doClose)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
self.resetButton = self.buttonBox.addButton(QDialogButtonBox.Reset)
self.resetButton.clicked.connect(self._resetList)
# Assemble
self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(0)
self.outerBox.addWidget(self.headLabel)
self.outerBox.addWidget(self.helpLabel)
self.outerBox.addSpacing(self.mainConf.pxInt(8))
self.outerBox.addSpacing(vSp)
self.outerBox.addWidget(self.listBox)
self.outerBox.addSpacing(self.mainConf.pxInt(12))
self.outerBox.addSpacing(vSp)
self.outerBox.addLayout(self.optBox)
self.outerBox.addSpacing(bSp)
self.outerBox.addWidget(self.buttonBox)
self.setLayout(self.outerBox)
self.rejected.connect(self._doClose)
self._populateList()
# Load Content
self._loadContent(sHandle, itemList)
logger.debug("GuiDocMerge initialisation complete")
return
##
# Buttons
##
def _doMerge(self):
"""Perform the merge of the files in the selected folder, and
create a new file in the same parent folder. The old files are
not removed in the merge process, and must be deleted manually.
def getData(self):
"""Return the user's choices.
"""
logger.verbose("GuiDocMerge merge button clicked")
finalOrder = []
finalItems = []
for i in range(self.listBox.count()):
finalOrder.append(self.listBox.item(i).data(Qt.UserRole))
item = self.listBox.item(i)
if item is not None and item.checkState() == Qt.Checked:
finalItems.append(item.data(Qt.UserRole))
if len(finalOrder) == 0:
self.mainGui.makeAlert(self.tr(
"No source documents found. Nothing to do."
), nwAlert.ERROR)
return False
self._data["moveToTrash"] = self.trashSwitch.isChecked()
self._data["finalItems"] = finalItems
theText = ""
for tHandle in finalOrder:
inDoc = NWDoc(self.theProject, tHandle)
docText = inDoc.readDocument()
docErr = inDoc.getError()
if docText is None and docErr:
self.mainGui.makeAlert([
self.tr("Failed to open document file."), docErr
], nwAlert.ERROR)
if docText:
theText += docText.rstrip("\n")+"\n\n"
return self._data
if self.sourceItem is None:
self.mainGui.makeAlert(self.tr(
"No source folder selected. Nothing to do."
), nwAlert.ERROR)
return False
##
# Slots
##
srcItem = self.theProject.tree[self.sourceItem]
if srcItem is None:
self.mainGui.makeAlert(self.tr("Internal error."), nwAlert.ERROR)
return False
nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemParent)
newItem = self.theProject.tree[nHandle]
newItem.setStatus(srcItem.itemStatus)
newItem.setImport(srcItem.itemImport)
outDoc = NWDoc(self.theProject, nHandle)
if not outDoc.writeDocument(theText):
self.mainGui.makeAlert([
self.tr("Could not save document."), outDoc.getError()
], nwAlert.ERROR)
return False
self.mainGui.projView.revealNewTreeItem(nHandle)
self.mainGui.openDocument(nHandle, doScroll=True)
self._doClose()
return True
def _doClose(self):
"""Close the dialog window without doing anything.
def _resetList(self):
"""Reset the content of the list box to its original state.
"""
self.close()
logger.debug("Resetting list box content")
sHandle = self._data.get("sHandle", None)
itemList = self._data.get("origItems", [])
self._loadContent(sHandle, itemList)
return
##
# Internal Functions
##
def _populateList(self):
"""Get the item selected in the tree, check that it is a folder,
and try to find all files associated with it. The valid files
are then added to the list view in order. The list itself can be
reordered by the user.
def _loadContent(self, sHandle, itemList):
"""Load content from a given list of items.
"""
tHandle = self.mainGui.projView.getSelectedHandle()
self.sourceItem = tHandle
if tHandle is None:
return False
self._data = {}
self._data["sHandle"] = sHandle
self._data["origItems"] = itemList
nwItem = self.theProject.tree[tHandle]
if nwItem is None:
return False
if nwItem.itemType is not nwItemType.FOLDER:
self.mainGui.makeAlert(self.tr(
"Element selected in the project tree must be a folder."
), nwAlert.ERROR)
return False
for sHandle in self.mainGui.projView.getTreeFromHandle(tHandle):
newItem = QListWidgetItem()
nwItem = self.theProject.tree[sHandle]
if nwItem.itemType is not nwItemType.FILE:
self.listBox.clear()
for tHandle in itemList:
nwItem = self.theProject.tree[tHandle]
if nwItem is None or not nwItem.isFileType():
continue
itemIcon = self.mainTheme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading
)
newItem = QListWidgetItem()
newItem.setIcon(itemIcon)
newItem.setText(nwItem.itemName)
newItem.setData(Qt.UserRole, sHandle)
newItem.setData(Qt.UserRole, tHandle)
newItem.setCheckState(Qt.Checked)
self.listBox.addItem(newItem)
return True
return
# END Class GuiDocMerge
+131 -160
View File
@@ -1,10 +1,11 @@
"""
novelWriter GUI Doc Split Tool
================================
GUI class for splitting a single document into multiple documents
novelWriter GUI Doc Split Dialog
==================================
Custom dialog class for splitting documents.
File History:
Created: 2020-02-01 [0.4.3]
Created: 2020-02-01 [0.4.3]
Rewritten: 2022-10-12 [2.0b1]
This file is a part of novelWriter
Copyright 20182022, Veronica Berglyd Olsen
@@ -29,32 +30,34 @@ import novelwriter
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QComboBox, QListWidget, QAbstractItemView,
QListWidgetItem, QDialogButtonBox, QLabel
QListWidgetItem, QDialogButtonBox, QLabel, QGridLayout
)
from novelwriter.core import NWDoc
from novelwriter.enum import nwAlert, nwItemType
from novelwriter.gui.custom import QHelpLabel
from novelwriter.custom import QHelpLabel, QSwitch
logger = logging.getLogger(__name__)
class GuiDocSplit(QDialog):
def __init__(self, mainGui):
QDialog.__init__(self, mainGui)
LINE_ROLE = Qt.UserRole
LEVEL_ROLE = Qt.UserRole + 1
LABEL_ROLE = Qt.UserRole + 2
def __init__(self, mainGui, sHandle):
super().__init__(parent=mainGui)
logger.debug("Initialising GuiDocSplit ...")
self.setObjectName("GuiDocSplit")
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.sourceItem = None
self.sourceText = []
self._data = {}
self._text = []
self.outerBox = QVBoxLayout()
self.setWindowTitle(self.tr("Split Document"))
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
@@ -63,6 +66,18 @@ class GuiDocSplit(QDialog):
self.mainGui.mainTheme.helpText
)
# Values
iPx = self.mainTheme.baseIconSize
hSp = self.mainConf.pxInt(12)
vSp = self.mainConf.pxInt(8)
bSp = self.mainConf.pxInt(12)
pOptions = self.theProject.options
spLevel = pOptions.getInt("GuiDocSplit", "spLevel", 3)
intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True)
docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True)
# Header Selection
self.listBox = QListWidget()
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
self.listBox.setMinimumWidth(self.mainConf.pxInt(400))
@@ -73,206 +88,162 @@ class GuiDocSplit(QDialog):
self.splitLevel.addItem(self.tr("Split up to Header Level 2 (Chapter)"), 2)
self.splitLevel.addItem(self.tr("Split up to Header Level 3 (Scene)"), 3)
self.splitLevel.addItem(self.tr("Split up to Header Level 4 (Section)"), 4)
spIndex = self.splitLevel.findData(
self.theProject.options.getInt("GuiDocSplit", "spLevel", 3)
)
spIndex = self.splitLevel.findData(spLevel)
if spIndex != -1:
self.splitLevel.setCurrentIndex(spIndex)
self.splitLevel.currentIndexChanged.connect(self._populateList)
self.splitLevel.currentIndexChanged.connect(self._reloadList)
# Split Options
self.folderLabel = QLabel(self.tr("Split into a new folder"))
self.folderSwitch = QSwitch(width=2*iPx, height=iPx)
self.folderSwitch.setChecked(intoFolder)
self.hierarchyLabel = QLabel(self.tr("Create document hierarchy"))
self.hierarchySwitch = QSwitch(width=2*iPx, height=iPx)
self.hierarchySwitch.setChecked(docHierarchy)
self.trashLabel = QLabel(self.tr("Move split document to Trash"))
self.trashSwitch = QSwitch(width=2*iPx, height=iPx)
self.optBox = QGridLayout()
self.optBox.addWidget(self.folderLabel, 0, 0)
self.optBox.addWidget(self.folderSwitch, 0, 1)
self.optBox.addWidget(self.hierarchyLabel, 1, 0)
self.optBox.addWidget(self.hierarchySwitch, 1, 1)
self.optBox.addWidget(self.trashLabel, 2, 0)
self.optBox.addWidget(self.trashSwitch, 2, 1)
self.optBox.setVerticalSpacing(vSp)
self.optBox.setHorizontalSpacing(hSp)
self.optBox.setColumnStretch(3, 1)
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self._doSplit)
self.buttonBox.rejected.connect(self._doClose)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
# Assemble
self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(0)
self.outerBox.addWidget(self.headLabel)
self.outerBox.addWidget(self.helpLabel)
self.outerBox.addSpacing(self.mainConf.pxInt(8))
self.outerBox.addSpacing(vSp)
self.outerBox.addWidget(self.listBox)
self.outerBox.addWidget(self.splitLevel)
self.outerBox.addSpacing(self.mainConf.pxInt(12))
self.outerBox.addSpacing(vSp)
self.outerBox.addLayout(self.optBox)
self.outerBox.addSpacing(bSp)
self.outerBox.addWidget(self.buttonBox)
self.setLayout(self.outerBox)
self.rejected.connect(self._doClose)
self._populateList()
# Load Content
self._loadContent(sHandle)
logger.debug("GuiDocSplit initialisation complete")
return
##
# Buttons
##
def _doSplit(self):
"""Perform the split of the file, create a new folder in the
same parent folder, and multiple files depending on split level
settings. The old file is not removed in the split process, and
must be deleted manually.
def getData(self):
"""Return the user's choices. Also save the users options for
the next time the dialog is used.
"""
logger.verbose("GuiDocSplit split button clicked")
if self.sourceItem is None:
self.mainGui.makeAlert(self.tr(
"No source document selected. Nothing to do."
), nwAlert.ERROR)
return False
srcItem = self.theProject.tree[self.sourceItem]
if srcItem is None:
self.mainGui.makeAlert(self.tr(
"Could not parse source document."
), nwAlert.ERROR)
return False
inDoc = NWDoc(self.theProject, self.sourceItem)
theText = inDoc.readDocument()
docErr = inDoc.getError()
if theText is None and docErr:
self.mainGui.makeAlert([
self.tr("Failed to open document file."), docErr
], nwAlert.ERROR)
if theText is None:
theText = ""
nLines = len(self.sourceText)
logger.debug("Splitting document %s with %d lines", self.sourceItem, nLines)
finalOrder = []
headerList = []
for i in range(self.listBox.count()):
listItem = self.listBox.item(i)
wTitle = listItem.text()
lineNo = listItem.data(Qt.UserRole)
finalOrder.append([wTitle, lineNo, nLines])
if i > 0:
finalOrder[i-1][2] = lineNo
item = self.listBox.item(i)
if item is not None:
headerList.append((
item.data(self.LINE_ROLE),
item.data(self.LEVEL_ROLE),
item.data(self.LABEL_ROLE),
))
nFiles = len(finalOrder)
if nFiles == 0:
self.mainGui.makeAlert(self.tr(
"No headers found. Nothing to do."
), nwAlert.ERROR)
return False
spLevel = self.splitLevel.currentData()
intoFolder = self.folderSwitch.isChecked()
docHierarchy = self.hierarchySwitch.isChecked()
moveToTrash = self.trashSwitch.isChecked()
msgYes = self.mainGui.askQuestion(
self.tr("Split Document"),
"{0}<br><br>{1}".format(
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 False
self._data["spLevel"] = spLevel
self._data["headerList"] = headerList
self._data["intoFolder"] = intoFolder
self._data["docHierarchy"] = docHierarchy
self._data["moveToTrash"] = moveToTrash
# Create the folder
fHandle = self.theProject.newFolder(srcItem.itemName, srcItem.itemParent)
self.mainGui.projView.revealNewTreeItem(fHandle)
logger.verbose("Creating folder '%s'", fHandle)
pOptions = self.theProject.options
pOptions.setValue("GuiDocSplit", "spLevel", spLevel)
pOptions.setValue("GuiDocSplit", "intoFolder", intoFolder)
pOptions.setValue("GuiDocSplit", "docHierarchy", docHierarchy)
# Loop through, and create the files
for wTitle, iStart, iEnd in finalOrder:
return self._data, self._text
wTitle = wTitle.lstrip("#").strip()
nHandle = self.theProject.newFile(wTitle, fHandle)
newItem = self.theProject.tree[nHandle]
newItem.setStatus(srcItem.itemStatus)
newItem.setImport(srcItem.itemImport)
logger.verbose(
"Creating new document '%s' with text from line %d to %d",
nHandle, iStart+1, iEnd
)
##
# Slots
##
theText = "\n".join(self.sourceText[iStart:iEnd])
theText = theText.rstrip("\n") + "\n\n"
outDoc = NWDoc(self.theProject, nHandle)
if not outDoc.writeDocument(theText):
self.mainGui.makeAlert([
self.tr("Could not save document."), outDoc.getError()
], nwAlert.ERROR)
return False
self.mainGui.projView.revealNewTreeItem(nHandle)
self._doClose()
return True
def _doClose(self):
"""Close the dialog window without doing anything.
def _reloadList(self):
"""Reload the content of the list box.
"""
self.theProject.options.saveSettings()
self.close()
sHandle = self._data.get("sHandle", None)
self._loadContent(sHandle)
return
##
# Internal Functions
##
def _populateList(self):
"""Get the item selected in the tree, check that it is a folder,
and try to find all files associated with it. The valid files
are then added to the list view in order. The list itself can be
reordered by the user.
def _loadContent(self, sHandle):
"""Load content from a given source item.
"""
self._data = {}
self._data["sHandle"] = sHandle
self.listBox.clear()
if self.sourceItem is None:
self.sourceItem = self.mainGui.projView.getSelectedHandle()
if self.sourceItem is None:
return False
nwItem = self.theProject.tree[self.sourceItem]
if nwItem is None:
return False
if nwItem.itemType is not nwItemType.FILE:
self.mainGui.makeAlert(self.tr(
"Element selected in the project tree must be a file."
), nwAlert.ERROR)
return False
inDoc = NWDoc(self.theProject, self.sourceItem)
theText = inDoc.readDocument()
if theText is None:
theText = ""
return False
nwItem = self.theProject.tree[sHandle]
if nwItem is None or not nwItem.isFileType():
return
spLevel = self.splitLevel.currentData()
self.theProject.options.setValue("GuiDocSplit", "spLevel", spLevel)
logger.debug(
"Scanning document '%s' for headings level <= %d",
self.sourceItem, spLevel
)
if not self._text:
inDoc = self.theProject.storage.getDocument(sHandle)
self._text = (inDoc.readDocument() or "").splitlines()
self.sourceText = theText.splitlines()
for lineNo, aLine in enumerate(self.sourceText):
for lineNo, aLine in enumerate(self._text):
onLine = -1
hLevel = 0
hLabel = aLine.strip()
if aLine.startswith("# ") and spLevel >= 1:
onLine = lineNo
hLevel = 1
hLabel = aLine[2:].strip()
elif aLine.startswith("## ") and spLevel >= 2:
onLine = lineNo
hLevel = 2
hLabel = aLine[3:].strip()
elif aLine.startswith("### ") and spLevel >= 3:
onLine = lineNo
hLevel = 3
hLabel = aLine[4:].strip()
elif aLine.startswith("#### ") and spLevel >= 4:
onLine = lineNo
hLevel = 4
hLabel = aLine[5:].strip()
elif aLine.startswith("#! ") and spLevel >= 1:
onLine = lineNo
hLevel = 1
hLabel = aLine[3:].strip()
elif aLine.startswith("##! ") and spLevel >= 2:
onLine = lineNo
hLevel = 2
hLabel = aLine[4:].strip()
if onLine >= 0:
if onLine >= 0 and hLevel > 0:
newItem = QListWidgetItem()
newItem.setText(aLine.strip())
newItem.setData(Qt.UserRole, onLine)
newItem.setData(self.LINE_ROLE, onLine)
newItem.setData(self.LEVEL_ROLE, hLevel)
newItem.setData(self.LABEL_ROLE, hLabel)
self.listBox.addItem(newItem)
return True
return
# END Class GuiDocSplit
+1 -1
View File
@@ -36,7 +36,7 @@ logger = logging.getLogger(__name__)
class GuiEditLabel(QDialog):
def __init__(self, parent, text=""):
QDialog.__init__(self, parent=parent)
super().__init__(parent=parent)
self.setObjectName("GuiEditLabel")
self.setWindowTitle(self.tr("Item Label"))
+100 -125
View File
@@ -23,7 +23,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/>.
"""
import os
import logging
import novelwriter
@@ -34,8 +33,7 @@ from PyQt5.QtWidgets import (
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox
)
from novelwriter.enum import nwAlert
from novelwriter.gui.custom import QSwitch, QConfigLayout, PagedDialog
from novelwriter.custom import QSwitch, QConfigLayout, PagedDialog
from novelwriter.dialogs.quotes import GuiQuoteSelect
logger = logging.getLogger(__name__)
@@ -44,7 +42,7 @@ logger = logging.getLogger(__name__)
class GuiPreferences(PagedDialog):
def __init__(self, mainGui):
PagedDialog.__init__(self, mainGui)
super().__init__(parent=mainGui)
logger.debug("Initialising GuiPreferences ...")
self.setObjectName("GuiPreferences")
@@ -55,13 +53,13 @@ class GuiPreferences(PagedDialog):
self.setWindowTitle(self.tr("Preferences"))
self.tabGeneral = GuiPreferencesGeneral(self.mainGui)
self.tabProjects = GuiPreferencesProjects(self.mainGui)
self.tabDocs = GuiPreferencesDocuments(self.mainGui)
self.tabEditor = GuiPreferencesEditor(self.mainGui)
self.tabSyntax = GuiPreferencesSyntax(self.mainGui)
self.tabAuto = GuiPreferencesAutomation(self.mainGui)
self.tabQuote = GuiPreferencesQuotes(self.mainGui)
self.tabGeneral = GuiPreferencesGeneral(self)
self.tabProjects = GuiPreferencesProjects(self)
self.tabDocs = GuiPreferencesDocuments(self)
self.tabEditor = GuiPreferencesEditor(self)
self.tabSyntax = GuiPreferencesSyntax(self)
self.tabAuto = GuiPreferencesAutomation(self)
self.tabQuote = GuiPreferencesQuotes(self)
self.addTab(self.tabGeneral, self.tr("General"))
self.addTab(self.tabProjects, self.tr("Projects"))
@@ -76,12 +74,38 @@ class GuiPreferences(PagedDialog):
self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox)
self.resize(*self.mainConf.getPreferencesSize())
self.resize(*self.mainConf.preferencesWinSize)
# Settings
self._updateTheme = False
self._updateSyntax = False
self._needsRestart = False
self._refreshTree = False
logger.debug("GuiPreferences initialisation complete")
return
##
# Properties
##
@property
def updateTheme(self):
return self._updateTheme
@property
def updateSyntax(self):
return self._updateSyntax
@property
def needsRestart(self):
return self._needsRestart
@property
def refreshTree(self):
return self._refreshTree
##
# Slots
##
@@ -92,8 +116,7 @@ class GuiPreferences(PagedDialog):
"""
logger.debug("Saving new preferences")
needsRestart, refreshTree = self.tabGeneral.saveValues()
self.tabGeneral.saveValues()
self.tabProjects.saveValues()
self.tabDocs.saveValues()
self.tabEditor.saveValues()
@@ -101,15 +124,8 @@ class GuiPreferences(PagedDialog):
self.tabAuto.saveValues()
self.tabQuote.saveValues()
if needsRestart:
self.mainGui.makeAlert(self.tr(
"Some changes will not be applied until novelWriter has been restarted."
), nwAlert.INFO)
if refreshTree:
self.mainGui.projView.populateTree()
self._saveWindowSize()
self.mainConf.saveConfig()
self.accept()
return
@@ -128,9 +144,7 @@ class GuiPreferences(PagedDialog):
def _saveWindowSize(self):
"""Save the dialog window size.
"""
winWidth = self.mainConf.rpxInt(self.width())
winHeight = self.mainConf.rpxInt(self.height())
self.mainConf.setPreferencesSize(winWidth, winHeight)
self.mainConf.setPreferencesWinSize(self.width(), self.height())
return
# END Class GuiPreferences
@@ -138,12 +152,13 @@ class GuiPreferences(PagedDialog):
class GuiPreferencesGeneral(QWidget):
def __init__(self, mainGui):
QWidget.__init__(self, mainGui)
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.prefsGui = prefsGui
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = QConfigLayout()
@@ -156,19 +171,19 @@ class GuiPreferencesGeneral(QWidget):
minWidth = self.mainConf.pxInt(200)
# Select Locale
self.guiLang = QComboBox()
self.guiLang.setMinimumWidth(minWidth)
self.guiLocale = QComboBox()
self.guiLocale.setMinimumWidth(minWidth)
theLangs = self.mainConf.listLanguages(self.mainConf.LANG_NW)
for lang, langName in theLangs:
self.guiLang.addItem(langName, lang)
langIdx = self.guiLang.findData(self.mainConf.guiLang)
self.guiLocale.addItem(langName, lang)
langIdx = self.guiLocale.findData(self.mainConf.guiLocale)
if langIdx != -1:
self.guiLang.setCurrentIndex(langIdx)
self.guiLocale.setCurrentIndex(langIdx)
self.mainForm.addRow(
self.tr("Main GUI language"),
self.guiLang,
self.tr("Requires restart.")
self.guiLocale,
self.tr("Requires restart to take effect.")
)
# Select Theme
@@ -184,23 +199,7 @@ class GuiPreferencesGeneral(QWidget):
self.mainForm.addRow(
self.tr("Main GUI theme"),
self.guiTheme,
self.tr("Requires restart.")
)
# Select Icon Theme
self.guiIcons = QComboBox()
self.guiIcons.setMinimumWidth(minWidth)
self.iconCache = self.mainTheme.iconCache.listThemes()
for iconDir, iconName in self.iconCache:
self.guiIcons.addItem(iconName, iconDir)
iconIdx = self.guiIcons.findData(self.mainConf.guiIcons)
if iconIdx != -1:
self.guiIcons.setCurrentIndex(iconIdx)
self.mainForm.addRow(
self.tr("Main icon theme"),
self.guiIcons,
self.tr("Requires restart.")
self.tr("General colour theme and icons.")
)
# Editor Theme
@@ -230,7 +229,7 @@ class GuiPreferencesGeneral(QWidget):
self.mainForm.addRow(
self.tr("Font family"),
self.guiFont,
self.tr("Requires restart."),
self.tr("Requires restart to take effect."),
theButton=self.fontButton
)
@@ -243,7 +242,7 @@ class GuiPreferencesGeneral(QWidget):
self.mainForm.addRow(
self.tr("Font size"),
self.guiFontSize,
self.tr("Requires restart."),
self.tr("Requires restart to take effect."),
theUnit=self.tr("pt")
)
@@ -288,29 +287,23 @@ class GuiPreferencesGeneral(QWidget):
def saveValues(self):
"""Save the values set for this tab.
"""
guiLang = self.guiLang.currentData()
guiLocale = self.guiLocale.currentData()
guiTheme = self.guiTheme.currentData()
guiIcons = self.guiIcons.currentData()
guiSyntax = self.guiSyntax.currentData()
guiFont = self.guiFont.text()
guiFontSize = self.guiFontSize.value()
emphLabels = self.emphLabels.isChecked()
# Check if restart is needed
needsRestart = False
needsRestart |= self.mainConf.guiLang != guiLang
needsRestart |= self.mainConf.guiTheme != guiTheme
needsRestart |= self.mainConf.guiIcons != guiIcons
needsRestart |= self.mainConf.guiFont != guiFont
needsRestart |= self.mainConf.guiFontSize != guiFontSize
# Update Flags
self.prefsGui._updateTheme |= self.mainConf.guiTheme != guiTheme
self.prefsGui._updateSyntax |= self.mainConf.guiSyntax != guiSyntax
self.prefsGui._needsRestart |= self.mainConf.guiLocale != guiLocale
self.prefsGui._needsRestart |= self.mainConf.guiFont != guiFont
self.prefsGui._needsRestart |= self.mainConf.guiFontSize != guiFontSize
self.prefsGui._refreshTree |= self.mainConf.emphLabels != emphLabels
# Check if refreshing project tree is needed
refreshTree = False
refreshTree |= self.mainConf.emphLabels != emphLabels
self.mainConf.guiLang = guiLang
self.mainConf.guiLocale = guiLocale
self.mainConf.guiTheme = guiTheme
self.mainConf.guiIcons = guiIcons
self.mainConf.guiSyntax = guiSyntax
self.mainConf.guiFont = guiFont
self.mainConf.guiFontSize = guiFontSize
@@ -319,9 +312,7 @@ class GuiPreferencesGeneral(QWidget):
self.mainConf.hideVScroll = self.hideVScroll.isChecked()
self.mainConf.hideHScroll = self.hideHScroll.isChecked()
self.mainConf.confChanged = True
return needsRestart, refreshTree
return
##
# Slots
@@ -344,12 +335,12 @@ class GuiPreferencesGeneral(QWidget):
class GuiPreferencesProjects(QWidget):
def __init__(self, mainGui):
QWidget.__init__(self, mainGui)
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = QConfigLayout()
@@ -391,7 +382,7 @@ class GuiPreferencesProjects(QWidget):
self.mainForm.addGroupLabel(self.tr("Project Backup"))
# Backup Path
self.backupPath = self.mainConf.backupPath
self.backupPath = self.mainConf.backupPath()
self.backupGetPath = QPushButton(self.tr("Browse"))
self.backupGetPath.clicked.connect(self._backupFolder)
self.backupPathRow = self.mainForm.addRow(
@@ -458,7 +449,7 @@ class GuiPreferencesProjects(QWidget):
self.mainConf.autoSaveProj = self.autoSaveProj.value()
# Project Backup
self.mainConf.backupPath = self.backupPath
self.mainConf.setBackupPath(self.backupPath)
self.mainConf.backupOnClose = self.backupOnClose.isChecked()
self.mainConf.askBeforeBackup = self.askBeforeBackup.isChecked()
@@ -466,8 +457,6 @@ class GuiPreferencesProjects(QWidget):
self.mainConf.stopWhenIdle = self.stopWhenIdle.isChecked()
self.mainConf.userIdleTime = round(self.userIdleTime.value() * 60)
self.mainConf.confChanged = True
return
##
@@ -477,12 +466,9 @@ class GuiPreferencesProjects(QWidget):
def _backupFolder(self):
"""Open a dialog to select the backup folder.
"""
currDir = self.backupPath
if not os.path.isdir(currDir):
currDir = ""
currDir = self.backupPath or ""
newDir = QFileDialog.getExistingDirectory(
self, self.tr("Backup Directory"), currDir, options=QFileDialog.ShowDirsOnly
self, self.tr("Backup Directory"), str(currDir), options=QFileDialog.ShowDirsOnly
)
if newDir:
self.backupPath = newDir
@@ -505,12 +491,12 @@ class GuiPreferencesProjects(QWidget):
class GuiPreferencesDocuments(QWidget):
def __init__(self, mainGui):
QWidget.__init__(self, mainGui)
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = QConfigLayout()
@@ -640,8 +626,6 @@ class GuiPreferencesDocuments(QWidget):
self.mainConf.textMargin = self.textMargin.value()
self.mainConf.tabWidth = self.tabWidth.value()
self.mainConf.confChanged = True
return
##
@@ -666,12 +650,12 @@ class GuiPreferencesDocuments(QWidget):
class GuiPreferencesEditor(QWidget):
def __init__(self, mainGui):
QWidget.__init__(self, mainGui)
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = QConfigLayout()
@@ -831,8 +815,6 @@ class GuiPreferencesEditor(QWidget):
self.mainConf.autoScroll = self.autoScroll.isChecked()
self.mainConf.autoScrollPos = self.autoScrollPos.value()
self.mainConf.confChanged = True
return
# END Class GuiPreferencesEditor
@@ -840,12 +822,12 @@ class GuiPreferencesEditor(QWidget):
class GuiPreferencesSyntax(QWidget):
def __init__(self, mainGui):
QWidget.__init__(self, mainGui)
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = QConfigLayout()
@@ -922,8 +904,6 @@ class GuiPreferencesSyntax(QWidget):
# Text Errors
self.mainConf.showMultiSpaces = self.showMultiSpaces.isChecked()
self.mainConf.confChanged = True
return
##
@@ -943,12 +923,12 @@ class GuiPreferencesSyntax(QWidget):
class GuiPreferencesAutomation(QWidget):
def __init__(self, mainGui):
QWidget.__init__(self, mainGui)
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = QConfigLayout()
@@ -1076,8 +1056,6 @@ class GuiPreferencesAutomation(QWidget):
self.mainConf.fmtPadAfter = self.fmtPadAfter.text().strip()
self.mainConf.fmtPadThin = self.fmtPadThin.isChecked()
self.mainConf.confChanged = True
return
##
@@ -1100,12 +1078,12 @@ class GuiPreferencesAutomation(QWidget):
class GuiPreferencesQuotes(QWidget):
def __init__(self, mainGui):
QWidget.__init__(self, mainGui)
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = QConfigLayout()
@@ -1126,7 +1104,7 @@ class GuiPreferencesQuotes(QWidget):
self.quoteSym["SO"].setReadOnly(True)
self.quoteSym["SO"].setFixedWidth(qWidth)
self.quoteSym["SO"].setAlignment(Qt.AlignCenter)
self.quoteSym["SO"].setText(self.mainConf.fmtSingleQuotes[0])
self.quoteSym["SO"].setText(self.mainConf.fmtSQuoteOpen)
self.btnSingleStyleO = QPushButton("...")
self.btnSingleStyleO.setMaximumWidth(bWidth)
self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO"))
@@ -1142,7 +1120,7 @@ class GuiPreferencesQuotes(QWidget):
self.quoteSym["SC"].setReadOnly(True)
self.quoteSym["SC"].setFixedWidth(qWidth)
self.quoteSym["SC"].setAlignment(Qt.AlignCenter)
self.quoteSym["SC"].setText(self.mainConf.fmtSingleQuotes[1])
self.quoteSym["SC"].setText(self.mainConf.fmtSQuoteClose)
self.btnSingleStyleC = QPushButton("...")
self.btnSingleStyleC.setMaximumWidth(bWidth)
self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC"))
@@ -1159,7 +1137,7 @@ class GuiPreferencesQuotes(QWidget):
self.quoteSym["DO"].setReadOnly(True)
self.quoteSym["DO"].setFixedWidth(qWidth)
self.quoteSym["DO"].setAlignment(Qt.AlignCenter)
self.quoteSym["DO"].setText(self.mainConf.fmtDoubleQuotes[0])
self.quoteSym["DO"].setText(self.mainConf.fmtDQuoteOpen)
self.btnDoubleStyleO = QPushButton("...")
self.btnDoubleStyleO.setMaximumWidth(bWidth)
self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO"))
@@ -1175,7 +1153,7 @@ class GuiPreferencesQuotes(QWidget):
self.quoteSym["DC"].setReadOnly(True)
self.quoteSym["DC"].setFixedWidth(qWidth)
self.quoteSym["DC"].setAlignment(Qt.AlignCenter)
self.quoteSym["DC"].setText(self.mainConf.fmtDoubleQuotes[1])
self.quoteSym["DC"].setText(self.mainConf.fmtDQuoteClose)
self.btnDoubleStyleC = QPushButton("...")
self.btnDoubleStyleC.setMaximumWidth(bWidth)
self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC"))
@@ -1192,13 +1170,10 @@ class GuiPreferencesQuotes(QWidget):
"""Save the values set for this tab.
"""
# Quotation Style
self.mainConf.fmtSingleQuotes[0] = self.quoteSym["SO"].text()
self.mainConf.fmtSingleQuotes[1] = self.quoteSym["SC"].text()
self.mainConf.fmtDoubleQuotes[0] = self.quoteSym["DO"].text()
self.mainConf.fmtDoubleQuotes[1] = self.quoteSym["DC"].text()
self.mainConf.confChanged = True
self.mainConf.fmtSQuoteOpen = self.quoteSym["SO"].text()
self.mainConf.fmtSQuoteClose = self.quoteSym["SC"].text()
self.mainConf.fmtDQuoteOpen = self.quoteSym["DO"].text()
self.mainConf.fmtDQuoteClose = self.quoteSym["DC"].text()
return
##
+67 -20
View File
@@ -27,16 +27,18 @@ import math
import logging
import novelwriter
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtCore import Qt, QSize, pyqtSlot
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (
QWidget, QDialogButtonBox, QVBoxLayout, QTreeWidget, QTreeWidgetItem,
QLabel, QSpinBox, QGridLayout, QHBoxLayout, QLineEdit, QAbstractItemView
QAbstractItemView, QComboBox, QDialogButtonBox, QGridLayout, QHBoxLayout,
QLabel, QLineEdit, QSpinBox, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
QWidget
)
from novelwriter.enum import nwItemClass
from novelwriter.common import numberToRoman
from novelwriter.constants import nwUnicode
from novelwriter.gui.custom import PagedDialog, QSwitch
from novelwriter.custom import PagedDialog, QSwitch
from novelwriter.constants import nwLabels, nwUnicode
logger = logging.getLogger(__name__)
@@ -44,7 +46,7 @@ logger = logging.getLogger(__name__)
class GuiProjectDetails(PagedDialog):
def __init__(self, mainGui):
PagedDialog.__init__(self, mainGui)
super().__init__(parent=mainGui)
logger.debug("Initialising GuiProjectDetails ...")
self.setObjectName("GuiProjectDetails")
@@ -140,7 +142,7 @@ class GuiProjectDetails(PagedDialog):
class GuiProjectDetailsMain(QWidget):
def __init__(self, mainGui, theProject):
QWidget.__init__(self, mainGui)
super().__init__(parent=mainGui)
self.mainConf = novelwriter.CONFIG
self.theProject = theProject
@@ -155,7 +157,7 @@ class GuiProjectDetailsMain(QWidget):
# Header
# ======
self.bookTitle = QLabel(self.theProject.bookTitle)
self.bookTitle = QLabel(self.theProject.data.title)
bookFont = self.bookTitle.font()
bookFont.setPointSizeF(2.2*fPt)
bookFont.setWeight(QFont.Bold)
@@ -164,7 +166,7 @@ class GuiProjectDetailsMain(QWidget):
self.bookTitle.setWordWrap(True)
self.projName = QLabel(
self.tr("Working Title: {0}").format(self.theProject.projName)
self.tr("Working Title: {0}").format(self.theProject.data.name)
)
workFont = self.projName.font()
workFont.setPointSizeF(0.8*fPt)
@@ -173,7 +175,9 @@ class GuiProjectDetailsMain(QWidget):
self.projName.setAlignment(Qt.AlignHCenter)
self.projName.setWordWrap(True)
self.bookAuthors = QLabel(self.tr("By {0}").format(self.theProject.getAuthors()))
self.bookAuthors = QLabel(self.tr("By {0}").format(
self.theProject.getFormattedAuthors()
))
authFont = self.bookAuthors.font()
authFont.setPointSizeF(1.2*fPt)
self.bookAuthors.setFont(authFont)
@@ -253,10 +257,10 @@ class GuiProjectDetailsMain(QWidget):
self.wordCountVal.setText(f"{nwCount:n}")
self.chapCountVal.setText(f"{hCounts[2]:n}")
self.sceneCountVal.setText(f"{hCounts[3]:n}")
self.revCountVal.setText(f"{self.theProject.saveCount:n}")
self.revCountVal.setText(f"{self.theProject.data.saveCount:n}")
self.editTimeVal.setText(f"{edTime//3600:02d}:{edTime%3600//60:02d}")
self.projPathVal.setText(self.theProject.projPath)
self.projPathVal.setText(str(self.theProject.storage.storagePath))
return
@@ -272,7 +276,7 @@ class GuiProjectDetailsContents(QWidget):
C_PROG = 4
def __init__(self, mainGui, theProject):
QWidget.__init__(self, mainGui)
super().__init__(parent=mainGui)
self.mainConf = novelwriter.CONFIG
self.theProject = theProject
@@ -281,12 +285,26 @@ class GuiProjectDetailsContents(QWidget):
# Internal
self._theToC = []
self._currentRoot = None
iPx = self.mainTheme.baseIconSize
hPx = self.mainConf.pxInt(12)
vPx = self.mainConf.pxInt(4)
pOptions = self.theProject.options
# Header
# ======
self.tocLabel = QLabel("<b>%s</b>" % self.tr("Table of Contents"))
self.novelValue = QComboBox(self)
self.novelValue.setMinimumWidth(self.mainConf.pxInt(200))
self.novelValue.currentIndexChanged.connect(self._novelValueChanged)
self.headBox = QHBoxLayout()
self.headBox.addWidget(self.tocLabel)
self.headBox.addWidget(self.novelValue)
# Contents Tree
# =============
@@ -389,7 +407,7 @@ class GuiProjectDetailsContents(QWidget):
# ========
self.outerBox = QVBoxLayout()
self.outerBox.addWidget(QLabel("<b>%s</b>" % self.tr("Table of Contents")))
self.outerBox.addLayout(self.headBox)
self.outerBox.addWidget(self.tocTree)
self.outerBox.addLayout(self.optionsBox)
@@ -412,19 +430,35 @@ class GuiProjectDetailsContents(QWidget):
def updateValues(self):
"""Populate the tree.
"""
self._prepareData()
self._currentRoot = None
self._populateNovelList()
rootHandle = self.novelValue.currentData()
self._prepareData(rootHandle)
self._populateTree()
return
##
# Internal Functions
##
def _prepareData(self):
"""Extract the data for the tree.
def _populateNovelList(self):
"""Fill the novel combo box with a list of all novel folders.
"""
self._theToC = []
self._theToC = self.theProject.index.getTableOfContents(2)
self.novelValue.clear()
tIcon = self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
for tHandle, nwItem in self.theProject.tree.iterRoots(nwItemClass.NOVEL):
self.novelValue.addItem(tIcon, nwItem.itemName, tHandle)
return
def _prepareData(self, rootHandle):
"""Extract the information from the project index.
"""
logger.debug("Populating ToC from handle '%s'", rootHandle)
self._theToC = self.theProject.index.getTableOfContents(rootHandle, 2)
self._theToC.append(("", 0, self.tr("END"), 0))
return
@@ -432,6 +466,18 @@ class GuiProjectDetailsContents(QWidget):
# Slots
##
@pyqtSlot()
def _novelValueChanged(self):
"""Refresh the tree with another root item.
"""
rootHandle = self.novelValue.currentData()
if rootHandle != self._currentRoot:
self._prepareData(rootHandle)
self._populateTree()
self._currentRoot = rootHandle
return
@pyqtSlot()
def _populateTree(self):
"""Set the content of the chapter/page tree.
"""
@@ -466,10 +512,11 @@ class GuiProjectDetailsContents(QWidget):
progPage = f"{cPage:n}"
progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%"
hDec = self.mainTheme.getHeaderDecoration(tLevel)
if tTitle.strip() == "":
tTitle = self.tr("Untitled")
newItem.setIcon(self.C_TITLE, self.mainTheme.getIcon("doc_h%d" % tLevel))
newItem.setData(self.C_TITLE, Qt.DecorationRole, hDec)
newItem.setText(self.C_TITLE, tTitle)
newItem.setText(self.C_WORDS, f"{wCount:n}")
newItem.setText(self.C_PAGES, f"{pCount:n}")
+16 -27
View File
@@ -23,10 +23,10 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import os
import logging
import novelwriter
from pathlib import Path
from datetime import datetime
from PyQt5.QtGui import QKeySequence
@@ -54,7 +54,7 @@ class GuiProjectLoad(QDialog):
C_TIME = 2
def __init__(self, mainGui):
QDialog.__init__(self, mainGui)
super().__init__(parent=mainGui)
logger.debug("Initialising GuiProjectLoad ...")
self.setObjectName("GuiProjectLoad")
@@ -77,7 +77,6 @@ class GuiProjectLoad(QDialog):
self.setWindowTitle(self.tr("Open Project"))
self.setMinimumWidth(self.mainConf.pxInt(650))
self.setMinimumHeight(self.mainConf.pxInt(400))
self.setModal(True)
self.nwIcon = QLabel()
self.nwIcon.setPixmap(self.mainGui.mainTheme.getPixmap("novelwriter", (nPx, nPx)))
@@ -158,7 +157,6 @@ class GuiProjectLoad(QDialog):
def _doOpenRecent(self):
"""Close the dialog window with a recent project selected.
"""
logger.verbose("GuiProjectLoad open button clicked")
self._saveSettings()
self.openPath = None
@@ -183,7 +181,6 @@ class GuiProjectLoad(QDialog):
def _doBrowse(self):
"""Browse for a folder path.
"""
logger.verbose("GuiProjectLoad browse button clicked")
extFilter = [
self.tr("novelWriter Project File ({0})").format(nwFiles.PROJ_FILE),
self.tr("All files ({0})").format("*"),
@@ -192,8 +189,8 @@ class GuiProjectLoad(QDialog):
self, self.tr("Open Project"), "", filter=";;".join(extFilter)
)
if projFile:
thePath = os.path.abspath(os.path.dirname(projFile))
self.selPath.setText(thePath)
thePath = Path(projFile).absolute()
self.selPath.setText(str(thePath))
self.openPath = thePath
self.openState = self.OPEN_STATE
self.accept()
@@ -203,7 +200,6 @@ class GuiProjectLoad(QDialog):
def _doCancel(self):
"""Close the dialog window without doing anything.
"""
logger.verbose("GuiProjectLoad close button clicked")
self.openPath = None
self.openState = self.NONE_STATE
self.close()
@@ -212,7 +208,6 @@ class GuiProjectLoad(QDialog):
def _doNewProject(self):
"""Create a new project.
"""
logger.verbose("GuiProjectLoad new project button clicked")
self._saveSettings()
self.openPath = None
self.openState = self.NEW_STATE
@@ -233,7 +228,7 @@ class GuiProjectLoad(QDialog):
).format(projName)
)
if msgYes:
self.mainConf.removeFromRecentCache(
self.mainConf.recentProjects.remove(
selList[0].data(self.C_NAME, Qt.UserRole)
)
self._populateList()
@@ -262,29 +257,23 @@ class GuiProjectLoad(QDialog):
colWidths[self.C_NAME] = self.listBox.columnWidth(self.C_NAME)
colWidths[self.C_COUNT] = self.listBox.columnWidth(self.C_COUNT)
colWidths[self.C_TIME] = self.listBox.columnWidth(self.C_TIME)
self.mainConf.setProjColWidths(colWidths)
self.mainConf.setProjLoadColWidths(colWidths)
return
def _populateList(self):
"""Populate the list box with recent project data.
"""
dataList = []
for projPath in self.mainConf.recentProj:
theEntry = self.mainConf.recentProj[projPath]
theTitle = theEntry.get("title", "")
theTime = theEntry.get("time", 0)
theWords = theEntry.get("words", 0)
dataList.append([theTitle, theTime, theWords, projPath])
self.listBox.clear()
sortList = sorted(dataList, key=lambda x: x[1], reverse=True)
for theTitle, theTime, theWords, projPath in sortList:
dataList = self.mainConf.recentProjects.listEntries()
sortList = sorted(dataList, key=lambda x: x[3], reverse=True)
nwxIcon = self.mainGui.mainTheme.getIcon("proj_nwx")
for path, title, words, time in sortList:
newItem = QTreeWidgetItem([""]*4)
newItem.setIcon(self.C_NAME, self.mainGui.mainTheme.getIcon("proj_nwx"))
newItem.setText(self.C_NAME, theTitle)
newItem.setData(self.C_NAME, Qt.UserRole, projPath)
newItem.setText(self.C_COUNT, formatInt(theWords))
newItem.setText(self.C_TIME, datetime.fromtimestamp(theTime).strftime("%x %X"))
newItem.setIcon(self.C_NAME, nwxIcon)
newItem.setText(self.C_NAME, title)
newItem.setData(self.C_NAME, Qt.UserRole, path)
newItem.setText(self.C_COUNT, formatInt(words))
newItem.setText(self.C_TIME, datetime.fromtimestamp(time).strftime("%x %X"))
newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter)
@@ -294,7 +283,7 @@ class GuiProjectLoad(QDialog):
if self.listBox.topLevelItemCount() > 0:
self.listBox.topLevelItem(0).setSelected(True)
projColWidth = self.mainConf.getProjColWidths()
projColWidth = self.mainConf.projLoadColWidths
if len(projColWidth) == 3:
self.listBox.setColumnWidth(self.C_NAME, projColWidth[self.C_NAME])
self.listBox.setColumnWidth(self.C_COUNT, projColWidth[self.C_COUNT])
+75 -49
View File
@@ -36,15 +36,20 @@ from PyQt5.QtWidgets import (
from novelwriter.enum import nwAlert
from novelwriter.common import simplified
from novelwriter.gui.custom import QSwitch, PagedDialog, QConfigLayout
from novelwriter.custom import QSwitch, PagedDialog, QConfigLayout
logger = logging.getLogger(__name__)
class GuiProjectSettings(PagedDialog):
def __init__(self, mainGui):
PagedDialog.__init__(self, mainGui)
TAB_MAIN = 0
TAB_STATUS = 1
TAB_IMPORT = 2
TAB_REPLACE = 3
def __init__(self, mainGui, focusTab=TAB_MAIN):
super().__init__(parent=mainGui)
logger.debug("Initialising GuiProjectSettings ...")
self.setObjectName("GuiProjectSettings")
@@ -67,10 +72,10 @@ class GuiProjectSettings(PagedDialog):
self.mainConf.pxInt(pOptions.getInt("GuiProjectSettings", "winHeight", wH))
)
self.tabMain = GuiProjectEditMain(self.mainGui, self.theProject)
self.tabStatus = GuiProjectEditStatus(self.mainGui, self.theProject, True)
self.tabImport = GuiProjectEditStatus(self.mainGui, self.theProject, False)
self.tabReplace = GuiProjectEditReplace(self.mainGui, self.theProject)
self.tabMain = GuiProjectEditMain(self)
self.tabStatus = GuiProjectEditStatus(self, True)
self.tabImport = GuiProjectEditStatus(self, False)
self.tabReplace = GuiProjectEditReplace(self)
self.addTab(self.tabMain, self.tr("Settings"))
self.addTab(self.tabStatus, self.tr("Status"))
@@ -83,12 +88,19 @@ class GuiProjectSettings(PagedDialog):
self.addControls(self.buttonBox)
# Flags
self.spellChanged = False
self._spellChanged = False
# Focus Tab
self._focusTab(focusTab)
logger.debug("GuiProjectSettings initialisation complete")
return
@property
def spellChanged(self):
return self._spellChanged
##
# Slots
##
@@ -96,21 +108,19 @@ class GuiProjectSettings(PagedDialog):
def _doSave(self):
"""Save settings and close dialog.
"""
logger.verbose("GuiProjectSettings save button clicked")
projName = self.tabMain.editName.text()
bookTitle = self.tabMain.editTitle.text()
bookAuthors = self.tabMain.editAuthors.toPlainText()
spellLang = self.tabMain.spellLang.currentData()
doBackup = not self.tabMain.doBackup.isChecked()
self.theProject.setProjectName(projName)
self.theProject.setBookTitle(bookTitle)
self.theProject.setBookAuthors(bookAuthors)
self.theProject.setProjBackup(doBackup)
self.theProject.data.setName(projName)
self.theProject.data.setTitle(bookTitle)
self.theProject.data.setAuthors(bookAuthors)
self.theProject.data.setDoBackup(doBackup)
# Remember this as updating spell dictionary can be expensive
self.spellChanged = self.theProject.setSpellLang(spellLang)
self._spellChanged = self.theProject.data.setSpellLang(spellLang)
if self.tabStatus.colChanged:
newList, delList = self.tabStatus.getNewList()
@@ -125,7 +135,7 @@ class GuiProjectSettings(PagedDialog):
if self.tabReplace.arChanged:
newList = self.tabReplace.getNewList()
self.theProject.setAutoReplace(newList)
self.theProject.data.setAutoReplace(newList)
self._saveGuiSettings()
self.accept()
@@ -143,6 +153,19 @@ class GuiProjectSettings(PagedDialog):
# Internal Functions
##
def _focusTab(self, tab):
"""Change which is the focused tab.
"""
if tab == self.TAB_MAIN:
self.setCurrentWidget(self.tabMain)
elif tab == self.TAB_STATUS:
self.setCurrentWidget(self.tabStatus)
elif tab == self.TAB_IMPORT:
self.setCurrentWidget(self.tabImport)
elif tab == self.TAB_REPLACE:
self.setCurrentWidget(self.tabReplace)
return
def _saveGuiSettings(self):
"""Save GUI settings.
"""
@@ -166,12 +189,12 @@ class GuiProjectSettings(PagedDialog):
class GuiProjectEditMain(QWidget):
def __init__(self, mainGui, theProject):
QWidget.__init__(self, mainGui)
def __init__(self, projGui):
super().__init__(parent=projGui)
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.theProject = theProject
self.mainGui = projGui.mainGui
self.theProject = projGui.theProject
# The Form
self.mainForm = QConfigLayout()
@@ -186,7 +209,7 @@ class GuiProjectEditMain(QWidget):
self.editName = QLineEdit()
self.editName.setMaxLength(200)
self.editName.setMaximumWidth(xW)
self.editName.setText(self.theProject.projName)
self.editName.setText(self.theProject.data.name)
self.mainForm.addRow(
self.tr("Project name"),
self.editName,
@@ -196,7 +219,7 @@ class GuiProjectEditMain(QWidget):
self.editTitle = QLineEdit()
self.editTitle.setMaxLength(200)
self.editTitle.setMaximumWidth(xW)
self.editTitle.setText(self.theProject.bookTitle)
self.editTitle.setText(self.theProject.data.title)
self.mainForm.addRow(
self.tr("Novel title"),
self.editTitle,
@@ -206,7 +229,7 @@ class GuiProjectEditMain(QWidget):
self.editAuthors = QPlainTextEdit()
self.editAuthors.setMaximumHeight(xH)
self.editAuthors.setMaximumWidth(xW)
self.editAuthors.setPlainText("\n".join(self.theProject.bookAuthors))
self.editAuthors.setPlainText("\n".join(self.theProject.data.authors))
self.mainForm.addRow(
self.tr("Author(s)"),
self.editAuthors,
@@ -229,13 +252,13 @@ class GuiProjectEditMain(QWidget):
)
spellIdx = 0
if self.theProject.projSpell is not None:
spellIdx = self.spellLang.findData(self.theProject.projSpell)
if self.theProject.data.spellLang is not None:
spellIdx = self.spellLang.findData(self.theProject.data.spellLang)
if spellIdx != -1:
self.spellLang.setCurrentIndex(spellIdx)
self.doBackup = QSwitch(self)
self.doBackup.setChecked(not self.theProject.doBackup)
self.doBackup.setChecked(not self.theProject.data.doBackup)
self.mainForm.addRow(
self.tr("No backup on close"),
self.doBackup,
@@ -256,20 +279,20 @@ class GuiProjectEditStatus(QWidget):
COL_ROLE = Qt.UserRole + 1
NUM_ROLE = Qt.UserRole + 2
def __init__(self, mainGui, theProject, isStatus):
QWidget.__init__(self, mainGui)
def __init__(self, projGui, isStatus):
super().__init__(parent=projGui)
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.theProject = theProject
self.mainTheme = mainGui.mainTheme
self.mainGui = projGui.mainGui
self.theProject = projGui.theProject
self.mainTheme = projGui.mainGui.mainTheme
if isStatus:
self.theStatus = self.theProject.statusItems
self.theStatus = self.theProject.data.itemStatus
pageLabel = self.tr("Novel File Status Levels")
colSetting = "statusColW"
else:
self.theStatus = self.theProject.importItems
self.theStatus = self.theProject.data.itemImport
pageLabel = self.tr("Note File Importance Levels")
colSetting = "importColW"
@@ -367,11 +390,12 @@ class GuiProjectEditStatus(QWidget):
newList = []
for n in range(self.listBox.topLevelItemCount()):
item = self.listBox.topLevelItem(n)
newList.append({
"key": item.data(self.COL_LABEL, self.KEY_ROLE),
"name": item.text(self.COL_LABEL),
"cols": item.data(self.COL_LABEL, self.COL_ROLE),
})
if item is not None:
newList.append({
"key": item.data(self.COL_LABEL, self.KEY_ROLE),
"name": item.text(self.COL_LABEL),
"cols": item.data(self.COL_LABEL, self.COL_ROLE),
})
return newList, self.colDeleted
return [], []
@@ -470,7 +494,8 @@ class GuiProjectEditStatus(QWidget):
self.listBox.insertTopLevelItem(nIndex, cItem)
self.listBox.clearSelection()
cItem.setSelected(True)
if cItem is not None:
cItem.setSelected(True)
self.colChanged = True
return
@@ -527,13 +552,13 @@ class GuiProjectEditReplace(QWidget):
COL_KEY = 0
COL_REPL = 1
def __init__(self, mainGui, theProject):
QWidget.__init__(self, mainGui)
def __init__(self, projGui):
super().__init__(parent=projGui)
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = theProject
self.mainGui = projGui.mainGui
self.mainTheme = projGui.mainGui.mainTheme
self.theProject = projGui.theProject
self.arChanged = False
wCol0 = self.mainConf.pxInt(
@@ -553,7 +578,7 @@ class GuiProjectEditReplace(QWidget):
self.listBox.setColumnWidth(self.COL_KEY, wCol0)
self.listBox.setIndentation(0)
for aKey, aVal in self.theProject.autoReplace.items():
for aKey, aVal in self.theProject.data.autoReplace.items():
newItem = QTreeWidgetItem(["<%s>" % aKey, aVal])
self.listBox.addTopLevelItem(newItem)
@@ -619,10 +644,11 @@ class GuiProjectEditReplace(QWidget):
newList = {}
for n in range(self.listBox.topLevelItemCount()):
tItem = self.listBox.topLevelItem(n)
aKey = self._stripNotAllowed(tItem.text(0))
aVal = tItem.text(1)
if len(aKey) > 0:
newList[aKey] = aVal
if tItem is not None:
aKey = self._stripNotAllowed(tItem.text(0))
aVal = tItem.text(1)
if len(aKey) > 0:
newList[aKey] = aVal
return newList
+1 -1
View File
@@ -43,7 +43,7 @@ class GuiQuoteSelect(QDialog):
selectedQuote = ""
def __init__(self, parent=None, currentQuote='"'):
QDialog.__init__(self, parent=parent)
super().__init__(parent=parent)
self.mainConf = novelwriter.CONFIG
+2 -2
View File
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
class GuiUpdates(QDialog):
def __init__(self, mainGui):
QDialog.__init__(self, mainGui)
super().__init__(parent=mainGui)
logger.debug("Initialising GuiUpdates ...")
self.setObjectName("GuiUpdates")
@@ -135,7 +135,7 @@ class GuiUpdates(QDialog):
logException()
relVersion = rawData.get("tag_name", "Unknown")
relDate = rawData.get("created_at", None)
relDate = rawData.get("created_at", "")
try:
relDate = datetime.strptime(relDate[:10], "%Y-%m-%d").strftime("%x")
+17 -11
View File
@@ -23,10 +23,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/>.
"""
import os
import logging
import novelwriter
from pathlib import Path
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QListWidget,
@@ -43,7 +44,7 @@ logger = logging.getLogger(__name__)
class GuiWordList(QDialog):
def __init__(self, mainGui):
QDialog.__init__(self, mainGui)
super().__init__(parent=mainGui)
logger.debug("Initialising GuiWordList ...")
self.setObjectName("GuiWordList")
@@ -150,13 +151,19 @@ class GuiWordList(QDialog):
"""
self._saveGuiSettings()
dctFile = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
tmpFile = dctFile + "~"
dctFile = self.theProject.storage.getMetaFile(nwFiles.PROJ_DICT)
if not isinstance(dctFile, Path):
return False
tmpFile = dctFile.with_suffix(".tmp")
try:
with open(tmpFile, mode="w", encoding="utf-8") as outFile:
for i in range(self.listBox.count()):
outFile.write(self.listBox.item(i).text() + "\n")
item = self.listBox.item(i)
if item is not None:
outFile.write(item.text() + "\n")
tmpFile.replace(dctFile)
except Exception:
logger.error("Could not save new word list")
@@ -164,9 +171,6 @@ class GuiWordList(QDialog):
self.reject()
return False
if os.path.isfile(dctFile):
os.unlink(dctFile)
os.rename(tmpFile, dctFile)
self.accept()
return True
@@ -185,10 +189,12 @@ class GuiWordList(QDialog):
def _loadWordList(self):
"""Load the project's word list, if it exists.
"""
self.listBox.clear()
wordList = self.theProject.storage.getMetaFile(nwFiles.PROJ_DICT)
if not isinstance(wordList, Path):
return False
wordList = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
if not os.path.isfile(wordList):
self.listBox.clear()
if not wordList.exists():
logger.debug("No project dictionary file found")
return False