diff --git a/novelwriter/gui/__init__.py b/novelwriter/gui/__init__.py index 8364e342..e3560a99 100644 --- a/novelwriter/gui/__init__.py +++ b/novelwriter/gui/__init__.py @@ -25,7 +25,6 @@ from novelwriter.gui.itemdetails import GuiItemDetails from novelwriter.gui.mainmenu import GuiMainMenu from novelwriter.gui.noveltree import GuiNovelTree from novelwriter.gui.outline import GuiOutline -from novelwriter.gui.outlinedetails import GuiOutlineDetails from novelwriter.gui.projtree import GuiProjectTree from novelwriter.gui.statusbar import GuiMainStatus from novelwriter.gui.theme import GuiTheme @@ -40,7 +39,6 @@ __all__ = [ "GuiMainStatus", "GuiNovelTree", "GuiOutline", - "GuiOutlineDetails", "GuiProjectTree", "GuiTheme", "GuiViewsBar", diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py index 1ac83ff1..3c31133a 100644 --- a/novelwriter/gui/outline.py +++ b/novelwriter/gui/outline.py @@ -4,7 +4,9 @@ novelWriter – GUI Project Outline GUI class for the project outline view File History: -Created: 2019-11-16 [0.4.1] +Created: 2019-11-16 [0.4.1] GuiOutlineView, GuiOutlineHeaderMenu +Created: 2020-06-02 [0.7.0] GuiOutlineDetails +Created: 2022-05-15 [1.7b1] GuiOutline This file is a part of novelWriter Copyright 2018–2022, Veronica Berglyd Olsen @@ -28,19 +30,79 @@ import novelwriter from time import time -from PyQt5.QtCore import Qt, QSize, pyqtSlot +from PyQt5.QtCore import ( + Qt, pyqtSignal, pyqtSlot, QSize, QT_TRANSLATE_NOOP +) from PyQt5.QtWidgets import ( - QTreeWidget, QTreeWidgetItem, QMenu, QAction, QAbstractItemView + QAbstractItemView, QAction, QGridLayout, QGroupBox, QHBoxLayout, QLabel, + QMenu, QScrollArea, QSplitter, QTreeWidget, QTreeWidgetItem, QVBoxLayout, + QWidget ) -from novelwriter.enum import nwItemLayout, nwItemType, nwOutline +from novelwriter.enum import nwItemLayout, nwItemType, nwOutline, nwView from novelwriter.common import checkInt from novelwriter.constants import trConst, nwKeyWords, nwLabels + logger = logging.getLogger(__name__) -class GuiOutline(QTreeWidget): +class GuiOutline(QWidget): + + viewChangeRequested = pyqtSignal(nwView) + + def __init__(self, theParent): + QWidget.__init__(self, theParent) + + self.mainConf = novelwriter.CONFIG + self.theParent = theParent + self.theProject = theParent.theProject + + self.outlineView = GuiOutlineView(self) + self.outlineData = GuiOutlineDetails(self) + + self.splitOutline = QSplitter(Qt.Vertical) + self.splitOutline.addWidget(self.outlineView) + self.splitOutline.addWidget(self.outlineData) + self.splitOutline.setSizes(self.mainConf.getOutlinePanePos()) + + # Assemble + self.outerBox = QVBoxLayout() + self.outerBox.setContentsMargins(0, 0, 0, 0) + self.outerBox.addWidget(self.splitOutline) + + self.setLayout(self.outerBox) + + return + + ## + # Methods + ## + + def splitSizes(self): + return self.splitOutline.sizes() + + def clearOutline(self): + self.outlineData.clearDetails() + return + + def initOutline(self): + self.outlineView.initOutline() + self.outlineData.initDetails() + return + + def closeOutline(self): + self.outlineView.closeOutline() + return + + def refreshView(self, overRide=False, novelChanged=False): + self.outlineView.refreshTree(overRide=overRide, novelChanged=novelChanged) + return + +# END Class GuiOutline + + +class GuiOutlineView(QTreeWidget): DEF_WIDTH = { nwOutline.TITLE: 200, @@ -82,17 +144,18 @@ class GuiOutline(QTreeWidget): nwOutline.SYNOP: False, } - def __init__(self, theParent): - QTreeWidget.__init__(self, theParent) + def __init__(self, theOutline): + QTreeWidget.__init__(self, theOutline) logger.debug("Initialising GuiOutline ...") self.mainConf = novelwriter.CONFIG - self.theParent = theParent - self.theProject = theParent.theProject - self.theTheme = theParent.theTheme - self.theIndex = theParent.theIndex - self.optState = theParent.theProject.optState + self.theOutline = theOutline + self.theParent = theOutline.theParent + self.theProject = theOutline.theParent.theProject + self.theTheme = theOutline.theParent.theTheme + self.theIndex = theOutline.theParent.theIndex + self.optState = theOutline.theParent.theProject.optState self.headerMenu = GuiOutlineHeaderMenu(self) self.setSelectionBehavior(QAbstractItemView.SelectRows) @@ -233,7 +296,7 @@ class GuiOutline(QTreeWidget): if selItems: tHandle = selItems[0].data(self._colIdx[nwOutline.TITLE], Qt.UserRole) sTitle = selItems[0].data(self._colIdx[nwOutline.LINE], Qt.UserRole) - self.theParent.projMeta.showItem(tHandle, sTitle) + self.theOutline.outlineData.showItem(tHandle, sTitle) self.theParent.treeView.setSelectedHandle(tHandle) return @@ -477,7 +540,7 @@ class GuiOutline(QTreeWidget): return newItem -# END Class GuiOutline +# END Class GuiOutlineView class GuiOutlineHeaderMenu(QMenu): @@ -533,3 +596,319 @@ class GuiOutlineHeaderMenu(QMenu): return # END Class GuiOutlineHeaderMenu + + +class GuiOutlineDetails(QScrollArea): + + LVL_MAP = { + "H1": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"), + "H2": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Chapter"), + "H3": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Scene"), + "H4": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Section"), + } + + def __init__(self, theOutline): + QScrollArea.__init__(self, theOutline) + + logger.debug("Initialising GuiOutlineDetails ...") + + self.mainConf = novelwriter.CONFIG + self.theOutline = theOutline + self.theParent = theOutline.theParent + self.theProject = theOutline.theParent.theProject + self.theTheme = theOutline.theParent.theTheme + self.theIndex = theOutline.theParent.theIndex + self.optState = theOutline.theParent.theProject.optState + + # Sizes + minTitle = 30*self.theTheme.textNWidth + maxTitle = 40*self.theTheme.textNWidth + wCount = self.theTheme.getTextWidth("999,999") + hSpace = int(self.mainConf.pxInt(10)) + vSpace = int(self.mainConf.pxInt(4)) + + # Details Area + self.titleLabel = QLabel("%s" % self.tr("Title")) + self.fileLabel = QLabel("%s" % self.tr("Document")) + self.itemLabel = QLabel("%s" % self.tr("Status")) + self.titleValue = QLabel("") + self.fileValue = QLabel("") + self.itemValue = QLabel("") + + self.titleValue.setMinimumWidth(minTitle) + self.titleValue.setMaximumWidth(maxTitle) + self.fileValue.setMinimumWidth(minTitle) + self.fileValue.setMaximumWidth(maxTitle) + self.itemValue.setMinimumWidth(minTitle) + self.itemValue.setMaximumWidth(maxTitle) + + # Stats Area + self.cCLabel = QLabel("%s" % self.tr("Characters")) + self.wCLabel = QLabel("%s" % self.tr("Words")) + self.pCLabel = QLabel("%s" % self.tr("Paragraphs")) + self.cCValue = QLabel("") + self.wCValue = QLabel("") + self.pCValue = QLabel("") + + self.cCValue.setMinimumWidth(wCount) + self.wCValue.setMinimumWidth(wCount) + self.pCValue.setMinimumWidth(wCount) + self.cCValue.setAlignment(Qt.AlignRight) + self.wCValue.setAlignment(Qt.AlignRight) + self.pCValue.setAlignment(Qt.AlignRight) + + # Synopsis + self.synopLabel = QLabel("%s" % self.tr("Synopsis")) + self.synopValue = QLabel("") + self.synopLWrap = QHBoxLayout() + self.synopValue.setWordWrap(True) + self.synopValue.setAlignment(Qt.AlignTop | Qt.AlignLeft) + self.synopLWrap.addWidget(self.synopValue, 1) + + # Tags + self.povKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])) + self.focKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])) + self.chrKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY])) + self.pltKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])) + self.timKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.TIME_KEY])) + self.wldKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY])) + self.objKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY])) + self.entKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY])) + self.cstKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY])) + + self.povKeyLWrap = QHBoxLayout() + self.focKeyLWrap = QHBoxLayout() + self.chrKeyLWrap = QHBoxLayout() + self.pltKeyLWrap = QHBoxLayout() + self.timKeyLWrap = QHBoxLayout() + self.wldKeyLWrap = QHBoxLayout() + self.objKeyLWrap = QHBoxLayout() + self.entKeyLWrap = QHBoxLayout() + self.cstKeyLWrap = QHBoxLayout() + + self.povKeyValue = QLabel("") + self.focKeyValue = QLabel("") + self.chrKeyValue = QLabel("") + self.pltKeyValue = QLabel("") + self.timKeyValue = QLabel("") + self.wldKeyValue = QLabel("") + self.objKeyValue = QLabel("") + self.entKeyValue = QLabel("") + self.cstKeyValue = QLabel("") + + self.povKeyValue.setWordWrap(True) + self.focKeyValue.setWordWrap(True) + self.chrKeyValue.setWordWrap(True) + self.pltKeyValue.setWordWrap(True) + self.timKeyValue.setWordWrap(True) + self.wldKeyValue.setWordWrap(True) + self.objKeyValue.setWordWrap(True) + self.entKeyValue.setWordWrap(True) + self.cstKeyValue.setWordWrap(True) + + self.povKeyValue.linkActivated.connect(self._tagClicked) + self.focKeyValue.linkActivated.connect(self._tagClicked) + self.chrKeyValue.linkActivated.connect(self._tagClicked) + self.pltKeyValue.linkActivated.connect(self._tagClicked) + self.timKeyValue.linkActivated.connect(self._tagClicked) + self.wldKeyValue.linkActivated.connect(self._tagClicked) + self.objKeyValue.linkActivated.connect(self._tagClicked) + self.entKeyValue.linkActivated.connect(self._tagClicked) + self.cstKeyValue.linkActivated.connect(self._tagClicked) + + self.povKeyLWrap.addWidget(self.povKeyValue, 1) + self.focKeyLWrap.addWidget(self.focKeyValue, 1) + self.chrKeyLWrap.addWidget(self.chrKeyValue, 1) + self.pltKeyLWrap.addWidget(self.pltKeyValue, 1) + self.timKeyLWrap.addWidget(self.timKeyValue, 1) + self.wldKeyLWrap.addWidget(self.wldKeyValue, 1) + self.objKeyLWrap.addWidget(self.objKeyValue, 1) + self.entKeyLWrap.addWidget(self.entKeyValue, 1) + self.cstKeyLWrap.addWidget(self.cstKeyValue, 1) + + # Selected Item Details + self.mainGroup = QGroupBox(self.tr("Title Details"), self) + self.mainForm = QGridLayout() + self.mainGroup.setLayout(self.mainForm) + + self.mainForm.addWidget(self.titleLabel, 0, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.mainForm.addWidget(self.titleValue, 0, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.mainForm.addWidget(self.cCLabel, 0, 2, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.mainForm.addWidget(self.cCValue, 0, 3, 1, 1, Qt.AlignTop | Qt.AlignRight) + self.mainForm.addWidget(self.fileLabel, 1, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.mainForm.addWidget(self.fileValue, 1, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.mainForm.addWidget(self.wCLabel, 1, 2, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.mainForm.addWidget(self.wCValue, 1, 3, 1, 1, Qt.AlignTop | Qt.AlignRight) + self.mainForm.addWidget(self.itemLabel, 2, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.mainForm.addWidget(self.itemValue, 2, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.mainForm.addWidget(self.pCLabel, 2, 2, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.mainForm.addWidget(self.pCValue, 2, 3, 1, 1, Qt.AlignTop | Qt.AlignRight) + self.mainForm.addWidget(self.synopLabel, 3, 0, 1, 4, Qt.AlignTop | Qt.AlignLeft) + self.mainForm.addLayout(self.synopLWrap, 4, 0, 1, 4, Qt.AlignTop | Qt.AlignLeft) + + self.mainForm.setColumnStretch(1, 1) + self.mainForm.setRowStretch(4, 1) + self.mainForm.setHorizontalSpacing(hSpace) + self.mainForm.setVerticalSpacing(vSpace) + + # Selected Item Tags + self.tagsGroup = QGroupBox(self.tr("Reference Tags"), self) + self.tagsForm = QGridLayout() + self.tagsGroup.setLayout(self.tagsForm) + + self.tagsForm.addWidget(self.povKeyLabel, 0, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addLayout(self.povKeyLWrap, 0, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addWidget(self.focKeyLabel, 1, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addLayout(self.focKeyLWrap, 1, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addWidget(self.chrKeyLabel, 2, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addLayout(self.chrKeyLWrap, 2, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addWidget(self.pltKeyLabel, 3, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addLayout(self.pltKeyLWrap, 3, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addWidget(self.timKeyLabel, 4, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addLayout(self.timKeyLWrap, 4, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addWidget(self.wldKeyLabel, 5, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addLayout(self.wldKeyLWrap, 5, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addWidget(self.objKeyLabel, 6, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addLayout(self.objKeyLWrap, 6, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addWidget(self.entKeyLabel, 7, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addLayout(self.entKeyLWrap, 7, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addWidget(self.cstKeyLabel, 8, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) + self.tagsForm.addLayout(self.cstKeyLWrap, 8, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) + + self.tagsForm.setColumnStretch(1, 1) + self.tagsForm.setRowStretch(8, 1) + self.tagsForm.setHorizontalSpacing(hSpace) + self.tagsForm.setVerticalSpacing(vSpace) + + # Assemble + self.outerWidget = QWidget() + self.outerBox = QHBoxLayout() + self.outerBox.addWidget(self.mainGroup, 0) + self.outerBox.addWidget(self.tagsGroup, 1) + + self.outerWidget.setLayout(self.outerBox) + self.setWidget(self.outerWidget) + + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + self.setWidgetResizable(True) + + self.initDetails() + + logger.debug("GuiOutlineDetails initialisation complete") + + return + + def initDetails(self): + """Set or update outline settings. + """ + # Scroll bars + if self.mainConf.hideVScroll: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + + if self.mainConf.hideHScroll: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) + + return + + def clearDetails(self): + """Clear all the data labels. + """ + self.titleLabel.setText("%s" % self.tr("Title")) + self.titleValue.setText("") + self.fileValue.setText("") + self.itemValue.setText("") + self.cCValue.setText("") + self.wCValue.setText("") + self.pCValue.setText("") + self.synopValue.setText("") + self.povKeyValue.setText("") + self.focKeyValue.setText("") + self.chrKeyValue.setText("") + self.pltKeyValue.setText("") + self.timKeyValue.setText("") + self.wldKeyValue.setText("") + self.objKeyValue.setText("") + self.entKeyValue.setText("") + self.cstKeyValue.setText("") + return + + def showItem(self, tHandle, sTitle): + """Update the content of the tree with the given handle and line + number pointing to a header. + """ + nwItem = self.theProject.projTree[tHandle] + novIdx = self.theIndex.getNovelData(tHandle, sTitle) + theRefs = self.theIndex.getReferences(tHandle, sTitle) + if nwItem is None or novIdx is None: + return False + + if novIdx["level"] in self.LVL_MAP: + self.titleLabel.setText("%s" % self.tr(self.LVL_MAP[novIdx["level"]])) + else: + self.titleLabel.setText("%s" % self.tr("Title")) + self.titleValue.setText(novIdx["title"]) + + itemStatus, _ = nwItem.getImportStatus() + + self.fileValue.setText(nwItem.itemName) + self.itemValue.setText(itemStatus) + + cC = checkInt(novIdx["cCount"], 0) + wC = checkInt(novIdx["wCount"], 0) + pC = checkInt(novIdx["pCount"], 0) + + self.cCValue.setText(f"{cC:n}") + self.wCValue.setText(f"{wC:n}") + self.pCValue.setText(f"{pC:n}") + + self.synopValue.setText(novIdx["synopsis"]) + + self.povKeyValue.setText(self._formatTags(theRefs, nwKeyWords.POV_KEY)) + self.focKeyValue.setText(self._formatTags(theRefs, nwKeyWords.FOCUS_KEY)) + self.chrKeyValue.setText(self._formatTags(theRefs, nwKeyWords.CHAR_KEY)) + self.pltKeyValue.setText(self._formatTags(theRefs, nwKeyWords.PLOT_KEY)) + self.timKeyValue.setText(self._formatTags(theRefs, nwKeyWords.TIME_KEY)) + self.wldKeyValue.setText(self._formatTags(theRefs, nwKeyWords.WORLD_KEY)) + self.objKeyValue.setText(self._formatTags(theRefs, nwKeyWords.OBJECT_KEY)) + self.entKeyValue.setText(self._formatTags(theRefs, nwKeyWords.ENTITY_KEY)) + self.cstKeyValue.setText(self._formatTags(theRefs, nwKeyWords.CUSTOM_KEY)) + + return True + + ## + # Slots + ## + + def _tagClicked(self, theLink): + """Capture the click of a tag in the right-most column. + """ + logger.verbose("Clicked link: '%s'", theLink) + if len(theLink) > 0: + theBits = theLink.split("=") + if len(theBits) == 2: + self.theOutline.viewChangeRequested.emit(nwView.PROJECT) + self.theParent.docViewer.loadFromTag(theBits[1]) + return + + ## + # Internal Functions + ## + + def _formatTags(self, theRefs, theKey): + """Format the tags as clickable links. + """ + if theKey not in theRefs: + return "" + refTags = [] + for tTag in theRefs[theKey]: + refTags.append("%s" % ( + theKey[1:], tTag, tTag + )) + return ", ".join(refTags) + +# END Class GuiOutlineDetails diff --git a/novelwriter/gui/outlinedetails.py b/novelwriter/gui/outlinedetails.py deleted file mode 100644 index 358a6531..00000000 --- a/novelwriter/gui/outlinedetails.py +++ /dev/null @@ -1,355 +0,0 @@ -""" -novelWriter – GUI Project Outline Details -========================================= -GUI class for the project outline details panel - -File History: -Created: 2020-06-02 [0.7.0] - -This file is a part of novelWriter -Copyright 2018–2022, 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 . -""" - -import logging -import novelwriter - -from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP, pyqtSignal -from PyQt5.QtWidgets import ( - QScrollArea, QWidget, QGridLayout, QHBoxLayout, QGroupBox, QLabel -) - -from novelwriter.enum import nwView -from novelwriter.common import checkInt -from novelwriter.constants import trConst, nwKeyWords, nwLabels - -logger = logging.getLogger(__name__) - - -class GuiOutlineDetails(QScrollArea): - - LVL_MAP = { - "H1": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"), - "H2": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Chapter"), - "H3": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Scene"), - "H4": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Section"), - } - - viewChangeRequested = pyqtSignal(nwView) - - def __init__(self, theParent): - QScrollArea.__init__(self, theParent) - - logger.debug("Initialising GuiOutlineDetails ...") - - self.mainConf = novelwriter.CONFIG - self.theParent = theParent - self.theProject = theParent.theProject - self.theTheme = theParent.theTheme - self.theIndex = theParent.theIndex - self.optState = theParent.theProject.optState - - # Sizes - minTitle = 30*self.theTheme.textNWidth - maxTitle = 40*self.theTheme.textNWidth - wCount = self.theTheme.getTextWidth("999,999") - hSpace = int(self.mainConf.pxInt(10)) - vSpace = int(self.mainConf.pxInt(4)) - - # Details Area - self.titleLabel = QLabel("%s" % self.tr("Title")) - self.fileLabel = QLabel("%s" % self.tr("Document")) - self.itemLabel = QLabel("%s" % self.tr("Status")) - self.titleValue = QLabel("") - self.fileValue = QLabel("") - self.itemValue = QLabel("") - - self.titleValue.setMinimumWidth(minTitle) - self.titleValue.setMaximumWidth(maxTitle) - self.fileValue.setMinimumWidth(minTitle) - self.fileValue.setMaximumWidth(maxTitle) - self.itemValue.setMinimumWidth(minTitle) - self.itemValue.setMaximumWidth(maxTitle) - - # Stats Area - self.cCLabel = QLabel("%s" % self.tr("Characters")) - self.wCLabel = QLabel("%s" % self.tr("Words")) - self.pCLabel = QLabel("%s" % self.tr("Paragraphs")) - self.cCValue = QLabel("") - self.wCValue = QLabel("") - self.pCValue = QLabel("") - - self.cCValue.setMinimumWidth(wCount) - self.wCValue.setMinimumWidth(wCount) - self.pCValue.setMinimumWidth(wCount) - self.cCValue.setAlignment(Qt.AlignRight) - self.wCValue.setAlignment(Qt.AlignRight) - self.pCValue.setAlignment(Qt.AlignRight) - - # Synopsis - self.synopLabel = QLabel("%s" % self.tr("Synopsis")) - self.synopValue = QLabel("") - self.synopLWrap = QHBoxLayout() - self.synopValue.setWordWrap(True) - self.synopValue.setAlignment(Qt.AlignTop | Qt.AlignLeft) - self.synopLWrap.addWidget(self.synopValue, 1) - - # Tags - self.povKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])) - self.focKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])) - self.chrKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY])) - self.pltKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])) - self.timKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.TIME_KEY])) - self.wldKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY])) - self.objKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY])) - self.entKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY])) - self.cstKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY])) - - self.povKeyLWrap = QHBoxLayout() - self.focKeyLWrap = QHBoxLayout() - self.chrKeyLWrap = QHBoxLayout() - self.pltKeyLWrap = QHBoxLayout() - self.timKeyLWrap = QHBoxLayout() - self.wldKeyLWrap = QHBoxLayout() - self.objKeyLWrap = QHBoxLayout() - self.entKeyLWrap = QHBoxLayout() - self.cstKeyLWrap = QHBoxLayout() - - self.povKeyValue = QLabel("") - self.focKeyValue = QLabel("") - self.chrKeyValue = QLabel("") - self.pltKeyValue = QLabel("") - self.timKeyValue = QLabel("") - self.wldKeyValue = QLabel("") - self.objKeyValue = QLabel("") - self.entKeyValue = QLabel("") - self.cstKeyValue = QLabel("") - - self.povKeyValue.setWordWrap(True) - self.focKeyValue.setWordWrap(True) - self.chrKeyValue.setWordWrap(True) - self.pltKeyValue.setWordWrap(True) - self.timKeyValue.setWordWrap(True) - self.wldKeyValue.setWordWrap(True) - self.objKeyValue.setWordWrap(True) - self.entKeyValue.setWordWrap(True) - self.cstKeyValue.setWordWrap(True) - - self.povKeyValue.linkActivated.connect(self._tagClicked) - self.focKeyValue.linkActivated.connect(self._tagClicked) - self.chrKeyValue.linkActivated.connect(self._tagClicked) - self.pltKeyValue.linkActivated.connect(self._tagClicked) - self.timKeyValue.linkActivated.connect(self._tagClicked) - self.wldKeyValue.linkActivated.connect(self._tagClicked) - self.objKeyValue.linkActivated.connect(self._tagClicked) - self.entKeyValue.linkActivated.connect(self._tagClicked) - self.cstKeyValue.linkActivated.connect(self._tagClicked) - - self.povKeyLWrap.addWidget(self.povKeyValue, 1) - self.focKeyLWrap.addWidget(self.focKeyValue, 1) - self.chrKeyLWrap.addWidget(self.chrKeyValue, 1) - self.pltKeyLWrap.addWidget(self.pltKeyValue, 1) - self.timKeyLWrap.addWidget(self.timKeyValue, 1) - self.wldKeyLWrap.addWidget(self.wldKeyValue, 1) - self.objKeyLWrap.addWidget(self.objKeyValue, 1) - self.entKeyLWrap.addWidget(self.entKeyValue, 1) - self.cstKeyLWrap.addWidget(self.cstKeyValue, 1) - - # Selected Item Details - self.mainGroup = QGroupBox(self.tr("Title Details"), self) - self.mainForm = QGridLayout() - self.mainGroup.setLayout(self.mainForm) - - self.mainForm.addWidget(self.titleLabel, 0, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.mainForm.addWidget(self.titleValue, 0, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.mainForm.addWidget(self.cCLabel, 0, 2, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.mainForm.addWidget(self.cCValue, 0, 3, 1, 1, Qt.AlignTop | Qt.AlignRight) - self.mainForm.addWidget(self.fileLabel, 1, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.mainForm.addWidget(self.fileValue, 1, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.mainForm.addWidget(self.wCLabel, 1, 2, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.mainForm.addWidget(self.wCValue, 1, 3, 1, 1, Qt.AlignTop | Qt.AlignRight) - self.mainForm.addWidget(self.itemLabel, 2, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.mainForm.addWidget(self.itemValue, 2, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.mainForm.addWidget(self.pCLabel, 2, 2, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.mainForm.addWidget(self.pCValue, 2, 3, 1, 1, Qt.AlignTop | Qt.AlignRight) - self.mainForm.addWidget(self.synopLabel, 3, 0, 1, 4, Qt.AlignTop | Qt.AlignLeft) - self.mainForm.addLayout(self.synopLWrap, 4, 0, 1, 4, Qt.AlignTop | Qt.AlignLeft) - - self.mainForm.setColumnStretch(1, 1) - self.mainForm.setRowStretch(4, 1) - self.mainForm.setHorizontalSpacing(hSpace) - self.mainForm.setVerticalSpacing(vSpace) - - # Selected Item Tags - self.tagsGroup = QGroupBox(self.tr("Reference Tags"), self) - self.tagsForm = QGridLayout() - self.tagsGroup.setLayout(self.tagsForm) - - self.tagsForm.addWidget(self.povKeyLabel, 0, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addLayout(self.povKeyLWrap, 0, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addWidget(self.focKeyLabel, 1, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addLayout(self.focKeyLWrap, 1, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addWidget(self.chrKeyLabel, 2, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addLayout(self.chrKeyLWrap, 2, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addWidget(self.pltKeyLabel, 3, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addLayout(self.pltKeyLWrap, 3, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addWidget(self.timKeyLabel, 4, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addLayout(self.timKeyLWrap, 4, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addWidget(self.wldKeyLabel, 5, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addLayout(self.wldKeyLWrap, 5, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addWidget(self.objKeyLabel, 6, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addLayout(self.objKeyLWrap, 6, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addWidget(self.entKeyLabel, 7, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addLayout(self.entKeyLWrap, 7, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addWidget(self.cstKeyLabel, 8, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft) - self.tagsForm.addLayout(self.cstKeyLWrap, 8, 1, 1, 1, Qt.AlignTop | Qt.AlignLeft) - - self.tagsForm.setColumnStretch(1, 1) - self.tagsForm.setRowStretch(8, 1) - self.tagsForm.setHorizontalSpacing(hSpace) - self.tagsForm.setVerticalSpacing(vSpace) - - # Assemble - self.outerWidget = QWidget() - self.outerBox = QHBoxLayout() - self.outerBox.addWidget(self.mainGroup, 0) - self.outerBox.addWidget(self.tagsGroup, 1) - - self.outerWidget.setLayout(self.outerBox) - self.setWidget(self.outerWidget) - - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) - self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) - self.setWidgetResizable(True) - - self.initDetails() - - logger.debug("GuiOutlineDetails initialisation complete") - - return - - def initDetails(self): - """Set or update outline settings. - """ - # Scroll bars - if self.mainConf.hideVScroll: - self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - else: - self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) - - if self.mainConf.hideHScroll: - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - else: - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) - - return - - def clearDetails(self): - """Clear all the data labels. - """ - self.titleLabel.setText("%s" % self.tr("Title")) - self.titleValue.setText("") - self.fileValue.setText("") - self.itemValue.setText("") - self.cCValue.setText("") - self.wCValue.setText("") - self.pCValue.setText("") - self.synopValue.setText("") - self.povKeyValue.setText("") - self.focKeyValue.setText("") - self.chrKeyValue.setText("") - self.pltKeyValue.setText("") - self.timKeyValue.setText("") - self.wldKeyValue.setText("") - self.objKeyValue.setText("") - self.entKeyValue.setText("") - self.cstKeyValue.setText("") - return - - def showItem(self, tHandle, sTitle): - """Update the content of the tree with the given handle and line - number pointing to a header. - """ - nwItem = self.theProject.projTree[tHandle] - novIdx = self.theIndex.getNovelData(tHandle, sTitle) - theRefs = self.theIndex.getReferences(tHandle, sTitle) - if nwItem is None or novIdx is None: - return False - - if novIdx["level"] in self.LVL_MAP: - self.titleLabel.setText("%s" % self.tr(self.LVL_MAP[novIdx["level"]])) - else: - self.titleLabel.setText("%s" % self.tr("Title")) - self.titleValue.setText(novIdx["title"]) - - itemStatus, _ = nwItem.getImportStatus() - - self.fileValue.setText(nwItem.itemName) - self.itemValue.setText(itemStatus) - - cC = checkInt(novIdx["cCount"], 0) - wC = checkInt(novIdx["wCount"], 0) - pC = checkInt(novIdx["pCount"], 0) - - self.cCValue.setText(f"{cC:n}") - self.wCValue.setText(f"{wC:n}") - self.pCValue.setText(f"{pC:n}") - - self.synopValue.setText(novIdx["synopsis"]) - - self.povKeyValue.setText(self._formatTags(theRefs, nwKeyWords.POV_KEY)) - self.focKeyValue.setText(self._formatTags(theRefs, nwKeyWords.FOCUS_KEY)) - self.chrKeyValue.setText(self._formatTags(theRefs, nwKeyWords.CHAR_KEY)) - self.pltKeyValue.setText(self._formatTags(theRefs, nwKeyWords.PLOT_KEY)) - self.timKeyValue.setText(self._formatTags(theRefs, nwKeyWords.TIME_KEY)) - self.wldKeyValue.setText(self._formatTags(theRefs, nwKeyWords.WORLD_KEY)) - self.objKeyValue.setText(self._formatTags(theRefs, nwKeyWords.OBJECT_KEY)) - self.entKeyValue.setText(self._formatTags(theRefs, nwKeyWords.ENTITY_KEY)) - self.cstKeyValue.setText(self._formatTags(theRefs, nwKeyWords.CUSTOM_KEY)) - - return True - - ## - # Slots - ## - - def _tagClicked(self, theLink): - """Capture the click of a tag in the right-most column. - """ - logger.verbose("Clicked link: '%s'", theLink) - if len(theLink) > 0: - theBits = theLink.split("=") - if len(theBits) == 2: - self.viewChangeRequested.emit(nwView.PROJECT) - self.theParent.docViewer.loadFromTag(theBits[1]) - return - - ## - # Internal Functions - ## - - def _formatTags(self, theRefs, theKey): - """Format the tags as clickable links. - """ - if theKey not in theRefs: - return "" - refTags = [] - for tTag in theRefs[theKey]: - refTags.append("%s" % ( - theKey[1:], tTag, tTag - )) - return ", ".join(refTags) - -# END Class GuiOutlineDetails diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index ad06cc59..645990e9 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -39,8 +39,8 @@ from PyQt5.QtWidgets import ( from novelwriter.gui import ( GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu, - GuiMainStatus, GuiNovelTree, GuiOutline, GuiOutlineDetails, GuiProjectTree, - GuiTheme, GuiViewsBar + GuiMainStatus, GuiNovelTree, GuiOutline, GuiProjectTree, GuiTheme, + GuiViewsBar ) from novelwriter.dialogs import ( GuiAbout, GuiDocMerge, GuiDocSplit, GuiItemEditor, GuiPreferences, @@ -112,7 +112,6 @@ class GuiMain(QMainWindow): self.docViewer = GuiDocViewer(self) self.treeMeta = GuiItemDetails(self) self.projView = GuiOutline(self) - self.projMeta = GuiOutlineDetails(self) self.mainMenu = GuiMainMenu(self) self.viewsBar = GuiViewsBar(self) @@ -128,7 +127,7 @@ class GuiMain(QMainWindow): self.treeView.wordCountsChanged.connect(self._updateStatusWordCount) self.viewsBar.viewChangeRequested.connect(self._changeView) - self.projMeta.viewChangeRequested.connect(self._changeView) + self.projView.viewChangeRequested.connect(self._changeView) # Project Tree Stack self.projStack = QStackedWidget() @@ -156,12 +155,6 @@ class GuiMain(QMainWindow): self.splitDocs.addWidget(self.docEditor) self.splitDocs.addWidget(self.splitView) - # Splitter : Project Outlie / Outline Details - self.splitOutline = QSplitter(Qt.Vertical) - self.splitOutline.addWidget(self.projView) - self.splitOutline.addWidget(self.projMeta) - self.splitOutline.setSizes(self.mainConf.getOutlinePanePos()) - # Splitter : Project Tree / Main Tabs self.splitMain = QSplitter(Qt.Horizontal) self.splitMain.setContentsMargins(0, 0, mPx, 0) @@ -172,7 +165,7 @@ class GuiMain(QMainWindow): # Main Stack : Editor / Outline self.mainStack = QStackedWidget() self.mainStack.addWidget(self.splitMain) - self.mainStack.addWidget(self.splitOutline) + self.mainStack.addWidget(self.projView) self.mainStack.currentChanged.connect(self._mainStackChanged) # Indices of Splitter Widgets @@ -185,7 +178,7 @@ class GuiMain(QMainWindow): # Indices of Tab Widgets self.idxEditorView = self.mainStack.indexOf(self.splitMain) - self.idxOutlineView = self.mainStack.indexOf(self.splitOutline) + self.idxOutlineView = self.mainStack.indexOf(self.projView) self.idxTreeView = self.projStack.indexOf(self.treeView) self.idxNovelView = self.projStack.indexOf(self.novelView) @@ -293,7 +286,7 @@ class GuiMain(QMainWindow): self.docEditor.clearEditor() self.docEditor.setDictionaries() self.closeDocViewer() - self.projMeta.clearDetails() + self.projView.clearOutline() # General self.statusBar.clearStatus() @@ -904,7 +897,7 @@ class GuiMain(QMainWindow): logger.verbose("Forcing a rebuild of the Project Outline") self._changeView(nwView.OUTLINE) - self.projView.refreshTree(overRide=True) + self.projView.refreshView(overRide=True) return True @@ -955,7 +948,6 @@ class GuiMain(QMainWindow): self.treeView.initTree() self.novelView.initTree() self.projView.initOutline() - self.projMeta.initDetails() self._updateStatusWordCount() return @@ -1192,7 +1184,7 @@ class GuiMain(QMainWindow): if not self.isFocusMode: self.mainConf.setMainPanePos(self.splitMain.sizes()) self.mainConf.setDocPanePos(self.splitDocs.sizes()) - self.mainConf.setOutlinePanePos(self.splitOutline.sizes()) + self.mainConf.setOutlinePanePos(self.projView.splitSizes()) if self.viewMeta.isVisible(): self.mainConf.setViewPanePos(self.splitView.sizes()) @@ -1500,7 +1492,7 @@ class GuiMain(QMainWindow): self.projStack.setCurrentWidget(self.novelView) elif view == nwView.OUTLINE: - self.mainStack.setCurrentWidget(self.splitOutline) + self.mainStack.setCurrentWidget(self.projView) elif view == nwView.DETAILS: self.showProjectDetailsDialog() @@ -1590,7 +1582,7 @@ class GuiMain(QMainWindow): logger.verbose("Novel tree changed while Outline tab active") if self.hasProject: self.treeView.flushTreeOrder() - self.projView.refreshTree(novelChanged=True) + self.projView.refreshView(novelChanged=True) return @@ -1623,7 +1615,7 @@ class GuiMain(QMainWindow): elif tabIndex == self.idxOutlineView: logger.verbose("Project outline tab activated") if self.hasProject: - self.projView.refreshTree() + self.projView.refreshView() return