Merge branch 'main' into new_project_wizard
This commit is contained in:
+66
-23
@@ -34,19 +34,21 @@ from os import path, remove, rename
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QApplication, QErrorMessage
|
||||
|
||||
from nw.error import exceptionHandler
|
||||
from nw.config import Config
|
||||
|
||||
__package__ = "novelWriter"
|
||||
__package__ = "nw"
|
||||
__author__ = "Veronica Berglyd Olsen"
|
||||
__copyright__ = "Copyright 2018–2020, Veronica Berglyd Olsen"
|
||||
__license__ = "GPLv3"
|
||||
__version__ = "0.10.1"
|
||||
__hexversion__ = "0x001001f0"
|
||||
__date__ = "2020-07-11"
|
||||
__version__ = "0.11.0"
|
||||
__hexversion__ = "0x001100f0"
|
||||
__date__ = "2020-08-08"
|
||||
__maintainer__ = "Veronica Berglyd Olsen"
|
||||
__email__ = "code@vkbo.net"
|
||||
__status__ = "Pre-Release"
|
||||
__url__ = "https://github.com/vkbo/novelWriter"
|
||||
__status__ = "Beta"
|
||||
__url__ = "https://novelwriter.io"
|
||||
__sourceurl__ = "https://github.com/vkbo/novelWriter"
|
||||
__issuesurl__ = "https://github.com/vkbo/novelWriter/issues"
|
||||
__domain__ = "novelwriter.io"
|
||||
__docurl__ = "https://novelwriter.readthedocs.io"
|
||||
@@ -90,7 +92,6 @@ CONFIG = Config()
|
||||
def main(sysArgs=None):
|
||||
"""Parses command line, sets up logging, and launches main GUI.
|
||||
"""
|
||||
|
||||
if sysArgs is None:
|
||||
sysArgs = sys.argv[1:]
|
||||
|
||||
@@ -111,7 +112,7 @@ def main(sysArgs=None):
|
||||
]
|
||||
|
||||
helpMsg = (
|
||||
"{appname} {version} ({status} {date})\n"
|
||||
"novelWriter {version} ({status} {date})\n"
|
||||
"{copyright}\n"
|
||||
"\n"
|
||||
"This program is distributed in the hope that it will be useful,\n"
|
||||
@@ -132,7 +133,6 @@ def main(sysArgs=None):
|
||||
" --data= Alternative user data path.\n"
|
||||
" --testmode Do not display GUI. Used by the test suite.\n"
|
||||
).format(
|
||||
appname = __package__,
|
||||
version = __version__,
|
||||
status = __status__,
|
||||
copyright = __copyright__,
|
||||
@@ -167,7 +167,9 @@ def main(sysArgs=None):
|
||||
print(helpMsg)
|
||||
sys.exit()
|
||||
elif inOpt in ("-v", "--version"):
|
||||
print("%s %s Version %s [%s]" % (__package__,__status__,__version__,__date__))
|
||||
print("novelWriter %s Version %s [%s]" % (
|
||||
__status__, __version__, __date__)
|
||||
)
|
||||
sys.exit()
|
||||
elif inOpt == "--info":
|
||||
debugLevel = logging.INFO
|
||||
@@ -197,7 +199,7 @@ def main(sysArgs=None):
|
||||
CONFIG.cmdOpen = cmdOpen
|
||||
|
||||
# Set Logging
|
||||
logFmt = logging.Formatter(fmt=logFormat, datefmt="%Y-%m-%d %H:%M:%S", style="{")
|
||||
logFmt = logging.Formatter(fmt=logFormat, style="{")
|
||||
|
||||
if not logFile == "" and toFile:
|
||||
if path.isfile(logFile+".bak"):
|
||||
@@ -217,8 +219,8 @@ def main(sysArgs=None):
|
||||
logger.addHandler(cHandle)
|
||||
|
||||
logger.setLevel(debugLevel)
|
||||
logger.info("Starting %s %s (%s) %s" % (
|
||||
__package__, __version__, __hexversion__, __date__
|
||||
logger.info("Starting novelWriter %s (%s) %s" % (
|
||||
__version__, __hexversion__, __date__
|
||||
))
|
||||
|
||||
# Check Packages and Versions
|
||||
@@ -249,13 +251,14 @@ def main(sysArgs=None):
|
||||
if errorData:
|
||||
errApp = QApplication([])
|
||||
errMsg = QErrorMessage()
|
||||
errMsg.setMinimumWidth(500)
|
||||
errMsg.setMinimumHeight(300)
|
||||
errMsg.resize(500, 300)
|
||||
errMsg.showMessage((
|
||||
"ERROR: %s cannot start due to the following issues:<br><br>"
|
||||
" - %s<br><br>Exiting."
|
||||
"<h3>A critical error has been encountered</h3>"
|
||||
"<p>novelWriter cannot start due to the following issues:<p>"
|
||||
"<p> - %s</p>"
|
||||
"<p>Shutting down ...</p>"
|
||||
) % (
|
||||
__package__, "<br> - ".join(errorData)
|
||||
"<br> - ".join(errorData)
|
||||
))
|
||||
errApp.exec_()
|
||||
sys.exit(1)
|
||||
@@ -268,13 +271,53 @@ def main(sysArgs=None):
|
||||
if testMode:
|
||||
nwGUI = GuiMain()
|
||||
return nwGUI
|
||||
|
||||
else:
|
||||
nwApp = QApplication([__package__,("-style=%s" % qtStyle)])
|
||||
nwApp.setApplicationName(__package__)
|
||||
nwApp = QApplication([CONFIG.appName, ("-style=%s" % qtStyle)])
|
||||
nwApp.setApplicationName(CONFIG.appName)
|
||||
nwApp.setApplicationVersion(__version__)
|
||||
nwApp.setWindowIcon(QIcon(CONFIG.appIcon))
|
||||
nwApp.setOrganizationDomain("novelwriter.io")
|
||||
nwGUI = GuiMain()
|
||||
sys.exit(nwApp.exec_())
|
||||
nwApp.setOrganizationDomain(__domain__)
|
||||
|
||||
# We try to catch critical errors while setting up the main GUI
|
||||
# by wrapping the main GUI in a try/except structure. This will
|
||||
# not catch all exceptions for other parts of the application.
|
||||
# For all other unhandled exceptions, we use a custom exception
|
||||
# handler that pops a dialog box with the error message.
|
||||
sys.excepthook = exceptionHandler
|
||||
|
||||
try:
|
||||
nwGUI = GuiMain()
|
||||
sys.exit(nwApp.exec_())
|
||||
|
||||
except Exception:
|
||||
|
||||
from traceback import print_tb
|
||||
from nw.error import formatHtmlErrMsg
|
||||
|
||||
exType, exValue, exTrace = sys.exc_info()
|
||||
|
||||
logger.critical("%s: %s" % (exType.__name__, str(exValue)))
|
||||
print_tb(exTrace)
|
||||
|
||||
try:
|
||||
del nwApp
|
||||
|
||||
errApp = QApplication([])
|
||||
errMsg = QErrorMessage()
|
||||
errMsg.setWindowTitle("Critical Error")
|
||||
errMsg.resize(800, 400)
|
||||
errMsg.showMessage((
|
||||
"<h3>A critical error has been encountered</h3>"
|
||||
"%s"
|
||||
"<p>Shutting down ...</p>"
|
||||
) % formatHtmlErrMsg(exType, exValue, exTrace))
|
||||
errApp.exec_()
|
||||
|
||||
except Exception as e:
|
||||
logger.critical("Could not create error message dialog.")
|
||||
logger.critical(str(e))
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
return
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
[Main]
|
||||
name = Typicons Colour Dark
|
||||
description = Coulorised icons for dark GUI theme based on Typicons.
|
||||
author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation)
|
||||
credit = Stephen Hutchings
|
||||
author = Veronica Berglyd Olsen (adaptation)
|
||||
credit = Stephen Hutchings (icon design)
|
||||
url = https://github.com/stephenhutchings/typicons.font
|
||||
license = CC BY-SA 4.0
|
||||
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
[Main]
|
||||
name = Typicons Colour Light
|
||||
description = Coulorised icons for light GUI theme based on Typicons.
|
||||
author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation)
|
||||
credit = Stephen Hutchings
|
||||
author = Veronica Berglyd Olsen (adaptation)
|
||||
credit = Stephen Hutchings (icon design)
|
||||
url = https://github.com/stephenhutchings/typicons.font
|
||||
license = CC BY-SA 4.0
|
||||
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
[Main]
|
||||
name = Typicons Grey Dark
|
||||
description = Greyscaled icons for dark GUI theme based on Typicons.
|
||||
author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation)
|
||||
credit = Stephen Hutchings
|
||||
author = Veronica Berglyd Olsen (adaptation)
|
||||
credit = Stephen Hutchings (icon design)
|
||||
url = https://github.com/stephenhutchings/typicons.font
|
||||
license = CC BY-SA 4.0
|
||||
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
[Main]
|
||||
name = Typicons Grey Light
|
||||
description = Greyscaled icons for light GUI theme based on Typicons.
|
||||
author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation)
|
||||
credit = Stephen Hutchings
|
||||
author = Veronica Berglyd Olsen (adaptation)
|
||||
credit = Stephen Hutchings (icon design)
|
||||
url = https://github.com/stephenhutchings/typicons.font
|
||||
license = CC BY-SA 4.0
|
||||
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
+24
-7
@@ -33,6 +33,7 @@ import nw
|
||||
|
||||
from os import path, mkdir, unlink, rename
|
||||
from time import time
|
||||
from shutil import which
|
||||
|
||||
from PyQt5.Qt import PYQT_VERSION_STR
|
||||
from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo
|
||||
@@ -52,8 +53,8 @@ class Config:
|
||||
def __init__(self):
|
||||
|
||||
# Set Application Variables
|
||||
self.appName = nw.__package__
|
||||
self.appHandle = nw.__package__.lower()
|
||||
self.appName = "novelWriter"
|
||||
self.appHandle = self.appName.lower()
|
||||
self.showGUI = True
|
||||
self.debugInfo = False
|
||||
self.cmdOpen = None
|
||||
@@ -76,9 +77,11 @@ class Config:
|
||||
self.graphPath = None
|
||||
self.dictPath = None
|
||||
self.iconPath = None
|
||||
self.helpPath = None
|
||||
|
||||
# Set default values
|
||||
# Runtime Settings and Variables
|
||||
self.confChanged = False
|
||||
self.hasHelp = False
|
||||
|
||||
## General
|
||||
self.guiTheme = "default"
|
||||
@@ -200,8 +203,8 @@ class Config:
|
||||
self.kernelVer = "Unknown"
|
||||
|
||||
# Packages
|
||||
self.hasEnchant = False
|
||||
self.hasSymSpell = False
|
||||
self.hasEnchant = False # The pyenchant package
|
||||
self.hasAssistant = False # The Qt Assistant executable
|
||||
|
||||
# Recent Cache
|
||||
self.recentProj = {}
|
||||
@@ -315,6 +318,11 @@ class Config:
|
||||
if self.spellLanguage is None:
|
||||
self.spellLanguage = "en"
|
||||
|
||||
# Check if local help files exist
|
||||
self.helpPath = path.join(self.assetPath, "help", "novelWriter.qhc")
|
||||
self.hasHelp = path.isfile(self.helpPath)
|
||||
self.hasHelp &= path.isfile(path.join(self.assetPath, "help", "novelWriter.qch"))
|
||||
|
||||
logger.debug("Config initialisation complete")
|
||||
|
||||
return True
|
||||
@@ -888,10 +896,19 @@ class Config:
|
||||
try:
|
||||
import enchant
|
||||
self.hasEnchant = True
|
||||
logger.debug("Checking package pyenchant: Ok")
|
||||
logger.debug("Checking package 'pyenchant': Ok")
|
||||
except:
|
||||
self.hasEnchant = False
|
||||
logger.debug("Checking package pyenchant: Missing")
|
||||
logger.debug("Checking package 'pyenchant': Missing")
|
||||
|
||||
try:
|
||||
self.hasAssistant = which("assistant")
|
||||
except:
|
||||
self.hasAssistant = False
|
||||
if self.hasAssistant:
|
||||
logger.debug("Checking executable 'assistant': Ok")
|
||||
else:
|
||||
logger.debug("Checking executable 'assistant': Missing")
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from nw.constants.iso import isoLanguage, isoCountry
|
||||
from nw.constants.constants import (
|
||||
nwConst, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes, nwUnicode,
|
||||
nwInsertSymbols
|
||||
nwConst, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes, nwUnicode
|
||||
)
|
||||
from nw.constants.enum import (
|
||||
nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline,
|
||||
@@ -19,7 +18,6 @@ __all__ = [
|
||||
"nwLabels",
|
||||
"nwQuotes",
|
||||
"nwUnicode",
|
||||
"nwInsertSymbols",
|
||||
"nwAlert",
|
||||
"nwDocAction",
|
||||
"nwItemClass",
|
||||
|
||||
@@ -299,18 +299,3 @@ class nwUnicode:
|
||||
H_LTRIS = "◂"
|
||||
|
||||
# END Class nwUnicode
|
||||
|
||||
class nwInsertSymbols():
|
||||
|
||||
SYMBOLS = {
|
||||
nwDocInsert.NO_INSERT : "",
|
||||
nwDocInsert.HARD_BREAK : " \n",
|
||||
nwDocInsert.NB_SPACE : nwUnicode.U_NBSP,
|
||||
nwDocInsert.THIN_SPACE : nwUnicode.U_THNSP,
|
||||
nwDocInsert.THIN_NB_SPACE : nwUnicode.U_THNBSP,
|
||||
nwDocInsert.SHORT_DASH : nwUnicode.U_ENDASH,
|
||||
nwDocInsert.LONG_DASH : nwUnicode.U_EMDASH,
|
||||
nwDocInsert.ELLIPSIS : nwUnicode.U_HELLIP,
|
||||
}
|
||||
|
||||
# END Enum nwDocInsert
|
||||
|
||||
@@ -107,6 +107,10 @@ class nwDocInsert(Enum):
|
||||
SHORT_DASH = 5
|
||||
LONG_DASH = 6
|
||||
ELLIPSIS = 7
|
||||
QUOTE_LS = 8
|
||||
QUOTE_RS = 9
|
||||
QUOTE_LD = 10
|
||||
QUOTE_RD = 11
|
||||
|
||||
# END Enum nwDocInsert
|
||||
|
||||
|
||||
+12
-14
@@ -370,23 +370,21 @@ class NWProject():
|
||||
if fileVersion == "1.0":
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.question(self.theParent, "Old Project Version", (
|
||||
"The project file and data is created by a %s version lower than 0.7. "
|
||||
"Do you want to upgrade the project to the most recent format?<br><br>"
|
||||
"Note that after the upgrade, you cannot open the project with an older "
|
||||
"version of %s any more, so make sure you have a recent backup."
|
||||
) % (
|
||||
nw.__package__, nw.__package__
|
||||
"The project file and data is created by a novelWriter version "
|
||||
"lower than 0.7. Do you want to upgrade the project to the "
|
||||
"most recent format?<br><br>Note that after the upgrade, you "
|
||||
"cannot open the project with an older version of novelWriter "
|
||||
"any more, so make sure you have a recent backup."
|
||||
))
|
||||
if msgRes != QMessageBox.Yes:
|
||||
return False
|
||||
|
||||
elif fileVersion != "1.1" and fileVersion != "1.2":
|
||||
self.makeAlert((
|
||||
"Unknown or unsupported {nw:s} project file format. "
|
||||
"The project cannot be opened by this version of {nw:s}. "
|
||||
"The file was saved with {nw:s} version {vers:s}."
|
||||
"Unknown or unsupported novelWriter project file format. "
|
||||
"The project cannot be opened by this version of novelWriter. "
|
||||
"The file was saved with novelWriter version {vers:s}."
|
||||
).format(
|
||||
nw = nw.__package__,
|
||||
vers = appVersion,
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
@@ -397,11 +395,11 @@ class NWProject():
|
||||
if int(hexVersion, 16) > int(nw.__hexversion__, 16) and self.mainConf.showGUI:
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.question(self.theParent, "Version Conflict", (
|
||||
"This project was saved by a newer version of %s, version %s. This is version %s. "
|
||||
"If you continue to open the project, some attributes and settings may not be "
|
||||
"preserved. Continue opening the project?"
|
||||
"This project was saved by a newer version of novelWriter, version %s. "
|
||||
"This is version %s. If you continue to open the project, some attributes "
|
||||
"and settings may not be preserved. Continue opening the project?"
|
||||
) % (
|
||||
nw.__package__, appVersion, nw.__version__
|
||||
appVersion, nw.__version__
|
||||
))
|
||||
if msgRes != QMessageBox.Yes:
|
||||
return False
|
||||
|
||||
@@ -43,7 +43,6 @@ class NWSpellCheck():
|
||||
|
||||
SP_INTERNAL = "internal"
|
||||
SP_ENCHANT = "enchant"
|
||||
SP_SYMSPELL = "symspell"
|
||||
|
||||
theDict = None
|
||||
PROJW = []
|
||||
|
||||
@@ -281,9 +281,9 @@ class Tokenizer():
|
||||
does the standard escaped characters.
|
||||
"""
|
||||
escapeDict = {
|
||||
"\*" : "*",
|
||||
"\~" : "~",
|
||||
"\_" : "_",
|
||||
r"\*" : "*",
|
||||
r"\~" : "~",
|
||||
r"\_" : "_",
|
||||
}
|
||||
escReplace = re.compile(
|
||||
"|".join([re.escape(k) for k in escapeDict.keys()]), flags=re.DOTALL
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Init
|
||||
|
||||
novelWriter – Exception Handling
|
||||
==================================
|
||||
Error handling functions
|
||||
|
||||
File History:
|
||||
Created: 2020-08-02 [0.10.2]
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2020, Veronica Berglyd Olsen
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
def formatHtmlErrMsg(exType, exValue, exTrace):
|
||||
"""Generates a HTML version of an exception.
|
||||
"""
|
||||
try:
|
||||
import sys
|
||||
from traceback import format_tb
|
||||
from nw import __issuesurl__, __version__
|
||||
from PyQt5.Qt import PYQT_VERSION_STR
|
||||
from PyQt5.QtCore import QT_VERSION_STR, QSysInfo
|
||||
|
||||
fmtTrace = ""
|
||||
for trEntry in format_tb(exTrace):
|
||||
for trLine in trEntry.split("\n"):
|
||||
stripLine = trLine.lstrip(" ")
|
||||
nIndent = len(trLine) - len(stripLine)
|
||||
fmtTrace += " "*nIndent + stripLine + "<br>"
|
||||
|
||||
theMessage = (
|
||||
"<p>Please report this error by submitting an issue report on "
|
||||
"GitHub, providing a description and this error message. "
|
||||
"URL: <{issueUrl}>.</p>"
|
||||
"<p><b>Environment</b><br>Version: {nwVersion}, OS: {osType} ({osKernel}),"
|
||||
"Python: {pyVersion} ({pyHexVer:#x}), Qt: {qtVers}, PyQt: {pyqtVers}</p>"
|
||||
"<p><b>Error Type</b><br>{exType}: {exMessage}</p>"
|
||||
"<p><b>Traceback</b><br>{exTrace}</p>"
|
||||
).format(
|
||||
nwVersion = __version__,
|
||||
osType = sys.platform,
|
||||
osKernel = QSysInfo.kernelVersion(),
|
||||
pyVersion = sys.version.split()[0],
|
||||
pyHexVer = sys.hexversion,
|
||||
qtVers = QT_VERSION_STR,
|
||||
pyqtVers = PYQT_VERSION_STR,
|
||||
issueUrl = __issuesurl__,
|
||||
exType = exType.__name__,
|
||||
exMessage = str(exValue),
|
||||
exTrace = fmtTrace
|
||||
)
|
||||
|
||||
return theMessage
|
||||
|
||||
except Exception as e:
|
||||
return "Could not generate error message.<br>%s" % str(e)
|
||||
|
||||
return "Could not generate error message."
|
||||
|
||||
|
||||
def exceptionHandler(exType, exValue, exTrace):
|
||||
"""Function to catch unhandled global exceptions.
|
||||
"""
|
||||
import logging
|
||||
from traceback import print_tb, format_tb
|
||||
from nw import CONFIG
|
||||
from PyQt5.QtWidgets import qApp, QApplication, QErrorMessage, QMessageBox
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.error("%s: %s" % (exType.__name__, str(exValue)))
|
||||
print_tb(exTrace)
|
||||
|
||||
if not CONFIG.showGUI:
|
||||
return
|
||||
|
||||
try:
|
||||
nwGUI = None
|
||||
for qWin in qApp.topLevelWidgets():
|
||||
if qWin.objectName() == "GuiMain":
|
||||
nwGUI = qWin
|
||||
break
|
||||
|
||||
if nwGUI is None:
|
||||
logger.warning("Could not find main GUI window so cannot open error dialog")
|
||||
return
|
||||
|
||||
errMsg = QErrorMessage(nwGUI)
|
||||
errMsg.setWindowTitle("Unhandled Error")
|
||||
errMsg.resize(800, 400)
|
||||
errMsg.showMessage((
|
||||
"<h3>An unhandled error has been encountered</h3>%s"
|
||||
) % formatHtmlErrMsg(exType, exValue, exTrace))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(str(e))
|
||||
|
||||
return
|
||||
+3
-3
@@ -54,14 +54,14 @@ class GuiAbout(QDialog):
|
||||
self.innerBox = QHBoxLayout()
|
||||
self.innerBox.setSpacing(self.mainConf.pxInt(16))
|
||||
|
||||
self.setWindowTitle("About %s" % nw.__package__)
|
||||
self.setWindowTitle("About %s" % self.mainConf.appName)
|
||||
self.setMinimumWidth(self.mainConf.pxInt(650))
|
||||
self.setMinimumHeight(self.mainConf.pxInt(600))
|
||||
|
||||
nPx = self.mainConf.pxInt(96)
|
||||
self.nwIcon = QLabel()
|
||||
self.nwIcon.setPixmap(self.theParent.theTheme.getPixmap("novelwriter", (nPx, nPx)))
|
||||
self.lblName = QLabel("<b>%s</b>" % nw.__package__)
|
||||
self.lblName = QLabel("<b>%s</b>" % self.mainConf.appName)
|
||||
self.lblVers = QLabel("v%s" % nw.__version__)
|
||||
self.lblDate = QLabel(datetime.strptime(nw.__date__, "%Y-%m-%d").strftime("%x"))
|
||||
|
||||
@@ -133,7 +133,7 @@ class GuiAbout(QDialog):
|
||||
"<h3>Credits</h3>"
|
||||
"<p>{credits:s}</p>"
|
||||
).format(
|
||||
name = nw.__package__,
|
||||
name = self.mainConf.appName,
|
||||
copyright = nw.__copyright__,
|
||||
website = nw.__url__,
|
||||
domain = nw.__domain__,
|
||||
|
||||
@@ -430,7 +430,6 @@ class GuiBuildNovel(QDialog):
|
||||
def _buildPreview(self):
|
||||
"""Build a preview of the project in the document viewer.
|
||||
"""
|
||||
|
||||
# Get Settings
|
||||
fmtTitle = self.fmtTitle.text().strip()
|
||||
fmtChapter = self.fmtChapter.text().strip()
|
||||
|
||||
+85
-23
@@ -52,7 +52,7 @@ from nw.core import NWDoc, NWSpellSimple, countWords
|
||||
from nw.gui.dochighlight import GuiDocHighlighter
|
||||
from nw.common import transferCase
|
||||
from nw.constants import (
|
||||
nwAlert, nwUnicode, nwDocAction, nwDocInsert, nwInsertSymbols, nwItemClass
|
||||
nwAlert, nwUnicode, nwDocAction, nwDocInsert, nwItemClass
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -79,6 +79,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self.wordCount = 0
|
||||
self.paraCount = 0
|
||||
self.lastEdit = 0
|
||||
self.lastFind = None
|
||||
self.bigDoc = False
|
||||
self.doReplace = False
|
||||
self.nonWord = "\"'"
|
||||
@@ -89,7 +90,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self.typSQOpen = self.mainConf.fmtSingleQuotes[0]
|
||||
self.typSQClose = self.mainConf.fmtSingleQuotes[1]
|
||||
|
||||
# Core Elements
|
||||
# Core Elements and Signals
|
||||
self.qDocument = self.document()
|
||||
self.qDocument.contentsChange.connect(self._docChange)
|
||||
|
||||
@@ -590,8 +591,33 @@ class GuiDocEditor(QTextEdit):
|
||||
"""
|
||||
if isinstance(theInsert, str):
|
||||
theText = theInsert
|
||||
elif theInsert in nwInsertSymbols.SYMBOLS:
|
||||
theText = nwInsertSymbols.SYMBOLS[theInsert]
|
||||
elif theInsert in nwDocInsert:
|
||||
if theInsert == nwDocInsert.NO_INSERT:
|
||||
theText = "",
|
||||
elif theInsert == nwDocInsert.HARD_BREAK:
|
||||
theText = " \n",
|
||||
elif theInsert == nwDocInsert.NB_SPACE:
|
||||
theText = nwUnicode.U_NBSP,
|
||||
elif theInsert == nwDocInsert.THIN_SPACE:
|
||||
theText = nwUnicode.U_THNSP,
|
||||
elif theInsert == nwDocInsert.THIN_NB_SPACE:
|
||||
theText = nwUnicode.U_THNBSP,
|
||||
elif theInsert == nwDocInsert.SHORT_DASH:
|
||||
theText = nwUnicode.U_ENDASH,
|
||||
elif theInsert == nwDocInsert.LONG_DASH:
|
||||
theText = nwUnicode.U_EMDASH,
|
||||
elif theInsert == nwDocInsert.ELLIPSIS:
|
||||
theText = nwUnicode.U_HELLIP,
|
||||
elif theInsert == nwDocInsert.QUOTE_LS:
|
||||
theText = self.typSQOpen
|
||||
elif theInsert == nwDocInsert.QUOTE_RS:
|
||||
theText = self.typSQClose
|
||||
elif theInsert == nwDocInsert.QUOTE_LD:
|
||||
theText = self.typDQOpen
|
||||
elif theInsert == nwDocInsert.QUOTE_RD:
|
||||
theText = self.typDQClose
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
theCursor = self.textCursor()
|
||||
@@ -660,6 +686,7 @@ class GuiDocEditor(QTextEdit):
|
||||
triggers the syntax highlighter.
|
||||
"""
|
||||
self.lastEdit = time()
|
||||
self.lastFind = None
|
||||
if not self.docChanged:
|
||||
self.setDocumentChanged(True)
|
||||
if not self.wcTimer.isActive():
|
||||
@@ -1145,19 +1172,21 @@ class GuiDocEditor(QTextEdit):
|
||||
"""
|
||||
theCursor = self.textCursor()
|
||||
if theCursor.hasSelection():
|
||||
selText = theCursor.selectedText()
|
||||
self.docSearch.setSearchText(theCursor.selectedText())
|
||||
else:
|
||||
selText = ""
|
||||
self.docSearch.setSearchText(selText)
|
||||
self.docSearch.setSearchText(None)
|
||||
self.updateDocMargins()
|
||||
return
|
||||
|
||||
def _beginReplace(self):
|
||||
"""Opens the replace line of the search bar and sets the replace
|
||||
text.
|
||||
"""Opens the replace line of the search bar and sets the find
|
||||
text if a selection has been made, and resets the replace text.
|
||||
"""
|
||||
self._beginSearch()
|
||||
theCursor = self.textCursor()
|
||||
if theCursor.hasSelection():
|
||||
self.docSearch.setSearchText(theCursor.selectedText())
|
||||
self.docSearch.setReplaceText("")
|
||||
self.updateDocMargins()
|
||||
return
|
||||
|
||||
def _findNext(self, isBackward=False):
|
||||
@@ -1177,7 +1206,7 @@ class GuiDocEditor(QTextEdit):
|
||||
if self.docSearch.isWholeWord:
|
||||
findOpt |= QTextDocument.FindWholeWords
|
||||
|
||||
searchFor = self.docSearch.getSearchText()
|
||||
searchFor = self.docSearch.getSearchObject()
|
||||
wasFound = self.find(searchFor, findOpt)
|
||||
if not wasFound:
|
||||
if self.docSearch.doNextFile and not isBackward:
|
||||
@@ -1190,7 +1219,11 @@ class GuiDocEditor(QTextEdit):
|
||||
QTextCursor.End if isBackward else QTextCursor.Start
|
||||
)
|
||||
self.setTextCursor(theCursor)
|
||||
self.find(searchFor, findOpt)
|
||||
wasFound = self.find(searchFor, findOpt)
|
||||
|
||||
if wasFound:
|
||||
theCursor = self.textCursor()
|
||||
self.lastFind = (theCursor.selectionStart(), theCursor.selectionEnd())
|
||||
|
||||
return
|
||||
|
||||
@@ -1200,26 +1233,48 @@ class GuiDocEditor(QTextEdit):
|
||||
next automatically when done.
|
||||
"""
|
||||
if not self.docSearch.isVisible():
|
||||
# The search tool is not active, so we activate it.
|
||||
self._beginSearch()
|
||||
return
|
||||
|
||||
theCursor = self.textCursor()
|
||||
if not theCursor.hasSelection():
|
||||
# We have no text selected at all, so just make this a
|
||||
# regular find next call.
|
||||
self._findNext()
|
||||
return
|
||||
|
||||
if self.lastFind is None and theCursor.hasSelection():
|
||||
# If we have a selection but no search, it may have been the
|
||||
# text we triggered the search with, in which case we search
|
||||
# again from the beginning of that selection to make sure we
|
||||
# have a valid result.
|
||||
sPos = theCursor.selectionStart()
|
||||
theCursor.clearSelection()
|
||||
theCursor.setPosition(sPos)
|
||||
self.setTextCursor(theCursor)
|
||||
self._findNext()
|
||||
theCursor = self.textCursor()
|
||||
|
||||
if self.lastFind is None:
|
||||
# In case the above didn't find a result, we give up here.
|
||||
return
|
||||
|
||||
searchFor = self.docSearch.getSearchText()
|
||||
replWith = self.docSearch.getReplaceText()
|
||||
selText = theCursor.selectedText()
|
||||
|
||||
if self.docSearch.doMatchCap:
|
||||
replWith = transferCase(selText, replWith)
|
||||
replWith = transferCase(theCursor.selectedText(), replWith)
|
||||
|
||||
if not self.docSearch.isCaseSense:
|
||||
isMatch = searchFor.lower() == selText.lower()
|
||||
else:
|
||||
isMatch = searchFor == selText
|
||||
# Make sure the selected text was selected by an actual find
|
||||
# call, and not the user.
|
||||
try:
|
||||
isFind = self.lastFind[0] == theCursor.selectionStart()
|
||||
isFind &= self.lastFind[1] == theCursor.selectionEnd()
|
||||
except:
|
||||
isFind = False
|
||||
|
||||
if isMatch:
|
||||
if isFind:
|
||||
theCursor.beginEditBlock()
|
||||
theCursor.removeSelectedText()
|
||||
theCursor.insertText(replWith)
|
||||
@@ -1229,9 +1284,10 @@ class GuiDocEditor(QTextEdit):
|
||||
logger.verbose("Replaced occurrence of '%s' with '%s' on line %d" % (
|
||||
searchFor, replWith, theCursor.blockNumber()
|
||||
))
|
||||
else:
|
||||
logger.error("The selected text is not a search result, skipping replace")
|
||||
|
||||
if searchFor:
|
||||
self._findNext()
|
||||
self._findNext()
|
||||
|
||||
return
|
||||
|
||||
@@ -1500,7 +1556,8 @@ class GuiDocEditSearch(QFrame):
|
||||
"""
|
||||
if not self.isVisible():
|
||||
self.setVisible(True)
|
||||
self.searchBox.setText(theText)
|
||||
if theText is not None:
|
||||
self.searchBox.setText(theText)
|
||||
self.searchBox.setFocus()
|
||||
if self.isRegEx:
|
||||
self._alertSearchValid(True)
|
||||
@@ -1515,7 +1572,7 @@ class GuiDocEditSearch(QFrame):
|
||||
self.replaceBox.setText(theText)
|
||||
return True
|
||||
|
||||
def getSearchText(self):
|
||||
def getSearchObject(self):
|
||||
"""Return the current search text either as text or as a regular
|
||||
expression object.
|
||||
"""
|
||||
@@ -1543,6 +1600,11 @@ class GuiDocEditSearch(QFrame):
|
||||
|
||||
return theText
|
||||
|
||||
def getSearchText(self):
|
||||
"""Return the current search text.
|
||||
"""
|
||||
return self.searchBox.text()
|
||||
|
||||
def getReplaceText(self):
|
||||
"""Return the current replace text.
|
||||
"""
|
||||
|
||||
+111
-17
@@ -28,7 +28,9 @@
|
||||
import logging
|
||||
import nw
|
||||
|
||||
from PyQt5.QtCore import QUrl
|
||||
from os import path
|
||||
|
||||
from PyQt5.QtCore import QUrl, QProcess
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtWidgets import QMenuBar, QAction, QMessageBox
|
||||
|
||||
@@ -47,6 +49,10 @@ class GuiMainMenu(QMenuBar):
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
|
||||
# Internals
|
||||
self.assistProc = None
|
||||
|
||||
# Build Menu
|
||||
self._buildProjectMenu()
|
||||
self._buildDocumentMenu()
|
||||
self._buildEditMenu()
|
||||
@@ -67,15 +73,40 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def setAvailableRoot(self):
|
||||
for itemClass in nwItemClass:
|
||||
if itemClass == nwItemClass.NO_CLASS: continue
|
||||
if itemClass == nwItemClass.TRASH: continue
|
||||
if itemClass == nwItemClass.NO_CLASS:
|
||||
continue
|
||||
if itemClass == nwItemClass.TRASH:
|
||||
continue
|
||||
self.rootItems[itemClass].setEnabled(
|
||||
self.theProject.projTree.checkRootUnique(itemClass)
|
||||
)
|
||||
return
|
||||
|
||||
def closeHelp(self):
|
||||
"""Close the process used for the Qt Assistant, if it is open.
|
||||
"""
|
||||
if self.assistProc is None:
|
||||
return
|
||||
|
||||
if self.assistProc.state() == QProcess.Starting:
|
||||
if self.assistProc.waitForStarted(10000):
|
||||
self.assistProc.terminate()
|
||||
else:
|
||||
self.assistProc.kill()
|
||||
|
||||
elif self.assistProc.state() == QProcess.Running:
|
||||
self.assistProc.terminate()
|
||||
if not self.assistProc.waitForFinished(10000):
|
||||
self.assistProc.kill()
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Update Menu on Settings Changed
|
||||
##
|
||||
@@ -134,10 +165,26 @@ class GuiMainMenu(QMenuBar):
|
||||
msgBox.aboutQt(self.theParent,"About Qt")
|
||||
return True
|
||||
|
||||
def _openHelp(self):
|
||||
"""Open the documentation URL in the system's default browser.
|
||||
def _openAssistant(self):
|
||||
"""Open the documentation in Qt Assistant.
|
||||
"""
|
||||
QDesktopServices.openUrl(QUrl(nw.__docurl__))
|
||||
if not self.mainConf.hasHelp:
|
||||
self._openWebsite(nw.__docurl__)
|
||||
return False
|
||||
|
||||
self.assistProc = QProcess(self)
|
||||
self.assistProc.start("assistant", ["-collectionFile", self.mainConf.helpPath])
|
||||
|
||||
if not self.assistProc.waitForStarted(10000):
|
||||
self._openWebsite(nw.__docurl__)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _openWebsite(self, theUrl):
|
||||
"""Open an URL in the system's default browser.
|
||||
"""
|
||||
QDesktopServices.openUrl(QUrl(theUrl))
|
||||
return True
|
||||
|
||||
def _openIssue(self):
|
||||
@@ -258,7 +305,7 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
# Project > Exit
|
||||
self.aExitNW = QAction("Exit", self)
|
||||
self.aExitNW.setStatusTip("Exit %s" % nw.__package__)
|
||||
self.aExitNW.setStatusTip("Exit %s" % self.mainConf.appName)
|
||||
self.aExitNW.setShortcut("Ctrl+Q")
|
||||
self.aExitNW.triggered.connect(self._menuExit)
|
||||
self.projMenu.addAction(self.aExitNW)
|
||||
@@ -485,6 +532,37 @@ class GuiMainMenu(QMenuBar):
|
||||
# Insert > Separator
|
||||
self.insertMenu.addSeparator()
|
||||
|
||||
# Insert > Left Single Quote
|
||||
self.aInsQuoteLS = QAction("Left Single Quote", self)
|
||||
self.aInsQuoteLS.setStatusTip("Insert left single quote")
|
||||
self.aInsQuoteLS.setShortcut("Ctrl+K, 1")
|
||||
self.aInsQuoteLS.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_LS))
|
||||
self.insertMenu.addAction(self.aInsQuoteLS)
|
||||
|
||||
# Insert > Right Single Quote
|
||||
self.aInsQuoteRS = QAction("Right Single Quote", self)
|
||||
self.aInsQuoteRS.setStatusTip("Insert right single quote")
|
||||
self.aInsQuoteRS.setShortcut("Ctrl+K, 2")
|
||||
self.aInsQuoteRS.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_RS))
|
||||
self.insertMenu.addAction(self.aInsQuoteRS)
|
||||
|
||||
# Insert > Left Double Quote
|
||||
self.aInsQuoteLD = QAction("Left Double Quote", self)
|
||||
self.aInsQuoteLD.setStatusTip("Insert left double quote")
|
||||
self.aInsQuoteLD.setShortcut("Ctrl+K, 3")
|
||||
self.aInsQuoteLD.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_LD))
|
||||
self.insertMenu.addAction(self.aInsQuoteLD)
|
||||
|
||||
# Insert > Right Double Quote
|
||||
self.aInsQuoteRD = QAction("Right Double Quote", self)
|
||||
self.aInsQuoteRD.setStatusTip("Insert right double quote")
|
||||
self.aInsQuoteRD.setShortcut("Ctrl+K, 4")
|
||||
self.aInsQuoteRD.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_RD))
|
||||
self.insertMenu.addAction(self.aInsQuoteRD)
|
||||
|
||||
# Insert > Separator
|
||||
self.insertMenu.addSeparator()
|
||||
|
||||
# Insert > Hard Line Break
|
||||
self.aInsHardBreak = QAction("Hard Line Break", self)
|
||||
self.aInsHardBreak.setStatusTip("Insert a hard line break")
|
||||
@@ -773,8 +851,8 @@ class GuiMainMenu(QMenuBar):
|
||||
self.helpMenu = self.addMenu("&Help")
|
||||
|
||||
# Help > About
|
||||
self.aAboutNW = QAction("About %s" % nw.__package__, self)
|
||||
self.aAboutNW.setStatusTip("About %s" % nw.__package__)
|
||||
self.aAboutNW = QAction("About %s" % self.mainConf.appName, self)
|
||||
self.aAboutNW.setStatusTip("About %s" % self.mainConf.appName)
|
||||
self.aAboutNW.triggered.connect(self._showAbout)
|
||||
self.helpMenu.addAction(self.aAboutNW)
|
||||
|
||||
@@ -787,17 +865,33 @@ class GuiMainMenu(QMenuBar):
|
||||
# Help > Separator
|
||||
self.helpMenu.addSeparator()
|
||||
|
||||
# Document > Preview
|
||||
self.aHelp = QAction("Online Documentation", self)
|
||||
self.aHelp.setStatusTip("View online documentation")
|
||||
self.aHelp.setShortcut("F1")
|
||||
self.aHelp.triggered.connect(self._openHelp)
|
||||
self.helpMenu.addAction(self.aHelp)
|
||||
# Document > Documentation
|
||||
if self.mainConf.hasHelp and self.mainConf.hasAssistant:
|
||||
self.aHelpLoc = QAction("Documentation (Local)", self)
|
||||
self.aHelpLoc.setStatusTip("View local documentation with Qt Assistant")
|
||||
self.aHelpLoc.triggered.connect(self._openAssistant)
|
||||
self.aHelpLoc.setShortcut("F1")
|
||||
self.helpMenu.addAction(self.aHelpLoc)
|
||||
|
||||
self.aHelpWeb = QAction("Documentation (Online)", self)
|
||||
self.aHelpWeb.setStatusTip("View online documentation")
|
||||
self.aHelpWeb.triggered.connect(lambda: self._openWebsite(nw.__docurl__))
|
||||
if self.mainConf.hasHelp and self.mainConf.hasAssistant:
|
||||
self.aHelpWeb.setShortcut("Shift+F1")
|
||||
else:
|
||||
self.aHelpWeb.setShortcuts(["F1","Shift+F1"])
|
||||
self.helpMenu.addAction(self.aHelpWeb)
|
||||
|
||||
# Document > Go to Website
|
||||
self.aWebsite = QAction("Open the novelWriter Website", self)
|
||||
self.aWebsite.setStatusTip("View the main website")
|
||||
self.aWebsite.triggered.connect(lambda: self._openWebsite(nw.__url__))
|
||||
self.helpMenu.addAction(self.aWebsite)
|
||||
|
||||
# Document > Report Issue
|
||||
self.aIssue = QAction("Report an Issue", self)
|
||||
self.aIssue.setStatusTip("View online documentation")
|
||||
self.aIssue.triggered.connect(self._openIssue)
|
||||
self.aIssue.setStatusTip("Report a bug or issue on GitHub")
|
||||
self.aIssue.triggered.connect(lambda: self._openWebsite(nw.__issuesurl__))
|
||||
self.helpMenu.addAction(self.aIssue)
|
||||
|
||||
return
|
||||
|
||||
+7
-10
@@ -108,7 +108,7 @@ class GuiPreferences(PagedDialog):
|
||||
msgBox = QMessageBox()
|
||||
msgBox.information(
|
||||
self, "Preferences",
|
||||
"Some changes will not be applied until %s has been restarted." % nw.__package__
|
||||
"Some changes will not be applied until novelWriter has been restarted."
|
||||
)
|
||||
|
||||
if validEntries:
|
||||
@@ -154,7 +154,7 @@ class GuiConfigEditGeneralTab(QWidget):
|
||||
self.mainForm.addRow(
|
||||
"Main GUI theme",
|
||||
self.selectTheme,
|
||||
"Changing this requires restarting %s." % nw.__package__
|
||||
"Changing this requires restarting novelWriter."
|
||||
)
|
||||
|
||||
## Select Icon Theme
|
||||
@@ -170,7 +170,7 @@ class GuiConfigEditGeneralTab(QWidget):
|
||||
self.mainForm.addRow(
|
||||
"Main icon theme",
|
||||
self.selectIcons,
|
||||
"Changing this requires restarting %s." % nw.__package__
|
||||
"Changing this requires restarting novelWriter."
|
||||
)
|
||||
|
||||
## Dark Icons
|
||||
@@ -193,7 +193,7 @@ class GuiConfigEditGeneralTab(QWidget):
|
||||
self.mainForm.addRow(
|
||||
"Font family",
|
||||
self.guiFont,
|
||||
"Changing this requires restarting %s." % nw.__package__,
|
||||
"Changing this requires restarting novelWriter.",
|
||||
theButton = self.fontButton
|
||||
)
|
||||
|
||||
@@ -206,7 +206,7 @@ class GuiConfigEditGeneralTab(QWidget):
|
||||
self.mainForm.addRow(
|
||||
"Font size",
|
||||
self.guiFontSize,
|
||||
"Changing this requires restarting %s." % nw.__package__,
|
||||
"Changing this requires restarting novelWriter.",
|
||||
theUnit = "pt"
|
||||
)
|
||||
|
||||
@@ -625,13 +625,10 @@ class GuiConfigEditEditingTab(QWidget):
|
||||
self.spellToolList = QComboBox(self)
|
||||
self.spellToolList.addItem("Internal (difflib)", NWSpellCheck.SP_INTERNAL)
|
||||
self.spellToolList.addItem("Spell Enchant (pyenchant)", NWSpellCheck.SP_ENCHANT)
|
||||
# self.spellToolList.addItem("SymSpell (symspellpy)", NWSpellCheck.SP_SYMSPELL)
|
||||
|
||||
theModel = self.spellToolList.model()
|
||||
idEnchant = self.spellToolList.findData(NWSpellCheck.SP_ENCHANT)
|
||||
# idSymSpell = self.spellToolList.findData(NWSpellCheck.SP_SYMSPELL)
|
||||
theModel = self.spellToolList.model()
|
||||
idEnchant = self.spellToolList.findData(NWSpellCheck.SP_ENCHANT)
|
||||
theModel.item(idEnchant).setEnabled(self.mainConf.hasEnchant)
|
||||
# theModel.item(idSymSpell).setEnabled(self.mainConf.hasSymSpell)
|
||||
|
||||
self.spellToolList.currentIndexChanged.connect(self._doUpdateSpellTool)
|
||||
toolIdx = self.spellToolList.findData(self.mainConf.spellTool)
|
||||
|
||||
+18
-10
@@ -56,6 +56,7 @@ class GuiMain(QMainWindow):
|
||||
QMainWindow.__init__(self)
|
||||
|
||||
logger.debug("Initialising GUI ...")
|
||||
self.setObjectName("GuiMain")
|
||||
self.mainConf = nw.CONFIG
|
||||
|
||||
# Some runtime info useful for debugging
|
||||
@@ -220,7 +221,7 @@ class GuiMain(QMainWindow):
|
||||
else:
|
||||
self.manageProjects()
|
||||
|
||||
logger.debug("%s is ready ..." % nw.__package__)
|
||||
logger.debug("novelWriter is ready ...")
|
||||
|
||||
return
|
||||
|
||||
@@ -384,13 +385,13 @@ class GuiMain(QMainWindow):
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.warning(
|
||||
self, "Project Locked", (
|
||||
"The project is already open by another instance of %s, and is "
|
||||
"therefore locked. Override lock and continue anyway?<br><br>"
|
||||
"The project is already open by another instance of novelWriter, and "
|
||||
"is therefore locked. Override lock and continue anyway?<br><br>"
|
||||
"Note: If the program or the computer previously crashed, the lock "
|
||||
"can safely be overridden. If, however, another instance of %s has "
|
||||
"the project open, overriding the lock may corrupt the project, and "
|
||||
"is not recommended.%s"
|
||||
) % (nw.__package__, nw.__package__, lockDetails),
|
||||
"can safely be overridden. If, however, another instance of "
|
||||
"novelWriter has the project open, overriding the lock may corrupt "
|
||||
"the project, and is not recommended.%s"
|
||||
) % lockDetails,
|
||||
QMessageBox.Yes | QMessageBox.No, QMessageBox.No
|
||||
)
|
||||
if msgRes == QMessageBox.Yes:
|
||||
@@ -874,7 +875,7 @@ class GuiMain(QMainWindow):
|
||||
if msgRes != QMessageBox.Yes:
|
||||
return False
|
||||
|
||||
logger.info("Exiting %s" % nw.__package__)
|
||||
logger.info("Exiting novelWriter")
|
||||
|
||||
if not self.isFocusMode:
|
||||
self.mainConf.setMainPanePos(self.splitMain.sizes())
|
||||
@@ -893,6 +894,7 @@ class GuiMain(QMainWindow):
|
||||
|
||||
self.mainConf.saveConfig()
|
||||
self.reportConfErr()
|
||||
self.mainMenu.closeHelp()
|
||||
|
||||
qApp.quit()
|
||||
|
||||
@@ -1006,6 +1008,10 @@ class GuiMain(QMainWindow):
|
||||
self.addAction(self.mainMenu.aInsENDash)
|
||||
self.addAction(self.mainMenu.aInsEMDash)
|
||||
self.addAction(self.mainMenu.aInsEllipsis)
|
||||
self.addAction(self.mainMenu.aInsQuoteLS)
|
||||
self.addAction(self.mainMenu.aInsQuoteRS)
|
||||
self.addAction(self.mainMenu.aInsQuoteLD)
|
||||
self.addAction(self.mainMenu.aInsQuoteRD)
|
||||
self.addAction(self.mainMenu.aInsHardBreak)
|
||||
self.addAction(self.mainMenu.aInsNBSpace)
|
||||
self.addAction(self.mainMenu.aInsThinSpace)
|
||||
@@ -1030,12 +1036,14 @@ class GuiMain(QMainWindow):
|
||||
self.addAction(self.mainMenu.aPreferences)
|
||||
|
||||
# Help
|
||||
self.addAction(self.mainMenu.aHelp)
|
||||
if self.mainConf.hasHelp and self.mainConf.hasAssistant:
|
||||
self.addAction(self.mainMenu.aHelpLoc)
|
||||
self.addAction(self.mainMenu.aHelpWeb)
|
||||
|
||||
return True
|
||||
|
||||
def _setWindowTitle(self, projName=None):
|
||||
winTitle = "%s" % nw.__package__
|
||||
winTitle = self.mainConf.appName
|
||||
if projName is not None:
|
||||
winTitle += " - %s" % projName
|
||||
self.setWindowTitle(winTitle)
|
||||
|
||||
Reference in New Issue
Block a user