novelWriter 2.2rc1__py3-none-any.whl → 2.3__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.2rc1.dist-info → novelWriter-2.3.dist-info}/METADATA +1 -1
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/RECORD +149 -132
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/WHEEL +1 -1
- novelWriter-2.3.dist-info/entry_points.txt +2 -0
- novelwriter/__init__.py +11 -6
- 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_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_de_DE.json +1 -0
- novelwriter/assets/i18n/project_en_US.json +1 -0
- novelwriter/assets/i18n/project_es_419.json +11 -0
- novelwriter/assets/i18n/project_fr_FR.json +11 -0
- novelwriter/assets/i18n/project_it_IT.json +11 -0
- novelwriter/assets/i18n/project_ja_JP.json +2 -1
- novelwriter/assets/i18n/project_nb_NO.json +1 -0
- novelwriter/assets/i18n/project_nl_NL.json +11 -0
- novelwriter/assets/i18n/project_pt_BR.json +11 -0
- novelwriter/assets/i18n/project_zh_CN.json +11 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +11 -2
- novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +4 -2
- novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +4 -2
- novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +4 -2
- novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_th-list.svg +9 -0
- novelwriter/assets/icons/typicons_light/icons.conf +11 -2
- novelwriter/assets/icons/typicons_light/mixed_document-new.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +4 -2
- novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +4 -2
- novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +4 -2
- novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_th-list.svg +9 -0
- novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
- novelwriter/assets/images/novelwriter-text-light.svg +4 -0
- novelwriter/assets/images/welcome-dark.jpg +0 -0
- novelwriter/assets/images/welcome-light.jpg +0 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/cyberpunk_night.conf +26 -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/tango.conf +23 -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 +4 -2
- novelwriter/assets/themes/cyberpunk_night.conf +29 -0
- novelwriter/assets/themes/default_dark.conf +2 -2
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +64 -66
- novelwriter/config.py +39 -44
- novelwriter/constants.py +39 -17
- novelwriter/core/buildsettings.py +8 -8
- novelwriter/core/coretools.py +198 -157
- novelwriter/core/docbuild.py +7 -4
- novelwriter/core/document.py +7 -7
- novelwriter/core/index.py +90 -57
- novelwriter/core/item.py +23 -5
- novelwriter/core/options.py +11 -10
- novelwriter/core/project.py +73 -47
- novelwriter/core/projectdata.py +3 -16
- novelwriter/core/projectxml.py +14 -42
- novelwriter/core/sessions.py +4 -3
- novelwriter/core/spellcheck.py +6 -4
- novelwriter/core/status.py +5 -4
- novelwriter/core/storage.py +183 -141
- novelwriter/core/tohtml.py +6 -4
- novelwriter/core/tokenizer.py +110 -83
- novelwriter/core/tomd.py +2 -2
- novelwriter/core/toodt.py +41 -31
- novelwriter/core/tree.py +5 -4
- novelwriter/dialogs/about.py +88 -179
- novelwriter/dialogs/docmerge.py +30 -20
- novelwriter/dialogs/docsplit.py +33 -22
- novelwriter/dialogs/editlabel.py +20 -8
- novelwriter/dialogs/preferences.py +562 -725
- novelwriter/dialogs/{projsettings.py → projectsettings.py} +301 -270
- novelwriter/dialogs/quotes.py +47 -36
- novelwriter/dialogs/wordlist.py +128 -59
- novelwriter/enum.py +25 -22
- novelwriter/error.py +2 -2
- novelwriter/extensions/circularprogress.py +12 -12
- novelwriter/extensions/configlayout.py +185 -146
- novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
- novelwriter/extensions/modified.py +81 -0
- novelwriter/extensions/novelselector.py +27 -13
- novelwriter/extensions/pagedsidebar.py +15 -20
- novelwriter/extensions/simpleprogress.py +8 -9
- novelwriter/extensions/statusled.py +9 -9
- novelwriter/extensions/switch.py +32 -64
- novelwriter/extensions/switchbox.py +2 -7
- novelwriter/extensions/versioninfo.py +153 -0
- novelwriter/gui/doceditor.py +250 -214
- novelwriter/gui/dochighlight.py +66 -94
- novelwriter/gui/docviewer.py +71 -98
- novelwriter/gui/docviewerpanel.py +140 -47
- novelwriter/gui/editordocument.py +3 -3
- novelwriter/gui/itemdetails.py +9 -9
- novelwriter/gui/mainmenu.py +47 -47
- novelwriter/gui/noveltree.py +53 -61
- novelwriter/gui/outline.py +100 -76
- novelwriter/gui/projtree.py +246 -112
- novelwriter/gui/sidebar.py +9 -8
- novelwriter/gui/statusbar.py +49 -7
- novelwriter/gui/theme.py +74 -76
- novelwriter/guimain.py +175 -330
- novelwriter/shared.py +68 -30
- novelwriter/tools/dictionaries.py +7 -8
- novelwriter/tools/lipsum.py +34 -28
- novelwriter/tools/manusbuild.py +3 -4
- novelwriter/tools/manuscript.py +25 -32
- novelwriter/tools/manussettings.py +194 -225
- novelwriter/tools/noveldetails.py +525 -0
- novelwriter/tools/welcome.py +819 -0
- novelwriter/tools/writingstats.py +26 -13
- novelWriter-2.2rc1.dist-info/entry_points.txt +0 -5
- novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +0 -8
- novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +0 -8
- novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +0 -8
- novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +0 -8
- novelwriter/assets/images/wizard-back.jpg +0 -0
- novelwriter/assets/text/gplv3_en.htm +0 -641
- novelwriter/assets/text/release_notes.htm +0 -17
- novelwriter/dialogs/projdetails.py +0 -525
- novelwriter/dialogs/projload.py +0 -298
- novelwriter/dialogs/updates.py +0 -182
- novelwriter/extensions/pageddialog.py +0 -130
- novelwriter/tools/projwizard.py +0 -478
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/top_level.txt +0 -0
novelwriter/dialogs/quotes.py
CHANGED
@@ -3,10 +3,10 @@ novelWriter – GUI Quotes Dialog
|
|
3
3
|
===============================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created: 2020-06-18 [0.9]
|
6
|
+
Created: 2020-06-18 [0.9.0] GuiQuoteSelect
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -26,10 +26,10 @@ from __future__ import annotations
|
|
26
26
|
import logging
|
27
27
|
|
28
28
|
from PyQt5.QtGui import QFontMetrics
|
29
|
-
from PyQt5.QtCore import Qt,
|
29
|
+
from PyQt5.QtCore import QSize, Qt, pyqtSlot
|
30
30
|
from PyQt5.QtWidgets import (
|
31
|
-
|
32
|
-
|
31
|
+
QDialog, QDialogButtonBox, QFrame, QHBoxLayout, QLabel, QListWidget,
|
32
|
+
QListWidgetItem, QVBoxLayout, QWidget
|
33
33
|
)
|
34
34
|
|
35
35
|
from novelwriter import CONFIG
|
@@ -40,18 +40,21 @@ logger = logging.getLogger(__name__)
|
|
40
40
|
|
41
41
|
class GuiQuoteSelect(QDialog):
|
42
42
|
|
43
|
-
|
43
|
+
_selected = ""
|
44
44
|
|
45
45
|
D_KEY = Qt.ItemDataRole.UserRole
|
46
46
|
|
47
|
-
def __init__(self, parent
|
47
|
+
def __init__(self, parent: QWidget, current: str = '"') -> None:
|
48
48
|
super().__init__(parent=parent)
|
49
49
|
|
50
|
+
logger.debug("Create: GuiQuoteSelect")
|
51
|
+
self.setObjectName("GuiQuoteSelect")
|
52
|
+
|
50
53
|
self.outerBox = QVBoxLayout()
|
51
54
|
self.innerBox = QHBoxLayout()
|
52
55
|
self.labelBox = QVBoxLayout()
|
53
56
|
|
54
|
-
self.
|
57
|
+
self._selected = current
|
55
58
|
|
56
59
|
qMetrics = QFontMetrics(self.font())
|
57
60
|
pxW = 7*qMetrics.boundingRectChar("M").width()
|
@@ -62,7 +65,7 @@ class GuiQuoteSelect(QDialog):
|
|
62
65
|
lblFont.setPointSizeF(4*lblFont.pointSizeF())
|
63
66
|
|
64
67
|
# Preview Label
|
65
|
-
self.previewLabel = QLabel(
|
68
|
+
self.previewLabel = QLabel(current)
|
66
69
|
self.previewLabel.setFont(lblFont)
|
67
70
|
self.previewLabel.setFixedSize(QSize(pxW, pxH))
|
68
71
|
self.previewLabel.setAlignment(Qt.AlignCenter)
|
@@ -74,12 +77,12 @@ class GuiQuoteSelect(QDialog):
|
|
74
77
|
|
75
78
|
minSize = 100
|
76
79
|
for sKey, sLabel in nwQuotes.SYMBOLS.items():
|
77
|
-
|
78
|
-
minSize = max(minSize, qMetrics.boundingRect(
|
79
|
-
qtItem = QListWidgetItem(
|
80
|
+
text = "[ %s ] %s" % (sKey, trConst(sLabel))
|
81
|
+
minSize = max(minSize, qMetrics.boundingRect(text).width())
|
82
|
+
qtItem = QListWidgetItem(text)
|
80
83
|
qtItem.setData(self.D_KEY, sKey)
|
81
84
|
self.listBox.addItem(qtItem)
|
82
|
-
if sKey ==
|
85
|
+
if sKey == current:
|
83
86
|
self.listBox.setCurrentItem(qtItem)
|
84
87
|
|
85
88
|
self.listBox.setMinimumWidth(minSize + CONFIG.pxInt(40))
|
@@ -87,8 +90,8 @@ class GuiQuoteSelect(QDialog):
|
|
87
90
|
|
88
91
|
# Buttons
|
89
92
|
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
90
|
-
self.buttonBox.accepted.connect(self.
|
91
|
-
self.buttonBox.rejected.connect(self.
|
93
|
+
self.buttonBox.accepted.connect(self.accept)
|
94
|
+
self.buttonBox.rejected.connect(self.reject)
|
92
95
|
|
93
96
|
# Assemble
|
94
97
|
self.labelBox.addWidget(self.previewLabel, 0, Qt.AlignTop)
|
@@ -102,32 +105,40 @@ class GuiQuoteSelect(QDialog):
|
|
102
105
|
|
103
106
|
self.setLayout(self.outerBox)
|
104
107
|
|
105
|
-
|
106
|
-
|
107
|
-
##
|
108
|
-
# Slots
|
109
|
-
##
|
108
|
+
logger.debug("Ready: GuiQuoteSelect")
|
110
109
|
|
111
|
-
def _selectedSymbol(self):
|
112
|
-
"""Update the preview label and the selected quote style.
|
113
|
-
"""
|
114
|
-
selItems = self.listBox.selectedItems()
|
115
|
-
if selItems:
|
116
|
-
theSymbol = selItems[0].data(self.D_KEY)
|
117
|
-
self.previewLabel.setText(theSymbol)
|
118
|
-
self.selectedQuote = theSymbol
|
119
110
|
return
|
120
111
|
|
121
|
-
def
|
122
|
-
""
|
123
|
-
"""
|
124
|
-
self.accept()
|
112
|
+
def __del__(self) -> None: # pragma: no cover
|
113
|
+
logger.debug("Delete: GuiQuoteSelect")
|
125
114
|
return
|
126
115
|
|
127
|
-
|
128
|
-
|
129
|
-
"""
|
130
|
-
self.
|
116
|
+
@property
|
117
|
+
def selectedQuote(self) -> str:
|
118
|
+
"""Return the selected quote symbol."""
|
119
|
+
return self._selected
|
120
|
+
|
121
|
+
@classmethod
|
122
|
+
def getQuote(cls, parent: QWidget, current: str = "") -> tuple[str, bool]:
|
123
|
+
"""Pop the dialog and return the result."""
|
124
|
+
cls = GuiQuoteSelect(parent, current=current)
|
125
|
+
cls.exec_()
|
126
|
+
quote = cls._selected
|
127
|
+
accepted = cls.result() == QDialog.DialogCode.Accepted
|
128
|
+
cls.deleteLater()
|
129
|
+
return quote, accepted
|
130
|
+
|
131
|
+
##
|
132
|
+
# Private Slots
|
133
|
+
##
|
134
|
+
|
135
|
+
@pyqtSlot()
|
136
|
+
def _selectedSymbol(self) -> None:
|
137
|
+
"""Update the preview label and the selected quote style."""
|
138
|
+
if items := self.listBox.selectedItems():
|
139
|
+
quote = items[0].data(self.D_KEY)
|
140
|
+
self.previewLabel.setText(quote)
|
141
|
+
self._selected = quote
|
131
142
|
return
|
132
143
|
|
133
144
|
# END Class GuiQuoteSelect
|
novelwriter/dialogs/wordlist.py
CHANGED
@@ -3,10 +3,10 @@ novelWriter – GUI User Wordlist
|
|
3
3
|
===============================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created: 2021-02-12 [1.2rc1]
|
6
|
+
Created: 2021-02-12 [1.2rc1] GuiWordList
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -26,15 +26,19 @@ from __future__ import annotations
|
|
26
26
|
import logging
|
27
27
|
|
28
28
|
from typing import TYPE_CHECKING
|
29
|
+
from pathlib import Path
|
29
30
|
|
30
|
-
from PyQt5.
|
31
|
+
from PyQt5.QtGui import QCloseEvent
|
32
|
+
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
31
33
|
from PyQt5.QtWidgets import (
|
32
|
-
QAbstractItemView, QDialog, QDialogButtonBox,
|
33
|
-
QLineEdit, QListWidget,
|
34
|
+
QAbstractItemView, QDialog, QDialogButtonBox, QFileDialog, QHBoxLayout,
|
35
|
+
QLineEdit, QListWidget, QPushButton, QVBoxLayout, qApp
|
34
36
|
)
|
35
37
|
|
36
38
|
from novelwriter import CONFIG, SHARED
|
39
|
+
from novelwriter.common import formatFileFilter
|
37
40
|
from novelwriter.core.spellcheck import UserDictionary
|
41
|
+
from novelwriter.extensions.configlayout import NColourLabel
|
38
42
|
|
39
43
|
if TYPE_CHECKING: # pragma: no cover
|
40
44
|
from novelwriter.guimain import GuiMain
|
@@ -44,7 +48,9 @@ logger = logging.getLogger(__name__)
|
|
44
48
|
|
45
49
|
class GuiWordList(QDialog):
|
46
50
|
|
47
|
-
|
51
|
+
newWordListReady = pyqtSignal()
|
52
|
+
|
53
|
+
def __init__(self, mainGui: GuiMain) -> None:
|
48
54
|
super().__init__(parent=mainGui)
|
49
55
|
|
50
56
|
logger.debug("Create: GuiWordList")
|
@@ -54,30 +60,46 @@ class GuiWordList(QDialog):
|
|
54
60
|
mS = CONFIG.pxInt(250)
|
55
61
|
wW = CONFIG.pxInt(320)
|
56
62
|
wH = CONFIG.pxInt(340)
|
57
|
-
pOptions = SHARED.project.options
|
58
63
|
|
59
64
|
self.setMinimumWidth(mS)
|
60
65
|
self.setMinimumHeight(mS)
|
61
66
|
self.resize(
|
62
|
-
CONFIG.pxInt(
|
63
|
-
CONFIG.pxInt(
|
67
|
+
CONFIG.pxInt(SHARED.project.options.getInt("GuiWordList", "winWidth", wW)),
|
68
|
+
CONFIG.pxInt(SHARED.project.options.getInt("GuiWordList", "winHeight", wH))
|
69
|
+
)
|
70
|
+
|
71
|
+
# Header
|
72
|
+
self.headLabel = NColourLabel(
|
73
|
+
"Project Word List", SHARED.theme.helpText, parent=self,
|
74
|
+
scale=NColourLabel.HEADER_SCALE
|
64
75
|
)
|
65
76
|
|
66
|
-
|
67
|
-
|
77
|
+
self.importButton = QPushButton(SHARED.theme.getIcon("import"), "", self)
|
78
|
+
self.importButton.setToolTip(self.tr("Import words from text file"))
|
79
|
+
self.importButton.clicked.connect(self._importWords)
|
68
80
|
|
69
|
-
self.
|
81
|
+
self.exportButton = QPushButton(SHARED.theme.getIcon("export"), "", self)
|
82
|
+
self.exportButton.setToolTip(self.tr("Export words to text file"))
|
83
|
+
self.exportButton.clicked.connect(self._exportWords)
|
70
84
|
|
71
|
-
self.
|
85
|
+
self.headerBox = QHBoxLayout()
|
86
|
+
self.headerBox.addWidget(self.headLabel, 1)
|
87
|
+
self.headerBox.addWidget(self.importButton, 0)
|
88
|
+
self.headerBox.addWidget(self.exportButton, 0)
|
89
|
+
|
90
|
+
# List Box
|
91
|
+
self.listBox = QListWidget(self)
|
72
92
|
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
|
93
|
+
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
|
73
94
|
self.listBox.setSortingEnabled(True)
|
74
95
|
|
75
|
-
|
96
|
+
# Add/Remove Form
|
97
|
+
self.newEntry = QLineEdit(self)
|
76
98
|
|
77
|
-
self.addButton = QPushButton(SHARED.theme.getIcon("add"), "")
|
99
|
+
self.addButton = QPushButton(SHARED.theme.getIcon("add"), "", self)
|
78
100
|
self.addButton.clicked.connect(self._doAdd)
|
79
101
|
|
80
|
-
self.delButton = QPushButton(SHARED.theme.getIcon("remove"), "")
|
102
|
+
self.delButton = QPushButton(SHARED.theme.getIcon("remove"), "", self)
|
81
103
|
self.delButton.clicked.connect(self._doDelete)
|
82
104
|
|
83
105
|
self.editBox = QHBoxLayout()
|
@@ -85,20 +107,19 @@ class GuiWordList(QDialog):
|
|
85
107
|
self.editBox.addWidget(self.addButton, 0)
|
86
108
|
self.editBox.addWidget(self.delButton, 0)
|
87
109
|
|
110
|
+
# Buttons
|
88
111
|
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Close)
|
89
112
|
self.buttonBox.accepted.connect(self._doSave)
|
90
|
-
self.buttonBox.rejected.connect(self.
|
113
|
+
self.buttonBox.rejected.connect(self.close)
|
91
114
|
|
92
115
|
# Assemble
|
93
|
-
# ========
|
94
|
-
|
95
116
|
self.outerBox = QVBoxLayout()
|
96
|
-
self.outerBox.
|
97
|
-
self.outerBox.addSpacing(CONFIG.pxInt(8))
|
117
|
+
self.outerBox.addLayout(self.headerBox, 0)
|
98
118
|
self.outerBox.addWidget(self.listBox, 1)
|
99
119
|
self.outerBox.addLayout(self.editBox, 0)
|
100
120
|
self.outerBox.addSpacing(CONFIG.pxInt(12))
|
101
121
|
self.outerBox.addWidget(self.buttonBox, 0)
|
122
|
+
self.outerBox.setSpacing(CONFIG.pxInt(4))
|
102
123
|
|
103
124
|
self.setLayout(self.outerBox)
|
104
125
|
|
@@ -108,82 +129,130 @@ class GuiWordList(QDialog):
|
|
108
129
|
|
109
130
|
return
|
110
131
|
|
111
|
-
def __del__(self): # pragma: no cover
|
132
|
+
def __del__(self) -> None: # pragma: no cover
|
112
133
|
logger.debug("Delete: GuiWordList")
|
113
134
|
return
|
114
135
|
|
115
136
|
##
|
116
|
-
#
|
137
|
+
# Events
|
117
138
|
##
|
118
139
|
|
119
|
-
def
|
120
|
-
"""
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
140
|
+
def closeEvent(self, event: QCloseEvent) -> None:
|
141
|
+
"""Capture the close event and perform cleanup."""
|
142
|
+
self._saveGuiSettings()
|
143
|
+
event.accept()
|
144
|
+
self.deleteLater()
|
145
|
+
return
|
125
146
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
).format(word))
|
130
|
-
return
|
147
|
+
##
|
148
|
+
# Private Slots
|
149
|
+
##
|
131
150
|
|
132
|
-
|
151
|
+
@pyqtSlot()
|
152
|
+
def _doAdd(self) -> None:
|
153
|
+
"""Add a new word to the word list."""
|
154
|
+
word = self.newEntry.text().strip()
|
133
155
|
self.newEntry.setText("")
|
134
|
-
|
156
|
+
self.listBox.clearSelection()
|
157
|
+
self._addWord(word)
|
158
|
+
if items := self.listBox.findItems(word, Qt.MatchExactly):
|
159
|
+
self.listBox.setCurrentItem(items[0])
|
160
|
+
self.listBox.scrollToItem(items[0], QAbstractItemView.ScrollHint.PositionAtCenter)
|
135
161
|
return
|
136
162
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
self.listBox.takeItem(self.listBox.row(
|
163
|
+
@pyqtSlot()
|
164
|
+
def _doDelete(self) -> None:
|
165
|
+
"""Delete the selected items."""
|
166
|
+
for item in self.listBox.selectedItems():
|
167
|
+
self.listBox.takeItem(self.listBox.row(item))
|
142
168
|
return
|
143
169
|
|
144
|
-
|
170
|
+
@pyqtSlot()
|
171
|
+
def _doSave(self) -> None:
|
145
172
|
"""Save the new word list and close."""
|
146
|
-
self._saveGuiSettings()
|
147
173
|
userDict = UserDictionary(SHARED.project)
|
148
|
-
for
|
149
|
-
|
150
|
-
if isinstance(item, QListWidgetItem):
|
151
|
-
word = item.text().strip()
|
152
|
-
if word:
|
153
|
-
userDict.add(word)
|
174
|
+
for word in self._listWords():
|
175
|
+
userDict.add(word)
|
154
176
|
userDict.save()
|
155
|
-
self.
|
156
|
-
|
177
|
+
self.newWordListReady.emit()
|
178
|
+
qApp.processEvents()
|
179
|
+
self.close()
|
180
|
+
return
|
157
181
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
self.
|
182
|
+
@pyqtSlot()
|
183
|
+
def _importWords(self) -> None:
|
184
|
+
"""Import words from file."""
|
185
|
+
SHARED.info(self.tr(
|
186
|
+
"Note: The import file must be a plain text file with UTF-8 or ASCII encoding."
|
187
|
+
))
|
188
|
+
ffilter = formatFileFilter(["*.txt", "*"])
|
189
|
+
path, _ = QFileDialog.getOpenFileName(
|
190
|
+
self, self.tr("Import File"), str(Path.home()), filter=ffilter
|
191
|
+
)
|
192
|
+
if path:
|
193
|
+
try:
|
194
|
+
with open(path, mode="r", encoding="utf-8") as fo:
|
195
|
+
words = set(w.strip() for w in fo.read().split())
|
196
|
+
except Exception as exc:
|
197
|
+
SHARED.error("Could not read file.", exc=exc)
|
198
|
+
return
|
199
|
+
for word in words:
|
200
|
+
self._addWord(word)
|
201
|
+
return
|
202
|
+
|
203
|
+
@pyqtSlot()
|
204
|
+
def _exportWords(self) -> None:
|
205
|
+
"""Export words to file."""
|
206
|
+
path, _ = QFileDialog.getSaveFileName(
|
207
|
+
self, self.tr("Export File"), str(Path.home())
|
208
|
+
)
|
209
|
+
if path:
|
210
|
+
try:
|
211
|
+
path = Path(path).with_suffix(".txt")
|
212
|
+
with open(path, mode="w", encoding="utf-8") as fo:
|
213
|
+
fo.write("\n".join(self._listWords()))
|
214
|
+
except Exception as exc:
|
215
|
+
SHARED.error("Could not write file.", exc=exc)
|
162
216
|
return
|
163
217
|
|
164
218
|
##
|
165
219
|
# Internal Functions
|
166
220
|
##
|
167
221
|
|
168
|
-
def _loadWordList(self):
|
222
|
+
def _loadWordList(self) -> None:
|
169
223
|
"""Load the project's word list, if it exists."""
|
170
224
|
userDict = UserDictionary(SHARED.project)
|
171
225
|
userDict.load()
|
172
226
|
self.listBox.clear()
|
173
227
|
for word in userDict:
|
174
|
-
|
175
|
-
self.listBox.addItem(word)
|
228
|
+
self.listBox.addItem(word)
|
176
229
|
return
|
177
230
|
|
178
|
-
def _saveGuiSettings(self):
|
231
|
+
def _saveGuiSettings(self) -> None:
|
179
232
|
"""Save GUI settings."""
|
180
233
|
winWidth = CONFIG.rpxInt(self.width())
|
181
234
|
winHeight = CONFIG.rpxInt(self.height())
|
182
235
|
|
236
|
+
logger.debug("Saving State: GuiWordList")
|
183
237
|
pOptions = SHARED.project.options
|
184
238
|
pOptions.setValue("GuiWordList", "winWidth", winWidth)
|
185
239
|
pOptions.setValue("GuiWordList", "winHeight", winHeight)
|
186
240
|
|
187
241
|
return
|
188
242
|
|
243
|
+
def _addWord(self, word: str) -> None:
|
244
|
+
"""Add a single word to the list."""
|
245
|
+
if word and not self.listBox.findItems(word, Qt.MatchExactly):
|
246
|
+
self.listBox.addItem(word)
|
247
|
+
self._changed = True
|
248
|
+
return
|
249
|
+
|
250
|
+
def _listWords(self) -> list[str]:
|
251
|
+
"""List all words in the list box."""
|
252
|
+
result = []
|
253
|
+
for i in range(self.listBox.count()):
|
254
|
+
if (item := self.listBox.item(i)) and (word := item.text().strip()):
|
255
|
+
result.append(word)
|
256
|
+
return result
|
257
|
+
|
189
258
|
# END Class GuiWordList
|
novelwriter/enum.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2018-11-02 [0.0.1]
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -47,7 +47,8 @@ class nwItemClass(Enum):
|
|
47
47
|
ENTITY = 7
|
48
48
|
CUSTOM = 8
|
49
49
|
ARCHIVE = 9
|
50
|
-
|
50
|
+
TEMPLATE = 10
|
51
|
+
TRASH = 11
|
51
52
|
|
52
53
|
# END Enum nwItemClass
|
53
54
|
|
@@ -95,9 +96,9 @@ class nwDocAction(Enum):
|
|
95
96
|
CUT = 3
|
96
97
|
COPY = 4
|
97
98
|
PASTE = 5
|
98
|
-
|
99
|
-
|
100
|
-
|
99
|
+
MD_ITALIC = 6
|
100
|
+
MD_BOLD = 7
|
101
|
+
MD_STRIKE = 8
|
101
102
|
S_QUOTE = 9
|
102
103
|
D_QUOTE = 10
|
103
104
|
SEL_ALL = 11
|
@@ -107,23 +108,24 @@ class nwDocAction(Enum):
|
|
107
108
|
BLOCK_H3 = 15
|
108
109
|
BLOCK_H4 = 16
|
109
110
|
BLOCK_COM = 17
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
111
|
+
BLOCK_IGN = 18
|
112
|
+
BLOCK_TXT = 19
|
113
|
+
BLOCK_TTL = 20
|
114
|
+
BLOCK_UNN = 21
|
115
|
+
REPL_SNG = 22
|
116
|
+
REPL_DBL = 23
|
117
|
+
RM_BREAKS = 24
|
118
|
+
ALIGN_L = 25
|
119
|
+
ALIGN_C = 26
|
120
|
+
ALIGN_R = 27
|
121
|
+
INDENT_L = 28
|
122
|
+
INDENT_R = 29
|
123
|
+
SC_ITALIC = 30
|
124
|
+
SC_BOLD = 31
|
125
|
+
SC_STRIKE = 32
|
126
|
+
SC_ULINE = 33
|
127
|
+
SC_SUP = 34
|
128
|
+
SC_SUB = 35
|
127
129
|
|
128
130
|
# END Enum nwDocAction
|
129
131
|
|
@@ -140,6 +142,7 @@ class nwDocInsert(Enum):
|
|
140
142
|
NEW_PAGE = 7
|
141
143
|
VSPACE_S = 8
|
142
144
|
VSPACE_M = 9
|
145
|
+
LIPSUM = 10
|
143
146
|
|
144
147
|
# END Enum nwDocInsert
|
145
148
|
|
novelwriter/error.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2020-08-02 [0.10.2]
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -50,7 +50,7 @@ def logException() -> None:
|
|
50
50
|
"""Log the content of an exception message."""
|
51
51
|
exType, exValue, _ = sys.exc_info()
|
52
52
|
if exType is not None:
|
53
|
-
logger.error("
|
53
|
+
logger.error(f"{exType.__name__}: {str(exValue)}", stacklevel=2)
|
54
54
|
return
|
55
55
|
|
56
56
|
|
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2023-06-07 [2.1b1]
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -84,17 +84,17 @@ class NProgressCircle(QProgressBar):
|
|
84
84
|
"""Custom painter for the progress bar."""
|
85
85
|
progress = 100.0*self.value()/self.maximum()
|
86
86
|
angle = ceil(16*3.6*progress)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
87
|
+
painter = QPainter(self)
|
88
|
+
painter.setRenderHint(QPainter.Antialiasing, True)
|
89
|
+
painter.setPen(self._dPen)
|
90
|
+
painter.setBrush(self._dBrush)
|
91
|
+
painter.drawEllipse(self._dRect)
|
92
|
+
painter.setPen(self._bPen)
|
93
|
+
painter.drawArc(self._cRect, 0, 360*16)
|
94
|
+
painter.setPen(self._cPen)
|
95
|
+
painter.drawArc(self._cRect, 90*16, -angle)
|
96
|
+
painter.setPen(self._tColor)
|
97
|
+
painter.drawText(self._cRect, Qt.AlignCenter, self._text or f"{progress:.1f} %")
|
98
98
|
return
|
99
99
|
|
100
100
|
# END Class NProgressCircle
|