+6
-1
@@ -87,6 +87,7 @@ def main(sysArgs=None):
|
|||||||
"logfile=",
|
"logfile=",
|
||||||
"version",
|
"version",
|
||||||
"config=",
|
"config=",
|
||||||
|
"data=",
|
||||||
"testmode",
|
"testmode",
|
||||||
"style=",
|
"style=",
|
||||||
]
|
]
|
||||||
@@ -105,6 +106,7 @@ def main(sysArgs=None):
|
|||||||
" -l, --logfile= Specify log file.\n"
|
" -l, --logfile= Specify log file.\n"
|
||||||
" --style= Set Qt5 style flag. Defaults to 'Fusion'.\n"
|
" --style= Set Qt5 style flag. Defaults to 'Fusion'.\n"
|
||||||
" --config= Alternative config file.\n"
|
" --config= Alternative config file.\n"
|
||||||
|
" --data= Alternative user data path.\n"
|
||||||
" --headless Do not display GUI. Useful for testing scripts.\n"
|
" --headless Do not display GUI. Useful for testing scripts.\n"
|
||||||
).format(
|
).format(
|
||||||
appname = __package__,
|
appname = __package__,
|
||||||
@@ -120,6 +122,7 @@ def main(sysArgs=None):
|
|||||||
toFile = False
|
toFile = False
|
||||||
toStd = True
|
toStd = True
|
||||||
confPath = None
|
confPath = None
|
||||||
|
dataPath = None
|
||||||
testMode = False
|
testMode = False
|
||||||
qtStyle = "Fusion"
|
qtStyle = "Fusion"
|
||||||
cmdOpen = None
|
cmdOpen = None
|
||||||
@@ -157,6 +160,8 @@ def main(sysArgs=None):
|
|||||||
qtStyle = inArg
|
qtStyle = inArg
|
||||||
elif inOpt in ("--config"):
|
elif inOpt in ("--config"):
|
||||||
confPath = inArg
|
confPath = inArg
|
||||||
|
elif inOpt in ("--data"):
|
||||||
|
dataPath = inArg
|
||||||
elif inOpt in ("--testmode"):
|
elif inOpt in ("--testmode"):
|
||||||
testMode = True
|
testMode = True
|
||||||
|
|
||||||
@@ -187,7 +192,7 @@ def main(sysArgs=None):
|
|||||||
|
|
||||||
logger.setLevel(debugLevel)
|
logger.setLevel(debugLevel)
|
||||||
|
|
||||||
CONFIG.initConfig(confPath)
|
CONFIG.initConfig(confPath, dataPath)
|
||||||
|
|
||||||
if testMode:
|
if testMode:
|
||||||
nwGUI = GuiMain()
|
nwGUI = GuiMain()
|
||||||
|
|||||||
@@ -88,6 +88,25 @@ def colRange(rgbStart, rgbEnd, nStep):
|
|||||||
|
|
||||||
return retCol
|
return retCol
|
||||||
|
|
||||||
|
def formatInt(theInt):
|
||||||
|
"""Formats an integer with k, M, G etc.
|
||||||
|
"""
|
||||||
|
postFix = ["k","M","G","T","P","E"]
|
||||||
|
theVal = float(theInt)
|
||||||
|
|
||||||
|
if theVal > 1000.0:
|
||||||
|
for pF in postFix:
|
||||||
|
theVal /= 1000.0
|
||||||
|
if theVal < 1000.0:
|
||||||
|
if theVal < 10.0:
|
||||||
|
return "%4.2f%s" % (theVal,pF)
|
||||||
|
elif theVal < 100.0:
|
||||||
|
return "%4.1f%s" % (theVal,pF)
|
||||||
|
else:
|
||||||
|
return "%3.0f%s" % (theVal,pF)
|
||||||
|
|
||||||
|
return "%d" % theInt
|
||||||
|
|
||||||
def splitVersionNumber(vString):
|
def splitVersionNumber(vString):
|
||||||
""" Splits a version string on the form aa.bb.cc into major, minor
|
""" Splits a version string on the form aa.bb.cc into major, minor
|
||||||
and patch, and computes an integer value aabbcc.
|
and patch, and computes an integer value aabbcc.
|
||||||
|
|||||||
+126
-45
@@ -12,10 +12,11 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import configparser
|
import configparser
|
||||||
|
import json
|
||||||
import sys
|
import sys
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path, mkdir
|
from os import path, mkdir, unlink, rename
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt5.Qt import PYQT_VERSION_STR
|
from PyQt5.Qt import PYQT_VERSION_STR
|
||||||
@@ -116,9 +117,6 @@ class Config:
|
|||||||
self.showRefPanel = True
|
self.showRefPanel = True
|
||||||
self.viewComments = True
|
self.viewComments = True
|
||||||
|
|
||||||
## Path
|
|
||||||
self.recentList = [""]*10
|
|
||||||
|
|
||||||
# Check Qt5 Versions
|
# Check Qt5 Versions
|
||||||
verQt = splitVersionNumber(QT_VERSION_STR)
|
verQt = splitVersionNumber(QT_VERSION_STR)
|
||||||
self.verQtString = QT_VERSION_STR
|
self.verQtString = QT_VERSION_STR
|
||||||
@@ -162,13 +160,19 @@ class Config:
|
|||||||
self.hasEnchant = False
|
self.hasEnchant = False
|
||||||
self.hasSymSpell = False
|
self.hasSymSpell = False
|
||||||
|
|
||||||
|
# Recent Cache
|
||||||
|
self.recentProj = {}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Actions
|
# Actions
|
||||||
##
|
##
|
||||||
|
|
||||||
def initConfig(self, confPath=None):
|
def initConfig(self, confPath=None, dataPath=None):
|
||||||
|
"""Initialise the config class. The manual setting of confPath
|
||||||
|
and dataPath is mainly intended for the test suite.
|
||||||
|
"""
|
||||||
|
|
||||||
if confPath is None:
|
if confPath is None:
|
||||||
confRoot = QStandardPaths.writableLocation(QStandardPaths.ConfigLocation)
|
confRoot = QStandardPaths.writableLocation(QStandardPaths.ConfigLocation)
|
||||||
@@ -177,11 +181,15 @@ class Config:
|
|||||||
logger.info("Setting config from alternative path: %s" % confPath)
|
logger.info("Setting config from alternative path: %s" % confPath)
|
||||||
self.confPath = confPath
|
self.confPath = confPath
|
||||||
|
|
||||||
if self.verQtValue >= 50400:
|
if dataPath is None:
|
||||||
dataRoot = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
|
if self.verQtValue >= 50400:
|
||||||
|
dataRoot = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
|
||||||
|
else:
|
||||||
|
dataRoot = QStandardPaths.writableLocation(QStandardPaths.DataLocation)
|
||||||
|
self.dataPath = path.join(path.abspath(dataRoot), self.appHandle)
|
||||||
else:
|
else:
|
||||||
dataRoot = QStandardPaths.writableLocation(QStandardPaths.DataLocation)
|
logger.info("Setting data path from alternative path: %s" % dataPath)
|
||||||
self.dataPath = path.join(path.abspath(dataRoot), self.appHandle)
|
self.dataPath = dataPath
|
||||||
|
|
||||||
logger.verbose("Config path: %s" % self.confPath)
|
logger.verbose("Config path: %s" % self.confPath)
|
||||||
logger.verbose("Data path: %s" % self.dataPath)
|
logger.verbose("Data path: %s" % self.dataPath)
|
||||||
@@ -212,25 +220,30 @@ class Config:
|
|||||||
self.confPath = None
|
self.confPath = None
|
||||||
|
|
||||||
# Check if config file exists
|
# Check if config file exists
|
||||||
if path.isfile(path.join(self.confPath,self.confFile)):
|
if self.confPath is not None:
|
||||||
# If it exists, load it
|
if path.isfile(path.join(self.confPath,self.confFile)):
|
||||||
self.loadConfig()
|
# If it exists, load it
|
||||||
else:
|
self.loadConfig()
|
||||||
# If it does not exist, save a copy of the default values
|
else:
|
||||||
self.saveConfig()
|
# If it does not exist, save a copy of the default values
|
||||||
|
self.saveConfig()
|
||||||
|
|
||||||
# If data folder does not exist, make it.
|
# If data folder does not exist, make it.
|
||||||
# This assumes that the os data folder itself exists.
|
# This assumes that the os data folder itself exists.
|
||||||
if not path.isdir(self.dataPath):
|
if self.dataPath is not None:
|
||||||
try:
|
if not path.isdir(self.dataPath):
|
||||||
mkdir(self.dataPath)
|
try:
|
||||||
except Exception as e:
|
mkdir(self.dataPath)
|
||||||
logger.error("Could not create folder: %s" % self.dataPath)
|
except Exception as e:
|
||||||
logger.error(str(e))
|
logger.error("Could not create folder: %s" % self.dataPath)
|
||||||
self.hasError = True
|
logger.error(str(e))
|
||||||
self.errData.append("Could not create folder: %s" % self.dataPath)
|
self.hasError = True
|
||||||
self.errData.append(str(e))
|
self.errData.append("Could not create folder: %s" % self.dataPath)
|
||||||
self.dataPath = None
|
self.errData.append(str(e))
|
||||||
|
self.dataPath = None
|
||||||
|
|
||||||
|
# Load recent projects cache
|
||||||
|
self.loadRecentCache()
|
||||||
|
|
||||||
# Check the availability of optional packages
|
# Check the availability of optional packages
|
||||||
self._checkOptionalPackages()
|
self._checkOptionalPackages()
|
||||||
@@ -389,10 +402,6 @@ class Config:
|
|||||||
self.lastPath = self._parseLine(
|
self.lastPath = self._parseLine(
|
||||||
cnfParse, cnfSec, "lastpath", self.CNF_STR, self.lastPath
|
cnfParse, cnfSec, "lastpath", self.CNF_STR, self.lastPath
|
||||||
)
|
)
|
||||||
for i in range(10):
|
|
||||||
self.recentList[i] = self._parseLine(
|
|
||||||
cnfParse, cnfSec, "recent%d" % i,self.CNF_STR, self.recentList[i]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check Certain Values for None
|
# Check Certain Values for None
|
||||||
self.spellLanguage = self._checkNone(self.spellLanguage)
|
self.spellLanguage = self._checkNone(self.spellLanguage)
|
||||||
@@ -471,8 +480,6 @@ class Config:
|
|||||||
cnfSec = "Path"
|
cnfSec = "Path"
|
||||||
cnfParse.add_section(cnfSec)
|
cnfParse.add_section(cnfSec)
|
||||||
cnfParse.set(cnfSec,"lastpath", str(self.lastPath))
|
cnfParse.set(cnfSec,"lastpath", str(self.lastPath))
|
||||||
for i in range(10):
|
|
||||||
cnfParse.set(cnfSec,"recent%d" % i, str(self.recentList[i]))
|
|
||||||
|
|
||||||
# Write config file
|
# Write config file
|
||||||
try:
|
try:
|
||||||
@@ -488,21 +495,86 @@ class Config:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def loadRecentCache(self):
|
||||||
|
"""Load the cache file for recent projects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.dataPath is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
cacheFile = path.join(self.dataPath, nwFiles.RECENT_FILE)
|
||||||
|
self.recentProj = {}
|
||||||
|
|
||||||
|
if path.isfile(cacheFile):
|
||||||
|
try:
|
||||||
|
with open(cacheFile, mode="r", encoding="utf8") as inFile:
|
||||||
|
theJson = inFile.read()
|
||||||
|
theData = json.loads(theJson)
|
||||||
|
|
||||||
|
for projPath in theData.keys():
|
||||||
|
theEntry = theData[projPath]
|
||||||
|
theTitle = ""
|
||||||
|
lastTime = 0
|
||||||
|
wordCount = 0
|
||||||
|
if "title" in theEntry.keys():
|
||||||
|
theTitle = theEntry["title"]
|
||||||
|
if "time" in theEntry.keys():
|
||||||
|
lastTime = int(theEntry["time"])
|
||||||
|
if "words" in theEntry.keys():
|
||||||
|
wordCount = int(theEntry["words"])
|
||||||
|
self.recentProj[projPath] = {
|
||||||
|
"title" : theTitle,
|
||||||
|
"time" : lastTime,
|
||||||
|
"words" : wordCount,
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.hasError = True
|
||||||
|
self.errData.append("Could not load recent project cache")
|
||||||
|
self.errData.append(str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def saveRecentCache(self):
|
||||||
|
"""Save the cache dictionary of recent projects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.dataPath is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
cacheFile = path.join(self.dataPath, nwFiles.RECENT_FILE)
|
||||||
|
cacheTemp = path.join(self.dataPath, nwFiles.RECENT_FILE+"~")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(cacheTemp, mode="w+", encoding="utf8") as outFile:
|
||||||
|
outFile.write(json.dumps(self.recentProj, indent=2))
|
||||||
|
except Exception as e:
|
||||||
|
self.hasError = True
|
||||||
|
self.errData.append("Could not save recent project cache")
|
||||||
|
self.errData.append(str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
if path.isfile(cacheFile):
|
||||||
|
unlink(cacheFile)
|
||||||
|
rename(cacheTemp, cacheFile)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def updateRecentCache(self, projPath, projTitle, wordCount, saveTime):
|
||||||
|
"""Add or update recent cache information o9n a given project.
|
||||||
|
"""
|
||||||
|
self.recentProj[path.abspath(projPath)] = {
|
||||||
|
"title" : projTitle,
|
||||||
|
"time" : int(saveTime),
|
||||||
|
"words" : int(wordCount),
|
||||||
|
}
|
||||||
|
return True
|
||||||
|
|
||||||
##
|
##
|
||||||
# Setters and Getters
|
# Setters and Getters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setRecent(self, recentPath):
|
|
||||||
if recentPath == "": return
|
|
||||||
if recentPath in self.recentList[0:10]:
|
|
||||||
self.recentList.remove(recentPath)
|
|
||||||
self.recentList.insert(0,recentPath)
|
|
||||||
return
|
|
||||||
|
|
||||||
def clearRecent(self):
|
|
||||||
self.recentList = [""]*10
|
|
||||||
return
|
|
||||||
|
|
||||||
def setConfPath(self, newPath):
|
def setConfPath(self, newPath):
|
||||||
if newPath is None:
|
if newPath is None:
|
||||||
return True
|
return True
|
||||||
@@ -513,6 +585,15 @@ class Config:
|
|||||||
self.confFile = path.basename(newPath)
|
self.confFile = path.basename(newPath)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def setDataPath(self, newPath):
|
||||||
|
if newPath is None:
|
||||||
|
return True
|
||||||
|
if not path.isdir(newPath):
|
||||||
|
logger.error("Config: Path not found. Using default data path instead.")
|
||||||
|
return False
|
||||||
|
self.dataPath = path.abspath(newPath)
|
||||||
|
return True
|
||||||
|
|
||||||
def setLastPath(self, lastPath):
|
def setLastPath(self, lastPath):
|
||||||
if lastPath is None or lastPath == "":
|
if lastPath is None or lastPath == "":
|
||||||
self.lastPath = ""
|
self.lastPath = ""
|
||||||
@@ -547,12 +628,12 @@ class Config:
|
|||||||
def setShowRefPanel(self, checkState):
|
def setShowRefPanel(self, checkState):
|
||||||
self.showRefPanel = checkState
|
self.showRefPanel = checkState
|
||||||
self.confChanged = True
|
self.confChanged = True
|
||||||
return
|
return self.showRefPanel
|
||||||
|
|
||||||
def setViewComments(self, checkState):
|
def setViewComments(self, checkState):
|
||||||
self.viewComments = checkState
|
self.viewComments = checkState
|
||||||
self.confChanged = True
|
self.confChanged = True
|
||||||
return
|
return self.viewComments
|
||||||
|
|
||||||
def getErrData(self):
|
def getErrData(self):
|
||||||
errMessage = "<br>".join(self.errData)
|
errMessage = "<br>".join(self.errData)
|
||||||
@@ -596,7 +677,7 @@ class Config:
|
|||||||
if checkVal is None:
|
if checkVal is None:
|
||||||
return None
|
return None
|
||||||
if isinstance(checkVal, str):
|
if isinstance(checkVal, str):
|
||||||
if checkVal.lower == "none":
|
if checkVal.lower() == "none":
|
||||||
return None
|
return None
|
||||||
return checkVal
|
return checkVal
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,13 @@ class nwConst():
|
|||||||
|
|
||||||
class nwFiles():
|
class nwFiles():
|
||||||
|
|
||||||
APP_ICON = "novelWriter.svg"
|
APP_ICON = "novelWriter.svg"
|
||||||
PROJ_FILE = "nwProject.nwx"
|
PROJ_FILE = "nwProject.nwx"
|
||||||
PROJ_DICT = "wordlist.txt"
|
PROJ_DICT = "wordlist.txt"
|
||||||
SESS_INFO = "sessionInfo.log"
|
SESS_INFO = "sessionInfo.log"
|
||||||
INDEX_FILE = "tagsIndex.json"
|
INDEX_FILE = "tagsIndex.json"
|
||||||
OPTS_FILE = "guiOptions.json"
|
OPTS_FILE = "guiOptions.json"
|
||||||
|
RECENT_FILE = "recentProjects.json"
|
||||||
|
|
||||||
# END Class nwFiles
|
# END Class nwFiles
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from nw.gui.dialogs.docsplit import GuiDocSplit
|
|||||||
from nw.gui.dialogs.export import GuiExport
|
from nw.gui.dialogs.export import GuiExport
|
||||||
from nw.gui.dialogs.itemeditor import GuiItemEditor
|
from nw.gui.dialogs.itemeditor import GuiItemEditor
|
||||||
from nw.gui.dialogs.projecteditor import GuiProjectEditor
|
from nw.gui.dialogs.projecteditor import GuiProjectEditor
|
||||||
|
from nw.gui.dialogs.projectload import GuiProjectLoad
|
||||||
from nw.gui.dialogs.sessionlog import GuiSessionLogView
|
from nw.gui.dialogs.sessionlog import GuiSessionLogView
|
||||||
from nw.gui.dialogs.timelineview import GuiTimeLineView
|
from nw.gui.dialogs.timelineview import GuiTimeLineView
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ __all__ = [
|
|||||||
"GuiExport",
|
"GuiExport",
|
||||||
"GuiItemEditor",
|
"GuiItemEditor",
|
||||||
"GuiProjectEditor",
|
"GuiProjectEditor",
|
||||||
|
"GuiProjectLoad",
|
||||||
"GuiSessionLogView",
|
"GuiSessionLogView",
|
||||||
"GuiTimeLineView",
|
"GuiTimeLineView",
|
||||||
"GuiDocDetails",
|
"GuiDocDetails",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from nw.gui.dialogs.docsplit import GuiDocSplit
|
|||||||
from nw.gui.dialogs.export import GuiExport
|
from nw.gui.dialogs.export import GuiExport
|
||||||
from nw.gui.dialogs.itemeditor import GuiItemEditor
|
from nw.gui.dialogs.itemeditor import GuiItemEditor
|
||||||
from nw.gui.dialogs.projecteditor import GuiProjectEditor
|
from nw.gui.dialogs.projecteditor import GuiProjectEditor
|
||||||
|
from nw.gui.dialogs.projectload import GuiProjectLoad
|
||||||
from nw.gui.dialogs.sessionlog import GuiSessionLogView
|
from nw.gui.dialogs.sessionlog import GuiSessionLogView
|
||||||
from nw.gui.dialogs.timelineview import GuiTimeLineView
|
from nw.gui.dialogs.timelineview import GuiTimeLineView
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ __all__ = [
|
|||||||
"GuiExport",
|
"GuiExport",
|
||||||
"GuiItemEditor",
|
"GuiItemEditor",
|
||||||
"GuiProjectEditor",
|
"GuiProjectEditor",
|
||||||
|
"GuiProjectLoad",
|
||||||
"GuiSessionLogView",
|
"GuiSessionLogView",
|
||||||
"GuiTimeLineView",
|
"GuiTimeLineView",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""novelWriter GUI Open Project
|
||||||
|
|
||||||
|
novelWriter – GUI Open Project
|
||||||
|
================================
|
||||||
|
New and open project dialog
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2020-02-26 [0.4.5]
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import nw
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QTreeWidget,
|
||||||
|
QAbstractItemView, QTreeWidgetItem
|
||||||
|
)
|
||||||
|
|
||||||
|
from nw.common import formatInt
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class GuiProjectLoad(QDialog):
|
||||||
|
|
||||||
|
def __init__(self, theParent):
|
||||||
|
QDialog.__init__(self, theParent)
|
||||||
|
|
||||||
|
logger.debug("Initialising GuiProjectLoad ...")
|
||||||
|
|
||||||
|
self.mainConf = nw.CONFIG
|
||||||
|
self.theParent = theParent
|
||||||
|
self.sourceItem = None
|
||||||
|
self.openPath = None
|
||||||
|
|
||||||
|
self.outerBox = QHBoxLayout()
|
||||||
|
self.innerBox = QVBoxLayout()
|
||||||
|
self.setWindowTitle("Open Project")
|
||||||
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
|
self.guiDeco = self.theParent.theTheme.loadDecoration("nwicon", (128, 128))
|
||||||
|
|
||||||
|
self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop)
|
||||||
|
self.outerBox.addLayout(self.innerBox)
|
||||||
|
|
||||||
|
self.projectForm = QGridLayout()
|
||||||
|
self.projectForm.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
self.listBox = QTreeWidget()
|
||||||
|
self.listBox.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||||
|
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
|
||||||
|
self.listBox.setColumnCount(4)
|
||||||
|
self.listBox.setHeaderLabels(["Working Title","Words","Accessed","Path"])
|
||||||
|
self.listBox.setRootIsDecorated(False)
|
||||||
|
|
||||||
|
treeHead = self.listBox.headerItem()
|
||||||
|
treeHead.setTextAlignment(1, Qt.AlignRight)
|
||||||
|
|
||||||
|
self.recentButton = QPushButton("Open")
|
||||||
|
self.recentButton.clicked.connect(self._doOpenRecent)
|
||||||
|
self.browseButton = QPushButton("Browse")
|
||||||
|
self.browseButton.clicked.connect(self._doBrowse)
|
||||||
|
self.closeButton = QPushButton("Close")
|
||||||
|
self.closeButton.clicked.connect(self._doClose)
|
||||||
|
|
||||||
|
self.projectForm.addWidget(self.listBox, 0, 0, 1, 4)
|
||||||
|
self.projectForm.addWidget(self.recentButton, 1, 1)
|
||||||
|
self.projectForm.addWidget(self.browseButton, 1, 2)
|
||||||
|
self.projectForm.addWidget(self.closeButton, 1, 3)
|
||||||
|
self.projectForm.setColumnStretch(0, 1)
|
||||||
|
|
||||||
|
self.innerBox.addLayout(self.projectForm)
|
||||||
|
|
||||||
|
self.rejected.connect(self._doClose)
|
||||||
|
self.setModal(True)
|
||||||
|
self.setMinimumWidth(750)
|
||||||
|
self.setMinimumHeight(450)
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
self._populateList()
|
||||||
|
|
||||||
|
logger.debug("GuiProjectLoad initialisation complete")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Buttons
|
||||||
|
##
|
||||||
|
|
||||||
|
def _doOpenRecent(self):
|
||||||
|
"""Close the dialog window with a recent project selected.
|
||||||
|
"""
|
||||||
|
logger.verbose("GuiProjectLoad open button clicked")
|
||||||
|
|
||||||
|
selItems = self.listBox.selectedItems()
|
||||||
|
if selItems:
|
||||||
|
self.openPath = selItems[0].text(3)
|
||||||
|
self.accept()
|
||||||
|
else:
|
||||||
|
self.openPath = None
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _doBrowse(self):
|
||||||
|
"""Close the dialog window with no selected path, triggering the
|
||||||
|
project browser dialog.
|
||||||
|
"""
|
||||||
|
logger.verbose("GuiProjectLoad browse button clicked")
|
||||||
|
self.openPath = None
|
||||||
|
self.accept()
|
||||||
|
return
|
||||||
|
|
||||||
|
def _doClose(self):
|
||||||
|
"""Close the dialog window without doing anything.
|
||||||
|
"""
|
||||||
|
logger.verbose("GuiProjectLoad close button clicked")
|
||||||
|
self.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _populateList(self):
|
||||||
|
"""Populate the list box with recent project data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
listOrder = []
|
||||||
|
listData = {}
|
||||||
|
for projPath in self.mainConf.recentProj.keys():
|
||||||
|
theEntry = self.mainConf.recentProj[projPath]
|
||||||
|
theTitle = ""
|
||||||
|
theTime = 0
|
||||||
|
theWords = 0
|
||||||
|
if "title" in theEntry.keys():
|
||||||
|
theTitle = theEntry["title"]
|
||||||
|
if "time" in theEntry.keys():
|
||||||
|
theTime = theEntry["time"]
|
||||||
|
if "words" in theEntry.keys():
|
||||||
|
theWords = theEntry["words"]
|
||||||
|
if theTime > 0:
|
||||||
|
listOrder.append(theTime)
|
||||||
|
listData[theTime] = [theTitle, theWords, projPath]
|
||||||
|
|
||||||
|
self.listBox.clear()
|
||||||
|
hasSelection = False
|
||||||
|
for timeStamp in sorted(listOrder, reverse=True):
|
||||||
|
newItem = QTreeWidgetItem([""]*4)
|
||||||
|
newItem.setText(0, listData[timeStamp][0])
|
||||||
|
newItem.setText(1, formatInt(listData[timeStamp][1]))
|
||||||
|
newItem.setText(2, datetime.fromtimestamp(timeStamp).strftime("%x %X"))
|
||||||
|
newItem.setText(3, listData[timeStamp][2])
|
||||||
|
newItem.setTextAlignment(1, Qt.AlignRight)
|
||||||
|
self.listBox.addTopLevelItem(newItem)
|
||||||
|
if not hasSelection:
|
||||||
|
newItem.setSelected(True)
|
||||||
|
hasSelection = True
|
||||||
|
|
||||||
|
self.listBox.resizeColumnToContents(0)
|
||||||
|
self.listBox.resizeColumnToContents(1)
|
||||||
|
self.listBox.resizeColumnToContents(2)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class GuiProjectLoad
|
||||||
+27
-5
@@ -45,10 +45,11 @@ class GuiIcons:
|
|||||||
}
|
}
|
||||||
|
|
||||||
DECO_MAP = {
|
DECO_MAP = {
|
||||||
"export" : "export.svg",
|
"nwicon" : ["icons", "novelWriter.svg"],
|
||||||
"merge" : "merge.svg",
|
"export" : ["graphics", "export.svg"],
|
||||||
"settings" : "gear.svg",
|
"merge" : ["graphics", "merge.svg"],
|
||||||
"split" : "split.svg",
|
"settings" : ["graphics", "gear.svg"],
|
||||||
|
"split" : ["graphics", "split.svg"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, theParent):
|
def __init__(self, theParent):
|
||||||
@@ -67,6 +68,9 @@ class GuiIcons:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def initIcons(self, priPath):
|
def initIcons(self, priPath):
|
||||||
|
"""Load all icons listed in the icon map. Can be overridden by
|
||||||
|
the selected theme.
|
||||||
|
"""
|
||||||
|
|
||||||
self.priPath = priPath
|
self.priPath = priPath
|
||||||
self.secPath = self.mainConf.iconPath
|
self.secPath = self.mainConf.iconPath
|
||||||
@@ -78,12 +82,19 @@ class GuiIcons:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def loadDecoration(self, decoKey, decoSize=None):
|
def loadDecoration(self, decoKey, decoSize=None):
|
||||||
|
"""Load graphical decoration element based on the decoration
|
||||||
|
map. This function always returns a QSwgWidget.
|
||||||
|
"""
|
||||||
|
|
||||||
if decoKey not in self.DECO_MAP:
|
if decoKey not in self.DECO_MAP:
|
||||||
logger.error("Decoration with name '%s' does not exist" % decoKey)
|
logger.error("Decoration with name '%s' does not exist" % decoKey)
|
||||||
return QSvgWidget()
|
return QSvgWidget()
|
||||||
|
|
||||||
svgPath = path.join(self.mainConf.graphPath, self.DECO_MAP[decoKey])
|
svgPath = path.join(
|
||||||
|
self.mainConf.assetPath,
|
||||||
|
self.DECO_MAP[decoKey][0],
|
||||||
|
self.DECO_MAP[decoKey][1]
|
||||||
|
)
|
||||||
if not path.isfile(svgPath):
|
if not path.isfile(svgPath):
|
||||||
logger.error("Decoration file '%s' not in assets folder" % self.DECO_MAP[decoKey])
|
logger.error("Decoration file '%s' not in assets folder" % self.DECO_MAP[decoKey])
|
||||||
return QSvgWidget()
|
return QSvgWidget()
|
||||||
@@ -95,11 +106,17 @@ class GuiIcons:
|
|||||||
return svgDeco
|
return svgDeco
|
||||||
|
|
||||||
def getIcon(self, iconKey, iconSize=None):
|
def getIcon(self, iconKey, iconSize=None):
|
||||||
|
"""Return an icon from the icon buffer. If it doesn't exist,
|
||||||
|
return an empty icon.
|
||||||
|
"""
|
||||||
if iconKey in self.qIcons:
|
if iconKey in self.qIcons:
|
||||||
return self.qIcons[iconKey]
|
return self.qIcons[iconKey]
|
||||||
return QIcon()
|
return QIcon()
|
||||||
|
|
||||||
def getPixmap(self, iconKey, iconSize):
|
def getPixmap(self, iconKey, iconSize):
|
||||||
|
"""Return an icon from the icon buffer as a QPixmap. If it
|
||||||
|
doesn't exist, return an empty QPixmap.
|
||||||
|
"""
|
||||||
if iconKey in self.qIcons:
|
if iconKey in self.qIcons:
|
||||||
return self.qIcons[iconKey].pixmap(iconSize[0], iconSize[1], QIcon.Normal)
|
return self.qIcons[iconKey].pixmap(iconSize[0], iconSize[1], QIcon.Normal)
|
||||||
return QPixmap()
|
return QPixmap()
|
||||||
@@ -109,6 +126,11 @@ class GuiIcons:
|
|||||||
##
|
##
|
||||||
|
|
||||||
def _loadIcon(self, iconKey):
|
def _loadIcon(self, iconKey):
|
||||||
|
"""Load an icon from the assets or theme folder, with a
|
||||||
|
preference for dark/light icons depending on theme type, if such
|
||||||
|
an icon exists. Prefer svg files over png files. Always returns
|
||||||
|
a QIcon.
|
||||||
|
"""
|
||||||
|
|
||||||
if iconKey not in self.ICON_MAP:
|
if iconKey not in self.ICON_MAP:
|
||||||
logger.error("Icon with name '%s' does not exist" % iconKey)
|
logger.error("Icon with name '%s' does not exist" % iconKey)
|
||||||
|
|||||||
+1
-39
@@ -48,11 +48,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def openRecentProject(self, menuItem, recentItem):
|
|
||||||
logger.verbose("User requested opening recent project #%d" % recentItem)
|
|
||||||
self.theParent.openProject(self.mainConf.recentList[recentItem])
|
|
||||||
return True
|
|
||||||
|
|
||||||
def setAvailableRoot(self):
|
def setAvailableRoot(self):
|
||||||
for itemClass in nwItemClass:
|
for itemClass in nwItemClass:
|
||||||
if itemClass == nwItemClass.NO_CLASS: continue
|
if itemClass == nwItemClass.NO_CLASS: continue
|
||||||
@@ -66,30 +61,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Update Menu on Settings Changed
|
# Update Menu on Settings Changed
|
||||||
##
|
##
|
||||||
|
|
||||||
def updateMenu(self):
|
|
||||||
self.updateRecentProjects()
|
|
||||||
return
|
|
||||||
|
|
||||||
def updateRecentProjects(self):
|
|
||||||
|
|
||||||
self.recentMenu.clear()
|
|
||||||
for n in range(len(self.mainConf.recentList)):
|
|
||||||
recentProject = self.mainConf.recentList[n]
|
|
||||||
if recentProject == "": continue
|
|
||||||
menuItem = QAction("%s" % recentProject, self.projMenu)
|
|
||||||
menuItem.triggered.connect(
|
|
||||||
lambda a1=menuItem, a2=n : self.openRecentProject(a1, a2)
|
|
||||||
)
|
|
||||||
self.recentMenu.addAction(menuItem)
|
|
||||||
|
|
||||||
self.recentMenu.addSeparator()
|
|
||||||
menuItem = QAction("Clear Recent Projects", self)
|
|
||||||
menuItem.setStatusTip("Clear the list of recent projects")
|
|
||||||
menuItem.triggered.connect(self._clearRecentProjects)
|
|
||||||
self.recentMenu.addAction(menuItem)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def setSpellCheck(self, theMode):
|
def setSpellCheck(self, theMode):
|
||||||
"""Set the spell check check box to theMode. This is controlled
|
"""Set the spell check check box to theMode. This is controlled
|
||||||
by the document editor class, which holds the master spell check
|
by the document editor class, which holds the master spell check
|
||||||
@@ -166,11 +137,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
QDesktopServices.openUrl(QUrl(nw.__docurl__))
|
QDesktopServices.openUrl(QUrl(nw.__docurl__))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _clearRecentProjects(self):
|
|
||||||
self.mainConf.clearRecent()
|
|
||||||
self.updateRecentProjects()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _showDocumentLocation(self):
|
def _showDocumentLocation(self):
|
||||||
self.theParent.docEditor.revealLocation()
|
self.theParent.docEditor.revealLocation()
|
||||||
return True
|
return True
|
||||||
@@ -194,7 +160,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self.aOpenProject = QAction("Open Project", self)
|
self.aOpenProject = QAction("Open Project", self)
|
||||||
self.aOpenProject.setStatusTip("Open project")
|
self.aOpenProject.setStatusTip("Open project")
|
||||||
self.aOpenProject.setShortcut("Ctrl+Shift+O")
|
self.aOpenProject.setShortcut("Ctrl+Shift+O")
|
||||||
self.aOpenProject.triggered.connect(lambda : self.theParent.openProject(None))
|
self.aOpenProject.triggered.connect(self.theParent.manageProjects)
|
||||||
self.projMenu.addAction(self.aOpenProject)
|
self.projMenu.addAction(self.aOpenProject)
|
||||||
|
|
||||||
# Project > Save Project
|
# Project > Save Project
|
||||||
@@ -211,10 +177,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self.aCloseProject.triggered.connect(lambda : self.theParent.closeProject(False))
|
self.aCloseProject.triggered.connect(lambda : self.theParent.closeProject(False))
|
||||||
self.projMenu.addAction(self.aCloseProject)
|
self.projMenu.addAction(self.aCloseProject)
|
||||||
|
|
||||||
# Project > Recent Projects
|
|
||||||
self.recentMenu = self.projMenu.addMenu("Recent Projects")
|
|
||||||
self.updateRecentProjects()
|
|
||||||
|
|
||||||
# Project > Project Settings
|
# Project > Project Settings
|
||||||
self.aProjectSettings = QAction("Project Settings", self)
|
self.aProjectSettings = QAction("Project Settings", self)
|
||||||
self.aProjectSettings.setStatusTip("Project settings")
|
self.aProjectSettings.setStatusTip("Project settings")
|
||||||
|
|||||||
+26
-9
@@ -27,7 +27,7 @@ from nw.gui import (
|
|||||||
GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor, GuiExport,
|
GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor, GuiExport,
|
||||||
GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar, GuiDocViewDetails,
|
GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar, GuiDocViewDetails,
|
||||||
GuiConfigEditor, GuiProjectEditor, GuiItemEditor, GuiTimeLineView,
|
GuiConfigEditor, GuiProjectEditor, GuiItemEditor, GuiTimeLineView,
|
||||||
GuiSessionLogView, GuiDocMerge, GuiDocSplit
|
GuiSessionLogView, GuiDocMerge, GuiDocSplit, GuiProjectLoad
|
||||||
)
|
)
|
||||||
from nw.project import NWProject, NWDoc, NWItem, NWIndex, NWBackup
|
from nw.project import NWProject, NWDoc, NWItem, NWIndex, NWBackup
|
||||||
from nw.tools import countWords
|
from nw.tools import countWords
|
||||||
@@ -64,7 +64,7 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
self.resize(*self.mainConf.winGeometry)
|
self.resize(*self.mainConf.winGeometry)
|
||||||
self._setWindowTitle()
|
self._setWindowTitle()
|
||||||
self.setWindowIcon(QIcon(path.join(self.mainConf.appIcon)))
|
self.setWindowIcon(QIcon(self.mainConf.appIcon))
|
||||||
|
|
||||||
# Main GUI Elements
|
# Main GUI Elements
|
||||||
self.statusBar = GuiMainStatus(self)
|
self.statusBar = GuiMainStatus(self)
|
||||||
@@ -183,6 +183,8 @@ class GuiMain(QMainWindow):
|
|||||||
if self.mainConf.cmdOpen is not None:
|
if self.mainConf.cmdOpen is not None:
|
||||||
logger.debug("Opening project from additional command line option")
|
logger.debug("Opening project from additional command line option")
|
||||||
self.openProject(self.mainConf.cmdOpen)
|
self.openProject(self.mainConf.cmdOpen)
|
||||||
|
else:
|
||||||
|
self.manageProjects()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -204,13 +206,30 @@ class GuiMain(QMainWindow):
|
|||||||
# Project Actions
|
# Project Actions
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def manageProjects(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.
|
||||||
|
"""
|
||||||
|
if not self.mainConf.showGUI:
|
||||||
|
return False
|
||||||
|
|
||||||
|
dlgProj = GuiProjectLoad(self)
|
||||||
|
dlgProj.exec_()
|
||||||
|
if dlgProj.result() == QDialog.Accepted:
|
||||||
|
self.openProject(dlgProj.openPath)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def newProject(self, projPath=None, forceNew=False):
|
def newProject(self, projPath=None, forceNew=False):
|
||||||
|
"""Create new project with a few default files and folders.
|
||||||
|
"""
|
||||||
|
|
||||||
if self.hasProject:
|
if self.hasProject:
|
||||||
msgBox = QMessageBox()
|
msgBox = QMessageBox()
|
||||||
msgRes = msgBox.warning(
|
msgRes = msgBox.warning(
|
||||||
self, "New Project",
|
self, "New Project",
|
||||||
"Please close the current project<br>before making a new one."
|
"Please close the current project before making a new one."
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -223,7 +242,7 @@ class GuiMain(QMainWindow):
|
|||||||
msgBox = QMessageBox()
|
msgBox = QMessageBox()
|
||||||
msgRes = msgBox.critical(
|
msgRes = msgBox.critical(
|
||||||
self, "New Project",
|
self, "New Project",
|
||||||
"A project already exists in that location.<br>Please choose another folder."
|
"A project already exists in that location. Please choose another folder."
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -238,9 +257,9 @@ class GuiMain(QMainWindow):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def closeProject(self, isYes=False):
|
def closeProject(self, isYes=False):
|
||||||
"""Closes the project if one is open.
|
"""Closes the project if one is open. isYes is passed on from
|
||||||
isYes is passed on from the close application event so the user
|
the close application event so the user doesn't get prompted
|
||||||
doesn't get prompted twice.
|
twice.
|
||||||
"""
|
"""
|
||||||
if not self.hasProject:
|
if not self.hasProject:
|
||||||
# There is no project loaded, everything OK
|
# There is no project loaded, everything OK
|
||||||
@@ -314,7 +333,6 @@ class GuiMain(QMainWindow):
|
|||||||
self.docEditor.setDictionaries()
|
self.docEditor.setDictionaries()
|
||||||
self.docEditor.setSpellCheck(self.theProject.spellCheck)
|
self.docEditor.setSpellCheck(self.theProject.spellCheck)
|
||||||
self.statusBar.setRefTime(self.theProject.projOpened)
|
self.statusBar.setRefTime(self.theProject.projOpened)
|
||||||
self.mainMenu.updateMenu()
|
|
||||||
|
|
||||||
# Restore previously open documents, if any
|
# Restore previously open documents, if any
|
||||||
if self.theProject.lastEdited is not None:
|
if self.theProject.lastEdited is not None:
|
||||||
@@ -344,7 +362,6 @@ class GuiMain(QMainWindow):
|
|||||||
self.treeView.saveTreeOrder()
|
self.treeView.saveTreeOrder()
|
||||||
self.theProject.saveProject()
|
self.theProject.saveProject()
|
||||||
self.theIndex.saveIndex()
|
self.theIndex.saveIndex()
|
||||||
self.mainMenu.updateRecentProjects()
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
+13
-3
@@ -297,7 +297,11 @@ class NWProject():
|
|||||||
self._appendItem(tHandle,pHandle,nwItem)
|
self._appendItem(tHandle,pHandle,nwItem)
|
||||||
|
|
||||||
self.optState.loadSettings()
|
self.optState.loadSettings()
|
||||||
self.mainConf.setRecent(self.projPath)
|
|
||||||
|
# Update recent projects
|
||||||
|
self.mainConf.updateRecentCache(self.projPath, self.projName, self.lastWCount, time())
|
||||||
|
self.mainConf.saveRecentCache()
|
||||||
|
|
||||||
self.theParent.setStatus("Opened Project: %s" % self.projName)
|
self.theParent.setStatus("Opened Project: %s" % self.projName)
|
||||||
|
|
||||||
self._scanProjectFolder()
|
self._scanProjectFolder()
|
||||||
@@ -319,6 +323,7 @@ class NWProject():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
self.projMeta = path.join(self.projPath,"meta")
|
self.projMeta = path.join(self.projPath,"meta")
|
||||||
|
saveTime = time()
|
||||||
|
|
||||||
if not self._checkFolder(self.projPath): return
|
if not self._checkFolder(self.projPath): return
|
||||||
if not self._checkFolder(self.projMeta): return
|
if not self._checkFolder(self.projMeta): return
|
||||||
@@ -330,7 +335,7 @@ class NWProject():
|
|||||||
nwXML = etree.Element("novelWriterXML",attrib={
|
nwXML = etree.Element("novelWriterXML",attrib={
|
||||||
"appVersion" : str(nw.__version__),
|
"appVersion" : str(nw.__version__),
|
||||||
"fileVersion" : "1.0",
|
"fileVersion" : "1.0",
|
||||||
"timeStamp" : datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
"timeStamp" : datetime.fromtimestamp(saveTime).strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
})
|
})
|
||||||
|
|
||||||
# Save Project Meta
|
# Save Project Meta
|
||||||
@@ -386,8 +391,13 @@ class NWProject():
|
|||||||
rename(saveFile, backFile)
|
rename(saveFile, backFile)
|
||||||
rename(tempFile, saveFile)
|
rename(tempFile, saveFile)
|
||||||
|
|
||||||
|
# Save project GUI options
|
||||||
self.optState.saveSettings()
|
self.optState.saveSettings()
|
||||||
self.mainConf.setRecent(self.projPath)
|
|
||||||
|
# Update recent projects
|
||||||
|
self.mainConf.updateRecentCache(self.projPath, self.projName, self.currWCount, saveTime)
|
||||||
|
self.mainConf.saveRecentCache()
|
||||||
|
|
||||||
self.theParent.setStatus("Saved Project: %s" % self.projName)
|
self.theParent.setStatus("Saved Project: %s" % self.projName)
|
||||||
self.setProjectChanged(False)
|
self.setProjectChanged(False)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[Main]
|
[Main]
|
||||||
timestamp = 2019-11-19 21:49:29
|
timestamp = 2020-02-26 18:10:35
|
||||||
theme = default
|
theme = default
|
||||||
syntax = default_light
|
syntax = default_light
|
||||||
guidark = False
|
guidark = False
|
||||||
@@ -49,14 +49,4 @@ viewcomments = True
|
|||||||
|
|
||||||
[Path]
|
[Path]
|
||||||
lastpath =
|
lastpath =
|
||||||
recent0 =
|
|
||||||
recent1 =
|
|
||||||
recent2 =
|
|
||||||
recent3 =
|
|
||||||
recent4 =
|
|
||||||
recent5 =
|
|
||||||
recent6 =
|
|
||||||
recent7 =
|
|
||||||
recent8 =
|
|
||||||
recent9 =
|
|
||||||
|
|
||||||
|
|||||||
+38
-2
@@ -13,7 +13,7 @@ theConf = Config()
|
|||||||
def testConfigInit(nwTemp,nwRef):
|
def testConfigInit(nwTemp,nwRef):
|
||||||
tmpConf = path.join(nwTemp,"novelwriter.conf")
|
tmpConf = path.join(nwTemp,"novelwriter.conf")
|
||||||
refConf = path.join(nwRef, "novelwriter.conf")
|
refConf = path.join(nwRef, "novelwriter.conf")
|
||||||
assert theConf.initConfig(nwTemp)
|
assert theConf.initConfig(nwTemp, nwTemp)
|
||||||
assert theConf.setLastPath("")
|
assert theConf.setLastPath("")
|
||||||
assert theConf.saveConfig()
|
assert theConf.saveConfig()
|
||||||
assert cmpFiles(tmpConf, refConf, [2])
|
assert cmpFiles(tmpConf, refConf, [2])
|
||||||
@@ -37,6 +37,14 @@ def testConfigSetConfPath(nwTemp):
|
|||||||
assert theConf.confFile == "novelwriter.conf"
|
assert theConf.confFile == "novelwriter.conf"
|
||||||
assert not theConf.confChanged
|
assert not theConf.confChanged
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testConfigSetDataPath(nwTemp):
|
||||||
|
assert theConf.setDataPath(None)
|
||||||
|
assert not theConf.setDataPath(path.join("somewhere","over","the","rainbow"))
|
||||||
|
assert theConf.setDataPath(nwTemp)
|
||||||
|
assert theConf.dataPath == nwTemp
|
||||||
|
assert not theConf.confChanged
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testConfigLoad():
|
def testConfigLoad():
|
||||||
assert theConf.loadConfig()
|
assert theConf.loadConfig()
|
||||||
@@ -67,7 +75,7 @@ def testConfigSetTreeColWidths(nwTemp,nwRef):
|
|||||||
assert not theConf.confChanged
|
assert not theConf.confChanged
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testConfigSetMainPanePos(nwTemp,nwRef):
|
def testConfigSetPanePos(nwTemp,nwRef):
|
||||||
tmpConf = path.join(nwTemp,"novelwriter.conf")
|
tmpConf = path.join(nwTemp,"novelwriter.conf")
|
||||||
refConf = path.join(nwRef, "novelwriter.conf")
|
refConf = path.join(nwRef, "novelwriter.conf")
|
||||||
assert theConf.setMainPanePos([0, 0])
|
assert theConf.setMainPanePos([0, 0])
|
||||||
@@ -77,3 +85,31 @@ def testConfigSetMainPanePos(nwTemp,nwRef):
|
|||||||
assert theConf.saveConfig()
|
assert theConf.saveConfig()
|
||||||
assert cmpFiles(tmpConf, refConf, [2])
|
assert cmpFiles(tmpConf, refConf, [2])
|
||||||
assert not theConf.confChanged
|
assert not theConf.confChanged
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testConfigFlags(nwTemp,nwRef):
|
||||||
|
tmpConf = path.join(nwTemp,"novelwriter.conf")
|
||||||
|
refConf = path.join(nwRef, "novelwriter.conf")
|
||||||
|
assert not theConf.setShowRefPanel(False)
|
||||||
|
assert theConf.setShowRefPanel(True)
|
||||||
|
assert not theConf.setViewComments(False)
|
||||||
|
assert theConf.setViewComments(True)
|
||||||
|
assert theConf.confChanged
|
||||||
|
assert theConf.saveConfig()
|
||||||
|
assert cmpFiles(tmpConf, refConf, [2])
|
||||||
|
assert not theConf.confChanged
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testConfigErrors(nwTemp):
|
||||||
|
nonPath = path.join("somewhere","over","the","rainbow")
|
||||||
|
assert theConf.initConfig(nonPath, nonPath)
|
||||||
|
assert theConf.hasError
|
||||||
|
assert not theConf.loadConfig()
|
||||||
|
assert not theConf.saveConfig()
|
||||||
|
assert not theConf.loadRecentCache()
|
||||||
|
assert len(theConf.getErrData()) > 0
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testConfigInternals():
|
||||||
|
assert theConf._checkNone(None) is None
|
||||||
|
assert theConf._checkNone("None") is None
|
||||||
|
|||||||
+8
-8
@@ -18,8 +18,8 @@ keyDelay = 10
|
|||||||
stepDelay = 50
|
stepDelay = 50
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testMainWindows(qtbot, nwTempGUI, nwRef):
|
def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp):
|
||||||
nwGUI = nw.main(["--testmode","--config=%s" % nwTempGUI])
|
nwGUI = nw.main(["--testmode","--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
|
||||||
qtbot.addWidget(nwGUI)
|
qtbot.addWidget(nwGUI)
|
||||||
nwGUI.show()
|
nwGUI.show()
|
||||||
qtbot.waitForWindowShown(nwGUI)
|
qtbot.waitForWindowShown(nwGUI)
|
||||||
@@ -254,8 +254,8 @@ def testMainWindows(qtbot, nwTempGUI, nwRef):
|
|||||||
# qtbot.stopForInteraction()
|
# qtbot.stopForInteraction()
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testTimeLineView(qtbot, nwTempGUI, nwRef):
|
def testTimeLineView(qtbot, nwTempGUI, nwRef, nwTemp):
|
||||||
nwGUI = nw.main(["--testmode","--config=%s" % nwTempGUI])
|
nwGUI = nw.main(["--testmode","--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
|
||||||
qtbot.addWidget(nwGUI)
|
qtbot.addWidget(nwGUI)
|
||||||
nwGUI.show()
|
nwGUI.show()
|
||||||
qtbot.waitForWindowShown(nwGUI)
|
qtbot.waitForWindowShown(nwGUI)
|
||||||
@@ -276,8 +276,8 @@ def testTimeLineView(qtbot, nwTempGUI, nwRef):
|
|||||||
nwGUI.closeMain()
|
nwGUI.closeMain()
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testProjectEditor(qtbot, nwTempGUI, nwRef):
|
def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
|
||||||
nwGUI = nw.main(["--testmode","--config=%s" % nwTempGUI])
|
nwGUI = nw.main(["--testmode","--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
|
||||||
qtbot.addWidget(nwGUI)
|
qtbot.addWidget(nwGUI)
|
||||||
nwGUI.show()
|
nwGUI.show()
|
||||||
qtbot.waitForWindowShown(nwGUI)
|
qtbot.waitForWindowShown(nwGUI)
|
||||||
@@ -363,8 +363,8 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef):
|
|||||||
# qtbot.stopForInteraction()
|
# qtbot.stopForInteraction()
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testItemEditor(qtbot, nwTempGUI, nwRef):
|
def testItemEditor(qtbot, nwTempGUI, nwRef, nwTemp):
|
||||||
nwGUI = nw.main(["--testmode","--config=%s" % nwTempGUI])
|
nwGUI = nw.main(["--testmode","--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
|
||||||
qtbot.addWidget(nwGUI)
|
qtbot.addWidget(nwGUI)
|
||||||
nwGUI.show()
|
nwGUI.show()
|
||||||
qtbot.waitForWindowShown(nwGUI)
|
qtbot.waitForWindowShown(nwGUI)
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ theProject = NWProject(theMain)
|
|||||||
theProject.handleSeed = 42
|
theProject.handleSeed = 42
|
||||||
|
|
||||||
@pytest.mark.project
|
@pytest.mark.project
|
||||||
def testProjectNew(nwTempProj,nwRef):
|
def testProjectNew(nwTempProj,nwRef,nwTemp):
|
||||||
projFile = path.join(nwTempProj,"nwProject.nwx")
|
projFile = path.join(nwTempProj,"nwProject.nwx")
|
||||||
refFile = path.join(nwRef,"proj","1_nwProject.nwx")
|
refFile = path.join(nwRef,"proj","1_nwProject.nwx")
|
||||||
assert theConf.initConfig(nwRef)
|
assert theConf.initConfig(nwRef, nwTemp)
|
||||||
assert theProject.newProject()
|
assert theProject.newProject()
|
||||||
assert theProject.setProjectPath(nwTempProj)
|
assert theProject.setProjectPath(nwTempProj)
|
||||||
assert theProject.saveProject()
|
assert theProject.saveProject()
|
||||||
|
|||||||
Reference in New Issue
Block a user