novelWriter 2.2rc1__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 (153) hide show
  1. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +141 -129
  3. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +11 -6
  5. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  6. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  7. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  8. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  9. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  10. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  11. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  12. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  13. novelwriter/assets/i18n/project_de_DE.json +1 -0
  14. novelwriter/assets/i18n/project_en_US.json +1 -0
  15. novelwriter/assets/i18n/project_es_419.json +11 -0
  16. novelwriter/assets/i18n/project_fr_FR.json +11 -0
  17. novelwriter/assets/i18n/project_it_IT.json +11 -0
  18. novelwriter/assets/i18n/project_ja_JP.json +2 -1
  19. novelwriter/assets/i18n/project_nb_NO.json +1 -0
  20. novelwriter/assets/i18n/project_zh_CN.json +11 -0
  21. novelwriter/assets/icons/typicons_dark/icons.conf +9 -2
  22. novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
  23. novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +4 -0
  24. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +3 -1
  25. novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +4 -0
  26. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +3 -1
  27. novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +4 -0
  28. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +3 -1
  29. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +4 -2
  30. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +4 -2
  31. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +4 -2
  32. novelwriter/assets/icons/typicons_dark/typ_document-add-col.svg +8 -0
  33. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
  34. novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
  35. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
  36. novelwriter/assets/icons/typicons_light/icons.conf +9 -2
  37. novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
  38. novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +4 -0
  39. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +3 -1
  40. novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +4 -0
  41. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +3 -1
  42. novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +4 -0
  43. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +3 -1
  44. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +4 -2
  45. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +4 -2
  46. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +4 -2
  47. novelwriter/assets/icons/typicons_light/typ_document-add-col.svg +8 -0
  48. novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
  49. novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
  50. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
  51. novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
  52. novelwriter/assets/images/novelwriter-text-light.svg +4 -0
  53. novelwriter/assets/images/welcome-dark.jpg +0 -0
  54. novelwriter/assets/images/welcome-light.jpg +0 -0
  55. novelwriter/assets/manual.pdf +0 -0
  56. novelwriter/assets/sample.zip +0 -0
  57. novelwriter/assets/syntax/default_dark.conf +1 -0
  58. novelwriter/assets/syntax/default_light.conf +1 -0
  59. novelwriter/assets/syntax/grey_dark.conf +1 -0
  60. novelwriter/assets/syntax/grey_light.conf +1 -0
  61. novelwriter/assets/syntax/light_owl.conf +1 -0
  62. novelwriter/assets/syntax/night_owl.conf +1 -0
  63. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  64. novelwriter/assets/syntax/solarized_light.conf +1 -0
  65. novelwriter/assets/syntax/tomorrow.conf +1 -0
  66. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  67. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  68. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  69. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  70. novelwriter/assets/text/credits_en.htm +4 -2
  71. novelwriter/assets/themes/default_dark.conf +2 -2
  72. novelwriter/assets/themes/default_light.conf +2 -2
  73. novelwriter/common.py +64 -66
  74. novelwriter/config.py +39 -44
  75. novelwriter/constants.py +39 -17
  76. novelwriter/core/buildsettings.py +8 -8
  77. novelwriter/core/coretools.py +194 -155
  78. novelwriter/core/docbuild.py +7 -4
  79. novelwriter/core/document.py +7 -7
  80. novelwriter/core/index.py +90 -57
  81. novelwriter/core/item.py +23 -5
  82. novelwriter/core/options.py +11 -10
  83. novelwriter/core/project.py +72 -47
  84. novelwriter/core/projectdata.py +3 -16
  85. novelwriter/core/projectxml.py +14 -42
  86. novelwriter/core/sessions.py +4 -3
  87. novelwriter/core/spellcheck.py +6 -4
  88. novelwriter/core/status.py +5 -4
  89. novelwriter/core/storage.py +179 -141
  90. novelwriter/core/tohtml.py +6 -4
  91. novelwriter/core/tokenizer.py +74 -46
  92. novelwriter/core/tomd.py +2 -2
  93. novelwriter/core/toodt.py +41 -31
  94. novelwriter/core/tree.py +5 -4
  95. novelwriter/dialogs/about.py +88 -179
  96. novelwriter/dialogs/docmerge.py +30 -20
  97. novelwriter/dialogs/docsplit.py +33 -22
  98. novelwriter/dialogs/editlabel.py +20 -8
  99. novelwriter/dialogs/preferences.py +562 -725
  100. novelwriter/dialogs/{projsettings.py → projectsettings.py} +301 -270
  101. novelwriter/dialogs/quotes.py +47 -36
  102. novelwriter/dialogs/wordlist.py +128 -59
  103. novelwriter/enum.py +25 -22
  104. novelwriter/error.py +2 -2
  105. novelwriter/extensions/circularprogress.py +12 -12
  106. novelwriter/extensions/configlayout.py +185 -146
  107. novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
  108. novelwriter/extensions/modified.py +81 -0
  109. novelwriter/extensions/novelselector.py +27 -13
  110. novelwriter/extensions/pagedsidebar.py +15 -20
  111. novelwriter/extensions/simpleprogress.py +8 -9
  112. novelwriter/extensions/statusled.py +9 -9
  113. novelwriter/extensions/switch.py +32 -64
  114. novelwriter/extensions/switchbox.py +2 -7
  115. novelwriter/extensions/versioninfo.py +153 -0
  116. novelwriter/gui/doceditor.py +250 -214
  117. novelwriter/gui/dochighlight.py +66 -94
  118. novelwriter/gui/docviewer.py +71 -98
  119. novelwriter/gui/docviewerpanel.py +140 -47
  120. novelwriter/gui/editordocument.py +3 -3
  121. novelwriter/gui/itemdetails.py +9 -9
  122. novelwriter/gui/mainmenu.py +47 -46
  123. novelwriter/gui/noveltree.py +53 -61
  124. novelwriter/gui/outline.py +100 -76
  125. novelwriter/gui/projtree.py +193 -67
  126. novelwriter/gui/sidebar.py +9 -8
  127. novelwriter/gui/statusbar.py +49 -7
  128. novelwriter/gui/theme.py +65 -74
  129. novelwriter/guimain.py +173 -330
  130. novelwriter/shared.py +68 -30
  131. novelwriter/tools/dictionaries.py +7 -8
  132. novelwriter/tools/lipsum.py +34 -28
  133. novelwriter/tools/manusbuild.py +3 -4
  134. novelwriter/tools/manuscript.py +25 -32
  135. novelwriter/tools/manussettings.py +194 -225
  136. novelwriter/tools/noveldetails.py +525 -0
  137. novelwriter/tools/welcome.py +802 -0
  138. novelwriter/tools/writingstats.py +26 -13
  139. novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +0 -8
  140. novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +0 -8
  141. novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +0 -8
  142. novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +0 -8
  143. novelwriter/assets/images/wizard-back.jpg +0 -0
  144. novelwriter/assets/text/gplv3_en.htm +0 -641
  145. novelwriter/assets/text/release_notes.htm +0 -17
  146. novelwriter/dialogs/projdetails.py +0 -525
  147. novelwriter/dialogs/projload.py +0 -298
  148. novelwriter/dialogs/updates.py +0 -182
  149. novelwriter/extensions/pageddialog.py +0 -130
  150. novelwriter/tools/projwizard.py +0 -478
  151. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
  152. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
  153. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
