diff --git a/nw/__init__.py b/nw/__init__.py
index 61aa56bb..e0696373 100644
--- a/nw/__init__.py
+++ b/nw/__init__.py
@@ -172,7 +172,7 @@ def main(sysArgs=None):
debugLevel = logging.INFO
elif inOpt == "--debug":
debugLevel = logging.DEBUG
- logFormat = "[{asctime:}] {name:>30}:{lineno:<4d} {levelname:8} {message:}"
+ logFormat = "[{asctime:}] {name:>20}:{lineno:<4d} {levelname:8} {message:}"
elif inOpt == "--logfile":
logFile = inArg
toFile = True
@@ -180,7 +180,7 @@ def main(sysArgs=None):
toStd = False
elif inOpt == "--verbose":
debugLevel = VERBOSE
- logFormat = "[{asctime:}] {name:>30}:{lineno:<4d} {levelname:8} {message:}"
+ logFormat = "[{asctime:}] {name:>20}:{lineno:<4d} {levelname:8} {message:}"
elif inOpt == "--style":
qtStyle = inArg
elif inOpt == "--config":
@@ -196,7 +196,7 @@ def main(sysArgs=None):
CONFIG.cmdOpen = cmdOpen
# Set Logging
- logFmt = logging.Formatter(fmt=logFormat,datefmt="%Y-%m-%d %H:%M:%S",style="{")
+ logFmt = logging.Formatter(fmt=logFormat, datefmt="%Y-%m-%d %H:%M:%S", style="{")
if not logFile == "" and toFile:
if path.isfile(logFile+".bak"):
diff --git a/nw/assets/icons/fallback/hash-dark.svg b/nw/assets/icons/fallback/hash-dark.svg
new file mode 100644
index 00000000..551d7993
--- /dev/null
+++ b/nw/assets/icons/fallback/hash-dark.svg
@@ -0,0 +1,55 @@
+
+
diff --git a/nw/assets/icons/fallback/hash.svg b/nw/assets/icons/fallback/hash.svg
new file mode 100644
index 00000000..1553201b
--- /dev/null
+++ b/nw/assets/icons/fallback/hash.svg
@@ -0,0 +1,55 @@
+
+
diff --git a/nw/assets/icons/typicons_colour_dark/hash.svg b/nw/assets/icons/typicons_colour_dark/hash.svg
new file mode 100644
index 00000000..b1c34da8
--- /dev/null
+++ b/nw/assets/icons/typicons_colour_dark/hash.svg
@@ -0,0 +1,55 @@
+
+
diff --git a/nw/assets/icons/typicons_colour_dark/icons.conf b/nw/assets/icons/typicons_colour_dark/icons.conf
index f3c96935..76290410 100644
--- a/nw/assets/icons/typicons_colour_dark/icons.conf
+++ b/nw/assets/icons/typicons_colour_dark/icons.conf
@@ -1,8 +1,9 @@
##
-# Icons: Typicons Colour Dark
-# Source: https://github.com/stephenhutchings/typicons.font
-# Credit: Stephen Hutchings
-# Modified: Veronica Berglyd Olsen
+# Icons: Typicons Colour Dark
+# Source: https://github.com/stephenhutchings/typicons.font
+# Credit: Stephen Hutchings
+# Modified: Veronica Berglyd Olsen
+# Additions: hash.svg
##
[Main]
@@ -39,3 +40,4 @@ save = download.svg
edit = pencil.svg
check = tick.svg
cross = times.svg
+hash = hash.svg
diff --git a/nw/assets/icons/typicons_colour_light/hash.svg b/nw/assets/icons/typicons_colour_light/hash.svg
new file mode 100644
index 00000000..e191e8b4
--- /dev/null
+++ b/nw/assets/icons/typicons_colour_light/hash.svg
@@ -0,0 +1,55 @@
+
+
diff --git a/nw/assets/icons/typicons_colour_light/icons.conf b/nw/assets/icons/typicons_colour_light/icons.conf
index d3df7d01..08042fc9 100644
--- a/nw/assets/icons/typicons_colour_light/icons.conf
+++ b/nw/assets/icons/typicons_colour_light/icons.conf
@@ -1,8 +1,9 @@
##
-# Icons: Typicons Colour Light
-# Source: https://github.com/stephenhutchings/typicons.font
-# Credit: Stephen Hutchings
-# Modified: Veronica Berglyd Olsen
+# Icons: Typicons Colour Light
+# Source: https://github.com/stephenhutchings/typicons.font
+# Credit: Stephen Hutchings
+# Modified: Veronica Berglyd Olsen
+# Additions: hash.svg
##
[Main]
@@ -39,3 +40,4 @@ save = download.svg
edit = pencil.svg
check = tick.svg
cross = times.svg
+hash = hash.svg
diff --git a/nw/assets/icons/typicons_grey_dark/hash.svg b/nw/assets/icons/typicons_grey_dark/hash.svg
new file mode 100644
index 00000000..a6b9a654
--- /dev/null
+++ b/nw/assets/icons/typicons_grey_dark/hash.svg
@@ -0,0 +1,55 @@
+
+
diff --git a/nw/assets/icons/typicons_grey_dark/icons.conf b/nw/assets/icons/typicons_grey_dark/icons.conf
index 75268e23..d81f4adf 100644
--- a/nw/assets/icons/typicons_grey_dark/icons.conf
+++ b/nw/assets/icons/typicons_grey_dark/icons.conf
@@ -1,7 +1,9 @@
##
-# Icons: Typicons Grey Dark
-# Source: https://github.com/stephenhutchings/typicons.font
-# Credit: Stephen Hutchings
+# Icons: Typicons Grey Dark
+# Source: https://github.com/stephenhutchings/typicons.font
+# Credit: Stephen Hutchings
+# Modified: Veronica Berglyd Olsen
+# Additions: hash.svg
##
[Main]
@@ -38,3 +40,4 @@ save = download.svg
edit = pencil.svg
check = tick.svg
cross = times.svg
+hash = hash.svg
diff --git a/nw/assets/icons/typicons_grey_light/hash.svg b/nw/assets/icons/typicons_grey_light/hash.svg
new file mode 100644
index 00000000..1553201b
--- /dev/null
+++ b/nw/assets/icons/typicons_grey_light/hash.svg
@@ -0,0 +1,55 @@
+
+
diff --git a/nw/assets/icons/typicons_grey_light/icons.conf b/nw/assets/icons/typicons_grey_light/icons.conf
index 766aed98..2b26abc8 100644
--- a/nw/assets/icons/typicons_grey_light/icons.conf
+++ b/nw/assets/icons/typicons_grey_light/icons.conf
@@ -1,7 +1,9 @@
##
-# Icons: Typicons Grey Light
-# Source: https://github.com/stephenhutchings/typicons.font
-# Credit: Stephen Hutchings
+# Icons: Typicons Grey Light
+# Source: https://github.com/stephenhutchings/typicons.font
+# Credit: Stephen Hutchings
+# Modified: Veronica Berglyd Olsen
+# Additions: hash.svg
##
[Main]
@@ -38,3 +40,4 @@ save = download.svg
edit = pencil.svg
check = tick.svg
cross = times.svg
+hash = hash.svg
diff --git a/nw/constants/constants.py b/nw/constants/constants.py
index 92378a73..66b026fc 100644
--- a/nw/constants/constants.py
+++ b/nw/constants/constants.py
@@ -136,7 +136,6 @@ class nwLabels():
}
OUTLINE_COLS = {
nwOutline.TITLE : "Title",
- nwOutline.HANDLE : "Handle",
nwOutline.LEVEL : "Level",
nwOutline.LABEL : "Document",
nwOutline.LINE : "Line",
diff --git a/nw/constants/enum.py b/nw/constants/enum.py
index c4a0f031..ee33c575 100644
--- a/nw/constants/enum.py
+++ b/nw/constants/enum.py
@@ -107,21 +107,20 @@ class nwAlert(Enum):
class nwOutline(Enum):
TITLE = 0
- HANDLE = 1
- LEVEL = 2
- LABEL = 3
- LINE = 4
- CCOUNT = 5
- WCOUNT = 6
- PCOUNT = 7
- POV = 8
- CHAR = 9
- PLOT = 10
- TIME = 11
- WORLD = 12
- OBJECT = 13
- ENTITY = 14
- CUSTOM = 15
- SYNOP = 16
+ LEVEL = 1
+ LABEL = 2
+ LINE = 3
+ CCOUNT = 4
+ WCOUNT = 5
+ PCOUNT = 6
+ POV = 7
+ CHAR = 8
+ PLOT = 9
+ TIME = 10
+ WORLD = 11
+ OBJECT = 12
+ ENTITY = 13
+ CUSTOM = 14
+ SYNOP = 15
# END Enum nwOutline
diff --git a/nw/gui/__init__.py b/nw/gui/__init__.py
index 28a7a654..4df869b5 100644
--- a/nw/gui/__init__.py
+++ b/nw/gui/__init__.py
@@ -5,23 +5,23 @@ from nw.gui.build import GuiBuildNovel
from nw.gui.docbars import GuiDocTitleBar
from nw.gui.docbars import GuiNoticeBar
from nw.gui.docbars import GuiSearchBar
-from nw.gui.docdetails import GuiDocDetails
+from nw.gui.docdetails import GuiDocViewDetails
from nw.gui.doceditor import GuiDocEditor
from nw.gui.docmerge import GuiDocMerge
from nw.gui.docsplit import GuiDocSplit
-from nw.gui.doctree import GuiDocTree
from nw.gui.docviewer import GuiDocViewer
+from nw.gui.itemdetails import GuiItemDetails
from nw.gui.itemeditor import GuiItemEditor
from nw.gui.mainmenu import GuiMainMenu
from nw.gui.outline import GuiProjectOutline
from nw.gui.preferences import GuiPreferences
-from nw.gui.projectload import GuiProjectLoad
-from nw.gui.projectsettings import GuiProjectSettings
+from nw.gui.projload import GuiProjectLoad
+from nw.gui.projsettings import GuiProjectSettings
+from nw.gui.projtree import GuiProjectTree
from nw.gui.sessionlog import GuiSessionLogView
from nw.gui.statusbar import GuiMainStatus
from nw.gui.theme import GuiIcons
from nw.gui.theme import GuiTheme
-from nw.gui.viewdetails import GuiDocViewDetails
__all__ = [
"GuiAbout",
@@ -29,21 +29,21 @@ __all__ = [
"GuiDocTitleBar",
"GuiNoticeBar",
"GuiSearchBar",
- "GuiDocDetails",
+ "GuiDocViewDetails",
"GuiDocEditor",
"GuiDocMerge",
"GuiDocSplit",
- "GuiDocTree",
"GuiDocViewer",
+ "GuiItemDetails",
"GuiItemEditor",
"GuiMainMenu",
"GuiProjectOutline",
"GuiPreferences",
"GuiProjectLoad",
"GuiProjectSettings",
+ "GuiProjectTree",
"GuiSessionLogView",
"GuiMainStatus",
"GuiIcons",
"GuiTheme",
- "GuiDocViewDetails",
]
diff --git a/nw/gui/build.py b/nw/gui/build.py
index 791dff67..f072b6ea 100644
--- a/nw/gui/build.py
+++ b/nw/gui/build.py
@@ -93,7 +93,7 @@ class GuiBuildNovel(QDialog):
# Title Formats
# =============
- self.titleGroup = QGroupBox("Novel Title Formats", self)
+ self.titleGroup = QGroupBox("Title Formats for Novel Files", self)
self.titleForm = QGridLayout(self)
self.titleGroup.setLayout(self.titleForm)
@@ -169,9 +169,9 @@ class GuiBuildNovel(QDialog):
# Text Options
# =============
- self.textGroup = QGroupBox("Text Options", self)
- self.textForm = QGridLayout(self)
- self.textGroup.setLayout(self.textForm)
+ self.formatGroup = QGroupBox("Formatting Options", self)
+ self.formatForm = QGridLayout(self)
+ self.formatGroup.setLayout(self.formatForm)
## Font Family
self.textFont = QLineEdit()
@@ -212,29 +212,29 @@ class GuiBuildNovel(QDialog):
self.optState.getBool("GuiBuildNovel", "noStyling", False)
)
- self.textForm.addWidget(QLabel("Font family"), 0, 0, 1, 1, Qt.AlignLeft)
- self.textForm.addWidget(self.textFont, 0, 1, 1, 1, Qt.AlignRight)
- self.textForm.addWidget(self.fontButton, 0, 2, 1, 1, Qt.AlignRight)
- self.textForm.addWidget(QLabel("Font size"), 1, 0, 1, 1, Qt.AlignLeft)
- self.textForm.addWidget(self.textSize, 1, 1, 1, 2, Qt.AlignRight)
- self.textForm.addWidget(QLabel("Justify text"), 2, 0, 1, 1, Qt.AlignLeft)
- self.textForm.addWidget(self.justifyText, 2, 1, 1, 2, Qt.AlignRight)
- self.textForm.addWidget(QLabel("Disable styling"), 3, 0, 1, 1, Qt.AlignLeft)
- self.textForm.addWidget(self.noStyling, 3, 1, 1, 2, Qt.AlignRight)
+ self.formatForm.addWidget(QLabel("Font family"), 0, 0, 1, 1, Qt.AlignLeft)
+ self.formatForm.addWidget(self.textFont, 0, 1, 1, 1, Qt.AlignRight)
+ self.formatForm.addWidget(self.fontButton, 0, 2, 1, 1, Qt.AlignRight)
+ self.formatForm.addWidget(QLabel("Font size"), 1, 0, 1, 1, Qt.AlignLeft)
+ self.formatForm.addWidget(self.textSize, 1, 1, 1, 2, Qt.AlignRight)
+ self.formatForm.addWidget(QLabel("Justify text"), 2, 0, 1, 1, Qt.AlignLeft)
+ self.formatForm.addWidget(self.justifyText, 2, 1, 1, 2, Qt.AlignRight)
+ self.formatForm.addWidget(QLabel("Disable styling"), 3, 0, 1, 1, Qt.AlignLeft)
+ self.formatForm.addWidget(self.noStyling, 3, 1, 1, 2, Qt.AlignRight)
- self.textForm.setColumnStretch(0, 1)
- self.textForm.setColumnStretch(1, 0)
- self.textForm.setColumnStretch(2, 0)
+ self.formatForm.setColumnStretch(0, 1)
+ self.formatForm.setColumnStretch(1, 0)
+ self.formatForm.setColumnStretch(2, 0)
# Include Switches
# ================
- self.includeGroup = QGroupBox("Include Non-Text Elements", self)
- self.includeForm = QGridLayout(self)
- self.includeGroup.setLayout(self.includeForm)
+ self.textGroup = QGroupBox("Text Options", self)
+ self.textForm = QGridLayout(self)
+ self.textGroup.setLayout(self.textForm)
self.includeSynopsis = QSwitch()
self.includeSynopsis.setToolTip(
- "Include synopsis type comments in the output."
+ "Include synopsis comments in the output."
)
self.includeSynopsis.setChecked(self.theProject.titleFormat["withSynopsis"])
@@ -250,21 +250,31 @@ class GuiBuildNovel(QDialog):
)
self.includeKeywords.setChecked(self.theProject.titleFormat["withKeywords"])
- self.includeForm.addWidget(QLabel("Include synopsis"), 0, 0, 1, 1, Qt.AlignLeft)
- self.includeForm.addWidget(self.includeSynopsis, 0, 1, 1, 1, Qt.AlignRight)
- self.includeForm.addWidget(QLabel("Include comments"), 1, 0, 1, 1, Qt.AlignLeft)
- self.includeForm.addWidget(self.includeComments, 1, 1, 1, 1, Qt.AlignRight)
- self.includeForm.addWidget(QLabel("Include keywords"), 2, 0, 1, 1, Qt.AlignLeft)
- self.includeForm.addWidget(self.includeKeywords, 2, 1, 1, 1, Qt.AlignRight)
+ self.includeBody = QSwitch()
+ self.includeBody.setToolTip(
+ "Include body text in the output."
+ )
+ self.includeBody.setChecked(
+ self.optState.getBool("GuiBuildNovel", "includeBody", True)
+ )
- self.includeForm.setColumnStretch(0, 1)
- self.includeForm.setColumnStretch(1, 0)
+ self.textForm.addWidget(QLabel("Include synopsis"), 0, 0, 1, 1, Qt.AlignLeft)
+ self.textForm.addWidget(self.includeSynopsis, 0, 1, 1, 1, Qt.AlignRight)
+ self.textForm.addWidget(QLabel("Include comments"), 1, 0, 1, 1, Qt.AlignLeft)
+ self.textForm.addWidget(self.includeComments, 1, 1, 1, 1, Qt.AlignRight)
+ self.textForm.addWidget(QLabel("Include keywords"), 2, 0, 1, 1, Qt.AlignLeft)
+ self.textForm.addWidget(self.includeKeywords, 2, 1, 1, 1, Qt.AlignRight)
+ self.textForm.addWidget(QLabel("Include body text"), 3, 0, 1, 1, Qt.AlignLeft)
+ self.textForm.addWidget(self.includeBody, 3, 1, 1, 1, Qt.AlignRight)
+
+ self.textForm.setColumnStretch(0, 1)
+ self.textForm.setColumnStretch(1, 0)
# Additional Options
# ==================
- self.addsGroup = QGroupBox("Additional Options", self)
- self.addsForm = QGridLayout(self)
- self.addsGroup.setLayout(self.addsForm)
+ self.fileGroup = QGroupBox("File Options", self)
+ self.fileForm = QGridLayout(self)
+ self.fileGroup.setLayout(self.fileForm)
self.novelFiles = QSwitch()
self.novelFiles.setToolTip(
@@ -290,26 +300,15 @@ class GuiBuildNovel(QDialog):
self.optState.getBool("GuiBuildNovel", "ignoreFlag", False)
)
- self.excludeBody = QSwitch()
- self.excludeBody.setToolTip(
- "Exclude body text in the output. Combine with 'Include synopsis' "
- "for making outline."
- )
- self.excludeBody.setChecked(
- self.optState.getBool("GuiBuildNovel", "excludeBody", False)
- )
+ self.fileForm.addWidget(QLabel("Include novel files"), 0, 0, 1, 1, Qt.AlignLeft)
+ self.fileForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight)
+ self.fileForm.addWidget(QLabel("Include note files"), 1, 0, 1, 1, Qt.AlignLeft)
+ self.fileForm.addWidget(self.noteFiles, 1, 1, 1, 1, Qt.AlignRight)
+ self.fileForm.addWidget(QLabel("Ignore export flag"), 2, 0, 1, 1, Qt.AlignLeft)
+ self.fileForm.addWidget(self.ignoreFlag, 2, 1, 1, 1, Qt.AlignRight)
- self.addsForm.addWidget(QLabel("Include novel files"), 0, 0, 1, 1, Qt.AlignLeft)
- self.addsForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight)
- self.addsForm.addWidget(QLabel("Include note files"), 1, 0, 1, 1, Qt.AlignLeft)
- self.addsForm.addWidget(self.noteFiles, 1, 1, 1, 1, Qt.AlignRight)
- self.addsForm.addWidget(QLabel("Ignore export flag"), 2, 0, 1, 1, Qt.AlignLeft)
- self.addsForm.addWidget(self.ignoreFlag, 2, 1, 1, 1, Qt.AlignRight)
- self.addsForm.addWidget(QLabel("Exclude body text"), 3, 0, 1, 1, Qt.AlignLeft)
- self.addsForm.addWidget(self.excludeBody, 3, 1, 1, 1, Qt.AlignRight)
-
- self.addsForm.setColumnStretch(0, 1)
- self.addsForm.setColumnStretch(1, 0)
+ self.fileForm.setColumnStretch(0, 1)
+ self.fileForm.setColumnStretch(1, 0)
# Build Button
# ============
@@ -373,9 +372,9 @@ class GuiBuildNovel(QDialog):
# Assemble GUI
# ============
self.toolsBox.addWidget(self.titleGroup)
+ self.toolsBox.addWidget(self.formatGroup)
self.toolsBox.addWidget(self.textGroup)
- self.toolsBox.addWidget(self.includeGroup)
- self.toolsBox.addWidget(self.addsGroup)
+ self.toolsBox.addWidget(self.fileGroup)
self.toolsBox.addStretch(1)
self.toolsBox.addWidget(self.buildProgress)
self.toolsBox.addWidget(self.buildNovel)
@@ -388,6 +387,7 @@ class GuiBuildNovel(QDialog):
self.outerBox.setStretch(1, 1)
self.setLayout(self.outerBox)
+ self.buildNovel.setFocus()
self.show()
@@ -436,7 +436,7 @@ class GuiBuildNovel(QDialog):
novelFiles = self.novelFiles.isChecked()
noteFiles = self.noteFiles.isChecked()
ignoreFlag = self.ignoreFlag.isChecked()
- excludeBody = self.excludeBody.isChecked()
+ includeBody = self.includeBody.isChecked()
makeHtml = ToHtml(self.theProject, self.theParent)
makeHtml.setTitleFormat(fmtTitle)
@@ -444,7 +444,7 @@ class GuiBuildNovel(QDialog):
makeHtml.setUnNumberedFormat(fmtUnnumbered)
makeHtml.setSceneFormat(fmtScene, fmtScene == "")
makeHtml.setSectionFormat(fmtSection, fmtSection == "")
- makeHtml.setBodyText(not excludeBody)
+ makeHtml.setBodyText(includeBody)
makeHtml.setSynopsis(incSynopsis)
makeHtml.setComments(incComments)
makeHtml.setKeywords(incKeywords)
@@ -867,7 +867,7 @@ class GuiBuildNovel(QDialog):
self.optState.setValue("GuiBuildNovel", "addNovel", self.novelFiles.isChecked())
self.optState.setValue("GuiBuildNovel", "addNotes", self.noteFiles.isChecked())
self.optState.setValue("GuiBuildNovel", "ignoreFlag", self.ignoreFlag.isChecked())
- self.optState.setValue("GuiBuildNovel", "excludeBody", self.excludeBody.isChecked())
+ self.optState.setValue("GuiBuildNovel", "includeBody", self.includeBody.isChecked())
self.optState.saveSettings()
return
diff --git a/nw/gui/docdetails.py b/nw/gui/docdetails.py
index 39bc6fe6..d4b2621c 100644
--- a/nw/gui/docdetails.py
+++ b/nw/gui/docdetails.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
-"""novelWriter GUI Document Details
+"""novelWriter GUI DocView Details
- novelWriter – GUI Document Details
-====================================
- Class holding the left side document details panel
+ novelWriter – GUI DocView Details
+===================================
+ Class holding the document view details panel
File History:
- Created: 2019-04-24 [0.0.1]
+ Created: 2019-10-31 [0.3.2]
This file is a part of novelWriter
Copyright 2020, Veronica Berglyd Olsen
@@ -28,238 +28,124 @@
import logging
import nw
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QFont, QIcon, QPixmap
-from PyQt5.QtWidgets import QFrame, QGridLayout, QLabel
-
-from nw.constants import (
- nwLabels, nwItemClass, nwItemType, nwItemLayout, nwUnicode
+from PyQt5.QtCore import Qt, QSize
+from PyQt5.QtWidgets import (
+ QWidget, QLabel, QScrollArea, QFrame, QToolButton, QCheckBox, QGridLayout
)
logger = logging.getLogger(__name__)
-class GuiDocDetails(QFrame):
+class GuiDocViewDetails(QWidget):
def __init__(self, theParent, theProject):
- QFrame.__init__(self, theParent)
+ QWidget.__init__(self, theParent)
- logger.debug("Initialising DocDetails ...")
+ logger.debug("Initialising DocViewDetails ...")
self.mainConf = nw.CONFIG
self.theParent = theParent
self.theProject = theProject
- self.theTheme = theParent.theTheme
- self.theHandle = None
+ self.currHandle = None
- self.mainBox = QGridLayout(self)
- self.mainBox.setVerticalSpacing(1)
- self.mainBox.setHorizontalSpacing(6)
- self.setLayout(self.mainBox)
+ self.outerBox = QGridLayout(self)
+ self.outerBox.setContentsMargins(0,0,0,0)
+ self.outerBox.setHorizontalSpacing(0)
+ self.outerBox.setVerticalSpacing(4)
- self.pS = 0.9*self.theTheme.fontPointSize
- self.iPx = self.theTheme.textIconSize
- self.sPx = int(round(0.8*self.theTheme.textIconSize))
+ self.refLabel = QLabel("Referenced By", self)
- self.expCheck = self.theTheme.getPixmap("check", (self.iPx, self.iPx))
- self.expCross = self.theTheme.getPixmap("cross", (self.iPx, self.iPx))
+ self.showHide = QToolButton()
+ self.showHide.setStyleSheet("QToolButton { border: none; }")
+ self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly)
+ self.showHide.setArrowType(Qt.DownArrow)
+ self.showHide.setCheckable(True)
+ self.showHide.setIconSize(QSize(16,16))
+ self.showHide.toggled.connect(self._doShowHide)
- self.fntLabel = QFont()
- self.fntLabel.setBold(True)
- self.fntLabel.setPointSizeF(self.pS)
+ self.isSticky = QCheckBox("Sticky")
+ self.isSticky.setChecked(False)
+ self.isSticky.toggled.connect(self._doSticky)
- self.fntValue = QFont()
- self.fntValue.setPointSizeF(self.pS)
+ self.refList = QLabel("None")
+ self.refList.setWordWrap(True)
+ self.refList.setAlignment(Qt.AlignTop)
+ self.refList.setScaledContents(True)
+ self.refList.linkActivated.connect(self._linkClicked)
- # Label
- self.labelName = QLabel("Label")
- self.labelName.setFont(self.fntLabel)
- self.labelName.setAlignment(Qt.AlignLeft | Qt.AlignBaseline)
+ self.scrollBox = QScrollArea()
+ self.scrollBox.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.scrollBox.setFrameStyle(QFrame.NoFrame)
+ self.scrollBox.setWidgetResizable(True)
+ self.scrollBox.setFixedHeight(80)
+ self.scrollBox.setWidget(self.refList)
- self.labelFlag = QLabel("")
- self.labelFlag.setAlignment(Qt.AlignRight | Qt.AlignBaseline)
+ self.outerBox.addWidget(self.showHide, 0, 0)
+ self.outerBox.addWidget(self.refLabel, 0, 1)
+ self.outerBox.addWidget(self.isSticky, 0, 2)
+ self.outerBox.addWidget(self.scrollBox, 1, 1, 1, 2)
+ self.outerBox.setColumnStretch(1, 1)
- self.labelData = QLabel("")
- self.labelData.setFont(self.fntValue)
- self.labelData.setAlignment(Qt.AlignLeft | Qt.AlignBaseline)
- self.labelData.setWordWrap(True)
+ self.setLayout(self.outerBox)
+ self.setContentsMargins(0,0,0,0)
- # Status
- self.statusName = QLabel("Status")
- self.statusName.setFont(self.fntLabel)
- self.statusName.setAlignment(Qt.AlignLeft)
+ self._doShowHide(self.mainConf.showRefPanel)
- self.statusFlag = QLabel("")
- self.statusFlag.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
-
- self.statusData = QLabel("")
- self.statusData.setFont(self.fntValue)
- self.statusData.setAlignment(Qt.AlignLeft)
-
- # Class
- self.className = QLabel("Class")
- self.className.setFont(self.fntLabel)
- self.className.setAlignment(Qt.AlignLeft)
-
- self.classFlag = QLabel("")
- self.classFlag.setFont(self.fntValue)
- self.classFlag.setAlignment(Qt.AlignRight)
-
- self.classData = QLabel("")
- self.classData.setFont(self.fntValue)
- self.classData.setAlignment(Qt.AlignLeft)
-
- # Layout
- self.layoutName = QLabel("Layout")
- self.layoutName.setFont(self.fntLabel)
- self.layoutName.setAlignment(Qt.AlignLeft)
-
- self.layoutFlag = QLabel("")
- self.layoutFlag.setFont(self.fntValue)
- self.layoutFlag.setAlignment(Qt.AlignRight)
-
- self.layoutData = QLabel("")
- self.layoutData.setFont(self.fntValue)
- self.layoutData.setAlignment(Qt.AlignLeft)
-
- # Character Count
- self.cCountName = QLabel(" Characters")
- self.cCountName.setFont(self.fntLabel)
- self.cCountName.setAlignment(Qt.AlignRight)
-
- self.cCountData = QLabel("")
- self.cCountData.setFont(self.fntValue)
- self.cCountData.setAlignment(Qt.AlignRight)
-
- # Word Count
- self.wCountName = QLabel(" Words")
- self.wCountName.setFont(self.fntLabel)
- self.wCountName.setAlignment(Qt.AlignRight)
-
- self.wCountData = QLabel("")
- self.wCountData.setFont(self.fntValue)
- self.wCountData.setAlignment(Qt.AlignRight)
-
- # Paragraph Count
- self.pCountName = QLabel(" Paragraphs")
- self.pCountName.setFont(self.fntLabel)
- self.pCountName.setAlignment(Qt.AlignRight)
-
- self.pCountData = QLabel("")
- self.pCountData.setFont(self.fntValue)
- self.pCountData.setAlignment(Qt.AlignRight)
-
- # Assemble
- self.mainBox.addWidget(self.labelName, 0, 0, 1, 1)
- self.mainBox.addWidget(self.labelFlag, 0, 1, 1, 1)
- self.mainBox.addWidget(self.labelData, 0, 2, 1, 3)
-
- self.mainBox.addWidget(self.statusName, 1, 0, 1, 1)
- self.mainBox.addWidget(self.statusFlag, 1, 1, 1, 1)
- self.mainBox.addWidget(self.statusData, 1, 2, 1, 1)
- self.mainBox.addWidget(self.cCountName, 1, 3, 1, 1)
- self.mainBox.addWidget(self.cCountData, 1, 4, 1, 1)
-
- self.mainBox.addWidget(self.className, 2, 0, 1, 1)
- self.mainBox.addWidget(self.classFlag, 2, 1, 1, 1)
- self.mainBox.addWidget(self.classData, 2, 2, 1, 1)
- self.mainBox.addWidget(self.wCountName, 2, 3, 1, 1)
- self.mainBox.addWidget(self.wCountData, 2, 4, 1, 1)
-
- self.mainBox.addWidget(self.layoutName, 3, 0, 1, 1)
- self.mainBox.addWidget(self.layoutFlag, 3, 1, 1, 1)
- self.mainBox.addWidget(self.layoutData, 3, 2, 1, 1)
- self.mainBox.addWidget(self.pCountName, 3, 3, 1, 1)
- self.mainBox.addWidget(self.pCountData, 3, 4, 1, 1)
-
- self.mainBox.setColumnStretch(0,0)
- self.mainBox.setColumnStretch(1,0)
- self.mainBox.setColumnStretch(2,1)
- self.mainBox.setColumnStretch(3,0)
- self.mainBox.setColumnStretch(4,0)
-
- # Make sure the columns for flags and counts don't resize too often
- flagWidth = self.theTheme.getTextWidth("Mm", self.fntValue)
- countWidth = self.theTheme.getTextWidth("99,999", self.fntValue)
- self.mainBox.setColumnMinimumWidth(1, flagWidth)
- self.mainBox.setColumnMinimumWidth(4, countWidth)
-
- logger.debug("DocDetails initialisation complete")
+ logger.debug("DocViewDetails initialisation complete")
return
- ###
- # Class Methods
+ def refreshReferences(self, tHandle):
+ """Update the current list of document references from the
+ project index.
+ """
+ self.currHandle = tHandle
+
+ if self.isSticky.isChecked():
+ return
+
+ theRefs = self.theParent.theIndex.getBackReferenceList(tHandle)
+ theList = []
+ for tHandle in theRefs:
+ tItem = self.theProject.projTree[tHandle]
+ if tItem is not None:
+ theList.append("%s" % (
+ tHandle, theRefs[tHandle], tItem.itemName
+ ))
+
+ self.refList.setText(", ".join(theList))
+ self.refList.adjustSize()
+
+ return
+
+ ##
+ # Internal Functions
##
- def updateCounts(self, tHandle, cC, wC, pC):
- """Just update the counts if the handle is the same as the one
- we're already showing.
+ def _linkClicked(self, theLink):
+ """Capture the link-click and forward it to the document viewer
+ class for handling.
"""
- if tHandle == self.theHandle:
- self.cCountData.setText("{:n}".format(cC))
- self.wCountData.setText("{:n}".format(wC))
- self.pCountData.setText("{:n}".format(pC))
+ logger.verbose("Clicked link: '%s'" % theLink)
+ if len(theLink) == 27:
+ tHandle = theLink[6:19]
+ self.theParent.viewDocument(tHandle, theLink)
return
- def updateViewBox(self, tHandle):
- """Populate the details box from a given handle.
+ def _doShowHide(self, chState):
+ """Toggle the expand/collapse of the panel.
"""
- self.theHandle = tHandle
- nwItem = self.theProject.projTree[tHandle]
-
- if nwItem is None:
- self.labelFlag.setText("")
- self.labelData.setText("")
- self.statusFlag.setText("")
- self.statusData.setText("")
- self.classFlag.setText("")
- self.classData.setText("")
- self.layoutFlag.setText("")
- self.layoutData.setText("")
- self.cCountData.setText("–")
- self.wCountData.setText("–")
- self.pCountData.setText("–")
-
+ self.scrollBox.setVisible(chState)
+ self.mainConf.setShowRefPanel(chState)
+ if chState:
+ self.showHide.setArrowType(Qt.DownArrow)
else:
- theLabel = nwItem.itemName
- if len(theLabel) > 100:
- theLabel = theLabel[:96].rstrip()+" ..."
-
- iStatus = nwItem.itemStatus
- if nwItem.itemClass == nwItemClass.NOVEL:
- iStatus = self.theProject.statusItems.checkEntry(iStatus) # Make sure it's valid
- flagIcon = self.theParent.statusIcons[iStatus]
- else:
- iStatus = self.theProject.importItems.checkEntry(iStatus) # Make sure it's valid
- flagIcon = self.theParent.importIcons[iStatus]
-
- if nwItem.itemType == nwItemType.FILE:
- if nwItem.isExported:
- self.labelFlag.setPixmap(self.expCheck)
- else:
- self.labelFlag.setPixmap(self.expCross)
- else:
- self.labelFlag.setPixmap(QPixmap(1,1))
- self.statusFlag.setPixmap(flagIcon.pixmap(self.sPx, self.sPx))
- self.classFlag.setText(nwLabels.CLASS_FLAG[nwItem.itemClass])
- if nwItem.itemLayout == nwItemLayout.NO_LAYOUT:
- self.layoutFlag.setText("-")
- else:
- self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout])
-
- self.labelData.setText(theLabel)
- self.statusData.setText(nwItem.itemStatus)
- self.classData.setText(nwLabels.CLASS_NAME[nwItem.itemClass])
- self.layoutData.setText(nwLabels.LAYOUT_NAME[nwItem.itemLayout])
-
- if nwItem.itemType == nwItemType.FILE:
- self.cCountData.setText("{:n}".format(nwItem.charCount))
- self.wCountData.setText("{:n}".format(nwItem.wordCount))
- self.pCountData.setText("{:n}".format(nwItem.paraCount))
- else:
- self.cCountData.setText("–")
- self.wCountData.setText("–")
- self.pCountData.setText("–")
-
+ self.showHide.setArrowType(Qt.RightArrow)
return
-# END Class GuiDocDetails
+ def _doSticky(self, chState):
+ """Toggle the sticky feature of the references.
+ """
+ if not chState and self.currHandle is not None:
+ self.refreshReferences(self.currHandle)
+ return
+
+# END Class GuiDocViewDetails
diff --git a/nw/gui/itemdetails.py b/nw/gui/itemdetails.py
new file mode 100644
index 00000000..93ffb788
--- /dev/null
+++ b/nw/gui/itemdetails.py
@@ -0,0 +1,265 @@
+# -*- coding: utf-8 -*-
+"""novelWriter GUI Document Details
+
+ novelWriter – GUI Document Details
+====================================
+ Class holding the left side document details panel
+
+ File History:
+ Created: 2019-04-24 [0.0.1]
+
+ This file is a part of novelWriter
+ Copyright 2020, 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 nw
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QFont, QIcon, QPixmap
+from PyQt5.QtWidgets import QFrame, QGridLayout, QLabel
+
+from nw.constants import (
+ nwLabels, nwItemClass, nwItemType, nwItemLayout, nwUnicode
+)
+
+logger = logging.getLogger(__name__)
+
+class GuiItemDetails(QFrame):
+
+ def __init__(self, theParent, theProject):
+ QFrame.__init__(self, theParent)
+
+ logger.debug("Initialising GuiItemDetails ...")
+ self.mainConf = nw.CONFIG
+ self.theParent = theParent
+ self.theProject = theProject
+ self.theTheme = theParent.theTheme
+ self.theHandle = None
+
+ self.mainBox = QGridLayout(self)
+ self.mainBox.setVerticalSpacing(1)
+ self.mainBox.setHorizontalSpacing(6)
+ self.setLayout(self.mainBox)
+
+ self.pS = 0.9*self.theTheme.fontPointSize
+ self.iPx = self.theTheme.textIconSize
+ self.sPx = int(round(0.8*self.theTheme.textIconSize))
+
+ self.expCheck = self.theTheme.getPixmap("check", (self.iPx, self.iPx))
+ self.expCross = self.theTheme.getPixmap("cross", (self.iPx, self.iPx))
+
+ self.fntLabel = QFont()
+ self.fntLabel.setBold(True)
+ self.fntLabel.setPointSizeF(self.pS)
+
+ self.fntValue = QFont()
+ self.fntValue.setPointSizeF(self.pS)
+
+ # Label
+ self.labelName = QLabel("Label")
+ self.labelName.setFont(self.fntLabel)
+ self.labelName.setAlignment(Qt.AlignLeft | Qt.AlignBaseline)
+
+ self.labelFlag = QLabel("")
+ self.labelFlag.setAlignment(Qt.AlignRight | Qt.AlignBaseline)
+
+ self.labelData = QLabel("")
+ self.labelData.setFont(self.fntValue)
+ self.labelData.setAlignment(Qt.AlignLeft | Qt.AlignBaseline)
+ self.labelData.setWordWrap(True)
+
+ # Status
+ self.statusName = QLabel("Status")
+ self.statusName.setFont(self.fntLabel)
+ self.statusName.setAlignment(Qt.AlignLeft)
+
+ self.statusFlag = QLabel("")
+ self.statusFlag.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
+
+ self.statusData = QLabel("")
+ self.statusData.setFont(self.fntValue)
+ self.statusData.setAlignment(Qt.AlignLeft)
+
+ # Class
+ self.className = QLabel("Class")
+ self.className.setFont(self.fntLabel)
+ self.className.setAlignment(Qt.AlignLeft)
+
+ self.classFlag = QLabel("")
+ self.classFlag.setFont(self.fntValue)
+ self.classFlag.setAlignment(Qt.AlignRight)
+
+ self.classData = QLabel("")
+ self.classData.setFont(self.fntValue)
+ self.classData.setAlignment(Qt.AlignLeft)
+
+ # Layout
+ self.layoutName = QLabel("Layout")
+ self.layoutName.setFont(self.fntLabel)
+ self.layoutName.setAlignment(Qt.AlignLeft)
+
+ self.layoutFlag = QLabel("")
+ self.layoutFlag.setFont(self.fntValue)
+ self.layoutFlag.setAlignment(Qt.AlignRight)
+
+ self.layoutData = QLabel("")
+ self.layoutData.setFont(self.fntValue)
+ self.layoutData.setAlignment(Qt.AlignLeft)
+
+ # Character Count
+ self.cCountName = QLabel(" Characters")
+ self.cCountName.setFont(self.fntLabel)
+ self.cCountName.setAlignment(Qt.AlignRight)
+
+ self.cCountData = QLabel("")
+ self.cCountData.setFont(self.fntValue)
+ self.cCountData.setAlignment(Qt.AlignRight)
+
+ # Word Count
+ self.wCountName = QLabel(" Words")
+ self.wCountName.setFont(self.fntLabel)
+ self.wCountName.setAlignment(Qt.AlignRight)
+
+ self.wCountData = QLabel("")
+ self.wCountData.setFont(self.fntValue)
+ self.wCountData.setAlignment(Qt.AlignRight)
+
+ # Paragraph Count
+ self.pCountName = QLabel(" Paragraphs")
+ self.pCountName.setFont(self.fntLabel)
+ self.pCountName.setAlignment(Qt.AlignRight)
+
+ self.pCountData = QLabel("")
+ self.pCountData.setFont(self.fntValue)
+ self.pCountData.setAlignment(Qt.AlignRight)
+
+ # Assemble
+ self.mainBox.addWidget(self.labelName, 0, 0, 1, 1)
+ self.mainBox.addWidget(self.labelFlag, 0, 1, 1, 1)
+ self.mainBox.addWidget(self.labelData, 0, 2, 1, 3)
+
+ self.mainBox.addWidget(self.statusName, 1, 0, 1, 1)
+ self.mainBox.addWidget(self.statusFlag, 1, 1, 1, 1)
+ self.mainBox.addWidget(self.statusData, 1, 2, 1, 1)
+ self.mainBox.addWidget(self.cCountName, 1, 3, 1, 1)
+ self.mainBox.addWidget(self.cCountData, 1, 4, 1, 1)
+
+ self.mainBox.addWidget(self.className, 2, 0, 1, 1)
+ self.mainBox.addWidget(self.classFlag, 2, 1, 1, 1)
+ self.mainBox.addWidget(self.classData, 2, 2, 1, 1)
+ self.mainBox.addWidget(self.wCountName, 2, 3, 1, 1)
+ self.mainBox.addWidget(self.wCountData, 2, 4, 1, 1)
+
+ self.mainBox.addWidget(self.layoutName, 3, 0, 1, 1)
+ self.mainBox.addWidget(self.layoutFlag, 3, 1, 1, 1)
+ self.mainBox.addWidget(self.layoutData, 3, 2, 1, 1)
+ self.mainBox.addWidget(self.pCountName, 3, 3, 1, 1)
+ self.mainBox.addWidget(self.pCountData, 3, 4, 1, 1)
+
+ self.mainBox.setColumnStretch(0,0)
+ self.mainBox.setColumnStretch(1,0)
+ self.mainBox.setColumnStretch(2,1)
+ self.mainBox.setColumnStretch(3,0)
+ self.mainBox.setColumnStretch(4,0)
+
+ # Make sure the columns for flags and counts don't resize too often
+ flagWidth = self.theTheme.getTextWidth("Mm", self.fntValue)
+ countWidth = self.theTheme.getTextWidth("99,999", self.fntValue)
+ self.mainBox.setColumnMinimumWidth(1, flagWidth)
+ self.mainBox.setColumnMinimumWidth(4, countWidth)
+
+ logger.debug("GuiItemDetails initialisation complete")
+
+ return
+
+ ###
+ # Class Methods
+ ##
+
+ def updateCounts(self, tHandle, cC, wC, pC):
+ """Just update the counts if the handle is the same as the one
+ we're already showing.
+ """
+ if tHandle == self.theHandle:
+ self.cCountData.setText("{:n}".format(cC))
+ self.wCountData.setText("{:n}".format(wC))
+ self.pCountData.setText("{:n}".format(pC))
+ return
+
+ def updateViewBox(self, tHandle):
+ """Populate the details box from a given handle.
+ """
+ self.theHandle = tHandle
+ nwItem = self.theProject.projTree[tHandle]
+
+ if nwItem is None:
+ self.labelFlag.setText("")
+ self.labelData.setText("")
+ self.statusFlag.setText("")
+ self.statusData.setText("")
+ self.classFlag.setText("")
+ self.classData.setText("")
+ self.layoutFlag.setText("")
+ self.layoutData.setText("")
+ self.cCountData.setText("–")
+ self.wCountData.setText("–")
+ self.pCountData.setText("–")
+
+ else:
+ theLabel = nwItem.itemName
+ if len(theLabel) > 100:
+ theLabel = theLabel[:96].rstrip()+" ..."
+
+ iStatus = nwItem.itemStatus
+ if nwItem.itemClass == nwItemClass.NOVEL:
+ iStatus = self.theProject.statusItems.checkEntry(iStatus) # Make sure it's valid
+ flagIcon = self.theParent.statusIcons[iStatus]
+ else:
+ iStatus = self.theProject.importItems.checkEntry(iStatus) # Make sure it's valid
+ flagIcon = self.theParent.importIcons[iStatus]
+
+ if nwItem.itemType == nwItemType.FILE:
+ if nwItem.isExported:
+ self.labelFlag.setPixmap(self.expCheck)
+ else:
+ self.labelFlag.setPixmap(self.expCross)
+ else:
+ self.labelFlag.setPixmap(QPixmap(1,1))
+ self.statusFlag.setPixmap(flagIcon.pixmap(self.sPx, self.sPx))
+ self.classFlag.setText(nwLabels.CLASS_FLAG[nwItem.itemClass])
+ if nwItem.itemLayout == nwItemLayout.NO_LAYOUT:
+ self.layoutFlag.setText("-")
+ else:
+ self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout])
+
+ self.labelData.setText(theLabel)
+ self.statusData.setText(nwItem.itemStatus)
+ self.classData.setText(nwLabels.CLASS_NAME[nwItem.itemClass])
+ self.layoutData.setText(nwLabels.LAYOUT_NAME[nwItem.itemLayout])
+
+ if nwItem.itemType == nwItemType.FILE:
+ self.cCountData.setText("{:n}".format(nwItem.charCount))
+ self.wCountData.setText("{:n}".format(nwItem.wordCount))
+ self.pCountData.setText("{:n}".format(nwItem.paraCount))
+ else:
+ self.cCountData.setText("–")
+ self.wCountData.setText("–")
+ self.pCountData.setText("–")
+
+ return
+
+# END Class GuiItemDetails
diff --git a/nw/gui/outline.py b/nw/gui/outline.py
index 982f21c9..12c26383 100644
--- a/nw/gui/outline.py
+++ b/nw/gui/outline.py
@@ -30,7 +30,7 @@ import nw
from time import time
-from PyQt5.QtCore import Qt
+from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QMenu, QAction, QAbstractItemView
)
@@ -43,7 +43,6 @@ class GuiProjectOutline(QTreeWidget):
DEF_WIDTH = {
nwOutline.TITLE : 200,
- nwOutline.HANDLE : 0,
nwOutline.LEVEL : 40,
nwOutline.LABEL : 150,
nwOutline.LINE : 40,
@@ -63,7 +62,6 @@ class GuiProjectOutline(QTreeWidget):
DEF_HIDDEN = {
nwOutline.TITLE : False,
- nwOutline.HANDLE : True,
nwOutline.LEVEL : True,
nwOutline.LABEL : False,
nwOutline.LINE : True,
@@ -103,6 +101,10 @@ class GuiProjectOutline(QTreeWidget):
self.setDragEnabled(False)
self.itemDoubleClicked.connect(self._treeDoubleClick)
+ iPx = self.theTheme.textIconSize
+ self.setIconSize(QSize(iPx, iPx))
+ self.setIndentation(iPx)
+
self.treeHead = self.header()
self.treeHead.setContextMenuPolicy(Qt.CustomContextMenu)
self.treeHead.customContextMenuRequested.connect(self._headerRightClick)
@@ -187,7 +189,7 @@ class GuiProjectOutline(QTreeWidget):
clicked, and send it to the main gui class for opening in the
document editor.
"""
- tHandle = tItem.text(self.colIndex[nwOutline.HANDLE])
+ tHandle = tItem.data(self.colIndex[nwOutline.TITLE], Qt.UserRole)
try:
tLine = int(tItem.text(self.colIndex[nwOutline.LINE]))
except:
@@ -332,7 +334,6 @@ class GuiProjectOutline(QTreeWidget):
# Make sure title column is always visible,
# and handle column always hidden
self.setColumnHidden(self.colIndex[nwOutline.TITLE], False)
- self.setColumnHidden(self.colIndex[nwOutline.HANDLE], True)
headItem = self.headerItem()
headItem.setTextAlignment(self.colIndex[nwOutline.CCOUNT], Qt.AlignRight)
@@ -408,9 +409,11 @@ class GuiProjectOutline(QTreeWidget):
newItem = QTreeWidgetItem()
newItem.setText(self.colIndex[nwOutline.TITLE], novIdx["title"])
- newItem.setText(self.colIndex[nwOutline.HANDLE], tHandle)
+ newItem.setData(self.colIndex[nwOutline.TITLE], Qt.UserRole, tHandle)
+ newItem.setIcon(self.colIndex[nwOutline.TITLE], self.theTheme.getIcon("hash"))
newItem.setText(self.colIndex[nwOutline.LEVEL], novIdx["level"])
newItem.setText(self.colIndex[nwOutline.LABEL], nwItem.itemName)
+ newItem.setIcon(self.colIndex[nwOutline.LABEL], self.theTheme.getIcon("proj_document"))
newItem.setText(self.colIndex[nwOutline.LINE], sTitle[1:].lstrip("0"))
newItem.setText(self.colIndex[nwOutline.SYNOP], novIdx["synopsis"])
newItem.setText(self.colIndex[nwOutline.CCOUNT], str(novIdx["cCount"]))
@@ -451,7 +454,7 @@ class GuiOutlineHeaderMenu(QMenu):
self.actionMap = {}
for hItem in nwOutline:
- if hItem == nwOutline.TITLE or hItem == nwOutline.HANDLE:
+ if hItem == nwOutline.TITLE:
continue
self.actionMap[hItem] = QAction(nwLabels.OUTLINE_COLS[hItem], self)
self.actionMap[hItem].setCheckable(True)
@@ -469,7 +472,7 @@ class GuiOutlineHeaderMenu(QMenu):
self.acceptToggle = False
for hItem in nwOutline:
- if hItem == nwOutline.TITLE or hItem == nwOutline.HANDLE or hItem not in hiddenState:
+ if hItem == nwOutline.TITLE or hItem not in hiddenState:
continue
self.actionMap[hItem].setChecked(not hiddenState[hItem])
diff --git a/nw/gui/projectload.py b/nw/gui/projload.py
similarity index 100%
rename from nw/gui/projectload.py
rename to nw/gui/projload.py
diff --git a/nw/gui/projectsettings.py b/nw/gui/projsettings.py
similarity index 100%
rename from nw/gui/projectsettings.py
rename to nw/gui/projsettings.py
diff --git a/nw/gui/doctree.py b/nw/gui/projtree.py
similarity index 92%
rename from nw/gui/doctree.py
rename to nw/gui/projtree.py
index 2f4477af..72246c3a 100644
--- a/nw/gui/doctree.py
+++ b/nw/gui/projtree.py
@@ -31,7 +31,8 @@ import nw
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QFont, QColor, QIcon
from PyQt5.QtWidgets import (
- qApp, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMessageBox
+ qApp, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMessageBox,
+ QHeaderView
)
from nw.core import NWDoc
@@ -41,18 +42,17 @@ from nw.constants import (
logger = logging.getLogger(__name__)
-class GuiDocTree(QTreeWidget):
+class GuiProjectTree(QTreeWidget):
C_NAME = 0
C_COUNT = 1
C_EXPORT = 2
C_FLAGS = 3
- C_HANDLE = 4
def __init__(self, theParent, theProject):
QTreeWidget.__init__(self, theParent)
- logger.debug("Initialising GuiDocTree ...")
+ logger.debug("Initialising GuiProjectTree ...")
self.mainConf = nw.CONFIG
self.theParent = theParent
self.theTheme = theParent.theTheme
@@ -69,24 +69,30 @@ class GuiDocTree(QTreeWidget):
self.setIconSize(QSize(iPx, iPx))
self.setExpandsOnDoubleClick(True)
self.setIndentation(iPx)
- self.setColumnCount(5)
- self.setHeaderLabels(["Label", "Words", "Inc", "Flags", "Handle"])
- self.hideColumn(self.C_HANDLE)
+ self.setColumnCount(4)
+ self.setHeaderLabels(["Label", "Words", "Inc", "Flags"])
- treeHead = self.headerItem()
- treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
- treeHead.setToolTip(self.C_NAME, "Item label")
- treeHead.setToolTip(self.C_COUNT, "Word count")
- treeHead.setToolTip(self.C_EXPORT, "Include in build")
- treeHead.setToolTip(self.C_FLAGS, "Status, class, and layout flags")
+ treeHeadItem = self.headerItem()
+ treeHeadItem.setTextAlignment(self.C_COUNT, Qt.AlignRight)
+ treeHeadItem.setToolTip(self.C_NAME, "Item label")
+ treeHeadItem.setToolTip(self.C_COUNT, "Word count")
+ treeHeadItem.setToolTip(self.C_EXPORT, "Include in build")
+ treeHeadItem.setToolTip(self.C_FLAGS, "Status, class, and layout flags")
# Force the font to fix font sizing issues on some platforms
# like Ubuntu. This must also be set when the rows are added.
self.setFont(self.theTheme.guiFont)
- treeHead.setFont(self.C_NAME, self.theTheme.guiFont)
- treeHead.setFont(self.C_COUNT,self.theTheme.guiFont)
- treeHead.setFont(self.C_EXPORT, self.theTheme.guiFont)
- treeHead.setFont(self.C_FLAGS, self.theTheme.guiFont)
+ treeHeadItem.setFont(self.C_NAME, self.theTheme.guiFont)
+ treeHeadItem.setFont(self.C_COUNT,self.theTheme.guiFont)
+ treeHeadItem.setFont(self.C_EXPORT, self.theTheme.guiFont)
+ treeHeadItem.setFont(self.C_FLAGS, self.theTheme.guiFont)
+
+ # Let the last column stretch, and set the minimum size to the
+ # size of the icon as the default Qt font metrics approach fails
+ # for some fonts like the Ubuntu font.
+ treeHeader = self.header()
+ treeHeader.setStretchLastSection(True)
+ treeHeader.setMinimumSectionSize(iPx+6)
# Allow Move by Drag & Drop
self.setDragEnabled(True)
@@ -102,13 +108,14 @@ class GuiDocTree(QTreeWidget):
# self.setSelectionBehavior(QAbstractItemView.SelectRows)
# Get user's column width preferences for NAME and COUNT
- for colN, colW in enumerate(self.mainConf.treeColWidth):
- self.setColumnWidth(colN, colW)
+ if len(self.mainConf.treeColWidth) <= 4:
+ for colN, colW in enumerate(self.mainConf.treeColWidth):
+ self.setColumnWidth(colN, colW)
- self.resizeColumnToContents(self.C_EXPORT)
+ # The last column should just auto-scale
self.resizeColumnToContents(self.C_FLAGS)
- logger.debug("GuiDocTree initialisation complete")
+ logger.debug("GuiProjectTree initialisation complete")
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
@@ -286,6 +293,7 @@ class GuiDocTree(QTreeWidget):
retVals = [
self.columnWidth(0),
self.columnWidth(1),
+ self.columnWidth(2),
]
return retVals
@@ -479,7 +487,7 @@ class GuiDocTree(QTreeWidget):
pCount = 0
for i in range(pItem.childCount()):
pCount += int(pItem.child(i).text(self.C_COUNT))
- pHandle = pItem.text(self.C_HANDLE)
+ pHandle = pItem.data(self.C_NAME, Qt.UserRole)
if not nDepth > 200 and pHandle != "":
self.propagateCount(pHandle, pCount, nDepth+1)
return
@@ -521,17 +529,17 @@ class GuiDocTree(QTreeWidget):
if len(selItem) == 0:
return None
if isinstance(selItem[0], QTreeWidgetItem):
- return selItem[0].text(self.C_HANDLE)
+ return selItem[0].data(self.C_NAME, Qt.UserRole)
return None
def getSelectedHandles(self):
"""Return a list of all currently selected item handles.
"""
- selItems = self.selectedItems()
+ selItems = self.selectedItems()
selHandles = []
for n in range(len(selItems)):
if isinstance(selItems[n], QTreeWidgetItem):
- selHandles.append(selItems[n].text(self.C_HANDLE))
+ selHandles.append(selItems[n].data(self.C_NAME, Qt.UserRole))
return selHandles
def setSelectedHandle(self, tHandle):
@@ -563,8 +571,8 @@ class GuiDocTree(QTreeWidget):
"""This is a recursive function returning all items in a tree
starting at a given QTreeWidgetItem.
"""
- tHandle = theItem.text(self.C_HANDLE)
- nwItem = self.theProject.projTree[tHandle]
+ tHandle = theItem.data(self.C_NAME, Qt.UserRole)
+ nwItem = self.theProject.projTree[tHandle]
nwItem.setExpanded(theItem.isExpanded())
nwItem.setOrder(theIndex)
theList.append(tHandle)
@@ -585,7 +593,6 @@ class GuiDocTree(QTreeWidget):
newItem.setText(self.C_COUNT, "0")
newItem.setText(self.C_EXPORT, "")
newItem.setText(self.C_FLAGS, "")
- newItem.setText(self.C_HANDLE, tHandle)
newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
@@ -596,6 +603,8 @@ class GuiDocTree(QTreeWidget):
newItem.setFont(self.C_COUNT, self.theTheme.guiFont)
newItem.setFont(self.C_FLAGS, self.theTheme.guiFont)
+ newItem.setData(self.C_NAME, Qt.UserRole, tHandle)
+
self.theMap[tHandle] = newItem
if pHandle is None:
if nwItem.itemType == nwItemType.ROOT:
@@ -649,10 +658,10 @@ class GuiDocTree(QTreeWidget):
newItem.setText(self.C_COUNT, "")
newItem.setText(self.C_EXPORT, "")
newItem.setText(self.C_FLAGS, "")
- newItem.setText(self.C_HANDLE, "")
self.addTopLevelItem(newItem)
self.orphRoot = newItem
newItem.setExpanded(True)
+ newItem.setData(self.C_NAME, "")
newItem.setIcon(self.C_NAME, self.theTheme.getIcon("warning"))
return
@@ -677,7 +686,7 @@ class GuiDocTree(QTreeWidget):
logger.error("Failed to find new parent item of %s" % tHandle)
return False
- pHandle = trItemP.text(self.C_HANDLE)
+ pHandle = trItemP.data(self.C_NAME, Qt.UserRole)
wC = int(trItemS.text(self.C_COUNT))
self.propagateCount(tHandle, -wC)
nwItemS.setParent(pHandle)
@@ -702,7 +711,7 @@ class GuiDocTree(QTreeWidget):
if trItemP is None:
logger.error("Failed to find new parent item of %s" % tHandle)
return
- pHandle = trItemP.text(self.C_HANDLE)
+ pHandle = trItemP.data(self.C_NAME, Qt.UserRole)
nwItemS.setParent(pHandle)
self.setTreeItemValues(tHandle)
self.theProject.setProjectChanged(True)
@@ -737,7 +746,7 @@ class GuiDocTree(QTreeWidget):
return
dItem = self.itemFromIndex(dIndex)
- dHandle = dItem.text(self.C_HANDLE)
+ dHandle = dItem.data(self.C_NAME, Qt.UserRole)
snItem = self.theProject.projTree[sHandle]
dnItem = self.theProject.projTree[dHandle]
if dnItem is None:
@@ -773,4 +782,4 @@ class GuiDocTree(QTreeWidget):
return
-# END Class GuiDocTree
+# END Class GuiProjectTree
diff --git a/nw/gui/theme.py b/nw/gui/theme.py
index 8613c683..49ea367c 100644
--- a/nw/gui/theme.py
+++ b/nw/gui/theme.py
@@ -516,6 +516,7 @@ class GuiIcons:
"edit" : (None, None),
"check" : (None, None),
"cross" : (None, None),
+ "hash" : (None, None),
## Other Icons
"warning" : (QStyle.SP_MessageBoxWarning, "dialog-warning"),
diff --git a/nw/gui/viewdetails.py b/nw/gui/viewdetails.py
deleted file mode 100644
index d4b2621c..00000000
--- a/nw/gui/viewdetails.py
+++ /dev/null
@@ -1,151 +0,0 @@
-# -*- coding: utf-8 -*-
-"""novelWriter GUI DocView Details
-
- novelWriter – GUI DocView Details
-===================================
- Class holding the document view details panel
-
- File History:
- Created: 2019-10-31 [0.3.2]
-
- This file is a part of novelWriter
- Copyright 2020, 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 nw
-
-from PyQt5.QtCore import Qt, QSize
-from PyQt5.QtWidgets import (
- QWidget, QLabel, QScrollArea, QFrame, QToolButton, QCheckBox, QGridLayout
-)
-
-logger = logging.getLogger(__name__)
-
-class GuiDocViewDetails(QWidget):
-
- def __init__(self, theParent, theProject):
- QWidget.__init__(self, theParent)
-
- logger.debug("Initialising DocViewDetails ...")
- self.mainConf = nw.CONFIG
- self.theParent = theParent
- self.theProject = theProject
- self.currHandle = None
-
- self.outerBox = QGridLayout(self)
- self.outerBox.setContentsMargins(0,0,0,0)
- self.outerBox.setHorizontalSpacing(0)
- self.outerBox.setVerticalSpacing(4)
-
- self.refLabel = QLabel("Referenced By", self)
-
- self.showHide = QToolButton()
- self.showHide.setStyleSheet("QToolButton { border: none; }")
- self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly)
- self.showHide.setArrowType(Qt.DownArrow)
- self.showHide.setCheckable(True)
- self.showHide.setIconSize(QSize(16,16))
- self.showHide.toggled.connect(self._doShowHide)
-
- self.isSticky = QCheckBox("Sticky")
- self.isSticky.setChecked(False)
- self.isSticky.toggled.connect(self._doSticky)
-
- self.refList = QLabel("None")
- self.refList.setWordWrap(True)
- self.refList.setAlignment(Qt.AlignTop)
- self.refList.setScaledContents(True)
- self.refList.linkActivated.connect(self._linkClicked)
-
- self.scrollBox = QScrollArea()
- self.scrollBox.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
- self.scrollBox.setFrameStyle(QFrame.NoFrame)
- self.scrollBox.setWidgetResizable(True)
- self.scrollBox.setFixedHeight(80)
- self.scrollBox.setWidget(self.refList)
-
- self.outerBox.addWidget(self.showHide, 0, 0)
- self.outerBox.addWidget(self.refLabel, 0, 1)
- self.outerBox.addWidget(self.isSticky, 0, 2)
- self.outerBox.addWidget(self.scrollBox, 1, 1, 1, 2)
- self.outerBox.setColumnStretch(1, 1)
-
- self.setLayout(self.outerBox)
- self.setContentsMargins(0,0,0,0)
-
- self._doShowHide(self.mainConf.showRefPanel)
-
- logger.debug("DocViewDetails initialisation complete")
-
- return
-
- def refreshReferences(self, tHandle):
- """Update the current list of document references from the
- project index.
- """
- self.currHandle = tHandle
-
- if self.isSticky.isChecked():
- return
-
- theRefs = self.theParent.theIndex.getBackReferenceList(tHandle)
- theList = []
- for tHandle in theRefs:
- tItem = self.theProject.projTree[tHandle]
- if tItem is not None:
- theList.append("%s" % (
- tHandle, theRefs[tHandle], tItem.itemName
- ))
-
- self.refList.setText(", ".join(theList))
- self.refList.adjustSize()
-
- return
-
- ##
- # Internal Functions
- ##
-
- def _linkClicked(self, theLink):
- """Capture the link-click and forward it to the document viewer
- class for handling.
- """
- logger.verbose("Clicked link: '%s'" % theLink)
- if len(theLink) == 27:
- tHandle = theLink[6:19]
- self.theParent.viewDocument(tHandle, theLink)
- return
-
- def _doShowHide(self, chState):
- """Toggle the expand/collapse of the panel.
- """
- self.scrollBox.setVisible(chState)
- self.mainConf.setShowRefPanel(chState)
- if chState:
- self.showHide.setArrowType(Qt.DownArrow)
- else:
- self.showHide.setArrowType(Qt.RightArrow)
- return
-
- def _doSticky(self, chState):
- """Toggle the sticky feature of the references.
- """
- if not chState and self.currHandle is not None:
- self.refreshReferences(self.currHandle)
- return
-
-# END Class GuiDocViewDetails
diff --git a/nw/guimain.py b/nw/guimain.py
index bfc98c71..ea6c050e 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -40,8 +40,8 @@ from PyQt5.QtWidgets import (
)
from nw.gui import (
- GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor,
- GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar, GuiDocViewDetails,
+ GuiMainMenu, GuiMainStatus, GuiTheme, GuiProjectTree, GuiDocEditor,
+ GuiDocViewer, GuiItemDetails, GuiSearchBar, GuiNoticeBar, GuiDocViewDetails,
GuiPreferences, GuiProjectSettings, GuiItemEditor, GuiProjectOutline,
GuiSessionLogView, GuiDocMerge, GuiDocSplit, GuiProjectLoad, GuiBuildNovel
)
@@ -91,12 +91,12 @@ class GuiMain(QMainWindow):
# Main GUI Elements
self.statusBar = GuiMainStatus(self)
self.noticeBar = GuiNoticeBar(self)
- self.treeView = GuiDocTree(self, self.theProject)
+ self.treeView = GuiProjectTree(self, self.theProject)
self.docEditor = GuiDocEditor(self, self.theProject)
self.docViewer = GuiDocViewer(self, self.theProject)
self.viewMeta = GuiDocViewDetails(self, self.theProject)
self.searchBar = GuiSearchBar(self)
- self.treeMeta = GuiDocDetails(self, self.theProject)
+ self.treeMeta = GuiItemDetails(self, self.theProject)
self.projView = GuiProjectOutline(self, self.theProject)
self.mainMenu = GuiMainMenu(self, self.theProject)
@@ -466,14 +466,15 @@ class GuiMain(QMainWindow):
self.docEditor.clearEditor()
return True
- def openDocument(self, tHandle, tLine=None):
+ def openDocument(self, tHandle, tLine=None, changeFocus=True):
"""Open a specific document, optionally at a given line.
"""
if self.hasProject:
self.closeDocument()
self.tabWidget.setCurrentWidget(self.splitView)
if self.docEditor.loadText(tHandle, tLine):
- self.docEditor.setFocus()
+ if changeFocus:
+ self.docEditor.setFocus()
self.theProject.setLastEdited(tHandle)
self.treeView.setSelectedHandle(tHandle)
else:
@@ -1022,7 +1023,7 @@ class GuiMain(QMainWindow):
"""The user double-clicked an item in the tree. If it is a file,
we open it. Otherwise, we do nothing.
"""
- tHandle = tItem.text(self.treeView.C_HANDLE)
+ tHandle = tItem.data(self.treeView.C_NAME, Qt.UserRole)
logger.verbose("User double clicked tree item with handle %s" % tHandle)
nwItem = self.theProject.projTree[tHandle]
if nwItem is not None:
@@ -1035,7 +1036,8 @@ class GuiMain(QMainWindow):
def _treeKeyPressReturn(self):
"""The user pressed return an item in the tree. If it is a file,
- we open it. Otherwise, we do nothing.
+ we open it. Otherwise, we do nothing. Pressing return does not
+ change focus to the editor as double click does.
"""
tHandle = self.treeView.getSelectedHandle()
logger.verbose("User pressed return on tree item with handle %s" % tHandle)
@@ -1043,7 +1045,7 @@ class GuiMain(QMainWindow):
if nwItem is not None:
if nwItem.itemType == nwItemType.FILE:
logger.verbose("Requested item %s is a file" % tHandle)
- self.openDocument(tHandle)
+ self.openDocument(tHandle, changeFocus=False)
else:
logger.verbose("Requested item %s is a folder" % tHandle)
return
diff --git a/tests/test_gui.py b/tests/test_gui.py
index e006cabb..fe52cabd 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -485,7 +485,7 @@ def testBuildTool(qtbot, nwTempBuild, nwLipsum, nwRef, nwTemp):
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton)
qtbot.wait(stepDelay)
- qtbot.mouseClick(nwBuild.excludeBody, Qt.LeftButton)
+ qtbot.mouseClick(nwBuild.includeBody, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)