novelWriter 2.4.3__py3-none-any.whl → 2.5__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.4.3.dist-info → novelWriter-2.5.dist-info}/METADATA +4 -5
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/RECORD +122 -112
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +33 -39
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/i18n/project_pl_PL.json +116 -0
- novelwriter/assets/i18n/project_pt_BR.json +74 -74
- novelwriter/assets/icons/typicons_dark/icons.conf +2 -0
- novelwriter/assets/icons/typicons_dark/nw_font.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_quote.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +2 -0
- novelwriter/assets/icons/typicons_light/nw_font.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_quote.svg +4 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/cyberpunk_night.conf +5 -3
- novelwriter/assets/syntax/default_dark.conf +32 -18
- novelwriter/assets/syntax/default_light.conf +24 -10
- novelwriter/assets/syntax/dracula.conf +44 -0
- novelwriter/assets/syntax/grey_dark.conf +5 -4
- novelwriter/assets/syntax/grey_light.conf +5 -4
- novelwriter/assets/syntax/light_owl.conf +7 -6
- novelwriter/assets/syntax/night_owl.conf +7 -6
- novelwriter/assets/syntax/snazzy.conf +42 -0
- novelwriter/assets/syntax/solarized_dark.conf +4 -3
- novelwriter/assets/syntax/solarized_light.conf +4 -3
- novelwriter/assets/syntax/tango.conf +27 -11
- novelwriter/assets/syntax/tomorrow.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night.conf +7 -6
- novelwriter/assets/syntax/tomorrow_night_blue.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night_bright.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +6 -5
- novelwriter/assets/text/credits_en.htm +52 -41
- novelwriter/assets/themes/cyberpunk_night.conf +3 -0
- novelwriter/assets/themes/default_dark.conf +2 -0
- novelwriter/assets/themes/default_light.conf +2 -0
- novelwriter/assets/themes/dracula.conf +48 -0
- novelwriter/assets/themes/solarized_dark.conf +2 -0
- novelwriter/assets/themes/solarized_light.conf +2 -0
- novelwriter/common.py +33 -12
- novelwriter/config.py +184 -98
- novelwriter/constants.py +47 -35
- novelwriter/core/buildsettings.py +68 -69
- novelwriter/core/coretools.py +5 -23
- novelwriter/core/docbuild.py +52 -40
- novelwriter/core/document.py +3 -5
- novelwriter/core/index.py +115 -45
- novelwriter/core/item.py +8 -19
- novelwriter/core/options.py +2 -4
- novelwriter/core/project.py +37 -61
- novelwriter/core/projectdata.py +1 -3
- novelwriter/core/projectxml.py +12 -15
- novelwriter/core/sessions.py +3 -5
- novelwriter/core/spellcheck.py +4 -9
- novelwriter/core/status.py +211 -164
- novelwriter/core/storage.py +0 -8
- novelwriter/core/tohtml.py +139 -105
- novelwriter/core/tokenizer.py +278 -122
- novelwriter/core/{tomd.py → tomarkdown.py} +97 -78
- novelwriter/core/toodt.py +257 -166
- novelwriter/core/toqdoc.py +419 -0
- novelwriter/core/tree.py +5 -7
- novelwriter/dialogs/about.py +11 -18
- novelwriter/dialogs/docmerge.py +17 -19
- novelwriter/dialogs/docsplit.py +17 -19
- novelwriter/dialogs/editlabel.py +6 -10
- novelwriter/dialogs/preferences.py +200 -164
- novelwriter/dialogs/projectsettings.py +225 -189
- novelwriter/dialogs/quotes.py +12 -9
- novelwriter/dialogs/wordlist.py +9 -15
- novelwriter/enum.py +35 -30
- novelwriter/error.py +8 -15
- novelwriter/extensions/configlayout.py +55 -21
- novelwriter/extensions/eventfilters.py +1 -5
- novelwriter/extensions/modified.py +70 -14
- novelwriter/extensions/novelselector.py +1 -3
- novelwriter/extensions/pagedsidebar.py +9 -12
- novelwriter/extensions/{circularprogress.py → progressbars.py} +30 -8
- novelwriter/extensions/statusled.py +40 -26
- novelwriter/extensions/switch.py +4 -6
- novelwriter/extensions/switchbox.py +7 -6
- novelwriter/extensions/versioninfo.py +3 -9
- novelwriter/gui/doceditor.py +120 -139
- novelwriter/gui/dochighlight.py +231 -186
- novelwriter/gui/docviewer.py +69 -108
- novelwriter/gui/docviewerpanel.py +3 -10
- novelwriter/gui/editordocument.py +1 -3
- novelwriter/gui/itemdetails.py +7 -11
- novelwriter/gui/mainmenu.py +22 -18
- novelwriter/gui/noveltree.py +11 -24
- novelwriter/gui/outline.py +15 -26
- novelwriter/gui/projtree.py +39 -65
- novelwriter/gui/search.py +10 -3
- novelwriter/gui/sidebar.py +2 -6
- novelwriter/gui/statusbar.py +29 -37
- novelwriter/gui/theme.py +26 -48
- novelwriter/guimain.py +162 -160
- novelwriter/shared.py +36 -19
- novelwriter/text/patterns.py +113 -0
- novelwriter/tools/dictionaries.py +10 -20
- novelwriter/tools/lipsum.py +10 -16
- novelwriter/tools/manusbuild.py +9 -11
- novelwriter/tools/manuscript.py +75 -149
- novelwriter/tools/manussettings.py +74 -76
- novelwriter/tools/noveldetails.py +16 -21
- novelwriter/tools/welcome.py +21 -26
- novelwriter/tools/writingstats.py +9 -12
- novelwriter/types.py +49 -4
- novelwriter/extensions/simpleprogress.py +0 -55
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/top_level.txt +0 -0
novelwriter/gui/docviewer.py
CHANGED
@@ -31,24 +31,22 @@ import logging
|
|
31
31
|
from enum import Enum
|
32
32
|
|
33
33
|
from PyQt5.QtCore import QPoint, Qt, QUrl, pyqtSignal, pyqtSlot
|
34
|
-
from PyQt5.QtGui import QCursor, QMouseEvent, QPalette, QResizeEvent, QTextCursor
|
34
|
+
from PyQt5.QtGui import QCursor, QMouseEvent, QPalette, QResizeEvent, QTextCursor
|
35
35
|
from PyQt5.QtWidgets import (
|
36
|
-
QAction, QApplication, QFrame, QHBoxLayout,
|
36
|
+
QAction, QApplication, QFrame, QHBoxLayout, QMenu, QTextBrowser,
|
37
37
|
QToolButton, QWidget
|
38
38
|
)
|
39
39
|
|
40
40
|
from novelwriter import CONFIG, SHARED
|
41
|
-
from novelwriter.common import cssCol
|
42
41
|
from novelwriter.constants import nwHeaders, nwUnicode
|
43
|
-
from novelwriter.core.
|
42
|
+
from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
|
44
43
|
from novelwriter.enum import nwDocAction, nwDocMode, nwItemType
|
45
44
|
from novelwriter.error import logException
|
45
|
+
from novelwriter.extensions.configlayout import NColourLabel
|
46
46
|
from novelwriter.extensions.eventfilters import WheelEventFilter
|
47
47
|
from novelwriter.extensions.modified import NIconToolButton
|
48
48
|
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
49
|
-
from novelwriter.types import
|
50
|
-
QtAlignCenterTop, QtAlignJustify, QtKeepAnchor, QtMouseLeft, QtMoveAnchor
|
51
|
-
)
|
49
|
+
from novelwriter.types import QtAlignCenterTop, QtKeepAnchor, QtMouseLeft, QtMoveAnchor
|
52
50
|
|
53
51
|
logger = logging.getLogger(__name__)
|
54
52
|
|
@@ -69,6 +67,7 @@ class GuiDocViewer(QTextBrowser):
|
|
69
67
|
|
70
68
|
# Internal Variables
|
71
69
|
self._docHandle = None
|
70
|
+
self._docTheme = TextDocumentTheme()
|
72
71
|
|
73
72
|
# Settings
|
74
73
|
self.setMinimumWidth(CONFIG.pxInt(300))
|
@@ -94,6 +93,9 @@ class GuiDocViewer(QTextBrowser):
|
|
94
93
|
self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
95
94
|
self.customContextMenuRequested.connect(self._openContextMenu)
|
96
95
|
|
96
|
+
# Function Mapping
|
97
|
+
self.changeFocusState = self.docHeader.changeFocusState
|
98
|
+
|
97
99
|
self.initViewer()
|
98
100
|
|
99
101
|
logger.debug("Ready: GuiDocViewer")
|
@@ -137,8 +139,10 @@ class GuiDocViewer(QTextBrowser):
|
|
137
139
|
|
138
140
|
def initViewer(self) -> None:
|
139
141
|
"""Set editor settings from main config."""
|
140
|
-
|
141
|
-
self.
|
142
|
+
# Set the font. See issues #1862 and #1875.
|
143
|
+
self.setFont(CONFIG.textFont)
|
144
|
+
self.docHeader.updateFont()
|
145
|
+
self.docFooter.updateFont()
|
142
146
|
|
143
147
|
# Set the widget colours to match syntax theme
|
144
148
|
mainPalette = self.palette()
|
@@ -151,16 +155,25 @@ class GuiDocViewer(QTextBrowser):
|
|
151
155
|
docPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
|
152
156
|
docPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
153
157
|
self.viewport().setPalette(docPalette)
|
154
|
-
|
155
158
|
self.docHeader.matchColours()
|
156
159
|
self.docFooter.matchColours()
|
157
160
|
|
161
|
+
# Update theme colours
|
162
|
+
self._docTheme.text = SHARED.theme.colText
|
163
|
+
self._docTheme.highlight = SHARED.theme.colMark
|
164
|
+
self._docTheme.head = SHARED.theme.colHead
|
165
|
+
self._docTheme.comment = SHARED.theme.colHidden
|
166
|
+
self._docTheme.note = SHARED.theme.colNote
|
167
|
+
self._docTheme.code = SHARED.theme.colCode
|
168
|
+
self._docTheme.modifier = SHARED.theme.colMod
|
169
|
+
self._docTheme.keyword = SHARED.theme.colKey
|
170
|
+
self._docTheme.tag = SHARED.theme.colTag
|
171
|
+
self._docTheme.optional = SHARED.theme.colOpt
|
172
|
+
self._docTheme.dialog = SHARED.theme.colDialN
|
173
|
+
self._docTheme.altdialog = SHARED.theme.colDialA
|
174
|
+
|
158
175
|
# Set default text margins
|
159
176
|
self.document().setDocumentMargin(0)
|
160
|
-
options = QTextOption()
|
161
|
-
if CONFIG.doJustify:
|
162
|
-
options.setAlignment(QtAlignJustify)
|
163
|
-
self.document().setDefaultTextOption(options)
|
164
177
|
|
165
178
|
# Scroll bars
|
166
179
|
if CONFIG.hideVScroll:
|
@@ -181,22 +194,6 @@ class GuiDocViewer(QTextBrowser):
|
|
181
194
|
|
182
195
|
return
|
183
196
|
|
184
|
-
def initFont(self) -> None:
|
185
|
-
"""Set the font of the main widget and sub-widgets. This needs
|
186
|
-
special attention since there appears to be a bug in Qt 5.15.3.
|
187
|
-
See issues #1862 and #1875.
|
188
|
-
"""
|
189
|
-
font = self.font()
|
190
|
-
font.setFamily(CONFIG.textFont)
|
191
|
-
font.setPointSize(CONFIG.textSize)
|
192
|
-
self.setFont(font)
|
193
|
-
|
194
|
-
# Reset sub-widget font to GUI font
|
195
|
-
self.docHeader.updateFont()
|
196
|
-
self.docFooter.updateFont()
|
197
|
-
|
198
|
-
return
|
199
|
-
|
200
197
|
def loadText(self, tHandle: str, updateHistory: bool = True) -> bool:
|
201
198
|
"""Load text into the viewer from an item handle."""
|
202
199
|
if not SHARED.project.tree.checkType(tHandle, nwItemType.FILE):
|
@@ -208,21 +205,23 @@ class GuiDocViewer(QTextBrowser):
|
|
208
205
|
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
|
209
206
|
|
210
207
|
sPos = self.verticalScrollBar().value()
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
208
|
+
qDoc = ToQTextDocument(SHARED.project)
|
209
|
+
qDoc.setJustify(CONFIG.doJustify)
|
210
|
+
qDoc.setDialogueHighlight(True)
|
211
|
+
qDoc.initDocument(CONFIG.textFont, self._docTheme)
|
212
|
+
qDoc.setKeywords(True)
|
213
|
+
qDoc.setComments(CONFIG.viewComments)
|
214
|
+
qDoc.setSynopsis(CONFIG.viewSynopsis)
|
217
215
|
|
218
216
|
# Be extra careful here to prevent crashes when first opening a
|
219
217
|
# project as a crash here leaves no way of recovering.
|
220
218
|
# See issue #298
|
221
219
|
try:
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
220
|
+
qDoc.setText(tHandle)
|
221
|
+
qDoc.doPreProcessing()
|
222
|
+
qDoc.tokenizeText()
|
223
|
+
qDoc.doConvert()
|
224
|
+
qDoc.appendFootnotes()
|
226
225
|
except Exception:
|
227
226
|
logger.error("Failed to generate preview for document with handle '%s'", tHandle)
|
228
227
|
logException()
|
@@ -238,11 +237,7 @@ class GuiDocViewer(QTextBrowser):
|
|
238
237
|
self.docHistory.append(tHandle)
|
239
238
|
|
240
239
|
self.setDocumentTitle(tHandle)
|
241
|
-
|
242
|
-
# Replace tabs before setting the HTML, and then put them back in
|
243
|
-
self.setHtml(aDoc.result.replace("\t", "!!tab!!"))
|
244
|
-
while self.find("!!tab!!"):
|
245
|
-
self.textCursor().insertText("\t")
|
240
|
+
self.setDocument(qDoc.document)
|
246
241
|
|
247
242
|
if self._docHandle == tHandle:
|
248
243
|
# This is a refresh, so we set the scrollbar back to where it was
|
@@ -257,9 +252,6 @@ class GuiDocViewer(QTextBrowser):
|
|
257
252
|
})
|
258
253
|
self.updateDocMargins()
|
259
254
|
|
260
|
-
# Since we change the content while it may still be rendering, we mark
|
261
|
-
# the document dirty again to make sure it's re-rendered properly.
|
262
|
-
self.redrawText()
|
263
255
|
QApplication.restoreOverrideCursor()
|
264
256
|
self.documentLoaded.emit(tHandle)
|
265
257
|
|
@@ -271,12 +263,6 @@ class GuiDocViewer(QTextBrowser):
|
|
271
263
|
self.loadText(self._docHandle, updateHistory=False)
|
272
264
|
return
|
273
265
|
|
274
|
-
def redrawText(self) -> None:
|
275
|
-
"""Redraw the text by marking the content as "dirty"."""
|
276
|
-
self.document().markContentsDirty(0, self.document().characterCount())
|
277
|
-
self.updateDocMargins()
|
278
|
-
return
|
279
|
-
|
280
266
|
def docAction(self, action: nwDocAction) -> bool:
|
281
267
|
"""Process document actions on the current document."""
|
282
268
|
logger.debug("Requesting action: '%s'", action.name)
|
@@ -296,6 +282,10 @@ class GuiDocViewer(QTextBrowser):
|
|
296
282
|
return False
|
297
283
|
return True
|
298
284
|
|
285
|
+
def anyFocus(self) -> bool:
|
286
|
+
"""Check if any widget or child widget has focus."""
|
287
|
+
return self.hasFocus() or self.isAncestorOf(QApplication.focusWidget())
|
288
|
+
|
299
289
|
def clearNavHistory(self) -> None:
|
300
290
|
"""Clear the navigation history."""
|
301
291
|
self.docHistory.clear()
|
@@ -380,12 +370,12 @@ class GuiDocViewer(QTextBrowser):
|
|
380
370
|
@pyqtSlot("QUrl")
|
381
371
|
def _linkClicked(self, url: QUrl) -> None:
|
382
372
|
"""Process a clicked link in the document."""
|
383
|
-
link
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
self.
|
373
|
+
if link := url.url():
|
374
|
+
logger.debug("Clicked link: '%s'", link)
|
375
|
+
if (bits := link.partition("_")) and bits[0] == "#tag" and bits[2]:
|
376
|
+
self.loadDocumentTagRequest.emit(bits[2], nwDocMode.VIEW)
|
377
|
+
else:
|
378
|
+
self.navigateTo(link)
|
389
379
|
return
|
390
380
|
|
391
381
|
@pyqtSlot("QPoint")
|
@@ -475,34 +465,6 @@ class GuiDocViewer(QTextBrowser):
|
|
475
465
|
self._makeSelection(selType)
|
476
466
|
return
|
477
467
|
|
478
|
-
def _makeStyleSheet(self) -> None:
|
479
|
-
"""Generate an appropriate style sheet for the document viewer,
|
480
|
-
based on the current syntax highlighter theme.
|
481
|
-
"""
|
482
|
-
colText = cssCol(SHARED.theme.colText)
|
483
|
-
colHead = cssCol(SHARED.theme.colHead)
|
484
|
-
colVals = cssCol(SHARED.theme.colVal)
|
485
|
-
colMark = cssCol(SHARED.theme.colMark)
|
486
|
-
colKeys = cssCol(SHARED.theme.colKey)
|
487
|
-
colOpts = cssCol(SHARED.theme.colOpt)
|
488
|
-
colHide = cssCol(SHARED.theme.colHidden)
|
489
|
-
colMods = cssCol(SHARED.theme.colMod)
|
490
|
-
self.document().setDefaultStyleSheet(
|
491
|
-
f"body {{color: {colText};}}\n"
|
492
|
-
f"h1, h2, h3, h4 {{color: {colHead};}}\n"
|
493
|
-
f"a {{color: {colVals};}}\n"
|
494
|
-
f"mark {{background-color: {colMark};}}\n"
|
495
|
-
f".tags {{color: {colKeys};}}\n"
|
496
|
-
f".optional {{color: {colOpts};}}\n"
|
497
|
-
f".comment {{color: {colHide};}}\n"
|
498
|
-
f".synopsis {{color: {colMods};}}\n"
|
499
|
-
".title {text-align: center;}\n"
|
500
|
-
)
|
501
|
-
|
502
|
-
return
|
503
|
-
|
504
|
-
# END Class GuiDocViewer
|
505
|
-
|
506
468
|
|
507
469
|
class GuiDocViewHistory:
|
508
470
|
|
@@ -616,15 +578,13 @@ class GuiDocViewHistory:
|
|
616
578
|
)
|
617
579
|
return
|
618
580
|
|
619
|
-
# END Class GuiDocViewHistory
|
620
|
-
|
621
|
-
|
622
|
-
# =============================================================================================== #
|
623
|
-
# The Embedded Document Header
|
624
|
-
# Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport
|
625
|
-
# =============================================================================================== #
|
626
581
|
|
627
582
|
class GuiDocViewHeader(QWidget):
|
583
|
+
"""The Embedded Document Header
|
584
|
+
|
585
|
+
Only used by DocViewer, and is at a fixed position in the
|
586
|
+
QTextBrowser's viewport.
|
587
|
+
"""
|
628
588
|
|
629
589
|
def __init__(self, docViewer: GuiDocViewer) -> None:
|
630
590
|
super().__init__(parent=docViewer)
|
@@ -645,9 +605,7 @@ class GuiDocViewHeader(QWidget):
|
|
645
605
|
self.setAutoFillBackground(True)
|
646
606
|
|
647
607
|
# Title Label
|
648
|
-
self.itemTitle =
|
649
|
-
self.itemTitle.setText("")
|
650
|
-
self.itemTitle.setIndent(0)
|
608
|
+
self.itemTitle = NColourLabel("", self, faded=SHARED.theme.fadedText)
|
651
609
|
self.itemTitle.setMargin(0)
|
652
610
|
self.itemTitle.setContentsMargins(0, 0, 0, 0)
|
653
611
|
self.itemTitle.setAutoFillBackground(True)
|
@@ -783,7 +741,14 @@ class GuiDocViewHeader(QWidget):
|
|
783
741
|
palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
|
784
742
|
palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
785
743
|
self.setPalette(palette)
|
786
|
-
self.itemTitle.
|
744
|
+
self.itemTitle.setTextColors(
|
745
|
+
color=palette.windowText().color(), faded=SHARED.theme.fadedText
|
746
|
+
)
|
747
|
+
return
|
748
|
+
|
749
|
+
def changeFocusState(self, state: bool) -> None:
|
750
|
+
"""Toggle focus state."""
|
751
|
+
self.itemTitle.setColorState(state)
|
787
752
|
return
|
788
753
|
|
789
754
|
def setHandle(self, tHandle: str) -> None:
|
@@ -842,15 +807,13 @@ class GuiDocViewHeader(QWidget):
|
|
842
807
|
self.docViewer.requestProjectItemSelected.emit(self._docHandle, True)
|
843
808
|
return
|
844
809
|
|
845
|
-
# END Class GuiDocViewHeader
|
846
|
-
|
847
|
-
|
848
|
-
# =============================================================================================== #
|
849
|
-
# The Embedded Document Footer
|
850
|
-
# Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport
|
851
|
-
# =============================================================================================== #
|
852
810
|
|
853
811
|
class GuiDocViewFooter(QWidget):
|
812
|
+
"""The Embedded Document Footer
|
813
|
+
|
814
|
+
Only used by DocViewer, and is at a fixed position in the
|
815
|
+
QTextBrowser's viewport.
|
816
|
+
"""
|
854
817
|
|
855
818
|
def __init__(self, docViewer: GuiDocViewer) -> None:
|
856
819
|
super().__init__(parent=docViewer)
|
@@ -976,5 +939,3 @@ class GuiDocViewFooter(QWidget):
|
|
976
939
|
CONFIG.viewSynopsis = state
|
977
940
|
self.docViewer.reloadText()
|
978
941
|
return
|
979
|
-
|
980
|
-
# END Class GuiDocViewFooter
|
@@ -209,9 +209,8 @@ class GuiDocViewerPanel(QWidget):
|
|
209
209
|
|
210
210
|
def _updateTabVisibility(self) -> None:
|
211
211
|
"""Hide class tabs with no content."""
|
212
|
-
|
213
|
-
|
214
|
-
self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0)
|
212
|
+
for tClass, cTab in self.kwTabs.items():
|
213
|
+
self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0)
|
215
214
|
return
|
216
215
|
|
217
216
|
def _loadAllTags(self) -> None:
|
@@ -222,8 +221,6 @@ class GuiDocViewerPanel(QWidget):
|
|
222
221
|
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
|
223
222
|
return
|
224
223
|
|
225
|
-
# END Class GuiDocViewerPanel
|
226
|
-
|
227
224
|
|
228
225
|
class _ViewPanelBackRefs(QTreeWidget):
|
229
226
|
|
@@ -361,8 +358,6 @@ class _ViewPanelBackRefs(QTreeWidget):
|
|
361
358
|
|
362
359
|
return
|
363
360
|
|
364
|
-
# END Class _ViewPanelBackRefs
|
365
|
-
|
366
361
|
|
367
362
|
class _ViewPanelKeyWords(QTreeWidget):
|
368
363
|
|
@@ -450,7 +445,7 @@ class _ViewPanelKeyWords(QTreeWidget):
|
|
450
445
|
nwItem.itemType, nwItem.itemClass,
|
451
446
|
nwItem.itemLayout, nwItem.mainHeading
|
452
447
|
)
|
453
|
-
impLabel, impIcon = nwItem.getImportStatus(
|
448
|
+
impLabel, impIcon = nwItem.getImportStatus()
|
454
449
|
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0) if nwItem.isDocumentLayout() else 5
|
455
450
|
hDec = SHARED.theme.getHeaderDecorationNarrow(iLevel)
|
456
451
|
|
@@ -529,5 +524,3 @@ class _ViewPanelKeyWords(QTreeWidget):
|
|
529
524
|
if index.column() not in (self.C_EDIT, self.C_VIEW):
|
530
525
|
self._parent.loadDocumentTagRequest.emit(tag, nwDocMode.VIEW)
|
531
526
|
return
|
532
|
-
|
533
|
-
# END Class _ViewPanelKeyWords
|
@@ -28,8 +28,8 @@ import logging
|
|
28
28
|
from collections.abc import Iterable
|
29
29
|
from time import time
|
30
30
|
|
31
|
-
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextDocument
|
32
31
|
from PyQt5.QtCore import QObject, pyqtSlot
|
32
|
+
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextDocument
|
33
33
|
from PyQt5.QtWidgets import QApplication, QPlainTextDocumentLayout
|
34
34
|
|
35
35
|
from novelwriter import SHARED
|
@@ -133,5 +133,3 @@ class GuiTextDocument(QTextDocument):
|
|
133
133
|
"""Set the spell check state of the syntax highlighter."""
|
134
134
|
self._syntax.setSpellCheck(state)
|
135
135
|
return
|
136
|
-
|
137
|
-
# END Class GuiTextDocument
|
novelwriter/gui/itemdetails.py
CHANGED
@@ -26,12 +26,14 @@ from __future__ import annotations
|
|
26
26
|
import logging
|
27
27
|
|
28
28
|
from PyQt5.QtCore import pyqtSlot
|
29
|
-
from PyQt5.QtWidgets import
|
29
|
+
from PyQt5.QtWidgets import QGridLayout, QLabel, QWidget
|
30
30
|
|
31
31
|
from novelwriter import CONFIG, SHARED
|
32
|
-
from novelwriter.
|
32
|
+
from novelwriter.common import elide
|
33
|
+
from novelwriter.constants import nwLabels, trConst
|
33
34
|
from novelwriter.types import (
|
34
|
-
QtAlignLeft, QtAlignLeftBase, QtAlignRight, QtAlignRightBase,
|
35
|
+
QtAlignLeft, QtAlignLeftBase, QtAlignRight, QtAlignRightBase,
|
36
|
+
QtAlignRightMiddle
|
35
37
|
)
|
36
38
|
|
37
39
|
logger = logging.getLogger(__name__)
|
@@ -236,10 +238,6 @@ class GuiItemDetails(QWidget):
|
|
236
238
|
# Label
|
237
239
|
# =====
|
238
240
|
|
239
|
-
label = nwItem.itemName
|
240
|
-
if len(label) > 100:
|
241
|
-
label = label[:96].rstrip()+" ..."
|
242
|
-
|
243
241
|
if nwItem.isFileType():
|
244
242
|
if nwItem.isActive:
|
245
243
|
self.labelIcon.setPixmap(SHARED.theme.getPixmap("checked", (iPx, iPx)))
|
@@ -248,12 +246,12 @@ class GuiItemDetails(QWidget):
|
|
248
246
|
else:
|
249
247
|
self.labelIcon.setPixmap(SHARED.theme.getPixmap("noncheckable", (iPx, iPx)))
|
250
248
|
|
251
|
-
self.labelData.setText(
|
249
|
+
self.labelData.setText(elide(nwItem.itemName, 100))
|
252
250
|
|
253
251
|
# Status
|
254
252
|
# ======
|
255
253
|
|
256
|
-
status, icon = nwItem.getImportStatus(
|
254
|
+
status, icon = nwItem.getImportStatus()
|
257
255
|
self.statusIcon.setPixmap(icon.pixmap(iPx, iPx))
|
258
256
|
self.statusData.setText(status)
|
259
257
|
|
@@ -297,5 +295,3 @@ class GuiItemDetails(QWidget):
|
|
297
295
|
self.wCountData.setText(f"{wC:n}")
|
298
296
|
self.pCountData.setText(f"{pC:n}")
|
299
297
|
return
|
300
|
-
|
301
|
-
# END Class GuiItemDetails
|
novelwriter/gui/mainmenu.py
CHANGED
@@ -25,17 +25,17 @@ from __future__ import annotations
|
|
25
25
|
|
26
26
|
import logging
|
27
27
|
|
28
|
-
from typing import TYPE_CHECKING
|
29
28
|
from pathlib import Path
|
29
|
+
from typing import TYPE_CHECKING
|
30
30
|
|
31
|
-
from PyQt5.QtGui import QDesktopServices
|
32
31
|
from PyQt5.QtCore import QUrl, pyqtSignal, pyqtSlot
|
33
|
-
from PyQt5.
|
32
|
+
from PyQt5.QtGui import QDesktopServices
|
33
|
+
from PyQt5.QtWidgets import QAction, QMenuBar
|
34
34
|
|
35
35
|
from novelwriter import CONFIG, SHARED
|
36
36
|
from novelwriter.common import openExternalPath
|
37
|
-
from novelwriter.constants import nwConst,
|
38
|
-
from novelwriter.enum import nwDocAction, nwDocInsert,
|
37
|
+
from novelwriter.constants import nwConst, nwKeyWords, nwLabels, nwUnicode, trConst
|
38
|
+
from novelwriter.enum import nwDocAction, nwDocInsert, nwFocus, nwView
|
39
39
|
from novelwriter.extensions.eventfilters import StatusTipFilter
|
40
40
|
|
41
41
|
if TYPE_CHECKING: # pragma: no cover
|
@@ -54,7 +54,7 @@ class GuiMainMenu(QMenuBar):
|
|
54
54
|
requestDocInsert = pyqtSignal(nwDocInsert)
|
55
55
|
requestDocInsertText = pyqtSignal(str)
|
56
56
|
requestDocKeyWordInsert = pyqtSignal(str)
|
57
|
-
requestFocusChange = pyqtSignal(
|
57
|
+
requestFocusChange = pyqtSignal(nwFocus)
|
58
58
|
requestViewChange = pyqtSignal(nwView)
|
59
59
|
|
60
60
|
def __init__(self, mainGui: GuiMain) -> None:
|
@@ -167,7 +167,7 @@ class GuiMainMenu(QMenuBar):
|
|
167
167
|
# Project > Edit
|
168
168
|
self.aEditItem = self.projMenu.addAction(self.tr("Rename Item"))
|
169
169
|
self.aEditItem.setShortcut("F2")
|
170
|
-
self.aEditItem.triggered.connect(lambda: self.mainGui.
|
170
|
+
self.aEditItem.triggered.connect(lambda: self.mainGui.projView.renameTreeItem(None))
|
171
171
|
|
172
172
|
# Project > Delete
|
173
173
|
self.aDeleteItem = self.projMenu.addAction(self.tr("Delete Item"))
|
@@ -202,7 +202,7 @@ class GuiMainMenu(QMenuBar):
|
|
202
202
|
# Document > Save
|
203
203
|
self.aSaveDoc = self.docuMenu.addAction(self.tr("Save Document"))
|
204
204
|
self.aSaveDoc.setShortcut("Ctrl+S")
|
205
|
-
self.aSaveDoc.triggered.connect(self.mainGui.
|
205
|
+
self.aSaveDoc.triggered.connect(self.mainGui.forceSaveDocument)
|
206
206
|
|
207
207
|
# Document > Close
|
208
208
|
self.aCloseDoc = self.docuMenu.addAction(self.tr("Close Document"))
|
@@ -303,24 +303,24 @@ class GuiMainMenu(QMenuBar):
|
|
303
303
|
self.viewMenu = self.addMenu(self.tr("&View"))
|
304
304
|
|
305
305
|
# View > TreeView
|
306
|
-
self.aFocusTree = self.viewMenu.addAction(self.tr("Go to
|
306
|
+
self.aFocusTree = self.viewMenu.addAction(self.tr("Go to Tree View"))
|
307
307
|
self.aFocusTree.setShortcut("Ctrl+T")
|
308
308
|
self.aFocusTree.triggered.connect(
|
309
|
-
lambda: self.requestFocusChange.emit(
|
309
|
+
lambda: self.requestFocusChange.emit(nwFocus.TREE)
|
310
310
|
)
|
311
311
|
|
312
312
|
# View > Document Editor
|
313
|
-
self.
|
314
|
-
self.
|
315
|
-
self.
|
316
|
-
lambda: self.requestFocusChange.emit(
|
313
|
+
self.aFocusDocument = self.viewMenu.addAction(self.tr("Go to Document"))
|
314
|
+
self.aFocusDocument.setShortcut("Ctrl+E")
|
315
|
+
self.aFocusDocument.triggered.connect(
|
316
|
+
lambda: self.requestFocusChange.emit(nwFocus.DOCUMENT)
|
317
317
|
)
|
318
318
|
|
319
319
|
# View > Outline
|
320
320
|
self.aFocusOutline = self.viewMenu.addAction(self.tr("Go to Outline"))
|
321
321
|
self.aFocusOutline.setShortcut("Ctrl+Shift+T")
|
322
322
|
self.aFocusOutline.triggered.connect(
|
323
|
-
lambda: self.requestFocusChange.emit(
|
323
|
+
lambda: self.requestFocusChange.emit(nwFocus.OUTLINE)
|
324
324
|
)
|
325
325
|
|
326
326
|
# View > Separator
|
@@ -422,7 +422,7 @@ class GuiMainMenu(QMenuBar):
|
|
422
422
|
self.aInsMSApos = self.mInsQuotes.addAction(self.tr("Alternative Apostrophe"))
|
423
423
|
self.aInsMSApos.setShortcut("Ctrl+K, '")
|
424
424
|
self.aInsMSApos.triggered.connect(
|
425
|
-
lambda: self.requestDocInsertText.emit(nwUnicode.
|
425
|
+
lambda: self.requestDocInsertText.emit(nwUnicode.U_MAPOS)
|
426
426
|
)
|
427
427
|
|
428
428
|
# Insert > Symbols
|
@@ -597,6 +597,12 @@ class GuiMainMenu(QMenuBar):
|
|
597
597
|
lambda: self.requestDocInsert.emit(nwDocInsert.LIPSUM)
|
598
598
|
)
|
599
599
|
|
600
|
+
# Insert > Footnote
|
601
|
+
self.aFootnote = self.insMenu.addAction(self.tr("Footnote"))
|
602
|
+
self.aFootnote.triggered.connect(
|
603
|
+
lambda: self.requestDocInsert.emit(nwDocInsert.FOOTNOTE)
|
604
|
+
)
|
605
|
+
|
600
606
|
return
|
601
607
|
|
602
608
|
def _buildFormatMenu(self) -> None:
|
@@ -985,5 +991,3 @@ class GuiMainMenu(QMenuBar):
|
|
985
991
|
self.aWebsite.triggered.connect(lambda: self._openWebsite(nwConst.URL_WEB))
|
986
992
|
|
987
993
|
return
|
988
|
-
|
989
|
-
# END Class GuiMainMenu
|
novelwriter/gui/noveltree.py
CHANGED
@@ -29,14 +29,13 @@ import logging
|
|
29
29
|
|
30
30
|
from enum import Enum
|
31
31
|
from time import time
|
32
|
-
from typing import TYPE_CHECKING
|
33
32
|
|
34
|
-
from PyQt5.QtCore import QModelIndex, QPoint, Qt,
|
33
|
+
from PyQt5.QtCore import QModelIndex, QPoint, Qt, pyqtSignal, pyqtSlot
|
35
34
|
from PyQt5.QtGui import QFocusEvent, QFont, QMouseEvent, QPalette, QResizeEvent
|
36
35
|
from PyQt5.QtWidgets import (
|
37
36
|
QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView,
|
38
|
-
QInputDialog, QMenu,
|
39
|
-
|
37
|
+
QInputDialog, QMenu, QToolTip, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
|
38
|
+
QWidget
|
40
39
|
)
|
41
40
|
|
42
41
|
from novelwriter import CONFIG, SHARED
|
@@ -47,10 +46,10 @@ from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
|
|
47
46
|
from novelwriter.extensions.modified import NIconToolButton
|
48
47
|
from novelwriter.extensions.novelselector import NovelSelector
|
49
48
|
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
50
|
-
from novelwriter.types import
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
from novelwriter.types import (
|
50
|
+
QtAlignRight, QtDecoration, QtMouseLeft, QtMouseMiddle, QtSizeExpanding,
|
51
|
+
QtUserRole
|
52
|
+
)
|
54
53
|
|
55
54
|
logger = logging.getLogger(__name__)
|
56
55
|
|
@@ -62,8 +61,6 @@ class NovelTreeColumn(Enum):
|
|
62
61
|
FOCUS = 2
|
63
62
|
PLOT = 3
|
64
63
|
|
65
|
-
# END Enum NovelTreeColumn
|
66
|
-
|
67
64
|
|
68
65
|
class GuiNovelView(QWidget):
|
69
66
|
|
@@ -71,10 +68,8 @@ class GuiNovelView(QWidget):
|
|
71
68
|
selectedItemChanged = pyqtSignal(str)
|
72
69
|
openDocumentRequest = pyqtSignal(str, Enum, str, bool)
|
73
70
|
|
74
|
-
def __init__(self,
|
75
|
-
super().__init__(parent=
|
76
|
-
|
77
|
-
self.mainGui = mainGui
|
71
|
+
def __init__(self, parent: QWidget) -> None:
|
72
|
+
super().__init__(parent=parent)
|
78
73
|
|
79
74
|
# Build GUI
|
80
75
|
self.novelTree = GuiNovelTree(self)
|
@@ -188,8 +183,6 @@ class GuiNovelView(QWidget):
|
|
188
183
|
self.novelTree.refreshHandle(tHandle)
|
189
184
|
return
|
190
185
|
|
191
|
-
# END Class GuiNovelView
|
192
|
-
|
193
186
|
|
194
187
|
class GuiNovelToolBar(QWidget):
|
195
188
|
|
@@ -199,7 +192,6 @@ class GuiNovelToolBar(QWidget):
|
|
199
192
|
logger.debug("Create: GuiNovelToolBar")
|
200
193
|
|
201
194
|
self.novelView = novelView
|
202
|
-
self.mainGui = novelView.mainGui
|
203
195
|
|
204
196
|
iSz = SHARED.theme.baseIconSize
|
205
197
|
mPx = CONFIG.pxInt(2)
|
@@ -215,7 +207,7 @@ class GuiNovelToolBar(QWidget):
|
|
215
207
|
self.novelValue.setFont(selFont)
|
216
208
|
self.novelValue.setListFormat(self.tr("Outline of {0}"))
|
217
209
|
self.novelValue.setMinimumWidth(CONFIG.pxInt(150))
|
218
|
-
self.novelValue.setSizePolicy(
|
210
|
+
self.novelValue.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
|
219
211
|
self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)
|
220
212
|
|
221
213
|
self.tbNovel = NIconToolButton(self, iSz)
|
@@ -353,8 +345,6 @@ class GuiNovelToolBar(QWidget):
|
|
353
345
|
self.aLastCol[colType] = aLast
|
354
346
|
return
|
355
347
|
|
356
|
-
# END Class GuiNovelToolBar
|
357
|
-
|
358
348
|
|
359
349
|
class GuiNovelTree(QTreeWidget):
|
360
350
|
|
@@ -375,7 +365,6 @@ class GuiNovelTree(QTreeWidget):
|
|
375
365
|
logger.debug("Create: GuiNovelTree")
|
376
366
|
|
377
367
|
self.novelView = novelView
|
378
|
-
self.mainGui = novelView.mainGui
|
379
368
|
|
380
369
|
# Internal Variables
|
381
370
|
self._treeMap = {}
|
@@ -490,7 +479,7 @@ class GuiNovelTree(QTreeWidget):
|
|
490
479
|
if rootHandle is None:
|
491
480
|
rootHandle = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
|
492
481
|
|
493
|
-
treeChanged =
|
482
|
+
treeChanged = SHARED.mainGui.projView.changedSince(self._lastBuild)
|
494
483
|
indexChanged = SHARED.project.index.rootChangedSince(rootHandle, self._lastBuild)
|
495
484
|
if not (treeChanged or indexChanged or overRide):
|
496
485
|
logger.debug("No changes have been made to the novel index")
|
@@ -783,5 +772,3 @@ class GuiNovelTree(QTreeWidget):
|
|
783
772
|
if tags:
|
784
773
|
lines.append(f"<b>{trConst(nwLabels.KEY_NAME[key])}</b>: {tags}")
|
785
774
|
return lines
|
786
|
-
|
787
|
-
# END Class GuiNovelTree
|