Merge branch 'main' into merge_1.6.6
This commit is contained in:
@@ -421,6 +421,12 @@ class PagedDialog(QDialog):
|
|||||||
self._buttonBox.addWidget(buttonBar)
|
self._buttonBox.addWidget(buttonBar)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def setCurrentWidget(self, widget):
|
||||||
|
"""Forward the changing of tab to the QTabWidget.
|
||||||
|
"""
|
||||||
|
self._tabBox.setCurrentWidget(widget)
|
||||||
|
return
|
||||||
|
|
||||||
# END Class PagedDialog
|
# END Class PagedDialog
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,12 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class GuiProjectSettings(PagedDialog):
|
class GuiProjectSettings(PagedDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
TAB_MAIN = 0
|
||||||
|
TAB_STATUS = 1
|
||||||
|
TAB_IMPORT = 2
|
||||||
|
TAB_REPLACE = 3
|
||||||
|
|
||||||
|
def __init__(self, mainGui, focusTab=TAB_MAIN):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectSettings ...")
|
logger.debug("Initialising GuiProjectSettings ...")
|
||||||
@@ -83,12 +88,19 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
self.addControls(self.buttonBox)
|
self.addControls(self.buttonBox)
|
||||||
|
|
||||||
# Flags
|
# Flags
|
||||||
self.spellChanged = False
|
self._spellChanged = False
|
||||||
|
|
||||||
|
# Focus Tab
|
||||||
|
self._focusTab(focusTab)
|
||||||
|
|
||||||
logger.debug("GuiProjectSettings initialisation complete")
|
logger.debug("GuiProjectSettings initialisation complete")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@property
|
||||||
|
def spellChanged(self):
|
||||||
|
return self._spellChanged
|
||||||
|
|
||||||
##
|
##
|
||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
@@ -108,7 +120,7 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
self.theProject.setProjBackup(doBackup)
|
self.theProject.setProjBackup(doBackup)
|
||||||
|
|
||||||
# Remember this as updating spell dictionary can be expensive
|
# Remember this as updating spell dictionary can be expensive
|
||||||
self.spellChanged = self.theProject.setSpellLang(spellLang)
|
self._spellChanged = self.theProject.setSpellLang(spellLang)
|
||||||
|
|
||||||
if self.tabStatus.colChanged:
|
if self.tabStatus.colChanged:
|
||||||
newList, delList = self.tabStatus.getNewList()
|
newList, delList = self.tabStatus.getNewList()
|
||||||
@@ -141,6 +153,19 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def _focusTab(self, tab):
|
||||||
|
"""Change which is the focused tab.
|
||||||
|
"""
|
||||||
|
if tab == self.TAB_MAIN:
|
||||||
|
self.setCurrentWidget(self.tabMain)
|
||||||
|
elif tab == self.TAB_STATUS:
|
||||||
|
self.setCurrentWidget(self.tabStatus)
|
||||||
|
elif tab == self.TAB_IMPORT:
|
||||||
|
self.setCurrentWidget(self.tabImport)
|
||||||
|
elif tab == self.TAB_REPLACE:
|
||||||
|
self.setCurrentWidget(self.tabReplace)
|
||||||
|
return
|
||||||
|
|
||||||
def _saveGuiSettings(self):
|
def _saveGuiSettings(self):
|
||||||
"""Save GUI settings.
|
"""Save GUI settings.
|
||||||
"""
|
"""
|
||||||
|
|||||||
+62
-55
@@ -41,8 +41,8 @@ from PyQt5.QtWidgets import (
|
|||||||
|
|
||||||
from novelwriter.core import DocMerger, DocSplitter
|
from novelwriter.core import DocMerger, DocSplitter
|
||||||
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert
|
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert
|
||||||
from novelwriter.dialogs import GuiDocMerge, GuiDocSplit, GuiEditLabel
|
from novelwriter.dialogs import GuiDocMerge, GuiDocSplit, GuiEditLabel, GuiProjectSettings
|
||||||
from novelwriter.constants import nwHeaders, trConst, nwLabels
|
from novelwriter.constants import nwHeaders, nwUnicode, trConst, nwLabels
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -62,6 +62,9 @@ class GuiProjectView(QWidget):
|
|||||||
selectedItemChanged = pyqtSignal(str)
|
selectedItemChanged = pyqtSignal(str)
|
||||||
openDocumentRequest = pyqtSignal(str, Enum, int, str)
|
openDocumentRequest = pyqtSignal(str, Enum, int, str)
|
||||||
|
|
||||||
|
# Requests for the main GUI
|
||||||
|
projectSettingsRequest = pyqtSignal(int)
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
@@ -102,9 +105,6 @@ class GuiProjectView(QWidget):
|
|||||||
self.keyContext.activated.connect(lambda: self.projTree.openContextOnSelected())
|
self.keyContext.activated.connect(lambda: self.projTree.openContextOnSelected())
|
||||||
|
|
||||||
# Function Mappings
|
# Function Mappings
|
||||||
self.revealNewTreeItem = self.projTree.revealNewTreeItem
|
|
||||||
self.renameTreeItem = self.projTree.renameTreeItem
|
|
||||||
self.getTreeFromHandle = self.projTree.getTreeFromHandle
|
|
||||||
self.emptyTrash = self.projTree.emptyTrash
|
self.emptyTrash = self.projTree.emptyTrash
|
||||||
self.requestDeleteItem = self.projTree.requestDeleteItem
|
self.requestDeleteItem = self.projTree.requestDeleteItem
|
||||||
self.setTreeItemValues = self.projTree.setTreeItemValues
|
self.setTreeItemValues = self.projTree.setTreeItemValues
|
||||||
@@ -161,6 +161,16 @@ class GuiProjectView(QWidget):
|
|||||||
"""
|
"""
|
||||||
return self.projTree.hasFocus()
|
return self.projTree.hasFocus()
|
||||||
|
|
||||||
|
def renameTreeItem(self, tHandle=None):
|
||||||
|
"""External request to rename an item or the currently selected
|
||||||
|
item. This is triggered by the global menu or keyboard shortcut.
|
||||||
|
"""
|
||||||
|
if tHandle is None:
|
||||||
|
tHandle = self.projTree.getSelectedHandle()
|
||||||
|
if tHandle:
|
||||||
|
return self.projTree.renameTreeItem(tHandle)
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Public Slots
|
# Public Slots
|
||||||
##
|
##
|
||||||
@@ -1111,10 +1121,12 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
open a context menu in-place.
|
open a context menu in-place.
|
||||||
"""
|
"""
|
||||||
tItem = None
|
tItem = None
|
||||||
|
hasChild = False
|
||||||
selItem = self.itemAt(clickPos)
|
selItem = self.itemAt(clickPos)
|
||||||
if isinstance(selItem, QTreeWidgetItem):
|
if isinstance(selItem, QTreeWidgetItem):
|
||||||
tHandle = selItem.data(self.C_NAME, Qt.UserRole)
|
tHandle = selItem.data(self.C_NAME, Qt.UserRole)
|
||||||
tItem = self.theProject.tree[tHandle]
|
tItem = self.theProject.tree[tHandle]
|
||||||
|
hasChild = selItem.childCount() > 0
|
||||||
|
|
||||||
if tItem is None:
|
if tItem is None:
|
||||||
logger.debug("No item found")
|
logger.debug("No item found")
|
||||||
@@ -1128,9 +1140,8 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
trashHandle = self.theProject.tree.trashRoot()
|
trashHandle = self.theProject.tree.trashRoot()
|
||||||
if tItem.itemHandle == trashHandle and trashHandle is not None:
|
if tItem.itemHandle == trashHandle and trashHandle is not None:
|
||||||
# The trash folder only has one option
|
# The trash folder only has one option
|
||||||
ctxMenu.addAction(
|
aEmptyTrash = ctxMenu.addAction(self.tr("Empty Trash"))
|
||||||
self.tr("Empty Trash"), lambda: self.emptyTrash()
|
aEmptyTrash.triggered.connect(lambda: self.emptyTrash())
|
||||||
)
|
|
||||||
ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
|
ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1140,15 +1151,14 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
isRoot = tItem.isRootType()
|
isRoot = tItem.isRootType()
|
||||||
isFolder = tItem.isFolderType()
|
isFolder = tItem.isFolderType()
|
||||||
isFile = tItem.isFileType()
|
isFile = tItem.isFileType()
|
||||||
hasChild = selItem.childCount() > 0
|
|
||||||
|
|
||||||
if isFile:
|
if isFile:
|
||||||
ctxMenu.addAction(
|
aOpenDoc = ctxMenu.addAction(self.tr("Open Document"))
|
||||||
self.tr("Open Document"),
|
aOpenDoc.triggered.connect(
|
||||||
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, -1, "")
|
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, -1, "")
|
||||||
)
|
)
|
||||||
ctxMenu.addAction(
|
aViewDoc = ctxMenu.addAction(self.tr("View Document"))
|
||||||
self.tr("View Document"),
|
aViewDoc.triggered.connect(
|
||||||
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "")
|
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "")
|
||||||
)
|
)
|
||||||
ctxMenu.addSeparator()
|
ctxMenu.addSeparator()
|
||||||
@@ -1156,29 +1166,40 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
# Edit Item Settings
|
# Edit Item Settings
|
||||||
# ==================
|
# ==================
|
||||||
|
|
||||||
ctxMenu.addAction(
|
aLabel = ctxMenu.addAction(self.tr("Change Label"))
|
||||||
self.tr("Change Label"), lambda: self.renameTreeItem(tHandle)
|
aLabel.triggered.connect(lambda: self.renameTreeItem(tHandle))
|
||||||
)
|
|
||||||
|
|
||||||
if isFile:
|
if isFile:
|
||||||
ctxMenu.addAction(
|
aActive = ctxMenu.addAction(self.tr("Toggle Active"))
|
||||||
self.tr("Toggle Active"), lambda: self._toggleItemActive(tHandle)
|
aActive.triggered.connect(lambda: self._toggleItemActive(tHandle))
|
||||||
)
|
|
||||||
|
|
||||||
|
checkMark = f" ({nwUnicode.U_CHECK})"
|
||||||
if tItem.isNovelLike():
|
if tItem.isNovelLike():
|
||||||
mStatus = ctxMenu.addMenu(self.tr("Set Status to ..."))
|
mStatus = ctxMenu.addMenu(self.tr("Set Status to ..."))
|
||||||
for n, (key, entry) in enumerate(self.theProject.statusItems.items()):
|
for n, (key, entry) in enumerate(self.theProject.statusItems.items()):
|
||||||
aStatus = mStatus.addAction(entry["icon"], entry["name"])
|
entryName = entry["name"] + (checkMark if tItem.itemStatus == key else "")
|
||||||
|
aStatus = mStatus.addAction(entry["icon"], entryName)
|
||||||
aStatus.triggered.connect(
|
aStatus.triggered.connect(
|
||||||
lambda n, key=key: self._changeItemStatus(tHandle, key)
|
lambda n, key=key: self._changeItemStatus(tHandle, key)
|
||||||
)
|
)
|
||||||
|
mStatus.addSeparator()
|
||||||
|
aManage1 = mStatus.addAction("Manage Labels ...")
|
||||||
|
aManage1.triggered.connect(
|
||||||
|
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.TAB_STATUS)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
mImport = ctxMenu.addMenu(self.tr("Set Importance to ..."))
|
mImport = ctxMenu.addMenu(self.tr("Set Importance to ..."))
|
||||||
for n, (key, entry) in enumerate(self.theProject.importItems.items()):
|
for n, (key, entry) in enumerate(self.theProject.importItems.items()):
|
||||||
aImport = mImport.addAction(entry["icon"], entry["name"])
|
entryName = entry["name"] + (checkMark if tItem.itemImport == key else "")
|
||||||
|
aImport = mImport.addAction(entry["icon"], entryName)
|
||||||
aImport.triggered.connect(
|
aImport.triggered.connect(
|
||||||
lambda n, key=key: self._changeItemImport(tHandle, key)
|
lambda n, key=key: self._changeItemImport(tHandle, key)
|
||||||
)
|
)
|
||||||
|
mImport.addSeparator()
|
||||||
|
aManage2 = mImport.addAction("Manage Labels ...")
|
||||||
|
aManage2.triggered.connect(
|
||||||
|
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.TAB_IMPORT)
|
||||||
|
)
|
||||||
|
|
||||||
# Transform Item
|
# Transform Item
|
||||||
# ==============
|
# ==============
|
||||||
@@ -1193,38 +1214,30 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
isNoteFile = isFile and tItem.isNoteLayout()
|
isNoteFile = isFile and tItem.isNoteLayout()
|
||||||
|
|
||||||
if (isNoteFile or isFolder) and tItem.documentAllowed():
|
if (isNoteFile or isFolder) and tItem.documentAllowed():
|
||||||
mTrans.addAction(
|
aConvert1 = mTrans.addAction(self.tr("Convert to {0}").format(trDoc))
|
||||||
self.tr("Convert to {0}").format(trDoc),
|
aConvert1.triggered.connect(
|
||||||
lambda: self._changeItemLayout(tHandle, nwItemLayout.DOCUMENT)
|
lambda: self._changeItemLayout(tHandle, nwItemLayout.DOCUMENT)
|
||||||
)
|
)
|
||||||
|
|
||||||
if isDocFile or isFolder:
|
if isDocFile or isFolder:
|
||||||
mTrans.addAction(
|
aConvert2 = mTrans.addAction(self.tr("Convert to {0}").format(trNote))
|
||||||
self.tr("Convert to {0}").format(trNote),
|
aConvert2.triggered.connect(
|
||||||
lambda: self._changeItemLayout(tHandle, nwItemLayout.NOTE)
|
lambda: self._changeItemLayout(tHandle, nwItemLayout.NOTE)
|
||||||
)
|
)
|
||||||
|
|
||||||
if hasChild and isFile:
|
if hasChild and isFile:
|
||||||
mTrans.addAction(
|
aMerge1 = mTrans.addAction(self.tr("Merge Child Items into Self"))
|
||||||
self.tr("Merge Child Items into Self"),
|
aMerge1.triggered.connect(lambda: self._mergeDocuments(tHandle, False))
|
||||||
lambda: self._mergeDocuments(tHandle, False)
|
aMerge2 = mTrans.addAction(self.tr("Merge Child Items into New"))
|
||||||
)
|
aMerge2.triggered.connect(lambda: self._mergeDocuments(tHandle, True))
|
||||||
mTrans.addAction(
|
|
||||||
self.tr("Merge Child Items into New"),
|
|
||||||
lambda: self._mergeDocuments(tHandle, True)
|
|
||||||
)
|
|
||||||
|
|
||||||
if hasChild and isFolder:
|
if hasChild and isFolder:
|
||||||
mTrans.addAction(
|
aMerge3 = mTrans.addAction(self.tr("Merge Documents in Folder"))
|
||||||
self.tr("Merge Documents in Folder"),
|
aMerge3.triggered.connect(lambda: self._mergeDocuments(tHandle, True))
|
||||||
lambda: self._mergeDocuments(tHandle, True)
|
|
||||||
)
|
|
||||||
|
|
||||||
if isFile:
|
if isFile:
|
||||||
mTrans.addAction(
|
aSplit1 = mTrans.addAction(self.tr("Split Document by Headers"))
|
||||||
self.tr("Split Document by Headers"),
|
aSplit1.triggered.connect(lambda: self._splitDocument(tHandle))
|
||||||
lambda: self._splitDocument(tHandle)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Expand/Collapse/Delete
|
# Expand/Collapse/Delete
|
||||||
# ======================
|
# ======================
|
||||||
@@ -1232,23 +1245,17 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
ctxMenu.addSeparator()
|
ctxMenu.addSeparator()
|
||||||
|
|
||||||
if hasChild:
|
if hasChild:
|
||||||
ctxMenu.addAction(
|
aExpand = ctxMenu.addAction(self.tr("Expand All"))
|
||||||
self.tr("Expand All"),
|
aExpand.triggered.connect(lambda: self.setExpandedFromHandle(tHandle, True))
|
||||||
lambda: self.setExpandedFromHandle(tHandle, True)
|
aCollapse = ctxMenu.addAction(self.tr("Collapse All"))
|
||||||
)
|
aCollapse.triggered.connect(lambda: self.setExpandedFromHandle(tHandle, False))
|
||||||
ctxMenu.addAction(
|
|
||||||
self.tr("Collapse All"),
|
|
||||||
lambda: self.setExpandedFromHandle(tHandle, False)
|
|
||||||
)
|
|
||||||
|
|
||||||
if tItem.itemClass == nwItemClass.TRASH or isRoot or (isFolder and not hasChild):
|
if tItem.itemClass == nwItemClass.TRASH or isRoot or (isFolder and not hasChild):
|
||||||
ctxMenu.addAction(
|
aDelete = ctxMenu.addAction(self.tr("Delete Permanently"))
|
||||||
self.tr("Delete Permanently"), lambda: self.permanentlyDeleteItem(tHandle)
|
aDelete.triggered.connect(lambda: self.permanentlyDeleteItem(tHandle))
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
ctxMenu.addAction(
|
aMoveTrash = ctxMenu.addAction(self.tr("Move to Trash"))
|
||||||
self.tr("Move to Trash"), lambda: self.moveItemToTrash(tHandle)
|
aMoveTrash.triggered.connect(lambda: self.moveItemToTrash(tHandle))
|
||||||
)
|
|
||||||
|
|
||||||
# Show Context Menu
|
# Show Context Menu
|
||||||
ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
|
ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
|
||||||
|
|||||||
+8
-10
@@ -208,6 +208,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
||||||
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
|
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
|
||||||
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
|
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
|
||||||
|
self.projView.projectSettingsRequest.connect(self.showProjectSettingsDialog)
|
||||||
|
|
||||||
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
||||||
self.novelView.openDocumentRequest.connect(self._openDocument)
|
self.novelView.openDocumentRequest.connect(self._openDocument)
|
||||||
@@ -806,15 +807,11 @@ class GuiMain(QMainWindow):
|
|||||||
logger.error("No project open")
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if tHandle is None:
|
if tHandle is None and (self.docEditor.anyFocus() or self.isFocusMode):
|
||||||
if self.docEditor.anyFocus() or self.isFocusMode:
|
tHandle = self.docEditor.docHandle()
|
||||||
tHandle = self.docEditor.docHandle()
|
self.projView.renameTreeItem(tHandle)
|
||||||
else:
|
|
||||||
tHandle = self.projView.getSelectedHandle()
|
|
||||||
if tHandle:
|
|
||||||
return self.projView.renameTreeItem(tHandle)
|
|
||||||
|
|
||||||
return False
|
return True
|
||||||
|
|
||||||
def rebuildTrees(self):
|
def rebuildTrees(self):
|
||||||
"""Rebuild the project tree.
|
"""Rebuild the project tree.
|
||||||
@@ -921,14 +918,15 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def showProjectSettingsDialog(self):
|
@pyqtSlot(int)
|
||||||
|
def showProjectSettingsDialog(self, focusTab=GuiProjectSettings.TAB_MAIN):
|
||||||
"""Open the project settings dialog.
|
"""Open the project settings dialog.
|
||||||
"""
|
"""
|
||||||
if not self.hasProject:
|
if not self.hasProject:
|
||||||
logger.error("No project open")
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
dlgProj = GuiProjectSettings(self)
|
dlgProj = GuiProjectSettings(self, focusTab=focusTab)
|
||||||
dlgProj.exec_()
|
dlgProj.exec_()
|
||||||
|
|
||||||
if dlgProj.result() == QDialog.Accepted:
|
if dlgProj.result() == QDialog.Accepted:
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:21:59">
|
|
||||||
<project>
|
|
||||||
<name>Project Name</name>
|
|
||||||
<title>Project Title</title>
|
|
||||||
<author>Jane Doe</author>
|
|
||||||
<author>John Doh</author>
|
|
||||||
<saveCount>1</saveCount>
|
|
||||||
<autoCount>1</autoCount>
|
|
||||||
<editTime>0</editTime>
|
|
||||||
</project>
|
|
||||||
<settings>
|
|
||||||
<doBackup>True</doBackup>
|
|
||||||
<language>None</language>
|
|
||||||
<spellCheck>False</spellCheck>
|
|
||||||
<spellLang>en</spellLang>
|
|
||||||
<lastEdited>None</lastEdited>
|
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>None</lastNovel>
|
|
||||||
<lastOutline>None</lastOutline>
|
|
||||||
<lastWordCount>9</lastWordCount>
|
|
||||||
<novelWordCount>9</novelWordCount>
|
|
||||||
<notesWordCount>0</notesWordCount>
|
|
||||||
<autoReplace>
|
|
||||||
<entry key="A">B</entry>
|
|
||||||
<entry key="C">D</entry>
|
|
||||||
<entry key="This">With This Stuff</entry>
|
|
||||||
</autoReplace>
|
|
||||||
<titleFormat>
|
|
||||||
<title>%title%</title>
|
|
||||||
<chapter>%title%</chapter>
|
|
||||||
<unnumbered>%title%</unnumbered>
|
|
||||||
<scene>* * *</scene>
|
|
||||||
<section></section>
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
|
||||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
|
||||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
|
||||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
|
||||||
<entry key="s000010" count="0" red="20" green="30" blue="40">Final</entry>
|
|
||||||
</status>
|
|
||||||
<importance>
|
|
||||||
<entry key="i000004" count="3" red="100" green="100" blue="100">New</entry>
|
|
||||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
|
||||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
|
||||||
<entry key="i000011" count="0" red="100" green="100" blue="100">Final</entry>
|
|
||||||
</importance>
|
|
||||||
</settings>
|
|
||||||
<content count="8">
|
|
||||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
|
||||||
<meta expanded="False" mainHeading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
|
||||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s000000" import="i000004">New Chapter</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
|
||||||
<meta expanded="False" mainHeading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
|
||||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
|
||||||
<meta expanded="False" mainHeading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
|
||||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
|
||||||
</item>
|
|
||||||
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s000000" import="i000004">Plot</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000a" parent="None" root="000000000000a" order="2" type="ROOT" class="CHARACTER">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s000000" import="i000004">Characters</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s000000" import="i000004">World</name>
|
|
||||||
</item>
|
|
||||||
</content>
|
|
||||||
</novelWriterXML>
|
|
||||||
@@ -19,56 +19,46 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from shutil import copyfile
|
from novelwriter.enum import nwItemType
|
||||||
from tools import cmpFiles, getGuiItem, buildTestProject
|
from tools import C, getGuiItem, buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtGui import QColor
|
from PyQt5.QtGui import QColor
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QDialog, QAction, QMessageBox, QColorDialog
|
from PyQt5.QtWidgets import QDialog, QAction, QMessageBox, QColorDialog
|
||||||
|
|
||||||
|
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||||
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
||||||
|
|
||||||
keyDelay = 2
|
keyDelay = 2
|
||||||
typeDelay = 1
|
typeDelay = 1
|
||||||
stepDelay = 20
|
stepDelay = 20
|
||||||
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
|
|
||||||
importKeys = ["i000004", "i000005", "i000006", "i000007"]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgProjSettings_Dialog(
|
def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
|
||||||
qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDir, refDir, mockRnd
|
"""Test the main dialog class. Saving settings is not tested in this
|
||||||
):
|
test, but are instead tested in the individual tab tests.
|
||||||
"""Test the full project settings dialog.
|
|
||||||
"""
|
"""
|
||||||
projFile = os.path.join(fncProj, "nwProject.nwx")
|
|
||||||
testFile = os.path.join(outDir, "guiProjSettings_Dialog_nwProject.nwx")
|
|
||||||
compFile = os.path.join(refDir, "guiProjSettings_Dialog_nwProject.nwx")
|
|
||||||
|
|
||||||
# Block message box
|
# Block message box
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
||||||
|
|
||||||
|
# Block the GUI blocking thread
|
||||||
|
monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *a: None)
|
||||||
|
monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.Accepted)
|
||||||
|
monkeypatch.setattr(GuiProjectSettings, "spellChanged", lambda *a: True)
|
||||||
|
|
||||||
# Check that we cannot open when there is no project
|
# Check that we cannot open when there is no project
|
||||||
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
||||||
assert getGuiItem("GuiProjectSettings") is None
|
assert getGuiItem("GuiProjectSettings") is None
|
||||||
|
|
||||||
# Create new project
|
# Pretend we have a project
|
||||||
buildTestProject(nwGUI, fncProj)
|
nwGUI.hasProject = True
|
||||||
nwGUI.mainConf.backupPath = fncDir
|
|
||||||
|
|
||||||
nwGUI.theProject.setSpellLang("en")
|
nwGUI.theProject.setSpellLang("en")
|
||||||
nwGUI.theProject.setBookAuthors("Jane Smith\nJohn Smith")
|
|
||||||
nwGUI.theProject.setAutoReplace({"A": "B", "C": "D"})
|
|
||||||
|
|
||||||
# Get the dialog object
|
# Get the dialog object
|
||||||
monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *a: None)
|
|
||||||
monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.Accepted)
|
|
||||||
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
|
|
||||||
|
|
||||||
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
||||||
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
|
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
|
||||||
|
|
||||||
@@ -77,83 +67,189 @@ def testDlgProjSettings_Dialog(
|
|||||||
projEdit.show()
|
projEdit.show()
|
||||||
qtbot.addWidget(projEdit)
|
qtbot.addWidget(projEdit)
|
||||||
|
|
||||||
|
# Switch Tabs
|
||||||
|
projEdit._focusTab(GuiProjectSettings.TAB_REPLACE)
|
||||||
|
assert projEdit._tabBox.currentWidget() == projEdit.tabReplace
|
||||||
|
|
||||||
|
projEdit._focusTab(GuiProjectSettings.TAB_IMPORT)
|
||||||
|
assert projEdit._tabBox.currentWidget() == projEdit.tabImport
|
||||||
|
|
||||||
|
projEdit._focusTab(GuiProjectSettings.TAB_STATUS)
|
||||||
|
assert projEdit._tabBox.currentWidget() == projEdit.tabStatus
|
||||||
|
|
||||||
|
projEdit._focusTab(GuiProjectSettings.TAB_MAIN)
|
||||||
|
assert projEdit._tabBox.currentWidget() == projEdit.tabMain
|
||||||
|
|
||||||
|
# Clean Up
|
||||||
|
projEdit._doClose()
|
||||||
|
# qtbot.stop()
|
||||||
|
|
||||||
|
# END Test testDlgProjSettings_Dialog
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
||||||
|
"""Test the main tab of the project settings dialog.
|
||||||
|
"""
|
||||||
|
# Block message box
|
||||||
|
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||||
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
||||||
|
|
||||||
|
# Mock components
|
||||||
|
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
|
||||||
|
|
||||||
|
# Create new project
|
||||||
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
mockRnd.reset()
|
||||||
|
nwGUI.mainConf.backupPath = fncDir
|
||||||
|
|
||||||
|
# Set some values
|
||||||
|
theProject = nwGUI.theProject
|
||||||
|
theProject.setSpellLang("en")
|
||||||
|
theProject.setBookAuthors("Jane Smith\nJohn Smith")
|
||||||
|
theProject.setAutoReplace({"A": "B", "C": "D"})
|
||||||
|
|
||||||
|
# Create Dialog
|
||||||
|
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_MAIN)
|
||||||
|
projSettings.show()
|
||||||
|
qtbot.addWidget(projSettings)
|
||||||
|
|
||||||
# Settings Tab
|
# Settings Tab
|
||||||
# ============
|
# ============
|
||||||
|
|
||||||
assert projEdit.tabMain.editName.text() == "New Project"
|
tabMain = projSettings.tabMain
|
||||||
assert projEdit.tabMain.editTitle.text() == "New Novel"
|
|
||||||
assert projEdit.tabMain.editAuthors.toPlainText() == "Jane Smith\nJohn Smith"
|
assert tabMain.editName.text() == "New Project"
|
||||||
assert projEdit.tabMain.spellLang.currentData() == "en"
|
assert tabMain.editTitle.text() == "New Novel"
|
||||||
assert projEdit.tabMain.doBackup.isChecked() is False
|
assert tabMain.editAuthors.toPlainText() == "Jane Smith\nJohn Smith"
|
||||||
|
assert tabMain.spellLang.currentData() == "en"
|
||||||
|
assert tabMain.doBackup.isChecked() is False
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
qtbot.wait(stepDelay)
|
||||||
projEdit.tabMain.editName.setText("")
|
tabMain.editName.setText("")
|
||||||
for c in "Project Name":
|
for c in "Project Name":
|
||||||
qtbot.keyClick(projEdit.tabMain.editName, c, delay=typeDelay)
|
qtbot.keyClick(tabMain.editName, c, delay=typeDelay)
|
||||||
projEdit.tabMain.editTitle.setText("")
|
tabMain.editTitle.setText("")
|
||||||
for c in "Project Title":
|
for c in "Project Title":
|
||||||
qtbot.keyClick(projEdit.tabMain.editTitle, c, delay=typeDelay)
|
qtbot.keyClick(tabMain.editTitle, c, delay=typeDelay)
|
||||||
|
|
||||||
projEdit.tabMain.editAuthors.clear()
|
tabMain.editAuthors.clear()
|
||||||
for c in "Jane Doe":
|
for c in "Jane Doe":
|
||||||
qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=typeDelay)
|
qtbot.keyClick(tabMain.editAuthors, c, delay=typeDelay)
|
||||||
qtbot.keyClick(projEdit.tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
|
||||||
for c in "John Doh":
|
for c in "John Doh":
|
||||||
qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=typeDelay)
|
qtbot.keyClick(tabMain.editAuthors, c, delay=typeDelay)
|
||||||
qtbot.keyClick(projEdit.tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
qtbot.wait(stepDelay)
|
||||||
assert projEdit.tabMain.editName.text() == "Project Name"
|
assert tabMain.editName.text() == "Project Name"
|
||||||
assert projEdit.tabMain.editTitle.text() == "Project Title"
|
assert tabMain.editTitle.text() == "Project Title"
|
||||||
assert projEdit.tabMain.editAuthors.toPlainText() == "Jane Doe\nJohn Doh\n"
|
assert tabMain.editAuthors.toPlainText() == "Jane Doe\nJohn Doh\n"
|
||||||
|
assert projSettings.spellChanged is False
|
||||||
|
|
||||||
|
projSettings._doSave()
|
||||||
|
assert theProject.projName == "Project Name"
|
||||||
|
assert theProject.bookTitle == "Project Title"
|
||||||
|
assert theProject.bookAuthors == ["Jane Doe", "John Doh"]
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
projSettings._doClose()
|
||||||
|
# qtbot.stop()
|
||||||
|
|
||||||
|
# END Test testDlgProjSettings_Main
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
||||||
|
"""Test the status and importance tabs of the project settings
|
||||||
|
dialog.
|
||||||
|
"""
|
||||||
|
# Block message box
|
||||||
|
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||||
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
||||||
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
|
# Mock components
|
||||||
|
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
|
||||||
|
|
||||||
|
# Create new project
|
||||||
|
mockRnd.reset()
|
||||||
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
nwGUI.mainConf.backupPath = fncDir
|
||||||
|
|
||||||
|
# Set some values
|
||||||
|
theProject = nwGUI.theProject
|
||||||
|
theProject.tree[C.hTitlePage].setStatus(C.sFinished)
|
||||||
|
theProject.tree[C.hChapterDoc].setStatus(C.sDraft)
|
||||||
|
theProject.tree[C.hSceneDoc].setStatus(C.sDraft)
|
||||||
|
|
||||||
|
nwGUI.projView.projTree.setSelectedHandle(C.hPlotRoot)
|
||||||
|
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
||||||
|
nwGUI.projView.projTree.setSelectedHandle(C.hCharRoot)
|
||||||
|
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
||||||
|
nwGUI.projView.projTree.setSelectedHandle(C.hWorldRoot)
|
||||||
|
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
||||||
|
|
||||||
|
hPlotNote = "0000000000010"
|
||||||
|
hCharNote = "0000000000011"
|
||||||
|
hWorldNote = "0000000000012"
|
||||||
|
|
||||||
|
theProject.tree[hPlotNote].setImport(C.iMajor)
|
||||||
|
theProject.tree[hCharNote].setImport(C.iMajor)
|
||||||
|
theProject.tree[hWorldNote].setImport(C.iMain)
|
||||||
|
|
||||||
|
# Create Dialog
|
||||||
|
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_STATUS)
|
||||||
|
projSettings.show()
|
||||||
|
qtbot.addWidget(projSettings)
|
||||||
|
|
||||||
# Status Tab
|
# Status Tab
|
||||||
# ==========
|
# ==========
|
||||||
|
|
||||||
projEdit._tabBox.setCurrentWidget(projEdit.tabStatus)
|
tabStatus = projSettings.tabStatus
|
||||||
|
|
||||||
assert projEdit.tabStatus.colChanged is False
|
assert tabStatus.colChanged is False
|
||||||
assert projEdit.tabStatus.getNewList() == ([], [])
|
assert tabStatus.getNewList() == ([], [])
|
||||||
assert projEdit.tabStatus.listBox.topLevelItemCount() == 4
|
assert tabStatus.listBox.topLevelItemCount() == 4
|
||||||
|
|
||||||
# Can't delete the first item (it's in use)
|
# Can't delete the first item (it's in use)
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
tabStatus.listBox.clearSelection()
|
||||||
projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True)
|
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(0))
|
||||||
qtbot.mouseClick(projEdit.tabStatus.delButton, Qt.LeftButton)
|
qtbot.mouseClick(tabStatus.delButton, Qt.LeftButton)
|
||||||
assert projEdit.tabStatus.listBox.topLevelItemCount() == 4
|
assert tabStatus.listBox.topLevelItemCount() == 4
|
||||||
|
|
||||||
# Can delete the third item
|
# Can delete the second item
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
tabStatus.listBox.clearSelection()
|
||||||
projEdit.tabStatus.listBox.topLevelItem(2).setSelected(True)
|
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(1))
|
||||||
qtbot.mouseClick(projEdit.tabStatus.delButton, Qt.LeftButton)
|
qtbot.mouseClick(tabStatus.delButton, Qt.LeftButton)
|
||||||
assert projEdit.tabStatus.listBox.topLevelItemCount() == 3
|
assert tabStatus.listBox.topLevelItemCount() == 3
|
||||||
|
|
||||||
# Add a new item
|
# Add a new item
|
||||||
monkeypatch.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
|
with monkeypatch.context() as mp:
|
||||||
qtbot.mouseClick(projEdit.tabStatus.addButton, Qt.LeftButton)
|
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
|
||||||
projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True)
|
qtbot.mouseClick(tabStatus.addButton, Qt.LeftButton)
|
||||||
for n in range(8):
|
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
|
||||||
qtbot.keyClick(projEdit.tabStatus.editName, Qt.Key_Backspace, delay=typeDelay)
|
for _ in range(8):
|
||||||
for c in "Final":
|
qtbot.keyClick(tabStatus.editName, Qt.Key_Backspace, delay=typeDelay)
|
||||||
qtbot.keyClick(projEdit.tabStatus.editName, c, delay=typeDelay)
|
for c in "Final":
|
||||||
qtbot.mouseClick(projEdit.tabStatus.colButton, Qt.LeftButton)
|
qtbot.keyClick(tabStatus.editName, c, delay=typeDelay)
|
||||||
qtbot.mouseClick(projEdit.tabStatus.saveButton, Qt.LeftButton)
|
qtbot.mouseClick(tabStatus.colButton, Qt.LeftButton)
|
||||||
assert projEdit.tabStatus.listBox.topLevelItemCount() == 4
|
qtbot.mouseClick(tabStatus.saveButton, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
assert tabStatus.listBox.topLevelItemCount() == 4
|
||||||
|
|
||||||
assert projEdit.tabStatus.colChanged is True
|
assert tabStatus.colChanged is True
|
||||||
assert projEdit.tabStatus.getNewList() == (
|
assert tabStatus.getNewList() == (
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"key": statusKeys[0],
|
"key": C.sNew,
|
||||||
"name": "New",
|
"name": "New",
|
||||||
"cols": (100, 100, 100)
|
"cols": (100, 100, 100)
|
||||||
}, {
|
}, {
|
||||||
"key": statusKeys[1],
|
"key": C.sDraft,
|
||||||
"name": "Note",
|
"name": "Draft",
|
||||||
"cols": (200, 50, 0)
|
"cols": (200, 150, 0)
|
||||||
}, {
|
}, {
|
||||||
"key": statusKeys[3],
|
"key": C.sFinished,
|
||||||
"name": "Finished",
|
"name": "Finished",
|
||||||
"cols": (50, 200, 0)
|
"cols": (50, 200, 0)
|
||||||
}, {
|
}, {
|
||||||
@@ -162,121 +258,201 @@ def testDlgProjSettings_Dialog(
|
|||||||
"cols": (20, 30, 40)
|
"cols": (20, 30, 40)
|
||||||
}
|
}
|
||||||
], [
|
], [
|
||||||
statusKeys[2] # Deleted item
|
C.sNote # Deleted item
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Move items
|
# Move items, none selected -> no change
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
tabStatus.listBox.clearSelection()
|
||||||
projEdit.tabStatus._moveItem(1)
|
tabStatus._moveItem(1)
|
||||||
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
|
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||||
statusKeys[0], statusKeys[1], statusKeys[3], None
|
C.sNew, C.sDraft, C.sFinished, None
|
||||||
]
|
]
|
||||||
|
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
# Move items, first selected, move up -> no change
|
||||||
projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True)
|
tabStatus.listBox.clearSelection()
|
||||||
projEdit.tabStatus._moveItem(-1)
|
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(0))
|
||||||
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
|
tabStatus._moveItem(-1)
|
||||||
statusKeys[0], statusKeys[1], statusKeys[3], None
|
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||||
|
C.sNew, C.sDraft, C.sFinished, None
|
||||||
]
|
]
|
||||||
|
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
# Move items, last selected, move up -> allowed
|
||||||
projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True)
|
tabStatus.listBox.clearSelection()
|
||||||
projEdit.tabStatus._moveItem(-1)
|
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
|
||||||
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
|
tabStatus._moveItem(-1)
|
||||||
statusKeys[0], statusKeys[1], None, statusKeys[3]
|
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||||
|
C.sNew, C.sDraft, None, C.sFinished
|
||||||
]
|
]
|
||||||
projEdit.tabStatus._moveItem(1)
|
|
||||||
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
|
# Move items, same selected, move down -> allowed
|
||||||
statusKeys[0], statusKeys[1], statusKeys[3], None
|
tabStatus._moveItem(1)
|
||||||
|
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||||
|
C.sNew, C.sDraft, C.sFinished, None
|
||||||
]
|
]
|
||||||
|
|
||||||
# Importance Tab
|
# Importance Tab
|
||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
projEdit._tabBox.setCurrentWidget(projEdit.tabImport)
|
tabImport = projSettings.tabImport
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
projSettings._focusTab(GuiProjectSettings.TAB_IMPORT)
|
||||||
projEdit.tabImport.listBox.topLevelItem(3).setSelected(True)
|
|
||||||
qtbot.mouseClick(projEdit.tabImport.delButton, Qt.LeftButton)
|
# Delete unused entry
|
||||||
qtbot.mouseClick(projEdit.tabImport.addButton, Qt.LeftButton)
|
tabImport.listBox.clearSelection()
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(1))
|
||||||
projEdit.tabImport.listBox.topLevelItem(3).setSelected(True)
|
qtbot.mouseClick(tabImport.delButton, Qt.LeftButton)
|
||||||
for n in range(8):
|
assert tabImport.listBox.topLevelItemCount() == 3
|
||||||
qtbot.keyClick(projEdit.tabImport.editName, Qt.Key_Backspace, delay=typeDelay)
|
|
||||||
for c in "Final":
|
# Add a new entry
|
||||||
qtbot.keyClick(projEdit.tabImport.editName, c, delay=typeDelay)
|
with monkeypatch.context() as mp:
|
||||||
qtbot.mouseClick(projEdit.tabImport.saveButton, Qt.LeftButton)
|
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
|
||||||
qtbot.wait(stepDelay)
|
qtbot.mouseClick(tabImport.addButton, Qt.LeftButton)
|
||||||
|
tabImport.listBox.clearSelection()
|
||||||
|
tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(3))
|
||||||
|
for _ in range(8):
|
||||||
|
qtbot.keyClick(tabImport.editName, Qt.Key_Backspace, delay=typeDelay)
|
||||||
|
for c in "Final":
|
||||||
|
qtbot.keyClick(tabImport.editName, c, delay=typeDelay)
|
||||||
|
qtbot.mouseClick(tabImport.colButton, Qt.LeftButton)
|
||||||
|
qtbot.mouseClick(tabImport.saveButton, Qt.LeftButton)
|
||||||
|
assert tabImport.listBox.topLevelItemCount() == 4
|
||||||
|
|
||||||
|
assert tabImport.colChanged is True
|
||||||
|
assert tabImport.getNewList() == (
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"key": C.iNew,
|
||||||
|
"name": "New",
|
||||||
|
"cols": (100, 100, 100)
|
||||||
|
}, {
|
||||||
|
"key": C.iMajor,
|
||||||
|
"name": "Major",
|
||||||
|
"cols": (200, 150, 0)
|
||||||
|
}, {
|
||||||
|
"key": C.iMain,
|
||||||
|
"name": "Main",
|
||||||
|
"cols": (50, 200, 0)
|
||||||
|
}, {
|
||||||
|
"key": None,
|
||||||
|
"name": "Final",
|
||||||
|
"cols": (20, 30, 40)
|
||||||
|
}
|
||||||
|
], [
|
||||||
|
C.iMinor # Deleted item
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check Project
|
||||||
|
projSettings._doSave()
|
||||||
|
|
||||||
|
statusItems = dict(theProject.statusItems.items())
|
||||||
|
assert statusItems[C.sNew]["name"] == "New"
|
||||||
|
assert statusItems[C.sDraft]["name"] == "Draft"
|
||||||
|
assert statusItems[C.sFinished]["name"] == "Finished"
|
||||||
|
assert statusItems["s000013"]["name"] == "Final"
|
||||||
|
|
||||||
|
importItems = dict(theProject.importItems.items())
|
||||||
|
assert importItems[C.iNew]["name"] == "New"
|
||||||
|
assert importItems[C.iMajor]["name"] == "Major"
|
||||||
|
assert importItems[C.iMain]["name"] == "Main"
|
||||||
|
assert importItems["i000014"]["name"] == "Final"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
# qtbot.stop()
|
||||||
|
projSettings._doClose()
|
||||||
|
|
||||||
|
# END Test testDlgProjSettings_StatusImport
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
||||||
|
"""Test the auto-replace tab of the project settings dialog.
|
||||||
|
"""
|
||||||
|
# Block message box
|
||||||
|
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||||
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
||||||
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
|
# Mock components
|
||||||
|
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
|
||||||
|
|
||||||
|
# Create new project
|
||||||
|
mockRnd.reset()
|
||||||
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
nwGUI.mainConf.backupPath = fncDir
|
||||||
|
|
||||||
|
# Set some values
|
||||||
|
theProject = nwGUI.theProject
|
||||||
|
theProject.autoReplace = {
|
||||||
|
"A": "B", "C": "D"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create Dialog
|
||||||
|
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_REPLACE)
|
||||||
|
projSettings.show()
|
||||||
|
qtbot.addWidget(projSettings)
|
||||||
|
|
||||||
# Auto-Replace Tab
|
# Auto-Replace Tab
|
||||||
# ================
|
# ================
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
tabReplace = projSettings.tabReplace
|
||||||
projEdit._tabBox.setCurrentWidget(projEdit.tabReplace)
|
|
||||||
|
|
||||||
assert projEdit.tabReplace.listBox.topLevelItem(0).text(0) == "<A>"
|
assert tabReplace.listBox.topLevelItem(0).text(0) == "<A>"
|
||||||
assert projEdit.tabReplace.listBox.topLevelItem(0).text(1) == "B"
|
assert tabReplace.listBox.topLevelItem(0).text(1) == "B"
|
||||||
assert projEdit.tabReplace.listBox.topLevelItem(1).text(0) == "<C>"
|
assert tabReplace.listBox.topLevelItem(1).text(0) == "<C>"
|
||||||
assert projEdit.tabReplace.listBox.topLevelItem(1).text(1) == "D"
|
assert tabReplace.listBox.topLevelItem(1).text(1) == "D"
|
||||||
|
assert tabReplace.listBox.topLevelItemCount() == 2
|
||||||
|
|
||||||
qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton)
|
# Nothing to save or delete
|
||||||
projEdit.tabReplace.listBox.topLevelItem(2).setSelected(True)
|
tabReplace.listBox.clearSelection()
|
||||||
projEdit.tabReplace.editKey.setText("")
|
assert tabReplace._saveEntry() is False
|
||||||
|
assert tabReplace._delEntry() is False
|
||||||
|
assert tabReplace.listBox.topLevelItemCount() == 2
|
||||||
|
|
||||||
|
# Create a new entry
|
||||||
|
qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton)
|
||||||
|
assert tabReplace.listBox.topLevelItemCount() == 3
|
||||||
|
assert tabReplace.listBox.topLevelItem(2).text(0) == "<keyword3>"
|
||||||
|
assert tabReplace.listBox.topLevelItem(2).text(1) == ""
|
||||||
|
|
||||||
|
# Edit the entry
|
||||||
|
tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(2))
|
||||||
|
tabReplace.editKey.setText("")
|
||||||
for c in "Th is ":
|
for c in "Th is ":
|
||||||
qtbot.keyClick(projEdit.tabReplace.editKey, c, delay=typeDelay)
|
qtbot.keyClick(tabReplace.editKey, c, delay=typeDelay)
|
||||||
projEdit.tabReplace.editValue.setText("")
|
tabReplace.editValue.setText("")
|
||||||
for c in "With This Stuff ":
|
for c in "With This Stuff ":
|
||||||
qtbot.keyClick(projEdit.tabReplace.editValue, c, delay=typeDelay)
|
qtbot.keyClick(tabReplace.editValue, c, delay=typeDelay)
|
||||||
qtbot.mouseClick(projEdit.tabReplace.saveButton, Qt.LeftButton)
|
qtbot.mouseClick(tabReplace.saveButton, Qt.LeftButton)
|
||||||
|
assert tabReplace.listBox.topLevelItem(2).text(0) == "<This>"
|
||||||
|
assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff "
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
# Create a new entry again
|
||||||
projEdit.tabReplace.listBox.clearSelection()
|
tabReplace.listBox.clearSelection()
|
||||||
assert not projEdit.tabReplace._saveEntry()
|
qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton)
|
||||||
assert not projEdit.tabReplace._delEntry()
|
assert tabReplace.listBox.topLevelItemCount() == 4
|
||||||
qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton)
|
|
||||||
|
|
||||||
|
# The list is sorted, so we must find it
|
||||||
newIdx = -1
|
newIdx = -1
|
||||||
for i in range(projEdit.tabReplace.listBox.topLevelItemCount()):
|
for i in range(tabReplace.listBox.topLevelItemCount()):
|
||||||
if projEdit.tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>":
|
if tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>":
|
||||||
newIdx = i
|
newIdx = i
|
||||||
break
|
break
|
||||||
|
|
||||||
assert newIdx >= 0
|
assert newIdx >= 0
|
||||||
newItem = projEdit.tabReplace.listBox.topLevelItem(newIdx)
|
|
||||||
projEdit.tabReplace.listBox.setCurrentItem(newItem)
|
|
||||||
qtbot.mouseClick(projEdit.tabReplace.delButton, Qt.LeftButton)
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Save & Check
|
# Then delete the new item
|
||||||
# ============
|
tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(newIdx))
|
||||||
|
qtbot.mouseClick(tabReplace.delButton, Qt.LeftButton)
|
||||||
|
assert tabReplace.listBox.topLevelItemCount() == 3
|
||||||
|
|
||||||
projEdit._doSave()
|
# Check Project
|
||||||
|
projSettings._doSave()
|
||||||
|
assert theProject.autoReplace == {
|
||||||
|
"A": "B", "C": "D", "This": "With This Stuff"
|
||||||
|
}
|
||||||
|
|
||||||
# Open again, and check project settings
|
# Clean up
|
||||||
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
# qtbot.stop()
|
||||||
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
|
projSettings._doClose()
|
||||||
|
|
||||||
projEdit = getGuiItem("GuiProjectSettings")
|
# END Test testDlgProjSettings_Replace
|
||||||
assert isinstance(projEdit, GuiProjectSettings)
|
|
||||||
|
|
||||||
qtbot.addWidget(projEdit)
|
|
||||||
assert projEdit.tabMain.editName.text() == "Project Name"
|
|
||||||
assert projEdit.tabMain.editTitle.text() == "Project Title"
|
|
||||||
theAuth = projEdit.tabMain.editAuthors.toPlainText().strip().splitlines()
|
|
||||||
assert len(theAuth) == 2
|
|
||||||
assert theAuth[0] == "Jane Doe"
|
|
||||||
assert theAuth[1] == "John Doh"
|
|
||||||
|
|
||||||
projEdit._doClose()
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
assert nwGUI.saveProject()
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Check the files
|
|
||||||
copyfile(projFile, testFile)
|
|
||||||
assert cmpFiles(testFile, compFile, [2, 8, 9, 10])
|
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
|
||||||
|
|
||||||
# END Test testDlgProjSettings_Dialog
|
|
||||||
|
|||||||
@@ -1144,7 +1144,7 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
assert nwGUI.openDocument(cHandle) is True
|
assert nwGUI.openDocument(cHandle) is True
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
assert nwGUI.saveDocument() is True
|
assert nwGUI.saveDocument() is True
|
||||||
assert nwGUI.projView.revealNewTreeItem(cHandle)
|
assert nwGUI.projView.projTree.revealNewTreeItem(cHandle)
|
||||||
nwGUI.docEditor.updateTagHighLighting()
|
nwGUI.docEditor.updateTagHighLighting()
|
||||||
|
|
||||||
# Follow Tag
|
# Follow Tag
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
|
|
||||||
# Add a second novel folder
|
# Add a second novel folder
|
||||||
newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL)
|
newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL)
|
||||||
nwGUI.projView.revealNewTreeItem(newHandle)
|
nwGUI.projView.projTree.revealNewTreeItem(newHandle)
|
||||||
|
|
||||||
# Check new values in dropdown list
|
# Check new values in dropdown list
|
||||||
assert outlineBar.novelValue.itemData(0) == lipHandle
|
assert outlineBar.novelValue.itemData(0) == lipHandle
|
||||||
@@ -202,7 +202,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
aHandle = nwGUI.theProject.newFile(dTitle, newHandle)
|
aHandle = nwGUI.theProject.newFile(dTitle, newHandle)
|
||||||
hHash = "#"*hLevel
|
hHash = "#"*hLevel
|
||||||
writeFile(os.path.join(nwLipsum, "content", f"{aHandle}.nwd"), f"{hHash} {dTitle}\n\n")
|
writeFile(os.path.join(nwLipsum, "content", f"{aHandle}.nwd"), f"{hHash} {dTitle}\n\n")
|
||||||
nwGUI.projView.revealNewTreeItem(aHandle)
|
nwGUI.projView.projTree.revealNewTreeItem(aHandle)
|
||||||
|
|
||||||
nwGUI.rebuildIndex()
|
nwGUI.rebuildIndex()
|
||||||
|
|
||||||
|
|||||||
@@ -160,11 +160,11 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
|
|||||||
# ============
|
# ============
|
||||||
|
|
||||||
# Also check error handling in reveal function
|
# Also check error handling in reveal function
|
||||||
assert projView.revealNewTreeItem("abc") is False
|
assert projView.projTree.revealNewTreeItem("abc") is False
|
||||||
|
|
||||||
# Add an item that cannot be displayed in the tree
|
# Add an item that cannot be displayed in the tree
|
||||||
nHandle = theProject.newFile("Test", None)
|
nHandle = theProject.newFile("Test", None)
|
||||||
assert projView.revealNewTreeItem(nHandle) is False
|
assert projView.projTree.revealNewTreeItem(nHandle) is False
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
# qtbot.stop()
|
# qtbot.stop()
|
||||||
@@ -184,10 +184,11 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
nwTree = nwGUI.projView
|
projView = nwGUI.projView
|
||||||
|
projTree = nwGUI.projView.projTree
|
||||||
|
|
||||||
# Try to move item with no project
|
# Try to move item with no project
|
||||||
assert nwTree.projTree.moveTreeItem(1) is False
|
assert projView.projTree.moveTreeItem(1) is False
|
||||||
|
|
||||||
# Create a project
|
# Create a project
|
||||||
prjDir = os.path.join(fncDir, "project")
|
prjDir = os.path.join(fncDir, "project")
|
||||||
@@ -197,68 +198,68 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
# Add some files
|
# Add some files
|
||||||
nwTree.setSelectedHandle(C.hChapterDir)
|
projView.setSelectedHandle(C.hChapterDir)
|
||||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Move with no selections
|
# Move with no selections
|
||||||
nwTree.projTree.clearSelection()
|
projTree.clearSelection()
|
||||||
assert nwTree.projTree.moveTreeItem(1) is False
|
assert projTree.moveTreeItem(1) is False
|
||||||
|
|
||||||
# Move second item up twice (should give same result)
|
# Move second item up twice (should give same result)
|
||||||
nwTree.setSelectedHandle(C.hSceneDoc)
|
projView.setSelectedHandle(C.hSceneDoc)
|
||||||
assert nwTree.projTree.moveTreeItem(-1) is True
|
assert projTree.moveTreeItem(-1) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hSceneDoc, C.hChapterDoc,
|
C.hChapterDir, C.hSceneDoc, C.hChapterDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
assert nwTree.projTree.moveTreeItem(-1) is False
|
assert projTree.moveTreeItem(-1) is False
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hSceneDoc, C.hChapterDoc,
|
C.hChapterDir, C.hSceneDoc, C.hChapterDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Restore
|
# Restore
|
||||||
assert nwTree.projTree.moveTreeItem(1) is True
|
assert projTree.moveTreeItem(1) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Move fifth item down twice (should give same result)
|
# Move fifth item down twice (should give same result)
|
||||||
nwTree.setSelectedHandle("0000000000011")
|
projView.setSelectedHandle("0000000000011")
|
||||||
assert nwTree.projTree.moveTreeItem(1) is True
|
assert projTree.moveTreeItem(1) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000012", "0000000000011",
|
"0000000000010", "0000000000012", "0000000000011",
|
||||||
]
|
]
|
||||||
assert nwTree.projTree.moveTreeItem(1) is False
|
assert projTree.moveTreeItem(1) is False
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000012", "0000000000011",
|
"0000000000010", "0000000000012", "0000000000011",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Restore
|
# Restore
|
||||||
assert nwTree.projTree.moveTreeItem(-1) is True
|
assert projTree.moveTreeItem(-1) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Move down again, and restore via undo
|
# Move down again, and restore via undo
|
||||||
nwTree.setSelectedHandle("0000000000011")
|
projView.setSelectedHandle("0000000000011")
|
||||||
assert nwTree.projTree.moveTreeItem(1) is True
|
assert projTree.moveTreeItem(1) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000012", "0000000000011",
|
"0000000000010", "0000000000012", "0000000000011",
|
||||||
]
|
]
|
||||||
assert nwTree.projTree.undoLastMove() is True
|
assert projTree.undoLastMove() is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
@@ -266,19 +267,19 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
# Root Folder
|
# Root Folder
|
||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
nwTree.setSelectedHandle(C.hNovelRoot)
|
projView.setSelectedHandle(C.hNovelRoot)
|
||||||
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
||||||
|
|
||||||
# Move novel folder up
|
# Move novel folder up
|
||||||
assert nwTree.projTree.moveTreeItem(-1) is False
|
assert projTree.moveTreeItem(-1) is False
|
||||||
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
||||||
|
|
||||||
# Move novel folder down
|
# Move novel folder down
|
||||||
assert nwTree.projTree.moveTreeItem(1) is True
|
assert projTree.moveTreeItem(1) is True
|
||||||
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 1
|
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 1
|
||||||
|
|
||||||
# Move novel folder up again
|
# Move novel folder up again
|
||||||
assert nwTree.projTree.moveTreeItem(-1) is True
|
assert projTree.moveTreeItem(-1) is True
|
||||||
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
@@ -299,76 +300,77 @@ def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir,
|
|||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
nwView = nwGUI.projView
|
projView = nwGUI.projView
|
||||||
|
projTree = nwGUI.projView.projTree
|
||||||
|
|
||||||
# Try to run with no project
|
# Try to run with no project
|
||||||
assert nwView.requestDeleteItem() is False
|
assert projView.requestDeleteItem() is False
|
||||||
|
|
||||||
# Create a project
|
# Create a project
|
||||||
prjDir = os.path.join(fncDir, "project")
|
prjDir = os.path.join(fncDir, "project")
|
||||||
buildTestProject(nwGUI, prjDir)
|
buildTestProject(nwGUI, prjDir)
|
||||||
|
|
||||||
# Try emptying the trash already now, when there is no trash folder
|
# Try emptying the trash already now, when there is no trash folder
|
||||||
assert nwView.emptyTrash() is False
|
assert projView.emptyTrash() is False
|
||||||
|
|
||||||
# Add some files
|
# Add some files
|
||||||
nwView.setSelectedHandle(C.hChapterDir)
|
projView.setSelectedHandle(C.hChapterDir)
|
||||||
assert nwView.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwView.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwView.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwView.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Delete item without focus -> blocked
|
# Delete item without focus -> blocked
|
||||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False)
|
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False)
|
||||||
nwView.setSelectedHandle("0000000000012")
|
projView.setSelectedHandle("0000000000012")
|
||||||
assert nwView.requestDeleteItem() is False
|
assert projView.requestDeleteItem() is False
|
||||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||||
|
|
||||||
# No selection made
|
# No selection made
|
||||||
nwView.projTree.clearSelection()
|
projTree.clearSelection()
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert nwView.requestDeleteItem() is False
|
assert projView.requestDeleteItem() is False
|
||||||
assert "no item to delete" in caplog.text
|
assert "no item to delete" in caplog.text
|
||||||
|
|
||||||
# Not a valid handle
|
# Not a valid handle
|
||||||
nwView.projTree.clearSelection()
|
projTree.clearSelection()
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert nwView.requestDeleteItem("0000000000000") is False
|
assert projView.requestDeleteItem("0000000000000") is False
|
||||||
assert "No tree item with handle '0000000000000'" in caplog.text
|
assert "No tree item with handle '0000000000000'" in caplog.text
|
||||||
|
|
||||||
# Delete Root Folders
|
# Delete Root Folders
|
||||||
# ===================
|
# ===================
|
||||||
|
|
||||||
assert nwView.requestDeleteItem(C.hNovelRoot) is False # Novel Root is blocked
|
assert projView.requestDeleteItem(C.hNovelRoot) is False # Novel Root is blocked
|
||||||
assert nwView.requestDeleteItem(C.hCharRoot) is True # Character Root
|
assert projView.requestDeleteItem(C.hCharRoot) is True # Character Root
|
||||||
|
|
||||||
# Delete File
|
# Delete File
|
||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
# Block adding trash folder
|
# Block adding trash folder
|
||||||
funcPointer = nwView.projTree._addTrashRoot
|
funcPointer = projTree._addTrashRoot
|
||||||
nwView.projTree._addTrashRoot = lambda *a: None
|
projTree._addTrashRoot = lambda *a: None
|
||||||
assert nwView.requestDeleteItem("0000000000012") is False
|
assert projView.requestDeleteItem("0000000000012") is False
|
||||||
nwView.projTree._addTrashRoot = funcPointer
|
projTree._addTrashRoot = funcPointer
|
||||||
|
|
||||||
# Delete last two documents, which also adds the trash folder
|
# Delete last two documents, which also adds the trash folder
|
||||||
assert nwView.requestDeleteItem("0000000000012") is True
|
assert projView.requestDeleteItem("0000000000012") is True
|
||||||
assert nwView.requestDeleteItem("0000000000011") is True
|
assert projView.requestDeleteItem("0000000000011") is True
|
||||||
assert nwView.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010"
|
"0000000000010"
|
||||||
]
|
]
|
||||||
trashHandle = nwGUI.theProject.tree.trashRoot()
|
trashHandle = nwGUI.theProject.tree.trashRoot()
|
||||||
assert nwView.getTreeFromHandle(trashHandle) == [
|
assert projTree.getTreeFromHandle(trashHandle) == [
|
||||||
trashHandle, "0000000000012", "0000000000011"
|
trashHandle, "0000000000012", "0000000000011"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Try to delete the trash folder
|
# Try to delete the trash folder
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert nwView.requestDeleteItem("0000000000013") is False
|
assert projView.requestDeleteItem("0000000000013") is False
|
||||||
assert "Cannot delete the Trash folder" in caplog.text
|
assert "Cannot delete the Trash folder" in caplog.text
|
||||||
|
|
||||||
nwGUI.closeProject()
|
nwGUI.closeProject()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot)
|
cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot)
|
||||||
newDoc = NWDoc(nwGUI.theProject, cHandle)
|
newDoc = NWDoc(nwGUI.theProject, cHandle)
|
||||||
newDoc.writeDocument("# A Note\n\n")
|
newDoc.writeDocument("# A Note\n\n")
|
||||||
nwGUI.projView.revealNewTreeItem(cHandle)
|
nwGUI.projView.projTree.revealNewTreeItem(cHandle)
|
||||||
nwGUI.rebuildIndex(beQuiet=True)
|
nwGUI.rebuildIndex(beQuiet=True)
|
||||||
|
|
||||||
# Reference Time
|
# Reference Time
|
||||||
|
|||||||
@@ -30,6 +30,17 @@ XML_IGNORE = ("<novelWriterXML", "<saveCount", "<autoCount", "<editTime")
|
|||||||
|
|
||||||
class C:
|
class C:
|
||||||
|
|
||||||
|
# Import and status items from test project when random generator is mocked
|
||||||
|
sNew = "s000000"
|
||||||
|
sNote = "s000001"
|
||||||
|
sDraft = "s000002"
|
||||||
|
sFinished = "s000003"
|
||||||
|
|
||||||
|
iNew = "i000004"
|
||||||
|
iMinor = "i000005"
|
||||||
|
iMajor = "i000006"
|
||||||
|
iMain = "i000007"
|
||||||
|
|
||||||
# Handles from test project when random generator is mocked
|
# Handles from test project when random generator is mocked
|
||||||
hInvalid = "0000000000000"
|
hInvalid = "0000000000000"
|
||||||
hNovelRoot = "0000000000008"
|
hNovelRoot = "0000000000008"
|
||||||
|
|||||||
Reference in New Issue
Block a user