novelWriter 2.2.1__py3-none-any.whl → 2.3b1__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.2.1.dist-info → novelWriter-2.3b1.dist-info}/METADATA +1 -1
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +102 -92
- novelwriter/__init__.py +4 -4
- novelwriter/assets/icons/typicons_dark/icons.conf +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_dark/typ_document-add-col.svg +8 -0
- 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_light/icons.conf +6 -0
- novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_light/typ_document-add-col.svg +8 -0
- 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/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/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 +4 -2
- novelwriter/assets/themes/default_dark.conf +2 -2
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +48 -37
- novelwriter/config.py +36 -41
- novelwriter/constants.py +38 -16
- novelwriter/core/buildsettings.py +7 -7
- novelwriter/core/coretools.py +192 -154
- novelwriter/core/docbuild.py +6 -3
- novelwriter/core/document.py +6 -6
- novelwriter/core/index.py +89 -56
- novelwriter/core/item.py +21 -3
- novelwriter/core/options.py +8 -7
- novelwriter/core/project.py +69 -44
- novelwriter/core/projectdata.py +1 -14
- novelwriter/core/projectxml.py +13 -41
- novelwriter/core/sessions.py +2 -1
- novelwriter/core/spellcheck.py +2 -1
- novelwriter/core/status.py +2 -1
- novelwriter/core/storage.py +178 -140
- novelwriter/core/tohtml.py +4 -2
- novelwriter/core/tokenizer.py +73 -45
- novelwriter/core/toodt.py +40 -30
- novelwriter/core/tree.py +3 -2
- novelwriter/dialogs/about.py +70 -160
- novelwriter/dialogs/docmerge.py +6 -5
- novelwriter/dialogs/docsplit.py +6 -6
- novelwriter/dialogs/editlabel.py +1 -1
- novelwriter/dialogs/preferences.py +553 -703
- novelwriter/dialogs/{projsettings.py → projectsettings.py} +288 -262
- novelwriter/dialogs/quotes.py +27 -23
- novelwriter/dialogs/wordlist.py +96 -40
- novelwriter/enum.py +20 -18
- novelwriter/error.py +1 -1
- novelwriter/extensions/circularprogress.py +11 -11
- novelwriter/extensions/configlayout.py +185 -134
- novelwriter/extensions/modified.py +81 -0
- novelwriter/extensions/novelselector.py +26 -12
- novelwriter/extensions/pagedsidebar.py +14 -16
- novelwriter/extensions/simpleprogress.py +5 -5
- novelwriter/extensions/statusled.py +8 -8
- novelwriter/extensions/switch.py +31 -63
- novelwriter/extensions/switchbox.py +1 -1
- novelwriter/extensions/versioninfo.py +153 -0
- novelwriter/gui/doceditor.py +178 -150
- novelwriter/gui/dochighlight.py +63 -92
- novelwriter/gui/docviewer.py +49 -51
- novelwriter/gui/docviewerpanel.py +72 -24
- novelwriter/gui/itemdetails.py +7 -7
- novelwriter/gui/mainmenu.py +14 -18
- novelwriter/gui/noveltree.py +9 -8
- novelwriter/gui/outline.py +98 -75
- novelwriter/gui/projtree.py +188 -61
- novelwriter/gui/sidebar.py +3 -4
- novelwriter/gui/statusbar.py +3 -4
- novelwriter/gui/theme.py +60 -68
- novelwriter/guimain.py +49 -156
- novelwriter/shared.py +15 -1
- novelwriter/tools/dictionaries.py +5 -6
- novelwriter/tools/manuscript.py +6 -6
- novelwriter/tools/manussettings.py +192 -221
- novelwriter/tools/noveldetails.py +525 -0
- novelwriter/tools/welcome.py +802 -0
- novelwriter/tools/writingstats.py +9 -9
- novelwriter/assets/images/wizard-back.jpg +0 -0
- novelwriter/assets/text/gplv3_en.htm +0 -641
- novelwriter/assets/text/release_notes.htm +0 -60
- novelwriter/dialogs/projdetails.py +0 -518
- novelwriter/dialogs/projload.py +0 -294
- novelwriter/dialogs/updates.py +0 -172
- novelwriter/extensions/pageddialog.py +0 -130
- novelwriter/tools/projwizard.py +0 -478
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
novelwriter/dialogs/projload.py
DELETED
@@ -1,294 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
novelWriter – GUI Open Project
|
3
|
-
==============================
|
4
|
-
|
5
|
-
File History:
|
6
|
-
Created: 2020-02-26 [0.4.5] GuiProjectLoad
|
7
|
-
|
8
|
-
This file is a part of novelWriter
|
9
|
-
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
|
-
|
11
|
-
This program is free software: you can redistribute it and/or modify
|
12
|
-
it under the terms of the GNU General Public License as published by
|
13
|
-
the Free Software Foundation, either version 3 of the License, or
|
14
|
-
(at your option) any later version.
|
15
|
-
|
16
|
-
This program is distributed in the hope that it will be useful, but
|
17
|
-
WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
19
|
-
General Public License for more details.
|
20
|
-
|
21
|
-
You should have received a copy of the GNU General Public License
|
22
|
-
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
23
|
-
"""
|
24
|
-
from __future__ import annotations
|
25
|
-
|
26
|
-
import logging
|
27
|
-
|
28
|
-
from typing import TYPE_CHECKING
|
29
|
-
from pathlib import Path
|
30
|
-
from datetime import datetime
|
31
|
-
|
32
|
-
from PyQt5.QtGui import QCloseEvent, QKeySequence
|
33
|
-
from PyQt5.QtCore import Qt, QSize, pyqtSlot
|
34
|
-
from PyQt5.QtWidgets import (
|
35
|
-
QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QTreeWidget,
|
36
|
-
QAbstractItemView, QTreeWidgetItem, QDialogButtonBox, QLabel, QShortcut,
|
37
|
-
QFileDialog, QLineEdit
|
38
|
-
)
|
39
|
-
|
40
|
-
from novelwriter import CONFIG, SHARED
|
41
|
-
from novelwriter.common import formatInt
|
42
|
-
from novelwriter.constants import nwFiles
|
43
|
-
|
44
|
-
if TYPE_CHECKING: # pragma: no cover
|
45
|
-
from novelwriter.guimain import GuiMain
|
46
|
-
|
47
|
-
logger = logging.getLogger(__name__)
|
48
|
-
|
49
|
-
|
50
|
-
class GuiProjectLoad(QDialog):
|
51
|
-
|
52
|
-
NONE_STATE = 0
|
53
|
-
NEW_STATE = 1
|
54
|
-
OPEN_STATE = 2
|
55
|
-
|
56
|
-
C_NAME = 0
|
57
|
-
C_COUNT = 1
|
58
|
-
C_TIME = 2
|
59
|
-
|
60
|
-
D_PATH = Qt.ItemDataRole.UserRole
|
61
|
-
|
62
|
-
def __init__(self, mainGui: GuiMain) -> None:
|
63
|
-
super().__init__(parent=mainGui)
|
64
|
-
|
65
|
-
logger.debug("Create: GuiProjectLoad")
|
66
|
-
self.setObjectName("GuiProjectLoad")
|
67
|
-
|
68
|
-
self.openState = self.NONE_STATE
|
69
|
-
self.openPath = None
|
70
|
-
|
71
|
-
sPx = CONFIG.pxInt(16)
|
72
|
-
nPx = CONFIG.pxInt(96)
|
73
|
-
iPx = SHARED.theme.baseIconSize
|
74
|
-
|
75
|
-
self.outerBox = QVBoxLayout()
|
76
|
-
self.innerBox = QHBoxLayout()
|
77
|
-
self.outerBox.setSpacing(sPx)
|
78
|
-
self.innerBox.setSpacing(sPx)
|
79
|
-
|
80
|
-
self.setWindowTitle(self.tr("Open Project"))
|
81
|
-
self.setMinimumWidth(CONFIG.pxInt(650))
|
82
|
-
self.setMinimumHeight(CONFIG.pxInt(400))
|
83
|
-
|
84
|
-
self.nwIcon = QLabel()
|
85
|
-
self.nwIcon.setPixmap(SHARED.theme.getPixmap("novelwriter", (nPx, nPx)))
|
86
|
-
self.innerBox.addWidget(self.nwIcon, 0, Qt.AlignTop)
|
87
|
-
|
88
|
-
self.projectForm = QGridLayout()
|
89
|
-
self.projectForm.setContentsMargins(0, 0, 0, 0)
|
90
|
-
|
91
|
-
self.listBox = QTreeWidget()
|
92
|
-
self.listBox.setSelectionMode(QAbstractItemView.SingleSelection)
|
93
|
-
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
|
94
|
-
self.listBox.setColumnCount(3)
|
95
|
-
self.listBox.setHeaderLabels([
|
96
|
-
self.tr("Working Title"),
|
97
|
-
self.tr("Words"),
|
98
|
-
self.tr("Last Opened"),
|
99
|
-
])
|
100
|
-
self.listBox.setRootIsDecorated(False)
|
101
|
-
self.listBox.itemSelectionChanged.connect(self._doSelectRecent)
|
102
|
-
self.listBox.itemDoubleClicked.connect(self._doOpenRecent)
|
103
|
-
self.listBox.setIconSize(QSize(iPx, iPx))
|
104
|
-
|
105
|
-
treeHead = self.listBox.headerItem()
|
106
|
-
if treeHead:
|
107
|
-
treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
|
108
|
-
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
|
109
|
-
|
110
|
-
self.lblRecent = QLabel("<b>%s</b>" % self.tr("Recently Opened Projects"))
|
111
|
-
self.lblPath = QLabel("<b>%s</b>" % self.tr("Path"))
|
112
|
-
self.selPath = QLineEdit("")
|
113
|
-
self.selPath.setReadOnly(True)
|
114
|
-
|
115
|
-
self.browseButton = QPushButton(SHARED.theme.getIcon("browse"), "", self)
|
116
|
-
self.browseButton.clicked.connect(self._doBrowse)
|
117
|
-
|
118
|
-
self.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3)
|
119
|
-
self.projectForm.addWidget(self.listBox, 1, 0, 1, 3)
|
120
|
-
self.projectForm.addWidget(self.lblPath, 2, 0, 1, 1)
|
121
|
-
self.projectForm.addWidget(self.selPath, 2, 1, 1, 1)
|
122
|
-
self.projectForm.addWidget(self.browseButton, 2, 2, 1, 1)
|
123
|
-
self.projectForm.setColumnStretch(0, 0)
|
124
|
-
self.projectForm.setColumnStretch(1, 1)
|
125
|
-
self.projectForm.setColumnStretch(2, 0)
|
126
|
-
self.projectForm.setVerticalSpacing(CONFIG.pxInt(4))
|
127
|
-
self.projectForm.setHorizontalSpacing(CONFIG.pxInt(8))
|
128
|
-
|
129
|
-
self.innerBox.addLayout(self.projectForm)
|
130
|
-
|
131
|
-
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel)
|
132
|
-
self.buttonBox.accepted.connect(self._doOpenRecent)
|
133
|
-
self.buttonBox.rejected.connect(self._doCancel)
|
134
|
-
|
135
|
-
self.newButton = self.buttonBox.addButton(self.tr("New"), QDialogButtonBox.ActionRole)
|
136
|
-
self.newButton.clicked.connect(self._doNewProject)
|
137
|
-
|
138
|
-
self.delButton = self.buttonBox.addButton(self.tr("Remove"), QDialogButtonBox.ActionRole)
|
139
|
-
self.delButton.clicked.connect(self._doDeleteRecent)
|
140
|
-
|
141
|
-
self.outerBox.addLayout(self.innerBox)
|
142
|
-
self.outerBox.addWidget(self.buttonBox)
|
143
|
-
self.setLayout(self.outerBox)
|
144
|
-
|
145
|
-
self._populateList()
|
146
|
-
self._doSelectRecent()
|
147
|
-
|
148
|
-
keyDelete = QShortcut(self.listBox)
|
149
|
-
keyDelete.setKey(QKeySequence(Qt.Key_Delete))
|
150
|
-
keyDelete.activated.connect(self._doDeleteRecent)
|
151
|
-
|
152
|
-
logger.debug("Ready: GuiProjectLoad")
|
153
|
-
|
154
|
-
return
|
155
|
-
|
156
|
-
def __del__(self) -> None: # pragma: no cover
|
157
|
-
logger.debug("Delete: GuiProjectLoad")
|
158
|
-
return
|
159
|
-
|
160
|
-
##
|
161
|
-
# Private Slots
|
162
|
-
##
|
163
|
-
|
164
|
-
@pyqtSlot()
|
165
|
-
def _doOpenRecent(self) -> None:
|
166
|
-
"""Close the dialog window with a recent project selected."""
|
167
|
-
self._saveSettings()
|
168
|
-
|
169
|
-
self.openPath = None
|
170
|
-
self.openState = self.NONE_STATE
|
171
|
-
|
172
|
-
selItems = self.listBox.selectedItems()
|
173
|
-
if selItems:
|
174
|
-
self.openPath = selItems[0].data(self.C_NAME, self.D_PATH)
|
175
|
-
self.openState = self.OPEN_STATE
|
176
|
-
self.accept()
|
177
|
-
|
178
|
-
return
|
179
|
-
|
180
|
-
@pyqtSlot()
|
181
|
-
def _doSelectRecent(self) -> None:
|
182
|
-
"""Update path when a recent item has been selected."""
|
183
|
-
selList = self.listBox.selectedItems()
|
184
|
-
if selList:
|
185
|
-
self.selPath.setText(selList[0].data(self.C_NAME, self.D_PATH))
|
186
|
-
return
|
187
|
-
|
188
|
-
@pyqtSlot()
|
189
|
-
def _doBrowse(self) -> None:
|
190
|
-
"""Browse for a folder path."""
|
191
|
-
extFilter = [
|
192
|
-
self.tr("novelWriter Project File ({0})").format(nwFiles.PROJ_FILE),
|
193
|
-
self.tr("All files ({0})").format("*"),
|
194
|
-
]
|
195
|
-
projFile, _ = QFileDialog.getOpenFileName(
|
196
|
-
self, self.tr("Open Project"), "", filter=";;".join(extFilter)
|
197
|
-
)
|
198
|
-
if projFile:
|
199
|
-
thePath = Path(projFile).absolute()
|
200
|
-
self.selPath.setText(str(thePath))
|
201
|
-
self.openPath = thePath
|
202
|
-
self.openState = self.OPEN_STATE
|
203
|
-
self.accept()
|
204
|
-
|
205
|
-
return
|
206
|
-
|
207
|
-
@pyqtSlot()
|
208
|
-
def _doCancel(self) -> None:
|
209
|
-
"""Close the dialog window without doing anything."""
|
210
|
-
self.openPath = None
|
211
|
-
self.openState = self.NONE_STATE
|
212
|
-
self.close()
|
213
|
-
return
|
214
|
-
|
215
|
-
@pyqtSlot()
|
216
|
-
def _doNewProject(self) -> None:
|
217
|
-
"""Create a new project."""
|
218
|
-
self._saveSettings()
|
219
|
-
self.openPath = None
|
220
|
-
self.openState = self.NEW_STATE
|
221
|
-
self.accept()
|
222
|
-
return
|
223
|
-
|
224
|
-
@pyqtSlot()
|
225
|
-
def _doDeleteRecent(self) -> None:
|
226
|
-
"""Remove an entry from the recent projects list."""
|
227
|
-
selList = self.listBox.selectedItems()
|
228
|
-
if selList:
|
229
|
-
projName = selList[0].text(self.C_NAME)
|
230
|
-
msgYes = SHARED.question(self.tr(
|
231
|
-
"Remove '{0}' from the recent projects list? "
|
232
|
-
"The project files will not be deleted."
|
233
|
-
).format(projName))
|
234
|
-
if msgYes:
|
235
|
-
CONFIG.recentProjects.remove(
|
236
|
-
selList[0].data(self.C_NAME, self.D_PATH)
|
237
|
-
)
|
238
|
-
self._populateList()
|
239
|
-
|
240
|
-
return
|
241
|
-
|
242
|
-
##
|
243
|
-
# Events
|
244
|
-
##
|
245
|
-
|
246
|
-
def closeEvent(self, event: QCloseEvent) -> None:
|
247
|
-
"""Capture the user closing the dialog and save settings."""
|
248
|
-
self._saveSettings()
|
249
|
-
event.accept()
|
250
|
-
return
|
251
|
-
|
252
|
-
##
|
253
|
-
# Internal Functions
|
254
|
-
##
|
255
|
-
|
256
|
-
def _saveSettings(self) -> None:
|
257
|
-
"""Save the changes made to the dialog."""
|
258
|
-
colWidths = [0, 0, 0]
|
259
|
-
colWidths[self.C_NAME] = self.listBox.columnWidth(self.C_NAME)
|
260
|
-
colWidths[self.C_COUNT] = self.listBox.columnWidth(self.C_COUNT)
|
261
|
-
colWidths[self.C_TIME] = self.listBox.columnWidth(self.C_TIME)
|
262
|
-
CONFIG.setProjLoadColWidths(colWidths)
|
263
|
-
return
|
264
|
-
|
265
|
-
def _populateList(self) -> None:
|
266
|
-
"""Populate the list box with recent project data."""
|
267
|
-
self.listBox.clear()
|
268
|
-
dataList = CONFIG.recentProjects.listEntries()
|
269
|
-
sortList = sorted(dataList, key=lambda x: x[3], reverse=True)
|
270
|
-
nwxIcon = SHARED.theme.getIcon("proj_nwx")
|
271
|
-
for path, title, words, time in sortList:
|
272
|
-
newItem = QTreeWidgetItem([""]*4)
|
273
|
-
newItem.setIcon(self.C_NAME, nwxIcon)
|
274
|
-
newItem.setText(self.C_NAME, title)
|
275
|
-
newItem.setData(self.C_NAME, self.D_PATH, path)
|
276
|
-
newItem.setText(self.C_COUNT, formatInt(words))
|
277
|
-
newItem.setText(self.C_TIME, datetime.fromtimestamp(time).strftime("%x %X"))
|
278
|
-
newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
|
279
|
-
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
|
280
|
-
newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter)
|
281
|
-
newItem.setFont(self.C_TIME, SHARED.theme.guiFontFixed)
|
282
|
-
self.listBox.addTopLevelItem(newItem)
|
283
|
-
|
284
|
-
self.listBox.setCurrentItem(self.listBox.topLevelItem(0))
|
285
|
-
|
286
|
-
projColWidth = CONFIG.projLoadColWidths
|
287
|
-
if len(projColWidth) == 3:
|
288
|
-
self.listBox.setColumnWidth(self.C_NAME, projColWidth[self.C_NAME])
|
289
|
-
self.listBox.setColumnWidth(self.C_COUNT, projColWidth[self.C_COUNT])
|
290
|
-
self.listBox.setColumnWidth(self.C_TIME, projColWidth[self.C_TIME])
|
291
|
-
|
292
|
-
return
|
293
|
-
|
294
|
-
# END Class GuiProjectLoad
|
novelwriter/dialogs/updates.py
DELETED
@@ -1,172 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
novelWriter – GUI Updates
|
3
|
-
=========================
|
4
|
-
|
5
|
-
File History:
|
6
|
-
Created: 2021-08-21 [1.5b1] GuiUpdates
|
7
|
-
|
8
|
-
This file is a part of novelWriter
|
9
|
-
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
|
-
|
11
|
-
This program is free software: you can redistribute it and/or modify
|
12
|
-
it under the terms of the GNU General Public License as published by
|
13
|
-
the Free Software Foundation, either version 3 of the License, or
|
14
|
-
(at your option) any later version.
|
15
|
-
|
16
|
-
This program is distributed in the hope that it will be useful, but
|
17
|
-
WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
19
|
-
General Public License for more details.
|
20
|
-
|
21
|
-
You should have received a copy of the GNU General Public License
|
22
|
-
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
23
|
-
"""
|
24
|
-
from __future__ import annotations
|
25
|
-
|
26
|
-
import json
|
27
|
-
import logging
|
28
|
-
|
29
|
-
from datetime import datetime
|
30
|
-
from urllib.request import Request, urlopen
|
31
|
-
|
32
|
-
from PyQt5.QtGui import QCloseEvent, QCursor
|
33
|
-
from PyQt5.QtCore import Qt
|
34
|
-
from PyQt5.QtWidgets import (
|
35
|
-
QWidget, qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QLabel
|
36
|
-
)
|
37
|
-
|
38
|
-
from novelwriter import CONFIG, SHARED, __version__, __date__
|
39
|
-
from novelwriter.common import logException
|
40
|
-
from novelwriter.constants import nwConst
|
41
|
-
|
42
|
-
logger = logging.getLogger(__name__)
|
43
|
-
|
44
|
-
|
45
|
-
class GuiUpdates(QDialog):
|
46
|
-
|
47
|
-
def __init__(self, parent: QWidget) -> None:
|
48
|
-
super().__init__(parent=parent)
|
49
|
-
|
50
|
-
logger.debug("Create: GuiUpdates")
|
51
|
-
self.setObjectName("GuiUpdates")
|
52
|
-
self.setWindowTitle(self.tr("Check for Updates"))
|
53
|
-
|
54
|
-
nPx = CONFIG.pxInt(96)
|
55
|
-
sPx = CONFIG.pxInt(16)
|
56
|
-
tPx = CONFIG.pxInt(8)
|
57
|
-
mPx = CONFIG.pxInt(4)
|
58
|
-
|
59
|
-
# Left Box
|
60
|
-
self.nwIcon = QLabel()
|
61
|
-
self.nwIcon.setPixmap(SHARED.theme.getPixmap("novelwriter", (nPx, nPx)))
|
62
|
-
|
63
|
-
self.leftBox = QVBoxLayout()
|
64
|
-
self.leftBox.addWidget(self.nwIcon)
|
65
|
-
self.leftBox.addStretch(1)
|
66
|
-
|
67
|
-
# Right Box
|
68
|
-
self.currentLabel = QLabel(self.tr("Current Release"))
|
69
|
-
self.currentValue = QLabel(self.tr(
|
70
|
-
"novelWriter {0} released on {1}"
|
71
|
-
).format(
|
72
|
-
"v%s" % __version__,
|
73
|
-
datetime.strptime(__date__, "%Y-%m-%d").strftime("%x"))
|
74
|
-
)
|
75
|
-
|
76
|
-
self.latestLabel = QLabel(self.tr("Latest Release"))
|
77
|
-
self.latestValue = QLabel(self.tr("Checking ..."))
|
78
|
-
self.latestLink = QLabel("")
|
79
|
-
self.latestLink.setOpenExternalLinks(True)
|
80
|
-
|
81
|
-
self.rightBox = QVBoxLayout()
|
82
|
-
self.rightBox.addWidget(self.currentLabel)
|
83
|
-
self.rightBox.addWidget(self.currentValue)
|
84
|
-
self.rightBox.addSpacing(tPx)
|
85
|
-
self.rightBox.addWidget(self.latestLabel)
|
86
|
-
self.rightBox.addWidget(self.latestValue)
|
87
|
-
self.rightBox.addSpacing(tPx)
|
88
|
-
self.rightBox.addWidget(self.latestLink)
|
89
|
-
self.rightBox.setSpacing(mPx)
|
90
|
-
|
91
|
-
hFont = self.currentLabel.font()
|
92
|
-
hFont.setBold(True)
|
93
|
-
self.currentLabel.setFont(hFont)
|
94
|
-
self.latestLabel.setFont(hFont)
|
95
|
-
|
96
|
-
# Buttons
|
97
|
-
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
|
98
|
-
self.buttonBox.rejected.connect(self.close)
|
99
|
-
|
100
|
-
# Assemble
|
101
|
-
self.innerBox = QHBoxLayout()
|
102
|
-
self.innerBox.addLayout(self.leftBox)
|
103
|
-
self.innerBox.addLayout(self.rightBox)
|
104
|
-
self.innerBox.setSpacing(sPx)
|
105
|
-
|
106
|
-
self.outerBox = QVBoxLayout()
|
107
|
-
self.outerBox.addLayout(self.innerBox)
|
108
|
-
self.outerBox.addWidget(self.buttonBox)
|
109
|
-
self.outerBox.setSpacing(sPx)
|
110
|
-
|
111
|
-
self.setLayout(self.outerBox)
|
112
|
-
|
113
|
-
logger.debug("Ready: GuiUpdates")
|
114
|
-
|
115
|
-
return
|
116
|
-
|
117
|
-
def __del__(self) -> None: # pragma: no cover
|
118
|
-
logger.debug("Delete: GuiUpdates")
|
119
|
-
return
|
120
|
-
|
121
|
-
def checkLatest(self) -> None:
|
122
|
-
"""Check for latest release."""
|
123
|
-
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
124
|
-
|
125
|
-
urlReq = Request("https://api.github.com/repos/vkbo/novelwriter/releases/latest")
|
126
|
-
urlReq.add_header("User-Agent", nwConst.USER_AGENT)
|
127
|
-
urlReq.add_header("Accept", "application/vnd.github.v3+json")
|
128
|
-
|
129
|
-
rawData = {}
|
130
|
-
try:
|
131
|
-
urlData = urlopen(urlReq, timeout=10)
|
132
|
-
rawData = json.loads(urlData.read().decode())
|
133
|
-
except Exception:
|
134
|
-
logger.error("Failed to contact GitHub API")
|
135
|
-
logException()
|
136
|
-
|
137
|
-
relVersion = rawData.get("tag_name", "Unknown")
|
138
|
-
relDate = rawData.get("created_at", "")
|
139
|
-
|
140
|
-
try:
|
141
|
-
relDate = datetime.strptime(relDate[:10], "%Y-%m-%d").strftime("%x")
|
142
|
-
except Exception:
|
143
|
-
relDate = "Unknown"
|
144
|
-
logException()
|
145
|
-
|
146
|
-
self.latestValue.setText(self.tr(
|
147
|
-
"novelWriter {0} released on {1}"
|
148
|
-
).format(
|
149
|
-
relVersion, relDate
|
150
|
-
))
|
151
|
-
|
152
|
-
self.latestLink.setText(self.tr(
|
153
|
-
"Download: {0}"
|
154
|
-
).format(
|
155
|
-
f'<a href="{nwConst.URL_WEB}">{nwConst.URL_WEB}</a>'
|
156
|
-
))
|
157
|
-
|
158
|
-
qApp.restoreOverrideCursor()
|
159
|
-
|
160
|
-
return
|
161
|
-
|
162
|
-
##
|
163
|
-
# Events
|
164
|
-
##
|
165
|
-
|
166
|
-
def closeEvent(self, event: QCloseEvent) -> None:
|
167
|
-
"""Capture the user closing the window."""
|
168
|
-
event.accept()
|
169
|
-
self.deleteLater()
|
170
|
-
return
|
171
|
-
|
172
|
-
# END Class GuiUpdates
|
@@ -1,130 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
novelWriter – Custom Widget: Paged Dialog
|
3
|
-
=========================================
|
4
|
-
|
5
|
-
File History:
|
6
|
-
Created: 2020-05-17 [0.5.1]
|
7
|
-
|
8
|
-
This file is a part of novelWriter
|
9
|
-
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
|
-
|
11
|
-
This program is free software: you can redistribute it and/or modify
|
12
|
-
it under the terms of the GNU General Public License as published by
|
13
|
-
the Free Software Foundation, either version 3 of the License, or
|
14
|
-
(at your option) any later version.
|
15
|
-
|
16
|
-
This program is distributed in the hope that it will be useful, but
|
17
|
-
WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
19
|
-
General Public License for more details.
|
20
|
-
|
21
|
-
You should have received a copy of the GNU General Public License
|
22
|
-
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
23
|
-
"""
|
24
|
-
from __future__ import annotations
|
25
|
-
|
26
|
-
from PyQt5.QtGui import QPaintEvent
|
27
|
-
from PyQt5.QtCore import QRect, QPoint, QSize
|
28
|
-
from PyQt5.QtWidgets import (
|
29
|
-
QDialog, QHBoxLayout, QStyle, QStyleOptionTab, QStylePainter, QTabBar,
|
30
|
-
QTabWidget, QVBoxLayout, QWidget
|
31
|
-
)
|
32
|
-
|
33
|
-
from novelwriter import CONFIG
|
34
|
-
|
35
|
-
|
36
|
-
class NPagedDialog(QDialog):
|
37
|
-
|
38
|
-
def __init__(self, parent: QWidget) -> None:
|
39
|
-
super().__init__(parent=parent)
|
40
|
-
|
41
|
-
self._tabBar = NVerticalTabBar(self)
|
42
|
-
self._tabBar.setExpanding(False)
|
43
|
-
|
44
|
-
self._tabBox = QTabWidget(self)
|
45
|
-
self._tabBox.setTabBar(self._tabBar)
|
46
|
-
self._tabBox.setTabPosition(QTabWidget.West)
|
47
|
-
|
48
|
-
self._buttonBox = QHBoxLayout()
|
49
|
-
|
50
|
-
self._outerBox = QVBoxLayout()
|
51
|
-
self._outerBox.addWidget(self._tabBox)
|
52
|
-
self._outerBox.addLayout(self._buttonBox)
|
53
|
-
|
54
|
-
# Default Margins
|
55
|
-
thisStyle = self.style()
|
56
|
-
mL = thisStyle.pixelMetric(QStyle.PM_LayoutLeftMargin)
|
57
|
-
mR = thisStyle.pixelMetric(QStyle.PM_LayoutRightMargin)
|
58
|
-
mT = thisStyle.pixelMetric(QStyle.PM_LayoutLeftMargin)
|
59
|
-
mB = thisStyle.pixelMetric(QStyle.PM_LayoutBottomMargin)
|
60
|
-
|
61
|
-
# Set Margins
|
62
|
-
self.setContentsMargins(0, 0, 0, 0)
|
63
|
-
self._outerBox.setContentsMargins(0, 0, 0, mB)
|
64
|
-
self._buttonBox.setContentsMargins(mL, 0, mR, 0)
|
65
|
-
self._outerBox.setSpacing(mT)
|
66
|
-
|
67
|
-
self.setLayout(self._outerBox)
|
68
|
-
|
69
|
-
return
|
70
|
-
|
71
|
-
def addTab(self, widget: QWidget, label: str) -> None:
|
72
|
-
"""Forward the adding of tabs to the QTabWidget."""
|
73
|
-
self._tabBox.addTab(widget, label)
|
74
|
-
return
|
75
|
-
|
76
|
-
def addControls(self, buttonBar: QWidget) -> None:
|
77
|
-
"""Add a button bar to the dialog."""
|
78
|
-
self._buttonBox.addWidget(buttonBar)
|
79
|
-
return
|
80
|
-
|
81
|
-
def setCurrentWidget(self, widget: QWidget) -> None:
|
82
|
-
"""Forward the changing of tab to the QTabWidget."""
|
83
|
-
self._tabBox.setCurrentWidget(widget)
|
84
|
-
return
|
85
|
-
|
86
|
-
# END Class NPagedDialog
|
87
|
-
|
88
|
-
|
89
|
-
class NVerticalTabBar(QTabBar):
|
90
|
-
|
91
|
-
def __init__(self, parent: QWidget) -> None:
|
92
|
-
super().__init__(parent=parent)
|
93
|
-
self._mW = CONFIG.pxInt(150)
|
94
|
-
return
|
95
|
-
|
96
|
-
def tabSizeHint(self, index: int) -> QSize:
|
97
|
-
"""Return a transposed size hint for the rotated bar."""
|
98
|
-
tSize = super().tabSizeHint(index)
|
99
|
-
tSize.transpose()
|
100
|
-
tSize.setWidth(min(tSize.width(), self._mW))
|
101
|
-
return tSize
|
102
|
-
|
103
|
-
def paintEvent(self, event: QPaintEvent) -> None:
|
104
|
-
"""Custom implementation of the label painter that rotates the
|
105
|
-
label 90 degrees.
|
106
|
-
"""
|
107
|
-
pObj = QStylePainter(self)
|
108
|
-
oObj = QStyleOptionTab()
|
109
|
-
|
110
|
-
for i in range(self.count()):
|
111
|
-
self.initStyleOption(oObj, i)
|
112
|
-
pObj.drawControl(QStyle.CE_TabBarTabShape, oObj)
|
113
|
-
pObj.save()
|
114
|
-
|
115
|
-
oSize = oObj.rect.size()
|
116
|
-
oSize.transpose()
|
117
|
-
oRect = QRect(QPoint(), oSize)
|
118
|
-
oRect.moveCenter(oObj.rect.center())
|
119
|
-
oObj.rect = oRect
|
120
|
-
|
121
|
-
oCenter = self.tabRect(i).center()
|
122
|
-
pObj.translate(oCenter)
|
123
|
-
pObj.rotate(90)
|
124
|
-
pObj.translate(-oCenter)
|
125
|
-
pObj.drawControl(QStyle.CE_TabBarTabLabel, oObj)
|
126
|
-
pObj.restore()
|
127
|
-
|
128
|
-
return
|
129
|
-
|
130
|
-
# END Class NVerticalTabBar
|