Updates to status and importance feature (#1207)

This commit is contained in:
Veronica Berglyd Olsen
2022-10-25 17:18:45 +02:00
committed by GitHub
11 changed files with 514 additions and 372 deletions
+6
View File
@@ -421,6 +421,12 @@ class PagedDialog(QDialog):
self._buttonBox.addWidget(buttonBar)
return
def setCurrentWidget(self, widget):
"""Forward the changing of tab to the QTabWidget.
"""
self._tabBox.setCurrentWidget(widget)
return
# END Class PagedDialog
+28 -3
View File
@@ -43,7 +43,12 @@ logger = logging.getLogger(__name__)
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)
logger.debug("Initialising GuiProjectSettings ...")
@@ -83,12 +88,19 @@ class GuiProjectSettings(PagedDialog):
self.addControls(self.buttonBox)
# Flags
self.spellChanged = False
self._spellChanged = False
# Focus Tab
self._focusTab(focusTab)
logger.debug("GuiProjectSettings initialisation complete")
return
@property
def spellChanged(self):
return self._spellChanged
##
# Slots
##
@@ -108,7 +120,7 @@ class GuiProjectSettings(PagedDialog):
self.theProject.setProjBackup(doBackup)
# 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:
newList, delList = self.tabStatus.getNewList()
@@ -141,6 +153,19 @@ class GuiProjectSettings(PagedDialog):
# 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):
"""Save GUI settings.
"""
+62 -55
View File
@@ -41,8 +41,8 @@ from PyQt5.QtWidgets import (
from novelwriter.core import DocMerger, DocSplitter
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert
from novelwriter.dialogs import GuiDocMerge, GuiDocSplit, GuiEditLabel
from novelwriter.constants import nwHeaders, trConst, nwLabels
from novelwriter.dialogs import GuiDocMerge, GuiDocSplit, GuiEditLabel, GuiProjectSettings
from novelwriter.constants import nwHeaders, nwUnicode, trConst, nwLabels
logger = logging.getLogger(__name__)
@@ -62,6 +62,9 @@ class GuiProjectView(QWidget):
selectedItemChanged = pyqtSignal(str)
openDocumentRequest = pyqtSignal(str, Enum, int, str)
# Requests for the main GUI
projectSettingsRequest = pyqtSignal(int)
def __init__(self, mainGui):
super().__init__(parent=mainGui)
@@ -102,9 +105,6 @@ class GuiProjectView(QWidget):
self.keyContext.activated.connect(lambda: self.projTree.openContextOnSelected())
# Function Mappings
self.revealNewTreeItem = self.projTree.revealNewTreeItem
self.renameTreeItem = self.projTree.renameTreeItem
self.getTreeFromHandle = self.projTree.getTreeFromHandle
self.emptyTrash = self.projTree.emptyTrash
self.requestDeleteItem = self.projTree.requestDeleteItem
self.setTreeItemValues = self.projTree.setTreeItemValues
@@ -161,6 +161,16 @@ class GuiProjectView(QWidget):
"""
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
##
@@ -1111,10 +1121,12 @@ class GuiProjectTree(QTreeWidget):
open a context menu in-place.
"""
tItem = None
hasChild = False
selItem = self.itemAt(clickPos)
if isinstance(selItem, QTreeWidgetItem):
tHandle = selItem.data(self.C_NAME, Qt.UserRole)
tItem = self.theProject.tree[tHandle]
hasChild = selItem.childCount() > 0
if tItem is None:
logger.debug("No item found")
@@ -1128,9 +1140,8 @@ class GuiProjectTree(QTreeWidget):
trashHandle = self.theProject.tree.trashRoot()
if tItem.itemHandle == trashHandle and trashHandle is not None:
# The trash folder only has one option
ctxMenu.addAction(
self.tr("Empty Trash"), lambda: self.emptyTrash()
)
aEmptyTrash = ctxMenu.addAction(self.tr("Empty Trash"))
aEmptyTrash.triggered.connect(lambda: self.emptyTrash())
ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
return True
@@ -1140,15 +1151,14 @@ class GuiProjectTree(QTreeWidget):
isRoot = tItem.isRootType()
isFolder = tItem.isFolderType()
isFile = tItem.isFileType()
hasChild = selItem.childCount() > 0
if isFile:
ctxMenu.addAction(
self.tr("Open Document"),
aOpenDoc = ctxMenu.addAction(self.tr("Open Document"))
aOpenDoc.triggered.connect(
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, -1, "")
)
ctxMenu.addAction(
self.tr("View Document"),
aViewDoc = ctxMenu.addAction(self.tr("View Document"))
aViewDoc.triggered.connect(
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "")
)
ctxMenu.addSeparator()
@@ -1156,29 +1166,40 @@ class GuiProjectTree(QTreeWidget):
# Edit Item Settings
# ==================
ctxMenu.addAction(
self.tr("Change Label"), lambda: self.renameTreeItem(tHandle)
)
aLabel = ctxMenu.addAction(self.tr("Change Label"))
aLabel.triggered.connect(lambda: self.renameTreeItem(tHandle))
if isFile:
ctxMenu.addAction(
self.tr("Toggle Active"), lambda: self._toggleItemActive(tHandle)
)
aActive = ctxMenu.addAction(self.tr("Toggle Active"))
aActive.triggered.connect(lambda: self._toggleItemActive(tHandle))
checkMark = f" ({nwUnicode.U_CHECK})"
if tItem.isNovelLike():
mStatus = ctxMenu.addMenu(self.tr("Set Status to ..."))
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(
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:
mImport = ctxMenu.addMenu(self.tr("Set Importance to ..."))
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(
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
# ==============
@@ -1193,38 +1214,30 @@ class GuiProjectTree(QTreeWidget):
isNoteFile = isFile and tItem.isNoteLayout()
if (isNoteFile or isFolder) and tItem.documentAllowed():
mTrans.addAction(
self.tr("Convert to {0}").format(trDoc),
aConvert1 = mTrans.addAction(self.tr("Convert to {0}").format(trDoc))
aConvert1.triggered.connect(
lambda: self._changeItemLayout(tHandle, nwItemLayout.DOCUMENT)
)
if isDocFile or isFolder:
mTrans.addAction(
self.tr("Convert to {0}").format(trNote),
aConvert2 = mTrans.addAction(self.tr("Convert to {0}").format(trNote))
aConvert2.triggered.connect(
lambda: self._changeItemLayout(tHandle, nwItemLayout.NOTE)
)
if hasChild and isFile:
mTrans.addAction(
self.tr("Merge Child Items into Self"),
lambda: self._mergeDocuments(tHandle, False)
)
mTrans.addAction(
self.tr("Merge Child Items into New"),
lambda: self._mergeDocuments(tHandle, True)
)
aMerge1 = mTrans.addAction(self.tr("Merge Child Items into Self"))
aMerge1.triggered.connect(lambda: self._mergeDocuments(tHandle, False))
aMerge2 = mTrans.addAction(self.tr("Merge Child Items into New"))
aMerge2.triggered.connect(lambda: self._mergeDocuments(tHandle, True))
if hasChild and isFolder:
mTrans.addAction(
self.tr("Merge Documents in Folder"),
lambda: self._mergeDocuments(tHandle, True)
)
aMerge3 = mTrans.addAction(self.tr("Merge Documents in Folder"))
aMerge3.triggered.connect(lambda: self._mergeDocuments(tHandle, True))
if isFile:
mTrans.addAction(
self.tr("Split Document by Headers"),
lambda: self._splitDocument(tHandle)
)
aSplit1 = mTrans.addAction(self.tr("Split Document by Headers"))
aSplit1.triggered.connect(lambda: self._splitDocument(tHandle))
# Expand/Collapse/Delete
# ======================
@@ -1232,23 +1245,17 @@ class GuiProjectTree(QTreeWidget):
ctxMenu.addSeparator()
if hasChild:
ctxMenu.addAction(
self.tr("Expand All"),
lambda: self.setExpandedFromHandle(tHandle, True)
)
ctxMenu.addAction(
self.tr("Collapse All"),
lambda: self.setExpandedFromHandle(tHandle, False)
)
aExpand = ctxMenu.addAction(self.tr("Expand All"))
aExpand.triggered.connect(lambda: self.setExpandedFromHandle(tHandle, True))
aCollapse = ctxMenu.addAction(self.tr("Collapse All"))
aCollapse.triggered.connect(lambda: self.setExpandedFromHandle(tHandle, False))
if tItem.itemClass == nwItemClass.TRASH or isRoot or (isFolder and not hasChild):
ctxMenu.addAction(
self.tr("Delete Permanently"), lambda: self.permanentlyDeleteItem(tHandle)
)
aDelete = ctxMenu.addAction(self.tr("Delete Permanently"))
aDelete.triggered.connect(lambda: self.permanentlyDeleteItem(tHandle))
else:
ctxMenu.addAction(
self.tr("Move to Trash"), lambda: self.moveItemToTrash(tHandle)
)
aMoveTrash = ctxMenu.addAction(self.tr("Move to Trash"))
aMoveTrash.triggered.connect(lambda: self.moveItemToTrash(tHandle))
# Show Context Menu
ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
+8 -10
View File
@@ -208,6 +208,7 @@ class GuiMain(QMainWindow):
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
self.projView.projectSettingsRequest.connect(self.showProjectSettingsDialog)
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
self.novelView.openDocumentRequest.connect(self._openDocument)
@@ -806,15 +807,11 @@ class GuiMain(QMainWindow):
logger.error("No project open")
return False
if tHandle is None:
if self.docEditor.anyFocus() or self.isFocusMode:
tHandle = self.docEditor.docHandle()
else:
tHandle = self.projView.getSelectedHandle()
if tHandle:
return self.projView.renameTreeItem(tHandle)
if tHandle is None and (self.docEditor.anyFocus() or self.isFocusMode):
tHandle = self.docEditor.docHandle()
self.projView.renameTreeItem(tHandle)
return False
return True
def rebuildTrees(self):
"""Rebuild the project tree.
@@ -921,14 +918,15 @@ class GuiMain(QMainWindow):
return
def showProjectSettingsDialog(self):
@pyqtSlot(int)
def showProjectSettingsDialog(self, focusTab=GuiProjectSettings.TAB_MAIN):
"""Open the project settings dialog.
"""
if not self.hasProject:
logger.error("No project open")
return False
dlgProj = GuiProjectSettings(self)
dlgProj = GuiProjectSettings(self, focusTab=focusTab)
dlgProj.exec_()
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>
+334 -158
View File
@@ -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/>.
"""
import os
import pytest
from shutil import copyfile
from tools import cmpFiles, getGuiItem, buildTestProject
from novelwriter.enum import nwItemType
from tools import C, getGuiItem, buildTestProject
from PyQt5.QtGui import QColor
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog, QAction, QMessageBox, QColorDialog
from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.dialogs.projsettings import GuiProjectSettings
keyDelay = 2
typeDelay = 1
stepDelay = 20
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
importKeys = ["i000004", "i000005", "i000006", "i000007"]
@pytest.mark.gui
def testDlgProjSettings_Dialog(
qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDir, refDir, mockRnd
):
"""Test the full project settings dialog.
def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
"""Test the main dialog class. Saving settings is not tested in this
test, but are instead tested in the individual tab tests.
"""
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
monkeypatch.setattr(QMessageBox, "question", 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
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
assert getGuiItem("GuiProjectSettings") is None
# Create new project
buildTestProject(nwGUI, fncProj)
nwGUI.mainConf.backupPath = fncDir
# Pretend we have a project
nwGUI.hasProject = True
nwGUI.theProject.setSpellLang("en")
nwGUI.theProject.setBookAuthors("Jane Smith\nJohn Smith")
nwGUI.theProject.setAutoReplace({"A": "B", "C": "D"})
# 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)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
@@ -77,83 +67,189 @@ def testDlgProjSettings_Dialog(
projEdit.show()
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
# ============
assert projEdit.tabMain.editName.text() == "New Project"
assert projEdit.tabMain.editTitle.text() == "New Novel"
assert projEdit.tabMain.editAuthors.toPlainText() == "Jane Smith\nJohn Smith"
assert projEdit.tabMain.spellLang.currentData() == "en"
assert projEdit.tabMain.doBackup.isChecked() is False
tabMain = projSettings.tabMain
assert tabMain.editName.text() == "New Project"
assert tabMain.editTitle.text() == "New Novel"
assert tabMain.editAuthors.toPlainText() == "Jane Smith\nJohn Smith"
assert tabMain.spellLang.currentData() == "en"
assert tabMain.doBackup.isChecked() is False
qtbot.wait(stepDelay)
projEdit.tabMain.editName.setText("")
tabMain.editName.setText("")
for c in "Project Name":
qtbot.keyClick(projEdit.tabMain.editName, c, delay=typeDelay)
projEdit.tabMain.editTitle.setText("")
qtbot.keyClick(tabMain.editName, c, delay=typeDelay)
tabMain.editTitle.setText("")
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":
qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=typeDelay)
qtbot.keyClick(projEdit.tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
qtbot.keyClick(tabMain.editAuthors, c, delay=typeDelay)
qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
for c in "John Doh":
qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=typeDelay)
qtbot.keyClick(projEdit.tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
qtbot.keyClick(tabMain.editAuthors, c, delay=typeDelay)
qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
qtbot.wait(stepDelay)
assert projEdit.tabMain.editName.text() == "Project Name"
assert projEdit.tabMain.editTitle.text() == "Project Title"
assert projEdit.tabMain.editAuthors.toPlainText() == "Jane Doe\nJohn Doh\n"
assert tabMain.editName.text() == "Project Name"
assert tabMain.editTitle.text() == "Project Title"
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
# ==========
projEdit._tabBox.setCurrentWidget(projEdit.tabStatus)
tabStatus = projSettings.tabStatus
assert projEdit.tabStatus.colChanged is False
assert projEdit.tabStatus.getNewList() == ([], [])
assert projEdit.tabStatus.listBox.topLevelItemCount() == 4
assert tabStatus.colChanged is False
assert tabStatus.getNewList() == ([], [])
assert tabStatus.listBox.topLevelItemCount() == 4
# Can't delete the first item (it's in use)
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True)
qtbot.mouseClick(projEdit.tabStatus.delButton, Qt.LeftButton)
assert projEdit.tabStatus.listBox.topLevelItemCount() == 4
tabStatus.listBox.clearSelection()
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(0))
qtbot.mouseClick(tabStatus.delButton, Qt.LeftButton)
assert tabStatus.listBox.topLevelItemCount() == 4
# Can delete the third item
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus.listBox.topLevelItem(2).setSelected(True)
qtbot.mouseClick(projEdit.tabStatus.delButton, Qt.LeftButton)
assert projEdit.tabStatus.listBox.topLevelItemCount() == 3
# Can delete the second item
tabStatus.listBox.clearSelection()
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(1))
qtbot.mouseClick(tabStatus.delButton, Qt.LeftButton)
assert tabStatus.listBox.topLevelItemCount() == 3
# Add a new item
monkeypatch.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
qtbot.mouseClick(projEdit.tabStatus.addButton, Qt.LeftButton)
projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True)
for n in range(8):
qtbot.keyClick(projEdit.tabStatus.editName, Qt.Key_Backspace, delay=typeDelay)
for c in "Final":
qtbot.keyClick(projEdit.tabStatus.editName, c, delay=typeDelay)
qtbot.mouseClick(projEdit.tabStatus.colButton, Qt.LeftButton)
qtbot.mouseClick(projEdit.tabStatus.saveButton, Qt.LeftButton)
assert projEdit.tabStatus.listBox.topLevelItemCount() == 4
qtbot.wait(stepDelay)
with monkeypatch.context() as mp:
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
qtbot.mouseClick(tabStatus.addButton, Qt.LeftButton)
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
for _ in range(8):
qtbot.keyClick(tabStatus.editName, Qt.Key_Backspace, delay=typeDelay)
for c in "Final":
qtbot.keyClick(tabStatus.editName, c, delay=typeDelay)
qtbot.mouseClick(tabStatus.colButton, Qt.LeftButton)
qtbot.mouseClick(tabStatus.saveButton, Qt.LeftButton)
assert tabStatus.listBox.topLevelItemCount() == 4
assert projEdit.tabStatus.colChanged is True
assert projEdit.tabStatus.getNewList() == (
assert tabStatus.colChanged is True
assert tabStatus.getNewList() == (
[
{
"key": statusKeys[0],
"key": C.sNew,
"name": "New",
"cols": (100, 100, 100)
}, {
"key": statusKeys[1],
"name": "Note",
"cols": (200, 50, 0)
"key": C.sDraft,
"name": "Draft",
"cols": (200, 150, 0)
}, {
"key": statusKeys[3],
"key": C.sFinished,
"name": "Finished",
"cols": (50, 200, 0)
}, {
@@ -162,121 +258,201 @@ def testDlgProjSettings_Dialog(
"cols": (20, 30, 40)
}
], [
statusKeys[2] # Deleted item
C.sNote # Deleted item
]
)
# Move items
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus._moveItem(1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
statusKeys[0], statusKeys[1], statusKeys[3], None
# Move items, none selected -> no change
tabStatus.listBox.clearSelection()
tabStatus._moveItem(1)
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
C.sNew, C.sDraft, C.sFinished, None
]
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True)
projEdit.tabStatus._moveItem(-1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
statusKeys[0], statusKeys[1], statusKeys[3], None
# Move items, first selected, move up -> no change
tabStatus.listBox.clearSelection()
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(0))
tabStatus._moveItem(-1)
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
C.sNew, C.sDraft, C.sFinished, None
]
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True)
projEdit.tabStatus._moveItem(-1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
statusKeys[0], statusKeys[1], None, statusKeys[3]
# Move items, last selected, move up -> allowed
tabStatus.listBox.clearSelection()
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
tabStatus._moveItem(-1)
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]] == [
statusKeys[0], statusKeys[1], statusKeys[3], None
# Move items, same selected, move down -> allowed
tabStatus._moveItem(1)
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
C.sNew, C.sDraft, C.sFinished, None
]
# Importance Tab
# ==============
projEdit._tabBox.setCurrentWidget(projEdit.tabImport)
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabImport.listBox.topLevelItem(3).setSelected(True)
qtbot.mouseClick(projEdit.tabImport.delButton, Qt.LeftButton)
qtbot.mouseClick(projEdit.tabImport.addButton, Qt.LeftButton)
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabImport.listBox.topLevelItem(3).setSelected(True)
for n in range(8):
qtbot.keyClick(projEdit.tabImport.editName, Qt.Key_Backspace, delay=typeDelay)
for c in "Final":
qtbot.keyClick(projEdit.tabImport.editName, c, delay=typeDelay)
qtbot.mouseClick(projEdit.tabImport.saveButton, Qt.LeftButton)
qtbot.wait(stepDelay)
tabImport = projSettings.tabImport
projSettings._focusTab(GuiProjectSettings.TAB_IMPORT)
# Delete unused entry
tabImport.listBox.clearSelection()
tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(1))
qtbot.mouseClick(tabImport.delButton, Qt.LeftButton)
assert tabImport.listBox.topLevelItemCount() == 3
# Add a new entry
with monkeypatch.context() as mp:
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
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
# ================
qtbot.wait(stepDelay)
projEdit._tabBox.setCurrentWidget(projEdit.tabReplace)
tabReplace = projSettings.tabReplace
assert projEdit.tabReplace.listBox.topLevelItem(0).text(0) == "<A>"
assert projEdit.tabReplace.listBox.topLevelItem(0).text(1) == "B"
assert projEdit.tabReplace.listBox.topLevelItem(1).text(0) == "<C>"
assert projEdit.tabReplace.listBox.topLevelItem(1).text(1) == "D"
assert tabReplace.listBox.topLevelItem(0).text(0) == "<A>"
assert tabReplace.listBox.topLevelItem(0).text(1) == "B"
assert tabReplace.listBox.topLevelItem(1).text(0) == "<C>"
assert tabReplace.listBox.topLevelItem(1).text(1) == "D"
assert tabReplace.listBox.topLevelItemCount() == 2
qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton)
projEdit.tabReplace.listBox.topLevelItem(2).setSelected(True)
projEdit.tabReplace.editKey.setText("")
# Nothing to save or delete
tabReplace.listBox.clearSelection()
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 ":
qtbot.keyClick(projEdit.tabReplace.editKey, c, delay=typeDelay)
projEdit.tabReplace.editValue.setText("")
qtbot.keyClick(tabReplace.editKey, c, delay=typeDelay)
tabReplace.editValue.setText("")
for c in "With This Stuff ":
qtbot.keyClick(projEdit.tabReplace.editValue, c, delay=typeDelay)
qtbot.mouseClick(projEdit.tabReplace.saveButton, Qt.LeftButton)
qtbot.keyClick(tabReplace.editValue, c, delay=typeDelay)
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)
projEdit.tabReplace.listBox.clearSelection()
assert not projEdit.tabReplace._saveEntry()
assert not projEdit.tabReplace._delEntry()
qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton)
# Create a new entry again
tabReplace.listBox.clearSelection()
qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton)
assert tabReplace.listBox.topLevelItemCount() == 4
# The list is sorted, so we must find it
newIdx = -1
for i in range(projEdit.tabReplace.listBox.topLevelItemCount()):
if projEdit.tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>":
for i in range(tabReplace.listBox.topLevelItemCount()):
if tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>":
newIdx = i
break
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
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
# Clean up
# qtbot.stop()
projSettings._doClose()
projEdit = getGuiItem("GuiProjectSettings")
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
# END Test testDlgProjSettings_Replace
+1 -1
View File
@@ -1144,7 +1144,7 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
assert nwGUI.openDocument(cHandle) is True
assert nwGUI.docEditor.replaceText(theText) is True
assert nwGUI.saveDocument() is True
assert nwGUI.projView.revealNewTreeItem(cHandle)
assert nwGUI.projView.projTree.revealNewTreeItem(cHandle)
nwGUI.docEditor.updateTagHighLighting()
# Follow Tag
+2 -2
View File
@@ -183,7 +183,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
# Add a second novel folder
newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL)
nwGUI.projView.revealNewTreeItem(newHandle)
nwGUI.projView.projTree.revealNewTreeItem(newHandle)
# Check new values in dropdown list
assert outlineBar.novelValue.itemData(0) == lipHandle
@@ -202,7 +202,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
aHandle = nwGUI.theProject.newFile(dTitle, newHandle)
hHash = "#"*hLevel
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()
+61 -59
View File
@@ -160,11 +160,11 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
# ============
# 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
nHandle = theProject.newFile("Test", None)
assert projView.revealNewTreeItem(nHandle) is False
assert projView.projTree.revealNewTreeItem(nHandle) is False
# Clean up
# qtbot.stop()
@@ -184,10 +184,11 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
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
assert nwTree.projTree.moveTreeItem(1) is False
assert projView.projTree.moveTreeItem(1) is False
# Create a project
prjDir = os.path.join(fncDir, "project")
@@ -197,68 +198,68 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
# ==============
# Add some files
nwTree.setSelectedHandle(C.hChapterDir)
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
projView.setSelectedHandle(C.hChapterDir)
assert projTree.newTreeItem(nwItemType.FILE) is True
assert projTree.newTreeItem(nwItemType.FILE) is True
assert projTree.newTreeItem(nwItemType.FILE) is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010", "0000000000011", "0000000000012",
]
# Move with no selections
nwTree.projTree.clearSelection()
assert nwTree.projTree.moveTreeItem(1) is False
projTree.clearSelection()
assert projTree.moveTreeItem(1) is False
# Move second item up twice (should give same result)
nwTree.setSelectedHandle(C.hSceneDoc)
assert nwTree.projTree.moveTreeItem(-1) is True
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
projView.setSelectedHandle(C.hSceneDoc)
assert projTree.moveTreeItem(-1) is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hSceneDoc, C.hChapterDoc,
"0000000000010", "0000000000011", "0000000000012",
]
assert nwTree.projTree.moveTreeItem(-1) is False
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
assert projTree.moveTreeItem(-1) is False
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hSceneDoc, C.hChapterDoc,
"0000000000010", "0000000000011", "0000000000012",
]
# Restore
assert nwTree.projTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
assert projTree.moveTreeItem(1) is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010", "0000000000011", "0000000000012",
]
# Move fifth item down twice (should give same result)
nwTree.setSelectedHandle("0000000000011")
assert nwTree.projTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
projView.setSelectedHandle("0000000000011")
assert projTree.moveTreeItem(1) is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010", "0000000000012", "0000000000011",
]
assert nwTree.projTree.moveTreeItem(1) is False
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
assert projTree.moveTreeItem(1) is False
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010", "0000000000012", "0000000000011",
]
# Restore
assert nwTree.projTree.moveTreeItem(-1) is True
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
assert projTree.moveTreeItem(-1) is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010", "0000000000011", "0000000000012",
]
# Move down again, and restore via undo
nwTree.setSelectedHandle("0000000000011")
assert nwTree.projTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
projView.setSelectedHandle("0000000000011")
assert projTree.moveTreeItem(1) is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010", "0000000000012", "0000000000011",
]
assert nwTree.projTree.undoLastMove() is True
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
assert projTree.undoLastMove() is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010", "0000000000011", "0000000000012",
]
@@ -266,19 +267,19 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
# Root Folder
# ===========
nwTree.setSelectedHandle(C.hNovelRoot)
projView.setSelectedHandle(C.hNovelRoot)
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
# 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
# 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
# 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
# Clean up
@@ -299,76 +300,77 @@ def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir,
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
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
assert nwView.requestDeleteItem() is False
assert projView.requestDeleteItem() is False
# Create a project
prjDir = os.path.join(fncDir, "project")
buildTestProject(nwGUI, prjDir)
# 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
nwView.setSelectedHandle(C.hChapterDir)
assert nwView.projTree.newTreeItem(nwItemType.FILE) is True
assert nwView.projTree.newTreeItem(nwItemType.FILE) is True
assert nwView.projTree.newTreeItem(nwItemType.FILE) is True
assert nwView.getTreeFromHandle(C.hChapterDir) == [
projView.setSelectedHandle(C.hChapterDir)
assert projTree.newTreeItem(nwItemType.FILE) is True
assert projTree.newTreeItem(nwItemType.FILE) is True
assert projTree.newTreeItem(nwItemType.FILE) is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010", "0000000000011", "0000000000012",
]
# Delete item without focus -> blocked
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False)
nwView.setSelectedHandle("0000000000012")
assert nwView.requestDeleteItem() is False
projView.setSelectedHandle("0000000000012")
assert projView.requestDeleteItem() is False
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# No selection made
nwView.projTree.clearSelection()
projTree.clearSelection()
caplog.clear()
assert nwView.requestDeleteItem() is False
assert projView.requestDeleteItem() is False
assert "no item to delete" in caplog.text
# Not a valid handle
nwView.projTree.clearSelection()
projTree.clearSelection()
caplog.clear()
assert nwView.requestDeleteItem("0000000000000") is False
assert projView.requestDeleteItem("0000000000000") is False
assert "No tree item with handle '0000000000000'" in caplog.text
# Delete Root Folders
# ===================
assert nwView.requestDeleteItem(C.hNovelRoot) is False # Novel Root is blocked
assert nwView.requestDeleteItem(C.hCharRoot) is True # Character Root
assert projView.requestDeleteItem(C.hNovelRoot) is False # Novel Root is blocked
assert projView.requestDeleteItem(C.hCharRoot) is True # Character Root
# Delete File
# ===========
# Block adding trash folder
funcPointer = nwView.projTree._addTrashRoot
nwView.projTree._addTrashRoot = lambda *a: None
assert nwView.requestDeleteItem("0000000000012") is False
nwView.projTree._addTrashRoot = funcPointer
funcPointer = projTree._addTrashRoot
projTree._addTrashRoot = lambda *a: None
assert projView.requestDeleteItem("0000000000012") is False
projTree._addTrashRoot = funcPointer
# Delete last two documents, which also adds the trash folder
assert nwView.requestDeleteItem("0000000000012") is True
assert nwView.requestDeleteItem("0000000000011") is True
assert nwView.getTreeFromHandle(C.hChapterDir) == [
assert projView.requestDeleteItem("0000000000012") is True
assert projView.requestDeleteItem("0000000000011") is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010"
]
trashHandle = nwGUI.theProject.tree.trashRoot()
assert nwView.getTreeFromHandle(trashHandle) == [
assert projTree.getTreeFromHandle(trashHandle) == [
trashHandle, "0000000000012", "0000000000011"
]
# Try to delete the trash folder
caplog.clear()
assert nwView.requestDeleteItem("0000000000013") is False
assert projView.requestDeleteItem("0000000000013") is False
assert "Cannot delete the Trash folder" in caplog.text
nwGUI.closeProject()
+1 -1
View File
@@ -40,7 +40,7 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot)
newDoc = NWDoc(nwGUI.theProject, cHandle)
newDoc.writeDocument("# A Note\n\n")
nwGUI.projView.revealNewTreeItem(cHandle)
nwGUI.projView.projTree.revealNewTreeItem(cHandle)
nwGUI.rebuildIndex(beQuiet=True)
# Reference Time
+11
View File
@@ -30,6 +30,17 @@ XML_IGNORE = ("<novelWriterXML", "<saveCount", "<autoCount", "<editTime")
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
hInvalid = "0000000000000"
hNovelRoot = "0000000000008"