Autosaving works, and document and project tracks whether they have been changed or not.

This commit is contained in:
Veronica K. B. Olsen
2019-05-05 00:11:46 +02:00
parent 9ea70aeeaf
commit 9565b14d8b
8 changed files with 97 additions and 20 deletions
+21 -3
View File
@@ -57,6 +57,10 @@ class Config:
self.treeColWidth = [120, 30, 50] self.treeColWidth = [120, 30, 50]
self.mainPanePos = [300, 800] self.mainPanePos = [300, 800]
## Project
self.autoSaveProj = 60
self.autoSaveDoc = 30
## Text Editor ## Text Editor
self.textFixedW = True self.textFixedW = True
self.textWidth = 600 self.textWidth = 600
@@ -115,6 +119,14 @@ class Config:
confParser.get(cnfSec,"mainpane"), 2, self.mainPanePos confParser.get(cnfSec,"mainpane"), 2, self.mainPanePos
) )
## Project
cnfSec = "Project"
if confParser.has_section(cnfSec):
if confParser.has_option(cnfSec,"autosaveproject"):
self.autoSaveProj = confParser.getint(cnfSec,"autosaveproject")
if confParser.has_option(cnfSec,"autosavedoc"):
self.autoSaveDoc = confParser.getint(cnfSec,"autosavedoc")
## Editor ## Editor
cnfSec = "Editor" cnfSec = "Editor"
if confParser.has_section(cnfSec): if confParser.has_section(cnfSec):
@@ -169,9 +181,15 @@ class Config:
## Sizes ## Sizes
cnfSec = "Sizes" cnfSec = "Sizes"
confParser.add_section(cnfSec) confParser.add_section(cnfSec)
confParser.set(cnfSec,"geometry", self.packList(self.winGeometry)) confParser.set(cnfSec,"geometry", self.packList(self.winGeometry))
confParser.set(cnfSec,"treecols", self.packList(self.treeColWidth)) confParser.set(cnfSec,"treecols", self.packList(self.treeColWidth))
confParser.set(cnfSec,"mainpane", self.packList(self.mainPanePos)) confParser.set(cnfSec,"mainpane", self.packList(self.mainPanePos))
## Project
cnfSec = "Project"
confParser.add_section(cnfSec)
confParser.set(cnfSec,"autosaveproject", str(self.autoSaveProj))
confParser.set(cnfSec,"autosavedoc", str(self.autoSaveDoc))
## Editor ## Editor
cnfSec = "Editor" cnfSec = "Editor"
+2 -2
View File
@@ -204,7 +204,7 @@ class GuiDocEditor(QTextEdit):
self.wcTimer.start() self.wcTimer.start()
if self.mainConf.doReplace and not self.hasSelection: if self.mainConf.doReplace and not self.hasSelection:
self._docAutoReplace(self.theDoc.findBlock(thePos)) self._docAutoReplace(self.theDoc.findBlock(thePos))
logger.verbose("Doc change signal took %.3f µs" % ((time()-self.lastEdit)*1e6)) # logger.verbose("Doc change signal took %.3f µs" % ((time()-self.lastEdit)*1e6))
return return
def _docAutoReplace(self, theBlock): def _docAutoReplace(self, theBlock):
@@ -267,7 +267,7 @@ class GuiDocEditor(QTextEdit):
""" """
sinceActive = time()-self.lastEdit sinceActive = time()-self.lastEdit
if sinceActive > 5*self.wcInterval: if sinceActive > 5*self.wcInterval:
logger.verbose("Stopping word count timer due to no activity over the last %.3f seconds" % sinceActive) logger.debug("Stopping word count timer due to no activity over the last %.3f seconds" % sinceActive)
self.wcTimer.stop() self.wcTimer.stop()
elif self.wCounter.isRunning(): elif self.wCounter.isRunning():
logger.verbose("Word counter thread is busy") logger.verbose("Word counter thread is busy")
+2 -2
View File
@@ -143,9 +143,9 @@ class GuiDocHighlighter(QSyntaxHighlighter):
} }
)) ))
# Build a QRegExp for each pattern # Build a QRegExp for each pattern and for the spell checker
self.rules = [(QRegularExpression(a),b) for (a,b) in self.hRules] self.rules = [(QRegularExpression(a),b) for (a,b) in self.hRules]
self.spellRx = QRegularExpression(r"[\w\'{:s}]+".format(self.mainConf.fmtSingleQuotes[1])) self.spellRx = QRegularExpression(r"[\w\'{:s}]+".format(self.mainConf.fmtApostrophe))
logger.debug("DocHighlighter initialisation complete") logger.debug("DocHighlighter initialisation complete")
+7 -1
View File
@@ -137,7 +137,7 @@ class GuiDocTree(QTreeWidget):
trItem.setSelected(True) trItem.setSelected(True)
self.theParent.editItem() self.theParent.editItem()
return return True
def moveTreeItem(self, nStep): def moveTreeItem(self, nStep):
"""Move an item up or down in the tree, but only if the treeView has focus. This also """Move an item up or down in the tree, but only if the treeView has focus. This also
@@ -163,6 +163,7 @@ class GuiDocTree(QTreeWidget):
pItem.insertChild(nIndex, cItem) pItem.insertChild(nIndex, cItem)
self.clearSelection() self.clearSelection()
cItem.setSelected(True) cItem.setSelected(True)
self.theProject.projChanged = True
return return
def saveTreeOrder(self): def saveTreeOrder(self):
@@ -208,6 +209,7 @@ class GuiDocTree(QTreeWidget):
nwItemS.setParent(self.theProject.trashRoot) nwItemS.setParent(self.theProject.trashRoot)
self.clearSelection() self.clearSelection()
trItemP.setSelected(True) trItemP.setSelected(True)
self.theProject.projChanged = True
elif nwItemS.itemType == nwItemType.FOLDER: elif nwItemS.itemType == nwItemType.FOLDER:
logger.debug("User requested folder %s deleted" % tHandle) logger.debug("User requested folder %s deleted" % tHandle)
@@ -220,6 +222,7 @@ class GuiDocTree(QTreeWidget):
trItemP.takeChild(tIndex) trItemP.takeChild(tIndex)
self.clearSelection() self.clearSelection()
trItemP.setSelected(True) trItemP.setSelected(True)
self.theProject.projChanged = True
else: else:
self.theParent.makeAlert(["Cannot delete folder.","It is not empty."],2) self.theParent.makeAlert(["Cannot delete folder.","It is not empty."],2)
return return
@@ -230,6 +233,7 @@ class GuiDocTree(QTreeWidget):
if trItemS.childCount() == 0: if trItemS.childCount() == 0:
self.takeTopLevelItem(tIndex) self.takeTopLevelItem(tIndex)
self.theParent.mainMenu.setAvailableRoot() self.theParent.mainMenu.setAvailableRoot()
self.theProject.projChanged = True
else: else:
self.theParent.makeAlert(["Cannot delete root folder.","It is not empty."],2) self.theParent.makeAlert(["Cannot delete root folder.","It is not empty."],2)
return return
@@ -387,6 +391,7 @@ class GuiDocTree(QTreeWidget):
pHandle = trItemP.text(self.C_HANDLE) pHandle = trItemP.text(self.C_HANDLE)
nwItemS.setParent(pHandle) nwItemS.setParent(pHandle)
self.setTreeItemValues(tHandle) self.setTreeItemValues(tHandle)
self.theProject.projChanged = True
return return
def _moveOrphanedItem(self, tHandle, dHandle): def _moveOrphanedItem(self, tHandle, dHandle):
@@ -401,6 +406,7 @@ class GuiDocTree(QTreeWidget):
pHandle = trItemP.text(self.C_HANDLE) pHandle = trItemP.text(self.C_HANDLE)
nwItemS.setParent(pHandle) nwItemS.setParent(pHandle)
self.setTreeItemValues(tHandle) self.setTreeItemValues(tHandle)
self.theProject.projChanged = True
return return
## ##
+46 -1
View File
@@ -16,7 +16,7 @@ import nw
from os import path from os import path
from PyQt5.QtWidgets import QWidget, QMainWindow, QVBoxLayout, QFrame, QSplitter, QFileDialog, QStackedWidget, QShortcut, QMessageBox from PyQt5.QtWidgets import QWidget, QMainWindow, QVBoxLayout, QFrame, QSplitter, QFileDialog, QStackedWidget, QShortcut, QMessageBox
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt, QTimer
from nw.gui.doctree import GuiDocTree from nw.gui.doctree import GuiDocTree
from nw.gui.doceditor import GuiDocEditor from nw.gui.doceditor import GuiDocEditor
@@ -91,6 +91,16 @@ class GuiMain(QMainWindow):
theCss = inFile.read() theCss = inFile.read()
self.setStyleSheet(theCss) self.setStyleSheet(theCss)
self.asProjTimer = QTimer()
self.asProjTimer.setInterval(int(self.mainConf.autoSaveProj*1000))
self.asProjTimer.timeout.connect(self._autoSaveProject)
self.asProjTimer.start()
self.asDocTimer = QTimer()
self.asDocTimer.setInterval(int(self.mainConf.autoSaveDoc*1000))
self.asDocTimer.timeout.connect(self._autoSaveDocument)
self.asDocTimer.start()
self.show() self.show()
logger.debug("GUI initialisation complete") logger.debug("GUI initialisation complete")
@@ -162,6 +172,8 @@ class GuiMain(QMainWindow):
## ##
def openDocument(self, tHandle): def openDocument(self, tHandle):
if self._takeDocumentAction():
self.saveDocument()
self.stackPane.setCurrentIndex(self.stackDoc) self.stackPane.setCurrentIndex(self.stackDoc)
self.docEditor.setText(self.theDocument.openDocument(tHandle)) self.docEditor.setText(self.theDocument.openDocument(tHandle))
self.docEditor.changeWidth() self.docEditor.changeWidth()
@@ -174,6 +186,7 @@ class GuiMain(QMainWindow):
self.theDocument.theItem.setWordCount(self.docEditor.wordCount) self.theDocument.theItem.setWordCount(self.docEditor.wordCount)
self.theDocument.theItem.setParaCount(self.docEditor.paraCount) self.theDocument.theItem.setParaCount(self.docEditor.paraCount)
self.theDocument.saveDocument(docHtml) self.theDocument.saveDocument(docHtml)
self.docEditor.theDoc.setModified(False)
return return
## ##
@@ -245,6 +258,10 @@ class GuiMain(QMainWindow):
def closeMain(self): def closeMain(self):
logger.info("Exiting %s" % nw.__package__) logger.info("Exiting %s" % nw.__package__)
if self._takeDocumentAction():
self.saveDocument()
if self._takeProjectAction():
self.saveProject()
self.mainConf.setWinSize(self.width(), self.height()) self.mainConf.setWinSize(self.width(), self.height())
self.mainConf.setTreeColWidths(self.treeView.getColumnSizes()) self.mainConf.setTreeColWidths(self.treeView.getColumnSizes())
self.mainConf.setMainPanePos(self.splitMain.sizes()) self.mainConf.setMainPanePos(self.splitMain.sizes())
@@ -270,6 +287,34 @@ class GuiMain(QMainWindow):
self.setWindowTitle(winTitle) self.setWindowTitle(winTitle)
return True return True
def _autoSaveProject(self):
if self._takeProjectAction():
logger.debug("Autosaving project")
self.saveProject()
return
def _autoSaveDocument(self):
if self._takeDocumentAction():
logger.debug("Autosaving document")
self.saveDocument()
return
def _takeProjectAction(self):
if self.theProject.projPath is None:
return False
if not self.theProject.projChanged:
return False
return True
def _takeDocumentAction(self):
if self.stackPane.currentIndex() != self.stackDoc:
return False
if self.theDocument.theItem is None:
return False
if not self.docEditor.theDoc.isModified():
return False
return True
## ##
# Events # Events
## ##
+15 -7
View File
@@ -33,6 +33,7 @@ class NWProject():
# Internal # Internal
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
self.theParent = theParent self.theParent = theParent
self.projChanged = False
# Project Settings # Project Settings
self.projTree = None self.projTree = None
@@ -111,6 +112,8 @@ class NWProject():
def clearProject(self): def clearProject(self):
self.projChanged = False
# Project Settings # Project Settings
self.projTree = {} self.projTree = {}
self.treeOrder = [] self.treeOrder = []
@@ -197,6 +200,7 @@ class NWProject():
self.theParent.statusBar.setStatus("Opened Project: %s" % self.projName) self.theParent.statusBar.setStatus("Opened Project: %s" % self.projName)
self._scanProjectFolder() self._scanProjectFolder()
self.projChanged = False
return True return True
@@ -255,6 +259,7 @@ class NWProject():
self.mainConf.setRecent(self.projPath) self.mainConf.setRecent(self.projPath)
self.theParent.statusBar.setStatus("Saved Project: %s" % self.projName) self.theParent.statusBar.setStatus("Saved Project: %s" % self.projName)
self.projChanged = False
return True return True
@@ -264,14 +269,17 @@ class NWProject():
def setProjectPath(self, projPath): def setProjectPath(self, projPath):
self.projPath = projPath self.projPath = projPath
self.projChanged = True
return True return True
def setProjectName(self, projName): def setProjectName(self, projName):
self.projName = projName.strip() self.projName = projName.strip()
self.projChanged = True
return True return True
def setBookTitle(self, bookTitle): def setBookTitle(self, bookTitle):
self.bookTitle = bookTitle.strip() self.bookTitle = bookTitle.strip()
self.projChanged = True
return True return True
def setBookAuthors(self, bookAuthors): def setBookAuthors(self, bookAuthors):
@@ -281,18 +289,16 @@ class NWProject():
if bookAuthor == "": if bookAuthor == "":
continue continue
self.bookAuthors.append(bookAuthor) self.bookAuthors.append(bookAuthor)
self.projChanged = True
return True return True
def setTreeOrder(self, newOrder): def setTreeOrder(self, newOrder):
if len(self.treeOrder) != len(newOrder): if len(self.treeOrder) != len(newOrder):
logger.warning("Size of new and old tree order does not match") logger.warning("Size of new and old tree order does not match")
self.treeOrder = newOrder self.treeOrder = newOrder
self.projChanged = True
return True return True
def setItemName(self, tHandle, theName):
self.projTree[tHandle].setName(theName)
return
## ##
# Get Functions # Get Functions
## ##
@@ -349,16 +355,16 @@ class NWProject():
continue continue
fHandle = fileItem[5]+fileItem[7:19] fHandle = fileItem[5]+fileItem[7:19]
if fHandle in self.treeOrder: if fHandle in self.treeOrder:
logger.verbose("Checking file %s, handle %s: OK" % (fileItem,fHandle)) logger.debug("Checking file %s, handle %s: OK" % (fileItem,fHandle))
else: else:
logger.verbose("Checking file %s, handle %s: Orphaned" % (fileItem,fHandle)) logger.debug("Checking file %s, handle %s: Orphaned" % (fileItem,fHandle))
orphanFiles.append(fHandle) orphanFiles.append(fHandle)
# Report status # Report status
if len(orphanFiles) > 0: if len(orphanFiles) > 0:
self.theParent.makeAlert("Found %d orphaned file(s) in project folder!" % len(orphanFiles),1) self.theParent.makeAlert("Found %d orphaned file(s) in project folder!" % len(orphanFiles),1)
else: else:
logger.verbose("File check OK") logger.debug("File check OK")
return return
# Handle orphans # Handle orphans
@@ -396,6 +402,8 @@ class NWProject():
else: else:
logger.error("Only one trash folder allowed") logger.error("Only one trash folder allowed")
self.projChanged = True
return return
def _makeStatusIcons(self): def _makeStatusIcons(self):
@@ -13,6 +13,6 @@ Some text here would look good as well, and maybe some "dialogue"?
So, this is some text that weve been adding to this document. It is utterly meaningless text, but since this is just dummy text, that doesnt really matter. The text is perfectly happy to live in this document regardless. So, this is some text that weve been adding to this document. It is utterly meaningless text, but since this is just dummy text, that doesnt really matter. The text is perfectly happy to live in this document regardless.
This paragraph is also meaningless. This paragraph is also meaningless. At least a bit.
+3 -3
View File
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="0.0.1" fileVersion="1.0" timeStamp="2019-05-04 22:24:32"> <novelWriterXML appVersion="0.0.1" fileVersion="1.0" timeStamp="2019-05-04 23:57:20">
<project> <project>
<name>Sample Project</name> <name>Sample Project</name>
<title>Sample Project</title> <title>Sample Project</title>
@@ -50,8 +50,8 @@
<status>1</status> <status>1</status>
<expanded>False</expanded> <expanded>False</expanded>
<layout>SCENE</layout> <layout>SCENE</layout>
<charCount>376</charCount> <charCount>392</charCount>
<wordCount>66</wordCount> <wordCount>70</wordCount>
<paraCount>4</paraCount> <paraCount>4</paraCount>
</item> </item>
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a"> <item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">