diff --git a/nw/config.py b/nw/config.py
index 18f2bc8e..2e0baacb 100644
--- a/nw/config.py
+++ b/nw/config.py
@@ -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
##
diff --git a/nw/constants.py b/nw/constants.py
index 51bde4e5..7919ba8c 100644
--- a/nw/constants.py
+++ b/nw/constants.py
@@ -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
diff --git a/nw/convert/text/tohtml.py b/nw/convert/text/tohtml.py
index c8615f75..fae40df4 100644
--- a/nw/convert/text/tohtml.py
+++ b/nw/convert/text/tohtml.py
@@ -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 += "
\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 "@%s
\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 += "%s: " % theLabel[theBits[0]]
+ if theBits[0] in nwLabels.KEY_NAME:
+ retText += "%s: " % nwLabels.KEY_NAME[theBits[0]]
for tTag in theBits[1:]:
refTags.append("%s" % (
theBits[0][1:], tTag, tTag
@@ -162,4 +152,11 @@ class ToHtml(Tokenizer):
return "%s
" % retText
+ def _formatComments(self, tText):
+
+ if not self.forPreview:
+ return "\n" % tText
+
+ return "\n" % tText
+
# END Class ToHtml
diff --git a/nw/enum.py b/nw/enum.py
index f83d9135..738ebce8 100644
--- a/nw/enum.py
+++ b/nw/enum.py
@@ -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
diff --git a/nw/gui/dialogs/configeditor.py b/nw/gui/dialogs/configeditor.py
index 2f13ef89..a77dcfc8 100644
--- a/nw/gui/dialogs/configeditor.py
+++ b/nw/gui/dialogs/configeditor.py
@@ -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()
diff --git a/nw/gui/elements/doceditor.py b/nw/gui/elements/doceditor.py
index 6b78eee6..852d3272 100644
--- a/nw/gui/elements/doceditor.py
+++ b/nw/gui/elements/doceditor.py
@@ -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
"
+ "Handle: {handle:s}
"
+ "Location: {fileLoc:s}"
+ ).format(
+ handle = self.theHandle,
+ fileLoc = str(self.nwDocument.fileLoc)
+ ))
+ return
+
##
# Document Events and Maintenance
##
diff --git a/nw/gui/elements/doctree.py b/nw/gui/elements/doctree.py
index d618e4ce..bc53ab59 100644
--- a/nw/gui/elements/doctree.py
+++ b/nw/gui/elements/doctree.py
@@ -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
diff --git a/nw/gui/elements/docviewer.py b/nw/gui/elements/docviewer.py
index 629a9824..468f4f2b 100644
--- a/nw/gui/elements/docviewer.py
+++ b/nw/gui/elements/docviewer.py
@@ -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],
diff --git a/nw/gui/elements/viewdetails.py b/nw/gui/elements/viewdetails.py
index 026955b4..d283cd20 100644
--- a/nw/gui/elements/viewdetails.py
+++ b/nw/gui/elements/viewdetails.py
@@ -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:
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index 7a42f6cf..23dff355 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -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")
diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py
index 872e9c66..c64c095d 100644
--- a/nw/gui/winmain.py
+++ b/nw/gui/winmain.py
@@ -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):
diff --git a/nw/project/backup.py b/nw/project/backup.py
index 0abef5da..bb69309a 100644
--- a/nw/project/backup.py
+++ b/nw/project/backup.py
@@ -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):
diff --git a/nw/project/document.py b/nw/project/document.py
index 0a3fa126..ad531fb3 100644
--- a/nw/project/document.py
+++ b/nw/project/document.py
@@ -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)
diff --git a/nw/project/index.py b/nw/project/index.py
index 08b872dc..db7d21e1 100644
--- a/nw/project/index.py
+++ b/nw/project/index.py
@@ -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:
diff --git a/nw/project/project.py b/nw/project/project.py
index 71891185..aa9bc9d2 100644
--- a/nw/project/project.py
+++ b/nw/project/project.py
@@ -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")
diff --git a/tests/reference/novelwriter.conf b/tests/reference/novelwriter.conf
index 0ba36f1a..b222cfb0 100644
--- a/tests/reference/novelwriter.conf
+++ b/tests/reference/novelwriter.conf
@@ -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 =