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):
|
||||
|
||||
Reference in New Issue
Block a user