Merge pull request #637 from vkbo/issue636_noveltab_wcount
Issue #636: Novel Tab Word Count
This commit is contained in:
@@ -49,7 +49,6 @@ class nwConst():
|
|||||||
class nwLists():
|
class nwLists():
|
||||||
"""Lists used for grouping various other constants.
|
"""Lists used for grouping various other constants.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Regular user-accessible item types
|
# Regular user-accessible item types
|
||||||
REG_TYPES = {nwItemType.ROOT, nwItemType.FOLDER, nwItemType.FILE}
|
REG_TYPES = {nwItemType.ROOT, nwItemType.FOLDER, nwItemType.FILE}
|
||||||
|
|
||||||
|
|||||||
@@ -608,6 +608,36 @@ class NWIndex():
|
|||||||
|
|
||||||
return hCount
|
return hCount
|
||||||
|
|
||||||
|
def getHandleWordCounts(self, tHandle):
|
||||||
|
"""Get all header word counts for a specific handle.
|
||||||
|
"""
|
||||||
|
theCounts = []
|
||||||
|
hRecord = self._novelIndex.get(tHandle, None)
|
||||||
|
if hRecord is None:
|
||||||
|
hRecord = self._noteIndex.get(tHandle, None)
|
||||||
|
if hRecord is None:
|
||||||
|
return theCounts
|
||||||
|
|
||||||
|
for sTitle, sData in hRecord.items():
|
||||||
|
theCounts.append(("%s:%s" % (tHandle, sTitle), sData["wCount"]))
|
||||||
|
|
||||||
|
return theCounts
|
||||||
|
|
||||||
|
def getHandleHeaders(self, tHandle):
|
||||||
|
"""Get all headers for a specific handle.
|
||||||
|
"""
|
||||||
|
theHeaders = []
|
||||||
|
hRecord = self._novelIndex.get(tHandle, None)
|
||||||
|
if hRecord is None:
|
||||||
|
hRecord = self._noteIndex.get(tHandle, None)
|
||||||
|
if hRecord is None:
|
||||||
|
return theHeaders
|
||||||
|
|
||||||
|
for sTitle, sData in hRecord.items():
|
||||||
|
theHeaders.append((sTitle, sData["level"], sData["title"]))
|
||||||
|
|
||||||
|
return theHeaders
|
||||||
|
|
||||||
def getTableOfContents(self, maxDepth, skipExcluded=True):
|
def getTableOfContents(self, maxDepth, skipExcluded=True):
|
||||||
"""Generate a table of contents up to a maxiumum depth.
|
"""Generate a table of contents up to a maxiumum depth.
|
||||||
"""
|
"""
|
||||||
|
|||||||
+33
-1
@@ -75,12 +75,14 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.mainConf = nw.CONFIG
|
self.mainConf = nw.CONFIG
|
||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
self.theTheme = theParent.theTheme
|
self.theTheme = theParent.theTheme
|
||||||
|
self.theIndex = theParent.theIndex
|
||||||
self.theProject = theParent.theProject
|
self.theProject = theParent.theProject
|
||||||
self.nwDocument = NWDoc(self.theProject, self.theParent)
|
self.nwDocument = NWDoc(self.theProject, self.theParent)
|
||||||
|
|
||||||
self.docChanged = False # Flag for changed status of document
|
self.docChanged = False # Flag for changed status of document
|
||||||
self.spellCheck = False # Flag for spell checking enabled
|
self.spellCheck = False # Flag for spell checking enabled
|
||||||
self.theHandle = None # The handle of the open file
|
self.theHandle = None # The handle of the open file
|
||||||
|
self.theHeaders = [] # Record of headers in the file
|
||||||
self.theDict = None # The current spell check dictionary
|
self.theDict = None # The current spell check dictionary
|
||||||
self.nonWord = "\"'" # Characters to not include in spell checking
|
self.nonWord = "\"'" # Characters to not include in spell checking
|
||||||
|
|
||||||
@@ -342,6 +344,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
self.docFooter.updateLineCount()
|
self.docFooter.updateLineCount()
|
||||||
self.lengthLast = self.qDocument.characterCount()
|
self.lengthLast = self.qDocument.characterCount()
|
||||||
|
self.theHeaders = self.theIndex.getHandleHeaders(self.theHandle)
|
||||||
|
|
||||||
qApp.processEvents()
|
qApp.processEvents()
|
||||||
self.setDocumentChanged(False)
|
self.setDocumentChanged(False)
|
||||||
@@ -398,6 +401,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
docText = self.getText()
|
docText = self.getText()
|
||||||
|
tHandle = theItem.itemHandle
|
||||||
|
|
||||||
cC, wC, pC = countWords(docText)
|
cC, wC, pC = countWords(docText)
|
||||||
self._updateCounts(cC, wC, pC)
|
self._updateCounts(cC, wC, pC)
|
||||||
@@ -410,7 +414,11 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.nwDocument.saveDocument(docText)
|
self.nwDocument.saveDocument(docText)
|
||||||
self.setDocumentChanged(False)
|
self.setDocumentChanged(False)
|
||||||
|
|
||||||
self.theParent.theIndex.scanText(theItem.itemHandle, docText)
|
self.theIndex.scanText(tHandle, docText)
|
||||||
|
if self._updateHeaders(checkLevel=True):
|
||||||
|
self.theParent.novelView.refreshTree()
|
||||||
|
else:
|
||||||
|
self.theParent.novelView.updateWordCounts(tHandle)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1219,6 +1227,30 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def _updateHeaders(self, checkPos=False, checkLevel=False):
|
||||||
|
"""Update the headers record and return True if anything
|
||||||
|
changed, if a check flag was provided.
|
||||||
|
"""
|
||||||
|
if self.theHandle is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
newHeaders = self.theIndex.getHandleHeaders(self.theHandle)
|
||||||
|
if checkPos:
|
||||||
|
newPos = [x[0] for x in newHeaders]
|
||||||
|
oldPos = [x[0] for x in self.theHeaders]
|
||||||
|
if checkLevel:
|
||||||
|
newLev = [x[1] for x in newHeaders]
|
||||||
|
oldLev = [x[1] for x in self.theHeaders]
|
||||||
|
|
||||||
|
self.theHeaders = newHeaders
|
||||||
|
|
||||||
|
if checkPos:
|
||||||
|
return newPos != oldPos
|
||||||
|
if checkLevel:
|
||||||
|
return newLev != oldLev
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def _replaceQuotes(self, sQuote, oQuote, cQuote):
|
def _replaceQuotes(self, sQuote, oQuote, cQuote):
|
||||||
"""Replace all straight quotes in the selected text.
|
"""Replace all straight quotes in the selected text.
|
||||||
"""
|
"""
|
||||||
|
|||||||
+27
-19
@@ -1,28 +1,27 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""novelWriter GUI Novel Tree
|
"""
|
||||||
|
novelWriter – GUI Novel Tree
|
||||||
|
============================
|
||||||
|
GUI classe for the main window novel tree
|
||||||
|
|
||||||
novelWriter – GUI Novel Tree
|
File History:
|
||||||
==============================
|
Created: 2020-12-20 [1.1a0]
|
||||||
Class holding the project's novel files tree view
|
|
||||||
|
|
||||||
File History:
|
This file is a part of novelWriter
|
||||||
Created: 2020-12-20 [1.1a0]
|
Copyright 2018–2020, Veronica Berglyd Olsen
|
||||||
|
|
||||||
This file is a part of novelWriter
|
This program is free software: you can redistribute it and/or modify
|
||||||
Copyright 2018–2020, Veronica Berglyd Olsen
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is distributed in the hope that it will be useful, but
|
||||||
it under the terms of the GNU General Public License as published by
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
(at your option) any later version.
|
General Public License for more details.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
You should have received a copy of the GNU General Public License
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
@@ -151,6 +150,15 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def updateWordCounts(self, tHandle):
|
||||||
|
"""Update the word count for a given handle.
|
||||||
|
"""
|
||||||
|
tHeaders = self.theIndex.getHandleWordCounts(tHandle)
|
||||||
|
for titleKey, wCount in tHeaders:
|
||||||
|
if titleKey in self._treeMap:
|
||||||
|
self._treeMap[titleKey].setText(self.C_WORDS, f"{wCount:n}")
|
||||||
|
return
|
||||||
|
|
||||||
def getColumnSizes(self):
|
def getColumnSizes(self):
|
||||||
"""Return the column widths for the tree columns.
|
"""Return the column widths for the tree columns.
|
||||||
"""
|
"""
|
||||||
|
|||||||
+19
-1
@@ -1,5 +1,23 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""novelWriter Test Config
|
"""
|
||||||
|
novelWriter – Test Suite Configuration
|
||||||
|
======================================
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
+19
-1
@@ -1,5 +1,23 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""novelWriter Test Dummy GUI Classes
|
"""
|
||||||
|
novelWriter – Test Suite Dummy Classes
|
||||||
|
======================================
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# =========================================================================== #
|
# =========================================================================== #
|
||||||
|
|||||||
@@ -572,12 +572,12 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
|||||||
"@char: Jane\n\n"
|
"@char: Jane\n\n"
|
||||||
"% this is a comment\n\n"
|
"% this is a comment\n\n"
|
||||||
"This is a story about Jane Smith.\n\n"
|
"This is a story about Jane Smith.\n\n"
|
||||||
"Well, not really.\n"
|
"Well, not really. She's still awesome though.\n"
|
||||||
))
|
))
|
||||||
# Whole document
|
# Whole document
|
||||||
cC, wC, pC = theIndex.getCounts(nHandle)
|
cC, wC, pC = theIndex.getCounts(nHandle)
|
||||||
assert cC == 124
|
assert cC == 152
|
||||||
assert wC == 24
|
assert wC == 28
|
||||||
assert pC == 4
|
assert pC == 4
|
||||||
|
|
||||||
# First part
|
# First part
|
||||||
@@ -588,8 +588,8 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
|||||||
|
|
||||||
# First part
|
# First part
|
||||||
cC, wC, pC = theIndex.getCounts(nHandle, "T000011")
|
cC, wC, pC = theIndex.getCounts(nHandle, "T000011")
|
||||||
assert cC == 62
|
assert cC == 90
|
||||||
assert wC == 12
|
assert wC == 16
|
||||||
assert pC == 2
|
assert pC == 2
|
||||||
|
|
||||||
# Get section counts for a note file
|
# Get section counts for a note file
|
||||||
@@ -605,12 +605,12 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
|||||||
"@char: Jane\n\n"
|
"@char: Jane\n\n"
|
||||||
"% this is a comment\n\n"
|
"% this is a comment\n\n"
|
||||||
"This is a story about Jane Smith.\n\n"
|
"This is a story about Jane Smith.\n\n"
|
||||||
"Well, not really.\n"
|
"Well, not really. She's still awesome though.\n"
|
||||||
))
|
))
|
||||||
# Whole document
|
# Whole document
|
||||||
cC, wC, pC = theIndex.getCounts(cHandle)
|
cC, wC, pC = theIndex.getCounts(cHandle)
|
||||||
assert cC == 124
|
assert cC == 152
|
||||||
assert wC == 24
|
assert wC == 28
|
||||||
assert pC == 4
|
assert pC == 4
|
||||||
|
|
||||||
# First part
|
# First part
|
||||||
@@ -621,8 +621,8 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
|||||||
|
|
||||||
# First part
|
# First part
|
||||||
cC, wC, pC = theIndex.getCounts(cHandle, "T000011")
|
cC, wC, pC = theIndex.getCounts(cHandle, "T000011")
|
||||||
assert cC == 62
|
assert cC == 90
|
||||||
assert wC == 12
|
assert wC == 16
|
||||||
assert pC == 2
|
assert pC == 2
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -650,7 +650,7 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
|||||||
theProject.projTree._treeOrder.remove("0000000000000")
|
theProject.projTree._treeOrder.remove("0000000000000")
|
||||||
|
|
||||||
# Extract stats
|
# Extract stats
|
||||||
assert theIndex.getNovelWordCount(False) == 30
|
assert theIndex.getNovelWordCount(False) == 34
|
||||||
assert theIndex.getNovelWordCount(True) == 6
|
assert theIndex.getNovelWordCount(True) == 6
|
||||||
assert theIndex.getNovelTitleCounts(False) == [0, 2, 1, 2, 0]
|
assert theIndex.getNovelTitleCounts(False) == [0, 2, 1, 2, 0]
|
||||||
assert theIndex.getNovelTitleCounts(True) == [0, 0, 1, 2, 0]
|
assert theIndex.getNovelTitleCounts(True) == [0, 0, 1, 2, 0]
|
||||||
@@ -670,9 +670,29 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
|||||||
assert theIndex.getTableOfContents(0, False) == []
|
assert theIndex.getTableOfContents(0, False) == []
|
||||||
assert theIndex.getTableOfContents(1, False) == [
|
assert theIndex.getTableOfContents(1, False) == [
|
||||||
("%s:T000001" % nHandle, 1, "Hello World!", 12),
|
("%s:T000001" % nHandle, 1, "Hello World!", 12),
|
||||||
("%s:T000011" % nHandle, 1, "Hello World!", 18),
|
("%s:T000011" % nHandle, 1, "Hello World!", 22),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Header Word Counts
|
||||||
|
bHandle = "0000000000000"
|
||||||
|
assert theIndex.getHandleWordCounts(bHandle) == []
|
||||||
|
assert theIndex.getHandleWordCounts(hHandle) == [("%s:T000001" % hHandle, 2)]
|
||||||
|
assert theIndex.getHandleWordCounts(sHandle) == [("%s:T000001" % sHandle, 2)]
|
||||||
|
assert theIndex.getHandleWordCounts(tHandle) == [("%s:T000001" % tHandle, 2)]
|
||||||
|
assert theIndex.getHandleWordCounts(nHandle) == [
|
||||||
|
("%s:T000001" % nHandle, 12), ("%s:T000011" % nHandle, 16)
|
||||||
]
|
]
|
||||||
|
|
||||||
assert theProject.closeProject()
|
assert theProject.closeProject()
|
||||||
|
|
||||||
|
# Header Record
|
||||||
|
bHandle = "0000000000000"
|
||||||
|
assert theIndex.getHandleHeaders(bHandle) == []
|
||||||
|
assert theIndex.getHandleHeaders(hHandle) == [("T000001", "H2", "Chapter One")]
|
||||||
|
assert theIndex.getHandleHeaders(sHandle) == [("T000001", "H3", "Scene One")]
|
||||||
|
assert theIndex.getHandleHeaders(tHandle) == [("T000001", "H3", "Scene Two")]
|
||||||
|
assert theIndex.getHandleHeaders(nHandle) == [
|
||||||
|
("T000001", "H1", "Hello World!"), ("T000011", "H1", "Hello World!")
|
||||||
|
]
|
||||||
|
|
||||||
# END Test testCoreIndex_ExtractData
|
# END Test testCoreIndex_ExtractData
|
||||||
|
|||||||
+19
-1
@@ -1,5 +1,23 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""novelWriter Test Tools
|
"""
|
||||||
|
novelWriter – Test Suite Tools
|
||||||
|
==============================
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|||||||
Reference in New Issue
Block a user