novelWriter 2.3.1__py3-none-any.whl → 2.4b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/METADATA +1 -1
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/RECORD +81 -70
- novelwriter/__init__.py +5 -5
- novelwriter/assets/icons/typicons_dark/icons.conf +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +7 -0
- novelwriter/assets/icons/typicons_dark/typ_arrow-down.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_arrow-right.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +1 -1
- novelwriter/assets/icons/typicons_dark/typ_refresh.svg +1 -1
- novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_times.svg +1 -1
- novelwriter/assets/icons/typicons_light/icons.conf +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +7 -0
- novelwriter/assets/icons/typicons_light/typ_arrow-down.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_arrow-right.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +1 -1
- novelwriter/assets/icons/typicons_light/typ_refresh.svg +1 -1
- novelwriter/assets/icons/typicons_light/typ_search-grey.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_times.svg +1 -1
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/default_dark.conf +1 -0
- novelwriter/assets/syntax/default_light.conf +1 -0
- novelwriter/assets/syntax/grey_dark.conf +1 -0
- novelwriter/assets/syntax/grey_light.conf +1 -0
- novelwriter/assets/syntax/light_owl.conf +1 -0
- novelwriter/assets/syntax/night_owl.conf +1 -0
- novelwriter/assets/syntax/solarized_dark.conf +1 -0
- novelwriter/assets/syntax/solarized_light.conf +1 -0
- novelwriter/assets/syntax/tomorrow.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
- novelwriter/assets/text/credits_en.htm +25 -23
- novelwriter/common.py +1 -1
- novelwriter/config.py +35 -12
- novelwriter/constants.py +5 -6
- novelwriter/core/buildsettings.py +60 -40
- novelwriter/core/coretools.py +98 -13
- novelwriter/core/docbuild.py +74 -7
- novelwriter/core/document.py +24 -3
- novelwriter/core/index.py +31 -112
- novelwriter/core/project.py +10 -15
- novelwriter/core/sessions.py +2 -2
- novelwriter/core/status.py +4 -4
- novelwriter/core/storage.py +8 -2
- novelwriter/core/tohtml.py +22 -25
- novelwriter/core/tokenizer.py +416 -232
- novelwriter/core/tomd.py +17 -8
- novelwriter/core/toodt.py +65 -7
- novelwriter/core/tree.py +8 -8
- novelwriter/dialogs/docsplit.py +7 -8
- novelwriter/dialogs/preferences.py +3 -6
- novelwriter/enum.py +17 -14
- novelwriter/extensions/modified.py +20 -2
- novelwriter/extensions/versioninfo.py +1 -1
- novelwriter/gui/doceditor.py +257 -279
- novelwriter/gui/dochighlight.py +29 -25
- novelwriter/gui/docviewer.py +139 -148
- novelwriter/gui/docviewerpanel.py +4 -24
- novelwriter/gui/editordocument.py +12 -1
- novelwriter/gui/itemdetails.py +6 -6
- novelwriter/gui/mainmenu.py +37 -16
- novelwriter/gui/noveltree.py +11 -19
- novelwriter/gui/outline.py +43 -20
- novelwriter/gui/projtree.py +35 -43
- novelwriter/gui/search.py +316 -0
- novelwriter/gui/sidebar.py +25 -30
- novelwriter/gui/theme.py +59 -6
- novelwriter/guimain.py +176 -173
- novelwriter/shared.py +26 -1
- novelwriter/text/__init__.py +3 -0
- novelwriter/text/counting.py +137 -0
- novelwriter/tools/manuscript.py +344 -55
- novelwriter/tools/manussettings.py +213 -71
- novelwriter/tools/welcome.py +1 -1
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/WHEEL +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/top_level.txt +0 -0
@@ -26,8 +26,9 @@ from __future__ import annotations
|
|
26
26
|
import logging
|
27
27
|
|
28
28
|
from time import time
|
29
|
+
from collections.abc import Iterable
|
29
30
|
|
30
|
-
from PyQt5.QtGui import QTextCursor, QTextDocument
|
31
|
+
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextDocument
|
31
32
|
from PyQt5.QtCore import QObject, pyqtSlot
|
32
33
|
from PyQt5.QtWidgets import QPlainTextDocumentLayout, qApp
|
33
34
|
from novelwriter import SHARED
|
@@ -113,6 +114,16 @@ class GuiTextDocument(QTextDocument):
|
|
113
114
|
return word, cPos, cLen, SHARED.spelling.suggestWords(word)
|
114
115
|
return "", -1, -1, []
|
115
116
|
|
117
|
+
def iterBlockByType(self, cType: int, maxCount: int = 1000) -> Iterable[QTextBlock]:
|
118
|
+
"""Iterate over all text blocks of a given type."""
|
119
|
+
count = 0
|
120
|
+
for i in range(self.blockCount()):
|
121
|
+
block = self.findBlockByNumber(i)
|
122
|
+
if count < maxCount and block.isValid() and block.userState() & cType > 0:
|
123
|
+
count += 1
|
124
|
+
yield block
|
125
|
+
return None
|
126
|
+
|
116
127
|
##
|
117
128
|
# Public Slots
|
118
129
|
##
|
novelwriter/gui/itemdetails.py
CHANGED
@@ -48,7 +48,7 @@ class GuiItemDetails(QWidget):
|
|
48
48
|
logger.debug("Create: GuiItemDetails")
|
49
49
|
|
50
50
|
# Internal Variables
|
51
|
-
self.
|
51
|
+
self._handle = None
|
52
52
|
|
53
53
|
# Sizes
|
54
54
|
hSp = CONFIG.pxInt(6)
|
@@ -194,7 +194,7 @@ class GuiItemDetails(QWidget):
|
|
194
194
|
|
195
195
|
def clearDetails(self) -> None:
|
196
196
|
"""Clear all the data values."""
|
197
|
-
self.
|
197
|
+
self._handle = None
|
198
198
|
self.labelIcon.clear()
|
199
199
|
self.labelData.clear()
|
200
200
|
self.statusIcon.clear()
|
@@ -210,11 +210,11 @@ class GuiItemDetails(QWidget):
|
|
210
210
|
|
211
211
|
def refreshDetails(self) -> None:
|
212
212
|
"""Reload the content of the details panel."""
|
213
|
-
self.updateViewBox(self.
|
213
|
+
self.updateViewBox(self._handle)
|
214
214
|
|
215
215
|
def updateTheme(self) -> None:
|
216
216
|
"""Update theme elements."""
|
217
|
-
self.updateViewBox(self.
|
217
|
+
self.updateViewBox(self._handle)
|
218
218
|
return
|
219
219
|
|
220
220
|
##
|
@@ -233,7 +233,7 @@ class GuiItemDetails(QWidget):
|
|
233
233
|
self.clearDetails()
|
234
234
|
return
|
235
235
|
|
236
|
-
self.
|
236
|
+
self._handle = tHandle
|
237
237
|
iPx = int(round(0.8*SHARED.theme.baseIconSize))
|
238
238
|
|
239
239
|
# Label
|
@@ -295,7 +295,7 @@ class GuiItemDetails(QWidget):
|
|
295
295
|
"""Update the counts if the handle is the same as the one we're
|
296
296
|
already showing. Otherwise, do nothing.
|
297
297
|
"""
|
298
|
-
if tHandle == self.
|
298
|
+
if tHandle == self._handle:
|
299
299
|
self.cCountData.setText(f"{cC:n}")
|
300
300
|
self.wCountData.setText(f"{wC:n}")
|
301
301
|
self.pCountData.setText(f"{pC:n}")
|
novelwriter/gui/mainmenu.py
CHANGED
@@ -33,9 +33,9 @@ from PyQt5.QtCore import QUrl, pyqtSignal, pyqtSlot
|
|
33
33
|
from PyQt5.QtWidgets import QMenuBar, QAction
|
34
34
|
|
35
35
|
from novelwriter import CONFIG, SHARED
|
36
|
-
from novelwriter.enum import nwDocAction, nwDocInsert, nwWidget
|
37
36
|
from novelwriter.common import openExternalPath
|
38
37
|
from novelwriter.constants import nwConst, trConst, nwKeyWords, nwLabels, nwUnicode
|
38
|
+
from novelwriter.enum import nwDocAction, nwDocInsert, nwView, nwWidget
|
39
39
|
from novelwriter.extensions.eventfilters import StatusTipFilter
|
40
40
|
|
41
41
|
if TYPE_CHECKING: # pragma: no cover
|
@@ -55,6 +55,7 @@ class GuiMainMenu(QMenuBar):
|
|
55
55
|
requestDocInsertText = pyqtSignal(str)
|
56
56
|
requestDocKeyWordInsert = pyqtSignal(str)
|
57
57
|
requestFocusChange = pyqtSignal(nwWidget)
|
58
|
+
requestViewChange = pyqtSignal(nwView)
|
58
59
|
|
59
60
|
def __init__(self, mainGui: GuiMain) -> None:
|
60
61
|
super().__init__(parent=mainGui)
|
@@ -196,12 +197,12 @@ class GuiMainMenu(QMenuBar):
|
|
196
197
|
# Document > Open
|
197
198
|
self.aOpenDoc = self.docuMenu.addAction(self.tr("Open Document"))
|
198
199
|
self.aOpenDoc.setShortcut("Ctrl+O")
|
199
|
-
self.aOpenDoc.triggered.connect(
|
200
|
+
self.aOpenDoc.triggered.connect(self.mainGui.openSelectedItem)
|
200
201
|
|
201
202
|
# Document > Save
|
202
203
|
self.aSaveDoc = self.docuMenu.addAction(self.tr("Save Document"))
|
203
204
|
self.aSaveDoc.setShortcut("Ctrl+S")
|
204
|
-
self.aSaveDoc.triggered.connect(
|
205
|
+
self.aSaveDoc.triggered.connect(self.mainGui.saveDocument)
|
205
206
|
|
206
207
|
# Document > Close
|
207
208
|
self.aCloseDoc = self.docuMenu.addAction(self.tr("Close Document"))
|
@@ -219,7 +220,7 @@ class GuiMainMenu(QMenuBar):
|
|
219
220
|
# Document > Close Preview
|
220
221
|
self.aCloseView = self.docuMenu.addAction(self.tr("Close Document View"))
|
221
222
|
self.aCloseView.setShortcut("Ctrl+Shift+R")
|
222
|
-
self.aCloseView.triggered.connect(
|
223
|
+
self.aCloseView.triggered.connect(self.mainGui.closeDocViewer)
|
223
224
|
|
224
225
|
# Document > Separator
|
225
226
|
self.docuMenu.addSeparator()
|
@@ -671,6 +672,12 @@ class GuiMainMenu(QMenuBar):
|
|
671
672
|
lambda: self.requestDocAction.emit(nwDocAction.SC_ULINE)
|
672
673
|
)
|
673
674
|
|
675
|
+
# Shortcode Mark
|
676
|
+
self.aScMark = self.mShortcodes.addAction(self.tr("Highlight"))
|
677
|
+
self.aScMark.triggered.connect(
|
678
|
+
lambda: self.requestDocAction.emit(nwDocAction.SC_MARK)
|
679
|
+
)
|
680
|
+
|
674
681
|
# Shortcode Superscript
|
675
682
|
self.aScSuper = self.mShortcodes.addAction(self.tr("Superscript"))
|
676
683
|
self.aScSuper.triggered.connect(
|
@@ -686,29 +693,29 @@ class GuiMainMenu(QMenuBar):
|
|
686
693
|
# Format > Separator
|
687
694
|
self.fmtMenu.addSeparator()
|
688
695
|
|
689
|
-
# Format >
|
690
|
-
self.aFmtHead1 = self.fmtMenu.addAction(self.tr("
|
696
|
+
# Format > Heading 1 (Partition)
|
697
|
+
self.aFmtHead1 = self.fmtMenu.addAction(self.tr("Heading 1 (Partition)"))
|
691
698
|
self.aFmtHead1.setShortcut("Ctrl+1")
|
692
699
|
self.aFmtHead1.triggered.connect(
|
693
700
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H1)
|
694
701
|
)
|
695
702
|
|
696
|
-
# Format >
|
697
|
-
self.aFmtHead2 = self.fmtMenu.addAction(self.tr("
|
703
|
+
# Format > Heading 2 (Chapter)
|
704
|
+
self.aFmtHead2 = self.fmtMenu.addAction(self.tr("Heading 2 (Chapter)"))
|
698
705
|
self.aFmtHead2.setShortcut("Ctrl+2")
|
699
706
|
self.aFmtHead2.triggered.connect(
|
700
707
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H2)
|
701
708
|
)
|
702
709
|
|
703
|
-
# Format >
|
704
|
-
self.aFmtHead3 = self.fmtMenu.addAction(self.tr("
|
710
|
+
# Format > Heading 3 (Scene)
|
711
|
+
self.aFmtHead3 = self.fmtMenu.addAction(self.tr("Heading 3 (Scene)"))
|
705
712
|
self.aFmtHead3.setShortcut("Ctrl+3")
|
706
713
|
self.aFmtHead3.triggered.connect(
|
707
714
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H3)
|
708
715
|
)
|
709
716
|
|
710
|
-
# Format >
|
711
|
-
self.aFmtHead4 = self.fmtMenu.addAction(self.tr("
|
717
|
+
# Format > Heading 4 (Section)
|
718
|
+
self.aFmtHead4 = self.fmtMenu.addAction(self.tr("Heading 4 (Section)"))
|
712
719
|
self.aFmtHead4.setShortcut("Ctrl+4")
|
713
720
|
self.aFmtHead4.triggered.connect(
|
714
721
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H4)
|
@@ -729,6 +736,12 @@ class GuiMainMenu(QMenuBar):
|
|
729
736
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_UNN)
|
730
737
|
)
|
731
738
|
|
739
|
+
# Format > Hard Scene
|
740
|
+
self.aFmtHardSc = self.fmtMenu.addAction(self.tr("Hard Scene"))
|
741
|
+
self.aFmtHardSc.triggered.connect(
|
742
|
+
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_HSC)
|
743
|
+
)
|
744
|
+
|
732
745
|
# Format > Separator
|
733
746
|
self.fmtMenu.addSeparator()
|
734
747
|
|
@@ -797,14 +810,14 @@ class GuiMainMenu(QMenuBar):
|
|
797
810
|
# Format > Separator
|
798
811
|
self.fmtMenu.addSeparator()
|
799
812
|
|
800
|
-
# Format > Replace Single Quotes
|
801
|
-
self.aFmtReplSng = self.fmtMenu.addAction(self.tr("
|
813
|
+
# Format > Replace Straight Single Quotes
|
814
|
+
self.aFmtReplSng = self.fmtMenu.addAction(self.tr("Replace Straight Single Quotes"))
|
802
815
|
self.aFmtReplSng.triggered.connect(
|
803
816
|
lambda: self.requestDocAction.emit(nwDocAction.REPL_SNG)
|
804
817
|
)
|
805
818
|
|
806
|
-
# Format > Replace Double Quotes
|
807
|
-
self.aFmtReplDbl = self.fmtMenu.addAction(self.tr("
|
819
|
+
# Format > Replace Straight Double Quotes
|
820
|
+
self.aFmtReplDbl = self.fmtMenu.addAction(self.tr("Replace Straight Double Quotes"))
|
808
821
|
self.aFmtReplDbl.triggered.connect(
|
809
822
|
lambda: self.requestDocAction.emit(nwDocAction.REPL_DBL)
|
810
823
|
)
|
@@ -849,6 +862,14 @@ class GuiMainMenu(QMenuBar):
|
|
849
862
|
self.aReplaceNext.setShortcut("Ctrl+Shift+1")
|
850
863
|
self.aReplaceNext.triggered.connect(lambda: self.mainGui.docEditor.replaceNext())
|
851
864
|
|
865
|
+
# Search > Separator
|
866
|
+
self.srcMenu.addSeparator()
|
867
|
+
|
868
|
+
# Search > Find in Project
|
869
|
+
self.aFindProj = self.srcMenu.addAction(self.tr("Find in Project"))
|
870
|
+
self.aFindProj.setShortcut("Ctrl+Shift+F")
|
871
|
+
self.aFindProj.triggered.connect(lambda: self.requestViewChange.emit(nwView.SEARCH))
|
872
|
+
|
852
873
|
return
|
853
874
|
|
854
875
|
def _buildToolsMenu(self) -> None:
|
novelwriter/gui/noveltree.py
CHANGED
@@ -35,16 +35,18 @@ from PyQt5.QtGui import QFocusEvent, QFont, QMouseEvent, QPalette, QResizeEvent
|
|
35
35
|
from PyQt5.QtCore import QModelIndex, QPoint, Qt, QSize, pyqtSlot, pyqtSignal
|
36
36
|
from PyQt5.QtWidgets import (
|
37
37
|
QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView,
|
38
|
-
QInputDialog, QMenu, QSizePolicy,
|
39
|
-
|
38
|
+
QInputDialog, QMenu, QSizePolicy, QToolTip, QTreeWidget, QTreeWidgetItem,
|
39
|
+
QVBoxLayout, QWidget
|
40
40
|
)
|
41
41
|
|
42
42
|
from novelwriter import CONFIG, SHARED
|
43
|
-
from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
|
44
43
|
from novelwriter.common import minmax
|
45
44
|
from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
|
46
45
|
from novelwriter.core.index import IndexHeading
|
46
|
+
from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
|
47
|
+
from novelwriter.extensions.modified import NIconToolButton
|
47
48
|
from novelwriter.extensions.novelselector import NovelSelector
|
49
|
+
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
48
50
|
|
49
51
|
if TYPE_CHECKING: # pragma: no cover
|
50
52
|
from novelwriter.guimain import GuiMain
|
@@ -215,15 +217,13 @@ class GuiNovelToolBar(QWidget):
|
|
215
217
|
self.novelValue.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
216
218
|
self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)
|
217
219
|
|
218
|
-
self.tbNovel =
|
220
|
+
self.tbNovel = NIconToolButton(self, iPx)
|
219
221
|
self.tbNovel.setToolTip(self.tr("Novel Root"))
|
220
|
-
self.tbNovel.setIconSize(QSize(iPx, iPx))
|
221
222
|
self.tbNovel.clicked.connect(self.novelValue.showPopup)
|
222
223
|
|
223
224
|
# Refresh Button
|
224
|
-
self.tbRefresh =
|
225
|
+
self.tbRefresh = NIconToolButton(self, iPx)
|
225
226
|
self.tbRefresh.setToolTip(self.tr("Refresh"))
|
226
|
-
self.tbRefresh.setIconSize(QSize(iPx, iPx))
|
227
227
|
self.tbRefresh.clicked.connect(self._refreshNovelTree)
|
228
228
|
|
229
229
|
# More Options Menu
|
@@ -241,11 +241,9 @@ class GuiNovelToolBar(QWidget):
|
|
241
241
|
self.aLastColSize = self.mLastCol.addAction(self.tr("Column Size"))
|
242
242
|
self.aLastColSize.triggered.connect(self._selectLastColumnSize)
|
243
243
|
|
244
|
-
self.tbMore =
|
244
|
+
self.tbMore = NIconToolButton(self, iPx)
|
245
245
|
self.tbMore.setToolTip(self.tr("More Options"))
|
246
|
-
self.tbMore.setIconSize(QSize(iPx, iPx))
|
247
246
|
self.tbMore.setMenu(self.mMore)
|
248
|
-
self.tbMore.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
|
249
247
|
|
250
248
|
# Assemble
|
251
249
|
self.outerBox = QHBoxLayout()
|
@@ -280,16 +278,10 @@ class GuiNovelToolBar(QWidget):
|
|
280
278
|
self.setPalette(qPalette)
|
281
279
|
|
282
280
|
# StyleSheets
|
283
|
-
|
284
|
-
buttonStyle = (
|
285
|
-
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
286
|
-
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
287
|
-
).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
288
|
-
buttonStyleMenu = f"{buttonStyle} QToolButton::menu-indicator {{image: none;}}"
|
289
|
-
|
281
|
+
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
|
290
282
|
self.tbNovel.setStyleSheet(buttonStyle)
|
291
283
|
self.tbRefresh.setStyleSheet(buttonStyle)
|
292
|
-
self.tbMore.setStyleSheet(
|
284
|
+
self.tbMore.setStyleSheet(buttonStyle)
|
293
285
|
|
294
286
|
self.novelValue.setStyleSheet(
|
295
287
|
"QComboBox {border-style: none; padding-left: 0;} "
|
@@ -750,7 +742,7 @@ class GuiNovelTree(QTreeWidget):
|
|
750
742
|
logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle)
|
751
743
|
|
752
744
|
pIndex = SHARED.project.index
|
753
|
-
novIdx = pIndex.
|
745
|
+
novIdx = pIndex.getItemHeading(tHandle, sTitle)
|
754
746
|
refTags = pIndex.getReferences(tHandle, sTitle)
|
755
747
|
if not novIdx:
|
756
748
|
return
|
novelwriter/gui/outline.py
CHANGED
@@ -798,14 +798,20 @@ class GuiOutlineDetails(QScrollArea):
|
|
798
798
|
hSpace = int(CONFIG.pxInt(10))
|
799
799
|
vSpace = int(CONFIG.pxInt(4))
|
800
800
|
|
801
|
+
bFont = SHARED.theme.guiFontB
|
802
|
+
|
801
803
|
# Details Area
|
802
|
-
self.titleLabel = QLabel(
|
803
|
-
self.fileLabel = QLabel(
|
804
|
-
self.itemLabel = QLabel(
|
804
|
+
self.titleLabel = QLabel(self.tr("Title"))
|
805
|
+
self.fileLabel = QLabel(self.tr("Document"))
|
806
|
+
self.itemLabel = QLabel(self.tr("Status"))
|
805
807
|
self.titleValue = QLabel("")
|
806
808
|
self.fileValue = QLabel("")
|
807
809
|
self.itemValue = QLabel("")
|
808
810
|
|
811
|
+
self.titleLabel.setFont(bFont)
|
812
|
+
self.fileLabel.setFont(bFont)
|
813
|
+
self.itemLabel.setFont(bFont)
|
814
|
+
|
809
815
|
self.titleValue.setMinimumWidth(minTitle)
|
810
816
|
self.titleValue.setMaximumWidth(maxTitle)
|
811
817
|
self.fileValue.setMinimumWidth(minTitle)
|
@@ -814,13 +820,17 @@ class GuiOutlineDetails(QScrollArea):
|
|
814
820
|
self.itemValue.setMaximumWidth(maxTitle)
|
815
821
|
|
816
822
|
# Stats Area
|
817
|
-
self.cCLabel = QLabel(
|
818
|
-
self.wCLabel = QLabel(
|
819
|
-
self.pCLabel = QLabel(
|
823
|
+
self.cCLabel = QLabel(self.tr("Characters"))
|
824
|
+
self.wCLabel = QLabel(self.tr("Words"))
|
825
|
+
self.pCLabel = QLabel(self.tr("Paragraphs"))
|
820
826
|
self.cCValue = QLabel("")
|
821
827
|
self.wCValue = QLabel("")
|
822
828
|
self.pCValue = QLabel("")
|
823
829
|
|
830
|
+
self.cCLabel.setFont(bFont)
|
831
|
+
self.wCLabel.setFont(bFont)
|
832
|
+
self.pCLabel.setFont(bFont)
|
833
|
+
|
824
834
|
self.cCValue.setMinimumWidth(wCount)
|
825
835
|
self.wCValue.setMinimumWidth(wCount)
|
826
836
|
self.pCValue.setMinimumWidth(wCount)
|
@@ -829,23 +839,36 @@ class GuiOutlineDetails(QScrollArea):
|
|
829
839
|
self.pCValue.setAlignment(Qt.AlignRight)
|
830
840
|
|
831
841
|
# Synopsis
|
832
|
-
self.synopLabel = QLabel(
|
842
|
+
self.synopLabel = QLabel(self.tr("Synopsis"))
|
843
|
+
self.synopLabel.setFont(bFont)
|
844
|
+
|
833
845
|
self.synopValue = QLabel("")
|
834
|
-
self.synopLWrap = QHBoxLayout()
|
835
846
|
self.synopValue.setWordWrap(True)
|
836
847
|
self.synopValue.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
848
|
+
|
849
|
+
self.synopLWrap = QHBoxLayout()
|
837
850
|
self.synopLWrap.addWidget(self.synopValue, 1)
|
838
851
|
|
839
852
|
# Tags
|
840
|
-
self.povKeyLabel = QLabel(
|
841
|
-
self.focKeyLabel = QLabel(
|
842
|
-
self.chrKeyLabel = QLabel(
|
843
|
-
self.pltKeyLabel = QLabel(
|
844
|
-
self.timKeyLabel = QLabel(
|
845
|
-
self.wldKeyLabel = QLabel(
|
846
|
-
self.objKeyLabel = QLabel(
|
847
|
-
self.entKeyLabel = QLabel(
|
848
|
-
self.cstKeyLabel = QLabel(
|
853
|
+
self.povKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY]))
|
854
|
+
self.focKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY]))
|
855
|
+
self.chrKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY]))
|
856
|
+
self.pltKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY]))
|
857
|
+
self.timKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.TIME_KEY]))
|
858
|
+
self.wldKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY]))
|
859
|
+
self.objKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY]))
|
860
|
+
self.entKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY]))
|
861
|
+
self.cstKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY]))
|
862
|
+
|
863
|
+
self.povKeyLabel.setFont(bFont)
|
864
|
+
self.focKeyLabel.setFont(bFont)
|
865
|
+
self.chrKeyLabel.setFont(bFont)
|
866
|
+
self.pltKeyLabel.setFont(bFont)
|
867
|
+
self.timKeyLabel.setFont(bFont)
|
868
|
+
self.wldKeyLabel.setFont(bFont)
|
869
|
+
self.objKeyLabel.setFont(bFont)
|
870
|
+
self.entKeyLabel.setFont(bFont)
|
871
|
+
self.cstKeyLabel.setFont(bFont)
|
849
872
|
|
850
873
|
self.povKeyLWrap = QHBoxLayout()
|
851
874
|
self.focKeyLWrap = QHBoxLayout()
|
@@ -989,7 +1012,7 @@ class GuiOutlineDetails(QScrollArea):
|
|
989
1012
|
|
990
1013
|
def clearDetails(self) -> None:
|
991
1014
|
"""Clear all the data labels."""
|
992
|
-
self.titleLabel.setText(
|
1015
|
+
self.titleLabel.setText(self.tr("Title"))
|
993
1016
|
self.titleValue.setText("")
|
994
1017
|
self.fileValue.setText("")
|
995
1018
|
self.itemValue.setText("")
|
@@ -1020,10 +1043,10 @@ class GuiOutlineDetails(QScrollArea):
|
|
1020
1043
|
"""
|
1021
1044
|
pIndex = SHARED.project.index
|
1022
1045
|
nwItem = SHARED.project.tree[tHandle]
|
1023
|
-
novIdx = pIndex.
|
1046
|
+
novIdx = pIndex.getItemHeading(tHandle, sTitle)
|
1024
1047
|
novRefs = pIndex.getReferences(tHandle, sTitle)
|
1025
1048
|
if nwItem and novIdx:
|
1026
|
-
self.titleLabel.setText(
|
1049
|
+
self.titleLabel.setText(self.tr(self.LVL_MAP.get(novIdx.level, "H1")))
|
1027
1050
|
self.titleValue.setText(novIdx.title)
|
1028
1051
|
|
1029
1052
|
itemStatus, _ = nwItem.getImportStatus(incIcon=False)
|
novelwriter/gui/projtree.py
CHANGED
@@ -38,20 +38,22 @@ from PyQt5.QtGui import (
|
|
38
38
|
from PyQt5.QtCore import QPoint, QTimer, Qt, QSize, pyqtSignal, pyqtSlot
|
39
39
|
from PyQt5.QtWidgets import (
|
40
40
|
QAbstractItemView, QAction, QDialog, QFrame, QHBoxLayout, QHeaderView,
|
41
|
-
QLabel, QMenu, QShortcut, QSizePolicy,
|
42
|
-
|
41
|
+
QLabel, QMenu, QShortcut, QSizePolicy, QTreeWidget, QTreeWidgetItem,
|
42
|
+
QVBoxLayout, QWidget
|
43
43
|
)
|
44
44
|
|
45
45
|
from novelwriter import CONFIG, SHARED
|
46
|
-
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout
|
47
46
|
from novelwriter.common import minmax
|
48
47
|
from novelwriter.constants import nwHeaders, nwUnicode, trConst, nwLabels
|
49
|
-
from novelwriter.core.item import NWItem
|
50
48
|
from novelwriter.core.coretools import DocDuplicator, DocMerger, DocSplitter
|
49
|
+
from novelwriter.core.item import NWItem
|
51
50
|
from novelwriter.dialogs.docmerge import GuiDocMerge
|
52
51
|
from novelwriter.dialogs.docsplit import GuiDocSplit
|
53
52
|
from novelwriter.dialogs.editlabel import GuiEditLabel
|
54
53
|
from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
54
|
+
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout
|
55
|
+
from novelwriter.extensions.modified import NIconToolButton
|
56
|
+
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
55
57
|
|
56
58
|
if TYPE_CHECKING: # pragma: no cover
|
57
59
|
from novelwriter.guimain import GuiMain
|
@@ -142,7 +144,6 @@ class GuiProjectView(QWidget):
|
|
142
144
|
self.requestDeleteItem = self.projTree.requestDeleteItem
|
143
145
|
self.getSelectedHandle = self.projTree.getSelectedHandle
|
144
146
|
self.changedSince = self.projTree.changedSince
|
145
|
-
self.createNewNote = self.projTree.createNewNote
|
146
147
|
|
147
148
|
return
|
148
149
|
|
@@ -240,6 +241,12 @@ class GuiProjectView(QWidget):
|
|
240
241
|
self.projBar.buildQuickLinksMenu()
|
241
242
|
return
|
242
243
|
|
244
|
+
@pyqtSlot(str, nwItemClass)
|
245
|
+
def createNewNote(self, tag: str, itemClass: nwItemClass) -> None:
|
246
|
+
"""Process new not request."""
|
247
|
+
self.projTree.createNewNote(tag, itemClass)
|
248
|
+
return
|
249
|
+
|
243
250
|
# END Class GuiProjectView
|
244
251
|
|
245
252
|
|
@@ -263,29 +270,26 @@ class GuiProjectToolBar(QWidget):
|
|
263
270
|
self.setAutoFillBackground(True)
|
264
271
|
|
265
272
|
# Widget Label
|
266
|
-
self.viewLabel = QLabel(
|
273
|
+
self.viewLabel = QLabel(self.tr("Project Content"))
|
274
|
+
self.viewLabel.setFont(SHARED.theme.guiFontB)
|
267
275
|
self.viewLabel.setContentsMargins(0, 0, 0, 0)
|
268
276
|
self.viewLabel.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
269
277
|
|
270
278
|
# Quick Links
|
271
279
|
self.mQuick = QMenu(self)
|
272
280
|
|
273
|
-
self.tbQuick =
|
281
|
+
self.tbQuick = NIconToolButton(self, iPx)
|
274
282
|
self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
|
275
283
|
self.tbQuick.setShortcut("Ctrl+L")
|
276
|
-
self.tbQuick.setIconSize(QSize(iPx, iPx))
|
277
284
|
self.tbQuick.setMenu(self.mQuick)
|
278
|
-
self.tbQuick.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
|
279
285
|
|
280
286
|
# Move Buttons
|
281
|
-
self.tbMoveU =
|
287
|
+
self.tbMoveU = NIconToolButton(self, iPx)
|
282
288
|
self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
|
283
|
-
self.tbMoveU.setIconSize(QSize(iPx, iPx))
|
284
289
|
self.tbMoveU.clicked.connect(lambda: self.projTree.moveTreeItem(-1))
|
285
290
|
|
286
|
-
self.tbMoveD =
|
291
|
+
self.tbMoveD = NIconToolButton(self, iPx)
|
287
292
|
self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
|
288
|
-
self.tbMoveD.setIconSize(QSize(iPx, iPx))
|
289
293
|
self.tbMoveD.clicked.connect(lambda: self.projTree.moveTreeItem(1))
|
290
294
|
|
291
295
|
# Add Item Menu
|
@@ -324,12 +328,10 @@ class GuiProjectToolBar(QWidget):
|
|
324
328
|
self.mAddRoot = self.mAdd.addMenu(trConst(nwLabels.ITEM_DESCRIPTION["root"]))
|
325
329
|
self._buildRootMenu()
|
326
330
|
|
327
|
-
self.tbAdd =
|
331
|
+
self.tbAdd = NIconToolButton(self, iPx)
|
328
332
|
self.tbAdd.setToolTip("%s [Ctrl+N]" % self.tr("Add Item"))
|
329
333
|
self.tbAdd.setShortcut("Ctrl+N")
|
330
|
-
self.tbAdd.setIconSize(QSize(iPx, iPx))
|
331
334
|
self.tbAdd.setMenu(self.mAdd)
|
332
|
-
self.tbAdd.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
|
333
335
|
|
334
336
|
# More Options Menu
|
335
337
|
self.mMore = QMenu(self)
|
@@ -343,11 +345,9 @@ class GuiProjectToolBar(QWidget):
|
|
343
345
|
self.aEmptyTrash = self.mMore.addAction(self.tr("Empty Trash"))
|
344
346
|
self.aEmptyTrash.triggered.connect(lambda: self.projTree.emptyTrash())
|
345
347
|
|
346
|
-
self.tbMore =
|
348
|
+
self.tbMore = NIconToolButton(self, iPx)
|
347
349
|
self.tbMore.setToolTip(self.tr("More Options"))
|
348
|
-
self.tbMore.setIconSize(QSize(iPx, iPx))
|
349
350
|
self.tbMore.setMenu(self.mMore)
|
350
|
-
self.tbMore.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
|
351
351
|
|
352
352
|
# Assemble
|
353
353
|
self.outerBox = QHBoxLayout()
|
@@ -377,18 +377,12 @@ class GuiProjectToolBar(QWidget):
|
|
377
377
|
qPalette.setBrush(QPalette.ColorRole.Window, qPalette.base())
|
378
378
|
self.setPalette(qPalette)
|
379
379
|
|
380
|
-
|
381
|
-
buttonStyle
|
382
|
-
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
383
|
-
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
384
|
-
).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
385
|
-
buttonStyleMenu = f"{buttonStyle} QToolButton::menu-indicator {{image: none;}}"
|
386
|
-
|
387
|
-
self.tbQuick.setStyleSheet(buttonStyleMenu)
|
380
|
+
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
|
381
|
+
self.tbQuick.setStyleSheet(buttonStyle)
|
388
382
|
self.tbMoveU.setStyleSheet(buttonStyle)
|
389
383
|
self.tbMoveD.setStyleSheet(buttonStyle)
|
390
|
-
self.tbAdd.setStyleSheet(
|
391
|
-
self.tbMore.setStyleSheet(
|
384
|
+
self.tbAdd.setStyleSheet(buttonStyle)
|
385
|
+
self.tbMore.setStyleSheet(buttonStyle)
|
392
386
|
|
393
387
|
self.tbQuick.setIcon(SHARED.theme.getIcon("bookmark"))
|
394
388
|
self.tbMoveU.setIcon(SHARED.theme.getIcon("up"))
|
@@ -606,18 +600,18 @@ class GuiProjectTree(QTreeWidget):
|
|
606
600
|
self._timeChanged = 0.0
|
607
601
|
return
|
608
602
|
|
609
|
-
def createNewNote(self, tag: str, itemClass: nwItemClass
|
603
|
+
def createNewNote(self, tag: str, itemClass: nwItemClass) -> None:
|
610
604
|
"""Create a new note. This function is used by the document
|
611
605
|
editor to create note files for unknown tags.
|
612
606
|
"""
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
607
|
+
if itemClass != nwItemClass.NO_CLASS:
|
608
|
+
if not (rHandle := SHARED.project.tree.findRoot(itemClass)):
|
609
|
+
self.newTreeItem(nwItemType.ROOT, itemClass)
|
610
|
+
rHandle = SHARED.project.tree.findRoot(itemClass)
|
611
|
+
if rHandle and (tHandle := SHARED.project.newFile(tag, rHandle)):
|
617
612
|
SHARED.project.writeNewFile(tHandle, 1, False, f"@tag: {tag}\n\n")
|
618
613
|
self.revealNewTreeItem(tHandle, wordCount=True)
|
619
|
-
|
620
|
-
return False
|
614
|
+
return
|
621
615
|
|
622
616
|
def newTreeItem(self, itemType: nwItemType, itemClass: nwItemClass | None = None,
|
623
617
|
hLevel: int = 1, isNote: bool = False, copyDoc: str | None = None) -> bool:
|
@@ -1834,7 +1828,7 @@ class _TreeContextMenu(QMenu):
|
|
1834
1828
|
|
1835
1829
|
def _itemHeader(self) -> None:
|
1836
1830
|
"""Check if there is a header that can be used for rename."""
|
1837
|
-
if hItem := SHARED.project.index.
|
1831
|
+
if hItem := SHARED.project.index.getItemHeading(self._handle, "T0001"):
|
1838
1832
|
action = self.addAction(self.tr("Rename to Heading"))
|
1839
1833
|
action.triggered.connect(
|
1840
1834
|
lambda: self.projTree.renameTreeItem(self._handle, hItem.title)
|
@@ -1933,7 +1927,7 @@ class _TreeContextMenu(QMenu):
|
|
1933
1927
|
action.triggered.connect(lambda: tree._mergeDocuments(tHandle, True))
|
1934
1928
|
|
1935
1929
|
if isFile:
|
1936
|
-
action = menu.addAction(self.tr("Split Document by
|
1930
|
+
action = menu.addAction(self.tr("Split Document by Headings"))
|
1937
1931
|
action.triggered.connect(lambda: tree._splitDocument(tHandle))
|
1938
1932
|
|
1939
1933
|
return
|
@@ -1947,11 +1941,9 @@ class _TreeContextMenu(QMenu):
|
|
1947
1941
|
action.triggered.connect(lambda: tree.setExpandedFromHandle(tHandle, True))
|
1948
1942
|
action = self.addAction(self.tr("Collapse All"))
|
1949
1943
|
action.triggered.connect(lambda: tree.setExpandedFromHandle(tHandle, False))
|
1950
|
-
|
1951
|
-
|
1952
|
-
|
1953
|
-
action = self.addAction(self.tr("Duplicate Document"))
|
1954
|
-
action.triggered.connect(lambda: tree._duplicateFromHandle(tHandle))
|
1944
|
+
|
1945
|
+
action = self.addAction(self.tr("Duplicate"))
|
1946
|
+
action.triggered.connect(lambda: tree._duplicateFromHandle(tHandle))
|
1955
1947
|
|
1956
1948
|
if self._item.itemClass == nwItemClass.TRASH or isRoot or (isFolder and not hasChild):
|
1957
1949
|
action = self.addAction(self.tr("Delete Permanently"))
|