+22
-14
@@ -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
|
||||
##
|
||||
|
||||
@@ -44,4 +44,8 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
return
|
||||
|
||||
def clearViewer(self):
|
||||
self.clear()
|
||||
return True
|
||||
|
||||
# END Class GuiDocViewer
|
||||
|
||||
+7
-1
@@ -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()
|
||||
|
||||
+99
-10
@@ -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<br>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.<br>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?<br>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_()
|
||||
|
||||
@@ -36,6 +36,11 @@ class NWDoc():
|
||||
|
||||
return
|
||||
|
||||
def clearDocument(self):
|
||||
self.theItem = None
|
||||
self.docHandle = None
|
||||
return
|
||||
|
||||
def openDocument(self, tHandle):
|
||||
|
||||
self.docHandle = tHandle
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-22 23:27:41">
|
||||
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-24 00:28:28">
|
||||
<project>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.1.3" fileVersion="1.0" timeStamp="2019-05-23 23:02:29">
|
||||
<project>
|
||||
<name></name>
|
||||
<title></title>
|
||||
</project>
|
||||
<settings>
|
||||
<spellCheck>False</spellCheck>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<status>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Note</entry>
|
||||
<entry blue="0" green="150" red="200">Draft</entry>
|
||||
<entry blue="0" green="200" red="50">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Minor</entry>
|
||||
<entry blue="0" green="150" red="200">Major</entry>
|
||||
<entry blue="0" green="200" red="50">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="6">
|
||||
<item handle="73475cb40a568" order="0" parent="None">
|
||||
<name>Novel</name>
|
||||
<type>ROOT</type>
|
||||
<class>NOVEL</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="25fc0e7096fc6" order="0" parent="73475cb40a568">
|
||||
<name>New Chapter</name>
|
||||
<type>FOLDER</type>
|
||||
<class>NOVEL</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="31489056e0916" order="0" parent="25fc0e7096fc6">
|
||||
<name>New Scene</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>0</charCount>
|
||||
<wordCount>0</wordCount>
|
||||
<paraCount>0</paraCount>
|
||||
<cursorPos>0</cursorPos>
|
||||
</item>
|
||||
<item handle="44cb730c42048" order="1" parent="None">
|
||||
<name>Characters</name>
|
||||
<type>ROOT</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="71ee45a3c0db9" order="2" parent="None">
|
||||
<name>Plot</name>
|
||||
<type>ROOT</type>
|
||||
<class>PLOT</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="811786ad1ae74" order="3" parent="None">
|
||||
<name>World</name>
|
||||
<type>ROOT</type>
|
||||
<class>WORLD</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
+40
-10
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user