From 01b66bc62e4c961f058e353cf8bdc1cf9d5bf463 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Sep 2022 17:04:21 +0200 Subject: [PATCH 1/3] Add some type and layout checker functions to the item class --- novelwriter/core/item.py | 41 ++++++++++++++++++++++--------- tests/test_core/test_core_item.py | 5 ++++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/novelwriter/core/item.py b/novelwriter/core/item.py index 88650475..95418079 100644 --- a/novelwriter/core/item.py +++ b/novelwriter/core/item.py @@ -292,6 +292,22 @@ class NWItem(): return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, "")) + def getImportStatus(self): + """Return the relevant importance or status label and icon for + the current item based on its class. + """ + if self.isNovelLike(): + stName = self.theProject.statusItems.name(self._status) + stIcon = self.theProject.statusItems.icon(self._status) + else: + stName = self.theProject.importItems.name(self._import) + stIcon = self.theProject.importItems.icon(self._import) + return stName, stIcon + + ## + # Checker Methods + ## + def isNovelLike(self): """Returns true if the item is of a novel-like class. """ @@ -307,17 +323,20 @@ class NWItem(): """ return self._class in (nwItemClass.NO_CLASS, nwItemClass.ARCHIVE, nwItemClass.TRASH) - def getImportStatus(self): - """Return the relevant importance or status label and icon for - the current item based on its class. - """ - if self.isNovelLike(): - stName = self.theProject.statusItems.name(self._status) - stIcon = self.theProject.statusItems.icon(self._status) - else: - stName = self.theProject.importItems.name(self._import) - stIcon = self.theProject.importItems.icon(self._import) - return stName, stIcon + def isRootType(self): + return self._type == nwItemType.ROOT + + def isFolderType(self): + return self._type == nwItemType.FOLDER + + def isFileType(self): + return self._type == nwItemType.FILE + + def isNoteLayout(self): + return self._layout == nwItemLayout.NOTE + + def isDocumentLayout(self): + return self._layout == nwItemLayout.DOCUMENT ## # Special Setters diff --git a/tests/test_core/test_core_item.py b/tests/test_core/test_core_item.py index fbc0ded3..e2fa75e0 100644 --- a/tests/test_core/test_core_item.py +++ b/tests/test_core/test_core_item.py @@ -203,12 +203,16 @@ def testCoreItem_Methods(mockGUI): theItem.setType("ROOT") assert theItem.describeMe() == "Root Folder" + assert theItem.isRootType() is True theItem.setType("FOLDER") assert theItem.describeMe() == "Folder" + assert theItem.isFolderType() is True theItem.setType("FILE") theItem.setLayout("DOCUMENT") + assert theItem.isFileType() is True + assert theItem.isDocumentLayout() is True assert theItem.describeMe() == "Novel Document" assert theItem.describeMe("H0") == "Novel Document" assert theItem.describeMe("H1") == "Novel Title Page" @@ -217,6 +221,7 @@ def testCoreItem_Methods(mockGUI): assert theItem.describeMe("H4") == "Novel Document" theItem.setLayout("NOTE") + assert theItem.isNoteLayout() is True assert theItem.describeMe() == "Project Note" # Status + Icon From 5cba10151a561dfa8c2e972f708a711768257249 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Sep 2022 17:06:16 +0200 Subject: [PATCH 2/3] Make the add new item feature a little smarter --- novelwriter/gui/projtree.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index a1b5bf8e..699938ac 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -41,7 +41,7 @@ from PyQt5.QtWidgets import ( from novelwriter.core import NWDoc from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert -from novelwriter.constants import trConst, nwLabels +from novelwriter.constants import nwHeaders, trConst, nwLabels from novelwriter.dialogs.editlabel import GuiEditLabel logger = logging.getLogger(__name__) @@ -447,16 +447,11 @@ class GuiProjectTree(QTreeWidget): ), nwAlert.ERROR) return False - # If the selected item is a file, the new item will be a - # sibling if the file has no children, otherwise a child + # Collect some information about the selected item that pItem = self.theProject.tree[sHandle] qItem = self._getTreeItem(sHandle) - if pItem.itemType == nwItemType.FILE and qItem.childCount() == 0: - nHandle = sHandle - sHandle = pItem.itemParent - if sHandle is None: - logger.error("Internal error") # Bug - return False + sLevel = nwHeaders.H_LEVEL.get(self.theProject.index.getHandleHeaderLevel(sHandle), 0) + sIsParent = False if qItem is None else qItem.childCount() > 0 if self.theProject.tree.isTrash(sHandle): self.mainGui.makeAlert(self.tr( @@ -464,19 +459,36 @@ class GuiProjectTree(QTreeWidget): ), nwAlert.ERROR) return False - # Ask for label + # Set default label and determine if new item is to be added + # as child or sibling to the selected item if itemType == nwItemType.FILE: if isNote: newLabel = self.tr("New Note") + asChild = sIsParent elif hLevel == 2: newLabel = self.tr("New Chapter") + asChild = sIsParent and pItem.isDocumentLayout() and sLevel < 2 elif hLevel == 3: newLabel = self.tr("New Scene") + asChild = sIsParent and pItem.isDocumentLayout() and sLevel < 3 else: newLabel = self.tr("New Document") + asChild = sIsParent and pItem.isDocumentLayout() else: newLabel = self.tr("New Folder") + asChild = False + if not (asChild or pItem.isFolderType() or pItem.isRootType()): + # Move to the parent item so that the new item is added + # as a sibling instead + nHandle = sHandle + sHandle = pItem.itemParent + if sHandle is None: + # Bug: We have a condition that is unhandled + logger.error("Internal error") + return False + + # Ask for label newLabel, dlgOk = GuiEditLabel.getLabel(self, text=newLabel) if not dlgOk: logger.info("New item creation cancelled by user") From 73b8e3e0fb0e07b34b42b2e18ab6ab6a9ed9bf1e Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Sep 2022 17:22:46 +0200 Subject: [PATCH 3/3] Use the new item checker functions instead of equal operator --- novelwriter/core/index.py | 4 ++-- novelwriter/core/project.py | 2 +- novelwriter/core/tree.py | 6 +++--- novelwriter/dialogs/docmerge.py | 2 +- novelwriter/dialogs/docsplit.py | 4 ++-- novelwriter/gui/itemdetails.py | 5 ++--- novelwriter/gui/projtree.py | 20 ++++++++++---------- novelwriter/tools/build.py | 4 ++-- 8 files changed, 23 insertions(+), 24 deletions(-) diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index 0b3afdc5..7ea713b6 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -221,7 +221,7 @@ class NWIndex: if theItem is None: logger.info("Not indexing unknown item '%s'", tHandle) return False - if theItem.itemType != nwItemType.FILE: + if not theItem.isFileType(): logger.info("Not indexing non-file item '%s'", tHandle) return False @@ -792,7 +792,7 @@ class ItemIndex: for tItem in self.theProject.tree: if tItem is None: continue - if tItem.itemLayout == nwItemLayout.NOTE: + if tItem.isNoteLayout(): continue if skipExcl and not tItem.isExported: continue diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index 4b470d69..1dffd1aa 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -183,7 +183,7 @@ class NWProject(): tItem = self._projTree[tHandle] if tItem is None: return False - if tItem.itemType != nwItemType.FILE: + if not tItem.isFileType(): return False newDoc = NWDoc(self, tHandle) diff --git a/novelwriter/core/tree.py b/novelwriter/core/tree.py index 10a7a78f..6bb24959 100644 --- a/novelwriter/core/tree.py +++ b/novelwriter/core/tree.py @@ -29,7 +29,7 @@ import logging from lxml import etree -from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout +from novelwriter.enum import nwItemClass, nwItemLayout from novelwriter.error import logException from novelwriter.common import checkHandle from novelwriter.constants import nwFiles @@ -94,7 +94,7 @@ class NWTree(): nwItem.setHandle(tHandle) nwItem.setParent(pHandle) - if nwItem.itemType == nwItemType.ROOT: + if nwItem.isRootType(): logger.verbose("Item '%s' is a root item", str(tHandle)) self._treeRoots[tHandle] = nwItem if nwItem.itemClass == nwItemClass.ARCHIVE: @@ -357,7 +357,7 @@ class NWTree(): tItem = self.__getitem__(tHandle) if tItem is None: return False - if tItem.itemType != nwItemType.FILE: + if not tItem.isFileType(): logger.error("Item '%s' is not a file", tHandle) return False if not isinstance(itemLayout, nwItemLayout): diff --git a/novelwriter/dialogs/docmerge.py b/novelwriter/dialogs/docmerge.py index 30eb602f..22a6eb6b 100644 --- a/novelwriter/dialogs/docmerge.py +++ b/novelwriter/dialogs/docmerge.py @@ -183,7 +183,7 @@ class GuiDocMerge(QDialog): for sHandle in self.mainGui.projView.getTreeFromHandle(tHandle): newItem = QListWidgetItem() nwItem = self.theProject.tree[sHandle] - if nwItem.itemType is not nwItemType.FILE: + if not nwItem.isFileType(): continue newItem.setText(nwItem.itemName) newItem.setData(Qt.UserRole, sHandle) diff --git a/novelwriter/dialogs/docsplit.py b/novelwriter/dialogs/docsplit.py index b507479e..e4c9b13a 100644 --- a/novelwriter/dialogs/docsplit.py +++ b/novelwriter/dialogs/docsplit.py @@ -33,7 +33,7 @@ from PyQt5.QtWidgets import ( ) from novelwriter.core import NWDoc -from novelwriter.enum import nwAlert, nwItemType +from novelwriter.enum import nwAlert from novelwriter.gui.custom import QHelpLabel logger = logging.getLogger(__name__) @@ -235,7 +235,7 @@ class GuiDocSplit(QDialog): if nwItem is None: return False - if nwItem.itemType is not nwItemType.FILE: + if not nwItem.isFileType(): self.mainGui.makeAlert(self.tr( "Element selected in the project tree must be a file." ), nwAlert.ERROR) diff --git a/novelwriter/gui/itemdetails.py b/novelwriter/gui/itemdetails.py index 859e58f2..a467ed04 100644 --- a/novelwriter/gui/itemdetails.py +++ b/novelwriter/gui/itemdetails.py @@ -30,7 +30,6 @@ from PyQt5.QtCore import Qt, pyqtSlot from PyQt5.QtGui import QFont, QPixmap from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel -from novelwriter.enum import nwItemType from novelwriter.constants import trConst, nwLabels logger = logging.getLogger(__name__) @@ -247,7 +246,7 @@ class GuiItemDetails(QWidget): if len(theLabel) > 100: theLabel = theLabel[:96].rstrip()+" ..." - if nwItem.itemType == nwItemType.FILE: + if nwItem.isFileType(): if nwItem.isExported: self.labelIcon.setPixmap(self._expCheck) else: @@ -284,7 +283,7 @@ class GuiItemDetails(QWidget): # Counts # ====== - if nwItem.itemType == nwItemType.FILE: + if nwItem.isFileType(): self.cCountData.setText(f"{nwItem.charCount:n}") self.wCountData.setText(f"{nwItem.wordCount:n}") self.pCountData.setText(f"{nwItem.paraCount:n}") diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index 699938ac..b8e4dfbf 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -700,7 +700,7 @@ class GuiProjectTree(QTreeWidget): wCount = self._getItemWordCount(tHandle) autoFlush = not bulkAction - if nwItemS.itemType == nwItemType.ROOT: + if nwItemS.isRootType(): # Only an empty ROOT folder can be deleted logger.debug("User requested a root folder '%s' deleted", tHandle) tIndex = self.indexOfTopLevelItem(trItemS) @@ -716,7 +716,7 @@ class GuiProjectTree(QTreeWidget): ), nwAlert.ERROR) return False - elif nwItemS.itemType == nwItemType.FOLDER and trItemS.childCount() == 0: + elif nwItemS.isFolderType() and trItemS.childCount() == 0: # An empty FOLDER is just deleted without any further checks logger.debug("User requested an empty folder '%s' deleted", tHandle) trItemP = trItemS.parent() @@ -803,12 +803,12 @@ class GuiProjectTree(QTreeWidget): trItem.setIcon(self.C_STATUS, statusIcon) trItem.setToolTip(self.C_STATUS, itemStatus) - if nwItem.itemType == nwItemType.FILE: + if nwItem.isFileType(): trItem.setIcon( self.C_EXPORT, self.mainTheme.getIcon("check" if nwItem.isExported else "cross") ) - if self.mainConf.emphLabels and nwItem.itemLayout == nwItemLayout.DOCUMENT: + if self.mainConf.emphLabels and nwItem.isDocumentLayout(): trFont = trItem.font(self.C_NAME) trFont.setBold(hLevel == "H1" or hLevel == "H2") trFont.setUnderline(hLevel == "H1") @@ -978,7 +978,7 @@ class GuiProjectTree(QTreeWidget): if tItem is None: return - if tItem.itemType == nwItemType.FILE: + if tItem.isFileType(): self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, -1, "") else: trItem = self._getTreeItem(tHandle) @@ -1019,7 +1019,7 @@ class GuiProjectTree(QTreeWidget): # Document Actions # ================ - isFile = tItem.itemType == nwItemType.FILE + isFile = tItem.isFileType() if isFile: ctxMenu.addAction( self.tr("Open Document"), @@ -1059,7 +1059,7 @@ class GuiProjectTree(QTreeWidget): ) if isFile and tItem.documentAllowed(): - if tItem.itemLayout == nwItemLayout.NOTE: + if tItem.isNoteLayout(): ctxMenu.addAction( self.tr("Change to {0}").format( trConst(nwLabels.LAYOUT_NAME[nwItemLayout.DOCUMENT]) @@ -1079,7 +1079,7 @@ class GuiProjectTree(QTreeWidget): # Delete Item # =========== - if tItem.itemClass == nwItemClass.TRASH or tItem.itemType == nwItemType.ROOT: + if tItem.itemClass == nwItemClass.TRASH or tItem.isRootType(): ctxMenu.addAction( self.tr("Delete Permanently"), lambda: self.deleteItem(tHandle) ) @@ -1118,7 +1118,7 @@ class GuiProjectTree(QTreeWidget): if tItem is None: return - if tItem.itemType == nwItemType.FILE: + if tItem.isFileType(): self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "") return @@ -1306,7 +1306,7 @@ class GuiProjectTree(QTreeWidget): self._treeMap[tHandle] = newItem if pHandle is None: - if nwItem.itemType == nwItemType.ROOT: + if nwItem.isRootType(): newItem.setFlags(newItem.flags() ^ Qt.ItemIsDragEnabled) self.addTopLevelItem(newItem) else: diff --git a/novelwriter/tools/build.py b/novelwriter/tools/build.py index efeff928..7a0eba6c 100644 --- a/novelwriter/tools/build.py +++ b/novelwriter/tools/build.py @@ -784,11 +784,11 @@ class GuiBuildNovel(QDialog): if not (theItem.isExported or ignoreFlag): return False - isNone = theItem.itemType != nwItemType.FILE + isNone = not theItem.isFileType() isNone |= theItem.itemLayout == nwItemLayout.NO_LAYOUT isNone |= theItem.isInactive() isNone |= theItem.itemParent is None - isNote = theItem.itemLayout == nwItemLayout.NOTE + isNote = theItem.isNoteLayout() isNovel = not isNone and not isNote if isNone: