From ca48e3716309f28844e885ab54ff7a8004f75c7b Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Sun, 15 May 2022 17:45:51 +0200
Subject: [PATCH] Move the outline view into a wrapper widget
---
novelwriter/gui/__init__.py | 2 -
novelwriter/gui/outline.py | 407 +++++++++++++++++++++++++++++-
novelwriter/gui/outlinedetails.py | 355 --------------------------
novelwriter/guimain.py | 30 +--
4 files changed, 404 insertions(+), 390 deletions(-)
delete mode 100644 novelwriter/gui/outlinedetails.py
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