Merge branch 'main' into release_1.0rc1

This commit is contained in:
Veronica K. B. Olsen
2020-10-18 11:45:37 +02:00
76 changed files with 992 additions and 457 deletions
+22
View File
@@ -35,6 +35,28 @@ from PyQt5.QtWidgets import QApplication, QErrorMessage
from nw.error import exceptionHandler
from nw.config import Config
#
# Version Scheme
# ================
# Generally follows PEP 440
# Hex Version:
# - Digit 1,2 : Major Version (01, 02, 03)
# = Digit 3,4 : Minor Version (01, 09, 10, 99)
# - Digit 5,6 : Patch Version (01, 09, 10, 99)
# = Digit 7 : Release Type (a: aplha, b: beta, c: candidate, f: final)
# - Digit 8 : Release Number (0-9)
#
# Example : Full Short Description
# -------------------------------------------------------------------------
# 0x010200a0 : 1.2-alpha0 1.2a0 Can be used for the dev branch
# 0x010200a1 : 1.2-alpha1 1.2a1 First alpha release
# 0x010200b1 : 1.2-beta1 1.2b1 First beta release
# 0x010200c1 : 1.2-rc1 1.2rc1 First release candidate
# 0x010200f0 : 1.2 1.2 Final release
# 0x010200f1 : 1.2-post1 1.2.post1 Post release, but not a code patch!
# 0x010201f0 : 1.2.1 1.2.1 Patch release
#
__package__ = "nw"
__author__ = "Veronica Berglyd Olsen"
__copyright__ = "Copyright 20182020, Veronica Berglyd Olsen"
+3 -3
View File
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
"""novelWriter Language Codes
"""novelWriter ISO Codes
novelWriter Language Codes
==============================
novelWriter ISO Codes
=========================
Handles translating language codes to language names
File History:
+9 -6
View File
@@ -67,7 +67,9 @@ class NWDoc():
def openDocument(self, tHandle, showStatus=True, isOrphan=False):
"""Open a document from handle, capturing potential file system
errors and parse meta data.
errors and parse meta data. If the document doesn't exist on
disk, return an empty string. If something went wrong, return
None.
"""
if not isHandle(tHandle):
return None
@@ -120,13 +122,13 @@ class NWDoc():
logger.verbose("DocMeta: '%s'" % self._docMeta)
if showStatus and not isOrphan:
self.theParent.statusBar.setStatus("Opened Document: %s" % self._theItem.itemName)
self.theParent.setStatus("Opened Document: %s" % self._theItem.itemName)
return theText
def saveDocument(self, docText):
"""Save the document via temp file in case of save failure, and
in any case keep a backup of the file.
"""Save the document. The file is saved via a temp file in case
of save failure. Returns True if successful, False if not.
"""
if self._docHandle is None:
return False
@@ -139,6 +141,7 @@ class NWDoc():
docPath = os.path.join(self.theProject.projContent, docFile)
docTemp = os.path.join(self.theProject.projContent, docFile+"~")
# DocMeta line
if self._theItem is None:
docMeta = ""
else:
@@ -166,12 +169,12 @@ class NWDoc():
os.unlink(docPath)
os.rename(docTemp, docPath)
self.theParent.statusBar.setStatus("Saved Document: %s" % self._theItem.itemName)
self.theParent.setStatus("Saved Document: %s" % self._theItem.itemName)
return True
def deleteDocument(self, tHandle):
"""Permanently delete a document source file and its backups
"""Permanently delete a document source file and related files
from the project data folder.
"""
if not isHandle(tHandle):
+3 -3
View File
@@ -279,7 +279,7 @@ class NWIndex():
if theItem.itemLayout == nwItemLayout.NO_LAYOUT:
logger.info("Not indexing no-layout item %s" % tHandle)
return False
if theItem.parHandle is None:
if theItem.itemParent is None:
logger.info("Not indexing orphaned item %s" % tHandle)
return False
@@ -288,7 +288,7 @@ class NWIndex():
self.textCounts[tHandle] = [cC, wC, pC]
# If the file is archived or trashed, we don't index the file itself
if self.theProject.projTree.isTrashRoot(theItem.parHandle):
if self.theProject.projTree.isTrashRoot(theItem.itemParent):
logger.info("Not indexing trash item %s" % tHandle)
return False
if theRoot.itemClass == nwItemClass.ARCHIVE:
@@ -583,7 +583,7 @@ class NWIndex():
def getCounts(self, tHandle, sTitle=None):
"""Returns the counts for a file, or a section of a file
starting at title nTitle.
starting at title sTitle if it is provided.
"""
cC = 0
wC = 0
+11 -8
View File
@@ -42,7 +42,7 @@ class NWItem():
self.itemName = ""
self.itemHandle = None
self.parHandle = None
self.itemParent = None
self.itemOrder = None
self.itemType = nwItemType.NO_TYPE
self.itemClass = nwItemClass.NO_CLASS
@@ -70,7 +70,7 @@ class NWItem():
xPack = etree.SubElement(xParent, "item", attrib={
"handle" : str(self.itemHandle),
"order" : str(self.itemOrder),
"parent" : str(self.parHandle),
"parent" : str(self.itemParent),
})
self._subPack(xPack, "name", text=str(self.itemName))
self._subPack(xPack, "type", text=str(self.itemType.name))
@@ -85,6 +85,7 @@ class NWItem():
self._subPack(xPack, "cursorPos", text=str(self.cursorPos), none=False)
else:
self._subPack(xPack, "expanded", text=str(self.isExpanded))
return
def unpackXML(self, xItem):
@@ -101,7 +102,7 @@ class NWItem():
return False
if "parent" in xItem.attrib:
self.parHandle = xItem.attrib["parent"]
self.itemParent = xItem.attrib["parent"]
setMap = {
"name" : self.setName,
@@ -131,9 +132,11 @@ class NWItem():
"""
if not none and (text is None or text == "None"):
return None
xSub = etree.SubElement(xParent, name, attrib=attrib)
xAttr = {} if attrib is None else attrib
xSub = etree.SubElement(xParent, name, attrib=xAttr)
if text is not None:
xSub.text = text
return
##
@@ -162,14 +165,14 @@ class NWItem():
"""Set the parent handle, and ensure that it is valid.
"""
if theParent is None:
self.parHandle = None
self.itemParent = None
elif isinstance(theParent, str):
if len(theParent) == 13:
self.parHandle = theParent
self.itemParent = theParent
else:
self.parHandle = None
self.itemParent = None
else:
self.parHandle = None
self.itemParent = None
return
def setOrder(self, theOrder):
+5 -5
View File
@@ -782,7 +782,7 @@ class NWProject():
"""Create a zip file of the entire project.
"""
logger.info("Backing up project")
self.theParent.statusBar.setStatus("Backing up project ...")
self.theParent.setStatus("Backing up project ...")
if self.mainConf.backupPath is None or self.mainConf.backupPath == "":
self.theParent.makeAlert((
@@ -847,7 +847,7 @@ class NWProject():
)
return False
self.theParent.statusBar.setStatus("Project backed up to '%s.zip'" % baseName)
self.theParent.setStatus("Project backed up to '%s.zip'" % baseName)
return True
@@ -1139,16 +1139,16 @@ class NWProject():
# Technically a bug since treeOrder is built from the
# same data as projTree
continue
elif tItem.parHandle is None:
elif tItem.itemParent is None:
# Item is a root, or already been identified as an
# orphaned item
sentItems.append(tHandle)
yield tItem
elif tItem.parHandle in sentItems:
elif tItem.itemParent in sentItems:
# Item's parent has been sent, so all is fine
sentItems.append(tHandle)
yield tItem
elif tItem.parHandle in iterItems:
elif tItem.itemParent in iterItems:
# Item's parent exists, but hasn't been sent yet, so add
# it again to the end
logger.warning("Item %s found before its parent" % tHandle)
+2 -2
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""novelWriter Spell Check Wrapper
"""novelWriter Spell Check Classes
novelWriter Spell Check Wrapper
novelWriter Spell Check Classes
===================================
Wrapper class for spell checking
+1
View File
@@ -41,6 +41,7 @@ class ToHtml(Tokenizer):
def __init__(self, theProject, theParent):
Tokenizer.__init__(self, theProject, theParent)
self.genMode = self.M_EXPORT
self.cssStyles = True
+16 -16
View File
@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
"""novelWriter Word Counter
"""novelWriter Various Tools
novelWriter Word Counter
============================
Simple word counter
novelWriter Various Tools
=============================
Various core tool functions
File History:
Created: 2019-04-22 [0.0.1] countWords
Created: 2019-10-13 [0.2.3] numberToWord, _numberToWordEN
Merged: 2020-05-08 [0.4.5] All of the above into this file
Created: 2019-04-22 [0.0.1] countWords
Created: 2019-10-13 [0.2.3] numberToWord, _numberToWordEN
Merged: 2020-05-08 [0.4.5] All of the above into this file
Created: 2020-07-05 [0.10.0] numberToRoman
This file is a part of novelWriter
Copyright 20182020, Veronica Berglyd Olsen
@@ -136,7 +137,6 @@ def numberToWord(numVal, theLanguage):
def _numberToWordEN(numVal):
"""Convert numbers to English words.
"""
numWord = ""
oneWord = ""
tenWord = ""
hunWord = ""
@@ -145,8 +145,8 @@ def _numberToWordEN(numVal):
return "Zero"
oneVal = numVal % 10
tenVal = (numVal-oneVal) % 100
hunVal = (numVal-tenVal-oneVal) % 1000
tenVal = (numVal - oneVal) % 100
hunVal = (numVal - tenVal - oneVal) % 1000
theHundreds = {
100: "One Hundred", 200: "Two Hundred", 300: "Three Hundred",
@@ -167,18 +167,18 @@ def _numberToWordEN(numVal):
}
hunWord = theHundreds.get(hunVal, "")
tenWord = theTens.get(tenVal, "")
if tenVal == 10:
oneWord = theTeens.get(oneVal, "")
numWord = ("%s %s" % (hunWord, oneWord)).strip()
return f"{hunWord} {oneWord}".strip()
else:
oneWord = theOnes.get(oneVal, "")
if tenVal == 0:
numWord = ("%s %s" % (hunWord, oneWord)).strip()
return f"{hunWord} {oneWord}".strip()
else:
tenWord = theTens.get(tenVal, "")
if oneVal == 0:
numWord = ("%s %s" % (hunWord, tenWord)).strip()
return f"{hunWord} {tenWord}".strip()
else:
numWord = ("%s %s-%s" % (hunWord, tenWord, oneWord)).strip()
return f"{hunWord} {tenWord}-{oneWord}".strip()
return numWord
return ""
+5 -5
View File
@@ -134,7 +134,7 @@ class NWTree():
for xItem in xContent:
nwItem = NWItem(self.theProject)
if nwItem.unpackXML(xItem):
self.append(nwItem.itemHandle, nwItem.parHandle, nwItem)
self.append(nwItem.itemHandle, nwItem.itemParent, nwItem)
nwItem.saveInitialCount()
return True
@@ -261,10 +261,10 @@ class NWTree():
tItem = self.__getitem__(tHandle)
if tItem is not None:
for i in range(nwConst.maxDepth + 1):
if tItem.parHandle is None:
if tItem.itemParent is None:
return tItem
else:
tHandle = tItem.parHandle
tHandle = tItem.itemParent
tItem = self.__getitem__(tHandle)
return None
@@ -279,10 +279,10 @@ class NWTree():
if tItem is not None:
tTree.append(tHandle)
for i in range(nwConst.maxDepth + 1):
if tItem.parHandle is None:
if tItem.itemParent is None:
return tTree
else:
tHandle = tItem.parHandle
tHandle = tItem.itemParent
tItem = self.__getitem__(tHandle)
if tItem is None:
return tTree
+1 -1
View File
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""novelWriter Init
"""novelWriter Exception Handling
novelWriter Exception Handling
==================================
-1
View File
@@ -222,7 +222,6 @@ class GuiAbout(QDialog):
hColB = self.theParent.theTheme.colHead[2],
)
self.pageAbout.document().setDefaultStyleSheet(styleSheet)
# self.pageCredit.document().setDefaultStyleSheet(styleSheet)
self.pageLicense.document().setDefaultStyleSheet(styleSheet)
return
+2 -2
View File
@@ -684,8 +684,8 @@ class GuiBuildNovel(QDialog):
isNone |= theItem.itemLayout == nwItemLayout.NO_LAYOUT
isNone |= theItem.itemClass == nwItemClass.NO_CLASS
isNone |= theItem.itemClass == nwItemClass.TRASH
isNone |= theItem.parHandle == self.theProject.projTree.trashRoot()
isNone |= theItem.parHandle is None
isNone |= theItem.itemParent == self.theProject.projTree.trashRoot()
isNone |= theItem.itemParent is None
isNote = theItem.itemLayout == nwItemLayout.NOTE
isNovel = not isNone and not isNote
+4 -4
View File
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
"""novelWriter Addition QConfigLayout
"""novelWriter Custom Widgets and Layouts
novelWriter Addition QConfigLayout
======================================
A custom Qt grid layout for config forms similar to QFormLayout
novelWriter Custom Widgets and Layouts
==========================================
Various custom widget and layout classes
File History:
Created: 2020-05-03 [0.4.5] QConfigLayout
+9 -10
View File
@@ -807,16 +807,16 @@ class GuiDocEditor(QTextEdit):
return self.docSearch.cycleFocus(toNext)
return True
def mouseReleaseEvent(self, mEvent):
def mouseReleaseEvent(self, theEvent):
"""If the mouse button is released and the control key is
pressed, check if we're clicking on a tag, and trigger the
follow tag function.
"""
if qApp.keyboardModifiers() == Qt.ControlModifier:
theCursor = self.cursorForPosition(mEvent.pos())
theCursor = self.cursorForPosition(theEvent.pos())
self._followTag(theCursor)
QTextEdit.mouseReleaseEvent(self, mEvent)
QTextEdit.mouseReleaseEvent(self, theEvent)
self.docFooter.updateLineCount()
return
@@ -1203,20 +1203,19 @@ class GuiDocEditor(QTextEdit):
"""Check if document size crosses the big document limit set in
config. If so, we will set the big document flag to True.
"""
newState = theSize > self.mainConf.bigDocLimit*1000
bigLim = self.mainConf.bigDocLimit*1000
newState = theSize > bigLim
if newState != self.bigDoc:
if newState:
logger.info(
"The document size is {:n} > {:n}, big doc mode has been enabled".format(
theSize, self.mainConf.bigDocLimit*1000
)
f"The document size is {theSize:n} > {bigLim:n}, "
f"big doc mode has been enabled"
)
else:
logger.info(
"The document size is {:n} <= {:n}, big doc mode has been disabled".format(
theSize, self.mainConf.bigDocLimit*1000
)
f"The document size is {theSize:n} <= {bigLim:n}, "
f"big doc mode has been disabled"
)
self.bigDoc = newState
+7 -3
View File
@@ -149,18 +149,22 @@ class GuiDocHighlighter(QSyntaxHighlighter):
# Quoted Strings
if self.mainConf.highlightQuotes:
fmtDO = self.mainConf.fmtDoubleQuotes[0]
fmtDC = self.mainConf.fmtDoubleQuotes[1]
fmtSO = self.mainConf.fmtSingleQuotes[0]
fmtSC = self.mainConf.fmtSingleQuotes[1]
self.hRules.append((
"\\B{:s}(.*?){:s}\\B".format('"', '"'), {
"\\B\"(.*?)\"\\B", {
0 : self.hStyles["dialogue1"],
}
))
self.hRules.append((
"\\B{:s}(.*?){:s}\\B".format(*self.mainConf.fmtDoubleQuotes), {
f"\\B{fmtDO:s}(.*?){fmtDC:s}\\B", {
0 : self.hStyles["dialogue2"],
}
))
self.hRules.append((
"\\B{:s}(.*?){:s}\\B".format(*self.mainConf.fmtSingleQuotes), {
f"\\B{fmtSO:s}(.*?){fmtSC:s}\\B", {
0 : self.hStyles["dialogue3"],
}
))
+1 -1
View File
@@ -127,7 +127,7 @@ class GuiDocMerge(QDialog):
), nwAlert.ERROR)
return
nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.parHandle)
nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.itemParent)
newItem = self.theProject.projTree[nHandle]
newItem.setStatus(srcItem.itemStatus)
+2 -2
View File
@@ -154,7 +154,7 @@ class GuiDocSplit(QDialog):
return
# Check that another folder can be created
parTree = self.theProject.projTree.getItemPath(srcItem.parHandle)
parTree = self.theProject.projTree.getItemPath(srcItem.itemParent)
if len(parTree) >= nwConst.maxDepth - 1:
self.theParent.makeAlert((
"Cannot add new folder for the document split. "
@@ -176,7 +176,7 @@ class GuiDocSplit(QDialog):
# Create the folder
fHandle = self.theProject.newFolder(
srcItem.itemName, srcItem.itemClass, srcItem.parHandle
srcItem.itemName, srcItem.itemClass, srcItem.itemParent
)
self.theParent.treeView.revealNewTreeItem(fHandle)
logger.verbose("Creating folder %s" % fHandle)
+7 -3
View File
@@ -439,6 +439,10 @@ class GuiOutline(QTreeWidget):
newItem = QTreeWidgetItem()
hIcon = "doc_%s" % tLevel.lower()
cC = int(novIdx["cCount"])
wC = int(novIdx["wCount"])
pC = int(novIdx["pCount"])
newItem.setText(self.colIndex[nwOutline.TITLE], novIdx["title"])
newItem.setData(self.colIndex[nwOutline.TITLE], Qt.UserRole, tHandle)
newItem.setIcon(self.colIndex[nwOutline.TITLE], self.theTheme.getIcon(hIcon))
@@ -448,9 +452,9 @@ class GuiOutline(QTreeWidget):
newItem.setText(self.colIndex[nwOutline.LINE], sTitle[1:].lstrip("0"))
newItem.setData(self.colIndex[nwOutline.LINE], Qt.UserRole, sTitle)
newItem.setText(self.colIndex[nwOutline.SYNOP], novIdx["synopsis"])
newItem.setText(self.colIndex[nwOutline.CCOUNT], "{:n}".format(novIdx["cCount"]))
newItem.setText(self.colIndex[nwOutline.WCOUNT], "{:n}".format(novIdx["wCount"]))
newItem.setText(self.colIndex[nwOutline.PCOUNT], "{:n}".format(novIdx["pCount"]))
newItem.setText(self.colIndex[nwOutline.CCOUNT], f"{cC:n}")
newItem.setText(self.colIndex[nwOutline.WCOUNT], f"{wC:n}")
newItem.setText(self.colIndex[nwOutline.PCOUNT], f"{pC:n}")
newItem.setTextAlignment(self.colIndex[nwOutline.CCOUNT], Qt.AlignRight)
newItem.setTextAlignment(self.colIndex[nwOutline.WCOUNT], Qt.AlignRight)
newItem.setTextAlignment(self.colIndex[nwOutline.PCOUNT], Qt.AlignRight)
+11 -7
View File
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
"""novelWriter GUI Project Outline
"""novelWriter GUI Project Outline Details
novelWriter GUI Project Outline
===================================
Class holding the project outline view
novelWriter GUI Project Outline Details
===========================================
Class holding the project outline details view
File History:
Created: 2020-06-02 [0.7.0]
@@ -266,9 +266,13 @@ class GuiOutlineDetails(QScrollArea):
self.fileValue.setText(nwItem.itemName)
self.itemValue.setText(nwItem.itemStatus)
self.cCValue.setText("{:n}".format(checkInt(novIdx["cCount"], 0)))
self.wCValue.setText("{:n}".format(checkInt(novIdx["wCount"], 0)))
self.pCValue.setText("{:n}".format(checkInt(novIdx["pCount"], 0)))
cC = checkInt(novIdx["cCount"], 0)
wC = checkInt(novIdx["wCount"], 0)
pC = checkInt(novIdx["pCount"], 0)
self.cCValue.setText(f"{cC:n}")
self.wCValue.setText(f"{wC:n}")
self.pCValue.setText(f"{pC:n}")
self.synopValue.setText(novIdx["synopsis"])
+4 -4
View File
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
"""novelWriter GUI Config Editor
"""novelWriter GUI Preferences
novelWriter GUI Config Editor
=================================
Class holding the config dialog
novelWriter GUI Preferences
===============================
Class holding the preferences dialog
File History:
Created: 2019-06-10 [0.1.5]
+1 -1
View File
@@ -3,7 +3,7 @@
novelWriter GUI Open Project
================================
New and open project dialog
The open project dialog
File History:
Created: 2020-02-26 [0.4.5]
+4 -3
View File
@@ -270,11 +270,12 @@ class GuiProjectEditMeta(QWidget):
self.revLabel = QLabel("Revision count:")
self.revLabel.setIndent(xInd)
self.revValue = QLabel("{:n}".format(self.theProject.saveCount))
self.revValue = QLabel(f"{self.theProject.saveCount:n}")
editHours = self.theProject.editTime/3600
self.editLabel = QLabel("Edit time:")
self.editLabel.setIndent(xInd)
self.editValue = QLabel("{:.2f} hours".format(self.theProject.editTime/3600))
self.editValue = QLabel(f"{editHours:.2f} hours")
self.statsLabel = QLabel("<b>Project Stats</b>")
@@ -294,7 +295,7 @@ class GuiProjectEditMeta(QWidget):
self.wordsLabel = QLabel("Word count:")
self.wordsLabel.setIndent(xInd)
self.wordsValue = QLabel("{:n}".format(self.theProject.currWCount))
self.wordsValue = QLabel(f"{self.theProject.currWCount:n}")
self.mainForm.addWidget(self.headLabel, 0, 0, 1, 2, Qt.AlignTop)
self.mainForm.addWidget(self.nameLabel, 1, 0, 1, 1, Qt.AlignTop)
+10 -10
View File
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
"""novelWriter GUI Document Tree
"""novelWriter GUI Project Tree
novelWriter GUI Document Tree
=================================
Class holding the left side document tree view
novelWriter GUI project Tree
================================
Class holding the left side project tree view
File History:
Created: 2018-09-29 [0.0.1] GuiProjectTree
@@ -219,7 +219,7 @@ class GuiProjectTree(QTreeWidget):
pItem = self.theProject.projTree[pHandle]
if pItem.itemType == nwItemType.FILE:
nHandle = pHandle
pHandle = pItem.parHandle
pHandle = pItem.itemParent
# If we again have no home, give up
if pHandle is None:
@@ -270,7 +270,7 @@ class GuiProjectTree(QTreeWidget):
"""
nwItem = self.theProject.projTree[tHandle]
trItem = self._addTreeItem(nwItem, nHandle)
pHandle = nwItem.parHandle
pHandle = nwItem.itemParent
if pHandle is not None and pHandle in self.theMap:
self.theMap[pHandle].setExpanded(True)
self.clearSelection()
@@ -430,7 +430,7 @@ class GuiProjectTree(QTreeWidget):
logger.error("Could not delete item")
return False
pHandle = nwItemS.parHandle
pHandle = nwItemS.itemParent
if self.theProject.projTree.isTrashRoot(pHandle):
# If the file is in the trash folder already, as the
# user if they want to permanently delete the file.
@@ -815,7 +815,7 @@ class GuiProjectTree(QTreeWidget):
project tree.
"""
tHandle = nwItem.itemHandle
pHandle = nwItem.parHandle
pHandle = nwItem.itemParent
tClass = nwItem.itemClass
newItem = QTreeWidgetItem([""]*4)
@@ -1022,11 +1022,11 @@ class GuiProjectTreeMenu(QMenu):
trashHandle = self.theTree.theProject.projTree.trashRoot()
inTrash = theItem.parHandle == trashHandle and trashHandle is not None
inTrash = theItem.itemParent == trashHandle and trashHandle is not None
isTrash = theItem.itemHandle == trashHandle and trashHandle is not None
isFile = theItem.itemType == nwItemType.FILE
isArch = theRoot.itemClass == nwItemClass.ARCHIVE
isOrph = isFile and theItem.parHandle is None
isOrph = isFile and theItem.itemParent is None
showOpen = isFile
showView = isFile
+8 -15
View File
@@ -195,28 +195,21 @@ class GuiMainStatus(QStatusBar):
self.statsText.setToolTip(
"Project word count (session change)"
)
self.statsText.setText((
"Words: {pWC:n} ({sWC:+n})"
).format(
pWC = self.projWords,
sWC = self.sessWords,
))
self.statsText.setText(
f"Words: {self.projWords:n} ({self.sessWords:+n})"
)
return
def _updateTime(self):
"""Update the session clock.
"""
if self.refTime is None:
theTime = "00:00:00"
self.timeText.setText("00:00:00")
else:
# This is much faster than using datetime format
tS = int(time() - self.refTime)
tM = int(tS/60)
tH = int(tM/60)
tM = tM - tH*60
tS = tS - tM*60 - tH*3600
theTime = "%02d:%02d:%02d" % (tH, tM, tS)
self.timeText.setText(theTime)
self.timeText.setText(
f"{tS//3600:02d}:{tS%3600//60:02d}:{tS%60:02d}"
)
return
# END Class GuiMainStatus
@@ -237,7 +230,7 @@ class StatusLED(QAbstractButton):
return
##
# Getters and Setters
# Setters
##
def setState(self, theState):
+4 -4
View File
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
"""novelWriter Theme Class
"""novelWriter Theme and Icons Classes
novelWriter Theme Class
===========================
This class reads and store the main theme
novelWriter Theme and Icons Classs
======================================
This class reads and stores the themes and the icons
File History:
Created: 2019-05-18 [0.1.3] GuiTheme
+42 -25
View File
@@ -62,7 +62,9 @@ class GuiMain(QMainWindow):
self.mainConf = nw.CONFIG
self.threadPool = QThreadPool()
# Some runtime info useful for debugging
# System Info
# ===========
logger.info("OS: %s" % self.mainConf.osType)
logger.info("Kernel: %s" % self.mainConf.kernelVer)
logger.info("Host: %s" % self.mainConf.hostName)
@@ -76,6 +78,9 @@ class GuiMain(QMainWindow):
self.mainConf.verPyString, self.mainConf.verPyHexVal)
)
# Core Classes
# ============
# Core Classes and settings
self.theTheme = GuiTheme(self)
self.theProject = NWProject(self)
@@ -89,7 +94,7 @@ class GuiMain(QMainWindow):
self.setWindowIcon(QIcon(self.mainConf.appIcon))
# Build the GUI
################
# =============
# Main GUI Elements
self.statusBar = GuiMainStatus(self)
@@ -106,7 +111,7 @@ class GuiMain(QMainWindow):
self.statusIcons = []
self.importIcons = []
# Assemble Main Window
# Project Tree View
self.treePane = QWidget()
self.treeBox = QVBoxLayout()
self.treeBox.setContentsMargins(0, 0, 0, 0)
@@ -114,20 +119,24 @@ class GuiMain(QMainWindow):
self.treeBox.addWidget(self.treeMeta)
self.treePane.setLayout(self.treeBox)
# Splitter : Document Viewer / Document Meta
self.splitView = QSplitter(Qt.Vertical)
self.splitView.addWidget(self.docViewer)
self.splitView.addWidget(self.viewMeta)
self.splitView.setSizes(self.mainConf.getViewPanePos())
# Splitter : Document Editor / Document Viewer
self.splitDocs = QSplitter(Qt.Horizontal)
self.splitDocs.addWidget(self.docEditor)
self.splitDocs.addWidget(self.splitView)
# Splitter : Project Outlie / Outline Details
self.splitOutline = QSplitter(Qt.Vertical)
self.splitOutline.addWidget(self.projView)
self.splitOutline.addWidget(self.projMeta)
self.splitOutline.setSizes(self.mainConf.getOutlinePanePos())
# Main Tabs : Edirot / Outline
self.tabWidget = QTabWidget()
self.tabWidget.setTabPosition(QTabWidget.East)
self.tabWidget.setStyleSheet("QTabWidget::pane {border: 0;}")
@@ -135,6 +144,7 @@ class GuiMain(QMainWindow):
self.tabWidget.addTab(self.splitOutline, "Outline")
self.tabWidget.currentChanged.connect(self._mainTabChanged)
# Splitter : Project Tree / Main Tabs
xCM = self.mainConf.pxInt(4)
self.splitMain = QSplitter(Qt.Horizontal)
self.splitMain.setContentsMargins(xCM, xCM, xCM, xCM)
@@ -142,6 +152,7 @@ class GuiMain(QMainWindow):
self.splitMain.addWidget(self.tabWidget)
self.splitMain.setSizes(self.mainConf.getMainPanePos())
# Indices of All Splitter Widgets
self.idxTree = self.splitMain.indexOf(self.treePane)
self.idxMain = self.splitMain.indexOf(self.tabWidget)
self.idxEditor = self.splitDocs.indexOf(self.docEditor)
@@ -151,6 +162,7 @@ class GuiMain(QMainWindow):
self.idxTabEdit = self.tabWidget.indexOf(self.splitDocs)
self.idxTabProj = self.tabWidget.indexOf(self.splitOutline)
# Splitter Behaviour
self.splitMain.setCollapsible(self.idxTree, False)
self.splitMain.setCollapsible(self.idxMain, False)
self.splitDocs.setCollapsible(self.idxEditor, False)
@@ -158,10 +170,11 @@ class GuiMain(QMainWindow):
self.splitView.setCollapsible(self.idxViewDoc, False)
self.splitView.setCollapsible(self.idxViewMeta, False)
# Editor / Viewer Default State
self.splitView.setVisible(False)
self.docEditor.closeSearch()
# Build the Tree View
# Initialise the Project Tree
self.treeView.itemSelectionChanged.connect(self._treeSingleClick)
self.treeView.itemDoubleClicked.connect(self._treeDoubleClick)
self.rebuildTree()
@@ -172,13 +185,13 @@ class GuiMain(QMainWindow):
self.setStatusBar(self.statusBar)
# Finalise Initialisation
##########################
# =======================
# Set Up Autosaving Project Timer
# Set Up Auto-Save Project Timer
self.asProjTimer = QTimer()
self.asProjTimer.timeout.connect(self._autoSaveProject)
# Set Up Autosaving Document Timer
# Set Up Auto-Save Document Timer
self.asDocTimer = QTimer()
self.asDocTimer.timeout.connect(self._autoSaveDocument)
@@ -203,11 +216,13 @@ class GuiMain(QMainWindow):
# Check that config loaded fine
self.reportConfErr()
# Initialise Main GUI
self.initMain()
self.asProjTimer.start()
self.asDocTimer.start()
self.statusBar.clearStatus()
# Handle Windows Mode
self.showNormal()
if self.mainConf.isFullScreen:
self.toggleFullScreenMode()
@@ -224,7 +239,7 @@ class GuiMain(QMainWindow):
self.showProjectLoadDialog()
logger.debug("novelWriter is ready ...")
self.statusBar.setStatus("novelWriter is ready ...")
self.setStatus("novelWriter is ready ...")
return
@@ -249,8 +264,7 @@ class GuiMain(QMainWindow):
##
def newProject(self, projData=None):
"""Create new project with a few default files and folders.
The variable forceNew is used for testing.
"""Create new project via the new project wizard.
"""
if self.hasProject:
self.makeAlert(
@@ -293,7 +307,7 @@ class GuiMain(QMainWindow):
def closeProject(self, isYes=False):
"""Closes the project if one is open. isYes is passed on from
the close application event so the user doesn't get prompted
twice.
twice to confirm.
"""
if not self.hasProject:
# There is no project loaded, everything OK
@@ -302,7 +316,7 @@ class GuiMain(QMainWindow):
if not isYes:
msgBox = QMessageBox()
msgRes = msgBox.question(
self, "Close Project", "Save changes and close current project?"
self, "Close Project", "Save changes and close the current project?"
)
if msgRes != QMessageBox.Yes:
return False
@@ -318,7 +332,7 @@ class GuiMain(QMainWindow):
if self.mainConf.askBeforeBackup:
msgBox = QMessageBox()
msgRes = msgBox.question(
self, "Backup Project", "Backup current project?"
self, "Backup Project", "Backup the current project?"
)
if msgRes != QMessageBox.Yes:
doBackup = False
@@ -433,6 +447,7 @@ class GuiMain(QMainWindow):
if self.theProject.projPath is None:
projPath = self.selectProjectPath()
self.theProject.setProjectPath(projPath)
if self.theProject.projPath is None:
return False
@@ -454,6 +469,7 @@ class GuiMain(QMainWindow):
if self.docEditor.docChanged:
self.saveDocument()
self.docEditor.clearEditor()
return True
def openDocument(self, tHandle, tLine=None, changeFocus=True, doScroll=False):
@@ -469,6 +485,7 @@ class GuiMain(QMainWindow):
self.treeView.setSelectedHandle(tHandle, doScroll=doScroll)
else:
return False
return True
def openNextDocument(self, tHandle, wrapAround=False):
@@ -546,6 +563,7 @@ class GuiMain(QMainWindow):
vPos[1] = bPos[1] - vPos[0]
self.splitDocs.setSizes(vPos)
self.viewMeta.setVisible(self.mainConf.showRefPanel)
self.docViewer.navigateTo(tAnchor)
return True
@@ -697,9 +715,9 @@ class GuiMain(QMainWindow):
for nDone, tItem in enumerate(self.theProject.projTree):
if tItem is not None:
self.statusBar.setStatus("Indexing: '%s'" % tItem.itemName)
self.setStatus("Indexing: '%s'" % tItem.itemName)
else:
self.statusBar.setStatus("Indexing: Unknown item")
self.setStatus("Indexing: Unknown item")
if tItem is not None and tItem.itemType == nwItemType.FILE:
logger.verbose("Scanning: %s" % tItem.itemName)
@@ -717,7 +735,7 @@ class GuiMain(QMainWindow):
self.treeView.projectWordCount()
tEnd = time()
self.statusBar.setStatus("Indexing completed in %.1f ms" % ((tEnd - tStart)*1000.0))
self.setStatus("Indexing completed in %.1f ms" % ((tEnd - tStart)*1000.0))
self.docEditor.updateTagHighLighting()
qApp.restoreOverrideCursor()
@@ -754,7 +772,8 @@ class GuiMain(QMainWindow):
def showProjectLoadDialog(self):
"""Opens the projects dialog for selecting either existing
projects from a cache of recently opened projects, or provide a
browse button for projects not yet cached.
browse button for projects not yet cached. Selecting to create a
new project is forwarded to the new project wizard.
"""
dlgProj = GuiProjectLoad(self)
dlgProj.exec_()
@@ -767,7 +786,7 @@ class GuiMain(QMainWindow):
return True
def showNewProjectDialog(self):
"""Open the wizard and assemble the project options dict.
"""Open the wizard and assemble a project options dict.
"""
newProj = GuiProjectWizard(self)
newProj.exec_()
@@ -865,8 +884,7 @@ class GuiMain(QMainWindow):
def makeAlert(self, theMessage, theLevel=nwAlert.INFO):
"""Alert both the user and the logger at the same time. Message
can be either a string or an array of strings. Severity level is
0 = info, 1 = warning, and 2 = error.
can be either a string or an array of strings.
"""
if isinstance(theMessage, list):
popMsg = "<br>".join(theMessage)
@@ -955,7 +973,7 @@ class GuiMain(QMainWindow):
return True
def setFocus(self, paneNo):
"""Switch focus to one of the three main gUi panes.
"""Switch focus to one of the three main GUI panes.
"""
if paneNo == 1:
self.treeView.setFocus()
@@ -1236,9 +1254,9 @@ class GuiMain(QMainWindow):
return
def _treeKeyPressReturn(self):
"""The user pressed return an item in the tree. If it is a file,
we open it. Otherwise, we do nothing. Pressing return does not
change focus to the editor as double click does.
"""The user pressed return on an item in the tree. If it is a
file, we open it. Otherwise, we do nothing. Pressing return does
not change focus to the editor as double click does.
"""
tHandle = self.treeView.getSelectedHandle()
logger.verbose("User pressed return on tree item with handle %s" % tHandle)
@@ -1257,7 +1275,6 @@ class GuiMain(QMainWindow):
"""
if self.docEditor.docSearch.isVisible():
self.docEditor.closeSearch()
return
elif self.isFocusMode:
self.toggleFocusMode()
return