Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add formatter helper for file extensions filters #1693

Merged
merged 2 commits into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion novelwriter/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
from novelwriter.error import logException
from novelwriter.constants import nwConst, nwUnicode
from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst

if TYPE_CHECKING: # pragma: no cover
from typing import TypeGuard # Requires Python 3.10
Expand Down Expand Up @@ -248,6 +248,19 @@ def formatVersion(value: str) -> str:
return value.lower().replace("a", " Alpha ").replace("b", " Beta ").replace("rc", " RC ")


def formatFileFilter(extensions: list[str | tuple[str, str]]) -> str:
"""Format a list of extensions, or extension + label pairs into a
QFileDialog extensions filter.
"""
result = []
for ext in extensions:
if isinstance(ext, str):
result.append(f"{trConst(nwLabels.FILE_FILTERS.get(ext))} ({ext})")
elif isinstance(ext, tuple) and len(ext) == 2:
result.append(f"{ext[0]} ({ext[1]})")
return ";;".join(result)


##
# String Functions
##
Expand Down
6 changes: 6 additions & 0 deletions novelwriter/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,12 @@ class nwLabels:
nwBuildFmt.J_HTML: ".json",
nwBuildFmt.J_NWD: ".json",
}
FILE_FILTERS = {
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
"*.nwd": QT_TRANSLATE_NOOP("Constant", "novelWriter files"),
"*": QT_TRANSLATE_NOOP("Constant", "All files"),
}
UNIT_NAME = {
"mm": QT_TRANSLATE_NOOP("Constant", "Millimetres"),
"cm": QT_TRANSLATE_NOOP("Constant", "Centimetres"),
Expand Down
8 changes: 3 additions & 5 deletions novelwriter/dialogs/wordlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
)

from novelwriter import CONFIG, SHARED
from novelwriter.common import formatFileFilter
from novelwriter.core.spellcheck import UserDictionary
from novelwriter.extensions.configlayout import NColourLabel

Expand Down Expand Up @@ -184,12 +185,9 @@ def _importWords(self) -> None:
SHARED.info(self.tr(
"Note: The import file must be a plain text file with UTF-8 or ASCII encoding."
))
extFilter = [
"{0} (*.txt)".format(self.tr("Text files")),
"{0} (*)".format(self.tr("All files")),
]
ffilter = formatFileFilter(["*.txt", "*"])
path, _ = QFileDialog.getOpenFileName(
self, self.tr("Import File"), str(Path.home()), filter=";;".join(extFilter)
self, self.tr("Import File"), str(Path.home()), filter=ffilter
)
if path:
try:
Expand Down
11 changes: 3 additions & 8 deletions novelwriter/guimain.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
from novelwriter.enum import (
nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwWidget, nwView
)
from novelwriter.common import hexToInt
from novelwriter.common import formatFileFilter, hexToInt

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -664,14 +664,9 @@ def importDocument(self) -> bool:
return False

lastPath = CONFIG.lastPath()
extFilter = [
"{0} (*.txt)".format(self.tr("Text files")),
"{0} (*.md)".format(self.tr("Markdown files")),
"{0} (*.nwd)".format(self.tr("novelWriter files")),
"{0} (*)".format(self.tr("All files")),
]
ffilter = formatFileFilter(["*.txt", "*.md", "*.nwd", "*"])
loadFile, _ = QFileDialog.getOpenFileName(
self, self.tr("Import File"), str(lastPath), filter=";;".join(extFilter)
self, self.tr("Import File"), str(lastPath), filter=ffilter
)
if not loadFile:
return False
Expand Down
8 changes: 4 additions & 4 deletions novelwriter/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QWidget
from novelwriter.common import formatFileFilter

