novelWriter 2.6b1__py3-none-any.whl → 2.6rc1__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.6rc1.dist-info}/METADATA +4 -4
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/RECORD +114 -98
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +50 -11
- 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_ru_RU.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.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 +38 -3
- novelwriter/config.py +19 -13
- novelwriter/constants.py +60 -45
- novelwriter/core/buildsettings.py +1 -1
- novelwriter/core/coretools.py +112 -126
- novelwriter/core/docbuild.py +4 -3
- novelwriter/core/document.py +1 -1
- novelwriter/core/index.py +10 -20
- novelwriter/core/item.py +40 -7
- novelwriter/core/itemmodel.py +518 -0
- novelwriter/core/options.py +1 -1
- novelwriter/core/project.py +68 -90
- novelwriter/core/projectdata.py +8 -2
- novelwriter/core/projectxml.py +1 -1
- novelwriter/core/sessions.py +1 -1
- novelwriter/core/spellcheck.py +1 -1
- novelwriter/core/status.py +24 -8
- novelwriter/core/storage.py +1 -1
- novelwriter/core/tree.py +269 -288
- novelwriter/dialogs/about.py +1 -1
- novelwriter/dialogs/docmerge.py +8 -18
- novelwriter/dialogs/docsplit.py +1 -1
- novelwriter/dialogs/editlabel.py +1 -1
- novelwriter/dialogs/preferences.py +4 -4
- novelwriter/dialogs/projectsettings.py +148 -98
- novelwriter/dialogs/quotes.py +1 -1
- novelwriter/dialogs/wordlist.py +11 -10
- novelwriter/enum.py +8 -1
- novelwriter/error.py +2 -2
- novelwriter/extensions/configlayout.py +7 -5
- novelwriter/extensions/eventfilters.py +1 -1
- novelwriter/extensions/modified.py +17 -5
- novelwriter/extensions/novelselector.py +1 -1
- novelwriter/extensions/pagedsidebar.py +4 -4
- novelwriter/extensions/progressbars.py +4 -4
- novelwriter/extensions/statusled.py +3 -3
- novelwriter/extensions/switch.py +3 -3
- novelwriter/extensions/switchbox.py +1 -1
- novelwriter/extensions/versioninfo.py +1 -1
- novelwriter/formats/shared.py +1 -1
- novelwriter/formats/todocx.py +35 -39
- novelwriter/formats/tohtml.py +15 -16
- novelwriter/formats/tokenizer.py +26 -22
- novelwriter/formats/tomarkdown.py +1 -1
- novelwriter/formats/toodt.py +54 -125
- novelwriter/formats/toqdoc.py +93 -45
- novelwriter/formats/toraw.py +1 -1
- novelwriter/gui/doceditor.py +233 -220
- novelwriter/gui/dochighlight.py +1 -1
- novelwriter/gui/docviewer.py +39 -10
- novelwriter/gui/docviewerpanel.py +15 -23
- novelwriter/gui/editordocument.py +1 -1
- novelwriter/gui/itemdetails.py +20 -27
- novelwriter/gui/mainmenu.py +14 -9
- novelwriter/gui/noveltree.py +13 -13
- novelwriter/gui/outline.py +18 -20
- novelwriter/gui/projtree.py +545 -1201
- novelwriter/gui/search.py +11 -19
- novelwriter/gui/sidebar.py +1 -1
- novelwriter/gui/statusbar.py +20 -3
- novelwriter/gui/theme.py +8 -4
- novelwriter/guimain.py +60 -48
- novelwriter/shared.py +53 -24
- novelwriter/text/counting.py +1 -1
- novelwriter/text/patterns.py +18 -6
- novelwriter/tools/dictionaries.py +1 -1
- novelwriter/tools/lipsum.py +1 -1
- novelwriter/tools/manusbuild.py +14 -12
- novelwriter/tools/manuscript.py +7 -7
- novelwriter/tools/manussettings.py +43 -53
- novelwriter/tools/noveldetails.py +1 -1
- novelwriter/tools/welcome.py +1 -1
- novelwriter/tools/writingstats.py +1 -1
- novelwriter/types.py +9 -3
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/top_level.txt +0 -0
novelwriter/dialogs/about.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2020-05-21 [0.5.2] GuiAbout
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright
|
9
|
+
Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
novelwriter/dialogs/docmerge.py
CHANGED
@@ -7,7 +7,7 @@ Created: 2020-01-23 [0.4.3] GuiDocMerge
|
|
7
7
|
Rewritten: 2022-10-06 [2.0rc1] GuiDocMerge
|
8
8
|
|
9
9
|
This file is a part of novelWriter
|
10
|
-
Copyright
|
10
|
+
Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
|
11
11
|
|
12
12
|
This program is free software: you can redistribute it and/or modify
|
13
13
|
it under the terms of the GNU General Public License as published by
|
@@ -162,23 +162,13 @@ class GuiDocMerge(NDialog):
|
|
162
162
|
self._data = {}
|
163
163
|
self._data["sHandle"] = sHandle
|
164
164
|
self._data["origItems"] = itemList
|
165
|
-
|
166
165
|
self.listBox.clear()
|
167
166
|
for tHandle in itemList:
|
168
|
-
nwItem
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
newItem = QListWidgetItem()
|
177
|
-
newItem.setIcon(itemIcon)
|
178
|
-
newItem.setText(nwItem.itemName)
|
179
|
-
newItem.setData(self.D_HANDLE, tHandle)
|
180
|
-
newItem.setCheckState(Qt.CheckState.Checked)
|
181
|
-
|
182
|
-
self.listBox.addItem(newItem)
|
183
|
-
|
167
|
+
if (nwItem := SHARED.project.tree[tHandle]) and nwItem.isFileType():
|
168
|
+
item = QListWidgetItem()
|
169
|
+
item.setIcon(nwItem.getMainIcon())
|
170
|
+
item.setText(nwItem.itemName)
|
171
|
+
item.setData(self.D_HANDLE, tHandle)
|
172
|
+
item.setCheckState(Qt.CheckState.Checked)
|
173
|
+
self.listBox.addItem(item)
|
184
174
|
return
|
novelwriter/dialogs/docsplit.py
CHANGED
@@ -7,7 +7,7 @@ Created: 2020-02-01 [0.4.3] GuiDocSplit
|
|
7
7
|
Rewritten: 2022-10-12 [2.0rc1] GuiDocSplit
|
8
8
|
|
9
9
|
This file is a part of novelWriter
|
10
|
-
Copyright
|
10
|
+
Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
|
11
11
|
|
12
12
|
This program is free software: you can redistribute it and/or modify
|
13
13
|
it under the terms of the GNU General Public License as published by
|
novelwriter/dialogs/editlabel.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2022-06-11 [2.0rc1] GuiEditLabel
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright
|
9
|
+
Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -7,7 +7,7 @@ Created: 2019-06-10 [0.1.5] GuiPreferences
|
|
7
7
|
Rewritten: 2024-01-08 [2.3b1] GuiPreferences
|
8
8
|
|
9
9
|
This file is a part of novelWriter
|
10
|
-
Copyright
|
10
|
+
Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors
|
11
11
|
|
12
12
|
This program is free software: you can redistribute it and/or modify
|
13
13
|
it under the terms of the GNU General Public License as published by
|
@@ -574,7 +574,7 @@ class GuiPreferences(NDialog):
|
|
574
574
|
self.dialogLine.setText(CONFIG.dialogLine)
|
575
575
|
self.mainForm.addRow(
|
576
576
|
self.tr("Dialogue line symbols"), self.dialogLine,
|
577
|
-
self.tr("Lines starting with these symbols are
|
577
|
+
self.tr("Lines starting with any of these symbols are dialogue.")
|
578
578
|
)
|
579
579
|
|
580
580
|
self.narratorBreak = QLineEdit(self)
|
@@ -583,8 +583,8 @@ class GuiPreferences(NDialog):
|
|
583
583
|
self.narratorBreak.setAlignment(QtAlignCenter)
|
584
584
|
self.narratorBreak.setText(CONFIG.narratorBreak)
|
585
585
|
self.mainForm.addRow(
|
586
|
-
self.tr("
|
587
|
-
self.tr("Symbol to indicate
|
586
|
+
self.tr("Narrator break symbol"), self.narratorBreak,
|
587
|
+
self.tr("Symbol to indicate a narrator break in dialogue.")
|
588
588
|
)
|
589
589
|
|
590
590
|
self.narratorDialog = QLineEdit(self)
|
@@ -7,7 +7,7 @@ Created: 2018-09-29 [0.0.1] GuiProjectSettings
|
|
7
7
|
Rewritten: 2024-01-26 [2.3b1] GuiProjectSettings
|
8
8
|
|
9
9
|
This file is a part of novelWriter
|
10
|
-
Copyright 2018
|
10
|
+
Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors
|
11
11
|
|
12
12
|
This program is free software: you can redistribute it and/or modify
|
13
13
|
it under the terms of the GNU General Public License as published by
|
@@ -24,18 +24,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
24
24
|
"""
|
25
25
|
from __future__ import annotations
|
26
26
|
|
27
|
+
import csv
|
27
28
|
import logging
|
28
29
|
|
30
|
+
from pathlib import Path
|
31
|
+
|
29
32
|
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
30
33
|
from PyQt5.QtGui import QCloseEvent, QColor
|
31
34
|
from PyQt5.QtWidgets import (
|
32
35
|
QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
|
33
|
-
QHBoxLayout, QLineEdit, QMenu, QStackedWidget,
|
34
|
-
QTreeWidgetItem, QVBoxLayout, QWidget
|
36
|
+
QFileDialog, QGridLayout, QHBoxLayout, QLineEdit, QMenu, QStackedWidget,
|
37
|
+
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
35
38
|
)
|
36
39
|
|
37
40
|
from novelwriter import CONFIG, SHARED
|
38
|
-
from novelwriter.common import qtLambda, simplified
|
41
|
+
from novelwriter.common import formatFileFilter, qtLambda, simplified
|
39
42
|
from novelwriter.constants import nwLabels, trConst
|
40
43
|
from novelwriter.core.status import NWStatus, StatusEntry
|
41
44
|
from novelwriter.enum import nwStatusShape
|
@@ -185,11 +188,11 @@ class GuiProjectSettings(NDialog):
|
|
185
188
|
|
186
189
|
if self.statusPage.changed:
|
187
190
|
logger.debug("Updating status labels")
|
188
|
-
project.
|
191
|
+
project.updateStatus("s", self.statusPage.getNewList())
|
189
192
|
|
190
193
|
if self.importPage.changed:
|
191
194
|
logger.debug("Updating importance labels")
|
192
|
-
project.
|
195
|
+
project.updateStatus("i", self.importPage.getNewList())
|
193
196
|
|
194
197
|
if self.replacePage.changed:
|
195
198
|
logger.debug("Updating auto-replace settings")
|
@@ -310,11 +313,13 @@ class _StatusPage(NFixedPage):
|
|
310
313
|
super().__init__(parent=parent)
|
311
314
|
|
312
315
|
if isStatus:
|
313
|
-
|
316
|
+
self._kind = self.tr("Status")
|
317
|
+
self._store = SHARED.project.data.itemStatus
|
314
318
|
pageLabel = self.tr("Novel Document Status Levels")
|
315
319
|
colSetting = "statusColW"
|
316
320
|
else:
|
317
|
-
|
321
|
+
self._kind = self.tr("Importance")
|
322
|
+
self._store = SHARED.project.data.itemImport
|
318
323
|
pageLabel = self.tr("Project Note Importance Levels")
|
319
324
|
colSetting = "importColW"
|
320
325
|
|
@@ -352,29 +357,42 @@ class _StatusPage(NFixedPage):
|
|
352
357
|
self.listBox.setIndentation(0)
|
353
358
|
self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
354
359
|
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
355
|
-
self.listBox.itemSelectionChanged.connect(self.
|
360
|
+
self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)
|
356
361
|
|
357
|
-
for key, entry in
|
362
|
+
for key, entry in self._store.iterItems():
|
358
363
|
self._addItem(key, StatusEntry.duplicate(entry))
|
359
364
|
|
360
365
|
# List Controls
|
361
366
|
self.addButton = NIconToolButton(self, iSz, "add")
|
362
|
-
self.addButton.
|
367
|
+
self.addButton.setToolTip(self.tr("Add Label"))
|
368
|
+
self.addButton.clicked.connect(self._onItemCreate)
|
363
369
|
|
364
370
|
self.delButton = NIconToolButton(self, iSz, "remove")
|
365
|
-
self.delButton.
|
371
|
+
self.delButton.setToolTip(self.tr("Delete Label"))
|
372
|
+
self.delButton.clicked.connect(self._onItemDelete)
|
366
373
|
|
367
374
|
self.upButton = NIconToolButton(self, iSz, "up")
|
375
|
+
self.upButton.setToolTip(self.tr("Move Up"))
|
368
376
|
self.upButton.clicked.connect(qtLambda(self._moveItem, -1))
|
369
377
|
|
370
|
-
self.
|
371
|
-
self.
|
378
|
+
self.downButton = NIconToolButton(self, iSz, "down")
|
379
|
+
self.downButton.setToolTip(self.tr("Move Down"))
|
380
|
+
self.downButton.clicked.connect(qtLambda(self._moveItem, 1))
|
381
|
+
|
382
|
+
self.importButton = NIconToolButton(self, iSz, "import")
|
383
|
+
self.importButton.setToolTip(self.tr("Import Labels"))
|
384
|
+
self.importButton.clicked.connect(self._importLabels)
|
385
|
+
|
386
|
+
self.exportButton = NIconToolButton(self, iSz, "export")
|
387
|
+
self.exportButton.setToolTip(self.tr("Export Labels"))
|
388
|
+
self.exportButton.clicked.connect(self._exportLabels)
|
372
389
|
|
373
390
|
# Edit Form
|
374
|
-
self.
|
375
|
-
self.
|
376
|
-
self.
|
377
|
-
self.
|
391
|
+
self.labelText = QLineEdit(self)
|
392
|
+
self.labelText.setMaxLength(40)
|
393
|
+
self.labelText.setPlaceholderText(self.tr("Select item to edit"))
|
394
|
+
self.labelText.setEnabled(False)
|
395
|
+
self.labelText.textEdited.connect(self._onNameEdit)
|
378
396
|
|
379
397
|
buttonStyle = (
|
380
398
|
f"QToolButton {{padding: 0 {bPd}px;}} "
|
@@ -386,13 +404,13 @@ class _StatusPage(NFixedPage):
|
|
386
404
|
self.colorButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
|
387
405
|
self.colorButton.setStyleSheet(buttonStyle)
|
388
406
|
self.colorButton.setEnabled(False)
|
389
|
-
self.colorButton.clicked.connect(self.
|
407
|
+
self.colorButton.clicked.connect(self._onColourSelect)
|
390
408
|
|
391
409
|
def buildMenu(menu: QMenu, items: dict[nwStatusShape, str]) -> None:
|
392
410
|
for shape, label in items.items():
|
393
411
|
icon = NWStatus.createIcon(self._iPx, iColor, shape)
|
394
412
|
action = menu.addAction(icon, trConst(label))
|
395
|
-
action.triggered.connect(
|
413
|
+
action.triggered.connect(qtLambda(self._selectShape, shape))
|
396
414
|
self._icons[shape] = icon
|
397
415
|
|
398
416
|
self.shapeMenu = QMenu(self)
|
@@ -408,33 +426,27 @@ class _StatusPage(NFixedPage):
|
|
408
426
|
self.shapeButton.setStyleSheet(buttonStyle)
|
409
427
|
self.shapeButton.setEnabled(False)
|
410
428
|
|
411
|
-
self.applyButton = QToolButton(self)
|
412
|
-
self.applyButton.setText(self.tr("Apply"))
|
413
|
-
self.applyButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
|
414
|
-
self.applyButton.setEnabled(False)
|
415
|
-
self.applyButton.clicked.connect(self._applyChanges)
|
416
|
-
|
417
429
|
# Assemble
|
418
430
|
self.listControls = QVBoxLayout()
|
419
431
|
self.listControls.addWidget(self.addButton)
|
420
432
|
self.listControls.addWidget(self.delButton)
|
421
433
|
self.listControls.addWidget(self.upButton)
|
422
|
-
self.listControls.addWidget(self.
|
434
|
+
self.listControls.addWidget(self.downButton)
|
423
435
|
self.listControls.addStretch(1)
|
436
|
+
self.listControls.addWidget(self.importButton)
|
437
|
+
self.listControls.addWidget(self.exportButton)
|
424
438
|
|
425
439
|
self.editBox = QHBoxLayout()
|
426
|
-
self.editBox.addWidget(self.
|
440
|
+
self.editBox.addWidget(self.labelText, 1)
|
427
441
|
self.editBox.addWidget(self.colorButton, 0)
|
428
442
|
self.editBox.addWidget(self.shapeButton, 0)
|
429
|
-
self.editBox.addWidget(self.applyButton, 0)
|
430
|
-
|
431
|
-
self.mainBox = QVBoxLayout()
|
432
|
-
self.mainBox.addWidget(self.listBox, 1)
|
433
|
-
self.mainBox.addLayout(self.editBox, 0)
|
434
443
|
|
435
|
-
self.innerBox =
|
436
|
-
self.innerBox.
|
437
|
-
self.innerBox.addLayout(self.listControls, 0)
|
444
|
+
self.innerBox = QGridLayout()
|
445
|
+
self.innerBox.addWidget(self.listBox, 0, 0)
|
446
|
+
self.innerBox.addLayout(self.listControls, 0, 1)
|
447
|
+
self.innerBox.addLayout(self.editBox, 1, 0)
|
448
|
+
self.innerBox.setRowStretch(0, 1)
|
449
|
+
self.innerBox.setColumnStretch(0, 1)
|
438
450
|
|
439
451
|
self.outerBox = QVBoxLayout()
|
440
452
|
self.outerBox.addWidget(self.pageTitle, 0)
|
@@ -474,16 +486,28 @@ class _StatusPage(NFixedPage):
|
|
474
486
|
# Private Slots
|
475
487
|
##
|
476
488
|
|
489
|
+
@pyqtSlot(str)
|
490
|
+
def _onNameEdit(self, text: str) -> None:
|
491
|
+
"""Update the status label text."""
|
492
|
+
if item := self._getSelectedItem():
|
493
|
+
name = simplified(text)
|
494
|
+
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
|
495
|
+
entry.name = name
|
496
|
+
item.setText(self.C_LABEL, name)
|
497
|
+
self._changed = True
|
498
|
+
return
|
499
|
+
|
477
500
|
@pyqtSlot()
|
478
|
-
def
|
501
|
+
def _onColourSelect(self) -> None:
|
479
502
|
"""Open a dialog to select the status icon colour."""
|
480
503
|
if (color := QColorDialog.getColor(self._color, self, self.trSelColor)).isValid():
|
481
504
|
self._color = color
|
482
505
|
self._setButtonIcons()
|
506
|
+
self._updateIcon()
|
483
507
|
return
|
484
508
|
|
485
509
|
@pyqtSlot()
|
486
|
-
def
|
510
|
+
def _onItemCreate(self) -> None:
|
487
511
|
"""Create a new status item."""
|
488
512
|
color = QColor(100, 100, 100)
|
489
513
|
shape = nwStatusShape.SQUARE
|
@@ -493,7 +517,7 @@ class _StatusPage(NFixedPage):
|
|
493
517
|
return
|
494
518
|
|
495
519
|
@pyqtSlot()
|
496
|
-
def
|
520
|
+
def _onItemDelete(self) -> None:
|
497
521
|
"""Delete a status item."""
|
498
522
|
if item := self._getSelectedItem():
|
499
523
|
iRow = self.listBox.indexOfTopLevelItem(item)
|
@@ -506,28 +530,7 @@ class _StatusPage(NFixedPage):
|
|
506
530
|
return
|
507
531
|
|
508
532
|
@pyqtSlot()
|
509
|
-
def
|
510
|
-
"""Save changes made to a status item."""
|
511
|
-
if item := self._getSelectedItem():
|
512
|
-
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
|
513
|
-
|
514
|
-
name = simplified(self.editName.text())
|
515
|
-
icon = NWStatus.createIcon(self._iPx, self._color, self._shape)
|
516
|
-
|
517
|
-
entry.name = name
|
518
|
-
entry.color = self._color
|
519
|
-
entry.shape = self._shape
|
520
|
-
entry.icon = icon
|
521
|
-
|
522
|
-
item.setText(self.C_LABEL, name)
|
523
|
-
item.setIcon(self.C_LABEL, icon)
|
524
|
-
|
525
|
-
self._changed = True
|
526
|
-
|
527
|
-
return
|
528
|
-
|
529
|
-
@pyqtSlot()
|
530
|
-
def _selectionChanged(self) -> None:
|
533
|
+
def _onSelectionChanged(self) -> None:
|
531
534
|
"""Extract the info of a selected item and populate the settings
|
532
535
|
boxes and button. If no item is selected, clear the form.
|
533
536
|
"""
|
@@ -537,25 +540,59 @@ class _StatusPage(NFixedPage):
|
|
537
540
|
self._shape = entry.shape
|
538
541
|
self._setButtonIcons()
|
539
542
|
|
540
|
-
self.
|
541
|
-
self.
|
542
|
-
self.
|
543
|
+
self.labelText.setText(entry.name)
|
544
|
+
self.labelText.selectAll()
|
545
|
+
self.labelText.setFocus()
|
543
546
|
|
544
|
-
self.
|
547
|
+
self.labelText.setEnabled(True)
|
545
548
|
self.colorButton.setEnabled(True)
|
546
549
|
self.shapeButton.setEnabled(True)
|
547
|
-
self.applyButton.setEnabled(True)
|
548
|
-
|
549
550
|
else:
|
550
551
|
self._color = QColor(100, 100, 100)
|
551
552
|
self._shape = nwStatusShape.SQUARE
|
552
553
|
self._setButtonIcons()
|
553
|
-
self.
|
554
|
+
self.labelText.setText("")
|
554
555
|
|
555
|
-
self.
|
556
|
+
self.labelText.setEnabled(False)
|
556
557
|
self.colorButton.setEnabled(False)
|
557
558
|
self.shapeButton.setEnabled(False)
|
558
|
-
|
559
|
+
return
|
560
|
+
|
561
|
+
@pyqtSlot()
|
562
|
+
def _importLabels(self) -> None:
|
563
|
+
"""Import labels from file."""
|
564
|
+
if path := QFileDialog.getOpenFileName(
|
565
|
+
self, self.tr("Import File"),
|
566
|
+
str(CONFIG.homePath()), filter=formatFileFilter(["*.csv", "*"]),
|
567
|
+
)[0]:
|
568
|
+
try:
|
569
|
+
with open(path, mode="r", encoding="utf-8") as fo:
|
570
|
+
for row in csv.reader(fo):
|
571
|
+
if entry := self._store.fromRaw(row):
|
572
|
+
self._addItem(None, entry)
|
573
|
+
self._changed = True
|
574
|
+
except Exception as exc:
|
575
|
+
SHARED.error("Could not read file.", exc=exc)
|
576
|
+
return
|
577
|
+
return
|
578
|
+
|
579
|
+
@pyqtSlot()
|
580
|
+
def _exportLabels(self) -> None:
|
581
|
+
"""Export labels to file."""
|
582
|
+
name = f"{SHARED.project.data.fileSafeName} - {self._kind}.csv"
|
583
|
+
if path := QFileDialog.getSaveFileName(
|
584
|
+
self, self.tr("Export File"), str(CONFIG.homePath() / name),
|
585
|
+
)[0]:
|
586
|
+
try:
|
587
|
+
path = Path(path).with_suffix(".csv")
|
588
|
+
with open(path, mode="w", encoding="utf-8") as fo:
|
589
|
+
writer = csv.writer(fo)
|
590
|
+
for n in range(self.listBox.topLevelItemCount()):
|
591
|
+
if item := self.listBox.topLevelItem(n):
|
592
|
+
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
|
593
|
+
writer.writerow([entry.shape.name, entry.color.name(), entry.name])
|
594
|
+
except Exception as exc:
|
595
|
+
SHARED.error("Could not write file.", exc=exc)
|
559
596
|
return
|
560
597
|
|
561
598
|
##
|
@@ -566,6 +603,19 @@ class _StatusPage(NFixedPage):
|
|
566
603
|
"""Set the current shape."""
|
567
604
|
self._shape = shape
|
568
605
|
self._setButtonIcons()
|
606
|
+
self._updateIcon()
|
607
|
+
return
|
608
|
+
|
609
|
+
def _updateIcon(self) -> None:
|
610
|
+
"""Apply changes made to a status icon."""
|
611
|
+
if item := self._getSelectedItem():
|
612
|
+
icon = NWStatus.createIcon(self._iPx, self._color, self._shape)
|
613
|
+
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
|
614
|
+
entry.color = self._color
|
615
|
+
entry.shape = self._shape
|
616
|
+
entry.icon = icon
|
617
|
+
item.setIcon(self.C_LABEL, icon)
|
618
|
+
self._changed = True
|
569
619
|
return
|
570
620
|
|
571
621
|
def _addItem(self, key: str | None, entry: StatusEntry) -> None:
|
@@ -644,7 +694,7 @@ class _ReplacePage(NFixedPage):
|
|
644
694
|
self.listBox.setIndentation(0)
|
645
695
|
self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
646
696
|
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
647
|
-
self.listBox.itemSelectionChanged.connect(self.
|
697
|
+
self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)
|
648
698
|
|
649
699
|
for aKey, aVal in SHARED.project.data.autoReplace.items():
|
650
700
|
newItem = QTreeWidgetItem(["<%s>" % aKey, aVal])
|
@@ -655,25 +705,22 @@ class _ReplacePage(NFixedPage):
|
|
655
705
|
|
656
706
|
# List Controls
|
657
707
|
self.addButton = NIconToolButton(self, iSz, "add")
|
658
|
-
self.addButton.clicked.connect(self.
|
708
|
+
self.addButton.clicked.connect(self._onEntryCreated)
|
659
709
|
|
660
710
|
self.delButton = NIconToolButton(self, iSz, "remove")
|
661
|
-
self.delButton.clicked.connect(self.
|
711
|
+
self.delButton.clicked.connect(self._onEntryDeleted)
|
662
712
|
|
663
713
|
# Edit Form
|
664
714
|
self.editKey = QLineEdit(self)
|
665
715
|
self.editKey.setPlaceholderText(self.tr("Select item to edit"))
|
666
716
|
self.editKey.setEnabled(False)
|
667
717
|
self.editKey.setMaxLength(40)
|
718
|
+
self.editKey.textEdited.connect(self._onKeyEdit)
|
668
719
|
|
669
720
|
self.editValue = QLineEdit(self)
|
670
721
|
self.editValue.setEnabled(False)
|
671
|
-
self.editValue.setMaxLength(
|
672
|
-
|
673
|
-
self.applyButton = QToolButton(self)
|
674
|
-
self.applyButton.setText(self.tr("Apply"))
|
675
|
-
self.applyButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
|
676
|
-
self.applyButton.clicked.connect(self._applyChanges)
|
722
|
+
self.editValue.setMaxLength(250)
|
723
|
+
self.editValue.textEdited.connect(self._onValueEdit)
|
677
724
|
|
678
725
|
# Assemble
|
679
726
|
self.listControls = QVBoxLayout()
|
@@ -684,7 +731,6 @@ class _ReplacePage(NFixedPage):
|
|
684
731
|
self.editBox = QHBoxLayout()
|
685
732
|
self.editBox.addWidget(self.editKey, 4)
|
686
733
|
self.editBox.addWidget(self.editValue, 5)
|
687
|
-
self.editBox.addWidget(self.applyButton, 0)
|
688
734
|
|
689
735
|
self.mainBox = QVBoxLayout()
|
690
736
|
self.mainBox.addWidget(self.listBox, 1)
|
@@ -716,7 +762,7 @@ class _ReplacePage(NFixedPage):
|
|
716
762
|
new = {}
|
717
763
|
for n in range(self.listBox.topLevelItemCount()):
|
718
764
|
if item := self.listBox.topLevelItem(n):
|
719
|
-
if key := self.
|
765
|
+
if key := self._stripKey(item.text(self.C_KEY)):
|
720
766
|
new[key] = item.text(self.C_REPL)
|
721
767
|
return new
|
722
768
|
|
@@ -728,13 +774,29 @@ class _ReplacePage(NFixedPage):
|
|
728
774
|
# Private Slots
|
729
775
|
##
|
730
776
|
|
777
|
+
@pyqtSlot(str)
|
778
|
+
def _onKeyEdit(self, text: str) -> None:
|
779
|
+
"""Update the key text."""
|
780
|
+
if (item := self._getSelectedItem()) and (key := self._stripKey(text)):
|
781
|
+
item.setText(self.C_KEY, f"<{key}>")
|
782
|
+
self._changed = True
|
783
|
+
return
|
784
|
+
|
785
|
+
@pyqtSlot(str)
|
786
|
+
def _onValueEdit(self, text: str) -> None:
|
787
|
+
"""Update the value text."""
|
788
|
+
if item := self._getSelectedItem():
|
789
|
+
item.setText(self.C_REPL, text)
|
790
|
+
self._changed = True
|
791
|
+
return
|
792
|
+
|
731
793
|
@pyqtSlot()
|
732
|
-
def
|
794
|
+
def _onSelectionChanged(self) -> None:
|
733
795
|
"""Extract the details from the selected item and populate the
|
734
796
|
edit form.
|
735
797
|
"""
|
736
798
|
if item := self._getSelectedItem():
|
737
|
-
self.editKey.setText(self.
|
799
|
+
self.editKey.setText(self._stripKey(item.text(self.C_KEY)))
|
738
800
|
self.editValue.setText(item.text(self.C_REPL))
|
739
801
|
self.editKey.setEnabled(True)
|
740
802
|
self.editValue.setEnabled(True)
|
@@ -748,26 +810,14 @@ class _ReplacePage(NFixedPage):
|
|
748
810
|
return
|
749
811
|
|
750
812
|
@pyqtSlot()
|
751
|
-
def
|
752
|
-
"""Save the form data into the list widget."""
|
753
|
-
if item := self._getSelectedItem():
|
754
|
-
key = self._stripNotAllowed(self.editKey.text())
|
755
|
-
value = self.editValue.text()
|
756
|
-
if key and value:
|
757
|
-
item.setText(self.C_KEY, f"<{key}>")
|
758
|
-
item.setText(self.C_REPL, value)
|
759
|
-
self._changed = True
|
760
|
-
return
|
761
|
-
|
762
|
-
@pyqtSlot()
|
763
|
-
def _addEntry(self) -> None:
|
813
|
+
def _onEntryCreated(self) -> None:
|
764
814
|
"""Add a new list entry."""
|
765
815
|
key = f"<keyword{self.listBox.topLevelItemCount() + 1:d}>"
|
766
816
|
self.listBox.addTopLevelItem(QTreeWidgetItem([key, ""]))
|
767
817
|
return
|
768
818
|
|
769
819
|
@pyqtSlot()
|
770
|
-
def
|
820
|
+
def _onEntryDeleted(self) -> None:
|
771
821
|
"""Delete the selected entry."""
|
772
822
|
if item := self._getSelectedItem():
|
773
823
|
self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(item))
|
@@ -784,6 +834,6 @@ class _ReplacePage(NFixedPage):
|
|
784
834
|
return items[0]
|
785
835
|
return None
|
786
836
|
|
787
|
-
def
|
837
|
+
def _stripKey(self, key: str) -> str:
|
788
838
|
"""Clean up the replace key string."""
|
789
839
|
return "".join(c for c in key if c.isalnum())
|
novelwriter/dialogs/quotes.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2020-06-18 [0.9.0] GuiQuoteSelect
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright
|
9
|
+
Copyright (C) 2021 Veronica Berglyd Olsen and novelWriter contributors
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
novelwriter/dialogs/wordlist.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2021-02-12 [1.2rc1] GuiWordList
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright
|
9
|
+
Copyright (C) 2021 Veronica Berglyd Olsen and novelWriter contributors
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -96,9 +96,11 @@ class GuiWordList(NDialog):
|
|
96
96
|
self.newEntry = QLineEdit(self)
|
97
97
|
|
98
98
|
self.addButton = NIconToolButton(self, iSz, "add")
|
99
|
+
self.addButton.setToolTip(self.tr("Add Word"))
|
99
100
|
self.addButton.clicked.connect(self._doAdd)
|
100
101
|
|
101
102
|
self.delButton = NIconToolButton(self, iSz, "remove")
|
103
|
+
self.delButton.setToolTip(self.tr("Remove Word"))
|
102
104
|
self.delButton.clicked.connect(self._doDelete)
|
103
105
|
|
104
106
|
self.editBox = QHBoxLayout()
|
@@ -184,11 +186,10 @@ class GuiWordList(NDialog):
|
|
184
186
|
SHARED.info(self.tr(
|
185
187
|
"Note: The import file must be a plain text file with UTF-8 or ASCII encoding."
|
186
188
|
))
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
)
|
191
|
-
if path:
|
189
|
+
if path := QFileDialog.getOpenFileName(
|
190
|
+
self, self.tr("Import File"), str(CONFIG.homePath()),
|
191
|
+
filter=formatFileFilter(["*.txt", "*"]),
|
192
|
+
)[0]:
|
192
193
|
try:
|
193
194
|
with open(path, mode="r", encoding="utf-8") as fo:
|
194
195
|
words = set(w.strip() for w in fo.read().split())
|
@@ -202,10 +203,10 @@ class GuiWordList(NDialog):
|
|
202
203
|
@pyqtSlot()
|
203
204
|
def _exportWords(self) -> None:
|
204
205
|
"""Export words to file."""
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
206
|
+
name = f"{SHARED.project.data.fileSafeName} - {self.windowTitle()}.txt"
|
207
|
+
if path := QFileDialog.getSaveFileName(
|
208
|
+
self, self.tr("Export File"), str(CONFIG.homePath() / name),
|
209
|
+
)[0]:
|
209
210
|
try:
|
210
211
|
path = Path(path).with_suffix(".txt")
|
211
212
|
with open(path, mode="w", encoding="utf-8") as fo:
|
novelwriter/enum.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2018-11-02 [0.0.1]
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018
|
9
|
+
Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -75,6 +75,13 @@ class nwTrinary(Enum):
|
|
75
75
|
POSITIVE = 1
|
76
76
|
|
77
77
|
|
78
|
+
class nwChange(Enum):
|
79
|
+
|
80
|
+
CREATE = 0
|
81
|
+
UPDATE = 1
|
82
|
+
DELETE = 2
|
83
|
+
|
84
|
+
|
78
85
|
class nwDocMode(Enum):
|
79
86
|
|
80
87
|
VIEW = 0
|
novelwriter/error.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2020-08-02 [0.10.2]
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright
|
9
|
+
Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -76,7 +76,7 @@ class NWErrorMessage(QDialog):
|
|
76
76
|
|
77
77
|
font = QFont()
|
78
78
|
font.setPointSize(round(0.9*self.font().pointSize()))
|
79
|
-
font.setFamily(QFontDatabase.systemFont(QFontDatabase.FixedFont).family())
|
79
|
+
font.setFamily(QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont).family())
|
80
80
|
|
81
81
|
self.msgBody = QPlainTextEdit()
|
82
82
|
self.msgBody.setFont(font)
|