diff --git a/nw/core/project.py b/nw/core/project.py index 3eb1850c..36589f3c 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -171,179 +171,6 @@ class NWProject(): # Project Methods ## - def newProject(self, projData={}): - """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) - - # Set the project path - if "projPath" in projData: - self.setProjectPath(projData["projPath"], newProject=True) - else: - logger.error("No project path set for the new project") - return False - - # Project Title and Authors - if "projName" in projData: - self.setProjectName(projData["projName"]) - else: - self.setProjectName("New Project") - if "projTitle" in projData: - self.setBookTitle(projData["projTitle"]) - if "projAuthors" in projData: - self.setBookAuthors(projData["projAuthors"]) - - 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) - hWorld = self.newRoot("World", nwItemClass.WORLD) - hChapF = self.newFolder("New Chapter", nwItemClass.NOVEL, hNovel) - hScene = self.newFile("New Scene", nwItemClass.NOVEL, hChapF) - - 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: - for newRoot in projData["addRoots"]: - if newRoot in nwItemClass: - self.newRoot(nwLabels.CLASS_NAME[newRoot], newRoot) - - # Create Chapters and Scenes - numChapters = 0 - numScenes = 0 - chFolders = False - if "numChapters" in projData: - numChapters = projData["numChapters"] - if "numScenes" in projData: - numScenes = projData["numScenes"] - if "chFolders" in projData: - chFolders = projData["chFolders"] - - # Create Chapters - if numChapters > 0: - for ch in range(numChapters): - chTitle = "Chapter %d" % (ch+1) - hParDir = hNovel - if chFolders: - hParDir = self.newFolder(chTitle, nwItemClass.NOVEL, hNovel) - hChapD = self.newFile(chTitle, nwItemClass.NOVEL, hParDir) - chItem = self.projTree[hChapD] - if hChapD is None: - logger.error("Something went wrong when creating chapter file.") - continue - chItem.setLayout(nwItemLayout.CHAPTER) - - # Create Chapter > Scenes - if numScenes > 0: - for sc in range(numScenes): - scTitle = "Scene %d.%d" % (ch+1, sc+1) - hScene = self.newFile(scTitle, nwItemClass.NOVEL, hParDir) - - # Create Scenes (No Chapters) - elif numScenes > 0: - for sc in range(numScenes): - scTitle = "Scene %d" % (sc+1) - hScene = self.newFile(scTitle, nwItemClass.NOVEL, hNovel) - - else: - # 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): - - self.setProjectPath(projPath, newProject=True) - 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): - - self.setProjectPath(projPath, newProject=True) - 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 - ) - - else: - self.makeAlert(( - "Failed to create a new example project. " - "Could not find the necessary files." - ), 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. @@ -379,15 +206,15 @@ class NWProject(): self.spellCheck = False self.autoOutline = True self.statusItems = NWStatus() - self.statusItems.addEntry("New", (100,100,100)) - self.statusItems.addEntry("Note", (200, 50, 0)) - self.statusItems.addEntry("Draft", (200,150, 0)) - self.statusItems.addEntry("Finished",( 50,200, 0)) + self.statusItems.addEntry("New", (100, 100, 100)) + self.statusItems.addEntry("Note", (200, 50, 0)) + self.statusItems.addEntry("Draft", (200, 150, 0)) + self.statusItems.addEntry("Finished",( 50, 200, 0)) self.importItems = NWStatus() - self.importItems.addEntry("New", (100,100,100)) - self.importItems.addEntry("Minor", (200, 50, 0)) - self.importItems.addEntry("Major", (200,150, 0)) - self.importItems.addEntry("Main", ( 50,200, 0)) + self.importItems.addEntry("New", (100, 100, 100)) + self.importItems.addEntry("Minor", (200, 50, 0)) + self.importItems.addEntry("Major", (200, 150, 0)) + self.importItems.addEntry("Main", ( 50, 200, 0)) self.lastEdited = None self.lastViewed = None self.lastWCount = 0 @@ -397,6 +224,104 @@ class NWProject(): return + def newProject(self, projData={}): + """Create a new project by populating the project tree with a + few starter items. + """ + popMinimal = projData.get("popMinimal", True) + popCustom = projData.get("popCustom", False) + popSample = projData.get("popSample", False) + + # Check if we're extracting the sample project. This is handled + # differently as it isn't actually a new project, so we forward + # this to another function and return here. + if popSample: + return self.extractSampleProject(projData) + + # Project Settings + projPath = projData.get("projPath", None) + projName = projData.get("projName", "New Project") + projTitle = projData.get("projTitle", "") + projAuthors = projData.get("projAuthors", "") + + if projPath is None: + logger.error("No project path set for the new project") + return False + + if not self.setProjectPath(projPath, newProject=True): + return False + + self.setProjectName(projName) + self.setBookTitle(projTitle) + self.setBookAuthors(projAuthors) + + if popMinimal: + # Creating a minimal project with a few root folders and a + # single chapter folder with a single file. + nHandle = self.newRoot("Novel", nwItemClass.NOVEL) + xHandle = self.newRoot("Plot", nwItemClass.PLOT) + xHandle = self.newRoot("Characters", nwItemClass.CHARACTER) + xHandle = self.newRoot("World", nwItemClass.WORLD) + tHandle = self.newFile("Title Page", nwItemClass.NOVEL, nHandle) + cHandle = self.newFolder("New Chapter", nwItemClass.NOVEL, nHandle) + fHandle = self.newFile("New Chapter", nwItemClass.NOVEL, cHandle) + XHandle = self.newFile("New Scene", nwItemClass.NOVEL, cHandle) + self.projTree.setFileItemLayout(tHandle, nwItemLayout.TITLE) + self.projTree.setFileItemLayout(fHandle, nwItemLayout.CHAPTER) + + 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 + nHandle = self.newRoot("Novel", nwItemClass.NOVEL) + for newRoot in projData.get("addRoots", []): + if newRoot in nwItemClass: + self.newRoot(nwLabels.CLASS_NAME[newRoot], newRoot) + + # Create a title page + tHandle = self.newFile("Title Page", nwItemClass.NOVEL, nHandle) + self.projTree.setFileItemLayout(tHandle, nwItemLayout.TITLE) + + # Create chapters and scenes + numChapters = projData.get("numChapters", 0) + numScenes = projData.get("numScenes", 0) + chFolders = projData.get("chFolders", False) + + # Create chapters + if numChapters > 0: + for ch in range(numChapters): + chTitle = "Chapter %d" % (ch+1) + pHandle = nHandle + if chFolders: + pHandle = self.newFolder(chTitle, nwItemClass.NOVEL, nHandle) + cHandle = self.newFile(chTitle, nwItemClass.NOVEL, pHandle) + self.projTree.setFileItemLayout(cHandle, nwItemLayout.CHAPTER) + + # Create chapter scenes + if numScenes > 0: + for sc in range(numScenes): + scTitle = "Scene %d.%d" % (ch+1, sc+1) + sHandle = self.newFile(scTitle, nwItemClass.NOVEL, pHandle) + + # Create scenes (no chapters) + elif numScenes > 0: + for sc in range(numScenes): + scTitle = "Scene %d" % (sc+1) + sHandle = self.newFile(scTitle, nwItemClass.NOVEL, nHandle) + + else: + # Fallback just in case. We shouldn't reach here. + self.newRoot("Novel", nwItemClass.NOVEL) + + # Finalise + self.projOpened = time() + self.setProjectChanged(True) + self.saveProject(autoSave=True) + + return True + def openProject(self, fileName, overrideLock=False): """Open the project file provided, or if doesn't exist, assume it is a folder, and look for the file within it. If successful, @@ -716,9 +641,9 @@ class NWProject(): if len(aKey) > 0: self._packProjectValue(xTitleFmt, aKey, aValue) - xStatus = etree.SubElement(xSettings,"status") + xStatus = etree.SubElement(xSettings, "status") self.statusItems.packEntries(xStatus) - xStatus = etree.SubElement(xSettings,"importance") + xStatus = etree.SubElement(xSettings, "importance") self.importItems.packEntries(xStatus) # Save Tree Content @@ -733,8 +658,8 @@ class NWProject(): with open(tempFile, mode="wb") as outFile: outFile.write(etree.tostring( nwXML, - pretty_print = True, - encoding = "utf-8", + pretty_print = True, + encoding = "utf-8", xml_declaration = True )) except Exception as e: @@ -794,7 +719,7 @@ class NWProject(): return True ## - # Backup Project + # Zip/Unzip Project ## def zipIt(self, doNotify): @@ -870,6 +795,68 @@ class NWProject(): 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. + """ + projName = projData.get("projName", "Sample Project") + projPath = projData.get("projPath", None) + if projPath is None: + 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): + + self.setProjectPath(projPath, newProject=True) + 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): + + self.setProjectPath(projPath, newProject=True) + 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 + ) + + else: + self.makeAlert(( + "Failed to create a new example project. " + "Could not find the necessary files." + ), nwAlert.ERROR) + + if isSuccess: + self.clearProject() + self.theParent.openProject(projPath) + self.theParent.rebuildIndex() + + return isSuccess + ## # Setters ## diff --git a/nw/core/tree.py b/nw/core/tree.py index e786a727..bafb5306 100644 --- a/nw/core/tree.py +++ b/nw/core/tree.py @@ -314,6 +314,22 @@ class NWTree(): self._handleSeed = theSeed return + def setFileItemLayout(self, tHandle, itemLayout): + """Set the nwItemLayout for a specific file. + """ + tItem = self.__getitem__(tHandle) + if tItem is None: + return False + if tItem.itemType != nwItemType.FILE: + logger.error("Item '%s' is not a file" % tHandle) + return False + if not isinstance(itemLayout, nwItemLayout): + return False + + tItem.setLayout(itemLayout) + + return True + ## # Getters ## diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index 124e0a59..ce7bf499 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -192,7 +192,7 @@ class GuiProjectTree(QTreeWidget): nHandle = pHandle pHandle = pItem.parHandle - # If we again has no home, give up + # If we again have no home, give up if pHandle is None: self.makeAlert( "Did not find anywhere to add the file or folder!", nwAlert.ERROR diff --git a/nw/gui/projwizard.py b/nw/gui/projwizard.py index 7460dbe2..40f014ec 100644 --- a/nw/gui/projwizard.py +++ b/nw/gui/projwizard.py @@ -324,6 +324,7 @@ class ProjWizardCustomPage(QWizardPage): self.addPlot.setChecked(True) self.addChar.setChecked(True) + self.addWorld.setChecked(True) self.rootForm.addWidget(self.lblPlot, 0, 0) self.rootForm.addWidget(self.lblChar, 1, 0) diff --git a/nw/guimain.py b/nw/guimain.py index 0a67ccfb..12a17c30 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -281,10 +281,7 @@ class GuiMain(QMainWindow): if projData is None: return False - projPath = None - if "projPath" in projData: - projPath = projData["projPath"] - + projPath = projData.get("projPath", None) if projPath is None or projData is None: logger.error("No projData or projPath set") return False