Merge pull request #17 from vkbo/extended_project_editor
Extended Project Editor
This commit is contained in:
+1
-12
@@ -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],
|
||||
]
|
||||
|
||||
@@ -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
|
||||
+7
-11
@@ -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)
|
||||
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
+228
-32
@@ -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("<b>Name</b>"))
|
||||
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
|
||||
|
||||
+18
-20
@@ -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
|
||||
|
||||
##
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
+8
-4
@@ -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):
|
||||
|
||||
+76
-28
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-19 14:27:22">
|
||||
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-22 23:27:41">
|
||||
<project>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
@@ -8,22 +8,37 @@
|
||||
</project>
|
||||
<settings>
|
||||
<spellCheck>True</spellCheck>
|
||||
<lastEdited>4cd0bd12b087d</lastEdited>
|
||||
<lastEdited>636b6aa9b697b</lastEdited>
|
||||
<lastViewed>636b6aa9b697b</lastViewed>
|
||||
<status>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Notes</entry>
|
||||
<entry blue="0" green="60" red="182">Started</entry>
|
||||
<entry blue="0" green="129" red="193">1st Draft</entry>
|
||||
<entry blue="0" green="129" red="193">2nd Draft</entry>
|
||||
<entry blue="0" green="129" red="193">3rd Draft</entry>
|
||||
<entry blue="58" green="180" red="58">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry blue="100" green="100" red="100">None</entry>
|
||||
<entry blue="188" green="122" red="0">Minor</entry>
|
||||
<entry blue="180" green="0" red="21">Major</entry>
|
||||
<entry blue="175" green="0" red="117">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="14">
|
||||
<item handle="7031beac91f75" order="0" parent="None">
|
||||
<name>Novel</name>
|
||||
<type>ROOT</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="53b69b83cdafc" order="0" parent="7031beac91f75">
|
||||
<name>Title Page</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>2</status>
|
||||
<status>Started</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>TITLE</layout>
|
||||
<charCount>23</charCount>
|
||||
@@ -35,14 +50,14 @@
|
||||
<name>Some Chapter</name>
|
||||
<type>FOLDER</type>
|
||||
<class>NOVEL</class>
|
||||
<status>1</status>
|
||||
<status>Notes</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="96b68994dfa3d" order="0" parent="e7ded148d6e4a">
|
||||
<name>New Scene</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>2</status>
|
||||
<status>Started</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>2571</charCount>
|
||||
@@ -54,19 +69,19 @@
|
||||
<name>File With Stuff</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>1</status>
|
||||
<status>Notes</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>578</charCount>
|
||||
<charCount>577</charCount>
|
||||
<wordCount>104</wordCount>
|
||||
<paraCount>5</paraCount>
|
||||
<cursorPos>658</cursorPos>
|
||||
<cursorPos>603</cursorPos>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
|
||||
<name>New File</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>Notes</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>69</charCount>
|
||||
@@ -78,21 +93,21 @@
|
||||
<name>Characters</name>
|
||||
<type>ROOT</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>0</status>
|
||||
<status>None</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="f7e2d9f330615" order="0" parent="f6622b4617424">
|
||||
<name>Main Characters</name>
|
||||
<type>FOLDER</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>0</status>
|
||||
<status>None</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="14298de4d9524" order="0" parent="f7e2d9f330615">
|
||||
<name>John Smith</name>
|
||||
<type>FILE</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>0</status>
|
||||
<status>Minor</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>NOTE</layout>
|
||||
<charCount>42</charCount>
|
||||
@@ -104,7 +119,7 @@
|
||||
<name>Jane Smith</name>
|
||||
<type>FILE</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>0</status>
|
||||
<status>Major</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>NOTE</layout>
|
||||
<charCount>51</charCount>
|
||||
@@ -116,14 +131,14 @@
|
||||
<name>Locations</name>
|
||||
<type>ROOT</type>
|
||||
<class>WORLD</class>
|
||||
<status>0</status>
|
||||
<status>None</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="b3e74dbc1f584" order="0" parent="15c4492bd5107">
|
||||
<name>Earth</name>
|
||||
<type>FILE</type>
|
||||
<class>WORLD</class>
|
||||
<status>0</status>
|
||||
<status>None</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>NOTE</layout>
|
||||
<charCount>0</charCount>
|
||||
@@ -135,14 +150,14 @@
|
||||
<name>Trash</name>
|
||||
<type>TRASH</type>
|
||||
<class>TRASH</class>
|
||||
<status>0</status>
|
||||
<status>None</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="4cd0bd12b087d" order="0" parent="98acd8c76c93a">
|
||||
<name>Orphaned File 2</name>
|
||||
<type>FILE</type>
|
||||
<class>NO_CLASS</class>
|
||||
<status>0</status>
|
||||
<status>None</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>NO_LAYOUT</layout>
|
||||
<charCount>14</charCount>
|
||||
|
||||
@@ -1,60 +1,72 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.1.2" fileVersion="1.0" timeStamp="2019-05-18 14:55:20">
|
||||
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-22 21:14:26">
|
||||
<project>
|
||||
<name></name>
|
||||
<title></title>
|
||||
</project>
|
||||
<settings>
|
||||
<spellCheck>False</spellCheck>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<spellCheck>True</spellCheck>
|
||||
<lastEdited>31489056e0916</lastEdited>
|
||||
<lastViewed>31489056e0916</lastViewed>
|
||||
<status>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Note</entry>
|
||||
<entry blue="0" green="150" red="200">Draft</entry>
|
||||
<entry blue="0" green="200" red="50">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Minor</entry>
|
||||
<entry blue="0" green="150" red="200">Major</entry>
|
||||
<entry blue="0" green="200" red="50">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="6">
|
||||
<item handle="73475cb40a568" order="0" parent="None">
|
||||
<name>Novel</name>
|
||||
<type>ROOT</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<expanded>False</expanded>
|
||||
<status>New</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="25fc0e7096fc6" order="0" parent="73475cb40a568">
|
||||
<name>New Chapter</name>
|
||||
<type>FOLDER</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<expanded>False</expanded>
|
||||
<status>New</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="31489056e0916" order="0" parent="25fc0e7096fc6">
|
||||
<name>New Scene</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>0</charCount>
|
||||
<wordCount>0</wordCount>
|
||||
<paraCount>0</paraCount>
|
||||
<cursorPos>0</cursorPos>
|
||||
<charCount>377</charCount>
|
||||
<wordCount>69</wordCount>
|
||||
<paraCount>2</paraCount>
|
||||
<cursorPos>443</cursorPos>
|
||||
</item>
|
||||
<item handle="44cb730c42048" order="1" parent="None">
|
||||
<name>Characters</name>
|
||||
<type>ROOT</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="71ee45a3c0db9" order="2" parent="None">
|
||||
<name>Plot</name>
|
||||
<type>ROOT</type>
|
||||
<class>PLOT</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="811786ad1ae74" order="3" parent="None">
|
||||
<name>World</name>
|
||||
<type>ROOT</type>
|
||||
<class>WORLD</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.1.2" fileVersion="1.0" timeStamp="2019-05-18 14:58:58">
|
||||
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-22 21:35:39">
|
||||
<project>
|
||||
<name>Project Name</name>
|
||||
<title>Project Title</title>
|
||||
@@ -10,27 +10,39 @@
|
||||
<spellCheck>False</spellCheck>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<status>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Note</entry>
|
||||
<entry blue="0" green="200" red="50">Finished</entry>
|
||||
<entry blue="0" green="0" red="0">Final</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Minor</entry>
|
||||
<entry blue="0" green="150" red="200">Major</entry>
|
||||
<entry blue="0" green="200" red="50">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="6">
|
||||
<item handle="73475cb40a568" order="0" parent="None">
|
||||
<name>Novel</name>
|
||||
<type>ROOT</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="25fc0e7096fc6" order="0" parent="73475cb40a568">
|
||||
<name>New Chapter</name>
|
||||
<type>FOLDER</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="31489056e0916" order="0" parent="25fc0e7096fc6">
|
||||
<name>New Scene</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>0</charCount>
|
||||
@@ -42,21 +54,21 @@
|
||||
<name>Characters</name>
|
||||
<type>ROOT</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="71ee45a3c0db9" order="2" parent="None">
|
||||
<name>Plot</name>
|
||||
<type>ROOT</type>
|
||||
<class>PLOT</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="811786ad1ae74" order="3" parent="None">
|
||||
<name>World</name>
|
||||
<type>ROOT</type>
|
||||
<class>WORLD</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.1.2" fileVersion="1.0" timeStamp="2019-05-18 15:00:34">
|
||||
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-22 21:16:34">
|
||||
<project>
|
||||
<name></name>
|
||||
<title></title>
|
||||
@@ -8,27 +8,39 @@
|
||||
<spellCheck>False</spellCheck>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<status>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Note</entry>
|
||||
<entry blue="0" green="150" red="200">Draft</entry>
|
||||
<entry blue="0" green="200" red="50">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Minor</entry>
|
||||
<entry blue="0" green="150" red="200">Major</entry>
|
||||
<entry blue="0" green="200" red="50">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="6">
|
||||
<item handle="73475cb40a568" order="0" parent="None">
|
||||
<name>Novel</name>
|
||||
<type>ROOT</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="25fc0e7096fc6" order="0" parent="73475cb40a568">
|
||||
<name>New Chapter</name>
|
||||
<type>FOLDER</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="31489056e0916" order="0" parent="25fc0e7096fc6">
|
||||
<name>Just a Page</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>1</status>
|
||||
<status>Note</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>PAGE</layout>
|
||||
<charCount>0</charCount>
|
||||
@@ -40,21 +52,21 @@
|
||||
<name>Characters</name>
|
||||
<type>ROOT</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="71ee45a3c0db9" order="2" parent="None">
|
||||
<name>Plot</name>
|
||||
<type>ROOT</type>
|
||||
<class>PLOT</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="811786ad1ae74" order="3" parent="None">
|
||||
<name>World</name>
|
||||
<type>ROOT</type>
|
||||
<class>WORLD</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.1.2" fileVersion="1.0" timeStamp="2019-05-18 14:46:29">
|
||||
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-22 20:48:51">
|
||||
<project>
|
||||
<name></name>
|
||||
<title></title>
|
||||
@@ -8,48 +8,60 @@
|
||||
<spellCheck>False</spellCheck>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<status>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Note</entry>
|
||||
<entry blue="0" green="150" red="200">Draft</entry>
|
||||
<entry blue="0" green="200" red="50">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Minor</entry>
|
||||
<entry blue="0" green="150" red="200">Major</entry>
|
||||
<entry blue="0" green="200" red="50">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="6">
|
||||
<item handle="73475cb40a568" order="None" parent="None">
|
||||
<name>Novel</name>
|
||||
<type>ROOT</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="44cb730c42048" order="None" parent="None">
|
||||
<name>Characters</name>
|
||||
<type>ROOT</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="71ee45a3c0db9" order="None" parent="None">
|
||||
<name>Plot</name>
|
||||
<type>ROOT</type>
|
||||
<class>PLOT</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="811786ad1ae74" order="None" parent="None">
|
||||
<name>World</name>
|
||||
<type>ROOT</type>
|
||||
<class>WORLD</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="25fc0e7096fc6" order="None" parent="73475cb40a568">
|
||||
<name>New Chapter</name>
|
||||
<type>FOLDER</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="31489056e0916" order="None" parent="25fc0e7096fc6">
|
||||
<name>New Scene</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>0</charCount>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.1.0" fileVersion="1.0" timeStamp="2019-05-12 14:40:19">
|
||||
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-22 20:52:32">
|
||||
<project>
|
||||
<name></name>
|
||||
<title></title>
|
||||
@@ -8,48 +8,60 @@
|
||||
<spellCheck>False</spellCheck>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<status>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Note</entry>
|
||||
<entry blue="0" green="150" red="200">Draft</entry>
|
||||
<entry blue="0" green="200" red="50">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Minor</entry>
|
||||
<entry blue="0" green="150" red="200">Major</entry>
|
||||
<entry blue="0" green="200" red="50">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="10">
|
||||
<item handle="73475cb40a568" order="None" parent="None">
|
||||
<name>Novel</name>
|
||||
<type>ROOT</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="44cb730c42048" order="None" parent="None">
|
||||
<name>Characters</name>
|
||||
<type>ROOT</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="71ee45a3c0db9" order="None" parent="None">
|
||||
<name>Plot</name>
|
||||
<type>ROOT</type>
|
||||
<class>PLOT</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="811786ad1ae74" order="None" parent="None">
|
||||
<name>World</name>
|
||||
<type>ROOT</type>
|
||||
<class>WORLD</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="25fc0e7096fc6" order="None" parent="73475cb40a568">
|
||||
<name>New Chapter</name>
|
||||
<type>FOLDER</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="31489056e0916" order="None" parent="25fc0e7096fc6">
|
||||
<name>New Scene</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>0</charCount>
|
||||
@@ -61,28 +73,28 @@
|
||||
<name>Timeline</name>
|
||||
<type>ROOT</type>
|
||||
<class>TIMELINE</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="d029fa3a95e17" order="None" parent="None">
|
||||
<name>Object</name>
|
||||
<type>ROOT</type>
|
||||
<class>OBJECT</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="81b8a03f97e87" order="None" parent="None">
|
||||
<name>Custom1</name>
|
||||
<type>ROOT</type>
|
||||
<class>CUSTOM</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="da4ea2a5506f2" order="None" parent="None">
|
||||
<name>Custom2</name>
|
||||
<type>ROOT</type>
|
||||
<class>CUSTOM</class>
|
||||
<status>0</status>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
+29
-12
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user