novelWriter 2.4.4__py3-none-any.whl → 2.5__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.4.4.dist-info → novelWriter-2.5.dist-info}/METADATA +4 -5
- {novelWriter-2.4.4.dist-info → novelWriter-2.5.dist-info}/RECORD +121 -111
- {novelWriter-2.4.4.dist-info → novelWriter-2.5.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +33 -39
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/i18n/project_pl_PL.json +116 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +2 -0
- novelwriter/assets/icons/typicons_dark/nw_font.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_quote.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +2 -0
- novelwriter/assets/icons/typicons_light/nw_font.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_quote.svg +4 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/cyberpunk_night.conf +5 -3
- novelwriter/assets/syntax/default_dark.conf +32 -18
- novelwriter/assets/syntax/default_light.conf +24 -10
- novelwriter/assets/syntax/dracula.conf +44 -0
- novelwriter/assets/syntax/grey_dark.conf +5 -4
- novelwriter/assets/syntax/grey_light.conf +5 -4
- novelwriter/assets/syntax/light_owl.conf +7 -6
- novelwriter/assets/syntax/night_owl.conf +7 -6
- novelwriter/assets/syntax/snazzy.conf +42 -0
- novelwriter/assets/syntax/solarized_dark.conf +4 -3
- novelwriter/assets/syntax/solarized_light.conf +4 -3
- novelwriter/assets/syntax/tango.conf +27 -11
- novelwriter/assets/syntax/tomorrow.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night.conf +7 -6
- novelwriter/assets/syntax/tomorrow_night_blue.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night_bright.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +6 -5
- novelwriter/assets/text/credits_en.htm +52 -41
- novelwriter/assets/themes/cyberpunk_night.conf +3 -0
- novelwriter/assets/themes/default_dark.conf +2 -0
- novelwriter/assets/themes/default_light.conf +2 -0
- novelwriter/assets/themes/dracula.conf +48 -0
- novelwriter/assets/themes/solarized_dark.conf +2 -0
- novelwriter/assets/themes/solarized_light.conf +2 -0
- novelwriter/common.py +33 -12
- novelwriter/config.py +184 -98
- novelwriter/constants.py +47 -35
- novelwriter/core/buildsettings.py +68 -69
- novelwriter/core/coretools.py +5 -23
- novelwriter/core/docbuild.py +52 -40
- novelwriter/core/document.py +3 -5
- novelwriter/core/index.py +115 -45
- novelwriter/core/item.py +8 -19
- novelwriter/core/options.py +2 -4
- novelwriter/core/project.py +37 -61
- novelwriter/core/projectdata.py +1 -3
- novelwriter/core/projectxml.py +12 -15
- novelwriter/core/sessions.py +3 -5
- novelwriter/core/spellcheck.py +4 -9
- novelwriter/core/status.py +211 -164
- novelwriter/core/storage.py +0 -8
- novelwriter/core/tohtml.py +139 -105
- novelwriter/core/tokenizer.py +278 -122
- novelwriter/core/{tomd.py → tomarkdown.py} +97 -78
- novelwriter/core/toodt.py +257 -166
- novelwriter/core/toqdoc.py +419 -0
- novelwriter/core/tree.py +5 -7
- novelwriter/dialogs/about.py +11 -18
- novelwriter/dialogs/docmerge.py +17 -19
- novelwriter/dialogs/docsplit.py +17 -19
- novelwriter/dialogs/editlabel.py +6 -10
- novelwriter/dialogs/preferences.py +200 -164
- novelwriter/dialogs/projectsettings.py +225 -189
- novelwriter/dialogs/quotes.py +12 -9
- novelwriter/dialogs/wordlist.py +9 -15
- novelwriter/enum.py +35 -30
- novelwriter/error.py +8 -15
- novelwriter/extensions/configlayout.py +55 -21
- novelwriter/extensions/eventfilters.py +1 -5
- novelwriter/extensions/modified.py +58 -14
- novelwriter/extensions/novelselector.py +1 -3
- novelwriter/extensions/pagedsidebar.py +9 -12
- novelwriter/extensions/{circularprogress.py → progressbars.py} +30 -8
- novelwriter/extensions/statusled.py +40 -26
- novelwriter/extensions/switch.py +4 -6
- novelwriter/extensions/switchbox.py +7 -6
- novelwriter/extensions/versioninfo.py +3 -9
- novelwriter/gui/doceditor.py +120 -139
- novelwriter/gui/dochighlight.py +231 -186
- novelwriter/gui/docviewer.py +69 -108
- novelwriter/gui/docviewerpanel.py +3 -10
- novelwriter/gui/editordocument.py +1 -3
- novelwriter/gui/itemdetails.py +7 -11
- novelwriter/gui/mainmenu.py +22 -18
- novelwriter/gui/noveltree.py +11 -24
- novelwriter/gui/outline.py +15 -26
- novelwriter/gui/projtree.py +35 -60
- novelwriter/gui/search.py +10 -3
- novelwriter/gui/sidebar.py +2 -6
- novelwriter/gui/statusbar.py +29 -37
- novelwriter/gui/theme.py +26 -48
- novelwriter/guimain.py +162 -160
- novelwriter/shared.py +36 -32
- novelwriter/text/patterns.py +113 -0
- novelwriter/tools/dictionaries.py +10 -20
- novelwriter/tools/lipsum.py +10 -16
- novelwriter/tools/manusbuild.py +9 -11
- novelwriter/tools/manuscript.py +71 -145
- novelwriter/tools/manussettings.py +71 -75
- novelwriter/tools/noveldetails.py +16 -21
- novelwriter/tools/welcome.py +21 -26
- novelwriter/tools/writingstats.py +9 -12
- novelwriter/types.py +49 -4
- novelwriter/extensions/simpleprogress.py +0 -55
- {novelWriter-2.4.4.dist-info → novelWriter-2.5.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.4.4.dist-info → novelWriter-2.5.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.4.4.dist-info → novelWriter-2.5.dist-info}/top_level.txt +0 -0
novelwriter/gui/outline.py
CHANGED
@@ -30,30 +30,28 @@ from __future__ import annotations
|
|
30
30
|
import csv
|
31
31
|
import logging
|
32
32
|
|
33
|
-
from time import time
|
34
33
|
from enum import Enum
|
34
|
+
from time import time
|
35
35
|
|
36
|
-
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
36
|
+
from PyQt5.QtCore import QT_TRANSLATE_NOOP, Qt, pyqtSignal, pyqtSlot
|
37
37
|
from PyQt5.QtWidgets import (
|
38
38
|
QAbstractItemView, QAction, QFileDialog, QFrame, QGridLayout, QGroupBox,
|
39
|
-
QHBoxLayout, QLabel, QMenu, QScrollArea,
|
40
|
-
|
39
|
+
QHBoxLayout, QLabel, QMenu, QScrollArea, QSplitter, QToolBar, QToolButton,
|
40
|
+
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
41
41
|
)
|
42
42
|
|
43
43
|
from novelwriter import CONFIG, SHARED
|
44
|
-
from novelwriter.enum import (
|
45
|
-
nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
|
46
|
-
)
|
47
|
-
from novelwriter.error import logException
|
48
44
|
from novelwriter.common import checkInt, formatFileFilter, makeFileNameSafe
|
49
|
-
from novelwriter.constants import nwHeaders,
|
45
|
+
from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
|
46
|
+
from novelwriter.enum import nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
|
47
|
+
from novelwriter.error import logException
|
50
48
|
from novelwriter.extensions.configlayout import NColourLabel
|
51
49
|
from novelwriter.extensions.novelselector import NovelSelector
|
52
50
|
from novelwriter.types import (
|
53
|
-
QtAlignLeftTop, QtAlignRight, QtAlignRightTop, QtDecoration,
|
51
|
+
QtAlignLeftTop, QtAlignRight, QtAlignRightTop, QtDecoration,
|
52
|
+
QtSizeExpanding, QtUserRole
|
54
53
|
)
|
55
54
|
|
56
|
-
|
57
55
|
logger = logging.getLogger(__name__)
|
58
56
|
|
59
57
|
|
@@ -196,8 +194,6 @@ class GuiOutlineView(QWidget):
|
|
196
194
|
self.outlineTree.refreshTree(rootHandle=(tHandle or None), overRide=True)
|
197
195
|
return
|
198
196
|
|
199
|
-
# END Class GuiOutlineView
|
200
|
-
|
201
197
|
|
202
198
|
class GuiOutlineToolBar(QToolBar):
|
203
199
|
|
@@ -215,11 +211,11 @@ class GuiOutlineToolBar(QToolBar):
|
|
215
211
|
self.setContentsMargins(0, 0, 0, 0)
|
216
212
|
|
217
213
|
stretch = QWidget(self)
|
218
|
-
stretch.setSizePolicy(
|
214
|
+
stretch.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
|
219
215
|
|
220
216
|
# Novel Selector
|
221
217
|
self.novelLabel = NColourLabel(
|
222
|
-
self.tr("Outline of"),
|
218
|
+
self.tr("Outline of"), self, scale=NColourLabel.HEADER_SCALE, bold=True
|
223
219
|
)
|
224
220
|
self.novelLabel.setContentsMargins(0, 0, CONFIG.pxInt(12), 0)
|
225
221
|
|
@@ -272,6 +268,7 @@ class GuiOutlineToolBar(QToolBar):
|
|
272
268
|
self.aExport.setIcon(SHARED.theme.getIcon("export"))
|
273
269
|
self.tbColumns.setIcon(SHARED.theme.getIcon("menu"))
|
274
270
|
self.tbColumns.setStyleSheet("QToolButton::menu-indicator {image: none;}")
|
271
|
+
self.novelLabel.setTextColors(color=self.palette().windowText().color())
|
275
272
|
return
|
276
273
|
|
277
274
|
def populateNovelList(self) -> None:
|
@@ -311,8 +308,6 @@ class GuiOutlineToolBar(QToolBar):
|
|
311
308
|
self.outlineExportRequest.emit()
|
312
309
|
return
|
313
310
|
|
314
|
-
# END Class GuiOutlineToolBar
|
315
|
-
|
316
311
|
|
317
312
|
class GuiOutlineTree(QTreeWidget):
|
318
313
|
|
@@ -529,12 +524,12 @@ class GuiOutlineTree(QTreeWidget):
|
|
529
524
|
@pyqtSlot()
|
530
525
|
def exportOutline(self) -> None:
|
531
526
|
"""Export the outline as a CSV file."""
|
532
|
-
path = CONFIG.lastPath() / f"{makeFileNameSafe(SHARED.project.data.name)}.csv"
|
527
|
+
path = CONFIG.lastPath("outline") / f"{makeFileNameSafe(SHARED.project.data.name)}.csv"
|
533
528
|
path, _ = QFileDialog.getSaveFileName(
|
534
529
|
self, self.tr("Save Outline As"), str(path), formatFileFilter(["*.csv", "*"])
|
535
530
|
)
|
536
531
|
if path:
|
537
|
-
CONFIG.setLastPath(path)
|
532
|
+
CONFIG.setLastPath("outline", path)
|
538
533
|
logger.info("Writing CSV file: %s", path)
|
539
534
|
cols = [col for col in self._treeOrder if not self._colHidden[col]]
|
540
535
|
order = [self._colIdx[col] for col in cols]
|
@@ -728,8 +723,6 @@ class GuiOutlineTree(QTreeWidget):
|
|
728
723
|
|
729
724
|
return
|
730
725
|
|
731
|
-
# END Class GuiOutlineTree
|
732
|
-
|
733
726
|
|
734
727
|
class GuiOutlineHeaderMenu(QMenu):
|
735
728
|
|
@@ -772,8 +765,6 @@ class GuiOutlineHeaderMenu(QMenu):
|
|
772
765
|
|
773
766
|
return
|
774
767
|
|
775
|
-
# END Class GuiOutlineHeaderMenu
|
776
|
-
|
777
768
|
|
778
769
|
class GuiOutlineDetails(QScrollArea):
|
779
770
|
|
@@ -1048,7 +1039,7 @@ class GuiOutlineDetails(QScrollArea):
|
|
1048
1039
|
self.titleLabel.setText(self.tr(self.LVL_MAP.get(novIdx.level, "H1")))
|
1049
1040
|
self.titleValue.setText(novIdx.title)
|
1050
1041
|
|
1051
|
-
itemStatus, _ = nwItem.getImportStatus(
|
1042
|
+
itemStatus, _ = nwItem.getImportStatus()
|
1052
1043
|
|
1053
1044
|
self.fileValue.setText(nwItem.itemName)
|
1054
1045
|
self.itemValue.setText(itemStatus)
|
@@ -1104,5 +1095,3 @@ class GuiOutlineDetails(QScrollArea):
|
|
1104
1095
|
return ", ".join(
|
1105
1096
|
[f"<a href='{tag}'>{tag}</a>" for tag in refs.get(key, [])]
|
1106
1097
|
)
|
1107
|
-
|
1108
|
-
# END Class GuiOutlineDetails
|
novelwriter/gui/projtree.py
CHANGED
@@ -30,14 +30,12 @@ import logging
|
|
30
30
|
|
31
31
|
from enum import Enum
|
32
32
|
from time import time
|
33
|
-
from typing import TYPE_CHECKING
|
34
33
|
|
35
34
|
from PyQt5.QtCore import QPoint, Qt, QTimer, pyqtSignal, pyqtSlot
|
36
35
|
from PyQt5.QtGui import QDragEnterEvent, QDragMoveEvent, QDropEvent, QIcon, QMouseEvent, QPalette
|
37
36
|
from PyQt5.QtWidgets import (
|
38
|
-
QAbstractItemView, QAction,
|
39
|
-
|
40
|
-
QVBoxLayout, QWidget
|
37
|
+
QAbstractItemView, QAction, QFrame, QHBoxLayout, QHeaderView, QLabel,
|
38
|
+
QMenu, QShortcut, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
41
39
|
)
|
42
40
|
|
43
41
|
from novelwriter import CONFIG, SHARED
|
@@ -52,10 +50,10 @@ from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
|
52
50
|
from novelwriter.enum import nwDocMode, nwItemClass, nwItemLayout, nwItemType
|
53
51
|
from novelwriter.extensions.modified import NIconToolButton
|
54
52
|
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
55
|
-
from novelwriter.types import
|
56
|
-
|
57
|
-
|
58
|
-
|
53
|
+
from novelwriter.types import (
|
54
|
+
QtAlignLeft, QtAlignRight, QtMouseLeft, QtMouseMiddle, QtSizeExpanding,
|
55
|
+
QtUserRole
|
56
|
+
)
|
59
57
|
|
60
58
|
logger = logging.getLogger(__name__)
|
61
59
|
|
@@ -78,10 +76,8 @@ class GuiProjectView(QWidget):
|
|
78
76
|
# Requests for the main GUI
|
79
77
|
projectSettingsRequest = pyqtSignal(int)
|
80
78
|
|
81
|
-
def __init__(self,
|
82
|
-
super().__init__(parent=
|
83
|
-
|
84
|
-
self.mainGui = mainGui
|
79
|
+
def __init__(self, parent: QWidget) -> None:
|
80
|
+
super().__init__(parent=parent)
|
85
81
|
|
86
82
|
# Build GUI
|
87
83
|
self.projTree = GuiProjectTree(self)
|
@@ -246,8 +242,6 @@ class GuiProjectView(QWidget):
|
|
246
242
|
self.projTree.createNewNote(tag, itemClass)
|
247
243
|
return
|
248
244
|
|
249
|
-
# END Class GuiProjectView
|
250
|
-
|
251
245
|
|
252
246
|
class GuiProjectToolBar(QWidget):
|
253
247
|
|
@@ -260,7 +254,6 @@ class GuiProjectToolBar(QWidget):
|
|
260
254
|
|
261
255
|
self.projView = projView
|
262
256
|
self.projTree = projView.projTree
|
263
|
-
self.mainGui = projView.mainGui
|
264
257
|
|
265
258
|
iSz = SHARED.theme.baseIconSize
|
266
259
|
mPx = CONFIG.pxInt(2)
|
@@ -272,7 +265,7 @@ class GuiProjectToolBar(QWidget):
|
|
272
265
|
self.viewLabel = QLabel(self.tr("Project Content"), self)
|
273
266
|
self.viewLabel.setFont(SHARED.theme.guiFontB)
|
274
267
|
self.viewLabel.setContentsMargins(0, 0, 0, 0)
|
275
|
-
self.viewLabel.setSizePolicy(
|
268
|
+
self.viewLabel.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
|
276
269
|
|
277
270
|
# Quick Links
|
278
271
|
self.mQuick = QMenu(self)
|
@@ -474,8 +467,6 @@ class GuiProjectToolBar(QWidget):
|
|
474
467
|
|
475
468
|
return
|
476
469
|
|
477
|
-
# END Class GuiProjectToolBar
|
478
|
-
|
479
470
|
|
480
471
|
class GuiProjectTree(QTreeWidget):
|
481
472
|
|
@@ -496,7 +487,6 @@ class GuiProjectTree(QTreeWidget):
|
|
496
487
|
logger.debug("Create: GuiProjectTree")
|
497
488
|
|
498
489
|
self.projView = projView
|
499
|
-
self.mainGui = projView.mainGui
|
500
490
|
|
501
491
|
# Internal Variables
|
502
492
|
self._treeMap: dict[str, QTreeWidgetItem] = {}
|
@@ -1007,8 +997,7 @@ class GuiProjectTree(QTreeWidget):
|
|
1007
997
|
trItemP.takeChild(tIndex)
|
1008
998
|
|
1009
999
|
for dHandle in reversed(self.getTreeFromHandle(tHandle)):
|
1010
|
-
|
1011
|
-
self.mainGui.closeDocument()
|
1000
|
+
SHARED.closeEditor(dHandle)
|
1012
1001
|
SHARED.project.removeItem(dHandle)
|
1013
1002
|
self._treeMap.pop(dHandle, None)
|
1014
1003
|
|
@@ -1031,7 +1020,7 @@ class GuiProjectTree(QTreeWidget):
|
|
1031
1020
|
if trItem is None or nwItem is None:
|
1032
1021
|
return
|
1033
1022
|
|
1034
|
-
itemStatus, statusIcon = nwItem.getImportStatus(
|
1023
|
+
itemStatus, statusIcon = nwItem.getImportStatus()
|
1035
1024
|
hLevel = nwItem.mainHeading
|
1036
1025
|
itemIcon = SHARED.theme.getItemIcon(
|
1037
1026
|
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel
|
@@ -1407,19 +1396,15 @@ class GuiProjectTree(QTreeWidget):
|
|
1407
1396
|
if not newFile:
|
1408
1397
|
itemList.remove(tHandle)
|
1409
1398
|
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
mrgData = dlgMerge.getData()
|
1416
|
-
mrgList = mrgData.get("finalItems", [])
|
1417
|
-
if not mrgList:
|
1399
|
+
data, status = GuiDocMerge.getData(SHARED.mainGui, tHandle, itemList)
|
1400
|
+
if status:
|
1401
|
+
items = data.get("finalItems", [])
|
1402
|
+
if not items:
|
1418
1403
|
SHARED.info(self.tr("No documents selected for merging."))
|
1419
1404
|
return False
|
1420
1405
|
|
1421
1406
|
# Save the open document first, in case it's part of merge
|
1422
|
-
|
1407
|
+
SHARED.saveEditor()
|
1423
1408
|
|
1424
1409
|
# Create merge object, and append docs
|
1425
1410
|
docMerger = DocMerger(SHARED.project)
|
@@ -1434,7 +1419,7 @@ class GuiProjectTree(QTreeWidget):
|
|
1434
1419
|
else:
|
1435
1420
|
return False
|
1436
1421
|
|
1437
|
-
for sHandle in
|
1422
|
+
for sHandle in items:
|
1438
1423
|
docMerger.appendText(sHandle, True, mLabel)
|
1439
1424
|
|
1440
1425
|
if not docMerger.writeTargetDoc():
|
@@ -1448,10 +1433,11 @@ class GuiProjectTree(QTreeWidget):
|
|
1448
1433
|
if newFile:
|
1449
1434
|
self.revealNewTreeItem(mHandle, nHandle=tHandle, wordCount=True)
|
1450
1435
|
|
1451
|
-
self.
|
1436
|
+
self.projView.openDocumentRequest.emit(mHandle, nwDocMode.EDIT, "", False)
|
1437
|
+
self.projView.setSelectedHandle(mHandle, doScroll=True)
|
1452
1438
|
|
1453
|
-
if
|
1454
|
-
for sHandle in reversed(
|
1439
|
+
if data.get("moveToTrash", False):
|
1440
|
+
for sHandle in reversed(data.get("finalItems", [])):
|
1455
1441
|
trItem = self._getTreeItem(sHandle)
|
1456
1442
|
if isinstance(trItem, QTreeWidgetItem) and trItem.childCount() == 0:
|
1457
1443
|
self.moveItemToTrash(sHandle, askFirst=False, flush=False)
|
@@ -1477,16 +1463,11 @@ class GuiProjectTree(QTreeWidget):
|
|
1477
1463
|
logger.error("Only valid document items can be split")
|
1478
1464
|
return False
|
1479
1465
|
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
splitData, splitText = dlgSplit.getData()
|
1486
|
-
|
1487
|
-
headerList = splitData.get("headerList", [])
|
1488
|
-
intoFolder = splitData.get("intoFolder", False)
|
1489
|
-
docHierarchy = splitData.get("docHierarchy", False)
|
1466
|
+
data, text, status = GuiDocSplit.getData(SHARED.mainGui, tHandle)
|
1467
|
+
if status:
|
1468
|
+
headerList = data.get("headerList", [])
|
1469
|
+
intoFolder = data.get("intoFolder", False)
|
1470
|
+
docHierarchy = data.get("docHierarchy", False)
|
1490
1471
|
|
1491
1472
|
docSplit = DocSplitter(SHARED.project, tHandle)
|
1492
1473
|
if intoFolder:
|
@@ -1496,7 +1477,7 @@ class GuiProjectTree(QTreeWidget):
|
|
1496
1477
|
else:
|
1497
1478
|
docSplit.setParentItem(tItem.itemParent)
|
1498
1479
|
|
1499
|
-
docSplit.splitDocument(headerList,
|
1480
|
+
docSplit.splitDocument(headerList, text)
|
1500
1481
|
for writeOk, dHandle, nHandle in docSplit.writeDocuments(docHierarchy):
|
1501
1482
|
SHARED.project.index.reIndexHandle(dHandle)
|
1502
1483
|
self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True)
|
@@ -1507,7 +1488,7 @@ class GuiProjectTree(QTreeWidget):
|
|
1507
1488
|
info=docSplit.getError()
|
1508
1489
|
)
|
1509
1490
|
|
1510
|
-
if
|
1491
|
+
if data.get("moveToTrash", False):
|
1511
1492
|
self.moveItemToTrash(tHandle, askFirst=False, flush=True)
|
1512
1493
|
|
1513
1494
|
self.saveTreeOrder()
|
@@ -1656,8 +1637,6 @@ class GuiProjectTree(QTreeWidget):
|
|
1656
1637
|
|
1657
1638
|
return
|
1658
1639
|
|
1659
|
-
# END Class GuiProjectTree
|
1660
|
-
|
1661
1640
|
|
1662
1641
|
class _UpdatableMenu(QMenu):
|
1663
1642
|
|
@@ -1721,8 +1700,6 @@ class _UpdatableMenu(QMenu):
|
|
1721
1700
|
self.menuItemTriggered.emit(str(action.data()))
|
1722
1701
|
return
|
1723
1702
|
|
1724
|
-
# END Class _UpdatableMenu
|
1725
|
-
|
1726
1703
|
|
1727
1704
|
class _TreeContextMenu(QMenu):
|
1728
1705
|
|
@@ -1828,7 +1805,7 @@ class _TreeContextMenu(QMenu):
|
|
1828
1805
|
|
1829
1806
|
def _itemHeader(self) -> None:
|
1830
1807
|
"""Check if there is a header that can be used for rename."""
|
1831
|
-
SHARED.
|
1808
|
+
SHARED.saveEditor()
|
1832
1809
|
if hItem := SHARED.project.index.getItemHeading(self._handle, "T0001"):
|
1833
1810
|
action = self.addAction(self.tr("Rename to Heading"))
|
1834
1811
|
action.triggered.connect(
|
@@ -1854,11 +1831,11 @@ class _TreeContextMenu(QMenu):
|
|
1854
1831
|
if self._item.isNovelLike():
|
1855
1832
|
menu = self.addMenu(self.tr("Set Status to ..."))
|
1856
1833
|
current = self._item.itemStatus
|
1857
|
-
for n, (key, entry) in enumerate(SHARED.project.data.itemStatus.
|
1858
|
-
name = entry
|
1834
|
+
for n, (key, entry) in enumerate(SHARED.project.data.itemStatus.iterItems()):
|
1835
|
+
name = entry.name
|
1859
1836
|
if not multi and current == key:
|
1860
1837
|
name += f" ({nwUnicode.U_CHECK})"
|
1861
|
-
action = menu.addAction(entry
|
1838
|
+
action = menu.addAction(entry.icon, name)
|
1862
1839
|
if multi:
|
1863
1840
|
action.triggered.connect(lambda n, key=key: self._iterSetItemStatus(key))
|
1864
1841
|
else:
|
@@ -1871,11 +1848,11 @@ class _TreeContextMenu(QMenu):
|
|
1871
1848
|
else:
|
1872
1849
|
menu = self.addMenu(self.tr("Set Importance to ..."))
|
1873
1850
|
current = self._item.itemImport
|
1874
|
-
for n, (key, entry) in enumerate(SHARED.project.data.itemImport.
|
1875
|
-
name = entry
|
1851
|
+
for n, (key, entry) in enumerate(SHARED.project.data.itemImport.iterItems()):
|
1852
|
+
name = entry.name
|
1876
1853
|
if not multi and current == key:
|
1877
1854
|
name += f" ({nwUnicode.U_CHECK})"
|
1878
|
-
action = menu.addAction(entry
|
1855
|
+
action = menu.addAction(entry.icon, name)
|
1879
1856
|
if multi:
|
1880
1857
|
action.triggered.connect(lambda n, key=key: self._iterSetItemImport(key))
|
1881
1858
|
else:
|
@@ -2075,5 +2052,3 @@ class _TreeContextMenu(QMenu):
|
|
2075
2052
|
else:
|
2076
2053
|
logger.info("Folder conversion cancelled")
|
2077
2054
|
return
|
2078
|
-
|
2079
|
-
# END Class _TreeContextMenu
|
novelwriter/gui/search.py
CHANGED
@@ -208,6 +208,12 @@ class GuiProjectSearch(QWidget):
|
|
208
208
|
self.searchResult.clear()
|
209
209
|
return
|
210
210
|
|
211
|
+
def refreshCurrentSearch(self) -> None:
|
212
|
+
"""Refresh the search if there is one."""
|
213
|
+
if self.searchResult.topLevelItemCount() > 0:
|
214
|
+
self._processSearch()
|
215
|
+
return
|
216
|
+
|
211
217
|
##
|
212
218
|
# Events
|
213
219
|
##
|
@@ -259,7 +265,7 @@ class GuiProjectSearch(QWidget):
|
|
259
265
|
if not self._blocked:
|
260
266
|
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
|
261
267
|
start = time()
|
262
|
-
SHARED.
|
268
|
+
SHARED.saveEditor()
|
263
269
|
self._blocked = True
|
264
270
|
self._map = {}
|
265
271
|
self.searchResult.clear()
|
@@ -298,18 +304,21 @@ class GuiProjectSearch(QWidget):
|
|
298
304
|
def _toggleCase(self, state: bool) -> None:
|
299
305
|
"""Enable/disable case sensitive mode."""
|
300
306
|
CONFIG.searchProjCase = state
|
307
|
+
self.refreshCurrentSearch()
|
301
308
|
return
|
302
309
|
|
303
310
|
@pyqtSlot(bool)
|
304
311
|
def _toggleWord(self, state: bool) -> None:
|
305
312
|
"""Enable/disable whole word search mode."""
|
306
313
|
CONFIG.searchProjWord = state
|
314
|
+
self.refreshCurrentSearch()
|
307
315
|
return
|
308
316
|
|
309
317
|
@pyqtSlot(bool)
|
310
318
|
def _toggleRegEx(self, state: bool) -> None:
|
311
319
|
"""Enable/disable regular expression search mode."""
|
312
320
|
CONFIG.searchProjRegEx = state
|
321
|
+
self.refreshCurrentSearch()
|
313
322
|
return
|
314
323
|
|
315
324
|
##
|
@@ -358,5 +367,3 @@ class GuiProjectSearch(QWidget):
|
|
358
367
|
QApplication.processEvents()
|
359
368
|
|
360
369
|
return
|
361
|
-
|
362
|
-
# END Class GuiProjectSearch
|
novelwriter/gui/sidebar.py
CHANGED
@@ -27,8 +27,8 @@ import logging
|
|
27
27
|
|
28
28
|
from typing import TYPE_CHECKING
|
29
29
|
|
30
|
-
from PyQt5.QtGui import QPalette
|
31
30
|
from PyQt5.QtCore import QEvent, QPoint, QSize, pyqtSignal
|
31
|
+
from PyQt5.QtGui import QPalette
|
32
32
|
from PyQt5.QtWidgets import QMenu, QVBoxLayout, QWidget
|
33
33
|
|
34
34
|
from novelwriter import CONFIG, SHARED
|
@@ -58,7 +58,7 @@ class GuiSideBar(QWidget):
|
|
58
58
|
iSz = QSize(iPx, iPx)
|
59
59
|
|
60
60
|
self.setContentsMargins(0, 0, 0, 0)
|
61
|
-
self.installEventFilter(StatusTipFilter(mainGui))
|
61
|
+
self.installEventFilter(StatusTipFilter(self.mainGui))
|
62
62
|
|
63
63
|
# Buttons
|
64
64
|
self.tbProject = NIconToolButton(self, iSz)
|
@@ -150,8 +150,6 @@ class GuiSideBar(QWidget):
|
|
150
150
|
|
151
151
|
return
|
152
152
|
|
153
|
-
# END Class GuiSideBar
|
154
|
-
|
155
153
|
|
156
154
|
class _PopRightMenu(QMenu):
|
157
155
|
|
@@ -162,5 +160,3 @@ class _PopRightMenu(QMenu):
|
|
162
160
|
offset = QPoint(parent.width(), parent.height() - self.height())
|
163
161
|
self.move(parent.mapToGlobal(offset))
|
164
162
|
return super(_PopRightMenu, self).event(event)
|
165
|
-
|
166
|
-
# END Class _PopRightMenu
|
novelwriter/gui/statusbar.py
CHANGED
@@ -27,26 +27,23 @@ import logging
|
|
27
27
|
|
28
28
|
from datetime import datetime
|
29
29
|
from time import time
|
30
|
-
from typing import TYPE_CHECKING, Literal
|
31
30
|
|
32
|
-
from PyQt5.QtCore import
|
33
|
-
from PyQt5.QtWidgets import QApplication, QStatusBar,
|
31
|
+
from PyQt5.QtCore import QLocale, pyqtSlot
|
32
|
+
from PyQt5.QtWidgets import QApplication, QLabel, QStatusBar, QWidget
|
34
33
|
|
35
34
|
from novelwriter import CONFIG, SHARED
|
36
35
|
from novelwriter.common import formatTime
|
37
36
|
from novelwriter.constants import nwConst
|
37
|
+
from novelwriter.enum import nwTrinary
|
38
38
|
from novelwriter.extensions.statusled import StatusLED
|
39
39
|
|
40
|
-
if TYPE_CHECKING: # pragma: no cover
|
41
|
-
from novelwriter.guimain import GuiMain
|
42
|
-
|
43
40
|
logger = logging.getLogger(__name__)
|
44
41
|
|
45
42
|
|
46
43
|
class GuiMainStatus(QStatusBar):
|
47
44
|
|
48
|
-
def __init__(self,
|
49
|
-
super().__init__(parent=
|
45
|
+
def __init__(self, parent: QWidget) -> None:
|
46
|
+
super().__init__(parent=parent)
|
50
47
|
|
51
48
|
logger.debug("Create: GuiMainStatus")
|
52
49
|
|
@@ -54,10 +51,6 @@ class GuiMainStatus(QStatusBar):
|
|
54
51
|
self._userIdle = False
|
55
52
|
self._debugInfo = False
|
56
53
|
|
57
|
-
colNone = SHARED.theme.statNone
|
58
|
-
colSaved = SHARED.theme.statSaved
|
59
|
-
colUnsaved = SHARED.theme.statUnsaved
|
60
|
-
|
61
54
|
iPx = SHARED.theme.baseIconHeight
|
62
55
|
|
63
56
|
# Permanent Widgets
|
@@ -74,7 +67,7 @@ class GuiMainStatus(QStatusBar):
|
|
74
67
|
self.addPermanentWidget(self.langText)
|
75
68
|
|
76
69
|
# The Editor Status
|
77
|
-
self.docIcon = StatusLED(
|
70
|
+
self.docIcon = StatusLED(iPx, iPx, self)
|
78
71
|
self.docText = QLabel(self.tr("Editor"), self)
|
79
72
|
self.docIcon.setContentsMargins(0, 0, 0, 0)
|
80
73
|
self.docText.setContentsMargins(0, 0, xM, 0)
|
@@ -82,7 +75,7 @@ class GuiMainStatus(QStatusBar):
|
|
82
75
|
self.addPermanentWidget(self.docText)
|
83
76
|
|
84
77
|
# The Project Status
|
85
|
-
self.projIcon = StatusLED(
|
78
|
+
self.projIcon = StatusLED(iPx, iPx, self)
|
86
79
|
self.projText = QLabel(self.tr("Project"), self)
|
87
80
|
self.projIcon.setContentsMargins(0, 0, 0, 0)
|
88
81
|
self.projText.setContentsMargins(0, 0, xM, 0)
|
@@ -123,8 +116,8 @@ class GuiMainStatus(QStatusBar):
|
|
123
116
|
self.setRefTime(-1.0)
|
124
117
|
self.setLanguage(*SHARED.spelling.describeDict())
|
125
118
|
self.setProjectStats(0, 0)
|
126
|
-
self.setProjectStatus(
|
127
|
-
self.setDocumentStatus(
|
119
|
+
self.setProjectStatus(nwTrinary.NEUTRAL)
|
120
|
+
self.setDocumentStatus(nwTrinary.NEUTRAL)
|
128
121
|
self.updateTime()
|
129
122
|
return
|
130
123
|
|
@@ -136,6 +129,13 @@ class GuiMainStatus(QStatusBar):
|
|
136
129
|
self.timePixmap = SHARED.theme.getPixmap("status_time", (iPx, iPx))
|
137
130
|
self.idlePixmap = SHARED.theme.getPixmap("status_idle", (iPx, iPx))
|
138
131
|
self.timeIcon.setPixmap(self.timePixmap)
|
132
|
+
|
133
|
+
colNone = SHARED.theme.statNone
|
134
|
+
colSaved = SHARED.theme.statSaved
|
135
|
+
colUnsaved = SHARED.theme.statUnsaved
|
136
|
+
self.docIcon.setColors(colNone, colSaved, colUnsaved)
|
137
|
+
self.projIcon.setColors(colNone, colSaved, colUnsaved)
|
138
|
+
|
139
139
|
return
|
140
140
|
|
141
141
|
##
|
@@ -147,12 +147,12 @@ class GuiMainStatus(QStatusBar):
|
|
147
147
|
self._refTime = refTime
|
148
148
|
return
|
149
149
|
|
150
|
-
def setProjectStatus(self, state:
|
150
|
+
def setProjectStatus(self, state: nwTrinary) -> None:
|
151
151
|
"""Set the project status colour icon."""
|
152
152
|
self.projIcon.setState(state)
|
153
153
|
return
|
154
154
|
|
155
|
-
def setDocumentStatus(self, state:
|
155
|
+
def setDocumentStatus(self, state: nwTrinary) -> None:
|
156
156
|
"""Set the document status colour icon."""
|
157
157
|
self.docIcon.setState(state)
|
158
158
|
return
|
@@ -215,13 +215,13 @@ class GuiMainStatus(QStatusBar):
|
|
215
215
|
@pyqtSlot(bool)
|
216
216
|
def updateProjectStatus(self, status: bool) -> None:
|
217
217
|
"""Update the project status."""
|
218
|
-
self.setProjectStatus(
|
218
|
+
self.setProjectStatus(nwTrinary.NEGATIVE if status else nwTrinary.POSITIVE)
|
219
219
|
return
|
220
220
|
|
221
221
|
@pyqtSlot(bool)
|
222
222
|
def updateDocumentStatus(self, status: bool) -> None:
|
223
223
|
"""Update the document status."""
|
224
|
-
self.setDocumentStatus(
|
224
|
+
self.setDocumentStatus(nwTrinary.NEGATIVE if status else nwTrinary.POSITIVE)
|
225
225
|
return
|
226
226
|
|
227
227
|
##
|
@@ -238,9 +238,8 @@ class GuiMainStatus(QStatusBar):
|
|
238
238
|
before starting novelWriter.
|
239
239
|
"""
|
240
240
|
import tracemalloc
|
241
|
-
from collections import Counter
|
242
241
|
|
243
|
-
|
242
|
+
count = len(QApplication.allWidgets())
|
244
243
|
if not self._debugInfo:
|
245
244
|
if tracemalloc.is_tracing():
|
246
245
|
self._traceMallocRef = "Total"
|
@@ -248,21 +247,14 @@ class GuiMainStatus(QStatusBar):
|
|
248
247
|
self._traceMallocRef = "Relative"
|
249
248
|
tracemalloc.start()
|
250
249
|
self._debugInfo = True
|
251
|
-
self._wCounts = Counter([type(x).__name__ for x in widgets])
|
252
|
-
|
253
|
-
if hasattr(self, "_wCounts"):
|
254
|
-
diff = Counter([type(x).__name__ for x in widgets]) - self._wCounts
|
255
|
-
for name, count in diff.items():
|
256
|
-
logger.debug("Widget '%s': +%d", name, count)
|
257
250
|
|
258
|
-
|
251
|
+
current, peak = tracemalloc.get_traced_memory()
|
259
252
|
stamp = datetime.now().strftime("%H:%M:%S")
|
260
|
-
|
261
|
-
f"
|
262
|
-
f"
|
263
|
-
f"
|
264
|
-
|
265
|
-
|
253
|
+
message = (
|
254
|
+
f"Widgets: {count} \u2013 "
|
255
|
+
f"{self._traceMallocRef} Memory: {current/1024:,.2f} kiB \u2013 "
|
256
|
+
f"Peak: {peak/1024:,.2f} kiB"
|
257
|
+
)
|
258
|
+
self.showMessage(f"Debug [{stamp}] {message}", 6000)
|
259
|
+
logger.debug("[MEMINFO] %s", message)
|
266
260
|
return
|
267
|
-
|
268
|
-
# END Class GuiMainStatus
|