From 9317dc97d8b308590e3b9bfe7041c2a5a3b7b4dc Mon Sep 17 00:00:00 2001 From: Jan Wester Date: Fri, 9 Aug 2019 00:16:02 +0200 Subject: [PATCH] Do not reinvent QFileDialog's default suffix According to issue #608 we were silently overwriting files when there was a suffix being generated for a chosen export filename when one was missing to begin with. Unfortunately, this helpful feature avoids all the conveniences offered by QFileDialog in regards to alerting the user to overwriting an existing file. Worse still, this feature already exists in QFileDialog and the native APIs it can rely on. This patch reimplements QFileDialog.getSaveFileName to allow the use of the default suffix feature as functions.getSaveFileNameWithSuffix and removes most of the magic involved with the old solution. --- manuskript/exporter/manuskript/HTML.py | 1 + manuskript/exporter/manuskript/markdown.py | 1 + manuskript/exporter/manuskript/plainText.py | 25 ++++++------------- manuskript/exporter/pandoc/HTML.py | 1 + manuskript/exporter/pandoc/PDF.py | 1 + manuskript/exporter/pandoc/abstractOutput.py | 1 + .../exporter/pandoc/abstractPlainText.py | 1 + manuskript/exporter/pandoc/outputFormats.py | 3 +++ manuskript/exporter/pandoc/plainText.py | 5 +++- manuskript/functions/__init__.py | 23 ++++++++++++++++- 10 files changed, 42 insertions(+), 20 deletions(-) diff --git a/manuskript/exporter/manuskript/HTML.py b/manuskript/exporter/manuskript/HTML.py index 02cd6f2..9f89087 100644 --- a/manuskript/exporter/manuskript/HTML.py +++ b/manuskript/exporter/manuskript/HTML.py @@ -21,6 +21,7 @@ class HTML(markdown): exportVarName = "lastManuskriptHTML" exportFilter = "HTML files (*.html);; Any files (*)" + exportDefaultSuffix = ".html" def isValid(self): return MD is not None diff --git a/manuskript/exporter/manuskript/markdown.py b/manuskript/exporter/manuskript/markdown.py index f338cb2..7bcae10 100644 --- a/manuskript/exporter/manuskript/markdown.py +++ b/manuskript/exporter/manuskript/markdown.py @@ -16,6 +16,7 @@ class markdown(plainText): exportVarName = "lastManuskriptMarkdown" exportFilter = "Markdown files (*.md);; Any files (*)" + exportDefaultSuffix = ".md" icon = "text-x-markdown" def settingsWidget(self): diff --git a/manuskript/exporter/manuskript/plainText.py b/manuskript/exporter/manuskript/plainText.py index 90250f4..49bf74f 100644 --- a/manuskript/exporter/manuskript/plainText.py +++ b/manuskript/exporter/manuskript/plainText.py @@ -5,7 +5,7 @@ from PyQt5.QtGui import QFont, QTextCharFormat from PyQt5.QtWidgets import QPlainTextEdit, qApp, QFrame, QFileDialog, QMessageBox from manuskript.exporter.basic import basicFormat -from manuskript.functions import mainWindow +from manuskript.functions import mainWindow, getSaveFileNameWithSuffix from manuskript.models import outlineItem from manuskript.ui.exporters.manuskript.plainTextSettings import exporterSettings import codecs @@ -24,6 +24,7 @@ class plainText(basicFormat): # Default settings used in self.getExportFilename. For easy subclassing when exporting plaintext. exportVarName = "lastPlainText" exportFilter = "Text files (*.txt);; Any files (*)" + exportDefaultSuffix = ".txt" # qt ignores the period, but it is clearer in our code to have it def __init__(self): pass @@ -64,28 +65,16 @@ class plainText(basicFormat): else: filename = "" - filename, filter = QFileDialog.getSaveFileName(settingsWidget.parent(), - caption=qApp.translate("Export", "Choose output file…"), - filter=filter, - directory=filename) + filename, filter = getSaveFileNameWithSuffix(settingsWidget.parent(), + caption=qApp.translate("Export", "Choose output file…"), + filter=filter, + directory=filename, + defaultSuffix=self.exportDefaultSuffix) if filename: s[varName] = filename settingsWidget.settings["Output"] = s - # Auto adds extension if necessary - try: - # Extract the extension from "Some name (*.ext)" - ext = filter.split("(")[1].split(")")[0] - ext = ext.split(".")[1] - if " " in ext: # In case there are multiple extensions: "Images (*.png *.jpg)" - ext = ext.split(" ")[0] - except: - ext = "" - - if ext and filename[-len(ext)-1:] != ".{}".format(ext): - filename += "." + ext - # Save settings settingsWidget.writeSettings() diff --git a/manuskript/exporter/pandoc/HTML.py b/manuskript/exporter/pandoc/HTML.py index c67f56b..4204441 100644 --- a/manuskript/exporter/pandoc/HTML.py +++ b/manuskript/exporter/pandoc/HTML.py @@ -16,6 +16,7 @@ class HTML(abstractPlainText): exportVarName = "lastPandocHTML" toFormat = "html" exportFilter = "HTML files (*.html);; Any files (*)" + exportDefaultSuffix = ".html" requires = { "Settings": True, "Preview": True, diff --git a/manuskript/exporter/pandoc/PDF.py b/manuskript/exporter/pandoc/PDF.py index cbb135c..5adf485 100644 --- a/manuskript/exporter/pandoc/PDF.py +++ b/manuskript/exporter/pandoc/PDF.py @@ -23,6 +23,7 @@ class PDF(abstractOutput): exportVarName = "lastPandocPDF" toFormat = "pdf" exportFilter = "PDF files (*.pdf);; Any files (*)" + exportDefaultSuffix = ".pdf" requires = { "Settings": True, "Preview": True, diff --git a/manuskript/exporter/pandoc/abstractOutput.py b/manuskript/exporter/pandoc/abstractOutput.py index 880d319..506b0fa 100644 --- a/manuskript/exporter/pandoc/abstractOutput.py +++ b/manuskript/exporter/pandoc/abstractOutput.py @@ -10,6 +10,7 @@ class abstractOutput(abstractPlainText): toFormat = "SUBCLASSME" icon = "SUBCLASSME" exportFilter = "SUBCLASSME" + exportDefaultSuffix = ".SUBCLASSME" requires = { "Settings": True, "Preview": False, diff --git a/manuskript/exporter/pandoc/abstractPlainText.py b/manuskript/exporter/pandoc/abstractPlainText.py index 1fd277e..5cfea99 100644 --- a/manuskript/exporter/pandoc/abstractPlainText.py +++ b/manuskript/exporter/pandoc/abstractPlainText.py @@ -16,6 +16,7 @@ class abstractPlainText(markdown): toFormat = "SUBCLASSME" icon = "SUBCLASSME" exportFilter = "SUBCLASSME" + exportDefaultSuffix = ".SUBCLASSME" def __init__(self, exporter): self.exporter = exporter diff --git a/manuskript/exporter/pandoc/outputFormats.py b/manuskript/exporter/pandoc/outputFormats.py index fb59401..9fa00a8 100644 --- a/manuskript/exporter/pandoc/outputFormats.py +++ b/manuskript/exporter/pandoc/outputFormats.py @@ -13,6 +13,7 @@ class ePub(abstractOutput): exportVarName = "lastPandocePub" toFormat = "epub" exportFilter = "ePub files (*.epub);; Any files (*)" + exportDefaultSuffix = ".epub" class OpenDocument(abstractOutput): @@ -23,6 +24,7 @@ class OpenDocument(abstractOutput): toFormat = "odt" icon = "application-vnd.oasis.opendocument.text" exportFilter = "OpenDocument files (*.odt);; Any files (*)" + exportDefaultSuffix = ".odt" class DocX(abstractOutput): @@ -33,4 +35,5 @@ class DocX(abstractOutput): toFormat = "docx" icon = "application-vnd.openxmlformats-officedocument.wordprocessingml.document" exportFilter = "DocX files (*.docx);; Any files (*)" + exportDefaultSuffix = ".docx" diff --git a/manuskript/exporter/pandoc/plainText.py b/manuskript/exporter/pandoc/plainText.py index 501789b..be4eb94 100644 --- a/manuskript/exporter/pandoc/plainText.py +++ b/manuskript/exporter/pandoc/plainText.py @@ -14,6 +14,7 @@ class markdown(abstractPlainText): exportVarName = "lastPandocMarkdown" toFormat = "markdown" exportFilter = "Markdown files (*.md);; Any files (*)" + exportDefaultSuffix = ".md" class reST(abstractPlainText): @@ -24,6 +25,7 @@ class reST(abstractPlainText): toFormat = "rst" icon = "text-plain" exportFilter = "reST files (*.rst);; Any files (*)" + exportDefaultSuffix = ".rst" class latex(abstractPlainText): @@ -35,6 +37,7 @@ class latex(abstractPlainText): toFormat = "latex" icon = "text-x-tex" exportFilter = "Tex files (*.tex);; Any files (*)" + exportDefaultSuffix = ".tex" class OPML(abstractPlainText): @@ -47,5 +50,5 @@ class OPML(abstractPlainText): toFormat = "opml" icon = "text-x-opml+xml" exportFilter = "OPML files (*.opml);; Any files (*)" - + exportDefaultSuffix = ".opml" diff --git a/manuskript/functions/__init__.py b/manuskript/functions/__init__.py index 8b4006e..51ee43b 100644 --- a/manuskript/functions/__init__.py +++ b/manuskript/functions/__init__.py @@ -9,7 +9,7 @@ from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject, QRegExp, QDir from PyQt5.QtCore import QUrl, QTimer from PyQt5.QtGui import QBrush, QIcon, QPainter, QColor, QImage, QPixmap from PyQt5.QtGui import QDesktopServices -from PyQt5.QtWidgets import qApp, QTextEdit +from PyQt5.QtWidgets import qApp, QFileDialog, QTextEdit from manuskript.enums import Outline @@ -386,6 +386,27 @@ def openURL(url): """ QDesktopServices.openUrl(QUrl(url)) +def getSaveFileNameWithSuffix(parent, caption, directory, filter, options=None, selectedFilter=None, defaultSuffix=None): + """ + A reimplemented version of QFileDialog.getSaveFileName() because we would like to make use + of the QFileDialog.defaultSuffix property that getSaveFileName() does not let us adjust. + + Note: knowing the selected filter is not an invitation to change the chosen filename later. + """ + dialog = QFileDialog(parent=parent, caption=caption, directory=directory, filter=filter) + if options: + dialog.setOptions(options) + if defaultSuffix: + dialog.setDefaultSuffix(defaultSuffix) + dialog.setFileMode(QFileDialog.AnyFile) + dialog.setSupportedSchemes(("file",)) + dialog.setAcceptMode(QFileDialog.AcceptSave) + if selectedFilter: + dialog.selectNameFilter(selectedFilter) + if (dialog.exec() == QFileDialog.Accepted): + return dialog.selectedFiles()[0], dialog.selectedNameFilter() + return None, None + def inspect(): """ Debugging tool. Call it to see a stack of calls up to that point.