novelWriter 2.5b1__py3-none-any.whl → 2.5rc1__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.5b1.dist-info → novelWriter-2.5rc1.dist-info}/METADATA +1 -1
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/RECORD +61 -61
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +3 -3
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/project_pt_BR.json +74 -74
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/themes/cyberpunk_night.conf +1 -0
- novelwriter/assets/themes/default_dark.conf +1 -0
- novelwriter/assets/themes/default_light.conf +1 -0
- novelwriter/assets/themes/dracula.conf +1 -0
- novelwriter/assets/themes/solarized_dark.conf +1 -0
- novelwriter/assets/themes/solarized_light.conf +1 -0
- novelwriter/common.py +2 -3
- novelwriter/config.py +67 -15
- novelwriter/constants.py +8 -10
- novelwriter/core/buildsettings.py +5 -3
- novelwriter/core/coretools.py +3 -1
- novelwriter/core/docbuild.py +1 -0
- novelwriter/core/tohtml.py +69 -29
- novelwriter/core/tokenizer.py +83 -14
- novelwriter/core/toodt.py +48 -21
- novelwriter/core/toqdoc.py +25 -9
- novelwriter/dialogs/about.py +10 -15
- novelwriter/dialogs/docmerge.py +16 -16
- novelwriter/dialogs/docsplit.py +16 -16
- novelwriter/dialogs/editlabel.py +6 -8
- novelwriter/dialogs/preferences.py +94 -68
- novelwriter/dialogs/projectsettings.py +10 -10
- novelwriter/dialogs/quotes.py +9 -5
- novelwriter/dialogs/wordlist.py +6 -6
- novelwriter/enum.py +4 -5
- novelwriter/extensions/configlayout.py +23 -4
- novelwriter/extensions/modified.py +22 -3
- novelwriter/extensions/{circularprogress.py → progressbars.py} +26 -3
- novelwriter/extensions/statusled.py +28 -22
- novelwriter/gui/doceditor.py +20 -11
- novelwriter/gui/dochighlight.py +30 -39
- novelwriter/gui/docviewer.py +21 -14
- novelwriter/gui/mainmenu.py +11 -11
- novelwriter/gui/outline.py +3 -3
- novelwriter/gui/projtree.py +19 -28
- novelwriter/gui/search.py +10 -1
- novelwriter/gui/statusbar.py +25 -29
- novelwriter/gui/theme.py +3 -0
- novelwriter/guimain.py +91 -84
- novelwriter/shared.py +10 -8
- novelwriter/text/patterns.py +113 -0
- novelwriter/tools/dictionaries.py +2 -8
- novelwriter/tools/lipsum.py +8 -12
- novelwriter/tools/manusbuild.py +9 -9
- novelwriter/tools/manuscript.py +10 -5
- novelwriter/tools/manussettings.py +7 -3
- novelwriter/tools/noveldetails.py +10 -10
- novelwriter/tools/welcome.py +10 -10
- novelwriter/tools/writingstats.py +3 -3
- novelwriter/types.py +5 -2
- novelwriter/extensions/simpleprogress.py +0 -53
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/top_level.txt +0 -0
novelwriter/gui/docviewer.py
CHANGED
@@ -33,7 +33,7 @@ from enum import Enum
|
|
33
33
|
from PyQt5.QtCore import QPoint, Qt, QUrl, pyqtSignal, pyqtSlot
|
34
34
|
from PyQt5.QtGui import QCursor, QMouseEvent, QPalette, QResizeEvent, QTextCursor
|
35
35
|
from PyQt5.QtWidgets import (
|
36
|
-
QAction, QApplication, QFrame, QHBoxLayout,
|
36
|
+
QAction, QApplication, QFrame, QHBoxLayout, QMenu, QTextBrowser,
|
37
37
|
QToolButton, QWidget
|
38
38
|
)
|
39
39
|
|
@@ -42,6 +42,7 @@ from novelwriter.constants import nwHeaders, nwUnicode
|
|
42
42
|
from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
|
43
43
|
from novelwriter.enum import nwDocAction, nwDocMode, nwItemType
|
44
44
|
from novelwriter.error import logException
|
45
|
+
from novelwriter.extensions.configlayout import NColourLabel
|
45
46
|
from novelwriter.extensions.eventfilters import WheelEventFilter
|
46
47
|
from novelwriter.extensions.modified import NIconToolButton
|
47
48
|
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
@@ -92,6 +93,9 @@ class GuiDocViewer(QTextBrowser):
|
|
92
93
|
self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
93
94
|
self.customContextMenuRequested.connect(self._openContextMenu)
|
94
95
|
|
96
|
+
# Function Mapping
|
97
|
+
self.changeFocusState = self.docHeader.changeFocusState
|
98
|
+
|
95
99
|
self.initViewer()
|
96
100
|
|
97
101
|
logger.debug("Ready: GuiDocViewer")
|
@@ -165,6 +169,8 @@ class GuiDocViewer(QTextBrowser):
|
|
165
169
|
self._docTheme.keyword = SHARED.theme.colKey
|
166
170
|
self._docTheme.tag = SHARED.theme.colTag
|
167
171
|
self._docTheme.optional = SHARED.theme.colOpt
|
172
|
+
self._docTheme.dialog = SHARED.theme.colDialN
|
173
|
+
self._docTheme.altdialog = SHARED.theme.colDialA
|
168
174
|
|
169
175
|
# Set default text margins
|
170
176
|
self.document().setDocumentMargin(0)
|
@@ -201,6 +207,7 @@ class GuiDocViewer(QTextBrowser):
|
|
201
207
|
sPos = self.verticalScrollBar().value()
|
202
208
|
qDoc = ToQTextDocument(SHARED.project)
|
203
209
|
qDoc.setJustify(CONFIG.doJustify)
|
210
|
+
qDoc.setDialogueHighlight(True)
|
204
211
|
qDoc.initDocument(CONFIG.textFont, self._docTheme)
|
205
212
|
qDoc.setKeywords(True)
|
206
213
|
qDoc.setComments(CONFIG.viewComments)
|
@@ -245,9 +252,6 @@ class GuiDocViewer(QTextBrowser):
|
|
245
252
|
})
|
246
253
|
self.updateDocMargins()
|
247
254
|
|
248
|
-
# Since we change the content while it may still be rendering, we mark
|
249
|
-
# the document dirty again to make sure it's re-rendered properly.
|
250
|
-
self.redrawText()
|
251
255
|
QApplication.restoreOverrideCursor()
|
252
256
|
self.documentLoaded.emit(tHandle)
|
253
257
|
|
@@ -259,12 +263,6 @@ class GuiDocViewer(QTextBrowser):
|
|
259
263
|
self.loadText(self._docHandle, updateHistory=False)
|
260
264
|
return
|
261
265
|
|
262
|
-
def redrawText(self) -> None:
|
263
|
-
"""Redraw the text by marking the content as "dirty"."""
|
264
|
-
self.document().markContentsDirty(0, self.document().characterCount())
|
265
|
-
self.updateDocMargins()
|
266
|
-
return
|
267
|
-
|
268
266
|
def docAction(self, action: nwDocAction) -> bool:
|
269
267
|
"""Process document actions on the current document."""
|
270
268
|
logger.debug("Requesting action: '%s'", action.name)
|
@@ -284,6 +282,10 @@ class GuiDocViewer(QTextBrowser):
|
|
284
282
|
return False
|
285
283
|
return True
|
286
284
|
|
285
|
+
def anyFocus(self) -> bool:
|
286
|
+
"""Check if any widget or child widget has focus."""
|
287
|
+
return self.hasFocus() or self.isAncestorOf(QApplication.focusWidget())
|
288
|
+
|
287
289
|
def clearNavHistory(self) -> None:
|
288
290
|
"""Clear the navigation history."""
|
289
291
|
self.docHistory.clear()
|
@@ -370,8 +372,10 @@ class GuiDocViewer(QTextBrowser):
|
|
370
372
|
"""Process a clicked link in the document."""
|
371
373
|
if link := url.url():
|
372
374
|
logger.debug("Clicked link: '%s'", link)
|
373
|
-
if (bits := link.partition("_")) and bits[2]:
|
375
|
+
if (bits := link.partition("_")) and bits[0] == "#tag" and bits[2]:
|
374
376
|
self.loadDocumentTagRequest.emit(bits[2], nwDocMode.VIEW)
|
377
|
+
else:
|
378
|
+
self.navigateTo(link)
|
375
379
|
return
|
376
380
|
|
377
381
|
@pyqtSlot("QPoint")
|
@@ -601,9 +605,7 @@ class GuiDocViewHeader(QWidget):
|
|
601
605
|
self.setAutoFillBackground(True)
|
602
606
|
|
603
607
|
# Title Label
|
604
|
-
self.itemTitle =
|
605
|
-
self.itemTitle.setText("")
|
606
|
-
self.itemTitle.setIndent(0)
|
608
|
+
self.itemTitle = NColourLabel("", self, faded=SHARED.theme.fadedText)
|
607
609
|
self.itemTitle.setMargin(0)
|
608
610
|
self.itemTitle.setContentsMargins(0, 0, 0, 0)
|
609
611
|
self.itemTitle.setAutoFillBackground(True)
|
@@ -742,6 +744,11 @@ class GuiDocViewHeader(QWidget):
|
|
742
744
|
self.itemTitle.setPalette(palette)
|
743
745
|
return
|
744
746
|
|
747
|
+
def changeFocusState(self, state: bool) -> None:
|
748
|
+
"""Toggle focus state."""
|
749
|
+
self.itemTitle.setColorState(state)
|
750
|
+
return
|
751
|
+
|
745
752
|
def setHandle(self, tHandle: str) -> None:
|
746
753
|
"""Sets the document title from the handle, or alternatively,
|
747
754
|
set the whole document path.
|
novelwriter/gui/mainmenu.py
CHANGED
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import QAction, QMenuBar
|
|
35
35
|
from novelwriter import CONFIG, SHARED
|
36
36
|
from novelwriter.common import openExternalPath
|
37
37
|
from novelwriter.constants import nwConst, nwKeyWords, nwLabels, nwUnicode, trConst
|
38
|
-
from novelwriter.enum import nwDocAction, nwDocInsert,
|
38
|
+
from novelwriter.enum import nwDocAction, nwDocInsert, nwFocus, nwView
|
39
39
|
from novelwriter.extensions.eventfilters import StatusTipFilter
|
40
40
|
|
41
41
|
if TYPE_CHECKING: # pragma: no cover
|
@@ -54,7 +54,7 @@ class GuiMainMenu(QMenuBar):
|
|
54
54
|
requestDocInsert = pyqtSignal(nwDocInsert)
|
55
55
|
requestDocInsertText = pyqtSignal(str)
|
56
56
|
requestDocKeyWordInsert = pyqtSignal(str)
|
57
|
-
requestFocusChange = pyqtSignal(
|
57
|
+
requestFocusChange = pyqtSignal(nwFocus)
|
58
58
|
requestViewChange = pyqtSignal(nwView)
|
59
59
|
|
60
60
|
def __init__(self, mainGui: GuiMain) -> None:
|
@@ -167,7 +167,7 @@ class GuiMainMenu(QMenuBar):
|
|
167
167
|
# Project > Edit
|
168
168
|
self.aEditItem = self.projMenu.addAction(self.tr("Rename Item"))
|
169
169
|
self.aEditItem.setShortcut("F2")
|
170
|
-
self.aEditItem.triggered.connect(lambda: self.mainGui.
|
170
|
+
self.aEditItem.triggered.connect(lambda: self.mainGui.projView.renameTreeItem(None))
|
171
171
|
|
172
172
|
# Project > Delete
|
173
173
|
self.aDeleteItem = self.projMenu.addAction(self.tr("Delete Item"))
|
@@ -303,24 +303,24 @@ class GuiMainMenu(QMenuBar):
|
|
303
303
|
self.viewMenu = self.addMenu(self.tr("&View"))
|
304
304
|
|
305
305
|
# View > TreeView
|
306
|
-
self.aFocusTree = self.viewMenu.addAction(self.tr("Go to
|
306
|
+
self.aFocusTree = self.viewMenu.addAction(self.tr("Go to Tree View"))
|
307
307
|
self.aFocusTree.setShortcut("Ctrl+T")
|
308
308
|
self.aFocusTree.triggered.connect(
|
309
|
-
lambda: self.requestFocusChange.emit(
|
309
|
+
lambda: self.requestFocusChange.emit(nwFocus.TREE)
|
310
310
|
)
|
311
311
|
|
312
312
|
# View > Document Editor
|
313
|
-
self.
|
314
|
-
self.
|
315
|
-
self.
|
316
|
-
lambda: self.requestFocusChange.emit(
|
313
|
+
self.aFocusDocument = self.viewMenu.addAction(self.tr("Go to Document"))
|
314
|
+
self.aFocusDocument.setShortcut("Ctrl+E")
|
315
|
+
self.aFocusDocument.triggered.connect(
|
316
|
+
lambda: self.requestFocusChange.emit(nwFocus.DOCUMENT)
|
317
317
|
)
|
318
318
|
|
319
319
|
# View > Outline
|
320
320
|
self.aFocusOutline = self.viewMenu.addAction(self.tr("Go to Outline"))
|
321
321
|
self.aFocusOutline.setShortcut("Ctrl+Shift+T")
|
322
322
|
self.aFocusOutline.triggered.connect(
|
323
|
-
lambda: self.requestFocusChange.emit(
|
323
|
+
lambda: self.requestFocusChange.emit(nwFocus.OUTLINE)
|
324
324
|
)
|
325
325
|
|
326
326
|
# View > Separator
|
@@ -422,7 +422,7 @@ class GuiMainMenu(QMenuBar):
|
|
422
422
|
self.aInsMSApos = self.mInsQuotes.addAction(self.tr("Alternative Apostrophe"))
|
423
423
|
self.aInsMSApos.setShortcut("Ctrl+K, '")
|
424
424
|
self.aInsMSApos.triggered.connect(
|
425
|
-
lambda: self.requestDocInsertText.emit(nwUnicode.
|
425
|
+
lambda: self.requestDocInsertText.emit(nwUnicode.U_MAPOS)
|
426
426
|
)
|
427
427
|
|
428
428
|
# Insert > Symbols
|
novelwriter/gui/outline.py
CHANGED
@@ -215,7 +215,7 @@ class GuiOutlineToolBar(QToolBar):
|
|
215
215
|
|
216
216
|
# Novel Selector
|
217
217
|
self.novelLabel = NColourLabel(
|
218
|
-
self.tr("Outline of"),
|
218
|
+
self.tr("Outline of"), self, scale=NColourLabel.HEADER_SCALE, bold=True
|
219
219
|
)
|
220
220
|
self.novelLabel.setContentsMargins(0, 0, CONFIG.pxInt(12), 0)
|
221
221
|
|
@@ -523,12 +523,12 @@ class GuiOutlineTree(QTreeWidget):
|
|
523
523
|
@pyqtSlot()
|
524
524
|
def exportOutline(self) -> None:
|
525
525
|
"""Export the outline as a CSV file."""
|
526
|
-
path = CONFIG.lastPath() / f"{makeFileNameSafe(SHARED.project.data.name)}.csv"
|
526
|
+
path = CONFIG.lastPath("outline") / f"{makeFileNameSafe(SHARED.project.data.name)}.csv"
|
527
527
|
path, _ = QFileDialog.getSaveFileName(
|
528
528
|
self, self.tr("Save Outline As"), str(path), formatFileFilter(["*.csv", "*"])
|
529
529
|
)
|
530
530
|
if path:
|
531
|
-
CONFIG.setLastPath(path)
|
531
|
+
CONFIG.setLastPath("outline", path)
|
532
532
|
logger.info("Writing CSV file: %s", path)
|
533
533
|
cols = [col for col in self._treeOrder if not self._colHidden[col]]
|
534
534
|
order = [self._colIdx[col] for col in cols]
|
novelwriter/gui/projtree.py
CHANGED
@@ -34,9 +34,8 @@ from time import time
|
|
34
34
|
from PyQt5.QtCore import QPoint, Qt, QTimer, pyqtSignal, pyqtSlot
|
35
35
|
from PyQt5.QtGui import QDragEnterEvent, QDragMoveEvent, QDropEvent, QIcon, QMouseEvent, QPalette
|
36
36
|
from PyQt5.QtWidgets import (
|
37
|
-
QAbstractItemView, QAction,
|
38
|
-
|
39
|
-
QWidget
|
37
|
+
QAbstractItemView, QAction, QFrame, QHBoxLayout, QHeaderView, QLabel,
|
38
|
+
QMenu, QShortcut, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
40
39
|
)
|
41
40
|
|
42
41
|
from novelwriter import CONFIG, SHARED
|
@@ -998,7 +997,7 @@ class GuiProjectTree(QTreeWidget):
|
|
998
997
|
trItemP.takeChild(tIndex)
|
999
998
|
|
1000
999
|
for dHandle in reversed(self.getTreeFromHandle(tHandle)):
|
1001
|
-
SHARED.
|
1000
|
+
SHARED.closeEditor(dHandle)
|
1002
1001
|
SHARED.project.removeItem(dHandle)
|
1003
1002
|
self._treeMap.pop(dHandle, None)
|
1004
1003
|
|
@@ -1397,19 +1396,15 @@ class GuiProjectTree(QTreeWidget):
|
|
1397
1396
|
if not newFile:
|
1398
1397
|
itemList.remove(tHandle)
|
1399
1398
|
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
mrgData = dlgMerge.getData()
|
1406
|
-
mrgList = mrgData.get("finalItems", [])
|
1407
|
-
if not mrgList:
|
1399
|
+
data, status = GuiDocMerge.getData(SHARED.mainGui, tHandle, itemList)
|
1400
|
+
if status:
|
1401
|
+
items = data.get("finalItems", [])
|
1402
|
+
if not items:
|
1408
1403
|
SHARED.info(self.tr("No documents selected for merging."))
|
1409
1404
|
return False
|
1410
1405
|
|
1411
1406
|
# Save the open document first, in case it's part of merge
|
1412
|
-
SHARED.
|
1407
|
+
SHARED.saveEditor()
|
1413
1408
|
|
1414
1409
|
# Create merge object, and append docs
|
1415
1410
|
docMerger = DocMerger(SHARED.project)
|
@@ -1424,7 +1419,7 @@ class GuiProjectTree(QTreeWidget):
|
|
1424
1419
|
else:
|
1425
1420
|
return False
|
1426
1421
|
|
1427
|
-
for sHandle in
|
1422
|
+
for sHandle in items:
|
1428
1423
|
docMerger.appendText(sHandle, True, mLabel)
|
1429
1424
|
|
1430
1425
|
if not docMerger.writeTargetDoc():
|
@@ -1441,8 +1436,8 @@ class GuiProjectTree(QTreeWidget):
|
|
1441
1436
|
self.projView.openDocumentRequest.emit(mHandle, nwDocMode.EDIT, "", False)
|
1442
1437
|
self.projView.setSelectedHandle(mHandle, doScroll=True)
|
1443
1438
|
|
1444
|
-
if
|
1445
|
-
for sHandle in reversed(
|
1439
|
+
if data.get("moveToTrash", False):
|
1440
|
+
for sHandle in reversed(data.get("finalItems", [])):
|
1446
1441
|
trItem = self._getTreeItem(sHandle)
|
1447
1442
|
if isinstance(trItem, QTreeWidgetItem) and trItem.childCount() == 0:
|
1448
1443
|
self.moveItemToTrash(sHandle, askFirst=False, flush=False)
|
@@ -1468,16 +1463,11 @@ class GuiProjectTree(QTreeWidget):
|
|
1468
1463
|
logger.error("Only valid document items can be split")
|
1469
1464
|
return False
|
1470
1465
|
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
splitData, splitText = dlgSplit.getData()
|
1477
|
-
|
1478
|
-
headerList = splitData.get("headerList", [])
|
1479
|
-
intoFolder = splitData.get("intoFolder", False)
|
1480
|
-
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)
|
1481
1471
|
|
1482
1472
|
docSplit = DocSplitter(SHARED.project, tHandle)
|
1483
1473
|
if intoFolder:
|
@@ -1487,7 +1477,7 @@ class GuiProjectTree(QTreeWidget):
|
|
1487
1477
|
else:
|
1488
1478
|
docSplit.setParentItem(tItem.itemParent)
|
1489
1479
|
|
1490
|
-
docSplit.splitDocument(headerList,
|
1480
|
+
docSplit.splitDocument(headerList, text)
|
1491
1481
|
for writeOk, dHandle, nHandle in docSplit.writeDocuments(docHierarchy):
|
1492
1482
|
SHARED.project.index.reIndexHandle(dHandle)
|
1493
1483
|
self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True)
|
@@ -1498,7 +1488,7 @@ class GuiProjectTree(QTreeWidget):
|
|
1498
1488
|
info=docSplit.getError()
|
1499
1489
|
)
|
1500
1490
|
|
1501
|
-
if
|
1491
|
+
if data.get("moveToTrash", False):
|
1502
1492
|
self.moveItemToTrash(tHandle, askFirst=False, flush=True)
|
1503
1493
|
|
1504
1494
|
self.saveTreeOrder()
|
@@ -1815,6 +1805,7 @@ class _TreeContextMenu(QMenu):
|
|
1815
1805
|
|
1816
1806
|
def _itemHeader(self) -> None:
|
1817
1807
|
"""Check if there is a header that can be used for rename."""
|
1808
|
+
SHARED.saveEditor()
|
1818
1809
|
if hItem := SHARED.project.index.getItemHeading(self._handle, "T0001"):
|
1819
1810
|
action = self.addAction(self.tr("Rename to Heading"))
|
1820
1811
|
action.triggered.connect(
|
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
|
##
|
novelwriter/gui/statusbar.py
CHANGED
@@ -27,7 +27,6 @@ import logging
|
|
27
27
|
|
28
28
|
from datetime import datetime
|
29
29
|
from time import time
|
30
|
-
from typing import Literal
|
31
30
|
|
32
31
|
from PyQt5.QtCore import QLocale, pyqtSlot
|
33
32
|
from PyQt5.QtWidgets import QApplication, QLabel, QStatusBar, QWidget
|
@@ -35,6 +34,7 @@ from PyQt5.QtWidgets import QApplication, QLabel, QStatusBar, QWidget
|
|
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
40
|
logger = logging.getLogger(__name__)
|
@@ -51,10 +51,6 @@ class GuiMainStatus(QStatusBar):
|
|
51
51
|
self._userIdle = False
|
52
52
|
self._debugInfo = False
|
53
53
|
|
54
|
-
colNone = SHARED.theme.statNone
|
55
|
-
colSaved = SHARED.theme.statSaved
|
56
|
-
colUnsaved = SHARED.theme.statUnsaved
|
57
|
-
|
58
54
|
iPx = SHARED.theme.baseIconHeight
|
59
55
|
|
60
56
|
# Permanent Widgets
|
@@ -71,7 +67,7 @@ class GuiMainStatus(QStatusBar):
|
|
71
67
|
self.addPermanentWidget(self.langText)
|
72
68
|
|
73
69
|
# The Editor Status
|
74
|
-
self.docIcon = StatusLED(
|
70
|
+
self.docIcon = StatusLED(iPx, iPx, self)
|
75
71
|
self.docText = QLabel(self.tr("Editor"), self)
|
76
72
|
self.docIcon.setContentsMargins(0, 0, 0, 0)
|
77
73
|
self.docText.setContentsMargins(0, 0, xM, 0)
|
@@ -79,7 +75,7 @@ class GuiMainStatus(QStatusBar):
|
|
79
75
|
self.addPermanentWidget(self.docText)
|
80
76
|
|
81
77
|
# The Project Status
|
82
|
-
self.projIcon = StatusLED(
|
78
|
+
self.projIcon = StatusLED(iPx, iPx, self)
|
83
79
|
self.projText = QLabel(self.tr("Project"), self)
|
84
80
|
self.projIcon.setContentsMargins(0, 0, 0, 0)
|
85
81
|
self.projText.setContentsMargins(0, 0, xM, 0)
|
@@ -120,8 +116,8 @@ class GuiMainStatus(QStatusBar):
|
|
120
116
|
self.setRefTime(-1.0)
|
121
117
|
self.setLanguage(*SHARED.spelling.describeDict())
|
122
118
|
self.setProjectStats(0, 0)
|
123
|
-
self.setProjectStatus(
|
124
|
-
self.setDocumentStatus(
|
119
|
+
self.setProjectStatus(nwTrinary.NEUTRAL)
|
120
|
+
self.setDocumentStatus(nwTrinary.NEUTRAL)
|
125
121
|
self.updateTime()
|
126
122
|
return
|
127
123
|
|
@@ -133,6 +129,13 @@ class GuiMainStatus(QStatusBar):
|
|
133
129
|
self.timePixmap = SHARED.theme.getPixmap("status_time", (iPx, iPx))
|
134
130
|
self.idlePixmap = SHARED.theme.getPixmap("status_idle", (iPx, iPx))
|
135
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
|
+
|
136
139
|
return
|
137
140
|
|
138
141
|
##
|
@@ -144,12 +147,12 @@ class GuiMainStatus(QStatusBar):
|
|
144
147
|
self._refTime = refTime
|
145
148
|
return
|
146
149
|
|
147
|
-
def setProjectStatus(self, state:
|
150
|
+
def setProjectStatus(self, state: nwTrinary) -> None:
|
148
151
|
"""Set the project status colour icon."""
|
149
152
|
self.projIcon.setState(state)
|
150
153
|
return
|
151
154
|
|
152
|
-
def setDocumentStatus(self, state:
|
155
|
+
def setDocumentStatus(self, state: nwTrinary) -> None:
|
153
156
|
"""Set the document status colour icon."""
|
154
157
|
self.docIcon.setState(state)
|
155
158
|
return
|
@@ -212,13 +215,13 @@ class GuiMainStatus(QStatusBar):
|
|
212
215
|
@pyqtSlot(bool)
|
213
216
|
def updateProjectStatus(self, status: bool) -> None:
|
214
217
|
"""Update the project status."""
|
215
|
-
self.setProjectStatus(
|
218
|
+
self.setProjectStatus(nwTrinary.NEGATIVE if status else nwTrinary.POSITIVE)
|
216
219
|
return
|
217
220
|
|
218
221
|
@pyqtSlot(bool)
|
219
222
|
def updateDocumentStatus(self, status: bool) -> None:
|
220
223
|
"""Update the document status."""
|
221
|
-
self.setDocumentStatus(
|
224
|
+
self.setDocumentStatus(nwTrinary.NEGATIVE if status else nwTrinary.POSITIVE)
|
222
225
|
return
|
223
226
|
|
224
227
|
##
|
@@ -236,9 +239,7 @@ class GuiMainStatus(QStatusBar):
|
|
236
239
|
"""
|
237
240
|
import tracemalloc
|
238
241
|
|
239
|
-
|
240
|
-
|
241
|
-
widgets = QApplication.allWidgets()
|
242
|
+
count = len(QApplication.allWidgets())
|
242
243
|
if not self._debugInfo:
|
243
244
|
if tracemalloc.is_tracing():
|
244
245
|
self._traceMallocRef = "Total"
|
@@ -246,19 +247,14 @@ class GuiMainStatus(QStatusBar):
|
|
246
247
|
self._traceMallocRef = "Relative"
|
247
248
|
tracemalloc.start()
|
248
249
|
self._debugInfo = True
|
249
|
-
self._wCounts = Counter([type(x).__name__ for x in widgets])
|
250
|
-
|
251
|
-
if hasattr(self, "_wCounts"):
|
252
|
-
diff = Counter([type(x).__name__ for x in widgets]) - self._wCounts
|
253
|
-
for name, count in diff.items():
|
254
|
-
logger.debug("Widget '%s': +%d", name, count)
|
255
250
|
|
256
|
-
|
251
|
+
current, peak = tracemalloc.get_traced_memory()
|
257
252
|
stamp = datetime.now().strftime("%H:%M:%S")
|
258
|
-
|
259
|
-
f"
|
260
|
-
f"
|
261
|
-
f"
|
262
|
-
|
263
|
-
|
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)
|
264
260
|
return
|
novelwriter/gui/theme.py
CHANGED
@@ -75,6 +75,7 @@ class GuiTheme:
|
|
75
75
|
self.statUnsaved = QColor(0, 0, 0)
|
76
76
|
self.statSaved = QColor(0, 0, 0)
|
77
77
|
self.helpText = QColor(0, 0, 0)
|
78
|
+
self.fadedText = QColor(0, 0, 0)
|
78
79
|
self.errorText = QColor(255, 0, 0)
|
79
80
|
|
80
81
|
# Loaded Syntax Settings
|
@@ -263,6 +264,7 @@ class GuiTheme:
|
|
263
264
|
sec = "GUI"
|
264
265
|
if parser.has_section(sec):
|
265
266
|
self.helpText = self._parseColour(parser, sec, "helptext")
|
267
|
+
self.fadedText = self._parseColour(parser, sec, "fadedtext")
|
266
268
|
self.errorText = self._parseColour(parser, sec, "errortext")
|
267
269
|
self.statNone = self._parseColour(parser, sec, "statusnone")
|
268
270
|
self.statUnsaved = self._parseColour(parser, sec, "statusunsaved")
|
@@ -405,6 +407,7 @@ class GuiTheme:
|
|
405
407
|
self.statUnsaved = QColor(200, 15, 39)
|
406
408
|
self.statSaved = QColor(2, 133, 37)
|
407
409
|
self.helpText = QColor(0, 0, 0)
|
410
|
+
self.fadedText = QColor(128, 128, 128)
|
408
411
|
self.errorText = QColor(255, 0, 0)
|
409
412
|
return
|
410
413
|
|