novelWriter 2.6b1__py3-none-any.whl → 2.6b2__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.6b1.dist-info → novelWriter-2.6b2.dist-info}/METADATA +3 -3
- {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/RECORD +68 -52
- {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +49 -10
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
- novelwriter/assets/i18n/project_de_DE.json +2 -2
- novelwriter/assets/i18n/project_ru_RU.json +11 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +7 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +6 -0
- novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +5 -0
- novelwriter/assets/icons/typicons_light/icons.conf +7 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_size-height.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_size-width.svg +6 -0
- novelwriter/assets/icons/typicons_light/nw_toolbar.svg +5 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/text/credits_en.htm +1 -0
- novelwriter/common.py +37 -2
- novelwriter/config.py +15 -12
- novelwriter/constants.py +24 -9
- novelwriter/core/coretools.py +111 -125
- novelwriter/core/docbuild.py +3 -2
- novelwriter/core/index.py +9 -19
- novelwriter/core/item.py +39 -6
- novelwriter/core/itemmodel.py +518 -0
- novelwriter/core/project.py +67 -89
- novelwriter/core/status.py +7 -5
- novelwriter/core/tree.py +268 -287
- novelwriter/dialogs/docmerge.py +7 -17
- novelwriter/dialogs/preferences.py +3 -3
- novelwriter/dialogs/projectsettings.py +2 -2
- novelwriter/enum.py +7 -0
- novelwriter/extensions/configlayout.py +6 -4
- novelwriter/formats/todocx.py +34 -38
- novelwriter/formats/tohtml.py +14 -15
- novelwriter/formats/tokenizer.py +21 -17
- novelwriter/formats/toodt.py +53 -124
- novelwriter/formats/toqdoc.py +92 -44
- novelwriter/gui/doceditor.py +230 -219
- novelwriter/gui/docviewer.py +38 -9
- novelwriter/gui/docviewerpanel.py +14 -22
- novelwriter/gui/itemdetails.py +17 -24
- novelwriter/gui/mainmenu.py +13 -8
- novelwriter/gui/noveltree.py +12 -12
- novelwriter/gui/outline.py +10 -11
- novelwriter/gui/projtree.py +548 -1202
- novelwriter/gui/search.py +9 -10
- novelwriter/gui/theme.py +7 -3
- novelwriter/guimain.py +59 -43
- novelwriter/shared.py +52 -23
- novelwriter/text/patterns.py +17 -5
- novelwriter/tools/manusbuild.py +13 -11
- novelwriter/tools/manussettings.py +42 -52
- novelwriter/types.py +7 -1
- {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/top_level.txt +0 -0
novelwriter/gui/search.py
CHANGED
@@ -30,15 +30,18 @@ from time import time
|
|
30
30
|
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
31
31
|
from PyQt5.QtGui import QCursor, QKeyEvent
|
32
32
|
from PyQt5.QtWidgets import (
|
33
|
-
QApplication, QFrame, QHBoxLayout,
|
34
|
-
|
33
|
+
QApplication, QFrame, QHBoxLayout, QLabel, QLineEdit, QToolBar,
|
34
|
+
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
35
35
|
)
|
36
36
|
|
37
37
|
from novelwriter import CONFIG, SHARED
|
38
38
|
from novelwriter.common import checkInt, cssCol
|
39
39
|
from novelwriter.core.coretools import DocSearch
|
40
40
|
from novelwriter.core.item import NWItem
|
41
|
-
from novelwriter.types import
|
41
|
+
from novelwriter.types import (
|
42
|
+
QtAlignMiddle, QtAlignRight, QtHeaderStretch, QtHeaderToContents,
|
43
|
+
QtUserRole
|
44
|
+
)
|
42
45
|
|
43
46
|
logger = logging.getLogger(__name__)
|
44
47
|
|
@@ -120,8 +123,8 @@ class GuiProjectSearch(QWidget):
|
|
120
123
|
|
121
124
|
treeHeader = self.searchResult.header()
|
122
125
|
treeHeader.setStretchLastSection(False)
|
123
|
-
treeHeader.setSectionResizeMode(self.C_NAME,
|
124
|
-
treeHeader.setSectionResizeMode(self.C_COUNT,
|
126
|
+
treeHeader.setSectionResizeMode(self.C_NAME, QtHeaderStretch)
|
127
|
+
treeHeader.setSectionResizeMode(self.C_COUNT, QtHeaderToContents)
|
125
128
|
|
126
129
|
# Assemble
|
127
130
|
self.headerBox = QHBoxLayout()
|
@@ -331,15 +334,11 @@ class GuiProjectSearch(QWidget):
|
|
331
334
|
"""Populate the result tree."""
|
332
335
|
if results and nwItem:
|
333
336
|
tHandle = nwItem.itemHandle
|
334
|
-
docIcon = SHARED.theme.getItemIcon(
|
335
|
-
nwItem.itemType, nwItem.itemClass,
|
336
|
-
nwItem.itemLayout, nwItem.mainHeading
|
337
|
-
)
|
338
337
|
ext = "+" if capped else ""
|
339
338
|
|
340
339
|
tItem = QTreeWidgetItem()
|
341
340
|
tItem.setText(self.C_NAME, nwItem.itemName)
|
342
|
-
tItem.setIcon(self.C_NAME,
|
341
|
+
tItem.setIcon(self.C_NAME, nwItem.getMainIcon())
|
343
342
|
tItem.setData(self.C_NAME, self.D_HANDLE, tHandle)
|
344
343
|
tItem.setText(self.C_COUNT, f"({len(results):n}{ext})")
|
345
344
|
tItem.setTextAlignment(self.C_COUNT, QtAlignRight)
|
novelwriter/gui/theme.py
CHANGED
@@ -151,6 +151,9 @@ class GuiTheme:
|
|
151
151
|
self.guiFont = QApplication.font()
|
152
152
|
self.guiFontB = QApplication.font()
|
153
153
|
self.guiFontB.setBold(True)
|
154
|
+
self.guiFontBU = QApplication.font()
|
155
|
+
self.guiFontBU.setBold(True)
|
156
|
+
self.guiFontBU.setUnderline(True)
|
154
157
|
self.guiFontSmall = QApplication.font()
|
155
158
|
self.guiFontSmall.setPointSizeF(0.9*self.guiFont.pointSizeF())
|
156
159
|
|
@@ -500,14 +503,15 @@ class GuiIcons:
|
|
500
503
|
|
501
504
|
# Format Icons
|
502
505
|
"fmt_bold", "fmt_bold-md", "fmt_italic", "fmt_italic-md", "fmt_mark", "fmt_strike",
|
503
|
-
"fmt_strike-md", "fmt_subscript", "fmt_superscript", "fmt_underline",
|
506
|
+
"fmt_strike-md", "fmt_subscript", "fmt_superscript", "fmt_underline", "margin_bottom",
|
507
|
+
"margin_left", "margin_right", "margin_top", "size_height", "size_width",
|
504
508
|
|
505
509
|
# General Button Icons
|
506
510
|
"add", "add_document", "backward", "bookmark", "browse", "checked", "close", "copy",
|
507
511
|
"cross", "document", "down", "edit", "export", "font", "forward", "import", "list",
|
508
512
|
"maximise", "menu", "minimise", "more", "noncheckable", "open", "panel", "quote",
|
509
|
-
"refresh", "remove", "revert", "search_replace", "search", "settings", "star", "
|
510
|
-
"up", "view",
|
513
|
+
"refresh", "remove", "revert", "search_replace", "search", "settings", "star", "toolbar",
|
514
|
+
"unchecked", "up", "view",
|
511
515
|
|
512
516
|
# Switches
|
513
517
|
"sticky-on", "sticky-off",
|
novelwriter/guimain.py
CHANGED
@@ -44,7 +44,7 @@ from novelwriter.dialogs.about import GuiAbout
|
|
44
44
|
from novelwriter.dialogs.preferences import GuiPreferences
|
45
45
|
from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
46
46
|
from novelwriter.dialogs.wordlist import GuiWordList
|
47
|
-
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwFocus, nwView
|
47
|
+
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwFocus, nwItemType, nwView
|
48
48
|
from novelwriter.gui.doceditor import GuiDocEditor
|
49
49
|
from novelwriter.gui.docviewer import GuiDocViewer
|
50
50
|
from novelwriter.gui.docviewerpanel import GuiDocViewerPanel
|
@@ -62,6 +62,7 @@ from novelwriter.tools.manuscript import GuiManuscript
|
|
62
62
|
from novelwriter.tools.noveldetails import GuiNovelDetails
|
63
63
|
from novelwriter.tools.welcome import GuiWelcome
|
64
64
|
from novelwriter.tools.writingstats import GuiWritingStats
|
65
|
+
from novelwriter.types import QtModShift
|
65
66
|
|
66
67
|
logger = logging.getLogger(__name__)
|
67
68
|
|
@@ -212,15 +213,19 @@ class GuiMain(QMainWindow):
|
|
212
213
|
SHARED.indexChangedTags.connect(self.docEditor.updateChangedTags)
|
213
214
|
SHARED.indexChangedTags.connect(self.docViewerPanel.updateChangedTags)
|
214
215
|
SHARED.indexCleared.connect(self.docViewerPanel.indexWasCleared)
|
215
|
-
SHARED.indexScannedText.connect(self.docViewerPanel.projectItemChanged)
|
216
|
-
SHARED.indexScannedText.connect(self.itemDetails.updateViewBox)
|
217
|
-
SHARED.indexScannedText.connect(self.projView.updateItemValues)
|
218
216
|
SHARED.mainClockTick.connect(self._timeTick)
|
217
|
+
SHARED.projectItemChanged.connect(self.docEditor.onProjectItemChanged)
|
218
|
+
SHARED.projectItemChanged.connect(self.docViewer.onProjectItemChanged)
|
219
|
+
SHARED.projectItemChanged.connect(self.docViewerPanel.onProjectItemChanged)
|
220
|
+
SHARED.projectItemChanged.connect(self.itemDetails.onProjectItemChanged)
|
221
|
+
SHARED.projectItemChanged.connect(self.projView.onProjectItemChanged)
|
219
222
|
SHARED.projectStatusChanged.connect(self.mainStatus.updateProjectStatus)
|
220
223
|
SHARED.projectStatusMessage.connect(self.mainStatus.setStatusMessage)
|
224
|
+
SHARED.rootFolderChanged.connect(self.novelView.updateRootItem)
|
225
|
+
SHARED.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
226
|
+
SHARED.rootFolderChanged.connect(self.projView.updateRootItem)
|
221
227
|
SHARED.spellLanguageChanged.connect(self.mainStatus.setLanguage)
|
222
228
|
SHARED.statusLabelsChanged.connect(self.docViewerPanel.updateStatusLabels)
|
223
|
-
SHARED.statusLabelsChanged.connect(self.projView.refreshUserLabels)
|
224
229
|
|
225
230
|
self.mainMenu.requestDocAction.connect(self._passDocumentAction)
|
226
231
|
self.mainMenu.requestDocInsert.connect(self._passDocumentInsert)
|
@@ -233,15 +238,7 @@ class GuiMain(QMainWindow):
|
|
233
238
|
|
234
239
|
self.projView.openDocumentRequest.connect(self._openDocument)
|
235
240
|
self.projView.projectSettingsRequest.connect(self.showProjectSettingsDialog)
|
236
|
-
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
|
237
|
-
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
238
|
-
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
|
239
241
|
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
240
|
-
self.projView.treeItemChanged.connect(self.docEditor.updateDocInfo)
|
241
|
-
self.projView.treeItemChanged.connect(self.docViewer.updateDocInfo)
|
242
|
-
self.projView.treeItemChanged.connect(self.docViewerPanel.projectItemChanged)
|
243
|
-
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
|
244
|
-
self.projView.wordCountsChanged.connect(self._updateStatusWordCount)
|
245
242
|
|
246
243
|
self.novelView.openDocumentRequest.connect(self._openDocument)
|
247
244
|
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
@@ -250,8 +247,6 @@ class GuiMain(QMainWindow):
|
|
250
247
|
self.projSearch.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
251
248
|
|
252
249
|
self.docEditor.closeEditorRequest.connect(self.closeDocEditor)
|
253
|
-
self.docEditor.docCountsChanged.connect(self.itemDetails.updateCounts)
|
254
|
-
self.docEditor.docCountsChanged.connect(self.projView.updateCounts)
|
255
250
|
self.docEditor.docTextChanged.connect(self.projSearch.textChanged)
|
256
251
|
self.docEditor.editedStatusChanged.connect(self.mainStatus.updateDocumentStatus)
|
257
252
|
self.docEditor.itemHandleChanged.connect(self.novelView.setActiveHandle)
|
@@ -259,7 +254,8 @@ class GuiMain(QMainWindow):
|
|
259
254
|
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
|
260
255
|
self.docEditor.novelItemMetaChanged.connect(self.novelView.updateNovelItemMeta)
|
261
256
|
self.docEditor.novelStructureChanged.connect(self.novelView.refreshTree)
|
262
|
-
self.docEditor.
|
257
|
+
self.docEditor.openDocumentRequest.connect(self._openDocument)
|
258
|
+
self.docEditor.requestNewNoteCreation.connect(SHARED.createNewNote)
|
263
259
|
self.docEditor.requestNextDocument.connect(self.openNextDocument)
|
264
260
|
self.docEditor.requestProjectItemRenamed.connect(self.projView.renameTreeItem)
|
265
261
|
self.docEditor.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
|
@@ -297,14 +293,25 @@ class GuiMain(QMainWindow):
|
|
297
293
|
self.keyReturn.setKey("Return")
|
298
294
|
self.keyReturn.activated.connect(self._keyPressReturn)
|
299
295
|
|
296
|
+
self.keyShiftReturn = QShortcut(self)
|
297
|
+
self.keyShiftReturn.setKey("Shift+Return")
|
298
|
+
self.keyShiftReturn.activated.connect(self._keyPressReturn)
|
299
|
+
|
300
300
|
self.keyEnter = QShortcut(self)
|
301
301
|
self.keyEnter.setKey("Enter")
|
302
302
|
self.keyEnter.activated.connect(self._keyPressReturn)
|
303
303
|
|
304
|
+
self.keyShiftEnter = QShortcut(self)
|
305
|
+
self.keyShiftEnter.setKey("Shift+Enter")
|
306
|
+
self.keyShiftEnter.activated.connect(self._keyPressReturn)
|
307
|
+
|
304
308
|
self.keyEscape = QShortcut(self)
|
305
309
|
self.keyEscape.setKey("Esc")
|
306
310
|
self.keyEscape.activated.connect(self._keyPressEscape)
|
307
311
|
|
312
|
+
# Internal Variables
|
313
|
+
self._lastTotalCount = 0
|
314
|
+
|
308
315
|
# Initialise Main GUI
|
309
316
|
self.initMain()
|
310
317
|
self.asProjTimer.start()
|
@@ -502,11 +509,9 @@ class GuiMain(QMainWindow):
|
|
502
509
|
|
503
510
|
def saveProject(self, autoSave: bool = False) -> bool:
|
504
511
|
"""Save the current project."""
|
505
|
-
if
|
506
|
-
|
507
|
-
|
508
|
-
self.projView.saveProjectTasks()
|
509
|
-
return SHARED.saveProject(autoSave=autoSave)
|
512
|
+
if SHARED.hasProject:
|
513
|
+
return SHARED.saveProject(autoSave=autoSave)
|
514
|
+
return False
|
510
515
|
|
511
516
|
##
|
512
517
|
# Document Actions
|
@@ -718,8 +723,11 @@ class GuiMain(QMainWindow):
|
|
718
723
|
logger.warning("No item selected")
|
719
724
|
return
|
720
725
|
|
721
|
-
if tHandle:
|
722
|
-
|
726
|
+
if tHandle and SHARED.project.tree.checkType(tHandle, nwItemType.FILE):
|
727
|
+
if QApplication.keyboardModifiers() == QtModShift:
|
728
|
+
self.viewDocument(tHandle)
|
729
|
+
else:
|
730
|
+
self.openDocument(tHandle, sTitle=sTitle, changeFocus=False, doScroll=False)
|
723
731
|
|
724
732
|
return
|
725
733
|
|
@@ -730,9 +738,8 @@ class GuiMain(QMainWindow):
|
|
730
738
|
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
|
731
739
|
tStart = time()
|
732
740
|
|
733
|
-
|
734
|
-
SHARED.project.
|
735
|
-
self.projView.populateTree()
|
741
|
+
SHARED.project.index.rebuild()
|
742
|
+
SHARED.project.tree.refreshAllItems()
|
736
743
|
self.novelView.refreshTree()
|
737
744
|
|
738
745
|
tEnd = time()
|
@@ -1044,13 +1051,8 @@ class GuiMain(QMainWindow):
|
|
1044
1051
|
self.initMain()
|
1045
1052
|
self.saveDocument()
|
1046
1053
|
|
1047
|
-
if restart:
|
1048
|
-
SHARED.info(self.tr(
|
1049
|
-
"Some changes will not be applied until novelWriter has been restarted."
|
1050
|
-
))
|
1051
|
-
|
1052
1054
|
if tree:
|
1053
|
-
|
1055
|
+
SHARED.project.tree.refreshAllItems()
|
1054
1056
|
|
1055
1057
|
if theme:
|
1056
1058
|
# We are doing this manually instead of connecting to
|
@@ -1076,8 +1078,16 @@ class GuiMain(QMainWindow):
|
|
1076
1078
|
self.projView.initSettings()
|
1077
1079
|
self.novelView.initSettings()
|
1078
1080
|
self.outlineView.initSettings()
|
1081
|
+
|
1082
|
+
# Force update of word count
|
1083
|
+
self._lastTotalCount = 0
|
1079
1084
|
self._updateStatusWordCount()
|
1080
1085
|
|
1086
|
+
if restart:
|
1087
|
+
SHARED.info(self.tr(
|
1088
|
+
"Some changes will not be applied until novelWriter has been restarted."
|
1089
|
+
))
|
1090
|
+
|
1081
1091
|
return
|
1082
1092
|
|
1083
1093
|
@pyqtSlot()
|
@@ -1213,8 +1223,10 @@ class GuiMain(QMainWindow):
|
|
1213
1223
|
self.mainStatus.setUserIdle(editIdle or userIdle)
|
1214
1224
|
SHARED.updateIdleTime(currTime, editIdle or userIdle)
|
1215
1225
|
self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
|
1216
|
-
if
|
1217
|
-
self.
|
1226
|
+
if int(currTime) % 5 == 0:
|
1227
|
+
self._updateStatusWordCount()
|
1228
|
+
if CONFIG.memInfo: # pragma: no cover
|
1229
|
+
self.mainStatus.memInfo()
|
1218
1230
|
return
|
1219
1231
|
|
1220
1232
|
@pyqtSlot()
|
@@ -1242,15 +1254,19 @@ class GuiMain(QMainWindow):
|
|
1242
1254
|
if not SHARED.hasProject:
|
1243
1255
|
self.mainStatus.setProjectStats(0, 0)
|
1244
1256
|
|
1245
|
-
SHARED.project.
|
1246
|
-
if
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1257
|
+
currentTotalCount = SHARED.project.currentTotalCount
|
1258
|
+
if self._lastTotalCount != currentTotalCount:
|
1259
|
+
self._lastTotalCount = currentTotalCount
|
1260
|
+
|
1261
|
+
SHARED.project.updateWordCounts()
|
1262
|
+
if CONFIG.incNotesWCount:
|
1263
|
+
iTotal = sum(SHARED.project.data.initCounts)
|
1264
|
+
cTotal = sum(SHARED.project.data.currCounts)
|
1265
|
+
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
1266
|
+
else:
|
1267
|
+
iNovel, _ = SHARED.project.data.initCounts
|
1268
|
+
cNovel, _ = SHARED.project.data.currCounts
|
1269
|
+
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
|
1254
1270
|
|
1255
1271
|
return
|
1256
1272
|
|
novelwriter/shared.py
CHANGED
@@ -26,6 +26,7 @@ from __future__ import annotations
|
|
26
26
|
|
27
27
|
import logging
|
28
28
|
|
29
|
+
from enum import Enum
|
29
30
|
from pathlib import Path
|
30
31
|
from time import time
|
31
32
|
from typing import TYPE_CHECKING, TypeVar
|
@@ -37,9 +38,11 @@ from PyQt5.QtWidgets import QFileDialog, QFontDialog, QMessageBox, QWidget
|
|
37
38
|
from novelwriter.common import formatFileFilter
|
38
39
|
from novelwriter.constants import nwFiles
|
39
40
|
from novelwriter.core.spellcheck import NWSpellEnchant
|
41
|
+
from novelwriter.enum import nwChange, nwItemClass
|
40
42
|
|
41
43
|
if TYPE_CHECKING: # pragma: no cover
|
42
44
|
from novelwriter.core.project import NWProject
|
45
|
+
from novelwriter.core.status import T_StatusKind
|
43
46
|
from novelwriter.gui.theme import GuiTheme
|
44
47
|
from novelwriter.guimain import GuiMain
|
45
48
|
|
@@ -55,15 +58,16 @@ class SharedData(QObject):
|
|
55
58
|
"_idleTime", "_idleRefTime",
|
56
59
|
)
|
57
60
|
|
58
|
-
projectStatusChanged = pyqtSignal(bool)
|
59
|
-
projectStatusMessage = pyqtSignal(str)
|
60
|
-
spellLanguageChanged = pyqtSignal(str, str)
|
61
61
|
focusModeChanged = pyqtSignal(bool)
|
62
|
-
|
62
|
+
indexAvailable = pyqtSignal()
|
63
63
|
indexChangedTags = pyqtSignal(list, list)
|
64
64
|
indexCleared = pyqtSignal()
|
65
|
-
indexAvailable = pyqtSignal()
|
66
65
|
mainClockTick = pyqtSignal()
|
66
|
+
projectItemChanged = pyqtSignal(str, Enum)
|
67
|
+
rootFolderChanged = pyqtSignal(str, Enum)
|
68
|
+
projectStatusChanged = pyqtSignal(bool)
|
69
|
+
projectStatusMessage = pyqtSignal(str)
|
70
|
+
spellLanguageChanged = pyqtSignal(str, str)
|
67
71
|
statusLabelsChanged = pyqtSignal(str)
|
68
72
|
|
69
73
|
def __init__(self) -> None:
|
@@ -173,10 +177,12 @@ class SharedData(QObject):
|
|
173
177
|
logger.debug("Thread Pool Max Count: %d", QThreadPool.globalInstance().maxThreadCount())
|
174
178
|
return
|
175
179
|
|
176
|
-
def
|
180
|
+
def closeDocument(self, tHandle: str | None = None) -> None:
|
177
181
|
"""Close the document editor, optionally a specific document."""
|
178
182
|
if tHandle is None or tHandle == self.mainGui.docEditor.docHandle:
|
179
183
|
self.mainGui.closeDocument()
|
184
|
+
if tHandle is None or tHandle == self.mainGui.docViewer.docHandle:
|
185
|
+
self.mainGui.closeViewerPanel()
|
180
186
|
return
|
181
187
|
|
182
188
|
def saveEditor(self, tHandle: str | None = None) -> None:
|
@@ -302,30 +308,52 @@ class SharedData(QObject):
|
|
302
308
|
QDesktopServices.openUrl(QUrl(url))
|
303
309
|
return
|
304
310
|
|
311
|
+
@pyqtSlot(str, nwItemClass)
|
312
|
+
def createNewNote(self, tag: str, itemClass: nwItemClass) -> None:
|
313
|
+
"""Process new note request."""
|
314
|
+
self.project.createNewNote(tag, itemClass)
|
315
|
+
return
|
316
|
+
|
305
317
|
##
|
306
|
-
# Signal
|
318
|
+
# Signal Proxies
|
307
319
|
##
|
308
320
|
|
309
|
-
def
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
if
|
314
|
-
self.indexChangedTags.emit(
|
315
|
-
|
316
|
-
|
317
|
-
|
321
|
+
def emitIndexChangedTags(
|
322
|
+
self, project: NWProject, updated: list[str], deleted: list[str]
|
323
|
+
) -> None:
|
324
|
+
"""Emit the indexChangedTags signal."""
|
325
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
326
|
+
self.indexChangedTags.emit(updated, deleted)
|
327
|
+
return
|
328
|
+
|
329
|
+
def emitIndexCleared(self, project: NWProject) -> None:
|
330
|
+
"""Emit the indexCleared signal."""
|
331
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
318
332
|
self.indexCleared.emit()
|
319
|
-
|
333
|
+
return
|
334
|
+
|
335
|
+
def emitIndexAvailable(self, project: NWProject) -> None:
|
336
|
+
"""Emit the indexAvailable signal."""
|
337
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
320
338
|
self.indexAvailable.emit()
|
321
339
|
return
|
322
340
|
|
323
|
-
def
|
324
|
-
"""Emit
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
341
|
+
def emitStatusLabelsChanged(self, project: NWProject, kind: T_StatusKind) -> None:
|
342
|
+
"""Emit the statusLabelsChanged signal."""
|
343
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
344
|
+
self.statusLabelsChanged.emit(kind)
|
345
|
+
return
|
346
|
+
|
347
|
+
def emitProjectItemChanged(self, project: NWProject, handle: str, change: nwChange) -> None:
|
348
|
+
"""Emit the projectItemChanged signal."""
|
349
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
350
|
+
self.projectItemChanged.emit(handle, change)
|
351
|
+
return
|
352
|
+
|
353
|
+
def emitRootFolderChanged(self, project: NWProject, handle: str, change: nwChange) -> None:
|
354
|
+
"""Emit the rootFolderChanged signal."""
|
355
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
356
|
+
self.rootFolderChanged.emit(handle, change)
|
329
357
|
return
|
330
358
|
|
331
359
|
##
|
@@ -386,6 +414,7 @@ class SharedData(QObject):
|
|
386
414
|
"""Create a new project and spell checking instance."""
|
387
415
|
from novelwriter.core.project import NWProject
|
388
416
|
if isinstance(self._project, NWProject):
|
417
|
+
self._project.clear()
|
389
418
|
del self._project
|
390
419
|
del self._spelling
|
391
420
|
self._project = NWProject()
|
novelwriter/text/patterns.py
CHANGED
@@ -28,11 +28,13 @@ import re
|
|
28
28
|
|
29
29
|
from novelwriter import CONFIG
|
30
30
|
from novelwriter.common import compact, uniqueCompact
|
31
|
-
from novelwriter.constants import nwRegEx
|
31
|
+
from novelwriter.constants import nwRegEx, nwUnicode
|
32
32
|
|
33
33
|
|
34
34
|
class RegExPatterns:
|
35
35
|
|
36
|
+
AMBIGUOUS = (nwUnicode.U_APOS, nwUnicode.U_RSQUO)
|
37
|
+
|
36
38
|
# Static RegExes
|
37
39
|
_rxUrl = re.compile(nwRegEx.URL, re.ASCII)
|
38
40
|
_rxWords = re.compile(nwRegEx.WORDS, re.UNICODE)
|
@@ -87,16 +89,25 @@ class RegExPatterns:
|
|
87
89
|
def dialogStyle(self) -> re.Pattern | None:
|
88
90
|
"""Dialogue detection rule based on user settings."""
|
89
91
|
if CONFIG.dialogStyle > 0:
|
90
|
-
end = "|$" if CONFIG.allowOpenDial else ""
|
91
92
|
rx = []
|
92
93
|
if CONFIG.dialogStyle in (1, 3):
|
93
94
|
qO = CONFIG.fmtSQuoteOpen.strip()[:1]
|
94
95
|
qC = CONFIG.fmtSQuoteClose.strip()[:1]
|
95
|
-
|
96
|
+
if qO == qC:
|
97
|
+
rx.append(f"(?:\\B{qO}.+?{qC}\\B)")
|
98
|
+
else:
|
99
|
+
rx.append(f"(?:{qO}[^{qO}]+{qC})")
|
100
|
+
if CONFIG.allowOpenDial:
|
101
|
+
rx.append(f"(?:{qO}.+?$)")
|
96
102
|
if CONFIG.dialogStyle in (2, 3):
|
97
103
|
qO = CONFIG.fmtDQuoteOpen.strip()[:1]
|
98
104
|
qC = CONFIG.fmtDQuoteClose.strip()[:1]
|
99
|
-
|
105
|
+
if qO == qC:
|
106
|
+
rx.append(f"(?:\\B{qO}.+?{qC}\\B)")
|
107
|
+
else:
|
108
|
+
rx.append(f"(?:{qO}[^{qO}]+{qC})")
|
109
|
+
if CONFIG.allowOpenDial:
|
110
|
+
rx.append(f"(?:{qO}.+?$)")
|
100
111
|
return re.compile("|".join(rx), re.UNICODE)
|
101
112
|
return None
|
102
113
|
|
@@ -106,7 +117,8 @@ class RegExPatterns:
|
|
106
117
|
if CONFIG.altDialogOpen and CONFIG.altDialogClose:
|
107
118
|
qO = re.escape(compact(CONFIG.altDialogOpen))
|
108
119
|
qC = re.escape(compact(CONFIG.altDialogClose))
|
109
|
-
|
120
|
+
qB = r"\B" if (qO == qC or qC in self.AMBIGUOUS) else ""
|
121
|
+
return re.compile(f"{qO}.*?{qC}{qB}", re.UNICODE)
|
110
122
|
return None
|
111
123
|
|
112
124
|
|
novelwriter/tools/manusbuild.py
CHANGED
@@ -179,14 +179,20 @@ class GuiManuscriptBuild(NDialog):
|
|
179
179
|
self.buildBox.setVerticalSpacing(sp4)
|
180
180
|
|
181
181
|
# Dialog Buttons
|
182
|
+
self.buttonBox = QDialogButtonBox(self)
|
183
|
+
|
182
184
|
self.btnOpen = QPushButton(SHARED.theme.getIcon("browse"), self.tr("Open Folder"), self)
|
183
185
|
self.btnOpen.setIconSize(bSz)
|
186
|
+
self.btnOpen.setAutoDefault(False)
|
187
|
+
self.buttonBox.addButton(self.btnOpen, QtRoleAction)
|
188
|
+
|
184
189
|
self.btnBuild = QPushButton(SHARED.theme.getIcon("export"), self.tr("&Build"), self)
|
185
190
|
self.btnBuild.setIconSize(bSz)
|
191
|
+
self.btnBuild.setAutoDefault(True)
|
192
|
+
self.buttonBox.addButton(self.btnBuild, QtRoleAction)
|
186
193
|
|
187
|
-
self.
|
188
|
-
self.
|
189
|
-
self.dlgButtons.addButton(self.btnBuild, QtRoleAction)
|
194
|
+
self.btnClose = self.buttonBox.addButton(QtDialogClose)
|
195
|
+
self.btnClose.setAutoDefault(False)
|
190
196
|
|
191
197
|
# Assemble GUI
|
192
198
|
# ============
|
@@ -213,7 +219,7 @@ class GuiManuscriptBuild(NDialog):
|
|
213
219
|
self.outerBox.addSpacing(sp4)
|
214
220
|
self.outerBox.addLayout(self.buildBox, 0)
|
215
221
|
self.outerBox.addSpacing(sp16)
|
216
|
-
self.outerBox.addWidget(self.
|
222
|
+
self.outerBox.addWidget(self.buttonBox, 0)
|
217
223
|
self.outerBox.setSpacing(0)
|
218
224
|
|
219
225
|
self.setLayout(self.outerBox)
|
@@ -229,7 +235,7 @@ class GuiManuscriptBuild(NDialog):
|
|
229
235
|
# Signals
|
230
236
|
self.btnReset.clicked.connect(self._doResetBuildName)
|
231
237
|
self.btnBrowse.clicked.connect(self._doSelectPath)
|
232
|
-
self.
|
238
|
+
self.buttonBox.clicked.connect(self._dialogButtonClicked)
|
233
239
|
self.listFormats.itemSelectionChanged.connect(self._resetProgress)
|
234
240
|
|
235
241
|
logger.debug("Ready: GuiManuscriptBuild")
|
@@ -260,7 +266,7 @@ class GuiManuscriptBuild(NDialog):
|
|
260
266
|
@pyqtSlot("QAbstractButton*")
|
261
267
|
def _dialogButtonClicked(self, button: QAbstractButton) -> None:
|
262
268
|
"""Handle button clicks from the dialog button box."""
|
263
|
-
role = self.
|
269
|
+
role = self.buttonBox.buttonRole(button)
|
264
270
|
if role == QtRoleAction:
|
265
271
|
if button == self.btnBuild:
|
266
272
|
self._runBuild()
|
@@ -388,13 +394,9 @@ class GuiManuscriptBuild(NDialog):
|
|
388
394
|
if isinstance(rItem, NWItem):
|
389
395
|
rootMap[rHandle] = rItem.itemName
|
390
396
|
|
391
|
-
itemIcon = SHARED.theme.getItemIcon(
|
392
|
-
nwItem.itemType, nwItem.itemClass,
|
393
|
-
nwItem.itemLayout, nwItem.mainHeading
|
394
|
-
)
|
395
397
|
rootName = rootMap.get(rHandle, "??????")
|
396
398
|
item = QListWidgetItem(f"{rootName}: {nwItem.itemName}")
|
397
|
-
item.setIcon(
|
399
|
+
item.setIcon(nwItem.getMainIcon())
|
398
400
|
self.listContent.addItem(item)
|
399
401
|
|
400
402
|
return
|