diff --git a/.gitignore b/.gitignore index 1cc84316..a515c603 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ novelWriter.qhc __pycache__ # Sample Project +/nw/assets/sample.zip /sample/cache /sample/meta *.bak diff --git a/nw/core/project.py b/nw/core/project.py index d92e9d74..8c4d1b8e 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -32,7 +32,7 @@ import nw from os import path, mkdir, listdir, unlink, rename, rmdir from lxml import etree from time import time -from shutil import make_archive +from shutil import make_archive, unpack_archive, copyfile from PyQt5.QtWidgets import QMessageBox @@ -175,6 +175,21 @@ class NWProject(): """Create a new project by populating the project tree with a few starter items. """ + popMinimal = True + popCustom = False + popSample = False + if "popMinimal" in projData: + popMinimal = projData["popMinimal"] + if "popCustom" in projData: + popCustom = projData["popCustom"] + if "popSample" in projData: + popSample = projData["popSample"] + + # Check if we're extracting the sample project. + # This is handled differently as it isn't actually a new project. + if popSample: + return self.extractSampleProject(projData) + # Project Title and Authors if "projName" in projData: self.setProjectName(projData["projName"]) @@ -185,17 +200,9 @@ class NWProject(): if "projAuthors" in projData: self.setBookAuthors(projData["projAuthors"]) - popMinimal = True - popSample = False - popCustom = False - if "popMinimal" in projData: - popMinimal = projData["popMinimal"] - if "popSample" in projData: - popSample = projData["popSample"] - if "popCustom" in projData: - popCustom = projData["popCustom"] - if popMinimal: + # Creating a minimal project with a few root folders, a + # single chapter folder with a single file. hNovel = self.newRoot("Novel", nwItemClass.NOVEL) hChars = self.newRoot("Characters", nwItemClass.CHARACTER) hWorld = self.newRoot("Plot", nwItemClass.PLOT) @@ -203,10 +210,11 @@ class NWProject(): hChapF = self.newFolder("New Chapter", nwItemClass.NOVEL, hNovel) hScene = self.newFile("New Scene", nwItemClass.NOVEL, hChapF) - elif popSample: - pass - elif popCustom: + # Create a project structure based on selected root folders + # and a number of chapters and scenes selected in the + # wizard's custom page. + # Create Root Folders hNovel = self.newRoot("Novel", nwItemClass.NOVEL) if "addRoots" in projData: @@ -255,12 +263,72 @@ class NWProject(): # Fallback just in case. self.newRoot("Novel", nwItemClass.NOVEL) + # Finalise self.projOpened = time() self.setProjectChanged(True) self.saveProject(autoSave=True) return True + def extractSampleProject(self, projData): + """Make a copy of the sample project. + First, try to copy the content of the sample folder to the new + project path, or if the folder doesn't exist, look for the zip + file in the assets folder. + """ + if "projName" in projData: + projName = projData["projName"] + else: + projName = "Sample Project" + + if "projPath" in projData: + projPath = projData["projPath"] + else: + logger.error("No project path set for the example project") + return False + + srcSample = path.abspath(path.join(self.mainConf.appRoot, "sample")) + pkgSample = path.join(self.mainConf.assetPath, "sample.zip") + + isSuccess = False + if path.isfile(pkgSample): + + try: + unpack_archive(pkgSample, projPath) + isSuccess = True + except Exception as e: + self.makeAlert( + ["Failed to create a new example project.", str(e)], nwAlert.ERROR + ) + + elif path.isdir(srcSample): + + try: + srcProj = path.join(srcSample, nwFiles.PROJ_FILE) + dstProj = path.join(projPath, nwFiles.PROJ_FILE) + copyfile(srcProj, dstProj) + + srcContent = path.join(srcSample, "content") + dstContent = path.join(projPath, "content") + for srcFile in listdir(srcContent): + srcDoc = path.join(srcContent, srcFile) + dstDoc = path.join(dstContent, srcFile) + copyfile(srcDoc, dstDoc) + + isSuccess = True + + except Exception as e: + self.makeAlert( + ["Failed to create a new example project.", str(e)], nwAlert.ERROR + ) + + if isSuccess: + self.clearProject() + self.theParent.openProject(projPath) + self.theParent.rebuildIndex() + + return isSuccess + def clearProject(self): """Clear the data for the current project, and set them to default values. diff --git a/nw/gui/projwizard.py b/nw/gui/projwizard.py index 17470587..7460dbe2 100644 --- a/nw/gui/projwizard.py +++ b/nw/gui/projwizard.py @@ -248,20 +248,20 @@ class ProjWizardPopulatePage(QWizardPage): vS = self.mainConf.pxInt(12) fS = self.mainConf.pxInt(4) - self.popSample = QRadioButton("Fill the project with sample files") self.popMinimal = QRadioButton("Fill the project with a minimal set of items") self.popCustom = QRadioButton("Show detailed options for filling the project") + self.popSample = QRadioButton("Fill the project with example files") self.popMinimal.setChecked(True) self.popBox = QVBoxLayout() self.popBox.setSpacing(fS) self.popBox.addWidget(self.popMinimal) - self.popBox.addWidget(self.popSample) self.popBox.addWidget(self.popCustom) + self.popBox.addWidget(self.popSample) - self.registerField("popSample", self.popSample) self.registerField("popMinimal", self.popMinimal) self.registerField("popCustom", self.popCustom) + self.registerField("popSample", self.popSample) # Assemble self.outerBox = QVBoxLayout() diff --git a/nw/guimain.py b/nw/guimain.py index ddcaa7ba..f59deeb0 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -293,11 +293,14 @@ class GuiMain(QMainWindow): logger.info("Creating new project") if self.theProject.setProjectPath(projPath, newProject=True): - self.theProject.newProject(projData) - self.rebuildTree() - self.saveProject() - self.hasProject = True - self.statusBar.setRefTime(self.theProject.projOpened) + if self.theProject.newProject(projData): + self.rebuildTree() + self.saveProject() + self.hasProject = True + self.statusBar.setRefTime(self.theProject.projOpened) + else: + self.theProject.clearProject() + return False else: self.theProject.clearProject() return False