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)