@@ -1,298 +0,0 @@
1
- """
2
- novelWriter – GUI Open Project
3
- ==============================
4
-
5
- File History:
6
- Created: 2020-02-26 [0.4.5]
7
-
8
- This file is a part of novelWriter
9
- Copyright 2018–2023, 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 QKeySequence
33
- from PyQt5.QtCore import Qt, QSize
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("...")
116
- self.browseButton.setMaximumWidth(int(2.5*SHARED.theme.getTextWidth("...")))
117
- self.browseButton.clicked.connect(self._doBrowse)
118
-
119
- self.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3)
120
- self.projectForm.addWidget(self.listBox, 1, 0, 1, 3)
121
- self.projectForm.addWidget(self.lblPath, 2, 0, 1, 1)
122
- self.projectForm.addWidget(self.selPath, 2, 1, 1, 1)
123
- self.projectForm.addWidget(self.browseButton, 2, 2, 1, 1)
124
- self.projectForm.setColumnStretch(0, 0)
125
- self.projectForm.setColumnStretch(1, 1)
126
- self.projectForm.setColumnStretch(2, 0)
127
- self.projectForm.setVerticalSpacing(CONFIG.pxInt(4))
128
- self.projectForm.setHorizontalSpacing(CONFIG.pxInt(8))
129
-
130
- self.innerBox.addLayout(self.projectForm)
131
-
132
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel)
133
- self.buttonBox.accepted.connect(self._doOpenRecent)
134
- self.buttonBox.rejected.connect(self._doCancel)
135
-
136
- self.newButton = self.buttonBox.addButton(self.tr("New"), QDialogButtonBox.ActionRole)
137
- self.newButton.clicked.connect(self._doNewProject)
138
-
139
- self.delButton = self.buttonBox.addButton(self.tr("Remove"), QDialogButtonBox.ActionRole)
140
- self.delButton.clicked.connect(self._doDeleteRecent)
141
-
142
- self.outerBox.addLayout(self.innerBox)
143
- self.outerBox.addWidget(self.buttonBox)
144
- self.setLayout(self.outerBox)
145
-
146
- self._populateList()
147
- self._doSelectRecent()
148
-
149
- keyDelete = QShortcut(self.listBox)
150
- keyDelete.setKey(QKeySequence(Qt.Key_Delete))
151
- keyDelete.activated.connect(self._doDeleteRecent)
152
-
153
- logger.debug("Ready: GuiProjectLoad")
154
-
155
- return
156
-
157
- def __del__(self): # pragma: no cover
158
- logger.debug("Delete: GuiProjectLoad")
159
- return
160
-
161
- ##
162
- # Buttons
163
- ##
164
-
165
- def _doOpenRecent(self):
166
- """Close the dialog window with a recent project selected.
167
- """
168
- self._saveSettings()
169
-
170
- self.openPath = None
171
- self.openState = self.NONE_STATE
172
-
173
- selItems = self.listBox.selectedItems()
174
- if selItems:
175
- self.openPath = selItems[0].data(self.C_NAME, self.D_PATH)
176
- self.openState = self.OPEN_STATE
177
- self.accept()
178
-
179
- return
180
-
181
- def _doSelectRecent(self):
182
- """A recent item has been selected.
183
- """
184
- selList = self.listBox.selectedItems()
185
- if selList:
186
- self.selPath.setText(selList[0].data(self.C_NAME, self.D_PATH))
187
- return
188
-
189
- def _doBrowse(self):
190
- """Browse for a folder path.
191
- """
192
- extFilter = [
193
- self.tr("novelWriter Project File ({0})").format(nwFiles.PROJ_FILE),
194
- self.tr("All files ({0})").format("*"),
195
- ]
196
- projFile, _ = QFileDialog.getOpenFileName(
197
- self, self.tr("Open Project"), "", filter=";;".join(extFilter)
198
- )
199
- if projFile:
200
- thePath = Path(projFile).absolute()
201
- self.selPath.setText(str(thePath))
202
- self.openPath = thePath
203
- self.openState = self.OPEN_STATE
204
- self.accept()
205
-
206
- return
207
-
208
- def _doCancel(self):
209
- """Close the dialog window without doing anything.
210
- """
211
- self.openPath = None
212
- self.openState = self.NONE_STATE
213
- self.close()
214
- return
215
-
216
- def _doNewProject(self):
217
- """Create a new project.
218
- """
219
- self._saveSettings()
220
- self.openPath = None
221
- self.openState = self.NEW_STATE
222
- self.accept()
223
- return
224
-
225
- def _doDeleteRecent(self):
226
- """Remove an entry from the recent projects list.
227
- """
228
- selList = self.listBox.selectedItems()
229
- if selList:
230
- projName = selList[0].text(self.C_NAME)
231
- msgYes = SHARED.question(self.tr(
232
- "Remove '{0}' from the recent projects list? "
233
- "The project files will not be deleted."
234
- ).format(projName))
235
- if msgYes:
236
- CONFIG.recentProjects.remove(
237
- selList[0].data(self.C_NAME, self.D_PATH)
238
- )
239
- self._populateList()
240
-
241
- return
242
-
243
- ##
244
- # Events
245
- ##
246
-
247
- def closeEvent(self, theEvent):
248
- """Capture the user closing the dialog so we can save settings.
249
- """
250
- self._saveSettings()
251
- theEvent.accept()
252
- return
253
-
254
- ##
255
- # Internal Functions
256
- ##
257
-
258
- def _saveSettings(self):
259
- """Save the changes made to the dialog.
260
- """
261
- colWidths = [0, 0, 0]
262
- colWidths[self.C_NAME] = self.listBox.columnWidth(self.C_NAME)
263
- colWidths[self.C_COUNT] = self.listBox.columnWidth(self.C_COUNT)
264
- colWidths[self.C_TIME] = self.listBox.columnWidth(self.C_TIME)
265
- CONFIG.setProjLoadColWidths(colWidths)
266
- return
267
-
268
- def _populateList(self):
269
- """Populate the list box with recent project data.
270
- """
271
- self.listBox.clear()
272
- dataList = CONFIG.recentProjects.listEntries()
273
- sortList = sorted(dataList, key=lambda x: x[3], reverse=True)
274
- nwxIcon = SHARED.theme.getIcon("proj_nwx")
275
- for path, title, words, time in sortList:
276
- newItem = QTreeWidgetItem([""]*4)
277
- newItem.setIcon(self.C_NAME, nwxIcon)
278
- newItem.setText(self.C_NAME, title)
279
- newItem.setData(self.C_NAME, self.D_PATH, path)
280
- newItem.setText(self.C_COUNT, formatInt(words))
281
- newItem.setText(self.C_TIME, datetime.fromtimestamp(time).strftime("%x %X"))
282
- newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
283
- newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
284
- newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter)
285
- newItem.setFont(self.C_TIME, SHARED.theme.guiFontFixed)
286
- self.listBox.addTopLevelItem(newItem)
287
-
288
- self.listBox.setCurrentItem(self.listBox.topLevelItem(0))
289
-
290
- projColWidth = CONFIG.projLoadColWidths
291
- if len(projColWidth) == 3:
292
- self.listBox.setColumnWidth(self.C_NAME, projColWidth[self.C_NAME])
293
- self.listBox.setColumnWidth(self.C_COUNT, projColWidth[self.C_COUNT])
294
- self.listBox.setColumnWidth(self.C_TIME, projColWidth[self.C_TIME])
295
-
296
- return
297
-
298
- # END Class GuiProjectLoad
@@ -1,182 +0,0 @@
1
- """
2
- novelWriter – GUI Updates
3
- =========================
4
-
5
- File History:
6
- Created: 2021-08-21 [1.5b1]
7
-
8
- This file is a part of novelWriter
9
- Copyright 2018–2023, 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, pyqtSlot
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._doClose)
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
- ##
173
- # Private Slots
174
- ##
175
-
176
- @pyqtSlot()
177
- def _doClose(self) -> None:
178
- """Close the dialog."""
179
- self.close()
180
- return
181
-
182
- # 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–2023, 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