Autosaving works, and document and project tracks whether they have been changed or not.
This commit is contained in:
+21
-3
@@ -57,6 +57,10 @@ class Config:
|
||||
self.treeColWidth = [120, 30, 50]
|
||||
self.mainPanePos = [300, 800]
|
||||
|
||||
## Project
|
||||
self.autoSaveProj = 60
|
||||
self.autoSaveDoc = 30
|
||||
|
||||
## Text Editor
|
||||
self.textFixedW = True
|
||||
self.textWidth = 600
|
||||
@@ -115,6 +119,14 @@ class Config:
|
||||
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
|
||||
cnfSec = "Editor"
|
||||
if confParser.has_section(cnfSec):
|
||||
@@ -169,9 +181,15 @@ class Config:
|
||||
## Sizes
|
||||
cnfSec = "Sizes"
|
||||
confParser.add_section(cnfSec)
|
||||
confParser.set(cnfSec,"geometry", self.packList(self.winGeometry))
|
||||
confParser.set(cnfSec,"treecols", self.packList(self.treeColWidth))
|
||||
confParser.set(cnfSec,"mainpane", self.packList(self.mainPanePos))
|
||||
confParser.set(cnfSec,"geometry", self.packList(self.winGeometry))
|
||||
confParser.set(cnfSec,"treecols", self.packList(self.treeColWidth))
|
||||
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
|
||||
cnfSec = "Editor"
|
||||
|
||||
+2
-2
@@ -204,7 +204,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self.wcTimer.start()
|
||||
if self.mainConf.doReplace and not self.hasSelection:
|
||||
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
|
||||
|
||||
def _docAutoReplace(self, theBlock):
|
||||
@@ -267,7 +267,7 @@ class GuiDocEditor(QTextEdit):
|
||||
"""
|
||||
sinceActive = time()-self.lastEdit
|
||||
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()
|
||||
elif self.wCounter.isRunning():
|
||||
logger.verbose("Word counter thread is busy")
|
||||
|
||||
@@ -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.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")
|
||||
|
||||
|
||||
+7
-1
@@ -137,7 +137,7 @@ class GuiDocTree(QTreeWidget):
|
||||
trItem.setSelected(True)
|
||||
self.theParent.editItem()
|
||||
|
||||
return
|
||||
return True
|
||||
|
||||
def moveTreeItem(self, nStep):
|
||||
"""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)
|
||||
self.clearSelection()
|
||||
cItem.setSelected(True)
|
||||
self.theProject.projChanged = True
|
||||
return
|
||||
|
||||
def saveTreeOrder(self):
|
||||
@@ -208,6 +209,7 @@ class GuiDocTree(QTreeWidget):
|
||||
nwItemS.setParent(self.theProject.trashRoot)
|
||||
self.clearSelection()
|
||||
trItemP.setSelected(True)
|
||||
self.theProject.projChanged = True
|
||||
|
||||
elif nwItemS.itemType == nwItemType.FOLDER:
|
||||
logger.debug("User requested folder %s deleted" % tHandle)
|
||||
@@ -220,6 +222,7 @@ class GuiDocTree(QTreeWidget):
|
||||
trItemP.takeChild(tIndex)
|
||||
self.clearSelection()
|
||||
trItemP.setSelected(True)
|
||||
self.theProject.projChanged = True
|
||||
else:
|
||||
self.theParent.makeAlert(["Cannot delete folder.","It is not empty."],2)
|
||||
return
|
||||
@@ -230,6 +233,7 @@ class GuiDocTree(QTreeWidget):
|
||||
if trItemS.childCount() == 0:
|
||||
self.takeTopLevelItem(tIndex)
|
||||
self.theParent.mainMenu.setAvailableRoot()
|
||||
self.theProject.projChanged = True
|
||||
else:
|
||||
self.theParent.makeAlert(["Cannot delete root folder.","It is not empty."],2)
|
||||
return
|
||||
@@ -387,6 +391,7 @@ class GuiDocTree(QTreeWidget):
|
||||
pHandle = trItemP.text(self.C_HANDLE)
|
||||
nwItemS.setParent(pHandle)
|
||||
self.setTreeItemValues(tHandle)
|
||||
self.theProject.projChanged = True
|
||||
return
|
||||
|
||||
def _moveOrphanedItem(self, tHandle, dHandle):
|
||||
@@ -401,6 +406,7 @@ class GuiDocTree(QTreeWidget):
|
||||
pHandle = trItemP.text(self.C_HANDLE)
|
||||
nwItemS.setParent(pHandle)
|
||||
self.setTreeItemValues(tHandle)
|
||||
self.theProject.projChanged = True
|
||||
return
|
||||
|
||||
##
|
||||
|
||||
+46
-1
@@ -16,7 +16,7 @@ import nw
|
||||
from os import path
|
||||
from PyQt5.QtWidgets import QWidget, QMainWindow, QVBoxLayout, QFrame, QSplitter, QFileDialog, QStackedWidget, QShortcut, QMessageBox
|
||||
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.doceditor import GuiDocEditor
|
||||
@@ -91,6 +91,16 @@ class GuiMain(QMainWindow):
|
||||
theCss = inFile.read()
|
||||
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()
|
||||
|
||||
logger.debug("GUI initialisation complete")
|
||||
@@ -162,6 +172,8 @@ class GuiMain(QMainWindow):
|
||||
##
|
||||
|
||||
def openDocument(self, tHandle):
|
||||
if self._takeDocumentAction():
|
||||
self.saveDocument()
|
||||
self.stackPane.setCurrentIndex(self.stackDoc)
|
||||
self.docEditor.setText(self.theDocument.openDocument(tHandle))
|
||||
self.docEditor.changeWidth()
|
||||
@@ -174,6 +186,7 @@ class GuiMain(QMainWindow):
|
||||
self.theDocument.theItem.setWordCount(self.docEditor.wordCount)
|
||||
self.theDocument.theItem.setParaCount(self.docEditor.paraCount)
|
||||
self.theDocument.saveDocument(docHtml)
|
||||
self.docEditor.theDoc.setModified(False)
|
||||
return
|
||||
|
||||
##
|
||||
@@ -245,6 +258,10 @@ class GuiMain(QMainWindow):
|
||||
|
||||
def closeMain(self):
|
||||
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.setTreeColWidths(self.treeView.getColumnSizes())
|
||||
self.mainConf.setMainPanePos(self.splitMain.sizes())
|
||||
@@ -270,6 +287,34 @@ class GuiMain(QMainWindow):
|
||||
self.setWindowTitle(winTitle)
|
||||
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
|
||||
##
|
||||
|
||||
+15
-7
@@ -33,6 +33,7 @@ class NWProject():
|
||||
# Internal
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theParent = theParent
|
||||
self.projChanged = False
|
||||
|
||||
# Project Settings
|
||||
self.projTree = None
|
||||
@@ -111,6 +112,8 @@ class NWProject():
|
||||
|
||||
def clearProject(self):
|
||||
|
||||
self.projChanged = False
|
||||
|
||||
# Project Settings
|
||||
self.projTree = {}
|
||||
self.treeOrder = []
|
||||
@@ -197,6 +200,7 @@ class NWProject():
|
||||
self.theParent.statusBar.setStatus("Opened Project: %s" % self.projName)
|
||||
|
||||
self._scanProjectFolder()
|
||||
self.projChanged = False
|
||||
|
||||
return True
|
||||
|
||||
@@ -255,6 +259,7 @@ class NWProject():
|
||||
|
||||
self.mainConf.setRecent(self.projPath)
|
||||
self.theParent.statusBar.setStatus("Saved Project: %s" % self.projName)
|
||||
self.projChanged = False
|
||||
|
||||
return True
|
||||
|
||||
@@ -264,14 +269,17 @@ class NWProject():
|
||||
|
||||
def setProjectPath(self, projPath):
|
||||
self.projPath = projPath
|
||||
self.projChanged = True
|
||||
return True
|
||||
|
||||
def setProjectName(self, projName):
|
||||
self.projName = projName.strip()
|
||||
self.projChanged = True
|
||||
return True
|
||||
|
||||
def setBookTitle(self, bookTitle):
|
||||
self.bookTitle = bookTitle.strip()
|
||||
self.projChanged = True
|
||||
return True
|
||||
|
||||
def setBookAuthors(self, bookAuthors):
|
||||
@@ -281,18 +289,16 @@ class NWProject():
|
||||
if bookAuthor == "":
|
||||
continue
|
||||
self.bookAuthors.append(bookAuthor)
|
||||
self.projChanged = True
|
||||
return True
|
||||
|
||||
def setTreeOrder(self, newOrder):
|
||||
if len(self.treeOrder) != len(newOrder):
|
||||
logger.warning("Size of new and old tree order does not match")
|
||||
self.treeOrder = newOrder
|
||||
self.projChanged = True
|
||||
return True
|
||||
|
||||
def setItemName(self, tHandle, theName):
|
||||
self.projTree[tHandle].setName(theName)
|
||||
return
|
||||
|
||||
##
|
||||
# Get Functions
|
||||
##
|
||||
@@ -349,16 +355,16 @@ class NWProject():
|
||||
continue
|
||||
fHandle = fileItem[5]+fileItem[7:19]
|
||||
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:
|
||||
logger.verbose("Checking file %s, handle %s: Orphaned" % (fileItem,fHandle))
|
||||
logger.debug("Checking file %s, handle %s: Orphaned" % (fileItem,fHandle))
|
||||
orphanFiles.append(fHandle)
|
||||
|
||||
# Report status
|
||||
if len(orphanFiles) > 0:
|
||||
self.theParent.makeAlert("Found %d orphaned file(s) in project folder!" % len(orphanFiles),1)
|
||||
else:
|
||||
logger.verbose("File check OK")
|
||||
logger.debug("File check OK")
|
||||
return
|
||||
|
||||
# Handle orphans
|
||||
@@ -396,6 +402,8 @@ class NWProject():
|
||||
else:
|
||||
logger.error("Only one trash folder allowed")
|
||||
|
||||
self.projChanged = True
|
||||
|
||||
return
|
||||
|
||||
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 we’ve been adding to this document. It is utterly meaningless text, but since this is just dummy text, that doesn’t 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.
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
@@ -50,8 +50,8 @@
|
||||
<status>1</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>376</charCount>
|
||||
<wordCount>66</wordCount>
|
||||
<charCount>392</charCount>
|
||||
<wordCount>70</wordCount>
|
||||
<paraCount>4</paraCount>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
|
||||
|
||||
Reference in New Issue
Block a user