diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 43aaa621..b16a7928 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -65,24 +65,14 @@ class GuiDocEditor(QTextEdit): # Editor State self.hasSelection = False + self.setMinimumWidth(300) + self.setAcceptRichText(False) - if self.mainConf.textFixedW: - self.setLineWrapMode(QTextEdit.FixedPixelWidth) - self.setLineWrapColumnOrWidth(self.mainConf.textWidth) - else: - mTB = self.mainConf.textMargin[0] - mLR = self.mainConf.textMargin[1] - self.setViewportMargins(mLR,mTB,mLR,mTB) + self.clearEditor() + self.initEditor() self.theDoc.setDocumentMargin(0) self.theDoc.contentsChange.connect(self._docChange) - if self.mainConf.doJustify: - self.theDoc.setDefaultTextOption(QTextOption(Qt.AlignJustify)) - - self.setReadOnly(True) - self.setMinimumWidth(300) - self.setAcceptRichText(False) - self.setFontPointSize(self.mainConf.textSize) # Custom Shortcuts QShortcut(QKeySequence("Ctrl+."), self, context=Qt.WidgetShortcut, activated=self._openSpellContext) @@ -100,6 +90,24 @@ class GuiDocEditor(QTextEdit): return + def clearEditor(self): + self.setReadOnly(True) + self.clear() + return True + + def initEditor(self): + self.setFontPointSize(self.mainConf.textSize) + if self.mainConf.textFixedW: + self.setLineWrapMode(QTextEdit.FixedPixelWidth) + self.setLineWrapColumnOrWidth(self.mainConf.textWidth) + else: + mTB = self.mainConf.textMargin[0] + mLR = self.mainConf.textMargin[1] + self.setViewportMargins(mLR,mTB,mLR,mTB) + if self.mainConf.doJustify: + self.theDoc.setDefaultTextOption(QTextOption(Qt.AlignJustify)) + return True + ## # Setters and Getters ## diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index f179d140..4f4aaa3d 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -44,4 +44,8 @@ class GuiDocViewer(QTextBrowser): return + def clearViewer(self): + self.clear() + return True + # END Class GuiDocViewer diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index ec8a8060..b3b3117e 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -137,7 +137,7 @@ class GuiMainMenu(QMenuBar): # Project > New Project menuItem = QAction(QIcon.fromTheme("folder-new"), "New Project", self) menuItem.setStatusTip("Create New Project") - menuItem.triggered.connect(self.theParent.newProject) + menuItem.triggered.connect(lambda : self.theParent.newProject(None)) self.projMenu.addAction(menuItem) # Project > Open Project @@ -154,6 +154,12 @@ class GuiMainMenu(QMenuBar): menuItem.triggered.connect(self.theParent.saveProject) self.projMenu.addAction(menuItem) + # Project > Close Project + menuItem = QAction("Close Project", self) + menuItem.setStatusTip("Close Project") + menuItem.triggered.connect(lambda : self.theParent.closeProject(False)) + self.projMenu.addAction(menuItem) + # Project > Recent Projects self.recentMenu = self.projMenu.addMenu(QIcon.fromTheme("document-open-recent"),"Recent Projects") self.updateRecentProjects() diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 20a0aa50..bd0bcfe4 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -46,6 +46,7 @@ class GuiMain(QMainWindow): self.theTheme = Theme() self.theProject = NWProject(self) self.theDocument = NWDoc(self.theProject, self) + self.hasProject = False self.resize(*self.mainConf.winGeometry) self._setWindowTitle() @@ -95,12 +96,10 @@ class GuiMain(QMainWindow): self.docViewer.setVisible(False) - # Build GUI Elements + # Build The Tree View self.treeView.itemSelectionChanged.connect(self._treeSingleClick) self.treeView.itemDoubleClicked.connect(self._treeDoubleClick) - self.treeView.buildTree() - self._makeStatusIcons() - self._makeImportIcons() + self.rebuildTree() # Set Main Window Elements self.setMenuBar(self.mainMenu) @@ -110,11 +109,13 @@ class GuiMain(QMainWindow): # Load Theme StyleSheet self.setStyleSheet(self.theTheme.cssData) + # Set Up Autosaving Project Timer self.asProjTimer = QTimer() self.asProjTimer.setInterval(int(self.mainConf.autoSaveProj*1000)) self.asProjTimer.timeout.connect(self._autoSaveProject) self.asProjTimer.start() + # Set Up Autosaving Document Timer self.asDocTimer = QTimer() self.asDocTimer.setInterval(int(self.mainConf.autoSaveDoc*1000)) self.asDocTimer.timeout.connect(self._autoSaveDocument) @@ -167,50 +168,127 @@ class GuiMain(QMainWindow): return + def clearGUI(self): + self.treeView.clearTree() + self.docEditor.clearEditor() + self.docViewer.clearViewer() + self.docViewer.setVisible(False) + return True + ## # Project Actions ## - def newProject(self): + def newProject(self, projPath=None, forceNew=False): + + if self.hasProject: + msgBox = QMessageBox() + msgRes = msgBox.warning( + self, "New Project", + "Please close the current project
before making a new one." + ) + return False + + if projPath is None: + projPath = self.newProjectDialog() + if projPath is None: + return False + + if path.isfile(path.join(projPath,self.theProject.projFile)) and not forceNew: + msgBox = QMessageBox() + msgRes = msgBox.critical( + self, "New Project", + "A project already exists in that location.
Please choose another folder." + ) + return False + logger.info("Creating new project") - self.treeView.clearTree() - if self.saveProject(): - self.theProject.newProject() - self.treeView.buildTree() + self.theProject.newProject() + self.theProject.setProjectPath(projPath) + self.rebuildTree() + self.saveProject() + self.hasProject = True + return True + def closeProject(self, isYes=False): + + if not self.hasProject: + return True + + if not isYes: + msgBox = QMessageBox() + msgRes = msgBox.question( + self, "Close Project", + "Close current project?
Unsaved changes will be saved." + ) + if msgRes != QMessageBox.Yes: + return False + + self.closeDocument() + if self.theProject.projChanged: + saveOK = self.saveProject() + else: + saveOK = True + + if saveOK: + self.theProject.clearProject() + self.clearGUI() + self.hasProject = False + + return saveOK + def openProject(self, projFile=None): + if projFile is None: projFile = self.openProjectDialog() if projFile is None: return False + + if not self.closeProject(): + return False + self.theProject.openProject(projFile) self._setWindowTitle(self.theProject.projName) self.rebuildTree() self.docEditor.setPwl(path.join(self.theProject.projMeta,"wordlist.txt")) self.docEditor.setSpellCheck(self.theProject.spellCheck) self.mainMenu.updateMenu() + if self.theProject.lastEdited is not None: self.openDocument(self.theProject.lastEdited) if self.theProject.lastViewed is not None: self.viewDocument(self.theProject.lastViewed) + + self.hasProject = True + return True def saveProject(self): + if self.theProject.projPath is None: projPath = self.saveProjectDialog() self.theProject.setProjectPath(projPath) + if self.theProject.projPath is None: + return False + self.treeView.saveTreeOrder() self.theProject.saveProject() self.mainMenu.updateRecentProjects() + return True ## # Document Actions ## + def closeDocument(self): + self.saveDocument() + self.theDocument.clearDocument() + return True + def openDocument(self, tHandle): - if self._takeDocumentAction(): + if self.docEditor.docChanged: self.saveDocument() self.docEditor.setText(self.theDocument.openDocument(tHandle)) self.docEditor.setReadOnly(False) @@ -329,6 +407,17 @@ class GuiMain(QMainWindow): return projPath return None + def newProjectDialog(self): + dlgOpt = QFileDialog.Options() + dlgOpt |= QFileDialog.ShowDirsOnly + dlgOpt |= QFileDialog.DontUseNativeDialog + projPath = QFileDialog.getExistingDirectory( + self,"Select Location for New novelWriter Project","",options=dlgOpt + ) + if projPath: + return projPath + return None + def editProjectDialog(self): dlgProj = GuiProjectEditor(self, self.theProject) dlgProj.exec_() diff --git a/nw/project/document.py b/nw/project/document.py index bb8ad4bd..83eaf153 100644 --- a/nw/project/document.py +++ b/nw/project/document.py @@ -36,6 +36,11 @@ class NWDoc(): return + def clearDocument(self): + self.theItem = None + self.docHandle = None + return + def openDocument(self, tHandle): self.docHandle = tHandle diff --git a/nw/project/project.py b/nw/project/project.py index c8d04db6..3c2f68fa 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -120,8 +120,6 @@ class NWProject(): def newProject(self): - self.clearProject() - hNovel = self.newRoot("Novel", nwItemClass.NOVEL) hChars = self.newRoot("Characters", nwItemClass.CHARACTER) hWorld = self.newRoot("Plot", nwItemClass.PLOT) @@ -318,7 +316,10 @@ class NWProject(): ## def setProjectPath(self, projPath): - self.projPath = projPath + if projPath is None or projPath == "": + self.projPath = None + else: + self.projPath = projPath self.setProjectChanged(True) return True diff --git a/sample/sampleNovel/nwProject.nwx b/sample/sampleNovel/nwProject.nwx index 769c4a83..0a81731f 100644 --- a/sample/sampleNovel/nwProject.nwx +++ b/sample/sampleNovel/nwProject.nwx @@ -1,5 +1,5 @@ - + Sample Project Sample Project diff --git a/tests/reference/gui/0_nwProject.nwx b/tests/reference/gui/0_nwProject.nwx new file mode 100644 index 00000000..a42179e9 --- /dev/null +++ b/tests/reference/gui/0_nwProject.nwx @@ -0,0 +1,73 @@ + + + + + + + + False + None + None + + New + Note + Draft + Finished + + + New + Minor + Major + Main + + + + + Novel + ROOT + NOVEL + New + False + + + New Chapter + FOLDER + NOVEL + New + False + + + New Scene + FILE + NOVEL + New + False + SCENE + 0 + 0 + 0 + 0 + + + Characters + ROOT + CHARACTER + New + False + + + Plot + ROOT + PLOT + New + False + + + World + ROOT + WORLD + New + False + + + diff --git a/tests/test_gui.py b/tests/test_gui.py index ac47fdd2..4abe812d 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -23,16 +23,50 @@ def testMainWindows(qtbot, nwTempGUI, nwRef): qtbot.waitForWindowShown(nwGUI) qtbot.wait(stepDelay) - # Create new, save, open project + # Create new, save, close project nwGUI.theProject.handleSeed = 42 - assert nwGUI.theProject.setProjectPath(nwTempGUI) - assert nwGUI.newProject() - assert nwGUI.theProject.setProjectPath(nwTempGUI) + assert nwGUI.newProject(nwTempGUI, True) assert nwGUI.saveProject() + assert nwGUI.closeProject(True) + + assert len(nwGUI.theProject.projTree) == 0 + assert len(nwGUI.theProject.treeOrder) == 0 + assert len(nwGUI.theProject.treeRoots) == 0 + assert nwGUI.theProject.trashRoot is None + assert nwGUI.theProject.projPath is None + assert nwGUI.theProject.projMeta is None + assert nwGUI.theProject.projCache is None + assert nwGUI.theProject.projFile == "nwProject.nwx" + assert nwGUI.theProject.projName == "" + assert nwGUI.theProject.bookTitle == "" + assert len(nwGUI.theProject.bookAuthors) == 0 + assert nwGUI.theProject.spellCheck == False + + # Check the files + projFile = path.join(nwTempGUI,"nwProject.nwx") + assert cmpFiles(projFile, path.join(nwRef,"gui","0_nwProject.nwx"), [2]) qtbot.wait(stepDelay) + + # qtbot.stopForInteraction() + + # Re-open project assert nwGUI.openProject(nwTempGUI) qtbot.wait(stepDelay) + # Check that we loaded the data + assert len(nwGUI.theProject.projTree) == 6 + assert len(nwGUI.theProject.treeOrder) == 6 + assert len(nwGUI.theProject.treeRoots) == 4 + assert nwGUI.theProject.trashRoot is None + assert nwGUI.theProject.projPath == nwTempGUI + assert nwGUI.theProject.projMeta == path.join(nwTempGUI,"meta") + assert nwGUI.theProject.projCache == path.join(nwTempGUI,"cache") + assert nwGUI.theProject.projFile == "nwProject.nwx" + assert nwGUI.theProject.projName == "" + assert nwGUI.theProject.bookTitle == "" + assert len(nwGUI.theProject.bookAuthors) == 0 + assert nwGUI.theProject.spellCheck == False + # Check that tree items have been created assert nwGUI.treeView._getTreeItem("73475cb40a568") is not None assert nwGUI.treeView._getTreeItem("25fc0e7096fc6") is not None @@ -131,9 +165,7 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef): # Create new, save, open project nwGUI.theProject.handleSeed = 42 - assert nwGUI.theProject.setProjectPath(nwTempGUI) - assert nwGUI.newProject() - assert nwGUI.theProject.setProjectPath(nwTempGUI) + assert nwGUI.newProject(nwTempGUI, True) projEdit = GuiProjectEditor(nwGUI, nwGUI.theProject) qtbot.addWidget(projEdit) @@ -194,9 +226,7 @@ def testItemEditor(qtbot, nwTempGUI, nwRef): # Create new, save, open project nwGUI.theProject.handleSeed = 42 - assert nwGUI.theProject.setProjectPath(nwTempGUI) - assert nwGUI.newProject() - assert nwGUI.theProject.setProjectPath(nwTempGUI) + assert nwGUI.newProject(nwTempGUI, True) itemEdit = GuiItemEditor(nwGUI, nwGUI.theProject, "31489056e0916") qtbot.addWidget(itemEdit)