+30
-3
@@ -62,6 +62,8 @@ class Config:
|
||||
## General
|
||||
self.guiTheme = "default"
|
||||
self.guiSyntax = "default_light"
|
||||
|
||||
## Sizes
|
||||
self.winGeometry = [1100, 650]
|
||||
self.treeColWidth = [120, 30, 50]
|
||||
self.mainPanePos = [300, 800]
|
||||
@@ -74,7 +76,7 @@ class Config:
|
||||
## Text Editor
|
||||
self.textFont = None
|
||||
self.textSize = 12
|
||||
self.textFixedW = False
|
||||
self.textFixedW = True
|
||||
self.textWidth = 600
|
||||
self.textMargin = 40
|
||||
self.tabWidth = 40
|
||||
@@ -94,12 +96,16 @@ class Config:
|
||||
|
||||
self.spellLanguage = "en_GB"
|
||||
|
||||
# Backup
|
||||
## Backup
|
||||
self.backupPath = ""
|
||||
self.backupOnClose = False
|
||||
self.askBeforeBackup = True
|
||||
|
||||
# Path
|
||||
## State
|
||||
self.showRefPanel = True
|
||||
self.viewComments = True
|
||||
|
||||
## Path
|
||||
self.recentList = [""]*10
|
||||
|
||||
# Check Qt5 Versions
|
||||
@@ -244,6 +250,11 @@ class Config:
|
||||
self.backupOnClose = self._parseLine(cnfParse, cnfSec, "backuponclose", self.CNF_BOOL, self.backupOnClose)
|
||||
self.askBeforeBackup = self._parseLine(cnfParse, cnfSec, "askbeforebackup", self.CNF_BOOL, self.askBeforeBackup)
|
||||
|
||||
## State
|
||||
cnfSec = "State"
|
||||
self.showRefPanel = self._parseLine(cnfParse, cnfSec, "showrefpanel", self.CNF_BOOL, self.showRefPanel)
|
||||
self.viewComments = self._parseLine(cnfParse, cnfSec, "viewcomments", self.CNF_BOOL, self.viewComments)
|
||||
|
||||
## Path
|
||||
cnfSec = "Path"
|
||||
self.lastPath = self._parseLine(cnfParse, cnfSec, "lastpath", self.CNF_STR, self.lastPath)
|
||||
@@ -312,6 +323,12 @@ class Config:
|
||||
cnfParse.set(cnfSec,"backuponclose", str(self.backupOnClose))
|
||||
cnfParse.set(cnfSec,"askbeforebackup",str(self.askBeforeBackup))
|
||||
|
||||
## State
|
||||
cnfSec = "State"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec,"showrefpanel",str(self.showRefPanel))
|
||||
cnfParse.set(cnfSec,"viewcomments",str(self.viewComments))
|
||||
|
||||
## Path
|
||||
cnfSec = "Path"
|
||||
cnfParse.add_section(cnfSec)
|
||||
@@ -385,6 +402,16 @@ class Config:
|
||||
self.confChanged = True
|
||||
return True
|
||||
|
||||
def setShowRefPanel(self, checkState):
|
||||
self.showRefPanel = checkState
|
||||
self.confChanged = True
|
||||
return
|
||||
|
||||
def setViewComments(self, checkState):
|
||||
self.viewComments = checkState
|
||||
self.confChanged = True
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
@@ -32,6 +32,20 @@ class nwFiles():
|
||||
|
||||
# END Class nwFiles
|
||||
|
||||
class nwKeyWords:
|
||||
|
||||
TAG_KEY = "@tag"
|
||||
POV_KEY = "@pov"
|
||||
CHAR_KEY = "@char"
|
||||
PLOT_KEY = "@plot"
|
||||
TIME_KEY = "@time"
|
||||
WORLD_KEY = "@location"
|
||||
OBJECT_KEY = "@object"
|
||||
ENTITY_KEY = "@entity"
|
||||
CUSTOM_KEY = "@custom"
|
||||
|
||||
# END Class nwKeyWords
|
||||
|
||||
class nwLabels():
|
||||
|
||||
CLASS_NAME = {
|
||||
@@ -42,6 +56,7 @@ class nwLabels():
|
||||
nwItemClass.WORLD : "Locations",
|
||||
nwItemClass.TIMELINE : "Timeline",
|
||||
nwItemClass.OBJECT : "Objects",
|
||||
nwItemClass.ENTITY : "Entity",
|
||||
nwItemClass.CUSTOM : "Custom",
|
||||
nwItemClass.TRASH : "Trash",
|
||||
}
|
||||
@@ -53,6 +68,7 @@ class nwLabels():
|
||||
nwItemClass.WORLD : "L",
|
||||
nwItemClass.TIMELINE : "T",
|
||||
nwItemClass.OBJECT : "O",
|
||||
nwItemClass.ENTITY : "E",
|
||||
nwItemClass.CUSTOM : "X",
|
||||
nwItemClass.TRASH : "R",
|
||||
}
|
||||
@@ -78,6 +94,17 @@ class nwLabels():
|
||||
nwItemLayout.SCENE : "Sc",
|
||||
nwItemLayout.NOTE : "Nt",
|
||||
}
|
||||
KEY_NAME = {
|
||||
nwKeyWords.TAG_KEY : "Tag",
|
||||
nwKeyWords.POV_KEY : "Point of View",
|
||||
nwKeyWords.CHAR_KEY : "Characters",
|
||||
nwKeyWords.PLOT_KEY : "Plot",
|
||||
nwKeyWords.TIME_KEY : "Time",
|
||||
nwKeyWords.WORLD_KEY : "Locations",
|
||||
nwKeyWords.OBJECT_KEY : "Objects",
|
||||
nwKeyWords.ENTITY_KEY : "Entities",
|
||||
nwKeyWords.CUSTOM_KEY : "Custom",
|
||||
}
|
||||
|
||||
# END Class nwLabels
|
||||
|
||||
|
||||
+13
-16
@@ -15,7 +15,7 @@ import re
|
||||
import nw
|
||||
|
||||
from nw.convert.tokenizer import Tokenizer
|
||||
from nw.constants import nwUnicode
|
||||
from nw.constants import nwUnicode, nwLabels
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -26,7 +26,7 @@ class ToHtml(Tokenizer):
|
||||
self.forPreview = False
|
||||
return
|
||||
|
||||
def setPreview(self, forPreview):
|
||||
def setPreview(self, forPreview, doComments):
|
||||
"""If we're using this class to generate markdown preview, we need to make a few changes to
|
||||
formatting, which is selected by this flag.
|
||||
"""
|
||||
@@ -34,6 +34,7 @@ class ToHtml(Tokenizer):
|
||||
self.forPreview = forPreview
|
||||
if forPreview:
|
||||
self.doKeywords = True
|
||||
self.doComments = doComments
|
||||
|
||||
return
|
||||
|
||||
@@ -118,7 +119,7 @@ class ToHtml(Tokenizer):
|
||||
thisPar.append(tTemp.rstrip()+" ")
|
||||
|
||||
elif tType == self.T_COMMENT and self.doComments:
|
||||
self.theResult += "<div class='comment'>%s</div>\n" % tText
|
||||
self.theResult += self._formatComments(tText)
|
||||
|
||||
elif tType == self.T_KEYWORD and self.doKeywords:
|
||||
self.theResult += self._formatTags(tText)
|
||||
@@ -134,17 +135,6 @@ class ToHtml(Tokenizer):
|
||||
if not self.forPreview:
|
||||
return "<pre>@%s</pre>\n" % tText
|
||||
|
||||
theLabel = {
|
||||
"@tag" : "Tag",
|
||||
"@pov" : "Point of View",
|
||||
"@char" : "Character(s)",
|
||||
"@plot" : "Plot",
|
||||
"@time" : "Time",
|
||||
"@location" : "Location(s)",
|
||||
"@object" : "Object(s)",
|
||||
"@custom" : "Custom",
|
||||
}
|
||||
|
||||
tText = "@"+tText
|
||||
isValid, theBits, thePos = self.theParent.theIndex.scanThis(tText)
|
||||
if not isValid or not theBits:
|
||||
@@ -152,8 +142,8 @@ class ToHtml(Tokenizer):
|
||||
|
||||
retText = ""
|
||||
refTags = []
|
||||
if theBits[0] in theLabel:
|
||||
retText += "<span class='tags'>%s:</span> " % theLabel[theBits[0]]
|
||||
if theBits[0] in nwLabels.KEY_NAME:
|
||||
retText += "<span class='tags'>%s:</span> " % nwLabels.KEY_NAME[theBits[0]]
|
||||
for tTag in theBits[1:]:
|
||||
refTags.append("<a href='#%s=%s'>%s</a>" % (
|
||||
theBits[0][1:], tTag, tTag
|
||||
@@ -162,4 +152,11 @@ class ToHtml(Tokenizer):
|
||||
|
||||
return "<div>%s</div>" % retText
|
||||
|
||||
def _formatComments(self, tText):
|
||||
|
||||
if not self.forPreview:
|
||||
return "<div class='comment'>%s</div>\n" % tText
|
||||
|
||||
return "<p class='comment'>%s</p>\n" % tText
|
||||
|
||||
# END Class ToHtml
|
||||
|
||||
+3
-2
@@ -31,8 +31,9 @@ class nwItemClass(Enum):
|
||||
WORLD = 4
|
||||
TIMELINE = 5
|
||||
OBJECT = 6
|
||||
CUSTOM = 7
|
||||
TRASH = 8
|
||||
ENTITY = 7
|
||||
CUSTOM = 8
|
||||
TRASH = 9
|
||||
|
||||
# END Enum nwItemClass
|
||||
|
||||
|
||||
@@ -322,17 +322,17 @@ class GuiConfigEditEditor(QWidget):
|
||||
self.textFlowForm = QGridLayout(self)
|
||||
self.textFlow.setLayout(self.textFlowForm)
|
||||
|
||||
self.textFlowFixed = QCheckBox("Fixed width",self)
|
||||
self.textFlowFixed.setToolTip("Make text in editor fixed width and scale margins instead.")
|
||||
self.textFlowFixed = QCheckBox("Max text width",self)
|
||||
self.textFlowFixed.setToolTip("Maximum width of the text.")
|
||||
if self.mainConf.textFixedW:
|
||||
self.textFlowFixed.setCheckState(Qt.Checked)
|
||||
else:
|
||||
self.textFlowFixed.setCheckState(Qt.Unchecked)
|
||||
self.textFlowWidth = QSpinBox(self)
|
||||
self.textFlowWidth.setMinimum(300)
|
||||
self.textFlowWidth.setMaximum(10000)
|
||||
self.textFlowWidth.setSingleStep(10)
|
||||
self.textFlowWidth.setValue(self.mainConf.textWidth)
|
||||
self.textFlowMax = QSpinBox(self)
|
||||
self.textFlowMax.setMinimum(300)
|
||||
self.textFlowMax.setMaximum(10000)
|
||||
self.textFlowMax.setSingleStep(10)
|
||||
self.textFlowMax.setValue(self.mainConf.textWidth)
|
||||
|
||||
self.textFlowJustify = QCheckBox("Justify text",self)
|
||||
self.textFlowJustify.setToolTip("Justify text in main document editor.")
|
||||
@@ -342,7 +342,7 @@ class GuiConfigEditEditor(QWidget):
|
||||
self.textFlowJustify.setCheckState(Qt.Unchecked)
|
||||
|
||||
self.textFlowForm.addWidget(self.textFlowFixed, 0, 0)
|
||||
self.textFlowForm.addWidget(self.textFlowWidth, 0, 1)
|
||||
self.textFlowForm.addWidget(self.textFlowMax, 0, 1)
|
||||
self.textFlowForm.addWidget(QLabel("px"), 0, 2)
|
||||
self.textFlowForm.addWidget(self.textFlowJustify, 1, 0)
|
||||
self.textFlowForm.setColumnStretch(4, 1)
|
||||
@@ -519,7 +519,7 @@ class GuiConfigEditEditor(QWidget):
|
||||
self.mainConf.textFont = textFont
|
||||
self.mainConf.textSize = textSize
|
||||
|
||||
textWidth = self.textFlowWidth.value()
|
||||
textWidth = self.textFlowMax.value()
|
||||
textFixedW = self.textFlowFixed.isChecked()
|
||||
doJustify = self.textFlowJustify.isChecked()
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ import nw
|
||||
from time import time
|
||||
|
||||
from PyQt5.QtCore import Qt, QTimer, QSizeF
|
||||
from PyQt5.QtWidgets import qApp, QTextEdit, QAction, QMenu, QShortcut
|
||||
from PyQt5.QtWidgets import qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox
|
||||
from PyQt5.QtGui import (
|
||||
QTextCursor, QTextOption, QIcon, QKeySequence, QFont, QColor, QPalette, QTextDocument
|
||||
QTextCursor, QTextOption, QIcon, QKeySequence, QFont, QColor, QPalette, QTextDocument,
|
||||
)
|
||||
|
||||
from nw.project.document import NWDoc
|
||||
@@ -293,8 +293,8 @@ class GuiDocEditor(QTextEdit):
|
||||
tW = self.mainConf.textWidth
|
||||
wW = self.width()
|
||||
tM = int((wW - sW - tW)/2)
|
||||
if tM < 0:
|
||||
tM = 0
|
||||
if tM < self.mainConf.textMargin:
|
||||
tM = self.mainConf.textMargin
|
||||
docFormat = self.qDocument.rootFrame().frameFormat()
|
||||
docFormat.setLeftMargin(tM)
|
||||
docFormat.setRightMargin(tM)
|
||||
@@ -332,6 +332,19 @@ class GuiDocEditor(QTextEdit):
|
||||
def isEmpty(self):
|
||||
return self.qDocument.isEmpty()
|
||||
|
||||
def revealLocation(self):
|
||||
if self.theHandle is not None:
|
||||
msgBox = QMessageBox()
|
||||
msgBox.information(self, "File Location", (
|
||||
"File details for the currently open file<br>"
|
||||
"Handle: {handle:s}<br>"
|
||||
"Location: {fileLoc:s}"
|
||||
).format(
|
||||
handle = self.theHandle,
|
||||
fileLoc = str(self.nwDocument.fileLoc)
|
||||
))
|
||||
return
|
||||
|
||||
##
|
||||
# Document Events and Maintenance
|
||||
##
|
||||
|
||||
+36
-15
@@ -18,7 +18,7 @@ from PyQt5.QtGui import QIcon, QFont, QColor
|
||||
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QAbstractItemView, QApplication
|
||||
|
||||
from nw.project.item import NWItem
|
||||
from nw.enum import nwItemType, nwItemClass, nwAlert
|
||||
from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
||||
from nw.constants import nwLabels
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -62,9 +62,14 @@ class GuiDocTree(QTreeWidget):
|
||||
self.setDragEnabled(True)
|
||||
self.setDragDropMode(QAbstractItemView.InternalMove)
|
||||
|
||||
# But don't allow drop on root level
|
||||
trRoot = self.invisibleRootItem()
|
||||
trRoot.setFlags(trRoot.flags() ^ Qt.ItemIsDropEnabled)
|
||||
|
||||
# Set Multiple Selection by CTRL
|
||||
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
# Disabled for now, until the merge files option has been added
|
||||
# self.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
# self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
|
||||
for colN in range(len(self.mainConf.treeColWidth)):
|
||||
self.setColumnWidth(colN,self.mainConf.treeColWidth[colN])
|
||||
@@ -183,7 +188,7 @@ class GuiDocTree(QTreeWidget):
|
||||
nIndex = tIndex + nStep
|
||||
if nIndex < 0 or nIndex >= nChild:
|
||||
return False
|
||||
cItem = self.takeTopLevelItem(tIndex)
|
||||
cItem = self.takeTopLevelItem(tIndex)
|
||||
self.insertTopLevelItem(nIndex, cItem)
|
||||
else:
|
||||
tIndex = pItem.indexOfChild(tItem)
|
||||
@@ -191,7 +196,7 @@ class GuiDocTree(QTreeWidget):
|
||||
nIndex = tIndex + nStep
|
||||
if nIndex < 0 or nIndex >= nChild:
|
||||
return False
|
||||
cItem = pItem.takeChild(tIndex)
|
||||
cItem = pItem.takeChild(tIndex)
|
||||
pItem.insertChild(nIndex, cItem)
|
||||
self.clearSelection()
|
||||
cItem.setSelected(True)
|
||||
@@ -446,17 +451,28 @@ class GuiDocTree(QTreeWidget):
|
||||
return
|
||||
|
||||
def _updateItemParent(self, tHandle):
|
||||
"""Update the parent handle of an item so that the information in the project is consistent
|
||||
with the treeView. Also move the word count over to the new parent tree.
|
||||
"""
|
||||
|
||||
trItemS = self._getTreeItem(tHandle)
|
||||
nwItemS = self.theProject.getItem(tHandle)
|
||||
trItemP = trItemS.parent()
|
||||
if trItemP is None:
|
||||
logger.error("Failed to find new parent item of %s" % tHandle)
|
||||
return
|
||||
return False
|
||||
|
||||
pHandle = trItemP.text(self.C_HANDLE)
|
||||
wC = int(trItemS.text(self.C_COUNT))
|
||||
self.propagateCount(tHandle, -wC)
|
||||
nwItemS.setParent(pHandle)
|
||||
self.propagateCount(tHandle, wC)
|
||||
self.setTreeItemValues(tHandle)
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
logger.debug("The parent of item %s has been changed to %s" % (tHandle,pHandle))
|
||||
|
||||
return True
|
||||
|
||||
def _moveOrphanedItem(self, tHandle, dHandle):
|
||||
trItemS = self._getTreeItem(tHandle)
|
||||
@@ -505,25 +521,30 @@ class GuiDocTree(QTreeWidget):
|
||||
dnItem = self.theProject.getItem(dHandle)
|
||||
isSame = snItem.itemClass == dnItem.itemClass
|
||||
isNone = snItem.itemClass == nwItemClass.NO_CLASS
|
||||
isNote = snItem.itemLayout == nwItemLayout.NOTE
|
||||
onFile = dnItem.itemType == nwItemType.FILE
|
||||
isRoot = snItem.itemType == nwItemType.ROOT
|
||||
onRoot = dnItem.itemType == nwItemType.ROOT
|
||||
isOnTop = self.dropIndicatorPosition() == QAbstractItemView.OnItem
|
||||
isAbove = self.dropIndicatorPosition() == QAbstractItemView.AboveItem
|
||||
isBelow = self.dropIndicatorPosition() == QAbstractItemView.BelowItem
|
||||
if (isSame or isNone) and not (onFile and isOnTop) and not isRoot:
|
||||
logger.verbose("Drag'n'drop of item %s accepted" % sHandle)
|
||||
if (isSame or isNone or isNote) and not (onFile and isOnTop) and not isRoot:
|
||||
logger.debug("Drag'n'drop of item %s accepted" % sHandle)
|
||||
QTreeWidget.dropEvent(self, theEvent)
|
||||
if isNone:
|
||||
self._moveOrphanedItem(sHandle, dHandle)
|
||||
self._cleanOrphanedRoot()
|
||||
else:
|
||||
self._updateItemParent(sHandle)
|
||||
elif isRoot and (isAbove or isBelow) and onRoot:
|
||||
logger.verbose("Drag'n'drop of item %s accepted" % sHandle)
|
||||
QTreeWidget.dropEvent(self, theEvent)
|
||||
if not isSame:
|
||||
logger.debug("Item %s class has been changed from %s to %s" % (
|
||||
sHandle,
|
||||
snItem.itemClass.name,
|
||||
dnItem.itemClass.name
|
||||
))
|
||||
snItem.setClass(dnItem.itemClass)
|
||||
self.setTreeItemValues(sHandle)
|
||||
else:
|
||||
logger.verbose("Drag'n'drop of item %s not accepted" % sHandle)
|
||||
logger.debug("Drag'n'drop of item %s not accepted" % sHandle)
|
||||
self.makeAlert("The item cannot be moved to that location.", nwAlert.ERROR)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
logger.debug("Generating preview for item %s" % tHandle)
|
||||
sPos = self.verticalScrollBar().value()
|
||||
aDoc = ToHtml(self.theProject, self.theParent)
|
||||
aDoc.setPreview(True)
|
||||
aDoc.setPreview(True, self.mainConf.viewComments)
|
||||
aDoc.setText(tHandle)
|
||||
aDoc.doAutoReplace()
|
||||
aDoc.tokenizeText()
|
||||
@@ -117,10 +117,14 @@ class GuiDocViewer(QTextBrowser):
|
||||
self.theHandle = tHandle
|
||||
self.theProject.setLastViewed(tHandle)
|
||||
|
||||
self.theParent.docMeta.refreshReferences(tHandle)
|
||||
self.theParent.viewMeta.refreshReferences(tHandle)
|
||||
|
||||
return True
|
||||
|
||||
def reloadText(self):
|
||||
self.loadText(self.theHandle)
|
||||
return
|
||||
|
||||
def loadFromTag(self, theTag):
|
||||
|
||||
logger.debug("Loading document from tag '%s'" % theTag)
|
||||
@@ -180,10 +184,6 @@ class GuiDocViewer(QTextBrowser):
|
||||
"a {{"
|
||||
" color: rgb({aColR},{aColG},{aColB});"
|
||||
"}}\n"
|
||||
"pre {{"
|
||||
" color: rgb({cColR},{cColG},{cColB});"
|
||||
" font-size: {preSize:.1f}pt;"
|
||||
"}}\n"
|
||||
"mark {{"
|
||||
" color: rgb({eColR},{eColG},{eColB});"
|
||||
"}}\n"
|
||||
@@ -197,6 +197,11 @@ class GuiDocViewer(QTextBrowser):
|
||||
" color: rgb({kColR},{kColG},{kColB});"
|
||||
" font-wright: bold;"
|
||||
"}}\n"
|
||||
".comment {{"
|
||||
" color: rgb({cColR},{cColG},{cColB});"
|
||||
" margin-left: 1em;"
|
||||
" margin-right: 1em;"
|
||||
"}}\n"
|
||||
).format(
|
||||
textSize = self.mainConf.textSize,
|
||||
preSize = self.mainConf.textSize*0.9,
|
||||
@@ -212,9 +217,9 @@ class GuiDocViewer(QTextBrowser):
|
||||
eColR = self.theTheme.colEmph[0],
|
||||
eColG = self.theTheme.colEmph[1],
|
||||
eColB = self.theTheme.colEmph[2],
|
||||
aColR = self.theTheme.colLink[0],
|
||||
aColG = self.theTheme.colLink[1],
|
||||
aColB = self.theTheme.colLink[2],
|
||||
aColR = self.theTheme.colVal[0],
|
||||
aColG = self.theTheme.colVal[1],
|
||||
aColB = self.theTheme.colVal[2],
|
||||
kColR = self.theTheme.colKey[0],
|
||||
kColG = self.theTheme.colKey[1],
|
||||
kColB = self.theTheme.colKey[2],
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
Class holding the document view details panel
|
||||
|
||||
File History:
|
||||
Created: 2019-09-31 [0.3.2]
|
||||
Created: 2019-10-31 [0.3.2]
|
||||
|
||||
"""
|
||||
|
||||
@@ -20,7 +20,7 @@ from PyQt5.QtWidgets import (
|
||||
QSizePolicy, QCheckBox, QGridLayout
|
||||
)
|
||||
|
||||
from nw.constants import nwLabels
|
||||
from nw.constants import nwLabels
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -48,7 +48,6 @@ class GuiDocViewDetails(QWidget):
|
||||
self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||
self.showHide.setArrowType(Qt.DownArrow)
|
||||
self.showHide.setCheckable(True)
|
||||
self.showHide.setChecked(True)
|
||||
self.showHide.setIconSize(QSize(16,16))
|
||||
self.showHide.toggled.connect(self._doShowHide)
|
||||
|
||||
@@ -66,8 +65,7 @@ class GuiDocViewDetails(QWidget):
|
||||
self.scrollBox.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.scrollBox.setFrameStyle(QFrame.NoFrame)
|
||||
self.scrollBox.setWidgetResizable(True)
|
||||
self.scrollBox.setMaximumHeight(300)
|
||||
self.scrollBox.setMinimumHeight(60)
|
||||
self.scrollBox.setFixedHeight(80)
|
||||
self.scrollBox.setWidget(self.refList)
|
||||
|
||||
self.outerBox.addWidget(self.showHide, 0, 0)
|
||||
@@ -79,6 +77,8 @@ class GuiDocViewDetails(QWidget):
|
||||
self.setLayout(self.outerBox)
|
||||
self.setContentsMargins(0,0,0,0)
|
||||
|
||||
self._doShowHide(self.mainConf.showRefPanel)
|
||||
|
||||
logger.debug("DocViewDetails initialisation complete")
|
||||
|
||||
return
|
||||
@@ -91,12 +91,6 @@ class GuiDocViewDetails(QWidget):
|
||||
return
|
||||
|
||||
theRefs = self.theParent.theIndex.buildReferenceList(tHandle)
|
||||
if theRefs:
|
||||
self.setVisible(True)
|
||||
else:
|
||||
self.setVisible(False)
|
||||
return
|
||||
|
||||
theList = []
|
||||
for tHandle in theRefs:
|
||||
tItem = self.theProject.getItem(tHandle)
|
||||
@@ -120,6 +114,7 @@ class GuiDocViewDetails(QWidget):
|
||||
|
||||
def _doShowHide(self, chState):
|
||||
self.scrollBox.setVisible(chState)
|
||||
self.mainConf.setShowRefPanel(chState)
|
||||
if chState:
|
||||
self.showHide.setArrowType(Qt.DownArrow)
|
||||
else:
|
||||
|
||||
+25
-1
@@ -112,6 +112,11 @@ class GuiMainMenu(QMenuBar):
|
||||
self.toolsSpellCheck.setChecked(False)
|
||||
return True
|
||||
|
||||
def _toggleViewComments(self):
|
||||
self.mainConf.setViewComments(self.docViewComments.isChecked())
|
||||
self.theParent.docViewer.reloadText()
|
||||
return True
|
||||
|
||||
def _showAbout(self):
|
||||
listPrefix = " • "
|
||||
aboutMsg = (
|
||||
@@ -163,6 +168,10 @@ class GuiMainMenu(QMenuBar):
|
||||
self.updateRecentProjects()
|
||||
return True
|
||||
|
||||
def _showDocumentLocation(self):
|
||||
self.theParent.docEditor.revealLocation()
|
||||
return True
|
||||
|
||||
##
|
||||
# Menu Builders
|
||||
##
|
||||
@@ -235,6 +244,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self.rootItems[nwItemClass.WORLD] = QAction("Location Root", rootMenu)
|
||||
self.rootItems[nwItemClass.TIMELINE] = QAction("Timeline Root", rootMenu)
|
||||
self.rootItems[nwItemClass.OBJECT] = QAction("Object Root", rootMenu)
|
||||
self.rootItems[nwItemClass.ENTITY] = QAction("Entity Root", rootMenu)
|
||||
self.rootItems[nwItemClass.CUSTOM] = QAction("Custom Root", rootMenu)
|
||||
nCount = 0
|
||||
for itemClass in self.rootItems.keys():
|
||||
@@ -330,10 +340,24 @@ class GuiMainMenu(QMenuBar):
|
||||
menuItem.triggered.connect(self.theParent.closeDocViewer)
|
||||
self.docuMenu.addAction(menuItem)
|
||||
|
||||
# Document > Toggle View Comments
|
||||
self.docViewComments = QAction("View Comments", self)
|
||||
self.docViewComments.setStatusTip("Show comments in view panel")
|
||||
self.docViewComments.setCheckable(True)
|
||||
self.docViewComments.setChecked(self.mainConf.viewComments)
|
||||
self.docViewComments.toggled.connect(self._toggleViewComments)
|
||||
self.docuMenu.addAction(self.docViewComments)
|
||||
|
||||
# Document > Separator
|
||||
self.docuMenu.addSeparator()
|
||||
|
||||
# Document > Close Preview
|
||||
# Document > Show File Details
|
||||
menuItem = QAction("Show File Details", self)
|
||||
menuItem.setStatusTip("Shows a message box with the document location in the project folder")
|
||||
menuItem.triggered.connect(self._showDocumentLocation)
|
||||
self.docuMenu.addAction(menuItem)
|
||||
|
||||
# Document > Import From File
|
||||
menuItem = QAction("Import from File", self)
|
||||
menuItem.setStatusTip("Import document from a text or markdown file")
|
||||
menuItem.setShortcut("Ctrl+Shift+I")
|
||||
|
||||
+5
-5
@@ -77,7 +77,7 @@ class GuiMain(QMainWindow):
|
||||
self.noticeBar = GuiNoticeBar(self)
|
||||
self.docEditor = GuiDocEditor(self, self.theProject)
|
||||
self.docViewer = GuiDocViewer(self, self.theProject)
|
||||
self.docMeta = GuiDocViewDetails(self, self.theProject)
|
||||
self.viewMeta = GuiDocViewDetails(self, self.theProject)
|
||||
self.searchBar = GuiSearchBar(self)
|
||||
self.treeMeta = GuiDocDetails(self, self.theProject)
|
||||
self.treeView = GuiDocTree(self, self.theProject)
|
||||
@@ -107,7 +107,7 @@ class GuiMain(QMainWindow):
|
||||
self.docView = QVBoxLayout()
|
||||
self.docView.setContentsMargins(0,0,0,0)
|
||||
self.docView.addWidget(self.docViewer)
|
||||
self.docView.addWidget(self.docMeta)
|
||||
self.docView.addWidget(self.viewMeta)
|
||||
self.docView.setStretch(0, 1)
|
||||
self.viewPane.setLayout(self.docView)
|
||||
|
||||
@@ -313,7 +313,7 @@ class GuiMain(QMainWindow):
|
||||
|
||||
return True
|
||||
|
||||
def saveProject(self):
|
||||
def saveProject(self, isAuto=False):
|
||||
"""Save the current project.
|
||||
"""
|
||||
if not self.hasProject:
|
||||
@@ -327,7 +327,7 @@ class GuiMain(QMainWindow):
|
||||
return False
|
||||
|
||||
self.treeView.saveTreeOrder()
|
||||
self.theProject.saveProject()
|
||||
self.theProject.saveProject(isAuto)
|
||||
self.theIndex.saveIndex()
|
||||
self.mainMenu.updateRecentProjects()
|
||||
|
||||
@@ -703,7 +703,7 @@ class GuiMain(QMainWindow):
|
||||
def _autoSaveProject(self):
|
||||
if self.hasProject and self.theProject.projChanged and self.theProject.projPath is not None:
|
||||
logger.debug("Autosaving project")
|
||||
self.saveProject()
|
||||
self.saveProject(isAuto=True)
|
||||
return
|
||||
|
||||
def _autoSaveDocument(self):
|
||||
|
||||
@@ -17,18 +17,16 @@ from os import path, mkdir, listdir
|
||||
from shutil import make_archive
|
||||
from datetime import datetime
|
||||
|
||||
from nw.enum import nwAlert
|
||||
from nw.enum import nwAlert
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class NWBackup():
|
||||
|
||||
def __init__(self, theParent, theProject):
|
||||
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theProject
|
||||
|
||||
return
|
||||
|
||||
def zipIt(self):
|
||||
|
||||
@@ -31,6 +31,7 @@ class NWDoc():
|
||||
self.theItem = None
|
||||
self.docHandle = None
|
||||
self.docEditable = False
|
||||
self.fileLoc = None
|
||||
|
||||
# Internal Mapping
|
||||
self.makeAlert = self.theParent.makeAlert
|
||||
@@ -58,7 +59,8 @@ class NWDoc():
|
||||
self.docEditable = False
|
||||
|
||||
docDir, docFile = self._assemblePath(self.FILE_MN)
|
||||
logger.debug("Opening document %s" % path.join(docDir,docFile))
|
||||
self.fileLoc = path.join(docDir,docFile)
|
||||
logger.debug("Opening document %s" % self.fileLoc)
|
||||
dataDir = path.join(self.theProject.projPath, docDir)
|
||||
docPath = path.join(dataDir, docFile)
|
||||
|
||||
|
||||
+30
-27
@@ -14,37 +14,37 @@ import logging
|
||||
import json
|
||||
import nw
|
||||
|
||||
from os import path
|
||||
from os import path
|
||||
|
||||
from nw.project.document import NWDoc
|
||||
from nw.enum import nwItemType, nwItemClass, nwItemLayout
|
||||
from nw.constants import nwFiles
|
||||
from nw.constants import nwFiles, nwKeyWords
|
||||
from nw.enum import nwAlert
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class NWIndex():
|
||||
|
||||
TAG_KEY = "@tag"
|
||||
POV_KEY = "@pov"
|
||||
CHAR_KEY = "@char"
|
||||
PLOT_KEY = "@plot"
|
||||
TIME_KEY = "@time"
|
||||
WORLD_KEY = "@location"
|
||||
OBJECT_KEY = "@object"
|
||||
CUSTOM_KEY = "@custom"
|
||||
|
||||
NOTE_KEYS = [TAG_KEY]
|
||||
NOVEL_KEYS = [PLOT_KEY, POV_KEY, CHAR_KEY, WORLD_KEY, TIME_KEY, OBJECT_KEY, CUSTOM_KEY]
|
||||
VALID_KEYS = [TAG_KEY, PLOT_KEY, POV_KEY, CHAR_KEY, WORLD_KEY, TIME_KEY, OBJECT_KEY, CUSTOM_KEY]
|
||||
VALID_KEYS = [
|
||||
nwKeyWords.TAG_KEY,
|
||||
nwKeyWords.PLOT_KEY,
|
||||
nwKeyWords.POV_KEY,
|
||||
nwKeyWords.CHAR_KEY,
|
||||
nwKeyWords.WORLD_KEY,
|
||||
nwKeyWords.TIME_KEY,
|
||||
nwKeyWords.OBJECT_KEY,
|
||||
nwKeyWords.ENTITY_KEY,
|
||||
nwKeyWords.CUSTOM_KEY
|
||||
]
|
||||
TAG_CLASS = {
|
||||
CHAR_KEY : [nwItemClass.CHARACTER, 1],
|
||||
POV_KEY : [nwItemClass.CHARACTER, 2],
|
||||
PLOT_KEY : [nwItemClass.PLOT, 1],
|
||||
TIME_KEY : [nwItemClass.TIMELINE, 1],
|
||||
WORLD_KEY : [nwItemClass.WORLD, 1],
|
||||
OBJECT_KEY : [nwItemClass.OBJECT, 1],
|
||||
CUSTOM_KEY : [nwItemClass.CUSTOM, 1],
|
||||
nwKeyWords.CHAR_KEY : [nwItemClass.CHARACTER, 1],
|
||||
nwKeyWords.POV_KEY : [nwItemClass.CHARACTER, 2],
|
||||
nwKeyWords.PLOT_KEY : [nwItemClass.PLOT, 1],
|
||||
nwKeyWords.TIME_KEY : [nwItemClass.TIMELINE, 1],
|
||||
nwKeyWords.WORLD_KEY : [nwItemClass.WORLD, 1],
|
||||
nwKeyWords.OBJECT_KEY : [nwItemClass.OBJECT, 1],
|
||||
nwKeyWords.ENTITY_KEY : [nwItemClass.ENTITY, 1],
|
||||
nwKeyWords.CUSTOM_KEY : [nwItemClass.CUSTOM, 1],
|
||||
}
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
@@ -79,9 +79,13 @@ class NWIndex():
|
||||
|
||||
def deleteHandle(self, tHandle):
|
||||
|
||||
delTags = []
|
||||
for tTag in self.tagIndex:
|
||||
if self.tagIndex[tTag][1] == tHandle:
|
||||
self.tagIndex.pop(tTag, None)
|
||||
delTags.append(tTag)
|
||||
|
||||
for tTag in delTags:
|
||||
self.tagIndex.pop(tTag, None)
|
||||
|
||||
self.refIndex.pop(tHandle, None)
|
||||
self.novelIndex.pop(tHandle, None)
|
||||
@@ -275,10 +279,9 @@ class NWIndex():
|
||||
if not isValid or len(theBits) == 0:
|
||||
return False
|
||||
|
||||
theKey = theBits[0]
|
||||
if theKey in self.NOVEL_KEYS:
|
||||
if theBits[0] != nwKeyWords.TAG_KEY:
|
||||
for aVal in theBits[1:]:
|
||||
self.refIndex[tHandle].append([nLine, theKey, aVal, nTitle])
|
||||
self.refIndex[tHandle].append([nLine, theBits[0], aVal, nTitle])
|
||||
|
||||
return True
|
||||
|
||||
@@ -290,7 +293,7 @@ class NWIndex():
|
||||
if not isValid or len(theBits) != 2:
|
||||
return False
|
||||
|
||||
if theBits[0] == self.TAG_KEY:
|
||||
if theBits[0] == nwKeyWords.TAG_KEY:
|
||||
self.tagIndex[theBits[1]] = [nLine, tHandle, itemClass.name]
|
||||
|
||||
return True
|
||||
@@ -355,7 +358,7 @@ class NWIndex():
|
||||
return isGood
|
||||
|
||||
# If we have a tag, only the first value is accepted, the rest is ignored
|
||||
if theBits[0] == self.TAG_KEY and nBits > 1:
|
||||
if theBits[0] == nwKeyWords.TAG_KEY and nBits > 1:
|
||||
isGood[0] = True
|
||||
if theBits[1] in self.tagIndex.keys():
|
||||
if self.tagIndex[theBits[1]][1] == tItem.itemHandle:
|
||||
|
||||
@@ -280,7 +280,7 @@ class NWProject():
|
||||
|
||||
return True
|
||||
|
||||
def saveProject(self):
|
||||
def saveProject(self, isAuto=False):
|
||||
|
||||
if self.projPath is None:
|
||||
self.makeAlert("Project path not set, cannot save.", nwAlert.ERROR)
|
||||
@@ -296,7 +296,8 @@ class NWProject():
|
||||
logger.debug("Saving project: %s" % self.projPath)
|
||||
|
||||
# Save a copy of the current file, just in case
|
||||
self._maintainPrevious()
|
||||
if not isAuto:
|
||||
self._maintainPrevious()
|
||||
|
||||
# Root element and project details
|
||||
logger.debug("Writing project meta")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[Main]
|
||||
timestamp = 2019-10-29 19:20:27
|
||||
timestamp = 2019-11-03 00:40:02
|
||||
theme = default
|
||||
syntax = default_light
|
||||
|
||||
@@ -16,7 +16,7 @@ autosavedoc = 30
|
||||
[Editor]
|
||||
textfont = None
|
||||
textsize = 12
|
||||
fixedwidth = False
|
||||
fixedwidth = True
|
||||
width = 600
|
||||
margin = 40
|
||||
tabwidth = 40
|
||||
@@ -38,6 +38,10 @@ backuppath =
|
||||
backuponclose = False
|
||||
askbeforebackup = True
|
||||
|
||||
[State]
|
||||
showrefpanel = True
|
||||
viewcomments = True
|
||||
|
||||
[Path]
|
||||
lastpath =
|
||||
recent0 =
|
||||
|
||||
Reference in New Issue
Block a user