diff --git a/nw/gui/timelineview.py b/nw/gui/timelineview.py index 4fc6dd33..160a3f8a 100644 --- a/nw/gui/timelineview.py +++ b/nw/gui/timelineview.py @@ -17,12 +17,13 @@ from os import path from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon, QColor, QPixmap from PyQt5.QtWidgets import ( - QDialog, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, - QDialogButtonBox, QLabel, QPushButton, QHeaderView + QDialog, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, QDialogButtonBox, QLabel, + QPushButton, QHeaderView, QGridLayout, QGroupBox, QCheckBox ) from nw.tools.optlaststate import OptLastState from nw.constants import nwFiles +from nw.enum import nwItemClass logger = logging.getLogger(__name__) @@ -45,6 +46,8 @@ class GuiTimeLineView(QDialog): self.numCols = 0 self.outerBox = QVBoxLayout() + self.filterBox = QVBoxLayout() + self.centreBox = QHBoxLayout() self.bottomBox = QHBoxLayout() self.setWindowTitle("Timeline View") @@ -55,6 +58,7 @@ class GuiTimeLineView(QDialog): self.optState.getSetting("winHeight") ) + # TimeLine Table self.mainTable = QTableWidget() self.mainTable.setGridStyle(Qt.NoPen) @@ -66,6 +70,41 @@ class GuiTimeLineView(QDialog): self.vHeader.setSectionResizeMode(QHeaderView.ResizeToContents) self.mainTable.setVerticalHeader(self.vHeader) + # Option Box + self.optFilter = QGroupBox("Include Tags", self) + self.optFilterGrid = QGridLayout(self) + self.optFilter.setLayout(self.optFilterGrid) + + self.filterPlot = QCheckBox("Plot tags", self) + self.filterPlot.setChecked(self.optState.getSetting("fPlot")) + self.filterChar = QCheckBox("Character tags", self) + self.filterChar.setChecked(self.optState.getSetting("fChar")) + self.filterWorld = QCheckBox("Location tags", self) + self.filterWorld.setChecked(self.optState.getSetting("fWorld")) + self.filterTime = QCheckBox("Timeline tags", self) + self.filterTime.setChecked(self.optState.getSetting("fTime")) + self.filterObject = QCheckBox("Object tags", self) + self.filterObject.setChecked(self.optState.getSetting("fObject")) + self.filterCustom = QCheckBox("Custom tags", self) + self.filterCustom.setChecked(self.optState.getSetting("fCustom")) + + self.optFilterGrid.addWidget(self.filterPlot, 0, 1) + self.optFilterGrid.addWidget(self.filterChar, 1, 1) + self.optFilterGrid.addWidget(self.filterWorld, 2, 1) + self.optFilterGrid.addWidget(self.filterTime, 3, 1) + self.optFilterGrid.addWidget(self.filterObject, 4, 1) + self.optFilterGrid.addWidget(self.filterCustom, 5, 1) + + self.optHide = QGroupBox("Filters", self) + self.optHideGrid = QGridLayout(self) + self.optHide.setLayout(self.optHideGrid) + + self.hideUnused = QCheckBox("Hide unused", self) + self.hideUnused.setChecked(self.optState.getSetting("hUnused")) + + self.optHideGrid.addWidget(self.hideUnused, 0, 1) + + # Button Box self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close) self.buttonBox.rejected.connect(self._doClose) @@ -75,14 +114,21 @@ class GuiTimeLineView(QDialog): self.btnRefresh = QPushButton("Refresh Table") self.btnRefresh.clicked.connect(self._buildNovelList) - self.setLayout(self.outerBox) - self.outerBox.addWidget(self.mainTable) - self.outerBox.addLayout(self.bottomBox) self.bottomBox.addWidget(self.btnRebuild) self.bottomBox.addWidget(self.btnRefresh) self.bottomBox.addStretch() self.bottomBox.addWidget(self.buttonBox) + # Assemble + self.filterBox.addWidget(self.optFilter) + self.filterBox.addWidget(self.optHide) + self.filterBox.addStretch() + self.centreBox.addWidget(self.mainTable) + self.centreBox.addLayout(self.filterBox) + self.outerBox.addLayout(self.centreBox) + self.outerBox.addLayout(self.bottomBox) + self.setLayout(self.outerBox) + self._buildNovelList() self.buttonBox.setFocus() @@ -94,13 +140,28 @@ class GuiTimeLineView(QDialog): def _buildNovelList(self): - self.theIndex.buildNovelList() - self.numRows = len(self.theIndex.novelList) - self.numCols = len(self.theIndex.tagIndex.keys()) - self.mainTable.clear() + self.theIndex.buildNovelList() + + self.numRows = len(self.theIndex.novelList) self.mainTable.setRowCount(self.numRows) - self.mainTable.setColumnCount(self.numCols) + + theFilters = {} + theFilters["exClass"] = [] + theFilters["hUnused"] = self.hideUnused.isChecked() + + if not self.filterPlot.isChecked(): + theFilters["exClass"].append(nwItemClass.PLOT) + if not self.filterChar.isChecked(): + theFilters["exClass"].append(nwItemClass.CHARACTER) + if not self.filterWorld.isChecked(): + theFilters["exClass"].append(nwItemClass.WORLD) + if not self.filterTime.isChecked(): + theFilters["exClass"].append(nwItemClass.TIMELINE) + if not self.filterObject.isChecked(): + theFilters["exClass"].append(nwItemClass.OBJECT) + if not self.filterCustom.isChecked(): + theFilters["exClass"].append(nwItemClass.CUSTOM) for n in range(len(self.theIndex.novelList)): iDepth = self.theIndex.novelList[n][1] @@ -108,14 +169,17 @@ class GuiTimeLineView(QDialog): newItem = QTableWidgetItem("%s%s " % (" "*iDepth,iTitle)) self.mainTable.setVerticalHeaderItem(n, newItem) - theMap = self.theIndex.buildTagNovelMap(self.theIndex.tagIndex.keys()) - nCol = 0 + theMap = self.theIndex.buildTagNovelMap(self.theIndex.tagIndex.keys(), theFilters) + self.numCols = len(theMap.keys()) + self.mainTable.setColumnCount(self.numCols) + + nCol = 0 for theTag, theCols in theMap.items(): newItem = QTableWidgetItem(" %s " % theTag) self.mainTable.setHorizontalHeaderItem(nCol, newItem) for n in range(len(theCols)): if theCols[n] == 1: - pxNew = QPixmap(10,10) + pxNew = QPixmap(10,10) pxNew.fill(QColor(0,120,0)) lblNew = QLabel() lblNew.setPixmap(pxNew) @@ -123,7 +187,7 @@ class GuiTimeLineView(QDialog): lblNew.setAttribute(Qt.WA_TranslucentBackground) self.mainTable.setCellWidget(n, nCol, lblNew) elif theCols[n] == 2: - pxNew = QPixmap(10,10) + pxNew = QPixmap(10,10) pxNew.fill(QColor(0,0,120)) lblNew = QLabel() lblNew.setPixmap(pxNew) @@ -140,9 +204,23 @@ class GuiTimeLineView(QDialog): winWidth = self.width() winHeight = self.height() + fPlot = self.filterPlot.isChecked() + fChar = self.filterChar.isChecked() + fWorld = self.filterWorld.isChecked() + fTime = self.filterTime.isChecked() + fObject = self.filterObject.isChecked() + fCustom = self.filterCustom.isChecked() + hUnused = self.hideUnused.isChecked() self.optState.setSetting("winWidth", winWidth) self.optState.setSetting("winHeight",winHeight) + self.optState.setSetting("fPlot", fPlot) + self.optState.setSetting("fChar", fChar) + self.optState.setSetting("fWorld", fWorld) + self.optState.setSetting("fTime", fTime) + self.optState.setSetting("fObject", fObject) + self.optState.setSetting("fCustom", fCustom) + self.optState.setSetting("hUnused", hUnused) self.optState.saveSettings() self.close() @@ -158,9 +236,16 @@ class TimeLineLastState(OptLastState): self.theState = { "winWidth" : 700, "winHeight" : 400, + "fPlot" : True, + "fChar" : True, + "fWorld" : True, + "fTime" : True, + "fObject" : True, + "fCustom" : True, + "hUnused" : True, } self.stringOpt = () - self.boolOpt = () + self.boolOpt = ("fPlot","fChar","fWorld","fTime","fObject","fCustom","hUnused") self.intOpt = ("winWidth","winHeight") return diff --git a/nw/project/index.py b/nw/project/index.py index 8a4c6bf1..622b26c3 100644 --- a/nw/project/index.py +++ b/nw/project/index.py @@ -319,18 +319,24 @@ class NWIndex(): return True - def buildTagNovelMap(self, theTags): + def buildTagNovelMap(self, theTags, theFilters=None): tagMap = {} tagClass = {} + exClass = [] + + if theFilters is not None: + if "exClass" in theFilters.keys(): + exClass = theFilters["exClass"] for theTag in theTags: - tagMap[theTag] = [0]*len(self.novelOrder) try: tagClass[theTag] = nwItemClass[self.tagIndex[theTag][2]] except: logger.error("Could not map '%s' to nwItemClass" % self.tagIndex[theTag][2]) tagClass[theTag] = None + if tagClass[theTag] not in exClass: + tagMap[theTag] = [0]*len(self.novelOrder) for tHandle in self.refIndex: for nLine, tKey, tTag, nTitle in self.refIndex[tHandle]: @@ -342,6 +348,13 @@ class NWIndex(): except: logger.error("Could not find '%s:%d' in novelOrder" % (tHandle, nTitle)) + if theFilters["hUnused"]: + tagMapFiltered = {} + for theTag in tagMap.keys(): + if sum(tagMap[theTag]) > 0: + tagMapFiltered[theTag] = tagMap[theTag] + return tagMapFiltered + return tagMap # END Class NWIndex diff --git a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd b/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd index 0a53e976..5b15e42b 100644 --- a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd +++ b/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd @@ -3,7 +3,7 @@ % Begin Meta @pov: Jane @char: John -@location: Earth +@location: Space % End Meta Some text here would look good as well, and maybe some "dialogue"? diff --git a/sample/sampleNovel/data_b/c0cbd2a407f3_main.nwd b/sample/sampleNovel/data_b/c0cbd2a407f3_main.nwd index 525f47c8..52291896 100644 --- a/sample/sampleNovel/data_b/c0cbd2a407f3_main.nwd +++ b/sample/sampleNovel/data_b/c0cbd2a407f3_main.nwd @@ -1,7 +1,7 @@ ### Another Scene @pov: John -@location: Space +@location: Earth This is the second scene in out story. We have no idea what’s going on, so we’re just going to ramble on until we have a few lines of text so that the editor has something to work with. diff --git a/sample/sampleNovel/nwProject.nwx b/sample/sampleNovel/nwProject.nwx index dba1732b..e6689e70 100644 --- a/sample/sampleNovel/nwProject.nwx +++ b/sample/sampleNovel/nwProject.nwx @@ -1,5 +1,5 @@ - + Sample Project Sample Project @@ -82,7 +82,7 @@ 713 132 6 - 85 + 72 Another Scene