novelWriter 2.2.1__py3-none-any.whl → 2.3b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/METADATA +1 -1
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +102 -92
- novelwriter/__init__.py +4 -4
- novelwriter/assets/icons/typicons_dark/icons.conf +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_dark/typ_document-add-col.svg +8 -0
- novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +6 -0
- novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_light/typ_document-add-col.svg +8 -0
- novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
- novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
- novelwriter/assets/images/novelwriter-text-light.svg +4 -0
- novelwriter/assets/images/welcome-dark.jpg +0 -0
- novelwriter/assets/images/welcome-light.jpg +0 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/default_dark.conf +1 -0
- novelwriter/assets/syntax/default_light.conf +1 -0
- novelwriter/assets/syntax/grey_dark.conf +1 -0
- novelwriter/assets/syntax/grey_light.conf +1 -0
- novelwriter/assets/syntax/light_owl.conf +1 -0
- novelwriter/assets/syntax/night_owl.conf +1 -0
- novelwriter/assets/syntax/solarized_dark.conf +1 -0
- novelwriter/assets/syntax/solarized_light.conf +1 -0
- novelwriter/assets/syntax/tomorrow.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
- novelwriter/assets/text/credits_en.htm +4 -2
- novelwriter/assets/themes/default_dark.conf +2 -2
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +48 -37
- novelwriter/config.py +36 -41
- novelwriter/constants.py +38 -16
- novelwriter/core/buildsettings.py +7 -7
- novelwriter/core/coretools.py +192 -154
- novelwriter/core/docbuild.py +6 -3
- novelwriter/core/document.py +6 -6
- novelwriter/core/index.py +89 -56
- novelwriter/core/item.py +21 -3
- novelwriter/core/options.py +8 -7
- novelwriter/core/project.py +69 -44
- novelwriter/core/projectdata.py +1 -14
- novelwriter/core/projectxml.py +13 -41
- novelwriter/core/sessions.py +2 -1
- novelwriter/core/spellcheck.py +2 -1
- novelwriter/core/status.py +2 -1
- novelwriter/core/storage.py +178 -140
- novelwriter/core/tohtml.py +4 -2
- novelwriter/core/tokenizer.py +73 -45
- novelwriter/core/toodt.py +40 -30
- novelwriter/core/tree.py +3 -2
- novelwriter/dialogs/about.py +70 -160
- novelwriter/dialogs/docmerge.py +6 -5
- novelwriter/dialogs/docsplit.py +6 -6
- novelwriter/dialogs/editlabel.py +1 -1
- novelwriter/dialogs/preferences.py +553 -703
- novelwriter/dialogs/{projsettings.py → projectsettings.py} +288 -262
- novelwriter/dialogs/quotes.py +27 -23
- novelwriter/dialogs/wordlist.py +96 -40
- novelwriter/enum.py +20 -18
- novelwriter/error.py +1 -1
- novelwriter/extensions/circularprogress.py +11 -11
- novelwriter/extensions/configlayout.py +185 -134
- novelwriter/extensions/modified.py +81 -0
- novelwriter/extensions/novelselector.py +26 -12
- novelwriter/extensions/pagedsidebar.py +14 -16
- novelwriter/extensions/simpleprogress.py +5 -5
- novelwriter/extensions/statusled.py +8 -8
- novelwriter/extensions/switch.py +31 -63
- novelwriter/extensions/switchbox.py +1 -1
- novelwriter/extensions/versioninfo.py +153 -0
- novelwriter/gui/doceditor.py +178 -150
- novelwriter/gui/dochighlight.py +63 -92
- novelwriter/gui/docviewer.py +49 -51
- novelwriter/gui/docviewerpanel.py +72 -24
- novelwriter/gui/itemdetails.py +7 -7
- novelwriter/gui/mainmenu.py +14 -18
- novelwriter/gui/noveltree.py +9 -8
- novelwriter/gui/outline.py +98 -75
- novelwriter/gui/projtree.py +188 -61
- novelwriter/gui/sidebar.py +3 -4
- novelwriter/gui/statusbar.py +3 -4
- novelwriter/gui/theme.py +60 -68
- novelwriter/guimain.py +49 -156
- novelwriter/shared.py +15 -1
- novelwriter/tools/dictionaries.py +5 -6
- novelwriter/tools/manuscript.py +6 -6
- novelwriter/tools/manussettings.py +192 -221
- novelwriter/tools/noveldetails.py +525 -0
- novelwriter/tools/welcome.py +802 -0
- novelwriter/tools/writingstats.py +9 -9
- novelwriter/assets/images/wizard-back.jpg +0 -0
- novelwriter/assets/text/gplv3_en.htm +0 -641
- novelwriter/assets/text/release_notes.htm +0 -60
- novelwriter/dialogs/projdetails.py +0 -518
- novelwriter/dialogs/projload.py +0 -294
- novelwriter/dialogs/updates.py +0 -172
- novelwriter/extensions/pageddialog.py +0 -130
- novelwriter/tools/projwizard.py +0 -478
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
novelwriter/gui/doceditor.py
CHANGED
@@ -56,7 +56,6 @@ from novelwriter import CONFIG, SHARED
|
|
56
56
|
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemClass, nwTrinary
|
57
57
|
from novelwriter.common import minmax, transferCase
|
58
58
|
from novelwriter.constants import nwKeyWords, nwLabels, nwShortcode, nwUnicode, trConst
|
59
|
-
from novelwriter.core.item import NWItem
|
60
59
|
from novelwriter.core.index import countWords
|
61
60
|
from novelwriter.tools.lipsum import GuiLipsum
|
62
61
|
from novelwriter.core.document import NWDocument
|
@@ -187,13 +186,12 @@ class GuiDocEditor(QPlainTextEdit):
|
|
187
186
|
# Set Up Document Word Counter
|
188
187
|
self.wcTimerDoc = QTimer()
|
189
188
|
self.wcTimerDoc.timeout.connect(self._runDocCounter)
|
189
|
+
self.wcTimerDoc.setInterval(5000)
|
190
190
|
|
191
191
|
self.wCounterDoc = BackgroundWordCounter(self)
|
192
192
|
self.wCounterDoc.setAutoDelete(False)
|
193
193
|
self.wCounterDoc.signals.countsReady.connect(self._updateDocCounts)
|
194
194
|
|
195
|
-
self.wcInterval = CONFIG.wordCountTimer
|
196
|
-
|
197
195
|
# Set Up Selection Word Counter
|
198
196
|
self.wcTimerSel = QTimer()
|
199
197
|
self.wcTimerSel.timeout.connect(self._runSelCounter)
|
@@ -277,14 +275,14 @@ class GuiDocEditor(QPlainTextEdit):
|
|
277
275
|
def updateSyntaxColours(self) -> None:
|
278
276
|
"""Update the syntax highlighting theme."""
|
279
277
|
mainPalette = self.palette()
|
280
|
-
mainPalette.setColor(QPalette.ColorRole.Window,
|
281
|
-
mainPalette.setColor(QPalette.ColorRole.Base,
|
282
|
-
mainPalette.setColor(QPalette.ColorRole.Text,
|
278
|
+
mainPalette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
|
279
|
+
mainPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
|
280
|
+
mainPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
283
281
|
self.setPalette(mainPalette)
|
284
282
|
|
285
283
|
docPalette = self.viewport().palette()
|
286
|
-
docPalette.setColor(QPalette.ColorRole.Base,
|
287
|
-
docPalette.setColor(QPalette.ColorRole.Text,
|
284
|
+
docPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
|
285
|
+
docPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
288
286
|
self.viewport().setPalette(docPalette)
|
289
287
|
|
290
288
|
self.docHeader.matchColours()
|
@@ -320,10 +318,10 @@ class GuiDocEditor(QPlainTextEdit):
|
|
320
318
|
SHARED.updateSpellCheckLanguage()
|
321
319
|
|
322
320
|
# Set font
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
self.setFont(
|
321
|
+
font = QFont()
|
322
|
+
font.setFamily(CONFIG.textFont)
|
323
|
+
font.setPointSize(CONFIG.textSize)
|
324
|
+
self.setFont(font)
|
327
325
|
|
328
326
|
# Set default text margins
|
329
327
|
# Due to cursor visibility, a part of the margin must be
|
@@ -359,17 +357,14 @@ class GuiDocEditor(QPlainTextEdit):
|
|
359
357
|
# Refresh the tab stops
|
360
358
|
self.setTabStopDistance(CONFIG.getTabWidth())
|
361
359
|
|
362
|
-
#
|
363
|
-
self.wcInterval = CONFIG.wordCountTimer
|
364
|
-
self.wcTimerDoc.setInterval(int(self.wcInterval*1000))
|
365
|
-
|
366
|
-
# If we have a document open, we should reload it in case the
|
360
|
+
# If we have a document open, we should refresh it in case the
|
367
361
|
# font changed, otherwise we just clear the editor entirely,
|
368
362
|
# which makes it read only.
|
369
|
-
if self._docHandle
|
370
|
-
self.clearEditor()
|
371
|
-
else:
|
363
|
+
if self._docHandle:
|
372
364
|
self._qDocument.syntaxHighlighter.rehighlight()
|
365
|
+
self.docHeader.setTitleFromHandle(self._docHandle)
|
366
|
+
else:
|
367
|
+
self.clearEditor()
|
373
368
|
|
374
369
|
return
|
375
370
|
|
@@ -741,9 +736,11 @@ class GuiDocEditor(QPlainTextEdit):
|
|
741
736
|
elif action == nwDocAction.BLOCK_H4:
|
742
737
|
self._formatBlock(nwDocAction.BLOCK_H4)
|
743
738
|
elif action == nwDocAction.BLOCK_COM:
|
744
|
-
self.
|
739
|
+
self._iterFormatBlocks(nwDocAction.BLOCK_COM)
|
740
|
+
elif action == nwDocAction.BLOCK_IGN:
|
741
|
+
self._iterFormatBlocks(nwDocAction.BLOCK_IGN)
|
745
742
|
elif action == nwDocAction.BLOCK_TXT:
|
746
|
-
self.
|
743
|
+
self._iterFormatBlocks(nwDocAction.BLOCK_TXT)
|
747
744
|
elif action == nwDocAction.BLOCK_TTL:
|
748
745
|
self._formatBlock(nwDocAction.BLOCK_TTL)
|
749
746
|
elif action == nwDocAction.BLOCK_UNN:
|
@@ -1190,7 +1187,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1190
1187
|
logger.debug("Word counter is busy")
|
1191
1188
|
return
|
1192
1189
|
|
1193
|
-
if time() - self._lastEdit <
|
1190
|
+
if time() - self._lastEdit < 25.0:
|
1194
1191
|
logger.debug("Running word counter")
|
1195
1192
|
SHARED.runInThreadPool(self.wCounterDoc)
|
1196
1193
|
|
@@ -1593,9 +1590,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1593
1590
|
|
1594
1591
|
posS = cursor.selectionStart()
|
1595
1592
|
posE = cursor.selectionEnd()
|
1596
|
-
closeCheck = (
|
1597
|
-
" ", "\n", nwUnicode.U_LSEP, nwUnicode.U_PSEP
|
1598
|
-
)
|
1593
|
+
closeCheck = (" ", "\n", nwUnicode.U_LSEP, nwUnicode.U_PSEP)
|
1599
1594
|
|
1600
1595
|
self._allowAutoReplace(False)
|
1601
1596
|
for posC in range(posS, posE+1):
|
@@ -1639,159 +1634,189 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1639
1634
|
|
1640
1635
|
return True
|
1641
1636
|
|
1642
|
-
def
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
if not block.isValid():
|
1647
|
-
logger.debug("Invalid block selected for action '%s'", str(action))
|
1648
|
-
return False
|
1649
|
-
|
1637
|
+
def _processBlockFormat(
|
1638
|
+
self, action: nwDocAction, text: str, toggle: bool = True
|
1639
|
+
) -> tuple[nwDocAction, str, int]:
|
1640
|
+
"""Process the formatting of a single text block."""
|
1650
1641
|
# Remove existing format first, if any
|
1651
|
-
|
1652
|
-
hasText = len(setText) > 0
|
1653
|
-
if setText.startswith("@"):
|
1642
|
+
if text.startswith("@"):
|
1654
1643
|
logger.error("Cannot apply block format to keyword/value line")
|
1655
|
-
return
|
1656
|
-
elif
|
1657
|
-
|
1658
|
-
|
1659
|
-
if action == nwDocAction.
|
1644
|
+
return nwDocAction.NO_ACTION, "", 0
|
1645
|
+
elif text.startswith("%~"):
|
1646
|
+
temp = text[2:].lstrip()
|
1647
|
+
offset = len(text) - len(temp)
|
1648
|
+
if toggle and action == nwDocAction.BLOCK_IGN:
|
1660
1649
|
action = nwDocAction.BLOCK_TXT
|
1661
|
-
elif
|
1662
|
-
|
1663
|
-
|
1664
|
-
if action == nwDocAction.BLOCK_COM:
|
1650
|
+
elif text.startswith("%"):
|
1651
|
+
temp = text[1:].lstrip()
|
1652
|
+
offset = len(text) - len(temp)
|
1653
|
+
if toggle and action == nwDocAction.BLOCK_COM:
|
1665
1654
|
action = nwDocAction.BLOCK_TXT
|
1666
|
-
elif
|
1667
|
-
|
1668
|
-
|
1669
|
-
elif
|
1670
|
-
|
1671
|
-
|
1672
|
-
elif
|
1673
|
-
|
1674
|
-
|
1675
|
-
elif
|
1676
|
-
|
1677
|
-
|
1678
|
-
elif
|
1679
|
-
|
1680
|
-
|
1681
|
-
elif
|
1682
|
-
|
1683
|
-
|
1684
|
-
elif
|
1685
|
-
|
1686
|
-
|
1687
|
-
elif
|
1688
|
-
|
1689
|
-
|
1690
|
-
elif
|
1691
|
-
|
1692
|
-
|
1693
|
-
elif
|
1694
|
-
|
1695
|
-
|
1655
|
+
elif text.startswith("# "):
|
1656
|
+
temp = text[2:]
|
1657
|
+
offset = 2
|
1658
|
+
elif text.startswith("## "):
|
1659
|
+
temp = text[3:]
|
1660
|
+
offset = 3
|
1661
|
+
elif text.startswith("### "):
|
1662
|
+
temp = text[4:]
|
1663
|
+
offset = 4
|
1664
|
+
elif text.startswith("#### "):
|
1665
|
+
temp = text[5:]
|
1666
|
+
offset = 5
|
1667
|
+
elif text.startswith("#! "):
|
1668
|
+
temp = text[3:]
|
1669
|
+
offset = 3
|
1670
|
+
elif text.startswith("##! "):
|
1671
|
+
temp = text[4:]
|
1672
|
+
offset = 4
|
1673
|
+
elif text.startswith(">> "):
|
1674
|
+
temp = text[3:]
|
1675
|
+
offset = 3
|
1676
|
+
elif text.startswith("> ") and action != nwDocAction.INDENT_R:
|
1677
|
+
temp = text[2:]
|
1678
|
+
offset = 2
|
1679
|
+
elif text.startswith(">>"):
|
1680
|
+
temp = text[2:]
|
1681
|
+
offset = 2
|
1682
|
+
elif text.startswith(">") and action != nwDocAction.INDENT_R:
|
1683
|
+
temp = text[1:]
|
1684
|
+
offset = 1
|
1696
1685
|
else:
|
1697
|
-
|
1698
|
-
|
1686
|
+
temp = text
|
1687
|
+
offset = 0
|
1699
1688
|
|
1700
1689
|
# Also remove formatting tags at the end
|
1701
|
-
if
|
1702
|
-
|
1703
|
-
elif
|
1704
|
-
|
1705
|
-
elif
|
1706
|
-
|
1707
|
-
elif
|
1708
|
-
|
1690
|
+
if text.endswith(" <<"):
|
1691
|
+
temp = temp[:-3]
|
1692
|
+
elif text.endswith(" <") and action != nwDocAction.INDENT_L:
|
1693
|
+
temp = temp[:-2]
|
1694
|
+
elif text.endswith("<<"):
|
1695
|
+
temp = temp[:-2]
|
1696
|
+
elif text.endswith("<") and action != nwDocAction.INDENT_L:
|
1697
|
+
temp = temp[:-1]
|
1709
1698
|
|
1710
1699
|
# Apply new format
|
1711
1700
|
if action == nwDocAction.BLOCK_COM:
|
1712
|
-
|
1713
|
-
|
1701
|
+
text = f"% {temp}"
|
1702
|
+
offset -= 2
|
1703
|
+
elif action == nwDocAction.BLOCK_IGN:
|
1704
|
+
text = f"%~ {temp}"
|
1705
|
+
offset -= 3
|
1714
1706
|
elif action == nwDocAction.BLOCK_H1:
|
1715
|
-
|
1716
|
-
|
1707
|
+
text = f"# {temp}"
|
1708
|
+
offset -= 2
|
1717
1709
|
elif action == nwDocAction.BLOCK_H2:
|
1718
|
-
|
1719
|
-
|
1710
|
+
text = f"## {temp}"
|
1711
|
+
offset -= 3
|
1720
1712
|
elif action == nwDocAction.BLOCK_H3:
|
1721
|
-
|
1722
|
-
|
1713
|
+
text = f"### {temp}"
|
1714
|
+
offset -= 4
|
1723
1715
|
elif action == nwDocAction.BLOCK_H4:
|
1724
|
-
|
1725
|
-
|
1716
|
+
text = f"#### {temp}"
|
1717
|
+
offset -= 5
|
1726
1718
|
elif action == nwDocAction.BLOCK_TTL:
|
1727
|
-
|
1728
|
-
|
1719
|
+
text = f"#! {temp}"
|
1720
|
+
offset -= 3
|
1729
1721
|
elif action == nwDocAction.BLOCK_UNN:
|
1730
|
-
|
1731
|
-
|
1722
|
+
text = f"##! {temp}"
|
1723
|
+
offset -= 4
|
1732
1724
|
elif action == nwDocAction.ALIGN_L:
|
1733
|
-
|
1725
|
+
text = f"{temp} <<"
|
1734
1726
|
elif action == nwDocAction.ALIGN_C:
|
1735
|
-
|
1736
|
-
|
1727
|
+
text = f">> {temp} <<"
|
1728
|
+
offset -= 3
|
1737
1729
|
elif action == nwDocAction.ALIGN_R:
|
1738
|
-
|
1739
|
-
|
1730
|
+
text = f">> {temp}"
|
1731
|
+
offset -= 3
|
1740
1732
|
elif action == nwDocAction.INDENT_L:
|
1741
|
-
|
1742
|
-
|
1733
|
+
text = f"> {temp}"
|
1734
|
+
offset -= 2
|
1743
1735
|
elif action == nwDocAction.INDENT_R:
|
1744
|
-
|
1736
|
+
text = f"{temp} <"
|
1745
1737
|
elif action == nwDocAction.BLOCK_TXT:
|
1746
|
-
|
1738
|
+
text = temp
|
1747
1739
|
else:
|
1748
1740
|
logger.error("Unknown or unsupported block format requested: '%s'", str(action))
|
1741
|
+
return nwDocAction.NO_ACTION, "", 0
|
1742
|
+
|
1743
|
+
return action, text, offset
|
1744
|
+
|
1745
|
+
def _formatBlock(self, action: nwDocAction) -> bool:
|
1746
|
+
"""Change the block format of the block under the cursor."""
|
1747
|
+
cursor = self.textCursor()
|
1748
|
+
block = cursor.block()
|
1749
|
+
if not block.isValid():
|
1750
|
+
logger.debug("Invalid block selected for action '%s'", str(action))
|
1751
|
+
return False
|
1752
|
+
|
1753
|
+
action, text, offset = self._processBlockFormat(action, block.text())
|
1754
|
+
if action == nwDocAction.NO_ACTION:
|
1749
1755
|
return False
|
1750
1756
|
|
1751
|
-
|
1757
|
+
pos = cursor.position()
|
1758
|
+
|
1752
1759
|
cursor.beginEditBlock()
|
1753
|
-
|
1754
|
-
cursor.
|
1755
|
-
|
1756
|
-
|
1757
|
-
|
1760
|
+
self._makeSelection(QTextCursor.SelectionType.BlockUnderCursor, cursor)
|
1761
|
+
cursor.insertText(text)
|
1762
|
+
cursor.endEditBlock()
|
1763
|
+
|
1764
|
+
if (move := pos - offset) >= 0:
|
1765
|
+
cursor.setPosition(move)
|
1766
|
+
self.setTextCursor(cursor)
|
1758
1767
|
|
1759
|
-
|
1760
|
-
# If the block already had text, we must insert a new block
|
1761
|
-
# first before we can add back the text to it.
|
1762
|
-
cursor.insertBlock()
|
1768
|
+
return True
|
1763
1769
|
|
1764
|
-
|
1770
|
+
def _iterFormatBlocks(self, action: nwDocAction) -> bool:
|
1771
|
+
"""Iterate over all selected blocks and apply format. If no
|
1772
|
+
selection is made, just forward the call to the single block
|
1773
|
+
formatter function.
|
1774
|
+
"""
|
1775
|
+
cursor = self.textCursor()
|
1776
|
+
blocks = self._selectedBlocks(cursor)
|
1777
|
+
if len(blocks) < 2:
|
1778
|
+
return self._formatBlock(action)
|
1765
1779
|
|
1766
|
-
|
1767
|
-
|
1780
|
+
toggle = True
|
1781
|
+
cursor.beginEditBlock()
|
1782
|
+
for block in blocks:
|
1783
|
+
blockText = block.text()
|
1784
|
+
pAction, text, _ = self._processBlockFormat(action, blockText, toggle)
|
1785
|
+
if pAction != nwDocAction.NO_ACTION and blockText.strip():
|
1786
|
+
action = pAction # First block decides further actions
|
1787
|
+
cursor.setPosition(block.position())
|
1788
|
+
self._makeSelection(QTextCursor.SelectionType.BlockUnderCursor, cursor)
|
1789
|
+
cursor.insertText(text)
|
1790
|
+
toggle = False
|
1768
1791
|
|
1769
1792
|
cursor.endEditBlock()
|
1770
|
-
self.setTextCursor(cursor)
|
1771
1793
|
|
1772
1794
|
return True
|
1773
1795
|
|
1796
|
+
def _selectedBlocks(self, cursor: QTextCursor) -> list[QTextBlock]:
|
1797
|
+
"""Return a list of all blocks selected by a cursor."""
|
1798
|
+
if cursor.hasSelection():
|
1799
|
+
iS = self._qDocument.findBlock(cursor.selectionStart()).blockNumber()
|
1800
|
+
iE = self._qDocument.findBlock(cursor.selectionEnd()).blockNumber()
|
1801
|
+
return [self._qDocument.findBlockByNumber(i) for i in range(iS, iE+1)]
|
1802
|
+
return []
|
1803
|
+
|
1774
1804
|
def _removeInParLineBreaks(self) -> None:
|
1775
1805
|
"""Strip line breaks within paragraphs in the selected text."""
|
1776
1806
|
cursor = self.textCursor()
|
1807
|
+
if not cursor.hasSelection():
|
1808
|
+
cursor.select(QTextCursor.SelectionType.Document)
|
1777
1809
|
|
1778
|
-
iS = 0
|
1779
|
-
iE = self._qDocument.blockCount() - 1
|
1780
1810
|
rS = 0
|
1781
1811
|
rE = self._qDocument.characterCount()
|
1782
|
-
if
|
1783
|
-
|
1784
|
-
|
1785
|
-
iS = sBlock.blockNumber()
|
1786
|
-
iE = eBlock.blockNumber()
|
1787
|
-
rS = sBlock.position()
|
1788
|
-
rE = eBlock.position() + eBlock.length()
|
1812
|
+
if sBlocks := self._selectedBlocks(cursor):
|
1813
|
+
rS = sBlocks[0].position()
|
1814
|
+
rE = sBlocks[-1].position() + sBlocks[-1].length()
|
1789
1815
|
|
1790
1816
|
# Clean up the text
|
1791
1817
|
currPar = []
|
1792
1818
|
cleanText = ""
|
1793
|
-
for
|
1794
|
-
cBlock = self._qDocument.findBlockByNumber(i)
|
1819
|
+
for cBlock in sBlocks:
|
1795
1820
|
cText = cBlock.text()
|
1796
1821
|
if cText.strip() == "":
|
1797
1822
|
if currPar:
|
@@ -1839,16 +1864,16 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1839
1864
|
if len(text) == 0:
|
1840
1865
|
return nwTrinary.NEUTRAL
|
1841
1866
|
|
1842
|
-
if text.startswith("@") and
|
1867
|
+
if text.startswith("@") and self._docHandle:
|
1843
1868
|
|
1844
1869
|
isGood, tBits, tPos = SHARED.project.index.scanThis(text)
|
1845
|
-
if not isGood:
|
1870
|
+
if not isGood or not tBits or tBits[0] == nwKeyWords.TAG_KEY:
|
1846
1871
|
return nwTrinary.NEUTRAL
|
1847
1872
|
|
1848
1873
|
tag = ""
|
1849
1874
|
exist = False
|
1850
1875
|
cPos = cursor.selectionStart() - block.position()
|
1851
|
-
tExist = SHARED.project.index.checkThese(tBits, self.
|
1876
|
+
tExist = SHARED.project.index.checkThese(tBits, self._docHandle)
|
1852
1877
|
for sTag, sPos, sExist in zip(reversed(tBits), reversed(tPos), reversed(tExist)):
|
1853
1878
|
if cPos >= sPos:
|
1854
1879
|
# The cursor is between the start of two tags
|
@@ -2045,9 +2070,11 @@ class GuiDocEditor(QPlainTextEdit):
|
|
2045
2070
|
|
2046
2071
|
return cursor
|
2047
2072
|
|
2048
|
-
def _makeSelection(self, mode: QTextCursor.SelectionType
|
2073
|
+
def _makeSelection(self, mode: QTextCursor.SelectionType,
|
2074
|
+
cursor: QTextCursor | None = None) -> None:
|
2049
2075
|
"""Select text based on selection mode."""
|
2050
|
-
cursor
|
2076
|
+
if cursor is None:
|
2077
|
+
cursor = self.textCursor()
|
2051
2078
|
cursor.clearSelection()
|
2052
2079
|
cursor.select(mode)
|
2053
2080
|
|
@@ -2330,9 +2357,9 @@ class GuiDocToolBar(QWidget):
|
|
2330
2357
|
def updateTheme(self) -> None:
|
2331
2358
|
"""Initialise GUI elements that depend on specific settings."""
|
2332
2359
|
palette = QPalette()
|
2333
|
-
palette.setColor(QPalette.ColorRole.Window,
|
2334
|
-
palette.setColor(QPalette.ColorRole.WindowText,
|
2335
|
-
palette.setColor(QPalette.ColorRole.Text,
|
2360
|
+
palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
|
2361
|
+
palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
|
2362
|
+
palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
2336
2363
|
self.setPalette(palette)
|
2337
2364
|
|
2338
2365
|
self.tbBoldMD.setIcon(SHARED.theme.getIcon("fmt_bold-md"))
|
@@ -2870,10 +2897,11 @@ class GuiDocEditHeader(QWidget):
|
|
2870
2897
|
self.minmaxButton.setIcon(SHARED.theme.getIcon("maximise"))
|
2871
2898
|
self.closeButton.setIcon(SHARED.theme.getIcon("close"))
|
2872
2899
|
|
2900
|
+
colText = SHARED.theme.colText
|
2873
2901
|
buttonStyle = (
|
2874
2902
|
"QToolButton {{border: none; background: transparent;}} "
|
2875
|
-
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
2876
|
-
).format(
|
2903
|
+
"QToolButton:hover {{border: none; background: rgba({0}, {1}, {2}, 0.2);}}"
|
2904
|
+
).format(colText.red(), colText.green(), colText.blue())
|
2877
2905
|
|
2878
2906
|
self.tbButton.setStyleSheet(buttonStyle)
|
2879
2907
|
self.searchButton.setStyleSheet(buttonStyle)
|
@@ -2889,9 +2917,9 @@ class GuiDocEditHeader(QWidget):
|
|
2889
2917
|
theme rather than the main GUI.
|
2890
2918
|
"""
|
2891
2919
|
palette = QPalette()
|
2892
|
-
palette.setColor(QPalette.ColorRole.Window,
|
2893
|
-
palette.setColor(QPalette.ColorRole.WindowText,
|
2894
|
-
palette.setColor(QPalette.ColorRole.Text,
|
2920
|
+
palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
|
2921
|
+
palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
|
2922
|
+
palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
2895
2923
|
|
2896
2924
|
self.setPalette(palette)
|
2897
2925
|
self.itemTitle.setPalette(palette)
|
@@ -3097,9 +3125,9 @@ class GuiDocEditFooter(QWidget):
|
|
3097
3125
|
theme rather than the main GUI.
|
3098
3126
|
"""
|
3099
3127
|
palette = QPalette()
|
3100
|
-
palette.setColor(QPalette.ColorRole.Window,
|
3101
|
-
palette.setColor(QPalette.ColorRole.WindowText,
|
3102
|
-
palette.setColor(QPalette.ColorRole.Text,
|
3128
|
+
palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
|
3129
|
+
palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
|
3130
|
+
palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
3103
3131
|
|
3104
3132
|
self.setPalette(palette)
|
3105
3133
|
self.statusText.setPalette(palette)
|