diff --git a/nw/gui/elements/outline.py b/nw/gui/elements/outline.py index 02f313e2..26ce7855 100644 --- a/nw/gui/elements/outline.py +++ b/nw/gui/elements/outline.py @@ -129,11 +129,23 @@ class GuiProjectOutline(QTreeWidget): tree. """ + # If it's the first time, we always build if self.firstView or overRide: self._loadHeaderState() self._populateTree() + self.firstView = False + return - self.firstView = False + # If the novel index has changed since the tree was last built, + # we rebuild the tree from the updated index. + lastChange = self.theParent.theIndex.timeNovel + logger.verbose("Last outline build: %.3f" % self.lastBuild) + logger.verbose("Novel index change: %.3f" % lastChange) + + doBuild = lastChange > self.lastBuild and self.theProject.autoOutline + if doBuild or overRide: + logger.debug("Rebuilding Project Outline") + self._populateTree() return diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 7d866564..a2898263 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -69,6 +69,13 @@ class GuiMainMenu(QMenuBar): self.aSpellCheck.setChecked(theMode) return + def setAutoOutline(self, theMode): + """Set the auto outline check box to theMode. Used during + initialisation. + """ + self.aAutoOutline.setChecked(theMode) + return + ## # Menu Action ## @@ -85,6 +92,12 @@ class GuiMainMenu(QMenuBar): self.theParent.docEditor.setSpellCheck(None) return True + def _toggleAutoOutline(self, theMode): + """Toggle auto outline when the menu entry is checked. + """ + self.theProject.setAutoOutline(theMode) + return True + def _toggleViewComments(self): self.mainConf.setViewComments(self.aViewDocComments.isChecked()) self.theParent.docViewer.reloadText() @@ -646,6 +659,24 @@ class GuiMainMenu(QMenuBar): self.aRebuildIndex.triggered.connect(self.theParent.rebuildIndex) self.toolsMenu.addAction(self.aRebuildIndex) + # Tools > Rebuild Outline + self.aRebuildOutline = QAction("Rebuild Outline", self) + self.aRebuildOutline.setStatusTip("Rebuild the novel outline tree") + self.aRebuildOutline.setShortcut("F10") + self.aRebuildOutline.triggered.connect(self.theParent.rebuildOutline) + self.toolsMenu.addAction(self.aRebuildOutline) + + # Tools > Toggle Auto Build Outline + self.aAutoOutline = QAction("Auto-Update Outline", self) + self.aAutoOutline.setStatusTip("Update project outline when a novel file is changed") + self.aAutoOutline.setCheckable(True) + self.aAutoOutline.toggled.connect(self._toggleAutoOutline) + self.aAutoOutline.setShortcut("Ctrl+F10") + self.toolsMenu.addAction(self.aAutoOutline) + + # Tools > Separator + self.toolsMenu.addSeparator() + # Tools > Backup self.aBackupProject = QAction("Backup Project", self) self.aBackupProject.setStatusTip("Backup Project") diff --git a/nw/guimain.py b/nw/guimain.py index fb35e4dc..cb1331bc 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -386,6 +386,7 @@ class GuiMain(QMainWindow): self.rebuildTree() self.docEditor.setDictionaries() self.docEditor.setSpellCheck(self.theProject.spellCheck) + self.mainMenu.setAutoOutline(self.theProject.autoOutline) self.statusBar.setRefTime(self.theProject.projOpened) # Restore previously open documents, if any @@ -438,6 +439,7 @@ class GuiMain(QMainWindow): def openDocument(self, tHandle): if self.hasProject: self.closeDocument() + self.tabWidget.setCurrentWidget(self.splitView) if self.docEditor.loadText(tHandle): self.docEditor.setFocus() self.theProject.setLastEdited(tHandle) @@ -464,6 +466,9 @@ class GuiMain(QMainWindow): logger.debug("No document selected, giving up") return False + # Make sure main tab is in Editor view + self.tabWidget.setCurrentWidget(self.splitView) + if self.docViewer.loadText(tHandle) and not self.viewPane.isVisible(): bPos = self.splitMain.sizes() self.viewPane.setVisible(True) @@ -652,6 +657,14 @@ class GuiMain(QMainWindow): return True + def rebuildOutline(self): + """Force a rebuild of the Outline view. + """ + logger.verbose("Forcing a rebuild of the Project Outline") + self.tabWidget.setCurrentWidget(self.splitOutline) + self.projView.refreshTree(overRide=True) + return True + ## # Main Dialogs ## @@ -986,7 +999,7 @@ class GuiMain(QMainWindow): def _keyPressEscape(self): """When the escape key is pressed somewhere in the main window, - do the following, in order. + do the following, in order: """ if self.searchBar.isVisible(): self.searchBar.setVisible(False) diff --git a/nw/project/index.py b/nw/project/index.py index 499d5765..6e344bfe 100644 --- a/nw/project/index.py +++ b/nw/project/index.py @@ -63,6 +63,11 @@ class NWIndex(): self.noteIndex = None self.textCounts = None + # TimeStamps + self.timeNovel = 0 + self.timeNote = 0 + self.timeIndex = 0 + self.clearIndex() return @@ -72,14 +77,21 @@ class NWIndex(): ## def clearIndex(self): + """Clear the index dictionaries and time stamps. + """ self.tagIndex = {} self.refIndex = {} self.novelIndex = {} self.noteIndex = {} self.textCounts = {} + self.timeNovel = 0 + self.timeNote = 0 + self.timeIndex = 0 return def deleteHandle(self, tHandle): + """Delete all entries of a given document handle. + """ delTags = [] for tTag in self.tagIndex: @@ -129,6 +141,11 @@ class NWIndex(): if "textCounts" in theData.keys(): self.textCounts = theData["textCounts"] + nowTime = time() + self.timeNovel = nowTime + self.timeNote = nowTime + self.timeIndex = nowTime + self.checkIndex() return True @@ -289,6 +306,14 @@ class NWIndex(): cC, wC, pC = countWords(theText) self.textCounts[tHandle] = [cC, wC, pC] + # Update timestamps for index changes + nowTime = time() + self.timeIndex = nowTime + if isNovel: + self.timeNovel = nowTime + else: + self.timeNote = nowTime + return True def indexTitle(self, tHandle, isNovel, aLine, nLine, itemLayout): diff --git a/nw/project/project.py b/nw/project/project.py index 202491af..d6f13c9a 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -67,6 +67,7 @@ class NWProject(): # Project Settings self.spellCheck = False + self.autoOutline = True self.statusItems = None self.importItems = None self.lastEdited = None @@ -169,6 +170,7 @@ class NWProject(): self.bookAuthors = [] self.autoReplace = {} self.spellCheck = False + self.autoOutline = True self.statusItems = NWStatus() self.statusItems.addEntry("New", (100,100,100)) self.statusItems.addEntry("Note", (200, 50, 0)) @@ -299,6 +301,8 @@ class NWProject(): continue if xItem.tag == "spellCheck": self.spellCheck = checkBool(xItem.text,False) + elif xItem.tag == "autoOutline": + self.autoOutline = checkBool(xItem.text,True) elif xItem.tag == "lastEdited": self.lastEdited = checkString(xItem.text,None,True) elif xItem.tag == "lastViewed": @@ -390,6 +394,7 @@ class NWProject(): # Save Project Settings xSettings = etree.SubElement(nwXML, "settings") self._saveProjectValue(xSettings, "spellCheck", self.spellCheck) + self._saveProjectValue(xSettings, "autoOutline", self.autoOutline) self._saveProjectValue(xSettings, "lastEdited", self.lastEdited) self._saveProjectValue(xSettings, "lastViewed", self.lastViewed) self._saveProjectValue(xSettings, "lastWordCount", self.currWCount) @@ -513,6 +518,12 @@ class NWProject(): self.setProjectChanged(True) return True + def setAutoOutline(self, theMode): + if self.autoOutline != theMode: + self.autoOutline = theMode + self.setProjectChanged(True) + return True + def setTreeOrder(self, newOrder): if len(self.treeOrder) != len(newOrder): logger.warning("Size of new and old tree order does not match") diff --git a/sample/sampleNovel/nwProject.nwx b/sample/sampleNovel/nwProject.nwx index a6828145..53cd2fe6 100644 --- a/sample/sampleNovel/nwProject.nwx +++ b/sample/sampleNovel/nwProject.nwx @@ -1,5 +1,5 @@ - + Sample Project Sample Project @@ -9,7 +9,8 @@ True - 88706ddc78b1b + True + 6a2d6d5f4f401 b3e74dbc1f584 869 @@ -70,7 +71,7 @@ 12 3 0 - 212 + 15 Making a Scene