Connected the syntax highlicghter to the index. It now shows valid keywords and values
This commit is contained in:
+23
-19
@@ -34,11 +34,12 @@ class GuiDocEditor(QTextEdit):
|
||||
logger.debug("Initialising DocEditor ...")
|
||||
|
||||
# Class Variables
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theParent = theParent
|
||||
self.docChanged = False
|
||||
self.pwlFile = None
|
||||
self.spellCheck = False
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theParent = theParent
|
||||
self.docChanged = False
|
||||
self.pwlFile = None
|
||||
self.spellCheck = False
|
||||
self.theDocument = theParent.theDocument
|
||||
|
||||
# Document Variables
|
||||
self.charCount = 0
|
||||
@@ -54,9 +55,9 @@ class GuiDocEditor(QTextEdit):
|
||||
self.typApos = self.mainConf.fmtApostrophe
|
||||
|
||||
# Core Elements
|
||||
self.theDoc = self.document()
|
||||
self.theQDoc = self.document()
|
||||
self.theDict = enchant.Dict(self.mainConf.spellLanguage)
|
||||
self.hLight = GuiDocHighlighter(self.theDoc, self.theParent.theTheme)
|
||||
self.hLight = GuiDocHighlighter(self.theQDoc, self.theParent)
|
||||
self.hLight.setDict(self.theDict)
|
||||
|
||||
# Context Menu
|
||||
@@ -71,8 +72,8 @@ class GuiDocEditor(QTextEdit):
|
||||
self.clearEditor()
|
||||
self.initEditor()
|
||||
|
||||
self.theDoc.setDocumentMargin(0)
|
||||
self.theDoc.contentsChange.connect(self._docChange)
|
||||
self.theQDoc.setDocumentMargin(0)
|
||||
self.theQDoc.contentsChange.connect(self._docChange)
|
||||
|
||||
# Custom Shortcuts
|
||||
QShortcut(QKeySequence("Ctrl+."), self, context=Qt.WidgetShortcut, activated=self._openSpellContext)
|
||||
@@ -109,7 +110,18 @@ class GuiDocEditor(QTextEdit):
|
||||
theOpt.setTabStopDistance(self.mainConf.tabWidth)
|
||||
if self.mainConf.doJustify:
|
||||
theOpt.setAlignment(Qt.AlignJustify)
|
||||
self.theDoc.setDefaultTextOption(theOpt)
|
||||
self.theQDoc.setDefaultTextOption(theOpt)
|
||||
return True
|
||||
|
||||
def loadText(self, tHandle):
|
||||
self.hLight.setHandle(tHandle)
|
||||
self.setPlainText(self.theDocument.openDocument(tHandle))
|
||||
self.setCursorPosition(self.theDocument.theItem.cursorPos)
|
||||
self.lastEdit = time()
|
||||
self._runCounter()
|
||||
self.wcTimer.start()
|
||||
self.setDocumentChanged(False)
|
||||
self.setReadOnly(False)
|
||||
return True
|
||||
|
||||
##
|
||||
@@ -121,14 +133,6 @@ class GuiDocEditor(QTextEdit):
|
||||
self.theParent.statusBar.setDocumentStatus(self.docChanged)
|
||||
return self.docChanged
|
||||
|
||||
def setText(self, theText):
|
||||
self.setPlainText(theText)
|
||||
self.lastEdit = time()
|
||||
self._runCounter()
|
||||
self.wcTimer.start()
|
||||
self.setDocumentChanged(False)
|
||||
return True
|
||||
|
||||
def getText(self):
|
||||
theText = self.toPlainText()
|
||||
return theText
|
||||
@@ -296,7 +300,7 @@ class GuiDocEditor(QTextEdit):
|
||||
if not self.wcTimer.isActive():
|
||||
self.wcTimer.start()
|
||||
if self.mainConf.doReplace and not self.hasSelection:
|
||||
self._docAutoReplace(self.theDoc.findBlock(thePos))
|
||||
self._docAutoReplace(self.theQDoc.findBlock(thePos))
|
||||
# logger.verbose("Doc change signal took %.3f µs" % ((time()-self.lastEdit)*1e6))
|
||||
return
|
||||
|
||||
|
||||
+84
-38
@@ -20,14 +20,17 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
|
||||
def __init__(self, theDoc, theTheme):
|
||||
def __init__(self, theDoc, theParent):
|
||||
QSyntaxHighlighter.__init__(self, theDoc)
|
||||
|
||||
logger.debug("Initialising DocHighlighter ...")
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theDoc = theDoc
|
||||
self.theTheme = theTheme
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.theIndex = theParent.theIndex
|
||||
self.theDict = None
|
||||
self.theHandle = None
|
||||
self.spellCheck = False
|
||||
self.hRules = []
|
||||
|
||||
@@ -90,12 +93,12 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
))
|
||||
|
||||
# Keyword/Value
|
||||
self.hRules.append((
|
||||
r"^(@.+?)\s*:\s*(.+?)$", {
|
||||
1 : self.hStyles["keyword"],
|
||||
2 : self.hStyles["value"],
|
||||
}
|
||||
))
|
||||
# self.hRules.append((
|
||||
# r"^(@.+?)\s*:\s*(.+?)$", {
|
||||
# 1 : self.hStyles["keyword"],
|
||||
# 2 : self.hStyles["value"],
|
||||
# }
|
||||
# ))
|
||||
|
||||
# Comments
|
||||
self.hRules.append((
|
||||
@@ -152,6 +155,10 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setDict(self, theDict):
|
||||
self.theDict = theDict
|
||||
return True
|
||||
@@ -160,6 +167,75 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
self.spellCheck = theMode
|
||||
return True
|
||||
|
||||
def setHandle(self, theHandle):
|
||||
self.theHandle = theHandle
|
||||
return True
|
||||
|
||||
##
|
||||
# Highlight Block
|
||||
##
|
||||
|
||||
def highlightBlock(self, theText):
|
||||
|
||||
if self.theHandle is None:
|
||||
self.setCurrentBlockState(0)
|
||||
return
|
||||
|
||||
if theText.startswith("@"):
|
||||
# Highlighting of keywords and commands
|
||||
tItem = self.theParent.theProject.getItem(self.theHandle)
|
||||
isValid, theBits, thePos = self.theIndex.scanThis(theText)
|
||||
isGood = self.theIndex.checkThese(theBits, tItem)
|
||||
if isValid:
|
||||
for n in range(len(theBits)):
|
||||
xPos = thePos[n]
|
||||
xLen = len(theBits[n])
|
||||
if isGood[n]:
|
||||
if n == 0:
|
||||
self.setFormat(xPos, xLen, self.hStyles["keyword"])
|
||||
else:
|
||||
self.setFormat(xPos, xLen, self.hStyles["value"])
|
||||
else:
|
||||
kwFmt = self.format(xPos)
|
||||
kwFmt.setUnderlineColor(self.colSpell)
|
||||
kwFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
|
||||
self.setFormat(xPos, xLen, kwFmt)
|
||||
|
||||
else:
|
||||
# Other text just uses regex
|
||||
for rX, xFmt in self.rules:
|
||||
rxItt = rX.globalMatch(theText, 0)
|
||||
while rxItt.hasNext():
|
||||
rxMatch = rxItt.next()
|
||||
for xM in xFmt.keys():
|
||||
xPos = rxMatch.capturedStart(xM)
|
||||
xLen = rxMatch.capturedLength(xM)
|
||||
self.setFormat(xPos, xLen, xFmt[xM])
|
||||
|
||||
self.setCurrentBlockState(0)
|
||||
|
||||
if self.theDict is None or not self.spellCheck or theText.startswith("@"):
|
||||
return
|
||||
|
||||
rxSpell = self.spellRx.globalMatch(theText.replace("_"," "), 0)
|
||||
while rxSpell.hasNext():
|
||||
rxMatch = rxSpell.next()
|
||||
if not self.theDict.check(rxMatch.captured(0)):
|
||||
if rxMatch.captured(0) == rxMatch.captured(0).upper():
|
||||
continue
|
||||
xPos = rxMatch.capturedStart(0)
|
||||
xLen = rxMatch.capturedLength(0)
|
||||
spFmt = self.format(xPos)
|
||||
spFmt.setUnderlineColor(self.colSpell)
|
||||
spFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
|
||||
self.setFormat(xPos, xLen, spFmt)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _makeFormat(self, fmtCol=None, fmtStyle=None, fmtSize=None):
|
||||
theFormat = QTextCharFormat()
|
||||
|
||||
@@ -181,34 +257,4 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
|
||||
return theFormat
|
||||
|
||||
def highlightBlock(self, theText):
|
||||
|
||||
for rX, xFmt in self.rules:
|
||||
rxItt = rX.globalMatch(theText, 0)
|
||||
while rxItt.hasNext():
|
||||
rxMatch = rxItt.next()
|
||||
for xM in xFmt.keys():
|
||||
xPos = rxMatch.capturedStart(xM)
|
||||
xLen = rxMatch.capturedLength(xM)
|
||||
self.setFormat(xPos, xLen, xFmt[xM])
|
||||
|
||||
self.setCurrentBlockState(0)
|
||||
|
||||
if self.theDict is None or not self.spellCheck or theText.startswith("@"):
|
||||
return
|
||||
|
||||
rxSpell = self.spellRx.globalMatch(theText.replace("_"," "), 0)
|
||||
while rxSpell.hasNext():
|
||||
rxMatch = rxSpell.next()
|
||||
if not self.theDict.check(rxMatch.captured(0)):
|
||||
if rxMatch.captured(0) == rxMatch.captured(0).upper(): continue
|
||||
xPos = rxMatch.capturedStart(0)
|
||||
xLen = rxMatch.capturedLength(0)
|
||||
spFmt = self.format(xPos)
|
||||
spFmt.setUnderlineColor(self.colSpell)
|
||||
spFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
|
||||
self.setFormat(xPos, xLen, spFmt)
|
||||
|
||||
return
|
||||
|
||||
# END Class DocHighlighter
|
||||
|
||||
+8
-10
@@ -53,7 +53,7 @@ class GuiMain(QMainWindow):
|
||||
self.theTheme = Theme()
|
||||
self.theProject = NWProject(self)
|
||||
self.theDocument = NWDoc(self.theProject, self)
|
||||
self.tagIndex = NWIndex(self.theProject, self)
|
||||
self.theIndex = NWIndex(self.theProject, self)
|
||||
self.hasProject = False
|
||||
|
||||
self.resize(*self.mainConf.winGeometry)
|
||||
@@ -212,7 +212,7 @@ class GuiMain(QMainWindow):
|
||||
|
||||
if saveOK:
|
||||
self.theProject.closeProject()
|
||||
self.tagIndex.clearIndex()
|
||||
self.theIndex.clearIndex()
|
||||
self.clearGUI()
|
||||
self.hasProject = False
|
||||
|
||||
@@ -236,7 +236,7 @@ class GuiMain(QMainWindow):
|
||||
return False
|
||||
|
||||
# Load the tag index
|
||||
self.tagIndex.loadIndex()
|
||||
self.theIndex.loadIndex()
|
||||
|
||||
# Update GUI
|
||||
self._setWindowTitle(self.theProject.projName)
|
||||
@@ -268,7 +268,7 @@ class GuiMain(QMainWindow):
|
||||
|
||||
self.treeView.saveTreeOrder()
|
||||
self.theProject.saveProject()
|
||||
self.tagIndex.saveIndex()
|
||||
self.theIndex.saveIndex()
|
||||
self.mainMenu.updateRecentProjects()
|
||||
|
||||
return True
|
||||
@@ -286,9 +286,7 @@ class GuiMain(QMainWindow):
|
||||
|
||||
def openDocument(self, tHandle):
|
||||
self.closeDocument()
|
||||
self.docEditor.setText(self.theDocument.openDocument(tHandle))
|
||||
self.docEditor.setReadOnly(False)
|
||||
self.docEditor.setCursorPosition(self.theDocument.theItem.cursorPos)
|
||||
self.docEditor.loadText(tHandle)
|
||||
self.docEditor.changeWidth()
|
||||
self.docEditor.setFocus()
|
||||
self.theProject.setLastEdited(tHandle)
|
||||
@@ -305,7 +303,7 @@ class GuiMain(QMainWindow):
|
||||
theItem.setCursorPos(cursPos)
|
||||
self.theDocument.saveDocument(docText)
|
||||
self.docEditor.setDocumentChanged(False)
|
||||
self.tagIndex.scanText(theItem.itemHandle, docText)
|
||||
self.theIndex.scanText(theItem.itemHandle, docText)
|
||||
return True
|
||||
|
||||
def viewDocument(self, tHandle=None):
|
||||
@@ -385,7 +383,7 @@ class GuiMain(QMainWindow):
|
||||
logger.debug("Rebuilding indices ...")
|
||||
|
||||
self.treeView.saveTreeOrder()
|
||||
self.tagIndex.clearIndex()
|
||||
self.theIndex.clearIndex()
|
||||
nItems = len(self.theProject.treeOrder)
|
||||
|
||||
dlgProg = QProgressDialog("Scanning files ...", "Cancel", 0, nItems, self)
|
||||
@@ -419,7 +417,7 @@ class GuiMain(QMainWindow):
|
||||
self.treeView.projectWordCount()
|
||||
|
||||
# Build tag index
|
||||
self.tagIndex.scanText(tHandle, theText)
|
||||
self.theIndex.scanText(tHandle, theText)
|
||||
|
||||
time.sleep(0.05)
|
||||
nDone += 1
|
||||
|
||||
+52
-22
@@ -36,24 +36,25 @@ 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"
|
||||
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 = [PLOT_KEY, POV_KEY, CHAR_KEY, WORLD_KEY, TIME_KEY, OBJECT_KEY, CUSTOM_KEY]
|
||||
VALID_CLASS = {
|
||||
nwItemClass.NOVEL : [],
|
||||
nwItemClass.PLOT : [PLOT_KEY],
|
||||
nwItemClass.CHARACTER : [POV_KEY, CHAR_KEY],
|
||||
nwItemClass.WORLD : [WORLD_KEY],
|
||||
nwItemClass.TIMELINE : [TIME_KEY],
|
||||
nwItemClass.OBJECT : [OBJECT_KEY],
|
||||
nwItemClass.CUSTOM : [CUSTOM_KEY],
|
||||
NOTE_KEYS = [TAG_KEY]
|
||||
NOVEL_KEYS = [PLOT_KEY, POV_KEY, CHAR_KEY, WORLD_KEY, TIME_KEY, OBJECT_KEY, CUSTOM_KEY]
|
||||
TAG_CLASS = {
|
||||
POV_KEY : nwItemClass.CHARACTER,
|
||||
CHAR_KEY : nwItemClass.CHARACTER,
|
||||
PLOT_KEY : nwItemClass.PLOT,
|
||||
TIME_KEY : nwItemClass.TIMELINE,
|
||||
WORLD_KEY : nwItemClass.WORLD,
|
||||
OBJECT_KEY : nwItemClass.OBJECT,
|
||||
CUSTOM_KEY : nwItemClass.CUSTOM,
|
||||
}
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
@@ -137,7 +138,8 @@ class NWIndex():
|
||||
theItem = self.theProject.getItem(tHandle)
|
||||
if theItem is None: return False
|
||||
if theItem.itemType != nwItemType.FILE: return False
|
||||
itemClass = theItem.itemClass
|
||||
itemClass = theItem.itemClass
|
||||
itemLayout = theItem.itemLayout
|
||||
|
||||
logger.debug("Indexing item with handle %s" % tHandle)
|
||||
|
||||
@@ -162,7 +164,7 @@ class NWIndex():
|
||||
if nChar == 0: continue
|
||||
if aLine[0] == "#":
|
||||
if isNovel:
|
||||
self.indexTitle(tHandle, aLine, nLine)
|
||||
self.indexTitle(tHandle, aLine, nLine, itemLayout)
|
||||
elif aLine[0] == "@":
|
||||
if isNovel:
|
||||
self.indexNoteRef(tHandle, aLine, nLine)
|
||||
@@ -171,7 +173,7 @@ class NWIndex():
|
||||
|
||||
return True
|
||||
|
||||
def indexTitle(self, tHandle, aLine, nLine):
|
||||
def indexTitle(self, tHandle, aLine, nLine, itemLayout):
|
||||
|
||||
if aLine.startswith("# "):
|
||||
hDepth = 1
|
||||
@@ -189,7 +191,7 @@ class NWIndex():
|
||||
return False
|
||||
|
||||
if hText != "":
|
||||
self.novelIndex[tHandle].append([nLine, hDepth, hText])
|
||||
self.novelIndex[tHandle].append([nLine, hDepth, hText, itemLayout.name])
|
||||
|
||||
return True
|
||||
|
||||
@@ -200,7 +202,7 @@ class NWIndex():
|
||||
return False
|
||||
|
||||
theKey = theBits[0]
|
||||
if theKey in self.NOTE_KEYS:
|
||||
if theKey in self.NOVEL_KEYS:
|
||||
for aVal in theBits[1:]:
|
||||
self.noteIndex[tHandle].append([nLine, theKey, aVal])
|
||||
|
||||
@@ -254,4 +256,32 @@ class NWIndex():
|
||||
|
||||
return True, theBits, thePos
|
||||
|
||||
def checkThese(self, theBits, tItem):
|
||||
|
||||
theBits = [aBit.lower() for aBit in theBits]
|
||||
nBits = len(theBits)
|
||||
isGood = [False]*nBits
|
||||
if nBits == 0:
|
||||
return []
|
||||
|
||||
# If we have a tag, the first value is always OK, rest is ignored
|
||||
if theBits[0] == self.TAG_KEY and nBits > 1:
|
||||
isGood[0] = True
|
||||
isGood[1] = True
|
||||
return isGood
|
||||
|
||||
# If we're still here, we better check the references
|
||||
if tItem.itemClass == nwItemClass.NOVEL:
|
||||
isGood[0] = theBits[0] in self.NOVEL_KEYS
|
||||
else:
|
||||
isGood[0] = theBits[0] in self.NOTE_KEYS
|
||||
if not isGood[0] or nBits == 1:
|
||||
return isGood
|
||||
|
||||
for n in range(1,nBits):
|
||||
if theBits[n] in self.tagIndex:
|
||||
isGood[n] = self.TAG_CLASS[theBits[0]].name == self.tagIndex[theBits[n]][2]
|
||||
|
||||
return isGood
|
||||
|
||||
# END Class NWIndex
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
% Begin Meta
|
||||
@POV: Jane
|
||||
@Char: Jane, John
|
||||
@Location: Earth
|
||||
% End Meta
|
||||
|
||||
Some text here would look good as well, and maybe some "dialogue"?
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Earth
|
||||
|
||||
@tag: Earth
|
||||
|
||||
Third planet from the sun, fairly dense, and with lots of people on it.
|
||||
@@ -101,3 +101,26 @@ Start: 2019-05-30 18:26:01 End: 2019-05-30 18:27:56 Words: 0
|
||||
Start: 2019-05-30 18:28:00 End: 2019-05-30 18:30:22 Words: 0
|
||||
Start: 2019-05-30 18:42:16 End: 2019-05-30 18:43:10 Words: 0
|
||||
Start: 2019-05-30 18:43:24 End: 2019-05-30 18:43:31 Words: 0
|
||||
Start: 2019-05-30 18:48:54 End: 2019-05-30 18:49:19 Words: 0
|
||||
Start: 2019-05-30 18:49:23 End: 2019-05-30 18:49:53 Words: 0
|
||||
Start: 2019-05-30 18:51:06 End: 2019-05-30 19:02:40 Words: 0
|
||||
Start: 2019-05-30 19:07:17 End: 2019-05-30 19:07:26 Words: 0
|
||||
Start: 2019-05-30 19:17:16 End: 2019-05-30 19:17:39 Words: 0
|
||||
Start: 2019-05-30 19:17:44 End: 2019-05-30 19:17:55 Words: 0
|
||||
Start: 2019-05-30 19:18:44 End: 2019-05-30 19:19:15 Words: 0
|
||||
Start: 2019-05-30 19:19:20 End: 2019-05-30 19:19:47 Words: 0
|
||||
Start: 2019-05-30 19:21:41 End: 2019-05-30 19:21:44 Words: 0
|
||||
Start: 2019-05-30 19:24:00 End: 2019-05-30 19:24:03 Words: 0
|
||||
Start: 2019-05-30 19:24:45 End: 2019-05-30 19:26:48 Words: 0
|
||||
Start: 2019-05-30 19:28:23 End: 2019-05-30 19:32:41 Words: 0
|
||||
Start: 2019-05-30 19:32:57 End: 2019-05-30 19:33:18 Words: 0
|
||||
Start: 2019-05-30 19:33:22 End: 2019-05-30 19:33:39 Words: 0
|
||||
Start: 2019-05-30 19:34:08 End: 2019-05-30 19:34:31 Words: 0
|
||||
Start: 2019-05-30 19:34:43 End: 2019-05-30 19:34:45 Words: 0
|
||||
Start: 2019-05-30 19:34:58 End: 2019-05-30 19:36:33 Words: 0
|
||||
Start: 2019-05-30 19:36:38 End: 2019-05-30 19:36:47 Words: 0
|
||||
Start: 2019-05-30 19:43:30 End: 2019-05-30 19:43:35 Words: 0
|
||||
Start: 2019-05-30 19:43:42 End: 2019-05-30 19:43:49 Words: 0
|
||||
Start: 2019-05-30 19:45:34 End: 2019-05-30 19:45:59 Words: 0
|
||||
Start: 2019-05-30 19:53:25 End: 2019-05-30 19:55:00 Words: 0
|
||||
Start: 2019-05-30 19:55:21 End: 2019-05-30 19:56:16 Words: 0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.1.4" fileVersion="1.0" timeStamp="2019-05-30 18:43:31">
|
||||
<novelWriterXML appVersion="0.1.4" fileVersion="1.0" timeStamp="2019-05-30 19:56:16">
|
||||
<project>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
@@ -76,7 +76,7 @@
|
||||
<charCount>656</charCount>
|
||||
<wordCount>121</wordCount>
|
||||
<paraCount>5</paraCount>
|
||||
<cursorPos>729</cursorPos>
|
||||
<cursorPos>105</cursorPos>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
|
||||
<name>New File</name>
|
||||
@@ -145,7 +145,7 @@
|
||||
<charCount>76</charCount>
|
||||
<wordCount>15</wordCount>
|
||||
<paraCount>1</paraCount>
|
||||
<cursorPos>0</cursorPos>
|
||||
<cursorPos>20</cursorPos>
|
||||
</item>
|
||||
<item handle="98acd8c76c93a" order="3" parent="None">
|
||||
<name>Trash</name>
|
||||
|
||||
Reference in New Issue
Block a user