Update the Project Tree with major improvements (#1079)
This commit is contained in:
@@ -48,7 +48,7 @@ The main shorcuts are as follows:
|
|||||||
":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)"
|
":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)"
|
||||||
":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
|
":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
|
||||||
":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`."
|
":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`."
|
||||||
":kbd:`Ctrl`:kbd:`N`", "Create new document."
|
":kbd:`Ctrl`:kbd:`N`", "Create new project item."
|
||||||
":kbd:`Ctrl`:kbd:`O`", "Open selected document."
|
":kbd:`Ctrl`:kbd:`O`", "Open selected document."
|
||||||
":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
|
":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
|
||||||
":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing."
|
":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing."
|
||||||
@@ -72,7 +72,6 @@ The main shorcuts are as follows:
|
|||||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph."
|
":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph."
|
||||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document."
|
":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document."
|
||||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file."
|
":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file."
|
||||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`N`", "Create new folder."
|
|
||||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project."
|
":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project."
|
||||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer."
|
":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer."
|
||||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project."
|
":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project."
|
||||||
|
|||||||
@@ -502,11 +502,6 @@ class NWIndex:
|
|||||||
"""
|
"""
|
||||||
return self._itemIndex.mainItemHeader(tHandle)
|
return self._itemIndex.mainItemHeader(tHandle)
|
||||||
|
|
||||||
def getHandleHeaderIntLevel(self, tHandle):
|
|
||||||
"""Get the integer header level of the first header of a handle.
|
|
||||||
"""
|
|
||||||
return H_LEVEL.get(self._itemIndex.mainItemHeader(tHandle), 0)
|
|
||||||
|
|
||||||
def getTableOfContents(self, maxDepth, skipExcl=True):
|
def getTableOfContents(self, maxDepth, skipExcl=True):
|
||||||
"""Generate a table of contents up to a maximum depth.
|
"""Generate a table of contents up to a maximum depth.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
|||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
checkString, checkBool, checkInt, isHandle, formatTimeStamp,
|
checkString, checkBool, checkInt, isHandle, formatTimeStamp,
|
||||||
makeFileNameSafe, hexToInt, simplified
|
makeFileNameSafe, hexToInt, minmax, simplified
|
||||||
)
|
)
|
||||||
from novelwriter.constants import trConst, nwFiles, nwLabels
|
from novelwriter.constants import trConst, nwFiles, nwLabels
|
||||||
|
|
||||||
@@ -154,6 +154,8 @@ class NWProject():
|
|||||||
def newFolder(self, label, pHandle):
|
def newFolder(self, label, pHandle):
|
||||||
"""Add a new folder with a given label and parent item.
|
"""Add a new folder with a given label and parent item.
|
||||||
"""
|
"""
|
||||||
|
if pHandle not in self._projTree:
|
||||||
|
return None
|
||||||
newItem = NWItem(self)
|
newItem = NWItem(self)
|
||||||
newItem.setName(label)
|
newItem.setName(label)
|
||||||
newItem.setType(nwItemType.FOLDER)
|
newItem.setType(nwItemType.FOLDER)
|
||||||
@@ -164,6 +166,8 @@ class NWProject():
|
|||||||
def newFile(self, label, pHandle):
|
def newFile(self, label, pHandle):
|
||||||
"""Add a new file with a given label and parent item.
|
"""Add a new file with a given label and parent item.
|
||||||
"""
|
"""
|
||||||
|
if pHandle not in self._projTree:
|
||||||
|
return None
|
||||||
newItem = NWItem(self)
|
newItem = NWItem(self)
|
||||||
newItem.setName(label)
|
newItem.setName(label)
|
||||||
newItem.setType(nwItemType.FILE)
|
newItem.setType(nwItemType.FILE)
|
||||||
@@ -171,6 +175,32 @@ class NWProject():
|
|||||||
self._projTree.updateItemData(newItem.itemHandle)
|
self._projTree.updateItemData(newItem.itemHandle)
|
||||||
return newItem.itemHandle
|
return newItem.itemHandle
|
||||||
|
|
||||||
|
def writeNewFile(self, tHandle, hLevel, isDocument):
|
||||||
|
"""Write content to a new document after it is created. This
|
||||||
|
will not run if the file exists and is not empty.
|
||||||
|
"""
|
||||||
|
tItem = self._projTree[tHandle]
|
||||||
|
if tItem is None:
|
||||||
|
return False
|
||||||
|
if tItem.itemType != nwItemType.FILE:
|
||||||
|
return False
|
||||||
|
|
||||||
|
newDoc = NWDoc(self, tHandle)
|
||||||
|
if newDoc.readDocument().strip():
|
||||||
|
return False
|
||||||
|
|
||||||
|
hshText = "#"*minmax(hLevel, 1, 4)
|
||||||
|
newText = f"{hshText} {tItem.itemName}\n\n"
|
||||||
|
if tItem.isNovelLike() and isDocument:
|
||||||
|
tItem.setLayout(nwItemLayout.DOCUMENT)
|
||||||
|
else:
|
||||||
|
tItem.setLayout(nwItemLayout.NOTE)
|
||||||
|
|
||||||
|
newDoc.writeDocument(newText)
|
||||||
|
self._projIndex.scanText(tHandle, newText)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def trashFolder(self):
|
def trashFolder(self):
|
||||||
"""Add the special trash root folder to the project.
|
"""Add the special trash root folder to the project.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class GuiPreferences(PagedDialog):
|
|||||||
), nwAlert.INFO)
|
), nwAlert.INFO)
|
||||||
|
|
||||||
if refreshTree:
|
if refreshTree:
|
||||||
self.theParent.treeView.buildTree()
|
self.theParent.treeView.populateTree()
|
||||||
|
|
||||||
self._saveWindowSize()
|
self._saveWindowSize()
|
||||||
self.accept()
|
self.accept()
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ from novelwriter.gui.itemdetails import GuiItemDetails
|
|||||||
from novelwriter.gui.mainmenu import GuiMainMenu
|
from novelwriter.gui.mainmenu import GuiMainMenu
|
||||||
from novelwriter.gui.noveltree import GuiNovelTree
|
from novelwriter.gui.noveltree import GuiNovelTree
|
||||||
from novelwriter.gui.outline import GuiOutline
|
from novelwriter.gui.outline import GuiOutline
|
||||||
from novelwriter.gui.projtree import GuiProjectTree
|
from novelwriter.gui.projtree import GuiProjectView
|
||||||
from novelwriter.gui.statusbar import GuiMainStatus
|
from novelwriter.gui.statusbar import GuiMainStatus
|
||||||
from novelwriter.gui.theme import GuiTheme
|
from novelwriter.gui.theme import GuiTheme
|
||||||
from novelwriter.gui.viewsbar import GuiViewsBar
|
from novelwriter.gui.viewsbar import GuiViewsBar
|
||||||
@@ -39,7 +39,7 @@ __all__ = [
|
|||||||
"GuiMainStatus",
|
"GuiMainStatus",
|
||||||
"GuiNovelTree",
|
"GuiNovelTree",
|
||||||
"GuiOutline",
|
"GuiOutline",
|
||||||
"GuiProjectTree",
|
"GuiProjectView",
|
||||||
"GuiTheme",
|
"GuiTheme",
|
||||||
"GuiViewsBar",
|
"GuiViewsBar",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ class GuiItemDetails(QWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot(str, int, int, int)
|
@pyqtSlot(str, int, int, int)
|
||||||
def doUpdateCounts(self, tHandle, cC, wC, pC):
|
def updateCounts(self, tHandle, cC, wC, pC):
|
||||||
"""Update the counts if the handle is the same as the one we're
|
"""Update the counts if the handle is the same as the one we're
|
||||||
already showing. Otherwise, do nothing.
|
already showing. Otherwise, do nothing.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ from PyQt5.QtCore import QUrl
|
|||||||
from PyQt5.QtGui import QDesktopServices
|
from PyQt5.QtGui import QDesktopServices
|
||||||
from PyQt5.QtWidgets import QMenuBar, QAction
|
from PyQt5.QtWidgets import QMenuBar, QAction
|
||||||
|
|
||||||
from novelwriter.enum import nwItemType, nwItemClass, nwDocAction, nwDocInsert, nwWidget
|
from novelwriter.enum import nwDocAction, nwDocInsert, nwWidget
|
||||||
from novelwriter.constants import trConst, nwKeyWords, nwLabels, nwUnicode
|
from novelwriter.constants import trConst, nwKeyWords, nwLabels, nwUnicode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -62,8 +62,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
|
|
||||||
# Function Pointers
|
# Function Pointers
|
||||||
self._docAction = self.theParent.passDocumentAction
|
self._docAction = self.theParent.passDocumentAction
|
||||||
self._moveTreeItem = self.theParent.treeView.moveTreeItem
|
|
||||||
self._newTreeItem = self.theParent.treeView.newTreeItem
|
|
||||||
self._docInsert = self.theParent.docEditor.insertText
|
self._docInsert = self.theParent.docEditor.insertText
|
||||||
self._insertKeyWord = self.theParent.docEditor.insertKeyWord
|
self._insertKeyWord = self.theParent.docEditor.insertKeyWord
|
||||||
|
|
||||||
@@ -164,33 +162,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Project > Separator
|
# Project > Separator
|
||||||
self.projMenu.addSeparator()
|
self.projMenu.addSeparator()
|
||||||
|
|
||||||
# Project > New Root
|
|
||||||
self.rootMenu = self.projMenu.addMenu(self.tr("Create Root Folder"))
|
|
||||||
self.rootItems = {}
|
|
||||||
self.rootItems[nwItemClass.NOVEL] = QAction(self.tr("Novel Root"), self.rootMenu)
|
|
||||||
self.rootItems[nwItemClass.PLOT] = QAction(self.tr("Plot Root"), self.rootMenu)
|
|
||||||
self.rootItems[nwItemClass.CHARACTER] = QAction(self.tr("Character Root"), self.rootMenu)
|
|
||||||
self.rootItems[nwItemClass.WORLD] = QAction(self.tr("Location Root"), self.rootMenu)
|
|
||||||
self.rootItems[nwItemClass.TIMELINE] = QAction(self.tr("Timeline Root"), self.rootMenu)
|
|
||||||
self.rootItems[nwItemClass.OBJECT] = QAction(self.tr("Object Root"), self.rootMenu)
|
|
||||||
self.rootItems[nwItemClass.ENTITY] = QAction(self.tr("Entity Root"), self.rootMenu)
|
|
||||||
self.rootItems[nwItemClass.CUSTOM] = QAction(self.tr("Custom Root"), self.rootMenu)
|
|
||||||
self.rootItems[nwItemClass.ARCHIVE] = QAction(self.tr("Archive Root"), self.rootMenu)
|
|
||||||
for n, itemClass in enumerate(self.rootItems.keys()):
|
|
||||||
self.rootItems[itemClass].triggered.connect(
|
|
||||||
lambda n, itemClass=itemClass: self._newTreeItem(nwItemType.ROOT, itemClass)
|
|
||||||
)
|
|
||||||
self.rootMenu.addAction(self.rootItems[itemClass])
|
|
||||||
|
|
||||||
# Project > New Folder
|
|
||||||
self.aCreateFolder = QAction(self.tr("Create Folder"), self)
|
|
||||||
self.aCreateFolder.setShortcut("Ctrl+Shift+N")
|
|
||||||
self.aCreateFolder.triggered.connect(lambda: self._newTreeItem(nwItemType.FOLDER))
|
|
||||||
self.projMenu.addAction(self.aCreateFolder)
|
|
||||||
|
|
||||||
# Project > Separator
|
|
||||||
self.projMenu.addSeparator()
|
|
||||||
|
|
||||||
# Project > Edit
|
# Project > Edit
|
||||||
self.aEditItem = QAction(self.tr("Edit Item"), self)
|
self.aEditItem = QAction(self.tr("Edit Item"), self)
|
||||||
self.aEditItem.setShortcuts(["Ctrl+E", "F2"])
|
self.aEditItem.setShortcuts(["Ctrl+E", "F2"])
|
||||||
@@ -203,24 +174,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self.aDeleteItem.triggered.connect(lambda: self.theParent.treeView.deleteItem(None))
|
self.aDeleteItem.triggered.connect(lambda: self.theParent.treeView.deleteItem(None))
|
||||||
self.projMenu.addAction(self.aDeleteItem)
|
self.projMenu.addAction(self.aDeleteItem)
|
||||||
|
|
||||||
# Project > Move Up
|
|
||||||
self.aMoveUp = QAction(self.tr("Move Item Up"), self)
|
|
||||||
self.aMoveUp.setShortcut("Ctrl+Up")
|
|
||||||
self.aMoveUp.triggered.connect(lambda: self._moveTreeItem(-1))
|
|
||||||
self.projMenu.addAction(self.aMoveUp)
|
|
||||||
|
|
||||||
# Project > Move Down
|
|
||||||
self.aMoveDown = QAction(self.tr("Move Item Down"), self)
|
|
||||||
self.aMoveDown.setShortcut("Ctrl+Down")
|
|
||||||
self.aMoveDown.triggered.connect(lambda: self._moveTreeItem(1))
|
|
||||||
self.projMenu.addAction(self.aMoveDown)
|
|
||||||
|
|
||||||
# Project > Undo Last Action
|
|
||||||
self.aMoveUndo = QAction(self.tr("Undo Last Move"), self)
|
|
||||||
self.aMoveUndo.setShortcut("Ctrl+Shift+Z")
|
|
||||||
self.aMoveUndo.triggered.connect(lambda: self.theParent.treeView.undoLastMove())
|
|
||||||
self.projMenu.addAction(self.aMoveUndo)
|
|
||||||
|
|
||||||
# Project > Empty Trash
|
# Project > Empty Trash
|
||||||
self.aEmptyTrash = QAction(self.tr("Empty Trash"), self)
|
self.aEmptyTrash = QAction(self.tr("Empty Trash"), self)
|
||||||
self.aEmptyTrash.triggered.connect(lambda: self.theParent.treeView.emptyTrash())
|
self.aEmptyTrash.triggered.connect(lambda: self.theParent.treeView.emptyTrash())
|
||||||
@@ -244,12 +197,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Document
|
# Document
|
||||||
self.docuMenu = self.addMenu(self.tr("&Document"))
|
self.docuMenu = self.addMenu(self.tr("&Document"))
|
||||||
|
|
||||||
# Document > New
|
|
||||||
self.aNewDoc = QAction(self.tr("New Document"), self)
|
|
||||||
self.aNewDoc.setShortcut("Ctrl+N")
|
|
||||||
self.aNewDoc.triggered.connect(lambda: self._newTreeItem(nwItemType.FILE))
|
|
||||||
self.docuMenu.addAction(self.aNewDoc)
|
|
||||||
|
|
||||||
# Document > Open
|
# Document > Open
|
||||||
self.aOpenDoc = QAction(self.tr("Open Document"), self)
|
self.aOpenDoc = QAction(self.tr("Open Document"), self)
|
||||||
self.aOpenDoc.setShortcut("Ctrl+O")
|
self.aOpenDoc.setShortcut("Ctrl+O")
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class GuiOutline(QWidget):
|
|||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
|
|
||||||
|
# Build GUI
|
||||||
self.outlineBar = GuiOutlineToolBar(self)
|
self.outlineBar = GuiOutlineToolBar(self)
|
||||||
self.outlineView = GuiOutlineView(self)
|
self.outlineView = GuiOutlineView(self)
|
||||||
self.outlineData = GuiOutlineDetails(self)
|
self.outlineData = GuiOutlineDetails(self)
|
||||||
@@ -225,6 +226,8 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
|
|
||||||
logger.debug("GuiOutlineToolBar initialisation complete")
|
logger.debug("GuiOutlineToolBar initialisation complete")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|||||||
+514
-308
File diff suppressed because it is too large
Load Diff
+43
-57
@@ -40,7 +40,7 @@ from PyQt5.QtWidgets import (
|
|||||||
|
|
||||||
from novelwriter.gui import (
|
from novelwriter.gui import (
|
||||||
GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu,
|
GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu,
|
||||||
GuiMainStatus, GuiNovelTree, GuiOutline, GuiProjectTree, GuiTheme,
|
GuiMainStatus, GuiNovelTree, GuiOutline, GuiProjectView, GuiTheme,
|
||||||
GuiViewsBar
|
GuiViewsBar
|
||||||
)
|
)
|
||||||
from novelwriter.dialogs import (
|
from novelwriter.dialogs import (
|
||||||
@@ -105,7 +105,7 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
# Main GUI Elements
|
# Main GUI Elements
|
||||||
self.statusBar = GuiMainStatus(self)
|
self.statusBar = GuiMainStatus(self)
|
||||||
self.treeView = GuiProjectTree(self)
|
self.treeView = GuiProjectView(self)
|
||||||
self.novelView = GuiNovelTree(self)
|
self.novelView = GuiNovelTree(self)
|
||||||
self.docEditor = GuiDocEditor(self)
|
self.docEditor = GuiDocEditor(self)
|
||||||
self.viewMeta = GuiDocViewDetails(self)
|
self.viewMeta = GuiDocViewDetails(self)
|
||||||
@@ -115,28 +115,6 @@ class GuiMain(QMainWindow):
|
|||||||
self.mainMenu = GuiMainMenu(self)
|
self.mainMenu = GuiMainMenu(self)
|
||||||
self.viewsBar = GuiViewsBar(self)
|
self.viewsBar = GuiViewsBar(self)
|
||||||
|
|
||||||
# Connect Signals Between Main Elements
|
|
||||||
self.viewsBar.viewChangeRequested.connect(self._changeView)
|
|
||||||
|
|
||||||
self.treeView.itemSelectionChanged.connect(self._treeSingleClick)
|
|
||||||
self.treeView.itemDoubleClicked.connect(self._treeDoubleClick)
|
|
||||||
self.treeView.novelItemChanged.connect(self._treeNovelItemChanged)
|
|
||||||
self.treeView.wordCountsChanged.connect(self._updateStatusWordCount)
|
|
||||||
self.treeView.treeItemChanged.connect(self.docEditor.updateDocInfo)
|
|
||||||
self.treeView.treeItemChanged.connect(self.docViewer.updateDocInfo)
|
|
||||||
self.treeView.treeItemChanged.connect(self.treeMeta.updateViewBox)
|
|
||||||
self.treeView.rootFolderChanged.connect(self.projView.updateRootItem)
|
|
||||||
|
|
||||||
self.docEditor.spellDictionaryChanged.connect(self.statusBar.setLanguage)
|
|
||||||
self.docEditor.docEditedStatusChanged.connect(self.statusBar.doUpdateDocumentStatus)
|
|
||||||
self.docEditor.docCountsChanged.connect(self.treeMeta.doUpdateCounts)
|
|
||||||
self.docEditor.docCountsChanged.connect(self.treeView.doUpdateCounts)
|
|
||||||
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
|
|
||||||
|
|
||||||
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
|
|
||||||
|
|
||||||
self.projView.loadDocumentTagRequest.connect(self._followTag)
|
|
||||||
|
|
||||||
# Project Tree Stack
|
# Project Tree Stack
|
||||||
self.projStack = QStackedWidget()
|
self.projStack = QStackedWidget()
|
||||||
self.projStack.addWidget(self.treeView)
|
self.projStack.addWidget(self.treeView)
|
||||||
@@ -214,6 +192,30 @@ class GuiMain(QMainWindow):
|
|||||||
self.setStatusBar(self.statusBar)
|
self.setStatusBar(self.statusBar)
|
||||||
self.addToolBar(Qt.LeftToolBarArea, self.viewsBar)
|
self.addToolBar(Qt.LeftToolBarArea, self.viewsBar)
|
||||||
|
|
||||||
|
# Connect Signals
|
||||||
|
# ===============
|
||||||
|
|
||||||
|
self.viewsBar.viewChangeRequested.connect(self._changeView)
|
||||||
|
|
||||||
|
self.treeView.selectedItemChanged.connect(self.treeMeta.updateViewBox)
|
||||||
|
self.treeView.openDocumentRequest.connect(self._openDocument)
|
||||||
|
self.treeView.novelItemChanged.connect(self._treeNovelItemChanged)
|
||||||
|
self.treeView.wordCountsChanged.connect(self._updateStatusWordCount)
|
||||||
|
self.treeView.treeItemChanged.connect(self.docEditor.updateDocInfo)
|
||||||
|
self.treeView.treeItemChanged.connect(self.docViewer.updateDocInfo)
|
||||||
|
self.treeView.treeItemChanged.connect(self.treeMeta.updateViewBox)
|
||||||
|
self.treeView.rootFolderChanged.connect(self.projView.updateRootItem)
|
||||||
|
|
||||||
|
self.docEditor.spellDictionaryChanged.connect(self.statusBar.setLanguage)
|
||||||
|
self.docEditor.docEditedStatusChanged.connect(self.statusBar.doUpdateDocumentStatus)
|
||||||
|
self.docEditor.docCountsChanged.connect(self.treeMeta.updateCounts)
|
||||||
|
self.docEditor.docCountsChanged.connect(self.treeView.updateCounts)
|
||||||
|
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
|
||||||
|
|
||||||
|
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
|
||||||
|
|
||||||
|
self.projView.loadDocumentTagRequest.connect(self._followTag)
|
||||||
|
|
||||||
# Finalise Initialisation
|
# Finalise Initialisation
|
||||||
# =======================
|
# =======================
|
||||||
|
|
||||||
@@ -289,7 +291,7 @@ class GuiMain(QMainWindow):
|
|||||||
"""Wrapper function to clear all sub-elements of the main GUI.
|
"""Wrapper function to clear all sub-elements of the main GUI.
|
||||||
"""
|
"""
|
||||||
# Project Area
|
# Project Area
|
||||||
self.treeView.clearTree()
|
self.treeView.clearProject()
|
||||||
self.novelView.clearTree()
|
self.novelView.clearTree()
|
||||||
self.treeMeta.clearDetails()
|
self.treeMeta.clearDetails()
|
||||||
|
|
||||||
@@ -539,7 +541,7 @@ class GuiMain(QMainWindow):
|
|||||||
logger.error("No project open")
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.treeView.saveTreeOrder()
|
self.treeView.saveProjectTree()
|
||||||
if self.theProject.saveProject(autoSave=autoSave):
|
if self.theProject.saveProject(autoSave=autoSave):
|
||||||
self.theProject.index.saveIndex()
|
self.theProject.index.saveIndex()
|
||||||
|
|
||||||
@@ -787,7 +789,7 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
tHandle = None
|
tHandle = None
|
||||||
tLine = None
|
tLine = None
|
||||||
if self.treeView.hasFocus():
|
if self.treeView.treeFocus():
|
||||||
tHandle = self.treeView.getSelectedHandle()
|
tHandle = self.treeView.getSelectedHandle()
|
||||||
elif self.novelView.hasFocus():
|
elif self.novelView.hasFocus():
|
||||||
tHandle, tLine = self.novelView.getSelectedHandle()
|
tHandle, tLine = self.novelView.getSelectedHandle()
|
||||||
@@ -822,7 +824,7 @@ class GuiMain(QMainWindow):
|
|||||||
def rebuildTrees(self):
|
def rebuildTrees(self):
|
||||||
"""Rebuild the project tree.
|
"""Rebuild the project tree.
|
||||||
"""
|
"""
|
||||||
self.treeView.buildTree()
|
self.treeView.populateTree()
|
||||||
self.novelView.refreshTree()
|
self.novelView.refreshTree()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -845,7 +847,7 @@ class GuiMain(QMainWindow):
|
|||||||
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||||
tStart = time()
|
tStart = time()
|
||||||
|
|
||||||
self.treeView.saveTreeOrder()
|
self.treeView.saveProjectTree()
|
||||||
self.theProject.index.clearIndex()
|
self.theProject.index.clearIndex()
|
||||||
|
|
||||||
for tItem in self.theProject.tree:
|
for tItem in self.theProject.tree:
|
||||||
@@ -917,7 +919,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.saveDocument()
|
self.saveDocument()
|
||||||
self.docEditor.initEditor()
|
self.docEditor.initEditor()
|
||||||
self.docViewer.initViewer()
|
self.docViewer.initViewer()
|
||||||
self.treeView.initTree()
|
self.treeView.initSettings()
|
||||||
self.novelView.initTree()
|
self.novelView.initTree()
|
||||||
self.projView.initOutline()
|
self.projView.initOutline()
|
||||||
self._updateStatusWordCount()
|
self._updateStatusWordCount()
|
||||||
@@ -1159,7 +1161,6 @@ class GuiMain(QMainWindow):
|
|||||||
self.mainConf.setViewPanePos(self.splitView.sizes())
|
self.mainConf.setViewPanePos(self.splitView.sizes())
|
||||||
|
|
||||||
self.mainConf.setShowRefPanel(self.viewMeta.isVisible())
|
self.mainConf.setShowRefPanel(self.viewMeta.isVisible())
|
||||||
self.mainConf.setTreeColWidths(self.treeView.getColumnSizes())
|
|
||||||
self.mainConf.setNovelColWidths(self.novelView.getColumnSizes())
|
self.mainConf.setNovelColWidths(self.novelView.getColumnSizes())
|
||||||
if not self.mainConf.isFullScreen:
|
if not self.mainConf.isFullScreen:
|
||||||
self.mainConf.setWinSize(self.width(), self.height())
|
self.mainConf.setWinSize(self.width(), self.height())
|
||||||
@@ -1468,6 +1469,17 @@ class GuiMain(QMainWindow):
|
|||||||
self.viewDocument(tHandle=tHandle, tAnchor=f"#{sTitle}")
|
self.viewDocument(tHandle=tHandle, tAnchor=f"#{sTitle}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(str, Enum)
|
||||||
|
def _openDocument(self, tHandle, tMode):
|
||||||
|
"""Handle an open document request.
|
||||||
|
"""
|
||||||
|
if tHandle is not None:
|
||||||
|
if tMode == nwDocMode.EDIT:
|
||||||
|
self.openDocument(tHandle, changeFocus=False)
|
||||||
|
elif tMode == nwDocMode.VIEW:
|
||||||
|
self.viewDocument(tHandle=tHandle)
|
||||||
|
return
|
||||||
|
|
||||||
@pyqtSlot(nwView)
|
@pyqtSlot(nwView)
|
||||||
def _changeView(self, view):
|
def _changeView(self, view):
|
||||||
"""Handle the requested change of view from the GuiViewBar.
|
"""Handle the requested change of view from the GuiViewBar.
|
||||||
@@ -1531,32 +1543,6 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _treeSingleClick(self):
|
|
||||||
"""Single click on a project tree item just updates the details
|
|
||||||
panel below the tree.
|
|
||||||
"""
|
|
||||||
tHandle = self.treeView.getSelectedHandle()
|
|
||||||
if tHandle is not None:
|
|
||||||
self.treeMeta.updateViewBox(tHandle)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot("QTreeWidgetItem*", int)
|
|
||||||
def _treeDoubleClick(self, tItem, colNo):
|
|
||||||
"""The user double-clicked an item in the tree. If it is a file,
|
|
||||||
we open it. Otherwise, we toggle the expanded status.
|
|
||||||
"""
|
|
||||||
tHandle = self.treeView.getSelectedHandle()
|
|
||||||
if tHandle is not None:
|
|
||||||
tItem = self.theProject.tree[tHandle]
|
|
||||||
if tItem is None:
|
|
||||||
return
|
|
||||||
if tItem.itemType == nwItemType.FILE:
|
|
||||||
self.openDocument(tHandle, changeFocus=False, doScroll=False)
|
|
||||||
else:
|
|
||||||
self.treeView.toggleExpanded(tHandle)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _treeNovelItemChanged(self):
|
def _treeNovelItemChanged(self):
|
||||||
"""Triggered when there is a change to a novel item in the
|
"""Triggered when there is a change to a novel item in the
|
||||||
|
|||||||
+13
-13
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-05 15:03:00">
|
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-11 13:36:01">
|
||||||
<project>
|
<project>
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
<author>Jay Doh</author>
|
<author>Jay Doh</author>
|
||||||
<saveCount>1334</saveCount>
|
<saveCount>1345</saveCount>
|
||||||
<autoCount>225</autoCount>
|
<autoCount>229</autoCount>
|
||||||
<editTime>67746</editTime>
|
<editTime>68286</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
<doBackup>False</doBackup>
|
<doBackup>False</doBackup>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<spellCheck>True</spellCheck>
|
<spellCheck>True</spellCheck>
|
||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<autoOutline>True</autoOutline>
|
<autoOutline>True</autoOutline>
|
||||||
<lastEdited>a520879ca0b45</lastEdited>
|
<lastEdited>636b6aa9b697b</lastEdited>
|
||||||
<lastViewed>636b6aa9b697b</lastViewed>
|
<lastViewed>636b6aa9b697b</lastViewed>
|
||||||
<lastWordCount>1363</lastWordCount>
|
<lastWordCount>1363</lastWordCount>
|
||||||
<novelWordCount>954</novelWordCount>
|
<novelWordCount>954</novelWordCount>
|
||||||
@@ -33,13 +33,13 @@
|
|||||||
<section></section>
|
<section></section>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="sf12341" count="7" red="100" green="100" blue="100">New</entry>
|
<entry key="sf12341" count="5" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="sf24ce6" count="1" red="200" green="50" blue="0">Notes</entry>
|
<entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
|
||||||
<entry key="sc24b8f" count="2" red="182" green="60" blue="0">Started</entry>
|
<entry key="sc24b8f" count="2" red="182" green="60" blue="0">Started</entry>
|
||||||
<entry key="s90e6c9" count="6" red="193" green="129" blue="0">1st Draft</entry>
|
<entry key="s90e6c9" count="7" red="193" green="129" blue="0">1st Draft</entry>
|
||||||
<entry key="sd51c5b" count="1" red="193" green="129" blue="0">2nd Draft</entry>
|
<entry key="sd51c5b" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||||
<entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
<entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||||
<entry key="s78ea90" count="0" red="58" green="180" blue="58">Finished</entry>
|
<entry key="s78ea90" count="1" red="58" green="180" blue="58">Finished</entry>
|
||||||
</status>
|
</status>
|
||||||
<importance>
|
<importance>
|
||||||
<entry key="ia857f0" count="5" red="100" green="100" blue="100">None</entry>
|
<entry key="ia857f0" count="5" red="100" green="100" blue="100">None</entry>
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
</item>
|
</item>
|
||||||
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
<meta expanded="False" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||||
<name status="sf12341" import="ia857f0" exported="True">Part One</name>
|
<name status="s90e6c9" import="ia857f0" exported="True">Part One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="True" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
<meta expanded="True" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
||||||
@@ -79,11 +79,11 @@
|
|||||||
</item>
|
</item>
|
||||||
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
<meta expanded="False" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
||||||
<name status="sf12341" import="ia857f0" exported="True">Interlude</name>
|
<name status="s78ea90" import="ia857f0" exported="True">Interlude</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
||||||
<meta expanded="False" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
<meta expanded="False" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
||||||
<name status="sd51c5b" import="ia857f0" exported="False">A Note on Structure</name>
|
<name status="sf24ce6" import="ia857f0" exported="False">A Note on Structure</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="True" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
|
<meta expanded="True" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
|
||||||
|
|||||||
+17
-13
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:08:21">
|
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-06 21:17:27">
|
||||||
<project>
|
<project>
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -16,9 +16,9 @@
|
|||||||
<autoOutline>True</autoOutline>
|
<autoOutline>True</autoOutline>
|
||||||
<lastEdited>None</lastEdited>
|
<lastEdited>None</lastEdited>
|
||||||
<lastViewed>None</lastViewed>
|
<lastViewed>None</lastViewed>
|
||||||
<lastWordCount>0</lastWordCount>
|
<lastWordCount>2</lastWordCount>
|
||||||
<novelWordCount>0</novelWordCount>
|
<novelWordCount>1</novelWordCount>
|
||||||
<notesWordCount>0</notesWordCount>
|
<notesWordCount>1</notesWordCount>
|
||||||
<autoReplace/>
|
<autoReplace/>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<title>%title%</title>
|
||||||
@@ -28,19 +28,19 @@
|
|||||||
<section></section>
|
<section></section>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000008" count="5" red="100" green="100" blue="100">New</entry>
|
<entry key="s000008" count="7" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
|
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
|
||||||
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
|
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||||
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
|
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||||
</status>
|
</status>
|
||||||
<importance>
|
<importance>
|
||||||
<entry key="i00000c" count="3" red="100" green="100" blue="100">New</entry>
|
<entry key="i00000c" count="4" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
|
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||||
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
|
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
|
||||||
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
|
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content count="10">
|
<content count="11">
|
||||||
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Novel</name>
|
<name status="s000008" import="i00000c">Novel</name>
|
||||||
@@ -73,13 +73,17 @@
|
|||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">New Scene</name>
|
<name status="s000008" import="i00000c" exported="True">New Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000028" parent="31489056e0916" root="None" order="0" type="FILE" class="NO_CLASS" layout="NO_LAYOUT">
|
<item handle="0000000000028" parent="0000000000015" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False"/>
|
||||||
<name status="None" import="None" exported="True">Hello</name>
|
<name status="s000008" import="i00000c">Stuff</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000029" parent="71ee45a3c0db9" root="None" order="0" type="FILE" class="NO_CLASS" layout="NO_LAYOUT">
|
<item handle="0000000000029" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" charCount="5" wordCount="1" paraCount="0" cursorPos="0"/>
|
||||||
<name status="None" import="None" exported="True">Jane</name>
|
<name status="s000008" import="i00000c" exported="True">Hello</name>
|
||||||
|
</item>
|
||||||
|
<item handle="000000000002a" parent="0000000000012" root="0000000000012" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" charCount="4" wordCount="1" paraCount="0" cursorPos="0"/>
|
||||||
|
<name status="s000008" import="i00000c" exported="True">Jane</name>
|
||||||
</item>
|
</item>
|
||||||
</content>
|
</content>
|
||||||
</novelWriterXML>
|
</novelWriterXML>
|
||||||
@@ -232,9 +232,6 @@ def testCoreIndex_CheckThese(mockGUI, fncDir, mockRnd):
|
|||||||
|
|
||||||
assert theIndex.getHandleHeaderLevel(cHandle) == "H1"
|
assert theIndex.getHandleHeaderLevel(cHandle) == "H1"
|
||||||
assert theIndex.getHandleHeaderLevel(nHandle) == "H1"
|
assert theIndex.getHandleHeaderLevel(nHandle) == "H1"
|
||||||
assert theIndex.getHandleHeaderIntLevel(cHandle) == 1
|
|
||||||
assert theIndex.getHandleHeaderIntLevel(nHandle) == 1
|
|
||||||
assert theIndex.getHandleHeaderIntLevel("stuff") == 0
|
|
||||||
|
|
||||||
# Zero Items
|
# Zero Items
|
||||||
assert theIndex.checkThese([], cItem) == []
|
assert theIndex.checkThese([], cItem) == []
|
||||||
|
|||||||
@@ -29,10 +29,14 @@ from lxml import etree
|
|||||||
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE
|
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE
|
||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
|
|
||||||
from novelwriter.core.project import NWProject
|
|
||||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||||
from novelwriter.common import formatTimeStamp
|
from novelwriter.common import formatTimeStamp
|
||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
|
from novelwriter.core.tree import NWTree
|
||||||
|
from novelwriter.core.index import NWIndex
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
from novelwriter.core.options import OptionState
|
||||||
|
from novelwriter.core.document import NWDoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
@@ -280,12 +284,12 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
|
def testCoreProject_NewFileFolder(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||||
"""Check that new files can be added to the project.
|
"""Check that new files can be added to the project.
|
||||||
"""
|
"""
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx")
|
testFile = os.path.join(outDir, "coreProject_NewFileFolder_nwProject.nwx")
|
||||||
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
|
compFile = os.path.join(refDir, "coreProject_NewFileFolder_nwProject.nwx")
|
||||||
|
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
buildTestProject(theProject, fncDir)
|
buildTestProject(theProject, fncDir)
|
||||||
@@ -295,9 +299,33 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
|
|||||||
assert theProject.closeProject() is True
|
assert theProject.closeProject() is True
|
||||||
assert theProject.openProject(projFile) is True
|
assert theProject.openProject(projFile) is True
|
||||||
|
|
||||||
assert isinstance(theProject.newFile("Hello", "31489056e0916"), str)
|
# Invalid call
|
||||||
assert isinstance(theProject.newFile("Jane", "71ee45a3c0db9"), str)
|
assert theProject.newFolder("New Folder", "1234567890abc") is None
|
||||||
assert theProject.projChanged
|
assert theProject.newFile("New File", "1234567890abc") is None
|
||||||
|
|
||||||
|
# Add files properly
|
||||||
|
assert theProject.newFolder("Stuff", "0000000000015") == "0000000000028"
|
||||||
|
assert theProject.newFile("Hello", "0000000000015") == "0000000000029"
|
||||||
|
assert theProject.newFile("Jane", "0000000000012") == "000000000002a"
|
||||||
|
|
||||||
|
assert "0000000000028" in theProject.tree
|
||||||
|
assert "0000000000029" in theProject.tree
|
||||||
|
assert "000000000002a" in theProject.tree
|
||||||
|
|
||||||
|
# Write to file, failed
|
||||||
|
assert theProject.writeNewFile("blabla", 1, True) is False # Not a handle
|
||||||
|
assert theProject.writeNewFile("0000000000028", 1, True) is False # Not a file
|
||||||
|
assert theProject.writeNewFile("0000000000014", 1, True) is False # Already has content
|
||||||
|
|
||||||
|
# Write to file, success
|
||||||
|
assert theProject.writeNewFile("0000000000029", 2, True) is True
|
||||||
|
assert NWDoc(theProject, "0000000000029").readDocument() == "## Hello\n\n"
|
||||||
|
|
||||||
|
assert theProject.writeNewFile("000000000002a", 1, False) is True
|
||||||
|
assert NWDoc(theProject, "000000000002a").readDocument() == "# Jane\n\n"
|
||||||
|
|
||||||
|
# Save, close and check
|
||||||
|
assert theProject.projChanged is True
|
||||||
assert theProject.saveProject() is True
|
assert theProject.saveProject() is True
|
||||||
assert theProject.closeProject() is True
|
assert theProject.closeProject() is True
|
||||||
|
|
||||||
@@ -305,7 +333,7 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
|
|||||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
||||||
assert theProject.projChanged is False
|
assert theProject.projChanged is False
|
||||||
|
|
||||||
# END Test testCoreProject_NewFile
|
# END Test testCoreProject_NewFileFolder
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
@@ -613,6 +641,11 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
|||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.openProject(nwMinimal)
|
theProject.openProject(nwMinimal)
|
||||||
|
|
||||||
|
# Storage Objects
|
||||||
|
assert isinstance(theProject.index, NWIndex)
|
||||||
|
assert isinstance(theProject.tree, NWTree)
|
||||||
|
assert isinstance(theProject.options, OptionState)
|
||||||
|
|
||||||
# Move Novel ROOT to after its files
|
# Move Novel ROOT to after its files
|
||||||
oldOrder = [
|
oldOrder = [
|
||||||
"a508bb932959c", # ROOT: Novel
|
"a508bb932959c", # ROOT: Novel
|
||||||
@@ -722,7 +755,7 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
|
|||||||
# Change Importance
|
# Change Importance
|
||||||
# =================
|
# =================
|
||||||
|
|
||||||
fHandle = theProject.newFile("Jane Doe", "8b9d2e465e150")
|
fHandle = theProject.newFile("Jane Doe", "0000000000012")
|
||||||
theProject.tree[fHandle].setImport("Main")
|
theProject.tree[fHandle].setImport("Main")
|
||||||
|
|
||||||
assert theProject.tree[fHandle].itemImport == importKeys[3]
|
assert theProject.tree[fHandle].itemImport == importKeys[3]
|
||||||
@@ -894,6 +927,10 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
|
|||||||
assert theProject.setProjectLang("en_GB") is True
|
assert theProject.setProjectLang("en_GB") is True
|
||||||
assert theProject.projLang == "en_GB"
|
assert theProject.projLang == "en_GB"
|
||||||
|
|
||||||
|
# Language Lookup
|
||||||
|
assert theProject.localLookup(1) == "One"
|
||||||
|
assert theProject.localLookup(10) == "Ten"
|
||||||
|
|
||||||
# Automatic outline update
|
# Automatic outline update
|
||||||
theProject.projChanged = False
|
theProject.projChanged = False
|
||||||
assert theProject.setAutoOutline(True)
|
assert theProject.setAutoOutline(True)
|
||||||
@@ -1007,7 +1044,8 @@ def testCoreProject_OrphanedFiles(mockGUI, nwLipsum):
|
|||||||
|
|
||||||
# Add a file with non-existent parent
|
# Add a file with non-existent parent
|
||||||
# This file will be renoved from the project on open
|
# This file will be renoved from the project on open
|
||||||
assert theProject.newFile("Oops", "0000000000000")
|
oHandle = theProject.newFile("Oops", "b3643d0f92e32")
|
||||||
|
theProject.tree[oHandle].setParent("1234567890abc")
|
||||||
|
|
||||||
# Save and close
|
# Save and close
|
||||||
assert theProject.saveProject() is True
|
assert theProject.saveProject() is True
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import pytest
|
|||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
from tools import getGuiItem, readFile, writeFile, buildTestProject
|
from tools import getGuiItem, readFile, writeFile, buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog
|
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog, QInputDialog
|
||||||
|
|
||||||
from novelwriter.enum import nwItemType, nwWidget
|
from novelwriter.enum import nwItemType, nwWidget
|
||||||
from novelwriter.dialogs import GuiDocMerge, GuiItemEditor
|
from novelwriter.dialogs import GuiDocMerge, GuiItemEditor
|
||||||
@@ -39,6 +39,7 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
# 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.Ok)
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
|
||||||
|
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
|
||||||
|
|
||||||
# Create a new project
|
# Create a new project
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
@@ -56,11 +57,11 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
# Add Project Content
|
# Add Project Content
|
||||||
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
|
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
|
||||||
nwGUI.switchFocus(nwWidget.TREE)
|
nwGUI.switchFocus(nwWidget.TREE)
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem(hChapterDir).setSelected(True)
|
||||||
nwGUI.treeView.newTreeItem(nwItemType.FILE)
|
nwGUI.treeView.projTree.newTreeItem(nwItemType.FILE)
|
||||||
nwGUI.treeView.newTreeItem(nwItemType.FILE)
|
nwGUI.treeView.projTree.newTreeItem(nwItemType.FILE)
|
||||||
nwGUI.treeView.newTreeItem(nwItemType.FILE)
|
nwGUI.treeView.projTree.newTreeItem(nwItemType.FILE)
|
||||||
|
|
||||||
assert nwGUI.saveProject() is True
|
assert nwGUI.saveProject() is True
|
||||||
assert nwGUI.closeProject() is True
|
assert nwGUI.closeProject() is True
|
||||||
@@ -82,8 +83,8 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
|
|
||||||
# Open the Merge tool
|
# Open the Merge tool
|
||||||
nwGUI.switchFocus(nwWidget.TREE)
|
nwGUI.switchFocus(nwWidget.TREE)
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem(hChapterDir).setSelected(True)
|
||||||
|
|
||||||
monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None)
|
monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None)
|
||||||
nwGUI.mainMenu.aMergeDocs.activate(QAction.Trigger)
|
nwGUI.mainMenu.aMergeDocs.activate(QAction.Trigger)
|
||||||
@@ -101,27 +102,27 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
assert nwMerge.listBox.count() == 0
|
assert nwMerge.listBox.count() == 0
|
||||||
|
|
||||||
# No item selected
|
# No item selected
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
assert nwMerge._populateList() is False
|
assert nwMerge._populateList() is False
|
||||||
assert nwMerge.listBox.count() == 0
|
assert nwMerge.listBox.count() == 0
|
||||||
|
|
||||||
# Non-existing item
|
# Non-existing item
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(NWTree, "__getitem__", lambda *a: None)
|
mp.setattr(NWTree, "__getitem__", lambda *a: None)
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem(hChapterDir).setSelected(True)
|
||||||
assert nwMerge._populateList() is False
|
assert nwMerge._populateList() is False
|
||||||
assert nwMerge.listBox.count() == 0
|
assert nwMerge.listBox.count() == 0
|
||||||
|
|
||||||
# Select a non-folder
|
# Select a non-folder
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem(hChapterOne).setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem(hChapterOne).setSelected(True)
|
||||||
assert nwMerge._populateList() is False
|
assert nwMerge._populateList() is False
|
||||||
assert nwMerge.listBox.count() == 0
|
assert nwMerge.listBox.count() == 0
|
||||||
|
|
||||||
# Select the chapter folder
|
# Select the chapter folder
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem(hChapterDir).setSelected(True)
|
||||||
assert nwMerge._populateList() is True
|
assert nwMerge._populateList() is True
|
||||||
assert nwMerge.listBox.count() == 5
|
assert nwMerge.listBox.count() == 5
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import pytest
|
|||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
from tools import getGuiItem, readFile, writeFile, buildTestProject
|
from tools import getGuiItem, readFile, writeFile, buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog
|
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog, QInputDialog
|
||||||
|
|
||||||
from novelwriter.enum import nwItemType, nwWidget
|
from novelwriter.enum import nwItemType, nwWidget
|
||||||
from novelwriter.dialogs import GuiDocSplit, GuiItemEditor
|
from novelwriter.dialogs import GuiDocSplit, GuiItemEditor
|
||||||
@@ -40,6 +40,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
# 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.Ok)
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
|
||||||
|
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
|
||||||
|
|
||||||
# Create a new project
|
# Create a new project
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
@@ -60,9 +61,9 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
# Add Project Content
|
# Add Project Content
|
||||||
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
|
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
|
||||||
nwGUI.switchFocus(nwWidget.TREE)
|
nwGUI.switchFocus(nwWidget.TREE)
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem(hNovelRoot).setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem(hNovelRoot).setSelected(True)
|
||||||
nwGUI.treeView.newTreeItem(nwItemType.FILE)
|
nwGUI.treeView.projTree.newTreeItem(nwItemType.FILE)
|
||||||
|
|
||||||
assert nwGUI.saveProject() is True
|
assert nwGUI.saveProject() is True
|
||||||
assert nwGUI.closeProject() is True
|
assert nwGUI.closeProject() is True
|
||||||
@@ -89,8 +90,8 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
|
|
||||||
# Open the Split tool
|
# Open the Split tool
|
||||||
nwGUI.switchFocus(nwWidget.TREE)
|
nwGUI.switchFocus(nwWidget.TREE)
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem(hToSplit).setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem(hToSplit).setSelected(True)
|
||||||
|
|
||||||
monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None)
|
monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None)
|
||||||
nwGUI.mainMenu.aSplitDoc.activate(QAction.Trigger)
|
nwGUI.mainMenu.aSplitDoc.activate(QAction.Trigger)
|
||||||
@@ -109,7 +110,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
|
|
||||||
# No item selected
|
# No item selected
|
||||||
nwSplit.sourceItem = None
|
nwSplit.sourceItem = None
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
assert nwSplit._populateList() is False
|
assert nwSplit._populateList() is False
|
||||||
assert nwSplit.listBox.count() == 0
|
assert nwSplit.listBox.count() == 0
|
||||||
|
|
||||||
@@ -117,15 +118,15 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(NWTree, "__getitem__", lambda *a: None)
|
mp.setattr(NWTree, "__getitem__", lambda *a: None)
|
||||||
nwSplit.sourceItem = None
|
nwSplit.sourceItem = None
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem(hToSplit).setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem(hToSplit).setSelected(True)
|
||||||
assert nwSplit._populateList() is False
|
assert nwSplit._populateList() is False
|
||||||
assert nwSplit.listBox.count() == 0
|
assert nwSplit.listBox.count() == 0
|
||||||
|
|
||||||
# Select a non-file
|
# Select a non-file
|
||||||
nwSplit.sourceItem = None
|
nwSplit.sourceItem = None
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem(hChapterDir).setSelected(True)
|
||||||
assert nwSplit._populateList() is False
|
assert nwSplit._populateList() is False
|
||||||
assert nwSplit.listBox.count() == 0
|
assert nwSplit.listBox.count() == 0
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ import pytest
|
|||||||
|
|
||||||
from tools import getGuiItem, buildTestProject
|
from tools import getGuiItem, buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QAction, QDialog, QMessageBox
|
from PyQt5.QtWidgets import QAction, QDialog, QMessageBox, QInputDialog
|
||||||
|
|
||||||
from novelwriter.gui import GuiProjectTree
|
|
||||||
from novelwriter.enum import nwItemLayout, nwItemType
|
from novelwriter.enum import nwItemLayout, nwItemType
|
||||||
from novelwriter.dialogs import GuiItemEditor
|
from novelwriter.dialogs import GuiItemEditor
|
||||||
from novelwriter.core.tree import NWTree
|
from novelwriter.core.tree import NWTree
|
||||||
|
from novelwriter.gui.projtree import GuiProjectTree
|
||||||
|
|
||||||
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
|
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
|
||||||
importKeys = ["i000004", "i000005", "i000006", "i000007"]
|
importKeys = ["i000004", "i000005", "i000006", "i000007"]
|
||||||
@@ -52,7 +52,7 @@ def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
tHandle = "000000000000f"
|
tHandle = "000000000000f"
|
||||||
|
|
||||||
# No Selection
|
# No Selection
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
assert nwGUI.editItem() is False
|
assert nwGUI.editItem() is False
|
||||||
|
|
||||||
# Force opening from editor
|
# Force opening from editor
|
||||||
@@ -154,6 +154,7 @@ def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
# Block message box
|
# Block message box
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||||
|
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
|
||||||
|
|
||||||
# Create Project and Open Document
|
# Create Project and Open Document
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
@@ -163,9 +164,9 @@ def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
assert nwGUI.theProject.importItems.name(importKeys[1]) == "Minor"
|
assert nwGUI.theProject.importItems.name(importKeys[1]) == "Minor"
|
||||||
|
|
||||||
# Create Note
|
# Create Note
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem("000000000000a").setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem("000000000000a").setSelected(True)
|
||||||
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
nwGUI.treeView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
|
||||||
|
|
||||||
# Open Note
|
# Open Note
|
||||||
assert nwGUI.openDocument("0000000000010")
|
assert nwGUI.openDocument("0000000000010")
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
nwGUI.treeView.setSelectedHandle("88243afbe5ed8")
|
nwGUI.treeView.setSelectedHandle("88243afbe5ed8")
|
||||||
|
|
||||||
# Middle-click the selected item
|
# Middle-click the selected item
|
||||||
theItem = nwGUI.treeView._getTreeItem("88243afbe5ed8")
|
theItem = nwGUI.treeView.projTree._getTreeItem("88243afbe5ed8")
|
||||||
theRect = nwGUI.treeView.visualItemRect(theItem)
|
theRect = nwGUI.treeView.projTree.visualItemRect(theItem)
|
||||||
qtbot.mouseClick(nwGUI.treeView.viewport(), Qt.MidButton, pos=theRect.center())
|
qtbot.mouseClick(nwGUI.treeView.projTree.viewport(), Qt.MidButton, pos=theRect.center())
|
||||||
assert nwGUI.docViewer.docHandle() == "88243afbe5ed8"
|
assert nwGUI.docViewer.docHandle() == "88243afbe5ed8"
|
||||||
|
|
||||||
# Reload the text
|
# Reload the text
|
||||||
|
|||||||
@@ -26,13 +26,12 @@ from shutil import copyfile
|
|||||||
from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile
|
from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QMessageBox, QDialog
|
from PyQt5.QtWidgets import QMessageBox, QDialog, QInputDialog
|
||||||
|
|
||||||
from novelwriter.gui import (
|
from novelwriter.gui import GuiDocEditor, GuiNovelTree, GuiOutline
|
||||||
GuiDocEditor, GuiProjectTree, GuiNovelTree, GuiOutline
|
|
||||||
)
|
|
||||||
from novelwriter.enum import nwItemType, nwWidget
|
from novelwriter.enum import nwItemType, nwWidget
|
||||||
from novelwriter.tools import GuiProjectWizard
|
from novelwriter.tools import GuiProjectWizard
|
||||||
|
from novelwriter.gui.projtree import GuiProjectTree
|
||||||
from novelwriter.dialogs.itemeditor import GuiItemEditor
|
from novelwriter.dialogs.itemeditor import GuiItemEditor
|
||||||
|
|
||||||
keyDelay = 2
|
keyDelay = 2
|
||||||
@@ -126,7 +125,7 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
mp.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||||
assert nwGUI.docEditor.docHandle() is None
|
assert nwGUI.docEditor.docHandle() is None
|
||||||
nwGUI.treeView._getTreeItem(sHandle).setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem(sHandle).setSelected(True)
|
||||||
nwGUI._keyPressReturn()
|
nwGUI._keyPressReturn()
|
||||||
assert nwGUI.docEditor.docHandle() == sHandle
|
assert nwGUI.docEditor.docHandle() == sHandle
|
||||||
assert nwGUI.closeDocument() is True
|
assert nwGUI.closeDocument() is True
|
||||||
@@ -174,6 +173,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
monkeypatch.setattr(GuiItemEditor, "result", lambda *a: QDialog.Accepted)
|
monkeypatch.setattr(GuiItemEditor, "result", lambda *a: QDialog.Accepted)
|
||||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||||
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
|
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
|
||||||
|
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
|
||||||
|
|
||||||
# Create new, save, close project
|
# Create new, save, close project
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
@@ -220,14 +220,14 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
assert nwGUI.theProject.spellCheck is False
|
assert nwGUI.theProject.spellCheck is False
|
||||||
|
|
||||||
# Check that tree items have been created
|
# Check that tree items have been created
|
||||||
assert nwGUI.treeView._getTreeItem("0000000000008") is not None
|
assert nwGUI.treeView.projTree._getTreeItem("0000000000008") is not None
|
||||||
assert nwGUI.treeView._getTreeItem("0000000000009") is not None
|
assert nwGUI.treeView.projTree._getTreeItem("0000000000009") is not None
|
||||||
assert nwGUI.treeView._getTreeItem("000000000000a") is not None
|
assert nwGUI.treeView.projTree._getTreeItem("000000000000a") is not None
|
||||||
assert nwGUI.treeView._getTreeItem("000000000000b") is not None
|
assert nwGUI.treeView.projTree._getTreeItem("000000000000b") is not None
|
||||||
assert nwGUI.treeView._getTreeItem("000000000000c") is not None
|
assert nwGUI.treeView.projTree._getTreeItem("000000000000c") is not None
|
||||||
assert nwGUI.treeView._getTreeItem("000000000000d") is not None
|
assert nwGUI.treeView.projTree._getTreeItem("000000000000d") is not None
|
||||||
assert nwGUI.treeView._getTreeItem("000000000000e") is not None
|
assert nwGUI.treeView.projTree._getTreeItem("000000000000e") is not None
|
||||||
assert nwGUI.treeView._getTreeItem("000000000000f") is not None
|
assert nwGUI.treeView.projTree._getTreeItem("000000000000f") is not None
|
||||||
|
|
||||||
nwGUI.mainMenu.aSpellCheck.setChecked(True)
|
nwGUI.mainMenu.aSpellCheck.setChecked(True)
|
||||||
assert nwGUI.mainMenu._toggleSpellCheck()
|
assert nwGUI.mainMenu._toggleSpellCheck()
|
||||||
@@ -240,9 +240,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
|
|
||||||
# Add a Character File
|
# Add a Character File
|
||||||
nwGUI.switchFocus(nwWidget.TREE)
|
nwGUI.switchFocus(nwWidget.TREE)
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem("000000000000a").setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem("000000000000a").setSelected(True)
|
||||||
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
nwGUI.treeView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
|
||||||
assert nwGUI.openSelectedItem()
|
assert nwGUI.openSelectedItem()
|
||||||
|
|
||||||
# Type something into the document
|
# Type something into the document
|
||||||
@@ -262,9 +262,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
|
|
||||||
# Add a Plot File
|
# Add a Plot File
|
||||||
nwGUI.switchFocus(nwWidget.TREE)
|
nwGUI.switchFocus(nwWidget.TREE)
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem("0000000000009").setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem("0000000000009").setSelected(True)
|
||||||
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
nwGUI.treeView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
|
||||||
assert nwGUI.openSelectedItem()
|
assert nwGUI.openSelectedItem()
|
||||||
|
|
||||||
# Type something into the document
|
# Type something into the document
|
||||||
@@ -284,9 +284,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
|
|
||||||
# Add a World File
|
# Add a World File
|
||||||
nwGUI.switchFocus(nwWidget.TREE)
|
nwGUI.switchFocus(nwWidget.TREE)
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem("000000000000b").setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem("000000000000b").setSelected(True)
|
||||||
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
nwGUI.treeView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
|
||||||
assert nwGUI.openSelectedItem()
|
assert nwGUI.openSelectedItem()
|
||||||
|
|
||||||
# Add Some Text
|
# Add Some Text
|
||||||
@@ -315,10 +315,10 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
|
|
||||||
# Select the 'New Scene' file
|
# Select the 'New Scene' file
|
||||||
nwGUI.switchFocus(nwWidget.TREE)
|
nwGUI.switchFocus(nwWidget.TREE)
|
||||||
nwGUI.treeView.clearSelection()
|
nwGUI.treeView.projTree.clearSelection()
|
||||||
nwGUI.treeView._getTreeItem("0000000000008").setExpanded(True)
|
nwGUI.treeView.projTree._getTreeItem("0000000000008").setExpanded(True)
|
||||||
nwGUI.treeView._getTreeItem("000000000000d").setExpanded(True)
|
nwGUI.treeView.projTree._getTreeItem("000000000000d").setExpanded(True)
|
||||||
nwGUI.treeView._getTreeItem("000000000000f").setSelected(True)
|
nwGUI.treeView.projTree._getTreeItem("000000000000f").setSelected(True)
|
||||||
assert nwGUI.openSelectedItem()
|
assert nwGUI.openSelectedItem()
|
||||||
|
|
||||||
# Type something into the document
|
# Type something into the document
|
||||||
@@ -461,7 +461,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
qtbot.wait(stepDelay)
|
qtbot.wait(stepDelay)
|
||||||
|
|
||||||
# Check a Quick Create and Delete
|
# Check a Quick Create and Delete
|
||||||
assert nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
assert nwGUI.treeView.projTree.newTreeItem(nwItemType.FILE, None)
|
||||||
newHandle = nwGUI.treeView.getSelectedHandle()
|
newHandle = nwGUI.treeView.getSelectedHandle()
|
||||||
assert nwGUI.theProject.tree["0000000000020"] is not None
|
assert nwGUI.theProject.tree["0000000000020"] is not None
|
||||||
assert nwGUI.treeView.deleteItem()
|
assert nwGUI.treeView.deleteItem()
|
||||||
|
|||||||
@@ -467,7 +467,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
|||||||
|
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
|
||||||
assert nwGUI.treeView._getTreeItem("000000000000f") is not None
|
assert nwGUI.treeView.projTree._getTreeItem("000000000000f") is not None
|
||||||
assert nwGUI.openDocument("000000000000f") is True
|
assert nwGUI.openDocument("000000000000f") is True
|
||||||
nwGUI.docEditor.clear()
|
nwGUI.docEditor.clear()
|
||||||
|
|
||||||
@@ -476,10 +476,10 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
|||||||
assert nwGUI.docEditor.getText() == "hello world"
|
assert nwGUI.docEditor.getText() == "hello world"
|
||||||
nwGUI.docEditor.clear()
|
nwGUI.docEditor.clear()
|
||||||
|
|
||||||
assert not nwGUI.docEditor.insertText(nwDocInsert.NO_INSERT)
|
assert nwGUI.docEditor.insertText(nwDocInsert.NO_INSERT) is False
|
||||||
assert nwGUI.docEditor.isEmpty()
|
assert nwGUI.docEditor.isEmpty()
|
||||||
|
|
||||||
assert not nwGUI.docEditor.insertText(None)
|
assert nwGUI.docEditor.insertText(None) is False
|
||||||
assert nwGUI.docEditor.isEmpty()
|
assert nwGUI.docEditor.isEmpty()
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stopForInteraction()
|
||||||
|
|||||||
@@ -24,11 +24,10 @@ import os
|
|||||||
|
|
||||||
from tools import buildTestProject
|
from tools import buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QAction, QMessageBox
|
from PyQt5.QtWidgets import QMessageBox, QInputDialog, QMenu
|
||||||
|
|
||||||
from novelwriter.guimain import GuiMain
|
|
||||||
from novelwriter.gui.projtree import GuiProjectTree
|
from novelwriter.gui.projtree import GuiProjectTree
|
||||||
from novelwriter.enum import nwItemType, nwItemClass
|
from novelwriter.enum import nwItemLayout, nwItemType, nwItemClass
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
@@ -40,86 +39,101 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
|
|||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(GuiMain, "editItem", lambda *a: None)
|
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
|
||||||
|
|
||||||
nwTree = nwGUI.treeView
|
nwTree = nwGUI.treeView
|
||||||
|
|
||||||
# Try to add item with no project
|
# Try to add item with no project
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is False
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) 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)
|
||||||
|
|
||||||
# No itemType set
|
# No itemType set
|
||||||
nwTree.clearSelection()
|
nwTree.projTree.clearSelection()
|
||||||
assert nwTree.newTreeItem(None) is False
|
assert nwTree.projTree.newTreeItem(None) is False
|
||||||
|
|
||||||
# Root Items
|
# Root Items
|
||||||
# ==========
|
# ==========
|
||||||
|
|
||||||
# No class set
|
# No class set
|
||||||
assert nwTree.newTreeItem(nwItemType.ROOT) is False
|
assert nwTree.projTree.newTreeItem(nwItemType.ROOT) is False
|
||||||
|
|
||||||
# Create root item
|
# Create root item
|
||||||
assert nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) is True
|
||||||
assert "0000000000010" in nwGUI.theProject.tree
|
assert "0000000000010" in nwGUI.theProject.tree
|
||||||
|
|
||||||
# File/Folder Items
|
# File/Folder Items
|
||||||
# =================
|
# =================
|
||||||
|
|
||||||
# No location selected for new item
|
# No location selected for new item
|
||||||
nwTree.clearSelection()
|
nwTree.projTree.clearSelection()
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is False
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
|
||||||
assert nwTree.newTreeItem(nwItemType.FOLDER) is False
|
assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is False
|
||||||
assert "Did not find anywhere" in caplog.text
|
assert "Did not find anywhere" in caplog.text
|
||||||
|
|
||||||
# Create new folder as child of Novel folder
|
# Create new folder as child of Novel folder
|
||||||
nwTree.setSelectedHandle("0000000000008")
|
nwTree.setSelectedHandle("0000000000008")
|
||||||
assert nwTree.newTreeItem(nwItemType.FOLDER) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True
|
||||||
assert nwGUI.theProject.tree["0000000000011"].itemParent == "0000000000008"
|
assert nwGUI.theProject.tree["0000000000011"].itemParent == "0000000000008"
|
||||||
assert nwGUI.theProject.tree["0000000000011"].itemRoot == "0000000000008"
|
assert nwGUI.theProject.tree["0000000000011"].itemRoot == "0000000000008"
|
||||||
assert nwGUI.theProject.tree["0000000000011"].itemClass == nwItemClass.NOVEL
|
assert nwGUI.theProject.tree["0000000000011"].itemClass == nwItemClass.NOVEL
|
||||||
|
|
||||||
# Add a new file in the new folder
|
# Add a new file in the new folder
|
||||||
nwTree.setSelectedHandle("0000000000011")
|
nwTree.setSelectedHandle("0000000000011")
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwGUI.theProject.tree["0000000000012"].itemParent == "0000000000011"
|
assert nwGUI.theProject.tree["0000000000012"].itemParent == "0000000000011"
|
||||||
assert nwGUI.theProject.tree["0000000000012"].itemRoot == "0000000000008"
|
assert nwGUI.theProject.tree["0000000000012"].itemRoot == "0000000000008"
|
||||||
assert nwGUI.theProject.tree["0000000000012"].itemClass == nwItemClass.NOVEL
|
assert nwGUI.theProject.tree["0000000000012"].itemClass == nwItemClass.NOVEL
|
||||||
|
|
||||||
# Add a new file next to the other new file
|
# Add a new chapter next to the other new file
|
||||||
nwTree.setSelectedHandle("0000000000012")
|
nwTree.setSelectedHandle("0000000000012")
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE, hLevel=2) is True
|
||||||
assert nwGUI.theProject.tree["0000000000013"].itemParent == "0000000000011"
|
assert nwGUI.theProject.tree["0000000000013"].itemParent == "0000000000011"
|
||||||
assert nwGUI.theProject.tree["0000000000013"].itemRoot == "0000000000008"
|
assert nwGUI.theProject.tree["0000000000013"].itemRoot == "0000000000008"
|
||||||
assert nwGUI.theProject.tree["0000000000013"].itemClass == nwItemClass.NOVEL
|
assert nwGUI.theProject.tree["0000000000013"].itemClass == nwItemClass.NOVEL
|
||||||
assert nwGUI.openDocument("0000000000013")
|
assert nwGUI.openDocument("0000000000013")
|
||||||
assert nwGUI.docEditor.getText() == "## New Document\n\n"
|
assert nwGUI.docEditor.getText() == "## New Chapter\n\n"
|
||||||
|
|
||||||
|
# Add a new scene next to the other new file
|
||||||
|
nwTree.setSelectedHandle("0000000000012")
|
||||||
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE, hLevel=3) is True
|
||||||
|
assert nwGUI.theProject.tree["0000000000014"].itemParent == "0000000000011"
|
||||||
|
assert nwGUI.theProject.tree["0000000000014"].itemRoot == "0000000000008"
|
||||||
|
assert nwGUI.theProject.tree["0000000000014"].itemClass == nwItemClass.NOVEL
|
||||||
|
assert nwGUI.openDocument("0000000000014")
|
||||||
|
assert nwGUI.docEditor.getText() == "### New Scene\n\n"
|
||||||
|
|
||||||
# Add a new file to the characters folder
|
# Add a new file to the characters folder
|
||||||
nwTree.setSelectedHandle("000000000000a")
|
nwTree.setSelectedHandle("000000000000a")
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True) is True
|
||||||
assert nwGUI.theProject.tree["0000000000014"].itemParent == "000000000000a"
|
assert nwGUI.theProject.tree["0000000000015"].itemParent == "000000000000a"
|
||||||
assert nwGUI.theProject.tree["0000000000014"].itemRoot == "000000000000a"
|
assert nwGUI.theProject.tree["0000000000015"].itemRoot == "000000000000a"
|
||||||
assert nwGUI.theProject.tree["0000000000014"].itemClass == nwItemClass.CHARACTER
|
assert nwGUI.theProject.tree["0000000000015"].itemClass == nwItemClass.CHARACTER
|
||||||
assert nwGUI.openDocument("0000000000014")
|
assert nwGUI.openDocument("0000000000015")
|
||||||
assert nwGUI.docEditor.getText() == "# New Note\n\n"
|
assert nwGUI.docEditor.getText() == "# New Note\n\n"
|
||||||
|
|
||||||
# Make sure the sibling folder bug trap works
|
# Make sure the sibling folder bug trap works
|
||||||
nwTree.setSelectedHandle("0000000000013")
|
nwTree.setSelectedHandle("0000000000013")
|
||||||
nwGUI.theProject.tree["0000000000013"].setParent(None) # This should not happen
|
nwGUI.theProject.tree["0000000000013"].setParent(None) # This should not happen
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is False
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
|
||||||
assert "Internal error" in caplog.text
|
assert "Internal error" in caplog.text
|
||||||
nwGUI.theProject.tree["0000000000013"].setParent("0000000000011")
|
nwGUI.theProject.tree["0000000000013"].setParent("0000000000011")
|
||||||
|
|
||||||
|
# Cancel during creation
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr(QInputDialog, "getText", lambda *a, **k: ("", False))
|
||||||
|
nwTree.setSelectedHandle("0000000000013")
|
||||||
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
|
||||||
|
|
||||||
# Get the trash folder
|
# Get the trash folder
|
||||||
nwTree._addTrashRoot()
|
nwTree.projTree._addTrashRoot()
|
||||||
trashHandle = nwGUI.theProject.trashFolder()
|
trashHandle = nwGUI.theProject.trashFolder()
|
||||||
nwTree.setSelectedHandle(trashHandle)
|
nwTree.setSelectedHandle(trashHandle)
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is False
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
|
||||||
assert "Cannot add new files or folders to the Trash folder" in caplog.text
|
assert "Cannot add new files or folders to the Trash folder" in caplog.text
|
||||||
|
|
||||||
# Other Checks
|
# Other Checks
|
||||||
@@ -148,12 +162,12 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(GuiMain, "editItem", lambda *a: None)
|
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
|
||||||
|
|
||||||
nwTree = nwGUI.treeView
|
nwTree = nwGUI.treeView
|
||||||
|
|
||||||
# Try to move item with no project
|
# Try to move item with no project
|
||||||
assert nwTree.moveTreeItem(1) is False
|
assert nwTree.projTree.moveTreeItem(1) is False
|
||||||
|
|
||||||
# Create a project
|
# Create a project
|
||||||
prjDir = os.path.join(fncDir, "project")
|
prjDir = os.path.join(fncDir, "project")
|
||||||
@@ -164,42 +178,33 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
|
|
||||||
# Add some files
|
# Add some files
|
||||||
nwTree.setSelectedHandle("000000000000d")
|
nwTree.setSelectedHandle("000000000000d")
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||||
"000000000000d", "000000000000e", "000000000000f",
|
"000000000000d", "000000000000e", "000000000000f",
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Move item without focus
|
|
||||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False)
|
|
||||||
assert nwTree.moveTreeItem(1) is False
|
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
|
||||||
"000000000000d", "000000000000e", "000000000000f",
|
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
|
||||||
]
|
|
||||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
|
||||||
|
|
||||||
# Move with no selections
|
# Move with no selections
|
||||||
nwTree.clearSelection()
|
nwTree.projTree.clearSelection()
|
||||||
assert nwTree.moveTreeItem(1) is False
|
assert nwTree.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("000000000000f")
|
nwTree.setSelectedHandle("000000000000f")
|
||||||
assert nwTree.moveTreeItem(-1) is True
|
assert nwTree.projTree.moveTreeItem(-1) is True
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||||
"000000000000d", "000000000000f", "000000000000e",
|
"000000000000d", "000000000000f", "000000000000e",
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
assert nwTree.moveTreeItem(-1) is False
|
assert nwTree.projTree.moveTreeItem(-1) is False
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||||
"000000000000d", "000000000000f", "000000000000e",
|
"000000000000d", "000000000000f", "000000000000e",
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Restore via menu entry
|
# Restore
|
||||||
nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger)
|
assert nwTree.projTree.moveTreeItem(1) is True
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||||
"000000000000d", "000000000000e", "000000000000f",
|
"000000000000d", "000000000000e", "000000000000f",
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
@@ -207,19 +212,19 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
|
|
||||||
# Move fifth item down twice (should give same result)
|
# Move fifth item down twice (should give same result)
|
||||||
nwTree.setSelectedHandle("0000000000011")
|
nwTree.setSelectedHandle("0000000000011")
|
||||||
assert nwTree.moveTreeItem(1) is True
|
assert nwTree.projTree.moveTreeItem(1) is True
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||||
"000000000000d", "000000000000e", "000000000000f",
|
"000000000000d", "000000000000e", "000000000000f",
|
||||||
"0000000000010", "0000000000012", "0000000000011",
|
"0000000000010", "0000000000012", "0000000000011",
|
||||||
]
|
]
|
||||||
assert nwTree.moveTreeItem(1) is False
|
assert nwTree.projTree.moveTreeItem(1) is False
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||||
"000000000000d", "000000000000e", "000000000000f",
|
"000000000000d", "000000000000e", "000000000000f",
|
||||||
"0000000000010", "0000000000012", "0000000000011",
|
"0000000000010", "0000000000012", "0000000000011",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Restore via menu entry
|
# Restore
|
||||||
nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger)
|
assert nwTree.projTree.moveTreeItem(-1) is True
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||||
"000000000000d", "000000000000e", "000000000000f",
|
"000000000000d", "000000000000e", "000000000000f",
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
@@ -227,12 +232,12 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
|
|
||||||
# Move down again, and restore via undo
|
# Move down again, and restore via undo
|
||||||
nwTree.setSelectedHandle("0000000000011")
|
nwTree.setSelectedHandle("0000000000011")
|
||||||
assert nwTree.moveTreeItem(1) is True
|
assert nwTree.projTree.moveTreeItem(1) is True
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||||
"000000000000d", "000000000000e", "000000000000f",
|
"000000000000d", "000000000000e", "000000000000f",
|
||||||
"0000000000010", "0000000000012", "0000000000011",
|
"0000000000010", "0000000000012", "0000000000011",
|
||||||
]
|
]
|
||||||
nwGUI.mainMenu.aMoveUndo.activate(QAction.Trigger)
|
assert nwTree.projTree.undoLastMove() is True
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||||
"000000000000d", "000000000000e", "000000000000f",
|
"000000000000d", "000000000000e", "000000000000f",
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
@@ -245,15 +250,15 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
|
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
|
||||||
|
|
||||||
# Move novel folder up
|
# Move novel folder up
|
||||||
assert nwTree.moveTreeItem(-1) is False
|
assert nwTree.projTree.moveTreeItem(-1) is False
|
||||||
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
|
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
|
||||||
|
|
||||||
# Move novel folder down
|
# Move novel folder down
|
||||||
assert nwTree.moveTreeItem(1) is True
|
assert nwTree.projTree.moveTreeItem(1) is True
|
||||||
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 1
|
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 1
|
||||||
|
|
||||||
# Move novel folder up again
|
# Move novel folder up again
|
||||||
assert nwTree.moveTreeItem(-1) is True
|
assert nwTree.projTree.moveTreeItem(-1) is True
|
||||||
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
|
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
@@ -272,7 +277,7 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
|||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
||||||
monkeypatch.setattr(GuiMain, "editItem", lambda *a: None)
|
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
|
||||||
|
|
||||||
nwTree = nwGUI.treeView
|
nwTree = nwGUI.treeView
|
||||||
|
|
||||||
@@ -289,9 +294,9 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
|||||||
|
|
||||||
# Add some files
|
# Add some files
|
||||||
nwTree.setSelectedHandle("000000000000d")
|
nwTree.setSelectedHandle("000000000000d")
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||||
"000000000000d", "000000000000e", "000000000000f",
|
"000000000000d", "000000000000e", "000000000000f",
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
@@ -304,13 +309,13 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
|||||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||||
|
|
||||||
# No selection made
|
# No selection made
|
||||||
nwTree.clearSelection()
|
nwTree.projTree.clearSelection()
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert nwTree.deleteItem() is False
|
assert nwTree.deleteItem() 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
|
||||||
nwTree.clearSelection()
|
nwTree.projTree.clearSelection()
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert nwTree.deleteItem("0000000000000") is False
|
assert nwTree.deleteItem("0000000000000") is False
|
||||||
assert "Could not find tree item" in caplog.text
|
assert "Could not find tree item" in caplog.text
|
||||||
@@ -326,10 +331,10 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
|||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
# Block adding trash folder
|
# Block adding trash folder
|
||||||
funcPointer = nwTree._addTrashRoot
|
funcPointer = nwTree.projTree._addTrashRoot
|
||||||
nwTree._addTrashRoot = lambda *a: None
|
nwTree.projTree._addTrashRoot = lambda *a: None
|
||||||
assert nwTree.deleteItem("0000000000012") is False
|
assert nwTree.deleteItem("0000000000012") is False
|
||||||
nwTree._addTrashRoot = funcPointer
|
nwTree.projTree._addTrashRoot = funcPointer
|
||||||
|
|
||||||
# Delete last two documents, which also adds the trash folder
|
# Delete last two documents, which also adds the trash folder
|
||||||
assert nwTree.deleteItem("0000000000012") is True
|
assert nwTree.deleteItem("0000000000012") is True
|
||||||
@@ -373,10 +378,10 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
|||||||
|
|
||||||
# Add a folder with two files
|
# Add a folder with two files
|
||||||
nwTree.setSelectedHandle("0000000000009")
|
nwTree.setSelectedHandle("0000000000009")
|
||||||
assert nwTree.newTreeItem(nwItemType.FOLDER) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True
|
||||||
nwTree.setSelectedHandle("0000000000014")
|
nwTree.setSelectedHandle("0000000000014")
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000015.nwd"))
|
assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000015.nwd"))
|
||||||
assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000016.nwd"))
|
assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000016.nwd"))
|
||||||
|
|
||||||
@@ -399,7 +404,7 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
|||||||
|
|
||||||
# Add an empty folder, which can be deleted with no further restrictions
|
# Add an empty folder, which can be deleted with no further restrictions
|
||||||
nwTree.setSelectedHandle("0000000000009")
|
nwTree.setSelectedHandle("0000000000009")
|
||||||
assert nwTree.newTreeItem(nwItemType.FOLDER) is True
|
assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True
|
||||||
assert nwTree.getTreeFromHandle("0000000000009") == ["0000000000009", "0000000000017"]
|
assert nwTree.getTreeFromHandle("0000000000009") == ["0000000000009", "0000000000017"]
|
||||||
|
|
||||||
nwTree.setSelectedHandle("0000000000017")
|
nwTree.setSelectedHandle("0000000000017")
|
||||||
@@ -441,7 +446,7 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
|||||||
assert os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd"))
|
assert os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd"))
|
||||||
|
|
||||||
# Delete proper
|
# Delete proper
|
||||||
assert nwTree._deleteTreeItem("000000000000e") is True
|
assert nwTree.projTree._deleteTreeItem("000000000000e") is True
|
||||||
assert not os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd"))
|
assert not os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd"))
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
@@ -449,3 +454,89 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
|||||||
nwGUI.closeProject()
|
nwGUI.closeProject()
|
||||||
|
|
||||||
# END Test testGuiProjTree_DeleteItems
|
# END Test testGuiProjTree_DeleteItems
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testGuiProjTree_ContextMenu(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||||
|
"""Test the building of the project tree context menu. All this does
|
||||||
|
is test that the menu builds. It doesn't open the actual menu,
|
||||||
|
"""
|
||||||
|
# Block message box
|
||||||
|
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
||||||
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
||||||
|
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||||
|
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
||||||
|
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
|
||||||
|
monkeypatch.setattr(QMenu, "exec_", lambda *a: None)
|
||||||
|
|
||||||
|
# Create a project
|
||||||
|
prjDir = os.path.join(fncDir, "project")
|
||||||
|
buildTestProject(nwGUI, prjDir)
|
||||||
|
|
||||||
|
# Handles for new objects
|
||||||
|
hNovelRoot = "0000000000008"
|
||||||
|
hTitlePage = "000000000000c"
|
||||||
|
hChapterDir = "000000000000d"
|
||||||
|
hChapterFile = "000000000000e"
|
||||||
|
hCharRoot = "000000000000a"
|
||||||
|
hCharNote = "0000000000011"
|
||||||
|
hNovelNote = "0000000000012"
|
||||||
|
|
||||||
|
projTree = nwGUI.treeView.projTree
|
||||||
|
projTree._getTreeItem(hNovelRoot).setExpanded(True)
|
||||||
|
projTree._getTreeItem(hChapterDir).setExpanded(True)
|
||||||
|
|
||||||
|
projTree._addTrashRoot()
|
||||||
|
hTrashRoot = projTree.theProject.tree.trashRoot()
|
||||||
|
|
||||||
|
projTree.setSelectedHandle(hCharRoot)
|
||||||
|
projTree.newTreeItem(nwItemType.FILE)
|
||||||
|
projTree.setSelectedHandle(hNovelRoot)
|
||||||
|
projTree.newTreeItem(nwItemType.FILE, isNote=True)
|
||||||
|
|
||||||
|
def itemPos(tHandle):
|
||||||
|
return projTree.visualItemRect(projTree._getTreeItem(tHandle)).center()
|
||||||
|
|
||||||
|
# No item under menu
|
||||||
|
assert projTree._openContextMenu(projTree.viewport().rect().bottomRight()) is False
|
||||||
|
|
||||||
|
# Generate the possible menu combinarions
|
||||||
|
assert projTree._openContextMenu(itemPos(hTrashRoot)) is True
|
||||||
|
assert projTree._openContextMenu(itemPos(hNovelRoot)) is True
|
||||||
|
assert projTree._openContextMenu(itemPos(hNovelNote)) is True
|
||||||
|
assert projTree._openContextMenu(itemPos(hTitlePage)) is True
|
||||||
|
assert projTree._openContextMenu(itemPos(hChapterDir)) is True
|
||||||
|
assert projTree._openContextMenu(itemPos(hChapterFile)) is True
|
||||||
|
assert projTree._openContextMenu(itemPos(hCharRoot)) is True
|
||||||
|
assert projTree._openContextMenu(itemPos(hCharNote)) is True
|
||||||
|
|
||||||
|
# Direct Edit Functions
|
||||||
|
# =====================
|
||||||
|
# Trigger the dedicated functions the menu entries connect to
|
||||||
|
nwItem = projTree.theProject.tree[hNovelNote]
|
||||||
|
|
||||||
|
# Toggle exported flag
|
||||||
|
assert nwItem.isExported is True
|
||||||
|
projTree._toggleItemExported(hNovelNote)
|
||||||
|
assert nwItem.isExported is False
|
||||||
|
|
||||||
|
# Change item status
|
||||||
|
assert nwItem.itemStatus == "s000000"
|
||||||
|
projTree._changeItemStatus(hNovelNote, "s000001")
|
||||||
|
assert nwItem.itemStatus == "s000001"
|
||||||
|
|
||||||
|
# Change item importance
|
||||||
|
assert nwItem.itemImport == "i000004"
|
||||||
|
projTree._changeItemImport(hNovelNote, "i000005")
|
||||||
|
assert nwItem.itemImport == "i000005"
|
||||||
|
|
||||||
|
# Change item layout
|
||||||
|
assert nwItem.itemLayout == nwItemLayout.NOTE
|
||||||
|
projTree._changeItemLayout(hNovelNote, nwItemLayout.DOCUMENT)
|
||||||
|
assert nwItem.itemLayout == nwItemLayout.DOCUMENT
|
||||||
|
projTree._changeItemLayout(hNovelNote, nwItemLayout.NOTE)
|
||||||
|
assert nwItem.itemLayout == nwItemLayout.NOTE
|
||||||
|
|
||||||
|
# qtbot.stop()
|
||||||
|
|
||||||
|
# END Test testGuiProjTree_ContextMenu
|
||||||
|
|||||||
Reference in New Issue
Block a user