Add an ignoreCase setting for autoReplace
This commit is contained in:
@@ -61,6 +61,7 @@ class ProjectData:
|
||||
"_author",
|
||||
"_autoCount",
|
||||
"_autoReplace",
|
||||
"_autoReplaceIgnoreCase",
|
||||
"_currCounts",
|
||||
"_dailyGoal",
|
||||
"_dailyGoalAuto",
|
||||
@@ -110,6 +111,7 @@ class ProjectData:
|
||||
self._rtl = False
|
||||
self._spellCheck = False
|
||||
self._spellLang = None
|
||||
self._autoReplaceIgnoreCase = False
|
||||
|
||||
# Writing Target
|
||||
self._targetCount = 0
|
||||
@@ -285,6 +287,11 @@ class ProjectData:
|
||||
"""Return the auto-replace dictionary."""
|
||||
return self._autoReplace
|
||||
|
||||
@property
|
||||
def autoReplaceIgnoreCase(self) -> bool:
|
||||
"""Return True if the auto replace should ignore case."""
|
||||
return self._autoReplaceIgnoreCase
|
||||
|
||||
@property
|
||||
def itemStatus(self) -> ItemStatus:
|
||||
"""Return the status settings object."""
|
||||
@@ -462,8 +469,9 @@ class ProjectData:
|
||||
if cNotes is not None:
|
||||
self._currCounts[3] = checkInt(cNotes, 0)
|
||||
|
||||
def setAutoReplace(self, value: dict) -> None:
|
||||
def setAutoReplace(self, value: dict, ignoreCase: Any) -> None:
|
||||
"""Set the auto-replace dictionary."""
|
||||
self._autoReplaceIgnoreCase = checkBool(ignoreCase, False)
|
||||
if isinstance(value, dict):
|
||||
self._autoReplace = {}
|
||||
for key, entry in value.items():
|
||||
|
||||
@@ -52,7 +52,7 @@ if TYPE_CHECKING:
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
FILE_VERSION = "1.6" # The current project file format version
|
||||
FILE_REVISION = "0" # The current project file format revision
|
||||
FILE_REVISION = "1" # The current project file format revision
|
||||
HEX_VERSION = 0x0106 # The current project file format version as hex
|
||||
LAST_BREAKING = 0x260200B2 # Last project format breaking change (not XML)
|
||||
|
||||
@@ -127,6 +127,8 @@ class ProjectXMLReader:
|
||||
1.6 The actual format released for 26.2. All documents are now
|
||||
stored with the .md file extension and a toml header. The
|
||||
project XML was not changed.
|
||||
|
||||
Rev 1: Added ignoreCase attribute to autoReplace (26.3).
|
||||
"""
|
||||
|
||||
def __init__(self, path: str | Path) -> None:
|
||||
@@ -297,9 +299,9 @@ class ProjectXMLReader:
|
||||
data.setInitLastHandles(self._parseDictKeyText(xItem))
|
||||
elif xItem.tag == "autoReplace":
|
||||
if self._version >= 0x0102:
|
||||
data.setAutoReplace(self._parseDictKeyText(xItem))
|
||||
data.setAutoReplace(self._parseDictKeyText(xItem), xItem.attrib.get("ignoreCase"))
|
||||
else: # Pre 1.2 format
|
||||
data.setAutoReplace(self._parseDictTagText(xItem))
|
||||
data.setAutoReplace(self._parseDictTagText(xItem), False)
|
||||
else:
|
||||
logger.warning("Ignored <root/settings/%s> in XML", xItem.tag)
|
||||
|
||||
@@ -546,7 +548,12 @@ class ProjectXMLWriter:
|
||||
self._packSingleValue(xSettings, "dailyTarget", str(data.dailyGoal), attrib=dailyAttr)
|
||||
self._packListValues(xSettings, "targetSkipRoots", data.targetSkipRoots)
|
||||
self._packDictKeyValue(xSettings, "lastHandle", data.lastHandle)
|
||||
self._packDictKeyValue(xSettings, "autoReplace", data.autoReplace)
|
||||
self._packDictKeyValue(
|
||||
xSettings,
|
||||
"autoReplace",
|
||||
data.autoReplace,
|
||||
attrib={"ignoreCase": yesNo(data.autoReplaceIgnoreCase)},
|
||||
)
|
||||
|
||||
# Save Status/Importance
|
||||
xStatus = ET.SubElement(xSettings, "status")
|
||||
@@ -594,22 +601,40 @@ class ProjectXMLWriter:
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _packSingleValue(self, xParent: ET.Element, name: str, value: str | None, attrib: dict | None = None) -> None:
|
||||
def _packSingleValue(
|
||||
self,
|
||||
xParent: ET.Element,
|
||||
name: str,
|
||||
value: str | None,
|
||||
attrib: dict | None = None,
|
||||
) -> None:
|
||||
"""Pack a single value into an XML element."""
|
||||
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
|
||||
xItem.text = str(value) or ""
|
||||
|
||||
def _packListValues(self, xParent: ET.Element, name: str, data: list | tuple | set) -> None:
|
||||
def _packListValues(
|
||||
self,
|
||||
xParent: ET.Element,
|
||||
name: str,
|
||||
data: list | tuple | set,
|
||||
attrib: dict | None = None,
|
||||
) -> None:
|
||||
"""Pack the entries of a sequence into an XML element."""
|
||||
xItem = ET.SubElement(xParent, name)
|
||||
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
|
||||
for value in data:
|
||||
if len(str(value)) > 0: # pragma: no branch
|
||||
xEntry = ET.SubElement(xItem, "entry")
|
||||
xEntry.text = str(value) or ""
|
||||
|
||||
def _packDictKeyValue(self, xParent: ET.Element, name: str, data: Mapping[str, str | None]) -> None:
|
||||
def _packDictKeyValue(
|
||||
self,
|
||||
xParent: ET.Element,
|
||||
name: str,
|
||||
data: Mapping[str, str | None],
|
||||
attrib: dict | None = None,
|
||||
) -> None:
|
||||
"""Pack the entries of a dictionary into an XML element."""
|
||||
xItem = ET.SubElement(xParent, name)
|
||||
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
|
||||
for key, value in data.items():
|
||||
if len(key) > 0: # pragma: no branch
|
||||
xEntry = ET.SubElement(xItem, "entry", attrib={"key": key})
|
||||
|
||||
@@ -230,9 +230,11 @@ class GuiProjectSettings(NDialog):
|
||||
logger.debug("Updating importance labels")
|
||||
project.updateStatus("i", self.importPage.getNewList())
|
||||
|
||||
autoReplaceIgnoreCase = self.replacePage.swtIgnoreCase.isChecked()
|
||||
|
||||
if self.replacePage.changed:
|
||||
logger.debug("Updating auto-replace settings")
|
||||
project.data.setAutoReplace(self.replacePage.getNewList())
|
||||
project.data.setAutoReplace(self.replacePage.getNewList(), autoReplaceIgnoreCase)
|
||||
|
||||
self.newProjectSettingsReady.emit()
|
||||
self.close()
|
||||
@@ -839,6 +841,7 @@ class _ReplacePage(NFixedPage):
|
||||
self._changed = False
|
||||
|
||||
wCol0 = SHARED.project.options.getInt("GuiProjectSettings", "replaceColW", 130)
|
||||
iPx = SHARED.theme.baseIconHeight
|
||||
|
||||
# Title
|
||||
self.pageTitle = NColorLabel(
|
||||
@@ -872,6 +875,14 @@ class _ReplacePage(NFixedPage):
|
||||
self.delButton = SHARED.theme.getFlatButton(nwToolButton.REMOVE, self)
|
||||
self.delButton.clicked.connect(self._onEntryDeleted)
|
||||
|
||||
# Settings
|
||||
self.swtIgnoreCase = NSwitch(self, height=iPx)
|
||||
self.swtIgnoreCase.setChecked(SHARED.project.data.autoReplaceIgnoreCase)
|
||||
self.swtIgnoreCase.toggled.connect(self._onIgnoreCaseToggled)
|
||||
|
||||
self.lblIgnoreCase = QLabel(self.tr("Ignore case for keywords"), self)
|
||||
self.lblIgnoreCase.setBuddy(self.swtIgnoreCase)
|
||||
|
||||
# Edit Form
|
||||
self.editKey = QLineEdit(self)
|
||||
self.editKey.setPlaceholderText(self.tr("Select item to edit"))
|
||||
@@ -890,6 +901,12 @@ class _ReplacePage(NFixedPage):
|
||||
self.listControls.addWidget(self.delButton)
|
||||
self.listControls.addStretch(1)
|
||||
|
||||
self.optsBox = QHBoxLayout()
|
||||
self.optsBox.addWidget(self.lblIgnoreCase)
|
||||
self.optsBox.addWidget(self.swtIgnoreCase)
|
||||
self.optsBox.setSpacing(12)
|
||||
self.optsBox.addStretch(1)
|
||||
|
||||
self.editBox = QHBoxLayout()
|
||||
self.editBox.addWidget(self.editKey, 4)
|
||||
self.editBox.addWidget(self.editValue, 5)
|
||||
@@ -897,6 +914,7 @@ class _ReplacePage(NFixedPage):
|
||||
self.mainBox = QVBoxLayout()
|
||||
self.mainBox.addWidget(self.listBox, 1)
|
||||
self.mainBox.addLayout(self.editBox, 0)
|
||||
self.mainBox.addLayout(self.optsBox, 0)
|
||||
|
||||
self.innerBox = QHBoxLayout()
|
||||
self.innerBox.addLayout(self.mainBox)
|
||||
@@ -947,6 +965,11 @@ class _ReplacePage(NFixedPage):
|
||||
item.setText(self.C_REPL, text)
|
||||
self._changed = True
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def _onIgnoreCaseToggled(self, state: bool) -> None:
|
||||
"""Mark the settings as changed."""
|
||||
self._changed = True
|
||||
|
||||
@pyqtSlot()
|
||||
def _onSelectionChanged(self) -> None:
|
||||
"""Extract the details from the selected item and populate the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="26.3b1" hexVersion="0x260300b1" fileVersion="1.6" fileRevision="0" timeStamp="2026-11-01 16:58:33">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2433" autoCount="437" editTime="112772" indexRevision="239">
|
||||
<novelWriterXML appVersion="26.3b1" hexVersion="0x260300b1" fileVersion="1.6" fileRevision="1" timeStamp="2026-11-01 18:00:41">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2441" autoCount="437" editTime="112928" indexRevision="240">
|
||||
<name>Sample Project</name>
|
||||
<author>Jane Smith</author>
|
||||
</project>
|
||||
@@ -19,7 +19,7 @@
|
||||
<entry key="novel">7031beac91f75</entry>
|
||||
<entry key="story">7031beac91f75</entry>
|
||||
</lastHandle>
|
||||
<autoReplace>
|
||||
<autoReplace ignoreCase="no">
|
||||
<entry key="A">B</entry>
|
||||
<entry key="B">E</entry>
|
||||
<entry key="C">D</entry>
|
||||
@@ -48,7 +48,7 @@
|
||||
<name status="sc24b8f" import="ia857f0">Novel</name>
|
||||
</item>
|
||||
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H1" charCount="283" wordCount="50" paraCount="5" cursorPos="413" />
|
||||
<meta expanded="no" heading="H1" charCount="283" wordCount="50" paraCount="5" cursorPos="79" />
|
||||
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
@@ -68,7 +68,7 @@
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="650" />
|
||||
<meta expanded="no" heading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="18" />
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Another Scene</name>
|
||||
</item>
|
||||
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
|
||||
Reference in New Issue
Block a user