Merge branch 'main' into i18n-de_DE-created

This commit is contained in:
Veronica Berglyd Olsen
2022-11-17 16:23:14 +01:00
committed by GitHub
50 changed files with 3478 additions and 1244 deletions
+2 -2
View File
@@ -154,11 +154,11 @@ def isHandle(value):
def isTitleTag(value):
"""Check if a string is a valid title string.
"""Check if a string is a valid title tag string.
"""
if not isinstance(value, str):
return False
if len(value) != 7:
if len(value) != 5:
return False
if not value.startswith("T"):
return False
-1
View File
@@ -61,7 +61,6 @@ class nwHeaders:
H_VALID = ("H0", "H1", "H2", "H3", "H4")
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
TT_NONE = "T000000"
# END Class nwHeaders
+123 -115
View File
@@ -41,6 +41,8 @@ from novelwriter.common import (
logger = logging.getLogger(__name__)
TT_NONE = "T0000"
class NWIndex:
"""This class holds the entire index for a given project. The index
@@ -102,7 +104,7 @@ class NWIndex:
"""
self.clearIndex()
for nwItem in self._project.tree:
if nwItem is not None and nwItem.isFileType():
if nwItem.isFileType():
tHandle = nwItem.itemHandle
theDoc = self._project.storage.getDocument(tHandle)
self.scanText(tHandle, theDoc.readDocument() or "")
@@ -281,50 +283,58 @@ class NWIndex:
def _scanActive(self, tHandle, theItem, theText, itemTags):
"""Scan an active document for meta data.
"""
nTitle = 0
findHeader = True
theLines = theText.splitlines()
nTitle = 0 # Line Number of the previous title
cTitle = TT_NONE # Tag of the current title
pTitle = TT_NONE # Tag of the previous title
firstHeader = True # First header has been seen
theLines = theText.splitlines()
for nLine, aLine in enumerate(theLines, start=1):
if len(aLine.strip()) == 0:
if aLine.strip() == "":
continue
if aLine.startswith("#"):
if findHeader:
hDepth, _ = self._splitHeading(aLine)
if hDepth != "H0":
theItem.setMainHeading(hDepth)
findHeader = False
hDepth, hText = self._splitHeading(aLine)
if hDepth == "H0":
continue
isTitle = self._indexTitle(tHandle, aLine, nLine)
if isTitle and nLine > 0:
if firstHeader:
theItem.setMainHeading(hDepth)
firstHeader = False
cTitle = self._itemIndex.addItemHeading(tHandle, nLine, hDepth, hText)
if cTitle != TT_NONE:
if nTitle > 0:
# We have a new title, so we need to count the words of the previous one
lastText = "\n".join(theLines[nTitle-1:nLine-1])
self._indexWordCounts(tHandle, lastText, nTitle)
self._indexWordCounts(tHandle, lastText, pTitle)
nTitle = nLine
pTitle = cTitle
elif aLine.startswith("@"):
self._indexKeyword(tHandle, aLine, nTitle, theItem.itemClass, itemTags)
if cTitle != TT_NONE:
self._indexKeyword(tHandle, aLine, cTitle, theItem.itemClass, itemTags)
elif aLine.startswith("%"):
if nTitle > 0:
if cTitle != TT_NONE:
toCheck = aLine[1:].lstrip()
synTag = toCheck[:9].lower()
tLen = len(aLine)
cLen = len(toCheck)
cOff = tLen - cLen
if synTag == "synopsis:":
self._indexSynopsis(tHandle, aLine[cOff+9:].strip(), nTitle)
sText = aLine[cOff+9:].strip()
self._itemIndex.setHeadingSynopsis(tHandle, cTitle, sText)
# Count words for remaining text after last heading
if nTitle > 0:
if pTitle != TT_NONE:
lastText = "\n".join(theLines[nTitle-1:])
self._indexWordCounts(tHandle, lastText, nTitle)
self._indexWordCounts(tHandle, lastText, pTitle)
# Also count words on a page with no titles
if nTitle == 0:
self._indexWordCounts(tHandle, theText, nTitle)
if cTitle == TT_NONE:
self._indexWordCounts(tHandle, theText, cTitle)
# Prune no longer used tags
for tTag, isActive in itemTags.items():
@@ -362,34 +372,14 @@ class NWIndex:
return "H2", aLine[4:].strip()
return "H0", ""
def _indexTitle(self, tHandle, aLine, nTitle):
"""Save information about the title and its location in the
file to the index.
"""
hDepth, hText = self._splitHeading(aLine)
if hDepth == "H0":
return False
sTitle = f"T{nTitle:06d}"
self._itemIndex.addItemHeading(tHandle, sTitle, hDepth, hText)
return True
def _indexWordCounts(self, tHandle, theText, nTitle):
def _indexWordCounts(self, tHandle, theText, sTitle):
"""Count text stats and save the counts to the index.
"""
sTitle = f"T{nTitle:06d}"
cC, wC, pC = countWords(theText)
self._itemIndex.setHeadingCounts(tHandle, sTitle, cC, wC, pC)
self._itemIndex.setHeadingCounts(tHandle, sTitle, cC, wC, pC)
return
def _indexSynopsis(self, tHandle, theText, nTitle):
"""Save the synopsis to the index.
"""
sTitle = f"T{nTitle:06d}"
self._itemIndex.setHeadingSynopsis(tHandle, sTitle, theText)
return
def _indexKeyword(self, tHandle, aLine, nTitle, itemClass, itemTags):
def _indexKeyword(self, tHandle, aLine, sTitle, itemClass, itemTags):
"""Validate and save the information about a reference to a tag
in another file, or the setting of a tag in the file. A record
of active tags is updated so that no longer used tags can be
@@ -404,7 +394,6 @@ class NWIndex:
logger.warning("Skipping invalid keyword '%s' in '%s'", theBits[0], tHandle)
return
sTitle = f"T{nTitle:06d}"
if theBits[0] == nwKeyWords.TAG_KEY:
tagName = theBits[1]
self._tagsIndex.add(tagName, tHandle, sTitle, itemClass)
@@ -491,6 +480,19 @@ class NWIndex:
# Extract Data
##
def getItemData(self, tHandle):
"""Get the index data for a given item.
"""
return self._itemIndex[tHandle]
def getItemHeader(self, tHandle, sTitle):
"""Get the header entry for a specific item and heading.
"""
tItem = self._itemIndex[tHandle]
if isinstance(tItem, IndexItem):
return tItem[sTitle]
return None
def novelStructure(self, rootHandle=None, skipExcl=True):
"""Iterate over all titles in the novel, in the correct order as
they appear in the tree view and in the respective document
@@ -518,21 +520,13 @@ class NWIndex:
hCount[iLevel] += 1
return hCount
def getHandleWordCounts(self, tHandle):
"""Get all header word counts for a specific handle.
def getHandleHeaderCount(self, tHandle):
"""Get the number of headers in an item.
"""
return [
(f"{tHandle}:{sTitle}", hItem.wordCount)
for sTitle, hItem in self._itemIndex.iterItemHeaders(tHandle)
]
def getHandleHeaders(self, tHandle):
"""Get all headers for a specific handle.
"""
return [
(sTitle, hItem.level, hItem.title)
for sTitle, hItem in self._itemIndex.iterItemHeaders(tHandle)
]
tItem = self._itemIndex[tHandle]
if isinstance(tItem, IndexItem):
return len(tItem)
return 0
def getTableOfContents(self, rootHandle, maxDepth, skipExcl=True):
"""Generate a table of contents up to a maximum depth.
@@ -598,13 +592,6 @@ class NWIndex:
return theRefs
def getNovelData(self, tHandle, sTitle):
"""Return the novel data of a given handle and title.
"""
if tHandle in self._itemIndex:
return self._itemIndex[tHandle][sTitle]
return None
def getBackReferenceList(self, tHandle):
"""Build a list of files referring back to our file, specified
by tHandle.
@@ -644,10 +631,22 @@ class TagsIndex:
control of the keys.
"""
__slots__ = ("_tags")
def __init__(self):
self._tags = {}
return
def __contains__(self, tagKey):
return tagKey in self._tags
def __delitem__(self, tagKey):
self._tags.pop(tagKey, None)
return
def __getitem__(self, tagKey):
return self._tags.get(tagKey, None)
##
# Methods
##
@@ -658,22 +657,6 @@ class TagsIndex:
self._tags = {}
return
def __contains__(self, tagKey):
"""Check if a tag exists in the index,
"""
return tagKey in self._tags
def __delitem__(self, tagKey):
"""Delete an entry in the index.
"""
self._tags.pop(tagKey, None)
return
def __getitem__(self, tagKey):
"""Return a tag, or return None if it isn't found.
"""
return self._tags.get(tagKey, None)
def add(self, tagKey, tHandle, sTitle, itemClass):
"""Add a key to the index and set all values.
"""
@@ -690,7 +673,7 @@ class TagsIndex:
def tagHeading(self, tagKey):
"""Get the heading of a given tag.
"""
return self._tags.get(tagKey, {}).get("heading", nwHeaders.TT_NONE)
return self._tags.get(tagKey, {}).get("heading", TT_NONE)
def tagClass(self, tagKey):
"""Get the class of a given tag.
@@ -749,11 +732,23 @@ class ItemIndex:
IndexHeading object for each header of the text.
"""
__slots__ = ("_project", "_items")
def __init__(self, project):
self._project = project
self._items = {}
return
def __contains__(self, tHandle):
return tHandle in self._items
def __delitem__(self, tHandle):
self._items.pop(tHandle, None)
return
def __getitem__(self, tHandle):
return self._items.get(tHandle, None)
##
# Methods
##
@@ -764,22 +759,6 @@ class ItemIndex:
self._items = {}
return
def __contains__(self, tHandle):
"""Check if an item exists in the index,
"""
return tHandle in self._items
def __delitem__(self, tHandle):
"""Delete an entry in the index.
"""
self._items.pop(tHandle, None)
return
def __getitem__(self, tHandle):
"""Return an item, or return None if it isn't found.
"""
return self._items.get(tHandle, None)
def add(self, tHandle, tItem):
"""Add a new item to the index. This will overwrite the item if
it already exists.
@@ -815,8 +794,6 @@ class ItemIndex:
a given root handle, or for all if root handle is None.
"""
for tItem in self._project.tree:
if tItem is None:
continue
if tItem.isNoteLayout():
continue
if skipExcl and not tItem.isActive:
@@ -839,13 +816,15 @@ class ItemIndex:
# Setters
##
def addItemHeading(self, tHandle, sTitle, hDepth, hText):
"""Set the main heading level of an item.
def addItemHeading(self, tHandle, lineNo, hDepth, hText):
"""Add a heading to an item.
"""
if tHandle in self._items:
tItem = self._items[tHandle]
tItem.addHeading(IndexHeading(sTitle, hDepth, hText))
return
sTitle = tItem.nextHeading()
tItem.addHeading(IndexHeading(sTitle, lineNo, hDepth, hText))
return sTitle
return TT_NONE
def setHeadingCounts(self, tHandle, sTitle, cC, wC, pC):
"""Set the character, word and paragraph counts of a heading
@@ -916,20 +895,31 @@ class IndexItem:
must be reset each time the item is re-indexed.
"""
__slots__ = ("_handle", "_item", "_headings", "_headings", "_count")
def __init__(self, tHandle, tItem):
self._handle = tHandle
self._item = tItem
self._headings = {}
self._index = 0
self._count = 0
# Add a placeholder heading
self._headings[nwHeaders.TT_NONE] = IndexHeading(nwHeaders.TT_NONE)
self._headings[TT_NONE] = IndexHeading(TT_NONE)
return
def __repr__(self):
return f"<IndexItem handle='{self._handle}'>"
def __len__(self):
return len(self._headings)
def __getitem__(self, sTitle):
return self._headings.get(sTitle, None)
def __contains__(self, sTitle):
return sTitle in self._headings
##
# Properties
##
@@ -946,8 +936,8 @@ class IndexItem:
"""Add a heading to the item. Also remove the placeholder entry
if it exists.
"""
if nwHeaders.TT_NONE in self._headings:
self._headings.pop(nwHeaders.TT_NONE)
if TT_NONE in self._headings:
self._headings.pop(TT_NONE)
self._headings[tHeading.key] = tHeading
return
@@ -984,12 +974,6 @@ class IndexItem:
# Data Methods
##
def __getitem__(self, sTitle):
return self._headings.get(sTitle, None)
def __contains__(self, sTitle):
return sTitle in self._headings
def items(self):
return self._headings.items()
@@ -1006,6 +990,12 @@ class IndexItem:
tags.append(tag)
return tags
def nextHeading(self):
"""Return the next heading key to be used.
"""
self._count += 1
return f"T{self._count:04d}"
##
# Pack/Unpack
##
@@ -1051,8 +1041,14 @@ class IndexHeading:
of all references made under each heading.
"""
def __init__(self, key, level="H0", title=""):
__slots__ = (
"_key", "_line", "_level", "_title", "_charCount", "_wordCount",
"_paraCount", "_synopsis", "_tag", "_refs",
)
def __init__(self, key, line=0, level="H0", title=""):
self._key = key
self._line = line
self._level = level
self._title = title
@@ -1077,6 +1073,10 @@ class IndexHeading:
def key(self):
return self._key
@property
def line(self):
return self._line
@property
def level(self):
return self._level
@@ -1120,6 +1120,12 @@ class IndexHeading:
self._level = level
return
def setLine(self, line):
"""Set the line number of a heading.
"""
self._line = max(0, checkInt(line, 0))
return
def setCounts(self, charCount, wordCount, paraCount):
"""Set the character, word and paragraph count. Make sure the
value is an integer and is not smaller than 0.
@@ -1161,6 +1167,7 @@ class IndexHeading:
return {
"level": self._level,
"title": self._title,
"line": self._line,
"tag": self._tag,
"cCount": self._charCount,
"wCount": self._wordCount,
@@ -1182,6 +1189,7 @@ class IndexHeading:
self.setLevel(data.get("level", "H0"))
self._title = str(data.get("title", ""))
self._tag = str(data.get("tag", ""))
self.setLine(data.get("line", 0))
self.setCounts(
data.get("cCount", 0),
data.get("wCount", 0),
+16 -33
View File
@@ -50,7 +50,6 @@ class NWTree:
self._treeRoots = {} # The root items of the tree
self._trashRoot = None # The handle of the trash root folder
self._archRoot = None # The handle of the archive root folder
self._theIndex = 0 # The current iterator index
self._treeChanged = False # True if tree structure has changed
return
@@ -62,12 +61,11 @@ class NWTree:
def clear(self):
"""Clear the item tree entirely.
"""
self._projTree = {}
self._treeOrder = []
self._treeRoots = {}
self._trashRoot = None
self._archRoot = None
self._theIndex = 0
self._projTree = {}
self._treeOrder = []
self._treeRoots = {}
self._trashRoot = None
self._archRoot = None
self._treeChanged = False
return
@@ -278,7 +276,7 @@ class NWTree:
"""
for tHandle in self._treeOrder:
nwItem = self.__getitem__(tHandle)
if nwItem is not None and nwItem.isRootType():
if isinstance(nwItem, NWItem) and nwItem.isRootType():
if itemClass is None or nwItem.itemClass == itemClass:
yield tHandle, nwItem
return
@@ -365,22 +363,18 @@ class NWTree:
return True
##
# Meta Methods
# Special Methods
##
def __len__(self):
"""Return the length counter. Does not check that it is correct!
"""The number of items in the project.
"""
return len(self._treeOrder)
def __bool__(self):
"""Returns True if the tree has any entries.
"""True if there are any items in the project.
"""
return len(self._treeOrder) > 0
##
# Item Access Methods
##
return bool(self._treeOrder)
def __getitem__(self, tHandle):
"""Return a project item based on its handle. Returns None if
@@ -417,25 +411,14 @@ class NWTree:
"""
return tHandle in self._treeOrder
##
# Iterator Methods
##
def __iter__(self):
"""Initiates the iterator.
"""Iterate through project items.
"""
self._theIndex = 0
return self
def __next__(self):
"""Returns the item from the next entry in the _treeOrder list.
"""
if self._theIndex < len(self._treeOrder):
theItem = self.__getitem__(self._treeOrder[self._theIndex])
self._theIndex += 1
return theItem
else:
raise StopIteration
for tHandle in self._treeOrder:
tItem = self._projTree.get(tHandle)
if isinstance(tItem, NWItem):
yield tItem
return
##
# Internal Functions
-9
View File
@@ -130,15 +130,6 @@ class nwAlert(Enum):
# END Enum nwAlert
class nwState(Enum):
NONE = 0
BAD = 1
GOOD = 2
# END Enum nwState
class nwView(Enum):
EDITOR = 0
+2 -2
View File
@@ -28,7 +28,7 @@ from novelwriter.gui.outline import GuiOutlineView
from novelwriter.gui.projtree import GuiProjectView
from novelwriter.gui.statusbar import GuiMainStatus
from novelwriter.gui.theme import GuiTheme
from novelwriter.gui.viewsbar import GuiViewsBar
from novelwriter.gui.sidebar import GuiSideBar
__all__ = [
"GuiDocEditor",
@@ -41,5 +41,5 @@ __all__ = [
"GuiOutlineView",
"GuiProjectView",
"GuiTheme",
"GuiViewsBar",
"GuiSideBar",
]
+34 -34
View File
@@ -51,8 +51,8 @@ from PyQt5.QtWidgets import (
)
from novelwriter.core import NWSpellEnchant, countWords
from novelwriter.enum import nwAlert, nwDocAction, nwDocInsert, nwDocMode
from novelwriter.common import transferCase
from novelwriter.enum import nwAlert, nwDocAction, nwDocInsert, nwDocMode, nwItemClass
from novelwriter.common import minmax, transferCase
from novelwriter.constants import nwConst, nwFiles, nwKeyWords, nwUnicode
from novelwriter.gui.dochighlight import GuiDocHighlighter
@@ -71,6 +71,8 @@ class GuiDocEditor(QTextEdit):
docEditedStatusChanged = pyqtSignal(bool)
docCountsChanged = pyqtSignal(str, int, int, int)
loadDocumentTagRequest = pyqtSignal(str, Enum)
novelStructureChanged = pyqtSignal()
novelItemMetaChanged = pyqtSignal(str)
def __init__(self, mainGui):
super().__init__(parent=mainGui)
@@ -88,7 +90,6 @@ class GuiDocEditor(QTextEdit):
self._docChanged = False # Flag for changed status of document
self._docHandle = None # The handle of the open file
self._docHeaders = [] # Record of headers in the file
self._spellCheck = False # Flag for spell checking enabled
self._nonWord = "\"'" # Characters to not include in spell checking
@@ -415,7 +416,7 @@ class GuiDocEditor(QTextEdit):
self._queuePos = self._nwItem.cursorPos
else:
self.setCursorPosition(self._nwItem.cursorPos)
else:
elif isinstance(tLine, int):
self.setCursorLine(tLine)
if self.mainConf.scrollPastEnd > 0:
@@ -425,7 +426,6 @@ class GuiDocEditor(QTextEdit):
self.document().rootFrame().setFrameFormat(docFrame)
self.docFooter.updateLineCount()
self._docHeaders = self.theProject.index.getHandleHeaders(self._docHandle)
qApp.processEvents()
self.document().clearUndoRedoStacks()
@@ -531,14 +531,16 @@ class GuiDocEditor(QTextEdit):
self.setDocumentChanged(False)
oldHeader = self._nwItem.mainHeading
oldCount = self.theProject.index.getHandleHeaderCount(tHandle)
self.theProject.index.scanText(tHandle, docText)
newHeader = self._nwItem.mainHeading
newCount = self.theProject.index.getHandleHeaderCount(tHandle)
# ToDo: This should be a signal
if self._updateHeaders():
self.mainGui.requestNovelTreeRefresh()
else:
self.mainGui.novelView.updateWordCounts(tHandle)
if self._nwItem.itemClass == nwItemClass.NOVEL:
if oldCount == newCount:
self.novelItemMetaChanged.emit(tHandle)
else:
self.novelStructureChanged.emit()
# ToDo: This should be a signal
if oldHeader != newHeader:
@@ -652,17 +654,30 @@ class GuiDocEditor(QTextEdit):
self.docEditedStatusChanged.emit(self._docChanged)
return self._docChanged
def setCursorPosition(self, thePosition):
def setCursorPosition(self, position):
"""Move the cursor to a given position in the document.
"""
if not isinstance(thePosition, int):
if not isinstance(position, int):
return False
nChars = self.document().characterCount()
if nChars > 1:
theCursor = self.textCursor()
theCursor.setPosition(min(max(thePosition, 0), nChars-1))
theCursor.setPosition(minmax(position, 0, nChars-1))
self.setTextCursor(theCursor)
# By default, the editor scrolls so the cursor is on the
# last line, so we must correct it. The user setting for
# auto-scroll is used to determine the scroll distance. This
# makes it compatible with the typewriter scrolling feature
# when it is enabled. By default, it's 30% of viewport.
vPos = self.verticalScrollBar().value()
cPos = self.cursorRect().topLeft().y()
mPos = int(self.mainConf.autoScrollPos*0.01 * self.viewport().height())
if cPos > mPos:
# Only scroll if the cursor is past the auto-scroll limit
self.verticalScrollBar().setValue(max(0, vPos + cPos - mPos))
self.docFooter.updateLineCount()
return True
@@ -675,18 +690,18 @@ class GuiDocEditor(QTextEdit):
self._nwItem.setCursorPos(cursPos)
return
def setCursorLine(self, theLine):
def setCursorLine(self, lineNo):
"""Move the cursor to a given line in the document.
"""
if not isinstance(theLine, int):
if not isinstance(lineNo, int):
return False
if theLine >= 0:
theBlock = self.document().findBlockByLineNumber(theLine)
lineIdx = lineNo - 1 # Block index is 0 offset, lineNo is 1 offset
if lineIdx >= 0:
theBlock = self.document().findBlockByLineNumber(lineIdx)
if theBlock:
self.setCursorPosition(theBlock.position())
self.docFooter.updateLineCount()
logger.debug("Cursor moved to line %d", theLine)
logger.debug("Cursor moved to line %d", lineNo)
return True
@@ -2065,21 +2080,6 @@ class GuiDocEditor(QTextEdit):
return False
return True
def _updateHeaders(self):
"""Update the headers record and return True if anything
changed, if a check flag was provided.
"""
if self._docHandle is None:
return False
newHeaders = self.theProject.index.getHandleHeaders(self._docHandle)
newLev = [x[1] for x in newHeaders]
oldLev = [x[1] for x in self._docHeaders]
self._docHeaders = newHeaders
return newLev != oldLev
def _checkDocSize(self, theSize):
"""Check if document size crosses the big document limit set in
config. If so, we will set the big document flag to True.
+6 -33
View File
@@ -323,43 +323,10 @@ class GuiDocViewer(QTextBrowser):
return
##
# Properties
##
def docHandle(self):
"""Return the handle of the currently open document. Returns
None if no document is open.
"""
return self._docHandle
##
# Setters
##
def setCursorPosition(self, thePosition):
"""Move the cursor to a given position in the document.
"""
if not isinstance(thePosition, int):
return False
if thePosition >= 0:
theCursor = self.textCursor()
theCursor.setPosition(thePosition)
self.setTextCursor(theCursor)
return True
def setCursorLine(self, theLine):
"""Move the cursor to a given line in the document.
"""
if not isinstance(theLine, int):
return False
if theLine >= 0:
theBlock = self.document().findBlockByLineNumber(theLine)
if theBlock:
self.setCursorPosition(theBlock.position())
logger.debug("Cursor moved to line %d", theLine)
return True
def setScrollPosition(self, thePos):
"""Set the scrollbar position.
"""
@@ -372,6 +339,12 @@ class GuiDocViewer(QTextBrowser):
# Getters
##
def docHandle(self):
"""Return the handle of the currently open document. Returns
None if no document is open.
"""
return self._docHandle
def getScrollPosition(self):
"""Get the scrollbar position. Returns 0 if no scrollbar.
"""
+93 -48
View File
@@ -40,7 +40,6 @@ from PyQt5.QtWidgets import (
)
from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
from novelwriter.common import checkInt
from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
logger = logging.getLogger(__name__)
@@ -60,7 +59,7 @@ class GuiNovelView(QWidget):
# Signals for user interaction with the novel tree
selectedItemChanged = pyqtSignal(str)
openDocumentRequest = pyqtSignal(str, Enum, int, str)
openDocumentRequest = pyqtSignal(str, Enum, str, bool)
def __init__(self, mainGui):
super().__init__(parent=mainGui)
@@ -83,7 +82,6 @@ class GuiNovelView(QWidget):
self.setLayout(self.outerBox)
# Function Mappings
self.updateWordCounts = self.novelTree.updateWordCounts
self.getSelectedHandle = self.novelTree.getSelectedHandle
self.setActiveHandle = self.novelTree.setActiveHandle
@@ -107,12 +105,6 @@ class GuiNovelView(QWidget):
self.novelTree.initSettings()
return
def refreshTree(self):
"""Refresh the current tree.
"""
self.novelTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("novelTree"))
return
def clearProject(self):
"""Clear project-related GUI content.
"""
@@ -164,6 +156,13 @@ class GuiNovelView(QWidget):
# Public Slots
##
@pyqtSlot()
def refreshTree(self):
"""Refresh the current tree.
"""
self.novelTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("novelTree"))
return
@pyqtSlot(str)
def updateRootItem(self, tHandle):
"""If any root item changes, rebuild the novel root menu.
@@ -171,6 +170,14 @@ class GuiNovelView(QWidget):
self.novelBar.buildNovelRootMenu()
return
@pyqtSlot(str)
def updateNovelItemMeta(self, tHandle):
"""The meta data of a novel item has changed, and the tree item
needs to be refreshed.
"""
self.novelTree.refreshHandle(tHandle)
return
# END Class GuiNovelView
@@ -470,7 +477,7 @@ class GuiNovelTree(QTreeWidget):
return
def refreshTree(self, rootHandle=None, overRide=False):
"""Called whenever the Novel tab is activated.
"""Refresh the tree if it has been changed.
"""
logger.debug("Requesting refresh of the novel tree")
if rootHandle is None:
@@ -495,13 +502,24 @@ class GuiNovelTree(QTreeWidget):
return
def updateWordCounts(self, tHandle):
"""Update the word count for a given handle.
def refreshHandle(self, tHandle):
"""Refresh the data for a given handle.
"""
tHeaders = self.theProject.index.getHandleWordCounts(tHandle)
for titleKey, wCount in tHeaders:
if titleKey in self._treeMap:
self._treeMap[titleKey].setText(self.C_WORDS, f"{wCount:n}")
idxData = self.theProject.index.getItemData(tHandle)
if idxData is None:
return
logger.debug("Refreshing meta data for item '%s'", tHandle)
for sTitle, tHeading in idxData.items():
sKey = f"{tHandle}:{sTitle}"
trItem = self._treeMap.get(sKey, None)
if trItem is None:
logger.debug("Heading '%s' not in novel tree", sKey)
self.refreshTree()
return
self._updateTreeItemValues(trItem, tHeading, tHandle, sTitle)
return
def getSelectedHandle(self):
@@ -509,14 +527,11 @@ class GuiNovelTree(QTreeWidget):
selected, return the first.
"""
selItem = self.selectedItems()
tHandle = None
tLine = 0
if selItem:
tHandle = selItem[0].data(self.C_TITLE, self.D_HANDLE)
sTitle = selItem[0].data(self.C_TITLE, self.D_TITLE)
tLine = checkInt(sTitle[1:], 1) - 1
return tHandle, tLine
return tHandle, sTitle
return None, None
def setLastColType(self, colType, doRefresh=True):
"""Change the content type of the last column and rebuild.
@@ -575,11 +590,11 @@ class GuiNovelTree(QTreeWidget):
if not isinstance(selItem, QTreeWidgetItem):
return
tHandle, _ = self.getSelectedHandle()
tHandle, sTitle = self.getSelectedHandle()
if tHandle is None:
return
self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "")
self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, sTitle or "", False)
return
@@ -590,6 +605,22 @@ class GuiNovelTree(QTreeWidget):
self.clearSelection()
return
def resizeEvent(self, event):
"""Elide labels in the extra column.
"""
super().resizeEvent(event)
newW = event.size().width()
oldW = event.oldSize().width()
if newW != oldW:
eliW = int(0.25 * newW)
fMetric = self.fontMetrics()
for i in range(self.topLevelItemCount()):
trItem = self.topLevelItem(i)
if isinstance(trItem, QTreeWidgetItem):
lastText = trItem.data(self.C_EXTRA, Qt.UserRole)
trItem.setText(self.C_EXTRA, fMetric.elidedText(lastText, Qt.ElideRight, eliW))
return
##
# Private Slots
##
@@ -621,8 +652,8 @@ class GuiNovelTree(QTreeWidget):
clicked, and send it to the main gui class for opening in the
document editor.
"""
tHandle, tLine = self.getSelectedHandle()
self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, tLine, "")
tHandle, sTitle = self.getSelectedHandle()
self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, sTitle or "", True)
return
##
@@ -638,30 +669,16 @@ class GuiNovelTree(QTreeWidget):
novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True)
for tKey, tHandle, sTitle, novIdx in novStruct:
iLevel = nwHeaders.H_LEVEL.get(novIdx.level, 0)
if iLevel == 0:
if novIdx.level == "H0":
continue
hDec = self.mainTheme.getHeaderDecoration(iLevel)
newItem = QTreeWidgetItem()
newItem.setData(self.C_TITLE, Qt.DecorationRole, hDec)
newItem.setText(self.C_TITLE, novIdx.title)
newItem.setData(self.C_TITLE, self.D_HANDLE, tHandle)
newItem.setData(self.C_TITLE, self.D_TITLE, sTitle)
newItem.setData(self.C_TITLE, self.D_KEY, tKey)
newItem.setFont(self.C_TITLE, self._hFonts[iLevel])
newItem.setText(self.C_WORDS, f"{novIdx.wordCount:n}")
newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
newItem.setData(self.C_MORE, Qt.DecorationRole, self._pMore)
# Custom column
lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
newItem.setText(self.C_EXTRA, lastText)
if lastText:
newItem.setToolTip(self.C_EXTRA, toolTip)
self._updateTreeItemValues(newItem, novIdx, tHandle, sTitle)
self._treeMap[tKey] = newItem
self.addTopLevelItem(newItem)
@@ -672,24 +689,52 @@ class GuiNovelTree(QTreeWidget):
return
def _updateTreeItemValues(self, trItem, idxItem, tHandle, sTitle):
"""Set the tree item values from the index entry.
"""
iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0)
hDec = self.mainTheme.getHeaderDecoration(iLevel)
trItem.setData(self.C_TITLE, Qt.DecorationRole, hDec)
trItem.setText(self.C_TITLE, idxItem.title)
trItem.setFont(self.C_TITLE, self._hFonts[iLevel])
trItem.setText(self.C_WORDS, f"{idxItem.wordCount:n}")
trItem.setData(self.C_MORE, Qt.DecorationRole, self._pMore)
# Custom column
mW = int(0.25 * self.viewport().width())
lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
elideText = self.fontMetrics().elidedText(lastText, Qt.ElideRight, mW)
trItem.setText(self.C_EXTRA, elideText)
trItem.setData(self.C_EXTRA, Qt.UserRole, lastText)
trItem.setToolTip(self.C_EXTRA, toolTip)
return
def _getLastColumnText(self, tHandle, sTitle):
"""Generate the text for the last column based on user settings.
"""
if self._lastCol == NovelTreeColumn.HIDDEN:
return "", ""
refData = []
refName = ""
theRefs = self.theProject.index.getReferences(tHandle, sTitle)
if self._lastCol == NovelTreeColumn.POV:
newText = ", ".join(theRefs[nwKeyWords.POV_KEY])
return newText, f"{self._povLabel}: {newText}"
refData = theRefs[nwKeyWords.POV_KEY]
refName = self._povLabel
elif self._lastCol == NovelTreeColumn.FOCUS:
newText = ", ".join(theRefs[nwKeyWords.FOCUS_KEY])
return newText, f"{self._focLabel}: {newText}"
refData = theRefs[nwKeyWords.FOCUS_KEY]
refName = self._focLabel
elif self._lastCol == NovelTreeColumn.PLOT:
newText = ", ".join(theRefs[nwKeyWords.PLOT_KEY])
return newText, f"{self._pltLabel}: {newText}"
refData = theRefs[nwKeyWords.PLOT_KEY]
refName = self._pltLabel
if refData:
toolText = ", ".join(refData)
return refData[0], f"{refName}: {toolText}"
return "", ""
@@ -699,7 +744,7 @@ class GuiNovelTree(QTreeWidget):
logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle)
pIndex = self.theProject.index
novIdx = pIndex.getNovelData(tHandle, sTitle)
novIdx = pIndex.getItemHeader(tHandle, sTitle)
refTags = pIndex.getReferences(tHandle, sTitle)
synopText = novIdx.synopsis
+19 -17
View File
@@ -55,6 +55,7 @@ logger = logging.getLogger(__name__)
class GuiOutlineView(QWidget):
loadDocumentTagRequest = pyqtSignal(str, Enum)
openDocumentRequest = pyqtSignal(str, Enum, str, bool)
def __init__(self, mainGui):
super().__init__(parent=mainGui)
@@ -72,6 +73,7 @@ class GuiOutlineView(QWidget):
self.splitOutline = QSplitter(Qt.Vertical)
self.splitOutline.addWidget(self.outlineTree)
self.splitOutline.addWidget(self.outlineData)
self.splitOutline.setOpaqueResize(False)
self.splitOutline.setSizes(self.mainConf.outlinePanePos)
# Assemble
@@ -375,15 +377,16 @@ class GuiOutlineTree(QTreeWidget):
hiddenStateChanged = pyqtSignal()
activeItemChanged = pyqtSignal(str, str)
def __init__(self, theOutline):
super().__init__(parent=theOutline)
def __init__(self, outlineView):
super().__init__(parent=outlineView)
logger.debug("Initialising GuiOutlineTree ...")
self.mainConf = novelwriter.CONFIG
self.mainGui = theOutline.mainGui
self.theProject = theOutline.mainGui.theProject
self.mainTheme = theOutline.mainGui.mainTheme
self.mainConf = novelwriter.CONFIG
self.outlineView = outlineView
self.mainGui = outlineView.mainGui
self.theProject = outlineView.mainGui.theProject
self.mainTheme = outlineView.mainGui.mainTheme
self.setUniformRowHeights(True)
self.setFrameStyle(QFrame.NoFrame)
@@ -524,13 +527,11 @@ class GuiOutlineTree(QTreeWidget):
selected, return the first.
"""
selItem = self.selectedItems()
tHandle = None
tLine = 0
if selItem:
tHandle = selItem[0].data(self._colIdx[nwOutline.TITLE], self.D_HANDLE)
tLine = checkInt(selItem[0].text(self._colIdx[nwOutline.LINE]), 1) - 1
return tHandle, tLine
sTitle = selItem[0].data(self._colIdx[nwOutline.TITLE], self.D_TITLE)
return tHandle, sTitle
return None, None
##
# Slots
@@ -542,8 +543,10 @@ class GuiOutlineTree(QTreeWidget):
clicked, and send it to the main gui class for opening in the
document editor.
"""
tHandle, tLine = self.getSelectedHandle()
self.mainGui.openDocument(tHandle, tLine=tLine - 1, doScroll=True)
tHandle, sTitle = self.getSelectedHandle()
if tHandle is None:
return
self.outlineView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, sTitle or "", True)
return
@pyqtSlot()
@@ -554,9 +557,8 @@ class GuiOutlineTree(QTreeWidget):
selItems = self.selectedItems()
if selItems:
tHandle = selItems[0].data(self._colIdx[nwOutline.TITLE], self.D_HANDLE)
sTitle = selItems[0].data(self._colIdx[nwOutline.TITLE], self.D_TITLE)
sTitle = selItems[0].data(self._colIdx[nwOutline.TITLE], self.D_TITLE)
self.activeItemChanged.emit(tHandle, sTitle)
return
@pyqtSlot(int, int, int)
@@ -718,7 +720,7 @@ class GuiOutlineTree(QTreeWidget):
trItem.setText(self._colIdx[nwOutline.LEVEL], novIdx.level)
trItem.setIcon(self._colIdx[nwOutline.LABEL], self._dIcon[nwItem.mainHeading])
trItem.setText(self._colIdx[nwOutline.LABEL], nwItem.itemName)
trItem.setText(self._colIdx[nwOutline.LINE], sTitle[1:].lstrip("0"))
trItem.setText(self._colIdx[nwOutline.LINE], f"{novIdx.line:n}")
trItem.setText(self._colIdx[nwOutline.SYNOP], novIdx.synopsis)
trItem.setText(self._colIdx[nwOutline.CCOUNT], f"{novIdx.charCount:n}")
trItem.setText(self._colIdx[nwOutline.WCOUNT], f"{novIdx.wordCount:n}")
@@ -1048,7 +1050,7 @@ class GuiOutlineDetails(QScrollArea):
"""
pIndex = self.theProject.index
nwItem = self.theProject.tree[tHandle]
novIdx = pIndex.getNovelData(tHandle, sTitle)
novIdx = pIndex.getItemHeader(tHandle, sTitle)
theRefs = pIndex.getReferences(tHandle, sTitle)
if nwItem is None or novIdx is None:
return False
+5 -5
View File
@@ -60,7 +60,7 @@ class GuiProjectView(QWidget):
# Signals for user interaction with the project tree
selectedItemChanged = pyqtSignal(str)
openDocumentRequest = pyqtSignal(str, Enum, int, str)
openDocumentRequest = pyqtSignal(str, Enum, str, bool)
# Requests for the main GUI
projectSettingsRequest = pyqtSignal(int)
@@ -1144,7 +1144,7 @@ class GuiProjectTree(QTreeWidget):
return
if tItem.isFileType():
self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, -1, "")
self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, "", True)
else:
trItem.setExpanded(not trItem.isExpanded())
@@ -1190,11 +1190,11 @@ class GuiProjectTree(QTreeWidget):
if isFile:
aOpenDoc = ctxMenu.addAction(self.tr("Open Document"))
aOpenDoc.triggered.connect(
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, -1, "")
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, "", True)
)
aViewDoc = ctxMenu.addAction(self.tr("View Document"))
aViewDoc.triggered.connect(
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "")
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, "", False)
)
ctxMenu.addSeparator()
@@ -1324,7 +1324,7 @@ class GuiProjectTree(QTreeWidget):
return
if tItem.isFileType():
self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "")
self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, "", False)
return
@@ -1,7 +1,7 @@
"""
novelWriter GUI Main Window Views ToolBar
novelWriter GUI Main Window SideBar
===========================================
GUI class for the main window "Views" toolbar
GUI class for the main window side bar
File History:
Created: 2022-05-10 [1.7b1]
@@ -36,14 +36,14 @@ from novelwriter.enum import nwView
logger = logging.getLogger(__name__)
class GuiViewsBar(QToolBar):
class GuiSideBar(QToolBar):
viewChangeRequested = pyqtSignal(nwView)
def __init__(self, mainGui):
super().__init__(parent=mainGui)
logger.debug("Initialising GuiViewsBar ...")
logger.debug("Initialising GuiSideBar ...")
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
@@ -123,7 +123,7 @@ class GuiViewsBar(QToolBar):
self.updateTheme()
logger.debug("GuiViewsBar initialisation complete")
logger.debug("GuiSideBar initialisation complete")
return
@@ -142,4 +142,4 @@ class GuiViewsBar(QToolBar):
return
# END Class GuiViewsBar
# END Class GuiSideBar
+14 -11
View File
@@ -34,7 +34,6 @@ from PyQt5.QtGui import QColor, QPainter
from PyQt5.QtWidgets import qApp, QStatusBar, QLabel, QAbstractButton
from novelwriter.common import formatTime
from novelwriter.enum import nwState
logger = logging.getLogger(__name__)
@@ -53,8 +52,8 @@ class GuiMainStatus(QStatusBar):
self.userIdle = False
colNone = QColor(*self.mainTheme.statNone)
colTrue = QColor(*self.mainTheme.statUnsaved)
colFalse = QColor(*self.mainTheme.statSaved)
colSaved = QColor(*self.mainTheme.statSaved)
colUnsaved = QColor(*self.mainTheme.statUnsaved)
iPx = self.mainTheme.baseIconSize
@@ -72,7 +71,7 @@ class GuiMainStatus(QStatusBar):
self.addPermanentWidget(self.langText)
# The Editor Status
self.docIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self)
self.docIcon = StatusLED(colNone, colSaved, colUnsaved, iPx, iPx, self)
self.docText = QLabel(self.tr("Editor"))
self.docIcon.setContentsMargins(0, 0, 0, 0)
self.docText.setContentsMargins(0, 0, xM, 0)
@@ -80,7 +79,7 @@ class GuiMainStatus(QStatusBar):
self.addPermanentWidget(self.docText)
# The Project Status
self.projIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self)
self.projIcon = StatusLED(colNone, colSaved, colUnsaved, iPx, iPx, self)
self.projText = QLabel(self.tr("Project"))
self.projIcon.setContentsMargins(0, 0, 0, 0)
self.projText.setContentsMargins(0, 0, xM, 0)
@@ -122,8 +121,8 @@ class GuiMainStatus(QStatusBar):
self.setRefTime(None)
self.setLanguage(None, "")
self.setProjectStats(0, 0)
self.setProjectStatus(nwState.NONE)
self.setDocumentStatus(nwState.NONE)
self.setProjectStatus(StatusLED.S_NONE)
self.setDocumentStatus(StatusLED.S_NONE)
self.updateTime()
return True
@@ -236,14 +235,14 @@ class GuiMainStatus(QStatusBar):
def doUpdateProjectStatus(self, isChanged):
"""Slot for updating the project status.
"""
self.setProjectStatus(nwState.GOOD if isChanged else nwState.BAD)
self.setProjectStatus(StatusLED.S_BAD if isChanged else StatusLED.S_GOOD)
return
@pyqtSlot(bool)
def doUpdateDocumentStatus(self, isChanged):
"""Slot for updating the document status.
"""
self.setDocumentStatus(nwState.GOOD if isChanged else nwState.BAD)
self.setDocumentStatus(StatusLED.S_BAD if isChanged else StatusLED.S_GOOD)
return
# END Class GuiMainStatus
@@ -251,6 +250,10 @@ class GuiMainStatus(QStatusBar):
class StatusLED(QAbstractButton):
S_NONE = 0
S_BAD = 1
S_GOOD = 2
def __init__(self, colNone, colGood, colBad, sW, sH, parent=None):
super().__init__(parent=parent)
@@ -271,9 +274,9 @@ class StatusLED(QAbstractButton):
def setState(self, theState):
"""Set the colour state.
"""
if theState == nwState.GOOD:
if theState == self.S_GOOD:
self._theCol = self._colGood
elif theState == nwState.BAD:
elif theState == self.S_BAD:
self._theCol = self._colBad
else:
self._theCol = self._colNone
+48 -34
View File
@@ -41,7 +41,7 @@ from PyQt5.QtWidgets import (
from novelwriter.gui import (
GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu,
GuiMainStatus, GuiNovelView, GuiOutlineView, GuiProjectView, GuiTheme,
GuiViewsBar
GuiSideBar
)
from novelwriter.dialogs import (
GuiAbout, GuiPreferences, GuiProjectDetails, GuiProjectLoad,
@@ -118,7 +118,7 @@ class GuiMain(QMainWindow):
self.itemDetails = GuiItemDetails(self)
self.outlineView = GuiOutlineView(self)
self.mainMenu = GuiMainMenu(self)
self.viewsBar = GuiViewsBar(self)
self.viewsBar = GuiSideBar(self)
# Project Tree Stack
self.projStack = QStackedWidget()
@@ -140,12 +140,14 @@ class GuiMain(QMainWindow):
self.splitView.addWidget(self.docViewer)
self.splitView.addWidget(self.viewMeta)
self.splitView.setHandleWidth(hWd)
self.splitView.setOpaqueResize(False)
self.splitView.setSizes(self.mainConf.viewPanePos)
# Splitter : Document Editor / Document Viewer
self.splitDocs = QSplitter(Qt.Horizontal)
self.splitDocs.addWidget(self.docEditor)
self.splitDocs.addWidget(self.splitView)
self.splitDocs.setOpaqueResize(False)
self.splitDocs.setHandleWidth(hWd)
# Splitter : Project Tree / Main Tabs
@@ -153,6 +155,7 @@ class GuiMain(QMainWindow):
self.splitMain.setContentsMargins(0, 0, 0, 0)
self.splitMain.addWidget(self.treePane)
self.splitMain.addWidget(self.splitDocs)
self.splitMain.setOpaqueResize(False)
self.splitMain.setHandleWidth(hWd)
self.splitMain.setSizes(self.mainConf.mainPanePos)
@@ -224,10 +227,13 @@ class GuiMain(QMainWindow):
self.docEditor.docCountsChanged.connect(self.itemDetails.updateCounts)
self.docEditor.docCountsChanged.connect(self.projView.updateCounts)
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
self.docEditor.novelStructureChanged.connect(self.novelView.refreshTree)
self.docEditor.novelItemMetaChanged.connect(self.novelView.updateNovelItemMeta)
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
self.outlineView.loadDocumentTagRequest.connect(self._followTag)
self.outlineView.openDocumentRequest.connect(self._openDocument)
# Finalise Initialisation
# =======================
@@ -306,7 +312,7 @@ class GuiMain(QMainWindow):
# Work Area
self.docEditor.clearEditor()
self.docEditor.setDictionaries()
self.closeDocViewer()
self.closeDocViewer(byUser=False)
self.outlineView.clearProject()
# General
@@ -593,14 +599,21 @@ class GuiMain(QMainWindow):
logger.debug("Requested item '%s' is not a document", tHandle)
return False
cHandle = self.docEditor.docHandle()
if cHandle == tHandle:
self.docEditor.setCursorLine(tLine)
if changeFocus:
self.docEditor.setFocus()
return True
self.closeDocument(beforeOpen=True)
self._changeView(nwView.EDITOR)
if self.docEditor.loadText(tHandle, tLine):
if changeFocus:
self.docEditor.setFocus()
self.theProject.data.setLastHandle(tHandle, "editor")
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
self.novelView.setActiveHandle(tHandle)
if changeFocus:
self.docEditor.setFocus()
else:
return False
@@ -618,7 +631,7 @@ class GuiMain(QMainWindow):
fHandle = None # The first file handle we encounter
foundIt = False # We've found tHandle, pick the next we see
for tItem in self.theProject.tree:
if tItem is None or not tItem.isFileType():
if not tItem.isFileType():
continue
if fHandle is None:
fHandle = tItem.itemHandle
@@ -648,7 +661,7 @@ class GuiMain(QMainWindow):
return True
def viewDocument(self, tHandle=None, tAnchor=None):
def viewDocument(self, tHandle=None, sTitle=None):
"""Load a document for viewing in the view panel.
"""
if not self.hasProject:
@@ -687,7 +700,8 @@ class GuiMain(QMainWindow):
self.splitDocs.setSizes(vPos)
self.viewMeta.setVisible(self.mainConf.showRefPanel)
self.docViewer.navigateTo(tAnchor)
if sTitle:
self.docViewer.navigateTo(f"#{sTitle}")
return True
@@ -775,17 +789,23 @@ class GuiMain(QMainWindow):
return False
tHandle = None
sTitle = None
tLine = None
if self.projView.treeHasFocus():
tHandle = self.projView.getSelectedHandle()
elif self.novelView.treeHasFocus():
tHandle, tLine = self.novelView.getSelectedHandle()
tHandle, sTitle = self.novelView.getSelectedHandle()
elif self.outlineView.treeHasFocus():
tHandle, tLine = self.outlineView.getSelectedHandle()
tHandle, sTitle = self.outlineView.getSelectedHandle()
else:
logger.warning("No item selected")
return False
if tHandle is not None and sTitle is not None:
hItem = self.theProject.index.getItemHeader(tHandle, sTitle)
if hItem is not None:
tLine = hItem.line
if tHandle is not None:
self.openDocument(tHandle, tLine=tLine, changeFocus=False, doScroll=False)
@@ -808,17 +828,8 @@ class GuiMain(QMainWindow):
"""Rebuild the project tree.
"""
self.projView.populateTree()
# self.novelView.refreshTree()
return
def requestNovelTreeRefresh(self):
"""Update the novel tree, but only if it is visible.
"""
if self.projStack.currentIndex() == self.idxNovelView and self.hasProject:
self.novelView.refreshTree()
return True
return False
def rebuildIndex(self, beQuiet=False):
"""Rebuild the entire index.
"""
@@ -833,6 +844,7 @@ class GuiMain(QMainWindow):
self.projView.saveProjectTasks()
self.theProject.index.rebuildIndex()
self.projView.populateTree()
self.novelView.refreshTree()
tEnd = time()
self.setStatus(
@@ -1208,14 +1220,19 @@ class GuiMain(QMainWindow):
self.theProject.data.setLastHandle(None, "editor")
return
def closeDocViewer(self):
def closeDocViewer(self, byUser=True):
"""Close the document view panel.
"""
self.docViewer.clearViewer()
self.theProject.data.setLastHandle(None, "viewer")
if byUser:
# Only reset the last handle if the user called this
self.theProject.data.setLastHandle(None, "viewer")
# Hide the panel
bPos = self.splitMain.sizes()
self.splitView.setVisible(False)
self.splitDocs.setSizes([bPos[1], 0])
return not self.splitView.isVisible()
def toggleFocusMode(self):
@@ -1471,18 +1488,22 @@ class GuiMain(QMainWindow):
if tMode == nwDocMode.EDIT:
self.openDocument(tHandle)
elif tMode == nwDocMode.VIEW:
self.viewDocument(tHandle=tHandle, tAnchor=f"#{sTitle}")
self.viewDocument(tHandle=tHandle, sTitle=sTitle)
return
@pyqtSlot(str, Enum, int, str)
def _openDocument(self, tHandle, tMode, tLine, tAnchor):
@pyqtSlot(str, Enum, str, bool)
def _openDocument(self, tHandle, tMode, sTitle, setFocus):
"""Handle an open document request from one of the tree views.
"""
if tHandle is not None:
if tMode == nwDocMode.EDIT:
self.openDocument(tHandle, tLine=tLine, changeFocus=False)
tLine = None
hItem = self.theProject.index.getItemHeader(tHandle, sTitle)
if hItem is not None:
tLine = hItem.line
self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus)
elif tMode == nwDocMode.VIEW:
self.viewDocument(tHandle=tHandle, tAnchor=(tAnchor or None))
self.viewDocument(tHandle=tHandle, sTitle=sTitle)
return
@pyqtSlot(nwView)
@@ -1564,7 +1585,6 @@ class GuiMain(QMainWindow):
self.docEditor.closeSearch()
elif self.isFocusMode:
self.toggleFocusMode()
return
@pyqtSlot(int)
@@ -1581,17 +1601,11 @@ class GuiMain(QMainWindow):
"""Activated when the project view tab is changed.
"""
sHandle = None
if stIndex == self.idxProjView:
sHandle = self.projView.getSelectedHandle()
elif stIndex == self.idxNovelView:
if self.hasProject:
self.novelView.refreshTree()
sHandle, _ = self.novelView.getSelectedHandle()
sHandle, _ = self.novelView.getSelectedHandle()
self.itemDetails.updateViewBox(sHandle)
return
# END Class GuiMain