Let the shared class handle alert dialogs
This commit is contained in:
@@ -28,21 +28,17 @@ from __future__ import annotations
|
|||||||
import shutil
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Iterable
|
from typing import Iterable
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt5.QtCore import QCoreApplication
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.enum import nwAlert
|
|
||||||
from novelwriter.common import minmax, simplified
|
from novelwriter.common import minmax, simplified
|
||||||
from novelwriter.constants import nwItemClass
|
from novelwriter.constants import nwItemClass
|
||||||
from novelwriter.core.item import NWItem
|
from novelwriter.core.item import NWItem
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
|
||||||
from novelwriter.guimain import GuiMain
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -312,8 +308,7 @@ class ProjectBuilder:
|
|||||||
parameter provided by the New Project Wizard.
|
parameter provided by the New Project Wizard.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, mainGui: GuiMain) -> None:
|
def __init__(self) -> None:
|
||||||
self.mainGui = mainGui
|
|
||||||
self.tr = partial(QCoreApplication.translate, "NWProject")
|
self.tr = partial(QCoreApplication.translate, "NWProject")
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -478,17 +473,17 @@ class ProjectBuilder:
|
|||||||
try:
|
try:
|
||||||
shutil.unpack_archive(pkgSample, projPath)
|
shutil.unpack_archive(pkgSample, projPath)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"Failed to create a new example project."
|
"Failed to create a new example project."
|
||||||
), level=nwAlert.ERROR, exc=exc)
|
), exc=exc)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"Failed to create a new example project. "
|
"Failed to create a new example project. "
|
||||||
"Could not find the necessary files. "
|
"Could not find the necessary files. "
|
||||||
"They seem to be missing from this installation."
|
"They seem to be missing from this installation."
|
||||||
), level=nwAlert.ERROR)
|
))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ class NWProject(QObject):
|
|||||||
# Project Methods
|
# Project Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def openProject(self, projPath: str | Path) -> bool:
|
def openProject(self, projPath: str | Path, clearLock: bool = False) -> bool:
|
||||||
"""Open the project file provided. If it doesn't exist, assume
|
"""Open the project file provided. If it doesn't exist, assume
|
||||||
it is a folder and look for the file within it. If successful,
|
it is a folder and look for the file within it. If successful,
|
||||||
parse the XML of the file and populate the project variables and
|
parse the XML of the file and populate the project variables and
|
||||||
@@ -230,6 +230,9 @@ class NWProject(QObject):
|
|||||||
# Project Lock
|
# Project Lock
|
||||||
# ============
|
# ============
|
||||||
|
|
||||||
|
if clearLock:
|
||||||
|
self._storage.clearLockFile()
|
||||||
|
|
||||||
lockStatus = self._storage.readLockFile()
|
lockStatus = self._storage.readLockFile()
|
||||||
if len(lockStatus) > 0:
|
if len(lockStatus) > 0:
|
||||||
if lockStatus[0] == "ERROR":
|
if lockStatus[0] == "ERROR":
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@@ -40,6 +41,9 @@ from novelwriter import CONFIG, SHARED
|
|||||||
from novelwriter.common import formatInt
|
from novelwriter.common import formatInt
|
||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
|
|
||||||
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -55,13 +59,12 @@ class GuiProjectLoad(QDialog):
|
|||||||
|
|
||||||
D_PATH = Qt.ItemDataRole.UserRole
|
D_PATH = Qt.ItemDataRole.UserRole
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui: GuiMain) -> None:
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Create: GuiProjectLoad")
|
logger.debug("Create: GuiProjectLoad")
|
||||||
self.setObjectName("GuiProjectLoad")
|
self.setObjectName("GuiProjectLoad")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
|
||||||
self.openState = self.NONE_STATE
|
self.openState = self.NONE_STATE
|
||||||
self.openPath = None
|
self.openPath = None
|
||||||
|
|
||||||
@@ -225,7 +228,7 @@ class GuiProjectLoad(QDialog):
|
|||||||
selList = self.listBox.selectedItems()
|
selList = self.listBox.selectedItems()
|
||||||
if selList:
|
if selList:
|
||||||
projName = selList[0].text(self.C_NAME)
|
projName = selList[0].text(self.C_NAME)
|
||||||
msgYes = self.mainGui.askQuestion(self.tr(
|
msgYes = SHARED.question(self.tr(
|
||||||
"Remove '{0}' from the recent projects list? "
|
"Remove '{0}' from the recent projects list? "
|
||||||
"The project files will not be deleted."
|
"The project files will not be deleted."
|
||||||
).format(projName))
|
).format(projName))
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.enum import nwAlert
|
|
||||||
from novelwriter.common import simplified
|
from novelwriter.common import simplified
|
||||||
from novelwriter.extensions.switch import NSwitch
|
from novelwriter.extensions.switch import NSwitch
|
||||||
from novelwriter.extensions.pageddialog import NPagedDialog
|
from novelwriter.extensions.pageddialog import NPagedDialog
|
||||||
@@ -288,8 +287,6 @@ class GuiProjectEditStatus(QWidget):
|
|||||||
def __init__(self, projGui, isStatus):
|
def __init__(self, projGui, isStatus):
|
||||||
super().__init__(parent=projGui)
|
super().__init__(parent=projGui)
|
||||||
|
|
||||||
self.mainGui = projGui.mainGui
|
|
||||||
|
|
||||||
if isStatus:
|
if isStatus:
|
||||||
self.theStatus = SHARED.project.data.itemStatus
|
self.theStatus = SHARED.project.data.itemStatus
|
||||||
pageLabel = self.tr("Novel File Status Levels")
|
pageLabel = self.tr("Novel File Status Levels")
|
||||||
@@ -441,9 +438,7 @@ class GuiProjectEditStatus(QWidget):
|
|||||||
if isinstance(selItem, QTreeWidgetItem):
|
if isinstance(selItem, QTreeWidgetItem):
|
||||||
iRow = self.listBox.indexOfTopLevelItem(selItem)
|
iRow = self.listBox.indexOfTopLevelItem(selItem)
|
||||||
if selItem.data(self.COL_LABEL, self.NUM_ROLE) > 0:
|
if selItem.data(self.COL_LABEL, self.NUM_ROLE) > 0:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr("Cannot delete a status item that is in use."))
|
||||||
"Cannot delete a status item that is in use."
|
|
||||||
), level=nwAlert.ERROR)
|
|
||||||
else:
|
else:
|
||||||
self.listBox.takeTopLevelItem(iRow)
|
self.listBox.takeTopLevelItem(iRow)
|
||||||
self.colDeleted.append(selItem.data(self.COL_LABEL, self.KEY_ROLE))
|
self.colDeleted.append(selItem.data(self.COL_LABEL, self.KEY_ROLE))
|
||||||
@@ -574,7 +569,6 @@ class GuiProjectEditReplace(QWidget):
|
|||||||
def __init__(self, projGui):
|
def __init__(self, projGui):
|
||||||
super().__init__(parent=projGui)
|
super().__init__(parent=projGui)
|
||||||
|
|
||||||
self.mainGui = projGui.mainGui
|
|
||||||
self.arChanged = False
|
self.arChanged = False
|
||||||
|
|
||||||
wCol0 = CONFIG.pxInt(
|
wCol0 = CONFIG.pxInt(
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.enum import nwAlert
|
|
||||||
from novelwriter.core.spellcheck import UserDictionary
|
from novelwriter.core.spellcheck import UserDictionary
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
@@ -52,8 +51,6 @@ class GuiWordList(QDialog):
|
|||||||
self.setObjectName("GuiWordList")
|
self.setObjectName("GuiWordList")
|
||||||
self.setWindowTitle(self.tr("Project Word List"))
|
self.setWindowTitle(self.tr("Project Word List"))
|
||||||
|
|
||||||
self.mainGui = mainGui
|
|
||||||
|
|
||||||
mS = CONFIG.pxInt(250)
|
mS = CONFIG.pxInt(250)
|
||||||
wW = CONFIG.pxInt(320)
|
wW = CONFIG.pxInt(320)
|
||||||
wH = CONFIG.pxInt(340)
|
wH = CONFIG.pxInt(340)
|
||||||
@@ -123,15 +120,13 @@ class GuiWordList(QDialog):
|
|||||||
"""Add a new word to the word list."""
|
"""Add a new word to the word list."""
|
||||||
word = self.newEntry.text().strip()
|
word = self.newEntry.text().strip()
|
||||||
if word == "":
|
if word == "":
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr("Cannot add a blank word."))
|
||||||
"Cannot add a blank word."
|
|
||||||
), level=nwAlert.ERROR)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.listBox.findItems(word, Qt.MatchExactly):
|
if self.listBox.findItems(word, Qt.MatchExactly):
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"The word '{0}' is already in the word list."
|
"The word '{0}' is already in the word list."
|
||||||
).format(word), level=nwAlert.ERROR)
|
).format(word))
|
||||||
return
|
return
|
||||||
|
|
||||||
self.listBox.addItem(word)
|
self.listBox.addItem(word)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.enum import nwAlert, nwDocAction, nwDocInsert, nwDocMode, nwItemClass
|
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemClass
|
||||||
from novelwriter.common import minmax, transferCase
|
from novelwriter.common import minmax, transferCase
|
||||||
from novelwriter.constants import nwConst, nwKeyWords, nwUnicode
|
from novelwriter.constants import nwConst, nwKeyWords, nwUnicode
|
||||||
from novelwriter.core.index import countWords
|
from novelwriter.core.index import countWords
|
||||||
@@ -380,14 +380,14 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
docSize = len(theDoc)
|
docSize = len(theDoc)
|
||||||
if docSize > nwConst.MAX_DOCSIZE:
|
if docSize > nwConst.MAX_DOCSIZE:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"The document you are trying to open is too big. "
|
"The document you are trying to open is too big. "
|
||||||
"The document size is {0} MB. "
|
"The document size is {0} MB. "
|
||||||
"The maximum size allowed is {1} MB."
|
"The maximum size allowed is {1} MB."
|
||||||
).format(
|
).format(
|
||||||
f"{docSize/1.0e6:.2f}",
|
f"{docSize/1.0e6:.2f}",
|
||||||
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
|
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
|
||||||
), level=nwAlert.ERROR)
|
))
|
||||||
self.clearEditor()
|
self.clearEditor()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -478,14 +478,14 @@ class GuiDocEditor(QTextEdit):
|
|||||||
"""
|
"""
|
||||||
docSize = len(theText)
|
docSize = len(theText)
|
||||||
if docSize > nwConst.MAX_DOCSIZE:
|
if docSize > nwConst.MAX_DOCSIZE:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"The text you are trying to add is too big. "
|
"The text you are trying to add is too big. "
|
||||||
"The text size is {0} MB. "
|
"The text size is {0} MB. "
|
||||||
"The maximum size allowed is {1} MB."
|
"The maximum size allowed is {1} MB."
|
||||||
).format(
|
).format(
|
||||||
f"{docSize/1.0e6:.2f}",
|
f"{docSize/1.0e6:.2f}",
|
||||||
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
|
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
|
||||||
), level=nwAlert.ERROR)
|
))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||||
@@ -524,7 +524,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
if not self._nwDocument.writeDocument(docText):
|
if not self._nwDocument.writeDocument(docText):
|
||||||
saveOk = False
|
saveOk = False
|
||||||
if self._nwDocument._currHash != self._nwDocument._prevHash:
|
if self._nwDocument._currHash != self._nwDocument._prevHash:
|
||||||
msgYes = self.mainGui.askQuestion(self.tr(
|
msgYes = SHARED.question(self.tr(
|
||||||
"This document has been changed outside of novelWriter "
|
"This document has been changed outside of novelWriter "
|
||||||
"while it was open. Overwrite the file on disk?"
|
"while it was open. Overwrite the file on disk?"
|
||||||
))
|
))
|
||||||
@@ -532,10 +532,9 @@ class GuiDocEditor(QTextEdit):
|
|||||||
saveOk = self._nwDocument.writeDocument(docText, forceWrite=True)
|
saveOk = self._nwDocument.writeDocument(docText, forceWrite=True)
|
||||||
|
|
||||||
if not saveOk:
|
if not saveOk:
|
||||||
self.mainGui.makeAlert(
|
SHARED.error(
|
||||||
self.tr("Could not save document."),
|
self.tr("Could not save document."),
|
||||||
info=self._nwDocument.getError(),
|
info=self._nwDocument.getError()
|
||||||
level=nwAlert.ERROR
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@@ -723,7 +722,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
if not CONFIG.hasEnchant:
|
if not CONFIG.hasEnchant:
|
||||||
if theMode:
|
if theMode:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.info(self.tr(
|
||||||
"Spell checking requires the package PyEnchant. "
|
"Spell checking requires the package PyEnchant. "
|
||||||
"It does not appear to be installed."
|
"It does not appear to be installed."
|
||||||
))
|
))
|
||||||
@@ -867,7 +866,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
if self._nwDocument is None:
|
if self._nwDocument is None:
|
||||||
logger.error("No document open")
|
logger.error("No document open")
|
||||||
return False
|
return False
|
||||||
self.mainGui.makeAlert(
|
SHARED.info(
|
||||||
self.tr("The currently open file is saved in:"),
|
self.tr("The currently open file is saved in:"),
|
||||||
info=self._nwDocument.getFileLocation()
|
info=self._nwDocument.getFileLocation()
|
||||||
)
|
)
|
||||||
@@ -1103,12 +1102,12 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self._lastFind = None
|
self._lastFind = None
|
||||||
|
|
||||||
if self.document().characterCount() > nwConst.MAX_DOCSIZE:
|
if self.document().characterCount() > nwConst.MAX_DOCSIZE:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"The document has grown too big and you cannot add more text to it. "
|
"The document has grown too big and you cannot add more text to it. "
|
||||||
"The maximum size of a single novelWriter document is {0} MB."
|
"The maximum size of a single novelWriter document is {0} MB."
|
||||||
).format(
|
).format(
|
||||||
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
|
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
|
||||||
), level=nwAlert.ERROR)
|
))
|
||||||
self.undo()
|
self.undo()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1665,9 +1664,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
"""
|
"""
|
||||||
theCursor = self.textCursor()
|
theCursor = self.textCursor()
|
||||||
if not theCursor.hasSelection():
|
if not theCursor.hasSelection():
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr("Please select some text before calling replace quotes."))
|
||||||
"Please select some text before calling replace quotes."
|
|
||||||
), level=nwAlert.ERROR)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
posS = theCursor.selectionStart()
|
posS = theCursor.selectionStart()
|
||||||
|
|||||||
+20
-30
@@ -49,7 +49,7 @@ from novelwriter.dialogs.docsplit import GuiDocSplit
|
|||||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||||
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
||||||
from novelwriter.enum import (
|
from novelwriter.enum import (
|
||||||
nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert, nwWidget
|
nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwWidget
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
@@ -582,9 +582,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
sHandle = self.getSelectedHandle()
|
sHandle = self.getSelectedHandle()
|
||||||
pItem = SHARED.project.tree[sHandle] if sHandle else None
|
pItem = SHARED.project.tree[sHandle] if sHandle else None
|
||||||
if sHandle is None or pItem is None:
|
if sHandle is None or pItem is None:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr("Did not find anywhere to add the file or folder!"))
|
||||||
"Did not find anywhere to add the file or folder!"
|
|
||||||
), level=nwAlert.ERROR)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Collect some information about the selected item
|
# Collect some information about the selected item
|
||||||
@@ -593,9 +591,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
sIsParent = False if qItem is None else qItem.childCount() > 0
|
sIsParent = False if qItem is None else qItem.childCount() > 0
|
||||||
|
|
||||||
if SHARED.project.tree.isTrash(sHandle):
|
if SHARED.project.tree.isTrash(sHandle):
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr("Cannot add new files or folders to the Trash folder."))
|
||||||
"Cannot add new files or folders to the Trash folder."
|
|
||||||
), level=nwAlert.ERROR)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Set default label and determine if new item is to be added
|
# Set default label and determine if new item is to be added
|
||||||
@@ -831,9 +827,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
|
|
||||||
logger.debug("Emptying Trash folder")
|
logger.debug("Emptying Trash folder")
|
||||||
if trashHandle is None:
|
if trashHandle is None:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.info(self.tr("There is currently no Trash folder in this project."))
|
||||||
"There is currently no Trash folder in this project."
|
|
||||||
))
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
theTrash = self.getTreeFromHandle(trashHandle)
|
theTrash = self.getTreeFromHandle(trashHandle)
|
||||||
@@ -842,12 +836,10 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
|
|
||||||
nTrash = len(theTrash)
|
nTrash = len(theTrash)
|
||||||
if nTrash == 0:
|
if nTrash == 0:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.info(self.tr("The Trash folder is already empty."))
|
||||||
"The Trash folder is already empty."
|
|
||||||
))
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
msgYes = self.mainGui.askQuestion(
|
msgYes = SHARED.question(
|
||||||
self.tr("Permanently delete {0} file(s) from Trash?").format(nTrash)
|
self.tr("Permanently delete {0} file(s) from Trash?").format(nTrash)
|
||||||
)
|
)
|
||||||
if not msgYes:
|
if not msgYes:
|
||||||
@@ -893,8 +885,8 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if askFirst:
|
if askFirst:
|
||||||
msgYes = self.mainGui.askQuestion(
|
msgYes = SHARED.question(
|
||||||
self.tr("Move '{0}' to Trash?").format(nwItemS.itemName),
|
self.tr("Move '{0}' to Trash?").format(nwItemS.itemName)
|
||||||
)
|
)
|
||||||
if not msgYes:
|
if not msgYes:
|
||||||
logger.info("Action cancelled by user")
|
logger.info("Action cancelled by user")
|
||||||
@@ -928,9 +920,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
if nwItemS.isRootType():
|
if nwItemS.isRootType():
|
||||||
# Only an empty ROOT folder can be deleted
|
# Only an empty ROOT folder can be deleted
|
||||||
if trItemS.childCount() > 0:
|
if trItemS.childCount() > 0:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr("Root folders can only be deleted when they are empty."))
|
||||||
"Root folders can only be deleted when they are empty."
|
|
||||||
), level=nwAlert.ERROR)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.debug("Permanently deleting root folder '%s'", tHandle)
|
logger.debug("Permanently deleting root folder '%s'", tHandle)
|
||||||
@@ -948,7 +938,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
if askFirst:
|
if askFirst:
|
||||||
msgYes = self.mainGui.askQuestion(
|
msgYes = SHARED.question(
|
||||||
self.tr("Permanently delete '{0}'?").format(nwItemS.itemName)
|
self.tr("Permanently delete '{0}'?").format(nwItemS.itemName)
|
||||||
)
|
)
|
||||||
if not msgYes:
|
if not msgYes:
|
||||||
@@ -1517,7 +1507,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
"""Convert a folder to a note or document."""
|
"""Convert a folder to a note or document."""
|
||||||
tItem = SHARED.project.tree[tHandle]
|
tItem = SHARED.project.tree[tHandle]
|
||||||
if tItem is not None and tItem.isFolderType():
|
if tItem is not None and tItem.isFolderType():
|
||||||
msgYes = self.mainGui.askQuestion(self.tr(
|
msgYes = SHARED.question(self.tr(
|
||||||
"Do you want to convert the folder to a {0}? "
|
"Do you want to convert the folder to a {0}? "
|
||||||
"This action cannot be reversed."
|
"This action cannot be reversed."
|
||||||
).format(trConst(nwLabels.LAYOUT_NAME[itemLayout])))
|
).format(trConst(nwLabels.LAYOUT_NAME[itemLayout])))
|
||||||
@@ -1559,7 +1549,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
mrgData = dlgMerge.getData()
|
mrgData = dlgMerge.getData()
|
||||||
mrgList = mrgData.get("finalItems", [])
|
mrgList = mrgData.get("finalItems", [])
|
||||||
if not mrgList:
|
if not mrgList:
|
||||||
self.mainGui.makeAlert(self.tr("No documents selected for merging."))
|
SHARED.info(self.tr("No documents selected for merging."))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Save the open document first, in case it's part of merge
|
# Save the open document first, in case it's part of merge
|
||||||
@@ -1582,9 +1572,9 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
docMerger.appendText(sHandle, True, mLabel)
|
docMerger.appendText(sHandle, True, mLabel)
|
||||||
|
|
||||||
if not docMerger.writeTargetDoc():
|
if not docMerger.writeTargetDoc():
|
||||||
self.mainGui.makeAlert(
|
SHARED.error(
|
||||||
self.tr("Could not write document content."),
|
self.tr("Could not write document content."),
|
||||||
info=docMerger.getError(), level=nwAlert.ERROR
|
info=docMerger.getError()
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -1646,9 +1636,9 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True)
|
self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True)
|
||||||
self._alertTreeChange(dHandle, flush=False)
|
self._alertTreeChange(dHandle, flush=False)
|
||||||
if not writeOk:
|
if not writeOk:
|
||||||
self.mainGui.makeAlert(
|
SHARED.error(
|
||||||
self.tr("Could not write document content."),
|
self.tr("Could not write document content."),
|
||||||
info=docSplit.getError(), level=nwAlert.ERROR
|
info=docSplit.getError()
|
||||||
)
|
)
|
||||||
|
|
||||||
if splitData.get("moveToTrash", False):
|
if splitData.get("moveToTrash", False):
|
||||||
@@ -1673,7 +1663,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
else:
|
else:
|
||||||
question = self.tr("Do you want to duplicate this item and all child items?")
|
question = self.tr("Do you want to duplicate this item and all child items?")
|
||||||
|
|
||||||
if not self.mainGui.askQuestion(question):
|
if not SHARED.question(question):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
docDup = DocDuplicator(SHARED.project)
|
docDup = DocDuplicator(SHARED.project)
|
||||||
@@ -1685,7 +1675,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
dupCount += 1
|
dupCount += 1
|
||||||
|
|
||||||
if dupCount != nItems:
|
if dupCount != nItems:
|
||||||
self.mainGui.makeAlert(self.tr("Could not duplicate all items."), level=nwAlert.WARN)
|
SHARED.warn(self.tr("Could not duplicate all items."))
|
||||||
|
|
||||||
self.saveTreeOrder()
|
self.saveTreeOrder()
|
||||||
|
|
||||||
@@ -1742,9 +1732,9 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
elif pHandle and pHandle in self._treeMap:
|
elif pHandle and pHandle in self._treeMap:
|
||||||
pItem = self._treeMap[pHandle]
|
pItem = self._treeMap[pHandle]
|
||||||
else:
|
else:
|
||||||
self.mainGui.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"There is nowhere to add item with name '{0}'."
|
"There is nowhere to add item with name '{0}'."
|
||||||
).format(nwItem.itemName), level=nwAlert.ERROR)
|
).format(nwItem.itemName))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
byIndex = -1
|
byIndex = -1
|
||||||
|
|||||||
+26
-78
@@ -31,7 +31,7 @@ from pathlib import Path
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QTimer, QThreadPool, pyqtSlot
|
from PyQt5.QtCore import Qt, QTimer, QThreadPool, pyqtSlot
|
||||||
from PyQt5.QtGui import QCloseEvent, QCursor, QIcon, QKeySequence, QPixmap
|
from PyQt5.QtGui import QCloseEvent, QCursor, QIcon, QKeySequence
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
qApp, QDialog, QFileDialog, QMainWindow, QMessageBox, QShortcut, QSplitter,
|
qApp, QDialog, QFileDialog, QMainWindow, QMessageBox, QShortcut, QSplitter,
|
||||||
QStackedWidget, QVBoxLayout, QWidget
|
QStackedWidget, QVBoxLayout, QWidget
|
||||||
@@ -63,10 +63,10 @@ from novelwriter.core.project import NWProject
|
|||||||
from novelwriter.core.coretools import ProjectBuilder
|
from novelwriter.core.coretools import ProjectBuilder
|
||||||
|
|
||||||
from novelwriter.enum import (
|
from novelwriter.enum import (
|
||||||
nwDocAction, nwDocMode, nwItemType, nwItemClass, nwAlert, nwWidget, nwView
|
nwDocAction, nwDocMode, nwItemType, nwItemClass, nwWidget, nwView
|
||||||
)
|
)
|
||||||
from novelwriter.common import getGuiItem, hexToInt
|
from novelwriter.common import getGuiItem, hexToInt
|
||||||
from novelwriter.constants import nwFiles, nwLabels, trConst
|
from novelwriter.constants import nwFiles
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -133,7 +133,6 @@ class GuiMain(QMainWindow):
|
|||||||
# =============
|
# =============
|
||||||
|
|
||||||
# Sizes
|
# Sizes
|
||||||
iPx = SHARED.theme.fontPixelSize
|
|
||||||
mPx = CONFIG.pxInt(4)
|
mPx = CONFIG.pxInt(4)
|
||||||
hWd = CONFIG.pxInt(4)
|
hWd = CONFIG.pxInt(4)
|
||||||
|
|
||||||
@@ -301,15 +300,6 @@ class GuiMain(QMainWindow):
|
|||||||
keyEscape.setKey(QKeySequence(Qt.Key_Escape))
|
keyEscape.setKey(QKeySequence(Qt.Key_Escape))
|
||||||
keyEscape.activated.connect(self._keyPressEscape)
|
keyEscape.activated.connect(self._keyPressEscape)
|
||||||
|
|
||||||
# Cache Alert Pixmaps
|
|
||||||
pxSize = (2*iPx, 2*iPx)
|
|
||||||
self.alertPix: dict[nwAlert, QPixmap] = {
|
|
||||||
nwAlert.INFO: SHARED.theme.getPixmap("alert_info", pxSize),
|
|
||||||
nwAlert.WARN: SHARED.theme.getPixmap("alert_warn", pxSize),
|
|
||||||
nwAlert.ERROR: SHARED.theme.getPixmap("alert_error", pxSize),
|
|
||||||
nwAlert.ASK: SHARED.theme.getPixmap("alert_question", pxSize),
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check that config loaded fine
|
# Check that config loaded fine
|
||||||
self.reportConfErr()
|
self.reportConfErr()
|
||||||
|
|
||||||
@@ -325,11 +315,11 @@ class GuiMain(QMainWindow):
|
|||||||
logger.debug("Ready: GUI")
|
logger.debug("Ready: GUI")
|
||||||
|
|
||||||
if __hexversion__[-2] == "a" and logger.getEffectiveLevel() > logging.DEBUG:
|
if __hexversion__[-2] == "a" and logger.getEffectiveLevel() > logging.DEBUG:
|
||||||
self.makeAlert(self.tr(
|
SHARED.warn(self.tr(
|
||||||
"You are running an untested development version of novelWriter. "
|
"You are running an untested development version of novelWriter. "
|
||||||
"Please be careful when working on a live project "
|
"Please be careful when working on a live project "
|
||||||
"and make sure you take regular backups."
|
"and make sure you take regular backups."
|
||||||
), level=nwAlert.WARN)
|
))
|
||||||
|
|
||||||
logger.info("novelWriter is ready ...")
|
logger.info("novelWriter is ready ...")
|
||||||
self.mainStatus.setStatusMessage(self.tr("novelWriter is ready ..."))
|
self.mainStatus.setStatusMessage(self.tr("novelWriter is ready ..."))
|
||||||
@@ -377,9 +367,9 @@ class GuiMain(QMainWindow):
|
|||||||
"""Create a new project via the new project wizard."""
|
"""Create a new project via the new project wizard."""
|
||||||
if SHARED.hasProject:
|
if SHARED.hasProject:
|
||||||
if not self.closeProject():
|
if not self.closeProject():
|
||||||
self.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"Cannot create a new project when another project is open."
|
"Cannot create a new project when another project is open."
|
||||||
), level=nwAlert.ERROR)
|
))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if projData is None:
|
if projData is None:
|
||||||
@@ -394,14 +384,14 @@ class GuiMain(QMainWindow):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if (Path(projPath) / nwFiles.PROJ_FILE).is_file():
|
if (Path(projPath) / nwFiles.PROJ_FILE).is_file():
|
||||||
self.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"A project already exists in that location. "
|
"A project already exists in that location. "
|
||||||
"Please choose another folder."
|
"Please choose another folder."
|
||||||
), level=nwAlert.ERROR)
|
))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.info("Creating new project")
|
logger.info("Creating new project")
|
||||||
nwProject = ProjectBuilder(self)
|
nwProject = ProjectBuilder()
|
||||||
if nwProject.buildProject(projData):
|
if nwProject.buildProject(projData):
|
||||||
self.openProject(projPath)
|
self.openProject(projPath)
|
||||||
else:
|
else:
|
||||||
@@ -419,7 +409,7 @@ class GuiMain(QMainWindow):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
if not isYes:
|
if not isYes:
|
||||||
msgYes = self.askQuestion("%s<br>%s" % (
|
msgYes = SHARED.question("%s<br>%s" % (
|
||||||
self.tr("Close the current project?"),
|
self.tr("Close the current project?"),
|
||||||
self.tr("Changes are saved automatically.")
|
self.tr("Changes are saved automatically.")
|
||||||
))
|
))
|
||||||
@@ -434,7 +424,7 @@ class GuiMain(QMainWindow):
|
|||||||
if SHARED.project.data.doBackup and CONFIG.backupOnClose:
|
if SHARED.project.data.doBackup and CONFIG.backupOnClose:
|
||||||
doBackup = True
|
doBackup = True
|
||||||
if CONFIG.askBeforeBackup:
|
if CONFIG.askBeforeBackup:
|
||||||
doBackup = self.askQuestion(self.tr("Backup the current project?"))
|
doBackup = SHARED.question(self.tr("Backup the current project?"))
|
||||||
|
|
||||||
if doBackup:
|
if doBackup:
|
||||||
SHARED.project.backupProject(False)
|
SHARED.project.backupProject(False)
|
||||||
@@ -505,9 +495,8 @@ class GuiMain(QMainWindow):
|
|||||||
except Exception:
|
except Exception:
|
||||||
lockDetails = ""
|
lockDetails = ""
|
||||||
|
|
||||||
if self.askQuestion(lockText, info=lockInfo, details=lockDetails, level=nwAlert.WARN):
|
if SHARED.question(lockText, info=lockInfo, details=lockDetails, warn=True):
|
||||||
SHARED.unlockProject()
|
if not SHARED.openProject(projFile, clearLock=True):
|
||||||
if not SHARED.openProject(projFile):
|
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
@@ -545,7 +534,7 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
# Check if we need to rebuild the index
|
# Check if we need to rebuild the index
|
||||||
if SHARED.project.index.indexBroken:
|
if SHARED.project.index.indexBroken:
|
||||||
self.makeAlert(self.tr("The project index is outdated or broken. Rebuilding index."))
|
SHARED.info(self.tr("The project index is outdated or broken. Rebuilding index."))
|
||||||
self.rebuildIndex()
|
self.rebuildIndex()
|
||||||
|
|
||||||
# Make sure the changed status is set to false on things opened
|
# Make sure the changed status is set to false on things opened
|
||||||
@@ -731,19 +720,19 @@ class GuiMain(QMainWindow):
|
|||||||
theText = inFile.read()
|
theText = inFile.read()
|
||||||
CONFIG.setLastPath(loadFile)
|
CONFIG.setLastPath(loadFile)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"Could not read file. The file must be an existing text file."
|
"Could not read file. The file must be an existing text file."
|
||||||
), level=nwAlert.ERROR, exc=exc)
|
), exc=exc)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.docEditor.docHandle is None:
|
if self.docEditor.docHandle is None:
|
||||||
self.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"Please open a document to import the text file into."
|
"Please open a document to import the text file into."
|
||||||
), level=nwAlert.ERROR)
|
))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not self.docEditor.isEmpty:
|
if not self.docEditor.isEmpty:
|
||||||
msgYes = self.askQuestion(self.tr(
|
msgYes = SHARED.question(self.tr(
|
||||||
"Importing the file will overwrite the current content of "
|
"Importing the file will overwrite the current content of "
|
||||||
"the document. Do you want to proceed?"
|
"the document. Do you want to proceed?"
|
||||||
))
|
))
|
||||||
@@ -845,7 +834,7 @@ class GuiMain(QMainWindow):
|
|||||||
qApp.restoreOverrideCursor()
|
qApp.restoreOverrideCursor()
|
||||||
|
|
||||||
if not beQuiet:
|
if not beQuiet:
|
||||||
self.makeAlert(self.tr("The project index has been successfully rebuilt."))
|
SHARED.info(self.tr("The project index has been successfully rebuilt."))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -891,7 +880,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.saveDocument()
|
self.saveDocument()
|
||||||
|
|
||||||
if dlgConf.needsRestart:
|
if dlgConf.needsRestart:
|
||||||
self.makeAlert(self.tr(
|
SHARED.info(self.tr(
|
||||||
"Some changes will not be applied until novelWriter has been restarted."
|
"Some changes will not be applied until novelWriter has been restarted."
|
||||||
))
|
))
|
||||||
|
|
||||||
@@ -1074,54 +1063,13 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def makeAlert(self, text: str, info: str = "", details: str = "",
|
|
||||||
level: nwAlert = nwAlert.INFO, exc: Exception | None = None) -> None:
|
|
||||||
"""Alert both the user and the logger at the same time."""
|
|
||||||
logText = " ".join(filter(None, [text, info, details]))
|
|
||||||
if level == nwAlert.INFO:
|
|
||||||
logger.info(logText, stacklevel=2)
|
|
||||||
elif level == nwAlert.WARN:
|
|
||||||
logger.warning(logText, stacklevel=2)
|
|
||||||
elif level == nwAlert.ERROR:
|
|
||||||
logger.error(logText, stacklevel=2, exc_info=exc)
|
|
||||||
|
|
||||||
if exc is not None:
|
|
||||||
tExc = f"{type(exc).__name__}: {str(exc)}"
|
|
||||||
info = f"{info}<br>{tExc}" if info else tExc
|
|
||||||
|
|
||||||
msgBox = QMessageBox(self)
|
|
||||||
msgBox.setWindowTitle(trConst(nwLabels.ALERT_NAME[level]))
|
|
||||||
msgBox.setText(text)
|
|
||||||
msgBox.setInformativeText(info)
|
|
||||||
msgBox.setDetailedText(details)
|
|
||||||
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
||||||
msgBox.setIconPixmap(self.alertPix[level])
|
|
||||||
msgBox.adjustSize()
|
|
||||||
msgBox.exec_()
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def askQuestion(self, text: str, info: str = "", details: str = "",
|
|
||||||
level: nwAlert = nwAlert.ASK) -> bool:
|
|
||||||
"""Ask the user a Yes/No question, and return the answer."""
|
|
||||||
msgBox = QMessageBox(self)
|
|
||||||
msgBox.setWindowTitle(trConst(nwLabels.ALERT_NAME[level]))
|
|
||||||
msgBox.setText(text)
|
|
||||||
msgBox.setInformativeText(info)
|
|
||||||
msgBox.setDetailedText(details)
|
|
||||||
msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
|
||||||
msgBox.setIconPixmap(self.alertPix[level])
|
|
||||||
msgBox.adjustSize()
|
|
||||||
msgBox.exec_()
|
|
||||||
return msgBox.result() == QMessageBox.Yes
|
|
||||||
|
|
||||||
def reportConfErr(self) -> bool:
|
def reportConfErr(self) -> bool:
|
||||||
"""Checks if the Config module has any errors to report, and let
|
"""Checks if the Config module has any errors to report, and let
|
||||||
the user know if this is the case. The Config module caches
|
the user know if this is the case. The Config module caches
|
||||||
errors since it is initialised before the GUI itself.
|
errors since it is initialised before the GUI itself.
|
||||||
"""
|
"""
|
||||||
if CONFIG.hasError:
|
if CONFIG.hasError:
|
||||||
self.makeAlert(CONFIG.errorText(), level=nwAlert.ERROR)
|
SHARED.error(CONFIG.errorText())
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -1132,7 +1080,7 @@ class GuiMain(QMainWindow):
|
|||||||
def closeMain(self) -> bool:
|
def closeMain(self) -> bool:
|
||||||
"""Save everything, and close novelWriter."""
|
"""Save everything, and close novelWriter."""
|
||||||
if SHARED.hasProject:
|
if SHARED.hasProject:
|
||||||
msgYes = self.askQuestion("%s<br>%s" % (
|
msgYes = SHARED.question("%s<br>%s" % (
|
||||||
self.tr("Do you want to exit novelWriter?"),
|
self.tr("Do you want to exit novelWriter?"),
|
||||||
self.tr("Changes are saved automatically.")
|
self.tr("Changes are saved automatically.")
|
||||||
))
|
))
|
||||||
@@ -1380,13 +1328,13 @@ class GuiMain(QMainWindow):
|
|||||||
"""
|
"""
|
||||||
tHandle, sTitle = SHARED.project.index.getTagSource(tag)
|
tHandle, sTitle = SHARED.project.index.getTagSource(tag)
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
self.makeAlert(self.tr(
|
SHARED.error(self.tr(
|
||||||
"Could not find the reference for tag '{0}'. It either doesn't "
|
"Could not find the reference for tag '{0}'. It either doesn't "
|
||||||
"exist, or the index is out of date. The index can be updated "
|
"exist, or the index is out of date. The index can be updated "
|
||||||
"from the Tools menu, or by pressing {1}."
|
"from the Tools menu, or by pressing {1}."
|
||||||
).format(
|
).format(
|
||||||
tag, "F9"
|
tag, "F9"
|
||||||
), level=nwAlert.ERROR)
|
))
|
||||||
return None, None
|
return None, None
|
||||||
return tHandle, sTitle
|
return tHandle, sTitle
|
||||||
|
|
||||||
|
|||||||
@@ -104,14 +104,14 @@ class SharedData(QObject):
|
|||||||
logger.debug("SharedData instance initialised")
|
logger.debug("SharedData instance initialised")
|
||||||
return
|
return
|
||||||
|
|
||||||
def openProject(self, path: str | Path) -> bool:
|
def openProject(self, path: str | Path, clearLock: bool = False) -> bool:
|
||||||
"""Open a project."""
|
"""Open a project."""
|
||||||
if self.project.isValid:
|
if self.project.isValid:
|
||||||
logger.error("A project is already open")
|
logger.error("A project is already open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._lockedBy = None
|
self._lockedBy = None
|
||||||
status = self.project.openProject(path)
|
status = self.project.openProject(path, clearLock=clearLock)
|
||||||
if status is False:
|
if status is False:
|
||||||
# We must cache the lock status before resetting the project
|
# We must cache the lock status before resetting the project
|
||||||
self._lockedBy = self.project.lockStatus
|
self._lockedBy = self.project.lockStatus
|
||||||
@@ -132,10 +132,6 @@ class SharedData(QObject):
|
|||||||
self._resetProject()
|
self._resetProject()
|
||||||
return
|
return
|
||||||
|
|
||||||
def unlockProject(self) -> bool:
|
|
||||||
"""Remove the project lock."""
|
|
||||||
return self.project.storage.clearLockFile()
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Alert Boxes
|
# Alert Boxes
|
||||||
##
|
##
|
||||||
@@ -171,10 +167,10 @@ class SharedData(QObject):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def question(self, text: str, info: str = "", details: str = "", warn: bool = False) -> bool:
|
def question(self, text: str, info: str = "", details: str = "", warn: bool = False) -> bool:
|
||||||
"""Open an error alert box."""
|
"""Open a question box."""
|
||||||
self._alert = _GuiAlert(self.mainGui, self.theme)
|
self._alert = _GuiAlert(self.mainGui, self.theme)
|
||||||
self._alert.setMessage(text, info, details)
|
self._alert.setMessage(text, info, details)
|
||||||
self._alert.setAlertType(_GuiAlert.ERROR, True)
|
self._alert.setAlertType(_GuiAlert.WARN if warn else _GuiAlert.ASK, True)
|
||||||
self._alert.exec_()
|
self._alert.exec_()
|
||||||
return self._alert.result() == QMessageBox.Yes
|
return self._alert.result() == QMessageBox.Yes
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from PyQt5.QtCore import QSize, QTimer, Qt, pyqtSlot
|
from PyQt5.QtCore import QSize, QTimer, Qt, pyqtSlot
|
||||||
@@ -36,7 +35,7 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.enum import nwAlert, nwBuildFmt
|
from novelwriter.enum import nwBuildFmt
|
||||||
from novelwriter.common import makeFileNameSafe
|
from novelwriter.common import makeFileNameSafe
|
||||||
from novelwriter.constants import nwLabels
|
from novelwriter.constants import nwLabels
|
||||||
from novelwriter.core.item import NWItem
|
from novelwriter.core.item import NWItem
|
||||||
@@ -44,9 +43,6 @@ from novelwriter.core.docbuild import NWBuildDocument
|
|||||||
from novelwriter.core.buildsettings import BuildSettings
|
from novelwriter.core.buildsettings import BuildSettings
|
||||||
from novelwriter.extensions.simpleprogress import NProgressSimple
|
from novelwriter.extensions.simpleprogress import NProgressSimple
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
|
||||||
from novelwriter.guimain import GuiMain
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -59,14 +55,12 @@ class GuiManuscriptBuild(QDialog):
|
|||||||
|
|
||||||
D_KEY = Qt.ItemDataRole.UserRole
|
D_KEY = Qt.ItemDataRole.UserRole
|
||||||
|
|
||||||
def __init__(self, parent: QWidget, mainGui: GuiMain, build: BuildSettings):
|
def __init__(self, parent: QWidget, build: BuildSettings):
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
logger.debug("Create: GuiManuscriptBuild")
|
logger.debug("Create: GuiManuscriptBuild")
|
||||||
self.setObjectName("GuiManuscriptBuild")
|
self.setObjectName("GuiManuscriptBuild")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
|
||||||
|
|
||||||
self._parent = parent
|
self._parent = parent
|
||||||
self._build = build
|
self._build = build
|
||||||
|
|
||||||
@@ -308,14 +302,14 @@ class GuiManuscriptBuild(QDialog):
|
|||||||
self.buildProgress.setValue(0)
|
self.buildProgress.setValue(0)
|
||||||
bPath = Path(self.buildPath.text())
|
bPath = Path(self.buildPath.text())
|
||||||
if not bPath.is_dir():
|
if not bPath.is_dir():
|
||||||
self.mainGui.makeAlert(self.tr("Output folder does not exist."), level=nwAlert.ERROR)
|
SHARED.error(self.tr("Output folder does not exist."))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
bExt = nwLabels.BUILD_EXT[bFormat]
|
bExt = nwLabels.BUILD_EXT[bFormat]
|
||||||
buildPath = (bPath / makeFileNameSafe(bName)).with_suffix(bExt)
|
buildPath = (bPath / makeFileNameSafe(bName)).with_suffix(bExt)
|
||||||
|
|
||||||
if buildPath.exists():
|
if buildPath.exists():
|
||||||
if not self.mainGui.askQuestion(
|
if not SHARED.question(
|
||||||
self.tr("The file already exists. Do you want to overwrite it?")
|
self.tr("The file already exists. Do you want to overwrite it?")
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ class GuiManuscript(QDialog):
|
|||||||
"""Delete the currently selected build settings entry."""
|
"""Delete the currently selected build settings entry."""
|
||||||
build = self._getSelectedBuild()
|
build = self._getSelectedBuild()
|
||||||
if build is not None:
|
if build is not None:
|
||||||
if self.mainGui.askQuestion(self.tr("Delete build '{0}'?".format(build.name))):
|
if SHARED.question(self.tr("Delete build '{0}'?".format(build.name))):
|
||||||
self._builds.removeBuild(build.buildID)
|
self._builds.removeBuild(build.buildID)
|
||||||
self._updateBuildsList()
|
self._updateBuildsList()
|
||||||
return
|
return
|
||||||
@@ -325,7 +325,7 @@ class GuiManuscript(QDialog):
|
|||||||
"""Open the build dialog and build the manuscript."""
|
"""Open the build dialog and build the manuscript."""
|
||||||
build = self._getSelectedBuild()
|
build = self._getSelectedBuild()
|
||||||
if isinstance(build, BuildSettings):
|
if isinstance(build, BuildSettings):
|
||||||
dlgBuild = GuiManuscriptBuild(self, self.mainGui, build)
|
dlgBuild = GuiManuscriptBuild(self, build)
|
||||||
dlgBuild.exec_()
|
dlgBuild.exec_()
|
||||||
|
|
||||||
# After the build is done, save build settings changes
|
# After the build is done, save build settings changes
|
||||||
|
|||||||
@@ -76,8 +76,6 @@ class GuiBuildSettings(QDialog):
|
|||||||
if CONFIG.osDarwin:
|
if CONFIG.osDarwin:
|
||||||
self.setWindowFlag(Qt.WindowType.Tool)
|
self.setWindowFlag(Qt.WindowType.Tool)
|
||||||
|
|
||||||
self.mainGui = mainGui
|
|
||||||
|
|
||||||
self._build = build
|
self._build = build
|
||||||
|
|
||||||
self.setWindowTitle(self.tr("Manuscript Build Settings"))
|
self.setWindowTitle(self.tr("Manuscript Build Settings"))
|
||||||
@@ -245,7 +243,7 @@ class GuiBuildSettings(QDialog):
|
|||||||
whether the user wants to save them.
|
whether the user wants to save them.
|
||||||
"""
|
"""
|
||||||
if self._build.changed:
|
if self._build.changed:
|
||||||
response = self.mainGui.askQuestion(self.tr(
|
response = SHARED.question(self.tr(
|
||||||
"Do you want to save your changes to '{0}'?".format(self._build.name)
|
"Do you want to save your changes to '{0}'?".format(self._build.name)
|
||||||
))
|
))
|
||||||
if response:
|
if response:
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.enum import nwAlert
|
|
||||||
from novelwriter.error import formatException
|
from novelwriter.error import formatException
|
||||||
from novelwriter.common import formatTime, checkInt, checkIntTuple, minmax
|
from novelwriter.common import formatTime, checkInt, checkIntTuple, minmax
|
||||||
from novelwriter.constants import nwConst
|
from novelwriter.constants import nwConst
|
||||||
@@ -72,8 +71,6 @@ class GuiWritingStats(QDialog):
|
|||||||
if CONFIG.osDarwin:
|
if CONFIG.osDarwin:
|
||||||
self.setWindowFlag(Qt.WindowType.Tool)
|
self.setWindowFlag(Qt.WindowType.Tool)
|
||||||
|
|
||||||
self.mainGui = mainGui
|
|
||||||
|
|
||||||
self.logData = []
|
self.logData = []
|
||||||
self.filterData = []
|
self.filterData = []
|
||||||
self.timeFilter = 0.0
|
self.timeFilter = 0.0
|
||||||
@@ -413,14 +410,14 @@ class GuiWritingStats(QDialog):
|
|||||||
|
|
||||||
# Report to user
|
# Report to user
|
||||||
if wSuccess:
|
if wSuccess:
|
||||||
self.mainGui.makeAlert(
|
SHARED.info(
|
||||||
self.tr("{0} file successfully written to:").format(textFmt),
|
self.tr("{0} file successfully written to:").format(textFmt),
|
||||||
info=savePath
|
info=savePath
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.mainGui.makeAlert(
|
SHARED.error(
|
||||||
self.tr("Failed to write {0} file.").format(textFmt),
|
self.tr("Failed to write {0} file.").format(textFmt),
|
||||||
info=errMsg, level=nwAlert.ERROR
|
info=errMsg
|
||||||
)
|
)
|
||||||
|
|
||||||
return wSuccess
|
return wSuccess
|
||||||
|
|||||||
@@ -31,31 +31,13 @@ class MockGuiMain(QWidget):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.mainStatus = MockStatusBar()
|
self.mainStatus = MockStatusBar()
|
||||||
self.projPath = ""
|
self.projPath = ""
|
||||||
|
|
||||||
# Test Variables
|
|
||||||
self.askResponse = True
|
|
||||||
self.lastAlert = ""
|
|
||||||
self.lastQuestion = ""
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def postLaunchTasks(self, cmdOpen):
|
def postLaunchTasks(self, cmdOpen):
|
||||||
return
|
return
|
||||||
|
|
||||||
def makeAlert(self, text, info="", detals="", level=0, exc=None):
|
|
||||||
assert isinstance(text, str)
|
|
||||||
print("%s: %s" % (str(level), text))
|
|
||||||
self.lastAlert = str(text)
|
|
||||||
return
|
|
||||||
|
|
||||||
def askQuestion(self, text, info="", details="", level=3):
|
|
||||||
print("Question: %s" % text)
|
|
||||||
self.lastQuestion = text
|
|
||||||
return self.askResponse
|
|
||||||
|
|
||||||
def setStatus(self, theMessage):
|
def setStatus(self, theMessage):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -72,16 +54,6 @@ class MockGuiMain(QWidget):
|
|||||||
def close(self):
|
def close(self):
|
||||||
return "close"
|
return "close"
|
||||||
|
|
||||||
# Test Functions
|
|
||||||
|
|
||||||
def undo(self):
|
|
||||||
self.askResponse = True
|
|
||||||
return
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
self.lastAlert = ""
|
|
||||||
return
|
|
||||||
|
|
||||||
# END Class MockGuiMain
|
# END Class MockGuiMain
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -410,7 +410,7 @@ def testCoreTools_NewMinimal(monkeypatch, fncPath, tstPaths, mockGUI, mockRnd):
|
|||||||
testFile = tstPaths.outDir / "coreTools_NewMinimal_nwProject.nwx"
|
testFile = tstPaths.outDir / "coreTools_NewMinimal_nwProject.nwx"
|
||||||
compFile = tstPaths.refDir / "coreTools_NewMinimal_nwProject.nwx"
|
compFile = tstPaths.refDir / "coreTools_NewMinimal_nwProject.nwx"
|
||||||
|
|
||||||
projBuild = ProjectBuilder(mockGUI)
|
projBuild = ProjectBuilder()
|
||||||
|
|
||||||
# Setting no data should fail
|
# Setting no data should fail
|
||||||
assert projBuild.buildProject({}) is False
|
assert projBuild.buildProject({}) is False
|
||||||
@@ -432,7 +432,7 @@ def testCoreTools_NewMinimal(monkeypatch, fncPath, tstPaths, mockGUI, mockRnd):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTools_NewCustomA(monkeypatch, fncPath, tstPaths, mockGUI, mockRnd):
|
def testCoreTools_NewCustomA(monkeypatch, fncPath, tstPaths, mockRnd):
|
||||||
"""Create a new project from a project wizard dictionary.
|
"""Create a new project from a project wizard dictionary.
|
||||||
Custom type with chapters and scenes.
|
Custom type with chapters and scenes.
|
||||||
"""
|
"""
|
||||||
@@ -460,7 +460,7 @@ def testCoreTools_NewCustomA(monkeypatch, fncPath, tstPaths, mockGUI, mockRnd):
|
|||||||
"numScenes": 3,
|
"numScenes": 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
projBuild = ProjectBuilder(mockGUI)
|
projBuild = ProjectBuilder()
|
||||||
assert projBuild.buildProject(projData) is True
|
assert projBuild.buildProject(projData) is True
|
||||||
|
|
||||||
copyfile(projFile, testFile)
|
copyfile(projFile, testFile)
|
||||||
@@ -470,7 +470,7 @@ def testCoreTools_NewCustomA(monkeypatch, fncPath, tstPaths, mockGUI, mockRnd):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTools_NewCustomB(monkeypatch, fncPath, tstPaths, mockGUI, mockRnd):
|
def testCoreTools_NewCustomB(monkeypatch, fncPath, tstPaths, mockRnd):
|
||||||
"""Create a new project from a project wizard dictionary.
|
"""Create a new project from a project wizard dictionary.
|
||||||
Custom type without chapters, but with scenes.
|
Custom type without chapters, but with scenes.
|
||||||
"""
|
"""
|
||||||
@@ -498,7 +498,7 @@ def testCoreTools_NewCustomB(monkeypatch, fncPath, tstPaths, mockGUI, mockRnd):
|
|||||||
"numScenes": 6,
|
"numScenes": 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
projBuild = ProjectBuilder(mockGUI)
|
projBuild = ProjectBuilder()
|
||||||
assert projBuild.buildProject(projData) is True
|
assert projBuild.buildProject(projData) is True
|
||||||
|
|
||||||
copyfile(projFile, testFile)
|
copyfile(projFile, testFile)
|
||||||
@@ -508,7 +508,7 @@ def testCoreTools_NewCustomB(monkeypatch, fncPath, tstPaths, mockGUI, mockRnd):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTools_NewSample(monkeypatch, fncPath, tstPaths, mockGUI):
|
def testCoreTools_NewSample(monkeypatch, fncPath, tstPaths):
|
||||||
"""Check that we can create a new project can be created from the
|
"""Check that we can create a new project can be created from the
|
||||||
provided sample project via a zip file.
|
provided sample project via a zip file.
|
||||||
"""
|
"""
|
||||||
@@ -522,7 +522,7 @@ def testCoreTools_NewSample(monkeypatch, fncPath, tstPaths, mockGUI):
|
|||||||
"popCustom": False,
|
"popCustom": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
projBuild = ProjectBuilder(mockGUI)
|
projBuild = ProjectBuilder()
|
||||||
|
|
||||||
# No path set
|
# No path set
|
||||||
assert projBuild.buildProject({"popSample": True}) is False
|
assert projBuild.buildProject({"popSample": True}) is False
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
|
|||||||
|
|
||||||
from tools import C, writeFile, buildTestProject
|
from tools import C, writeFile, buildTestProject
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.enum import nwDocAction, nwDocInsert
|
from novelwriter.enum import nwDocAction, nwDocInsert
|
||||||
from novelwriter.constants import nwKeyWords, nwUnicode
|
from novelwriter.constants import nwKeyWords, nwUnicode
|
||||||
from novelwriter.gui.doceditor import GuiDocEditor
|
from novelwriter.gui.doceditor import GuiDocEditor
|
||||||
@@ -653,21 +653,10 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd):
|
|||||||
# Reveal File Location
|
# Reveal File Location
|
||||||
# ====================
|
# ====================
|
||||||
|
|
||||||
theMessage = ""
|
|
||||||
|
|
||||||
def recordMsg(*args, **kwargs):
|
|
||||||
nonlocal theMessage
|
|
||||||
theMessage = "%s|%s" % (args[0], kwargs["info"])
|
|
||||||
return None
|
|
||||||
|
|
||||||
assert not theMessage
|
|
||||||
monkeypatch.setattr(nwGUI, "makeAlert", recordMsg)
|
|
||||||
nwGUI.mainMenu.aFileDetails.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFileDetails.activate(QAction.Trigger)
|
||||||
|
path = str(projPath / "content" / "000000000000f.nwd")
|
||||||
theBits = theMessage.split("|")
|
logMsg = SHARED.alert.logMessage if SHARED.alert else ""
|
||||||
assert len(theBits) == 2
|
assert logMsg == f"The currently open file is saved in: {path}"
|
||||||
assert theBits[0] == "The currently open file is saved in:"
|
|
||||||
assert theBits[1] == str(projPath / "content" / "000000000000f.nwd")
|
|
||||||
|
|
||||||
# qtbot.stop()
|
# qtbot.stop()
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ def testManuscriptBuild_Main(
|
|||||||
build = BuildSettings()
|
build = BuildSettings()
|
||||||
build.setLastPath(fncPath)
|
build.setLastPath(fncPath)
|
||||||
|
|
||||||
manus = GuiManuscriptBuild(nwGUI, nwGUI, build)
|
manus = GuiManuscriptBuild(nwGUI, build)
|
||||||
manus.show()
|
manus.show()
|
||||||
|
|
||||||
# Check initial values
|
# Check initial values
|
||||||
@@ -101,7 +101,7 @@ def testManuscriptBuild_Main(
|
|||||||
# Error Handling
|
# Error Handling
|
||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
manus = GuiManuscriptBuild(nwGUI, nwGUI, build)
|
manus = GuiManuscriptBuild(nwGUI, build)
|
||||||
manus.show()
|
manus.show()
|
||||||
|
|
||||||
# Name, path and format should be remembered
|
# Name, path and format should be remembered
|
||||||
|
|||||||
Reference in New Issue
Block a user