diff --git a/nw/gui/docdetails.py b/nw/gui/docdetails.py
index 1552640a..b1f91345 100644
--- a/nw/gui/docdetails.py
+++ b/nw/gui/docdetails.py
@@ -70,20 +70,9 @@ class GuiDocDetails(QFrame):
if nwItem is None:
colTwo = [""]*4
else:
- itemStatus = nwItem.itemStatus
- if nwItem.itemClass == nwItemClass.NOVEL:
- if itemStatus < 0 or itemStatus >= len(self.theParent.statusLabels):
- statusLabel = self.theParent.statusLabels[0]
- else:
- statusLabel = self.theParent.statusLabels[itemStatus]
- else:
- if itemStatus < 0 or itemStatus >= len(self.theParent.importLabels):
- statusLabel = self.theParent.importLabels[0]
- else:
- statusLabel = self.theParent.importLabels[itemStatus]
colTwo = [
nwItem.itemName,
- statusLabel,
+ nwItem.itemStatus,
nwLabels.CLASS_NAME[nwItem.itemClass],
nwLabels.LAYOUT_NAME[nwItem.itemLayout],
]
diff --git a/nw/gui/doctabs.py b/nw/gui/doctabs.py
deleted file mode 100644
index b8e5c46c..00000000
--- a/nw/gui/doctabs.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# -*- coding: utf-8 -*-
-"""novelWriter GUI Document Tabs
-
- novelWriter – GUI Document Tabs
-=================================
- Class holding the document tab view
-
- File History:
- Created: 2018-09-29 [0.0.1]
-
-"""
-
-import logging
-import nw
-
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QTabWidget, QWidget, QVBoxLayout
-
-from nw.gui.doceditor import GuiDocEditor
-from nw.gui.projecteditor import GuiProjectEditor
-from nw.gui.aboutview import GuiAboutView
-
-logger = logging.getLogger(__name__)
-
-class GuiDocTabs(QTabWidget):
-
- def __init__(self):
- QTabWidget.__init__(self)
-
- logger.debug("Initialising DocTabs ...")
- self.mainConf = nw.CONFIG
- self.tabList = []
-
- self.resize(900,500)
- self.setTabsClosable(True)
-
- self.tabBar().tabCloseRequested.connect(self._doCloseTab)
-
- self.createTab(None,nw.DOCTYPE_ABOUT)
-
- logger.debug("DocTabs initialisation complete")
-
- return
-
- def createTab(self, theName=None, tabType=None):
- if tabType == nw.DOCTYPE_ABOUT:
- self._createTabAbout()
- return True
- elif tabType == nw.DOCTYPE_PROJECT:
- self._createTabProject(theName)
- return True
- elif tabType == nw.DOCTYPE_DOC:
- self._createTabDoc(theName)
- return True
- return False
-
- #
- # Internal Functions
- #
-
- def _createTabDoc(self, docName=None):
- if docName is None:
- tabName = "New Document"
- else:
- tabName = docName
- thisTab = GuiDocEditor()
- self.addTab(thisTab,tabName)
- self.setCurrentWidget(thisTab)
- return True
-
- def _createTabProject(self, projectName=None):
- """Display or create the project settings page.
- """
- for i in range(self.count()):
- thisTab = self.widget(i)
- if isinstance(thisTab, GuiProjectEditor):
- self.setCurrentWidget(thisTab)
- return True
- if projectName is None:
- tabName = "New Project"
- else:
- tabName = projectName
- thisTab = GuiProjectEditor()
- self.addTab(thisTab,tabName)
- self.setCurrentWidget(thisTab)
- return True
-
- def _createTabAbout(self):
- """Display or create the About tab.
- """
- for i in range(self.count()):
- thisTab = self.widget(i)
- if isinstance(thisTab, GuiAboutView):
- self.setCurrentWidget(thisTab)
- return True
- thisTab = GuiAboutView()
- self.addTab(thisTab,"About")
- self.setCurrentWidget(thisTab)
- return True
-
- #
- # Signals
- #
-
- def _doCloseTab(self, tabIdx):
- logger.verbose("User requested tab %d to be closed." % tabIdx)
- self.removeTab(tabIdx)
- logger.verbose("Tab %d closed." % tabIdx)
- return
-
-# END Class GuiDocTabs
diff --git a/nw/gui/doctree.py b/nw/gui/doctree.py
index 8844866c..5ff18107 100644
--- a/nw/gui/doctree.py
+++ b/nw/gui/doctree.py
@@ -229,7 +229,7 @@ class GuiDocTree(QTreeWidget):
trItemP.takeChild(tIndex)
self.clearSelection()
trItemP.setSelected(True)
- self.theProject.setProjectChanged(True)
+ self.theProject.deleteItem(tHandle)
else:
self.makeAlert(["Cannot delete folder.","It is not empty."], nwAlert.ERROR)
return
@@ -259,19 +259,15 @@ class GuiDocTree(QTreeWidget):
tStatus = nwLabels.CLASS_FLAG[nwItem.itemClass]
if nwItem.itemType == nwItemType.FILE:
tStatus += "."+nwLabels.LAYOUT_FLAG[nwItem.itemLayout]
- nStatus = nwItem.itemStatus
+ iStatus = nwItem.itemStatus
if tClass == nwItemClass.NOVEL:
- if nStatus < 0 or nStatus >= len(self.theParent.statusIcons):
- flagIcon = self.theParent.statusIcons[0]
- else:
- flagIcon = self.theParent.statusIcons[nStatus]
+ iStatus = self.theProject.statusItems.checkEntry(iStatus) # Make sure it's a valid index
+ flagIcon = self.theParent.statusIcons[iStatus]
else:
- if nStatus < 0 or nStatus >= len(self.theParent.importIcons):
- flagIcon = self.theParent.importIcons[0]
- else:
- flagIcon = self.theParent.importIcons[nStatus]
+ iStatus = self.theProject.importItems.checkEntry(iStatus) # Make sure it's a valid index
+ flagIcon = self.theParent.importIcons[iStatus]
- trItem.setText(self.C_NAME,tName)
+ trItem.setText(self.C_NAME, tName)
trItem.setText(self.C_FLAGS,tStatus)
trItem.setIcon(self.C_FLAGS,flagIcon)
diff --git a/nw/gui/itemeditor.py b/nw/gui/itemeditor.py
index 3cc48140..06edf0fd 100644
--- a/nw/gui/itemeditor.py
+++ b/nw/gui/itemeditor.py
@@ -55,14 +55,14 @@ class GuiItemEditor(QDialog):
self.editLayout = QComboBox()
if self.theItem.itemClass == nwItemClass.NOVEL:
- for n in range(len(self.theParent.statusLabels)):
+ for sLabel, _, _ in self.theProject.statusItems:
self.editStatus.addItem(
- self.theParent.statusIcons[n], self.theParent.statusLabels[n], n
+ self.theParent.statusIcons[sLabel], sLabel, sLabel
)
else:
- for n in range(len(self.theParent.statusLabels)):
+ for sLabel, _, _ in self.theProject.importItems:
self.editStatus.addItem(
- self.theParent.importIcons[n], self.theParent.importLabels[n], n
+ self.theParent.importIcons[sLabel], sLabel, sLabel
)
self.validLayouts = []
diff --git a/nw/gui/projecteditor.py b/nw/gui/projecteditor.py
index c7638b87..0fdb1fb8 100644
--- a/nw/gui/projecteditor.py
+++ b/nw/gui/projecteditor.py
@@ -13,21 +13,29 @@
import logging
import nw
-from os import path
+from os import path
-from PyQt5.QtWidgets import QDialog, QHBoxLayout, QVBoxLayout, QGroupBox, QFormLayout, QLineEdit, QPlainTextEdit, QPushButton
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush
from PyQt5.QtSvg import QSvgWidget
+from PyQt5.QtWidgets import (
+ QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit, QLabel,
+ QWidget, QTabWidget, QDialogButtonBox, QListWidget, QListWidgetItem, QPushButton,
+ QColorDialog, QAbstractItemView
+)
+from nw.enum import nwAlert
logger = logging.getLogger(__name__)
class GuiProjectEditor(QDialog):
- def __init__(self, guiParent, theProject):
- QDialog.__init__(self, guiParent)
+ def __init__(self, theParent, theProject):
+ QDialog.__init__(self, theParent)
logger.debug("Initialising ProjectEditor ...")
self.mainConf = nw.CONFIG
+ self.theParent = theParent
self.theProject = theProject
self.outerBox = QHBoxLayout()
self.innerBox = QVBoxLayout()
@@ -37,13 +45,72 @@ class GuiProjectEditor(QDialog):
self.gradPath = path.abspath(path.join(self.mainConf.appPath,"graphics","block.svg"))
self.svgGradient = QSvgWidget(self.gradPath)
self.svgGradient.setFixedWidth(80)
+
+ self.theProject.countStatus()
+ self.tabMain = GuiProjectEditMain(self.theParent, self.theProject)
+ self.tabStatus = GuiProjectEditStatus(self.theParent, self.theProject.statusItems)
+ self.tabImport = GuiProjectEditStatus(self.theParent, self.theProject.importItems)
+
+ self.tabWidget = QTabWidget()
+ self.tabWidget.addTab(self.tabMain, "Settings")
+ self.tabWidget.addTab(self.tabStatus,"Status")
+ self.tabWidget.addTab(self.tabImport,"Importance")
+
self.setLayout(self.outerBox)
self.outerBox.addWidget(self.svgGradient)
self.outerBox.addLayout(self.innerBox)
- self.mainGroup = QGroupBox("Project Settings")
- self.mainForm = QFormLayout()
+ self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox.accepted.connect(self._doSave)
+ self.buttonBox.rejected.connect(self._doClose)
+ self.innerBox.addWidget(self.tabWidget)
+ self.innerBox.addWidget(self.buttonBox)
+
+ self.show()
+
+ logger.debug("ProjectEditor initialisation complete")
+
+ return
+
+ def _doSave(self):
+ logger.verbose("ProjectEditor save button clicked")
+
+ projName = self.tabMain.editName.text()
+ bookTitle = self.tabMain.editTitle.text()
+ bookAuthors = self.tabMain.editAuthors.toPlainText()
+ self.theProject.setProjectName(projName)
+ self.theProject.setBookTitle(bookTitle)
+ self.theProject.setBookAuthors(bookAuthors)
+
+ if self.tabStatus.colChanged:
+ statusCol = self.tabStatus.getNewList()
+ self.theProject.setStatusColours(statusCol)
+ if self.tabImport.colChanged:
+ importCol = self.tabImport.getNewList()
+ self.theProject.setImportColours(importCol)
+ if self.tabStatus.colChanged or self.tabImport.colChanged:
+ self.theParent.rebuildTree()
+
+ self.close()
+
+ return
+
+ def _doClose(self):
+ logger.verbose("ProjectEditor close button clicked")
+ self.close()
+ return
+
+# END Class GuiProjectEditor
+
+class GuiProjectEditMain(QWidget):
+
+ def __init__(self, theParent, theProject):
+ QWidget.__init__(self, theParent)
+
+ self.theParent = theParent
+ self.theProject = theProject
+ self.mainForm = QFormLayout()
self.editName = QLineEdit()
self.editTitle = QLineEdit()
self.editAuthors = QPlainTextEdit()
@@ -59,40 +126,169 @@ class GuiProjectEditor(QDialog):
bookAuthors += bookAuthor+"\n"
self.editAuthors.setPlainText(bookAuthors)
- self.buttonBox = QHBoxLayout()
- self.closeButton = QPushButton("Close")
- self.closeButton.clicked.connect(self._doClose)
+ self.setLayout(self.mainForm)
+
+ return
+
+# END Class GuiProjectEditMain
+
+class GuiProjectEditStatus(QWidget):
+
+ def __init__(self, theParent, theStatus):
+ QWidget.__init__(self, theParent)
+
+ self.theParent = theParent
+ self.theStatus = theStatus
+ self.colData = []
+ self.colCounts = []
+ self.colChanged = False
+ self.selColour = None
+
+ self.mainBox = QHBoxLayout()
+ self.mainForm = QVBoxLayout()
+
+ self.listBox = QListWidget()
+ self.listBox.setDragDropMode(QAbstractItemView.InternalMove)
+ self.listBox.itemSelectionChanged.connect(self._selectedItem)
+ self.listBox.model().rowsMoved.connect(self._rowsMoved)
+
+ for iName, iCol, nUse in self.theStatus:
+ self._addItem(iName, iCol, iName, nUse)
+
+ self.editName = QLineEdit()
+ self.newButton = QPushButton("New")
+ self.delButton = QPushButton("Delete")
self.saveButton = QPushButton("Save")
- self.saveButton.clicked.connect(self._doSave)
- self.buttonBox.addStretch(1)
- self.buttonBox.addWidget(self.closeButton)
- self.buttonBox.addWidget(self.saveButton)
+ self.colPixmap = QPixmap(16,16)
+ self.colPixmap.fill(QColor(120,120,120))
+ self.colButton = QPushButton(QIcon(self.colPixmap),"Colour")
+ self.colButton.setIconSize(self.colPixmap.rect().size())
- self.mainGroup.setLayout(self.mainForm)
- self.innerBox.addWidget(self.mainGroup)
- self.innerBox.addLayout(self.buttonBox)
+ self.newButton.clicked.connect(self._newItem)
+ self.delButton.clicked.connect(self._delItem)
+ self.saveButton.clicked.connect(self._saveItem)
+ self.colButton.clicked.connect(self._selectColour)
- self.show()
+ self.mainForm.addWidget(self.newButton)
+ self.mainForm.addWidget(self.delButton)
+ self.mainForm.addStretch(1)
+ self.mainForm.addWidget(QLabel("Name"))
+ self.mainForm.addWidget(self.editName)
+ self.mainForm.addWidget(self.colButton)
+ self.mainForm.addStretch(1)
+ self.mainForm.addWidget(self.saveButton)
- logger.debug("ProjectEditor initialisation complete")
+ self.mainBox.addWidget(self.listBox)
+ self.mainBox.addLayout(self.mainForm)
+
+ self.setLayout(self.mainBox)
return
- def _doSave(self):
- logger.verbose("ProjectEditor save button clicked")
- projName = self.editName.text()
- bookTitle = self.editTitle.text()
- bookAuthors = self.editAuthors.toPlainText()
- self.theProject.setProjectName(projName)
- self.theProject.setBookTitle(bookTitle)
- self.theProject.setBookAuthors(bookAuthors)
- self.theProject.setProjectChanged(True)
- self.close()
+ def getNewList(self):
+ if self.colChanged:
+ newList = []
+ for n in range(self.listBox.count()):
+ nItem = self.listBox.item(n)
+ nIdx = nItem.data(Qt.UserRole)
+ newList.append(self.colData[nIdx])
+ return newList
+ return None
+
+ ##
+ # User Actions
+ ##
+
+ def _selectColour(self):
+ logger.verbose("Item colour button clicked")
+ if self.selColour is not None:
+ newCol = QColorDialog.getColor(self.selColour, self, "Select Colour", QColorDialog.DontUseNativeDialog)
+ if newCol:
+ self.selColour = newCol
+ colPixmap = QPixmap(16,16)
+ colPixmap.fill(newCol)
+ self.colButton.setIcon(QIcon(colPixmap))
+ self.colButton.setIconSize(colPixmap.rect().size())
return
- def _doClose(self):
- logger.verbose("ProjectEditor close button clicked")
- self.close()
+ def _newItem(self):
+ logger.verbose("New item button clicked")
+ newItem = self._addItem("New Item", (0, 0, 0), None, 0)
+ newItem.setBackground(QBrush(QColor(0,255,0,80)))
+ self.colChanged = True
return
-# END Class GuiProjectEditor
+ def _delItem(self):
+ logger.verbose("Delete item button clicked")
+ selItem = self._getSelectedItem()
+ if selItem is not None:
+ iRow = self.listBox.row(selItem)
+ selIdx = selItem.data(Qt.UserRole)
+ if self.colCounts[selIdx] == 0:
+ self.listBox.takeItem(iRow)
+ self.colChanged = True
+ else:
+ self.theParent.makeAlert("Cannot delete status item that is in use.",nwAlert.ERROR)
+ return
+
+ def _saveItem(self):
+ logger.verbose("Save item button clicked")
+ selItem = self._getSelectedItem()
+ iRow = self.listBox.row(selItem)
+ if selItem is not None:
+ selIdx = selItem.data(Qt.UserRole)
+ self.colData[selIdx] = (
+ self.editName.text().strip(),
+ self.selColour.red(),
+ self.selColour.green(),
+ self.selColour.blue(),
+ self.colData[selIdx][4]
+ )
+ selItem.setText("%s [%d]" % (self.colData[selIdx][0], self.colCounts[selIdx]))
+ selItem.setIcon(self.colButton.icon())
+ self.colChanged = True
+ return
+
+ def _addItem(self, iName, iCol, oName, nUse):
+ newIcon = QPixmap(16,16)
+ newIcon.fill(QColor(*iCol))
+ newItem = QListWidgetItem()
+ newItem.setText("%s [%d]" % (iName, nUse))
+ newItem.setIcon(QIcon(newIcon))
+ newItem.setData(Qt.UserRole, len(self.colData))
+ self.listBox.addItem(newItem)
+ self.colData.append((iName,*iCol,oName))
+ self.colCounts.append(nUse)
+ return newItem
+
+ def _selectedItem(self):
+ logger.verbose("Item selected")
+ selItem = self._getSelectedItem()
+ if selItem is not None:
+ selIdx = selItem.data(Qt.UserRole)
+ selVal = self.colData[selIdx]
+ self.selColour = QColor(selVal[1],selVal[2],selVal[3])
+ newIcon = QPixmap(16,16)
+ newIcon.fill(self.selColour)
+ self.editName.setText(selVal[0])
+ self.colButton.setIcon(QIcon(newIcon))
+ return
+
+ ##
+ # Internal Functions
+ ##
+
+ def _getSelectedItem(self):
+ selItem = self.listBox.selectedItems()
+ if len(selItem) == 0:
+ return None
+ if isinstance(selItem[0], QListWidgetItem):
+ return selItem[0]
+ return None
+
+ def _rowsMoved(self):
+ logger.verbose("A drag move event occurred")
+ self.colChanged = True
+ return
+
+# END Class GuiProjectEditStatus
diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py
index 6bcea030..20a0aa50 100644
--- a/nw/gui/winmain.py
+++ b/nw/gui/winmain.py
@@ -61,10 +61,8 @@ class GuiMain(QMainWindow):
self.statusBar = GuiMainStatus(self)
# Minor Gui Elements
- self.statusIcons = []
- self.statusLabels = []
- self.importIcons = []
- self.importLabels = []
+ self.statusIcons = []
+ self.importIcons = []
# Assemble Main Window
self.treePane = QFrame()
@@ -186,12 +184,9 @@ class GuiMain(QMainWindow):
projFile = self.openProjectDialog()
if projFile is None:
return False
- self.treeView.clearTree()
self.theProject.openProject(projFile)
- self.treeView.buildTree()
self._setWindowTitle(self.theProject.projName)
- self._makeStatusIcons()
- self._makeImportIcons()
+ self.rebuildTree()
self.docEditor.setPwl(path.join(self.theProject.projMeta,"wordlist.txt"))
self.docEditor.setSpellCheck(self.theProject.spellCheck)
self.mainMenu.updateMenu()
@@ -302,6 +297,13 @@ class GuiMain(QMainWindow):
return
+ def rebuildTree(self):
+ self._makeStatusIcons()
+ self._makeImportIcons()
+ self.treeView.clearTree()
+ self.treeView.buildTree()
+ return
+
##
# Main Dialogs
##
@@ -397,23 +399,19 @@ class GuiMain(QMainWindow):
return True
def _makeStatusIcons(self):
- self.statusIcons = []
- self.statusLabels = []
- for sLabel, sR, sG, sB in self.theProject.statusCols:
+ self.statusIcons = {}
+ for sLabel, sCol, _ in self.theProject.statusItems:
theIcon = QPixmap(32,32)
- theIcon.fill(QColor(sR,sG,sB))
- self.statusIcons.append(QIcon(theIcon))
- self.statusLabels.append(sLabel)
+ theIcon.fill(QColor(*sCol))
+ self.statusIcons[sLabel] = QIcon(theIcon)
return
def _makeImportIcons(self):
- self.importIcons = []
- self.importLabels = []
- for sLabel, sR, sG, sB in self.theProject.importCols:
+ self.importIcons = {}
+ for sLabel, sCol, _ in self.theProject.importItems:
theIcon = QPixmap(32,32)
- theIcon.fill(QColor(sR,sG,sB))
- self.importIcons.append(QIcon(theIcon))
- self.importLabels.append(sLabel)
+ theIcon.fill(QColor(*sCol))
+ self.importIcons[sLabel] = QIcon(theIcon)
return
##
diff --git a/nw/project/document.py b/nw/project/document.py
index 1d0a8af3..bb8ad4bd 100644
--- a/nw/project/document.py
+++ b/nw/project/document.py
@@ -15,8 +15,7 @@ import nw
from os import path, mkdir, rename, unlink
-from nw.tools.analyse import TextAnalysis
-from nw.enum import nwAlert
+from nw.enum import nwAlert
logger = logging.getLogger(__name__)
diff --git a/nw/project/item.py b/nw/project/item.py
index 7ea631ac..367167c8 100644
--- a/nw/project/item.py
+++ b/nw/project/item.py
@@ -26,7 +26,9 @@ class NWItem():
MAX_DEPTH = 8
- def __init__(self):
+ def __init__(self, theProject):
+
+ self.theProject = theProject
self.itemName = ""
self.itemHandle = None
@@ -35,7 +37,7 @@ class NWItem():
self.itemType = nwItemType.NO_TYPE
self.itemClass = nwItemClass.NO_CLASS
self.itemLayout = nwItemLayout.NO_LAYOUT
- self.itemStatus = 0
+ self.itemStatus = None
self.isExpanded = False
# Document Meta Data
@@ -158,8 +160,10 @@ class NWItem():
return
def setStatus(self, theStatus):
- theStatus = checkInt(theStatus,0)
- self.itemStatus = theStatus
+ if self.itemClass == nwItemClass.NOVEL:
+ self.itemStatus = self.theProject.statusItems.checkEntry(theStatus)
+ else:
+ self.itemStatus = self.theProject.importItems.checkEntry(theStatus)
return
def setExpanded(self, expState):
diff --git a/nw/project/project.py b/nw/project/project.py
index a2462d4d..c8d04db6 100644
--- a/nw/project/project.py
+++ b/nw/project/project.py
@@ -13,15 +13,16 @@
import logging
import nw
-from os import path, mkdir, listdir
-from lxml import etree
-from hashlib import sha256
-from datetime import datetime
-from time import time
+from os import path, mkdir, listdir
+from lxml import etree
+from hashlib import sha256
+from datetime import datetime
+from time import time
-from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
-from nw.common import checkString, checkBool
-from nw.project.item import NWItem
+from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
+from nw.common import checkString, checkBool
+from nw.project.item import NWItem
+from nw.project.status import NWStatus
logger = logging.getLogger(__name__)
@@ -54,8 +55,8 @@ class NWProject():
# Project Settings
self.spellCheck = False
- self.statusCols = None
- self.importCols = None
+ self.statusItems = None
+ self.importItems = None
self.lastEdited = None
self.lastViewed = None
@@ -75,23 +76,25 @@ class NWProject():
if not self.checkRootUnique(rootClass):
self.makeAlert("Duplicate root item detected!", nwAlert.ERROR)
return None
- newItem = NWItem()
+ newItem = NWItem(self)
newItem.setName(rootName)
newItem.setType(nwItemType.ROOT)
newItem.setClass(rootClass)
+ newItem.setStatus(0)
self._appendItem(None,None,newItem)
return newItem.itemHandle
def newFolder(self, folderName, folderClass, pHandle):
- newItem = NWItem()
+ newItem = NWItem(self)
newItem.setName(folderName)
newItem.setType(nwItemType.FOLDER)
newItem.setClass(folderClass)
+ newItem.setStatus(0)
self._appendItem(None,pHandle,newItem)
return newItem.itemHandle
def newFile(self, fileName, fileClass, pHandle):
- newItem = NWItem()
+ newItem = NWItem(self)
newItem.setName(fileName)
newItem.setType(nwItemType.FILE)
if fileClass == nwItemClass.NOVEL:
@@ -99,11 +102,12 @@ class NWProject():
else:
newItem.setLayout(nwItemLayout.NOTE)
newItem.setClass(fileClass)
+ newItem.setStatus(0)
self._appendItem(None,pHandle,newItem)
return newItem.itemHandle
def addTrash(self):
- newItem = NWItem()
+ newItem = NWItem(self)
newItem.setName("Trash")
newItem.setType(nwItemType.TRASH)
newItem.setClass(nwItemClass.TRASH)
@@ -144,18 +148,16 @@ class NWProject():
self.bookTitle = ""
self.bookAuthors = []
self.spellCheck = False
- self.statusCols = [
- ("New", 100,100,100),
- ("Note", 200, 50, 0),
- ("Draft", 200,150, 0),
- ("Finished", 50,200, 0),
- ]
- self.importCols = [
- ("None", 100,100,100),
- ("Minor", 200, 50, 0),
- ("Major", 200,150, 0),
- ("Main", 50,200, 0),
- ]
+ self.statusItems = NWStatus()
+ self.statusItems.addEntry("New", (100,100,100))
+ self.statusItems.addEntry("Note", (200, 50, 0))
+ self.statusItems.addEntry("Draft", (200,150, 0))
+ self.statusItems.addEntry("Finished",( 50,200, 0))
+ self.importItems = NWStatus()
+ self.importItems.addEntry("New", (100,100,100))
+ self.importItems.addEntry("Minor", (200, 50, 0))
+ self.importItems.addEntry("Major", (200,150, 0))
+ self.importItems.addEntry("Main", ( 50,200, 0))
return
@@ -215,6 +217,10 @@ class NWProject():
self.lastEdited = checkString(xItem.text,None,True)
if xItem.tag == "lastViewed":
self.lastViewed = checkString(xItem.text,None,True)
+ if xItem.tag == "status":
+ self.statusItems.unpackEntries(xItem)
+ if xItem.tag == "importance":
+ self.importItems.unpackEntries(xItem)
elif xChild.tag == "content":
logger.debug("Found project content")
for xItem in xChild:
@@ -228,7 +234,7 @@ class NWProject():
pHandle = itemAttrib["parent"]
else:
pHandle = None
- nwItem = NWItem()
+ nwItem = NWItem(self)
for xValue in xItem:
nwItem.setFromTag(xValue.tag,xValue.text)
self._appendItem(tHandle,pHandle,nwItem)
@@ -276,6 +282,11 @@ class NWProject():
self._saveProjectValue(xSettings,"lastEdited",self.lastEdited)
self._saveProjectValue(xSettings,"lastViewed",self.lastViewed)
+ xStatus = etree.SubElement(xSettings,"status")
+ self.statusItems.packEntries(xStatus)
+ xStatus = etree.SubElement(xSettings,"importance")
+ self.importItems.packEntries(xStatus)
+
# Save Tree Content
logger.debug("Writing project content")
xContent = etree.SubElement(nwXML,"content",attrib={"count":str(len(self.treeOrder))})
@@ -353,6 +364,26 @@ class NWProject():
self.setProjectChanged(True)
return True
+ def setStatusColours(self, newCols):
+ replaceMap = self.statusItems.setNewEntries(newCols)
+ if self.projTree is not None:
+ for nwItem in self.projTree.values():
+ if nwItem.itemClass == nwItemClass.NOVEL:
+ if nwItem.itemStatus in replaceMap.keys():
+ nwItem.setStatus(replaceMap[nwItem.itemStatus])
+ self.setProjectChanged(True)
+ return
+
+ def setImportColours(self, newCols,):
+ replaceMap = self.importItems.setNewEntries(newCols)
+ if self.projTree is not None:
+ for nwItem in self.projTree.values():
+ if nwItem.itemClass != nwItemClass.NOVEL:
+ if nwItem.itemStatus in replaceMap.keys():
+ nwItem.setStatus(replaceMap[nwItem.itemStatus])
+ self.setProjectChanged(True)
+ return
+
def setProjectChanged(self, bValue):
self.projChanged = bValue
self.theParent.setProjectStatus(self.projChanged)
@@ -409,6 +440,13 @@ class NWProject():
# Class Methods
##
+ def deleteItem(self, tHandle):
+ """This only removes the item from the order list, but not from the project tree.
+ """
+ self.treeOrder.remove(tHandle)
+ self.setProjectChanged(True)
+ return True
+
def findRootItem(self, theClass):
for aRoot in self.treeRoots:
if theClass == self.projTree[aRoot].itemClass:
@@ -426,6 +464,16 @@ class NWProject():
return False
return True
+ def countStatus(self):
+ self.statusItems.resetCounts()
+ self.importItems.resetCounts()
+ for nwItem in self.projTree.values():
+ if nwItem.itemClass == nwItemClass.NOVEL:
+ self.statusItems.countEntry(nwItem.itemStatus)
+ else:
+ self.importItems.countEntry(nwItem.itemStatus)
+ return
+
##
# Internal Functions
##
@@ -492,7 +540,7 @@ class NWProject():
nOrph = 0
for oHandle in orphanFiles:
nOrph += 1
- orItem = NWItem()
+ orItem = NWItem(self)
orItem.setName("Orphaned File %d" % nOrph)
orItem.setType(nwItemType.FILE)
orItem.setClass(nwItemClass.NO_CLASS)
diff --git a/nw/project/status.py b/nw/project/status.py
new file mode 100644
index 00000000..1d19bf34
--- /dev/null
+++ b/nw/project/status.py
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+"""novelWriter Item Status
+
+ novelWriter – Item Status
+===========================
+ Class holding the project's item statuses
+
+ File History:
+ Created: 2019-05-19 [0.1.3]
+
+"""
+
+import logging
+import nw
+
+from lxml import etree
+
+from nw.enum import nwItemClass
+from nw.common import checkInt
+
+logger = logging.getLogger(__name__)
+
+class NWStatus():
+
+ def __init__(self):
+ self.theLabels = []
+ self.theColours = []
+ self.theCounts = []
+ self.theMap = {}
+ self.theLength = 0
+ self.theIndex = 0
+ return
+
+ def addEntry(self, theLabel, theColours):
+ theLabel = theLabel.strip()
+ if self.lookupEntry(theLabel) is None:
+ self.theLabels.append(theLabel)
+ self.theColours.append(theColours)
+ self.theCounts.append(0)
+ self.theMap[theLabel] = self.theLength
+ self.theLength += 1
+ return True
+
+ def lookupEntry(self, theLabel):
+ theLabel = theLabel.strip()
+ if theLabel in self.theMap.keys():
+ return self.theMap[theLabel]
+ return None
+
+ def checkEntry(self, theStatus):
+ if isinstance(theStatus, str):
+ theStatus = theStatus.strip()
+ if self.lookupEntry(theStatus) is not None:
+ return theStatus
+ theStatus = checkInt(theStatus, 0, False)
+ if theStatus >= 0 and theStatus < self.theLength:
+ return self.theLabels[theStatus]
+
+ def setNewEntries(self, newList):
+
+ replaceMap = {}
+
+ if newList is not None:
+
+ self.theLabels = []
+ self.theColours = []
+ self.theCounts = []
+ self.theMap = {}
+ self.theLength = 0
+ self.theIndex = 0
+
+ for nName, nR, nG, nB, oName in newList:
+ self.addEntry(nName, (nR, nG, nB))
+ if nName != oName and oName is not None:
+ replaceMap[oName] = nName
+
+ return replaceMap
+
+ def resetCounts(self):
+ self.theCounts = [0]*self.theLength
+ return
+
+ def countEntry(self, theLabel):
+ theIndex = self.lookupEntry(theLabel)
+ if theIndex is not None:
+ self.theCounts[theIndex] += 1
+ return
+
+ def packEntries(self, xParent):
+ for n in range(self.theLength):
+ xSub = etree.SubElement(xParent,"entry",attrib={
+ "red" : str(self.theColours[n][0]),
+ "green" : str(self.theColours[n][1]),
+ "blue" : str(self.theColours[n][2]),
+ })
+ xSub.text = self.theLabels[n]
+ return True
+
+ def unpackEntries(self, xParent):
+
+ theLabels = []
+ theColours = []
+
+ for xChild in xParent:
+ theLabels.append(xChild.text)
+ if "red" in xChild.attrib:
+ cR = checkInt(xChild.attrib["red"],0,False)
+ else:
+ cR = 0
+ if "green" in xChild.attrib:
+ cG = checkInt(xChild.attrib["green"],0,False)
+ else:
+ cG = 0
+ if "blue" in xChild.attrib:
+ cB = checkInt(xChild.attrib["blue"],0,False)
+ else:
+ cB = 0
+ theColours.append((cR,cG,cB))
+
+ if len(theLabels) > 0:
+ self.theLabels = []
+ self.theColours = []
+ self.theCounts = []
+ self.theMap = {}
+ self.theLength = 0
+ self.theIndex = 0
+
+ for n in range(len(theLabels)):
+ self.addEntry(theLabels[n], theColours[n])
+
+ return True
+
+ ##
+ # Iterator Bits
+ ##
+
+ def __getitem__(self, n):
+ if n >= 0 and n < self.theLength:
+ return self.theLabels[n], self.theColours[n], self.theCounts[n]
+ return None, None, None
+
+ def __iter__(self):
+ self.theIndex = 0
+ return self
+
+ def __next__(self):
+ if self.theIndex < self.theLength:
+ theLabel, theColour, theCount = self.__getitem__(self.theIndex)
+ self.theIndex += 1
+ return theLabel, theColour, theCount
+ else:
+ raise StopIteration
+
+# END Class NWStatus
diff --git a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd b/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd
index c74aae3f..72d112c6 100644
--- a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd
+++ b/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd
@@ -15,6 +15,6 @@ So, this is some __text__ that we’ve been adding to this document. It is utter
This paragraph is also meaningless. At least a bit. It’s also very short.
-This one is a bit longer. “It also has some dialogue in it” she said, before she moved on to check if the spelllchecker worked. It did. “Cool,” she concluded.
+This one is a bit longer. “It also has some dialogue in it” she said, before she moved on to check if the spellchecker worked. It did. “Cool,” she concluded.
diff --git a/sample/sampleNovel/nwProject.nwx b/sample/sampleNovel/nwProject.nwx
index 0d421f0e..769c4a83 100644
--- a/sample/sampleNovel/nwProject.nwx
+++ b/sample/sampleNovel/nwProject.nwx
@@ -1,5 +1,5 @@
-
+
Sample Project
Sample Project
@@ -8,22 +8,37 @@
True
- 4cd0bd12b087d
+ 636b6aa9b697b
636b6aa9b697b
+
+ New
+ Notes
+ Started
+ 1st Draft
+ 2nd Draft
+ 3rd Draft
+ Finished
+
+
+ None
+ Minor
+ Major
+ Main
+
-
Novel
ROOT
NOVEL
- 0
+ New
True
-
Title Page
FILE
NOVEL
- 2
+ Started
False
TITLE
23
@@ -35,14 +50,14 @@
Some Chapter
FOLDER
NOVEL
- 1
+ Notes
True
-
New Scene
FILE
NOVEL
- 2
+ Started
False
SCENE
2571
@@ -54,19 +69,19 @@
File With Stuff
FILE
NOVEL
- 1
+ Notes
False
SCENE
- 578
+ 577
104
5
- 658
+ 603
-
New File
FILE
NOVEL
- 0
+ Notes
False
SCENE
69
@@ -78,21 +93,21 @@
Characters
ROOT
CHARACTER
- 0
+ None
True
-
Main Characters
FOLDER
CHARACTER
- 0
+ None
True
-
John Smith
FILE
CHARACTER
- 0
+ Minor
False
NOTE
42
@@ -104,7 +119,7 @@
Jane Smith
FILE
CHARACTER
- 0
+ Major
False
NOTE
51
@@ -116,14 +131,14 @@
Locations
ROOT
WORLD
- 0
+ None
True
-
Earth
FILE
WORLD
- 0
+ None
False
NOTE
0
@@ -135,14 +150,14 @@
Trash
TRASH
TRASH
- 0
+ None
True
-
Orphaned File 2
FILE
NO_CLASS
- 0
+ None
False
NO_LAYOUT
14
diff --git a/tests/reference/gui/1_nwProject.nwx b/tests/reference/gui/1_nwProject.nwx
index cf0b0f54..971954a6 100644
--- a/tests/reference/gui/1_nwProject.nwx
+++ b/tests/reference/gui/1_nwProject.nwx
@@ -1,60 +1,72 @@
-
+
- False
- None
- None
+ True
+ 31489056e0916
+ 31489056e0916
+
+ New
+ Note
+ Draft
+ Finished
+
+
+ New
+ Minor
+ Major
+ Main
+
-
Novel
ROOT
NOVEL
- 0
- False
+ New
+ True
-
New Chapter
FOLDER
NOVEL
- 0
- False
+ New
+ True
-
New Scene
FILE
NOVEL
- 0
+ New
False
SCENE
- 0
- 0
- 0
- 0
+ 377
+ 69
+ 2
+ 443
-
Characters
ROOT
CHARACTER
- 0
+ New
False
-
Plot
ROOT
PLOT
- 0
+ New
False
-
World
ROOT
WORLD
- 0
+ New
False
diff --git a/tests/reference/gui/2_nwProject.nwx b/tests/reference/gui/2_nwProject.nwx
index 33070870..02f3c384 100644
--- a/tests/reference/gui/2_nwProject.nwx
+++ b/tests/reference/gui/2_nwProject.nwx
@@ -1,5 +1,5 @@
-
+
Project Name
Project Title
@@ -10,27 +10,39 @@
False
None
None
+
+ New
+ Note
+ Finished
+ Final
+
+
+ New
+ Minor
+ Major
+ Main
+
-
Novel
ROOT
NOVEL
- 0
+ New
False
-
New Chapter
FOLDER
NOVEL
- 0
+ New
False
-
New Scene
FILE
NOVEL
- 0
+ New
False
SCENE
0
@@ -42,21 +54,21 @@
Characters
ROOT
CHARACTER
- 0
+ New
False
-
Plot
ROOT
PLOT
- 0
+ New
False
-
World
ROOT
WORLD
- 0
+ New
False
diff --git a/tests/reference/gui/3_nwProject.nwx b/tests/reference/gui/3_nwProject.nwx
index ecc54350..510192ff 100644
--- a/tests/reference/gui/3_nwProject.nwx
+++ b/tests/reference/gui/3_nwProject.nwx
@@ -1,5 +1,5 @@
-
+
@@ -8,27 +8,39 @@
False
None
None
+
+ New
+ Note
+ Draft
+ Finished
+
+
+ New
+ Minor
+ Major
+ Main
+
-
Novel
ROOT
NOVEL
- 0
+ New
False
-
New Chapter
FOLDER
NOVEL
- 0
+ New
False
-
Just a Page
FILE
NOVEL
- 1
+ Note
False
PAGE
0
@@ -40,21 +52,21 @@
Characters
ROOT
CHARACTER
- 0
+ New
False
-
Plot
ROOT
PLOT
- 0
+ New
False
-
World
ROOT
WORLD
- 0
+ New
False
diff --git a/tests/reference/proj/1_nwProject.nwx b/tests/reference/proj/1_nwProject.nwx
index 181e1166..e9ff11f5 100644
--- a/tests/reference/proj/1_nwProject.nwx
+++ b/tests/reference/proj/1_nwProject.nwx
@@ -1,5 +1,5 @@
-
+
@@ -8,48 +8,60 @@
False
None
None
+
+ New
+ Note
+ Draft
+ Finished
+
+
+ New
+ Minor
+ Major
+ Main
+
-
Novel
ROOT
NOVEL
- 0
+ New
False
-
Characters
ROOT
CHARACTER
- 0
+ New
False
-
Plot
ROOT
PLOT
- 0
+ New
False
-
World
ROOT
WORLD
- 0
+ New
False
-
New Chapter
FOLDER
NOVEL
- 0
+ New
False
-
New Scene
FILE
NOVEL
- 0
+ New
False
SCENE
0
diff --git a/tests/reference/proj/2_nwProject.nwx b/tests/reference/proj/2_nwProject.nwx
index f9dbdef1..8c68bc42 100644
--- a/tests/reference/proj/2_nwProject.nwx
+++ b/tests/reference/proj/2_nwProject.nwx
@@ -1,5 +1,5 @@
-
+
@@ -8,48 +8,60 @@
False
None
None
+
+ New
+ Note
+ Draft
+ Finished
+
+
+ New
+ Minor
+ Major
+ Main
+
-
Novel
ROOT
NOVEL
- 0
+ New
False
-
Characters
ROOT
CHARACTER
- 0
+ New
False
-
Plot
ROOT
PLOT
- 0
+ New
False
-
World
ROOT
WORLD
- 0
+ New
False
-
New Chapter
FOLDER
NOVEL
- 0
+ New
False
-
New Scene
FILE
NOVEL
- 0
+ New
False
SCENE
0
@@ -61,28 +73,28 @@
Timeline
ROOT
TIMELINE
- 0
+ New
False
-
Object
ROOT
OBJECT
- 0
+ New
False
-
Custom1
ROOT
CUSTOM
- 0
+ New
False
-
Custom2
ROOT
CUSTOM
- 0
+ New
False
diff --git a/tests/test_gui.py b/tests/test_gui.py
index 681c2116..ac47fdd2 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -99,15 +99,19 @@ def testMainWindows(qtbot, nwTempGUI, nwRef):
qtbot.wait(stepDelay)
nwGUI.docEditor.wCounter.run()
qtbot.wait(stepDelay)
+ nwGUI.docEditor._updateCounts()
# Save the document
assert nwGUI.docEditor.docChanged
assert nwGUI.saveDocument()
+ assert not nwGUI.docEditor.docChanged
qtbot.wait(stepDelay)
# Open and view the edited document
assert nwGUI.openDocument("31489056e0916")
assert nwGUI.viewDocument("31489056e0916")
+ qtbot.wait(stepDelay)
+ assert nwGUI.saveProject()
# Check the files
projFile = path.join(nwTempGUI,"nwProject.nwx")
@@ -135,27 +139,40 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef):
qtbot.addWidget(projEdit)
for c in "Project Name":
- qtbot.keyClick(projEdit.editName, c, delay=keyDelay)
+ qtbot.keyClick(projEdit.tabMain.editName, c, delay=keyDelay)
for c in "Project Title":
- qtbot.keyClick(projEdit.editTitle, c, delay=keyDelay)
+ qtbot.keyClick(projEdit.tabMain.editTitle, c, delay=keyDelay)
for c in "Jane Doe":
- qtbot.keyClick(projEdit.editAuthors, c, delay=keyDelay)
- qtbot.keyClick(projEdit.editAuthors, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=keyDelay)
+ qtbot.keyClick(projEdit.tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
for c in "John Doh":
- qtbot.keyClick(projEdit.editAuthors, c, delay=keyDelay)
+ qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=keyDelay)
- qtbot.mouseClick(projEdit.saveButton, Qt.LeftButton)
+ #Test Status Tab
+ projEdit.tabWidget.setCurrentWidget(projEdit.tabStatus)
+ projEdit.tabStatus.listBox.item(2).setSelected(True)
+ qtbot.mouseClick(projEdit.tabStatus.delButton, Qt.LeftButton)
+ qtbot.mouseClick(projEdit.tabStatus.newButton, Qt.LeftButton)
+ projEdit.tabStatus.listBox.item(3).setSelected(True)
+ for n in range(8):
+ qtbot.keyClick(projEdit.tabStatus.editName, Qt.Key_Backspace, delay=keyDelay)
+ for c in "Final":
+ qtbot.keyClick(projEdit.tabStatus.editName, c, delay=keyDelay)
+ qtbot.mouseClick(projEdit.tabStatus.saveButton, Qt.LeftButton)
+ projEdit._doSave()
+
+ # Open again, and check project settings
projEdit = GuiProjectEditor(nwGUI, nwGUI.theProject)
qtbot.addWidget(projEdit)
- assert projEdit.editName.text() == "Project Name"
- assert projEdit.editTitle.text() == "Project Title"
- theAuth = projEdit.editAuthors.toPlainText().strip().splitlines()
+ assert projEdit.tabMain.editName.text() == "Project Name"
+ assert projEdit.tabMain.editTitle.text() == "Project Title"
+ theAuth = projEdit.tabMain.editAuthors.toPlainText().strip().splitlines()
assert len(theAuth) == 2
assert theAuth[0] == "Jane Doe"
assert theAuth[1] == "John Doh"
- qtbot.mouseClick(projEdit.closeButton, Qt.LeftButton)
+ projEdit._doClose()
qtbot.wait(stepDelay)
assert nwGUI.saveProject()
@@ -185,7 +202,7 @@ def testItemEditor(qtbot, nwTempGUI, nwRef):
qtbot.addWidget(itemEdit)
assert itemEdit.editName.text() == "New Scene"
- assert itemEdit.editStatus.currentData() == 0
+ assert itemEdit.editStatus.currentData() == "New"
assert itemEdit.editLayout.currentData() == nwItemLayout.SCENE
for c in "Just a Page":
@@ -199,7 +216,7 @@ def testItemEditor(qtbot, nwTempGUI, nwRef):
itemEdit = GuiItemEditor(nwGUI, nwGUI.theProject, "31489056e0916")
qtbot.addWidget(itemEdit)
assert itemEdit.editName.text() == "Just a Page"
- assert itemEdit.editStatus.currentData() == 1
+ assert itemEdit.editStatus.currentData() == "Note"
assert itemEdit.editLayout.currentData() == nwItemLayout.PAGE
qtbot.mouseClick(itemEdit.closeButton, Qt.LeftButton)