from novelwriter.constants import nwFiles
from novelwriter.core.spellcheck import NWSpellEnchant
Expand Down Expand Up @@ -221,13 +222,12 @@ def runInThreadPool(self, runnable: QRunnable, priority: int = 0) -> None:
def getProjectPath(self, parent: QWidget, path: str | Path | None = None,
allowZip: bool = False) -> Path | None:
"""Open the file dialog and select a novelWriter project file."""
label = (self.tr("novelWriter Project File or Zip")
label = (self.tr("novelWriter Project File or Zip File")
if allowZip else self.tr("novelWriter Project File"))
ext = f"{nwFiles.PROJ_FILE} *.zip" if allowZip else nwFiles.PROJ_FILE
ffilter = formatFileFilter([(label, ext), "*"])
selected, _ = QFileDialog.getOpenFileName(
parent, self.tr("Open Project"), str(path or ""), filter=";;".join(
[f"{label} ({ext})", "{0} (*)".format(self.tr("All Files"))]
)
parent, self.tr("Open Project"), str(path or ""), filter=ffilter
)
return Path(selected) if selected else None

Expand Down
11 changes: 5 additions & 6 deletions novelwriter/tools/dictionaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

from novelwriter import CONFIG, SHARED
from novelwriter.error import formatException
from novelwriter.common import openExternalPath, formatInt, getFileSize
from novelwriter.common import formatFileFilter, openExternalPath, formatInt, getFileSize

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -180,12 +180,11 @@ def closeEvent(self, event: QCloseEvent) -> None:
@pyqtSlot()
def _doBrowseHunspell(self):
"""Browse for a Free/Libre Office dictionary."""
extFilter = [
self.tr("Free or Libre Office extension ({0})").format("*.sox *.oxt"),
self.tr("All files ({0})").format("*"),
]
ffilter = formatFileFilter([
(self.tr("Free or Libre Office extension"), "*.sox *.oxt"), "*"
])
soxFile, _ = QFileDialog.getOpenFileName(
self, self.tr("Browse Files"), "", filter=";;".join(extFilter)
self, self.tr("Browse Files"), "", filter=ffilter
)
if soxFile:
path = Path(soxFile).absolute()
Expand Down
2 changes: 1 addition & 1 deletion novelwriter/tools/writingstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ def _saveData(self, dataFmt: int) -> bool:
# Generate the file name
savePath = CONFIG.lastPath() / f"sessionStats.{fileExt}"
savePath, _ = QFileDialog.getSaveFileName(
self, self.tr("Save Data As"), str(savePath), "%s (*.%s)" % (textFmt, fileExt)
self, self.tr("Save Data As"), str(savePath), f"{textFmt} (*.{fileExt})"
)
if not savePath:
return False
Expand Down
22 changes: 17 additions & 5 deletions tests/test_base/test_base_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@

from novelwriter.common import (
checkBool, checkFloat, checkInt, checkIntTuple, checkPath, checkString,
checkStringNone, checkUuid, formatInt, formatTime, formatTimeStamp,
formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass,
isItemLayout, isItemType, isTitleTag, jsonEncode, makeFileNameSafe, minmax,
numberToRoman, NWConfigParser, openExternalPath, readTextFile, simplified,
transferCase, xmlIndent, yesNo
checkStringNone, checkUuid, formatFileFilter, formatInt, formatTime,
formatTimeStamp, formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle,
isItemClass, isItemLayout, isItemType, isTitleTag, jsonEncode,
makeFileNameSafe, minmax, numberToRoman, NWConfigParser, openExternalPath,
readTextFile, simplified, transferCase, xmlIndent, yesNo
)


Expand Down Expand Up @@ -346,6 +346,18 @@ def testBaseCommon_formatVersion():
# END Test testBaseCommon_formatVersion


@pytest.mark.base
def testBaseCommon_formatFileFilter():
"""Test the formatFileFilter function."""
assert formatFileFilter(["*.txt"]) == "Text files (*.txt)"
assert formatFileFilter(["*.txt", "*"]) == "Text files (*.txt);;All files (*)"
assert formatFileFilter([("Stuff", "*.stuff"), "*.txt", "*"]) == (
"Stuff (*.stuff);;Text files (*.txt);;All files (*)"
)

# END Test testBaseCommon_formatFileFilter


@pytest.mark.base
def testBaseCommon_simplified():
"""Test the simplified function."""
Expand Down
Loading