diff --git a/nw/common.py b/nw/common.py index 872365df..e3454931 100644 --- a/nw/common.py +++ b/nw/common.py @@ -227,3 +227,12 @@ def fuzzyTime(secDiff): return "%d years ago" % int(round(secDiff/31557600)) return "beyond time and space" + +def makeFileNameSafe(theText): + """Returns a filename safe version of the text. + """ + cleanName = "" + for c in theText.strip(): + if c.isalpha() or c.isdigit() or c == " ": + cleanName += c + return cleanName diff --git a/nw/core/project.py b/nw/core/project.py index 496e8bc4..f35c007a 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -41,7 +41,9 @@ from nw.core.item import NWItem from nw.core.document import NWDoc from nw.core.status import NWStatus from nw.core.options import OptionState -from nw.common import checkString, checkBool, checkInt, formatTimeStamp +from nw.common import ( + checkString, checkBool, checkInt, formatTimeStamp, makeFileNameSafe +) from nw.constants import ( nwFiles, nwItemType, nwItemClass, nwItemLayout, nwAlert ) @@ -667,7 +669,7 @@ class NWProject(): ), nwAlert.ERROR) return False - cleanName = self.getFileSafeProjectName() + cleanName = makeFileNameSafe(self.projName) baseDir = path.abspath(path.join(self.mainConf.backupPath, cleanName)) if not path.isdir(baseDir): try: @@ -891,15 +893,6 @@ class NWProject(): # Getters ## - def getFileSafeProjectName(self): - """Returns a filename safe version of the project name. - """ - cleanName = "" - for c in self.projName.strip(): - if c.isalpha() or c.isdigit() or c == " ": - cleanName += c - return cleanName - def getSessionWordCount(self): """Returns the number of words added or removed this session. """ diff --git a/nw/gui/build.py b/nw/gui/build.py index a9fa5571..cee6c9fa 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -44,7 +44,7 @@ from PyQt5.QtWidgets import ( QFileDialog, QFontDialog, QSpinBox ) -from nw.common import fuzzyTime +from nw.common import fuzzyTime, makeFileNameSafe from nw.gui.custom import QSwitch from nw.core import ToHtml from nw.constants import ( @@ -623,7 +623,7 @@ class GuiBuildNovel(QDialog): # Generate the file name if fileExt: - cleanName = self.theProject.getFileSafeProjectName() + cleanName = makeFileNameSafe(self.theProject.projName) fileName = "%s.%s" % (cleanName, fileExt) saveDir = self.mainConf.lastPath savePath = path.join(saveDir, fileName) diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py index 0882b089..f0242edb 100644 --- a/nw/gui/preferences.py +++ b/nw/gui/preferences.py @@ -331,7 +331,6 @@ class GuiConfigEditGeneralTab(QWidget): def _backupFolder(self): """Open a dialog to select the backup folder. """ - currDir = self.backupPath if not path.isdir(currDir): currDir = "" diff --git a/nw/gui/projload.py b/nw/gui/projload.py index 738a493c..30332690 100644 --- a/nw/gui/projload.py +++ b/nw/gui/projload.py @@ -171,8 +171,7 @@ class GuiProjectLoad(QDialog): return def _doBrowse(self): - """Close the dialog window with no selected path, triggering the - project browser dialog. + """Browse for a folder path. """ logger.verbose("GuiProjectLoad browse button clicked") if self.mainConf.showGUI: diff --git a/nw/gui/projwizard.py b/nw/gui/projwizard.py index f6c5c4c4..d606ed23 100644 --- a/nw/gui/projwizard.py +++ b/nw/gui/projwizard.py @@ -28,12 +28,17 @@ import logging import nw +from os import path + from PyQt5.QtGui import QPixmap, QColor from PyQt5.QtWidgets import ( QWizard, QWizardPage, QLabel, QVBoxLayout, QLineEdit, QPlainTextEdit, - QFormLayout + QPushButton, QFileDialog, QHBoxLayout ) +from nw.common import makeFileNameSafe +from nw.gui.custom import QSwitch, QConfigLayout + logger = logging.getLogger(__name__) class GuiProjectWizard(QWizard): @@ -57,8 +62,11 @@ class GuiProjectWizard(QWizard): # self.setWizardStyle(QWizard.ModernStyle) self.setPixmap(QWizard.WatermarkPixmap, self.sideImage) - self.introPage = ProjWizardIntroPage() + self.introPage = ProjWizardIntroPage(self.theParent) + self.storagePage = ProjWizardFolderPage(self.theParent) + self.addPage(self.introPage) + self.addPage(self.storagePage) self.setOption(QWizard.NoBackButtonOnStartPage, True) # self.setOption(QWizard.ExtendedWatermarkPixmap, True) @@ -71,10 +79,12 @@ class GuiProjectWizard(QWizard): class ProjWizardIntroPage(QWizardPage): - def __init__(self): + def __init__(self, theParent): QWizardPage.__init__(self) - self.mainConf = nw.CONFIG + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theTheme = theParent.theTheme self.setTitle("Create New Project") self.theText = QLabel( @@ -85,11 +95,12 @@ class ProjWizardIntroPage(QWizardPage): ) self.theText.setWordWrap(True) - xW = self.mainConf.pxInt(250) + xW = self.mainConf.pxInt(300) xH = self.mainConf.pxInt(100) vS = self.mainConf.pxInt(12) fS = self.mainConf.pxInt(4) + # The Page Form self.projName = QLineEdit() self.projName.setMaxLength(200) self.projName.setFixedWidth(xW) @@ -105,12 +116,16 @@ class ProjWizardIntroPage(QWizardPage): self.projAuthors.setFixedWidth(xW) self.projAuthors.setPlaceholderText("Optional. One name per line.") - self.mainForm = QFormLayout() - self.mainForm.addRow("Working title", self.projName) - self.mainForm.addRow("Novel title", self.projTitle) - self.mainForm.addRow("Author(s)", self.projAuthors) + self.mainForm = QConfigLayout() + self.mainForm.addRow("Working Title", self.projName) + self.mainForm.addRow("Novel Title", self.projTitle) + self.mainForm.addRow("Author(s)", self.projAuthors) self.mainForm.setVerticalSpacing(fS) + self.registerField("projName*", self.projName) + self.registerField("projTitle", self.projTitle) + self.registerField("projAuthors", self.projAuthors) + # Assemble self.outerBox = QVBoxLayout() self.outerBox.setSpacing(vS) @@ -122,3 +137,77 @@ class ProjWizardIntroPage(QWizardPage): return # END Class ProjWizardIntroPage + +class ProjWizardFolderPage(QWizardPage): + + def __init__(self, theParent): + QWizardPage.__init__(self) + + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theTheme = theParent.theTheme + + self.setTitle("Select Project Folder") + self.theText = QLabel( + "Select a location to store the project. A new project folder " + "will be created in the selected location." + ) + self.theText.setWordWrap(True) + + xW = self.mainConf.pxInt(300) + # xH = self.mainConf.pxInt(100) + vS = self.mainConf.pxInt(12) + fS = self.mainConf.pxInt(4) + + self.projPath = QLineEdit("") + self.projPath.setFixedWidth(xW) + self.projPath.setPlaceholderText("Required") + + self.browseButton = QPushButton("...") + self.browseButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("..."))) + self.browseButton.clicked.connect(self._doBrowse) + + self.mainForm = QConfigLayout() + self.mainForm.addRow("Project Path", self.projPath, theButton=self.browseButton) + self.mainForm.setVerticalSpacing(fS) + + self.registerField("projPath*", self.projPath) + + # Assemble + self.outerBox = QVBoxLayout() + self.outerBox.setSpacing(vS) + self.outerBox.addWidget(self.theText) + self.outerBox.addLayout(self.mainForm) + self.outerBox.addStretch(1) + self.setLayout(self.outerBox) + + return + + ## + # Slots + ## + + def _doBrowse(self): + """Select a project folder. + """ + lastPath = self.mainConf.lastPath + if not path.isdir(lastPath): + lastPath = "" + + dlgOpt = QFileDialog.Options() + dlgOpt |= QFileDialog.ShowDirsOnly + dlgOpt |= QFileDialog.DontUseNativeDialog + projDir = QFileDialog.getExistingDirectory( + self,"Select Project Folder", lastPath, options=dlgOpt + ) + if projDir: + projName = self.field("projName") + if projName is not None: + fullDir = path.join(path.abspath(projDir), makeFileNameSafe(projName)) + self.projPath.setText(fullDir) + else: + self.projPath.setText("") + + return + +# END Class ProjWizardFolderPage