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.
Files changed (110) hide show
  1. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +102 -92
  3. novelwriter/__init__.py +4 -4
  4. novelwriter/assets/icons/typicons_dark/icons.conf +6 -0
  5. novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
  6. novelwriter/assets/icons/typicons_dark/typ_document-add-col.svg +8 -0
  7. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
  8. novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
  9. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
  10. novelwriter/assets/icons/typicons_light/icons.conf +6 -0
  11. novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
  12. novelwriter/assets/icons/typicons_light/typ_document-add-col.svg +8 -0
  13. novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
  14. novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
  15. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
  16. novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
  17. novelwriter/assets/images/novelwriter-text-light.svg +4 -0
  18. novelwriter/assets/images/welcome-dark.jpg +0 -0
  19. novelwriter/assets/images/welcome-light.jpg +0 -0
  20. novelwriter/assets/manual.pdf +0 -0
  21. novelwriter/assets/sample.zip +0 -0
  22. novelwriter/assets/syntax/default_dark.conf +1 -0
  23. novelwriter/assets/syntax/default_light.conf +1 -0
  24. novelwriter/assets/syntax/grey_dark.conf +1 -0
  25. novelwriter/assets/syntax/grey_light.conf +1 -0
  26. novelwriter/assets/syntax/light_owl.conf +1 -0
  27. novelwriter/assets/syntax/night_owl.conf +1 -0
  28. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  29. novelwriter/assets/syntax/solarized_light.conf +1 -0
  30. novelwriter/assets/syntax/tomorrow.conf +1 -0
  31. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  32. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  33. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  34. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  35. novelwriter/assets/text/credits_en.htm +4 -2
  36. novelwriter/assets/themes/default_dark.conf +2 -2
  37. novelwriter/assets/themes/default_light.conf +2 -2
  38. novelwriter/common.py +48 -37
  39. novelwriter/config.py +36 -41
  40. novelwriter/constants.py +38 -16
  41. novelwriter/core/buildsettings.py +7 -7
  42. novelwriter/core/coretools.py +192 -154
  43. novelwriter/core/docbuild.py +6 -3
  44. novelwriter/core/document.py +6 -6
  45. novelwriter/core/index.py +89 -56
  46. novelwriter/core/item.py +21 -3
  47. novelwriter/core/options.py +8 -7
  48. novelwriter/core/project.py +69 -44
  49. novelwriter/core/projectdata.py +1 -14
  50. novelwriter/core/projectxml.py +13 -41
  51. novelwriter/core/sessions.py +2 -1
  52. novelwriter/core/spellcheck.py +2 -1
  53. novelwriter/core/status.py +2 -1
  54. novelwriter/core/storage.py +178 -140
  55. novelwriter/core/tohtml.py +4 -2
  56. novelwriter/core/tokenizer.py +73 -45
  57. novelwriter/core/toodt.py +40 -30
  58. novelwriter/core/tree.py +3 -2
  59. novelwriter/dialogs/about.py +70 -160
  60. novelwriter/dialogs/docmerge.py +6 -5
  61. novelwriter/dialogs/docsplit.py +6 -6
  62. novelwriter/dialogs/editlabel.py +1 -1
  63. novelwriter/dialogs/preferences.py +553 -703
  64. novelwriter/dialogs/{projsettings.py → projectsettings.py} +288 -262
  65. novelwriter/dialogs/quotes.py +27 -23
  66. novelwriter/dialogs/wordlist.py +96 -40
  67. novelwriter/enum.py +20 -18
  68. novelwriter/error.py +1 -1
  69. novelwriter/extensions/circularprogress.py +11 -11
  70. novelwriter/extensions/configlayout.py +185 -134
  71. novelwriter/extensions/modified.py +81 -0
  72. novelwriter/extensions/novelselector.py +26 -12
  73. novelwriter/extensions/pagedsidebar.py +14 -16
  74. novelwriter/extensions/simpleprogress.py +5 -5
  75. novelwriter/extensions/statusled.py +8 -8
  76. novelwriter/extensions/switch.py +31 -63
  77. novelwriter/extensions/switchbox.py +1 -1
  78. novelwriter/extensions/versioninfo.py +153 -0
  79. novelwriter/gui/doceditor.py +178 -150
  80. novelwriter/gui/dochighlight.py +63 -92
  81. novelwriter/gui/docviewer.py +49 -51
  82. novelwriter/gui/docviewerpanel.py +72 -24
  83. novelwriter/gui/itemdetails.py +7 -7
  84. novelwriter/gui/mainmenu.py +14 -18
  85. novelwriter/gui/noveltree.py +9 -8
  86. novelwriter/gui/outline.py +98 -75
  87. novelwriter/gui/projtree.py +188 -61
  88. novelwriter/gui/sidebar.py +3 -4
  89. novelwriter/gui/statusbar.py +3 -4
  90. novelwriter/gui/theme.py +60 -68
  91. novelwriter/guimain.py +49 -156
  92. novelwriter/shared.py +15 -1
  93. novelwriter/tools/dictionaries.py +5 -6
  94. novelwriter/tools/manuscript.py +6 -6
  95. novelwriter/tools/manussettings.py +192 -221
  96. novelwriter/tools/noveldetails.py +525 -0
  97. novelwriter/tools/welcome.py +802 -0
  98. novelwriter/tools/writingstats.py +9 -9
  99. novelwriter/assets/images/wizard-back.jpg +0 -0
  100. novelwriter/assets/text/gplv3_en.htm +0 -641
  101. novelwriter/assets/text/release_notes.htm +0 -60
  102. novelwriter/dialogs/projdetails.py +0 -518
  103. novelwriter/dialogs/projload.py +0 -294
  104. novelwriter/dialogs/updates.py +0 -172
  105. novelwriter/extensions/pageddialog.py +0 -130
  106. novelwriter/tools/projwizard.py +0 -478
  107. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
  108. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +0 -0
  109. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
  110. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
@@ -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
@@ -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