mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-05-16 19:02:32 +12:00
Checkpoint: refactoring
This commit is contained in:
parent
ad01de4cd4
commit
04fc6a5ae4
|
@ -353,3 +353,17 @@ def customIcons():
|
||||||
|
|
||||||
def statusMessage(message, duration=5000):
|
def statusMessage(message, duration=5000):
|
||||||
mainWindow().statusBar().showMessage(message, duration)
|
mainWindow().statusBar().showMessage(message, duration)
|
||||||
|
|
||||||
|
def inspect():
|
||||||
|
"""
|
||||||
|
Debugging tool. Call it to see a stack of calls up to that point.
|
||||||
|
"""
|
||||||
|
import inspect, os
|
||||||
|
print("-----------------------")
|
||||||
|
for s in inspect.stack()[1:]:
|
||||||
|
print(" * {}:{} // {}".format(
|
||||||
|
os.path.basename(s.filename),
|
||||||
|
s.lineno,
|
||||||
|
s.function))
|
||||||
|
print(" " + "".join(s.code_context))
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# --!-- coding: utf8 --!--
|
# --!-- coding: utf8 --!--
|
||||||
|
|
||||||
import locale
|
|
||||||
|
|
||||||
from PyQt5.QtCore import QAbstractItemModel, QMimeData
|
from PyQt5.QtCore import QAbstractItemModel, QMimeData
|
||||||
from PyQt5.QtCore import QModelIndex
|
from PyQt5.QtCore import QModelIndex
|
||||||
from PyQt5.QtCore import QSize
|
from PyQt5.QtCore import QSize
|
||||||
|
@ -10,22 +8,9 @@ from PyQt5.QtCore import QVariant
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtGui import QIcon, QFont
|
from PyQt5.QtGui import QIcon, QFont
|
||||||
from PyQt5.QtWidgets import QTextEdit, qApp
|
from PyQt5.QtWidgets import QTextEdit, qApp
|
||||||
|
|
||||||
from manuskript import settings
|
|
||||||
from lxml import etree as ET
|
from lxml import etree as ET
|
||||||
|
|
||||||
from manuskript.enums import Outline
|
|
||||||
from manuskript import enums
|
from manuskript import enums
|
||||||
from manuskript.functions import mainWindow, toInt, wordCount
|
|
||||||
from manuskript.converters import HTML2PlainText
|
|
||||||
|
|
||||||
try:
|
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
|
||||||
except:
|
|
||||||
# Invalid locale, but not really a big deal because it's used only for
|
|
||||||
# number formating
|
|
||||||
pass
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
class abstractItem():
|
class abstractItem():
|
||||||
|
@ -53,12 +38,12 @@ class abstractItem():
|
||||||
if xml is not None:
|
if xml is not None:
|
||||||
self.setFromXML(xml)
|
self.setFromXML(xml)
|
||||||
|
|
||||||
if parent:
|
|
||||||
parent.appendChild(self)
|
|
||||||
|
|
||||||
if ID:
|
if ID:
|
||||||
self._data[self.enum.ID] = ID
|
self._data[self.enum.ID] = ID
|
||||||
|
|
||||||
|
if parent:
|
||||||
|
parent.appendChild(self)
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Model
|
# Model
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
@ -107,7 +92,7 @@ class abstractItem():
|
||||||
return self._data.get(self.enum.title, "")
|
return self._data.get(self.enum.title, "")
|
||||||
|
|
||||||
def ID(self):
|
def ID(self):
|
||||||
return self._data.get(self.enum.ID, 0)
|
return self._data.get(self.enum.ID)
|
||||||
|
|
||||||
def columnCount(self):
|
def columnCount(self):
|
||||||
return len(self.enum)
|
return len(self.enum)
|
||||||
|
@ -197,7 +182,7 @@ class abstractItem():
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
def getUniqueID(self, recursive=False):
|
def getUniqueID(self, recursive=False):
|
||||||
self.setData(Outline.ID, self._model.rootItem.findUniqueID())
|
self.setData(self.enum.ID, self._model.rootItem.findUniqueID())
|
||||||
|
|
||||||
if recursive:
|
if recursive:
|
||||||
for c in self.children():
|
for c in self.children():
|
||||||
|
@ -241,109 +226,38 @@ class abstractItem():
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
|
||||||
def data(self, column, role=Qt.DisplayRole):
|
def data(self, column, role=Qt.DisplayRole):
|
||||||
|
# Return value in self._data
|
||||||
# print("Data: ", column, role)
|
|
||||||
|
|
||||||
if role == Qt.DisplayRole or role == Qt.EditRole:
|
if role == Qt.DisplayRole or role == Qt.EditRole:
|
||||||
# if column == Outline.compile:
|
return self._data.get(column, "")
|
||||||
# return self.data(column, Qt.CheckStateRole)
|
|
||||||
|
|
||||||
if Outline(column) in self._data:
|
# Or return QVariant
|
||||||
return self._data[Outline(column)]
|
return QVariant()
|
||||||
|
|
||||||
elif column == Outline.revisions:
|
|
||||||
return []
|
|
||||||
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
elif role == Qt.DecorationRole and column == Outline.title:
|
|
||||||
if self.customIcon():
|
|
||||||
return QIcon.fromTheme(self.data(Outline.customIcon))
|
|
||||||
if self.isFolder():
|
|
||||||
return QIcon.fromTheme("folder")
|
|
||||||
elif self.isMD():
|
|
||||||
return QIcon.fromTheme("text-x-generic")
|
|
||||||
|
|
||||||
# elif role == Qt.ForegroundRole:
|
|
||||||
# if self.isCompile() in [0, "0"]:
|
|
||||||
# return QBrush(Qt.gray)
|
|
||||||
|
|
||||||
elif role == Qt.CheckStateRole and column == Outline.compile:
|
|
||||||
# print(self.title(), self.compile())
|
|
||||||
# if self._data[Outline(column)] and not self.compile():
|
|
||||||
# return Qt.PartiallyChecked
|
|
||||||
# else:
|
|
||||||
return self._data[Outline(column)]
|
|
||||||
|
|
||||||
elif role == Qt.FontRole:
|
|
||||||
f = QFont()
|
|
||||||
if column == Outline.wordCount and self.isFolder():
|
|
||||||
f.setItalic(True)
|
|
||||||
elif column == Outline.goal and self.isFolder() and self.data(Outline.setGoal) == None:
|
|
||||||
f.setItalic(True)
|
|
||||||
if self.isFolder():
|
|
||||||
f.setBold(True)
|
|
||||||
return f
|
|
||||||
|
|
||||||
def setData(self, column, data, role=Qt.DisplayRole):
|
def setData(self, column, data, role=Qt.DisplayRole):
|
||||||
if role not in [Qt.DisplayRole, Qt.EditRole, Qt.CheckStateRole]:
|
|
||||||
print(column, column == Outline.text, data, role)
|
|
||||||
return
|
|
||||||
|
|
||||||
if column == Outline.text and self.isFolder():
|
|
||||||
# Folder have no text
|
|
||||||
return
|
|
||||||
|
|
||||||
if column == Outline.goal:
|
|
||||||
self._data[Outline.setGoal] = toInt(data) if toInt(data) > 0 else ""
|
|
||||||
|
|
||||||
# Checking if we will have to recount words
|
|
||||||
updateWordCount = False
|
|
||||||
if column in [Outline.wordCount, Outline.goal, Outline.setGoal]:
|
|
||||||
updateWordCount = not Outline(column) in self._data or self._data[Outline(column)] != data
|
|
||||||
|
|
||||||
# Stuff to do before
|
|
||||||
if column == Outline.text:
|
|
||||||
self.addRevision()
|
|
||||||
|
|
||||||
# Setting data
|
# Setting data
|
||||||
self._data[Outline(column)] = data
|
self._data[column] = data
|
||||||
|
|
||||||
# Stuff to do afterwards
|
# Emit signal
|
||||||
if column == Outline.text:
|
self.emitDataChanged(cols=[column]) # new in 0.5.0
|
||||||
wc = wordCount(data)
|
|
||||||
self.setData(Outline.wordCount, wc)
|
|
||||||
self.emitDataChanged(cols=[Outline.text]) # new in 0.5.0
|
|
||||||
|
|
||||||
if column == Outline.compile:
|
|
||||||
self.emitDataChanged(cols=[Outline.title, Outline.compile], recursive=True)
|
|
||||||
|
|
||||||
if column == Outline.customIcon:
|
|
||||||
# If custom icon changed, we tell views to update title (so that icons
|
|
||||||
# will be updated as well)
|
|
||||||
self.emitDataChanged(cols=[Outline.title])
|
|
||||||
|
|
||||||
if updateWordCount:
|
|
||||||
self.updateWordCount()
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# XML
|
# XML
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# We don't want to write some datas (computed)
|
# We don't want to write some datas (computed)
|
||||||
XMLExclude = [Outline.wordCount, Outline.goal, Outline.goalPercentage, Outline.revisions]
|
XMLExclude = []
|
||||||
# We want to force some data even if they're empty
|
# We want to force some data even if they're empty
|
||||||
XMLForce = [Outline.compile]
|
XMLForce = []
|
||||||
|
|
||||||
def toXML(self):
|
def toXML(self):
|
||||||
|
"""
|
||||||
|
Returns a string containing the item (and children) in XML.
|
||||||
|
By default, saves all attributes from self.enum and lastPath.
|
||||||
|
You can define in XMLExclude and XMLForce what you want to be
|
||||||
|
excluded or forcibly included.
|
||||||
|
"""
|
||||||
item = ET.Element(self.name)
|
item = ET.Element(self.name)
|
||||||
|
|
||||||
## We don't want to write some datas (computed)
|
|
||||||
#exclude = [Outline.wordCount, Outline.goal, Outline.goalPercentage, Outline.revisions]
|
|
||||||
## We want to force some data even if they're empty
|
|
||||||
#force = [Outline.compile]
|
|
||||||
|
|
||||||
for attrib in self.enum:
|
for attrib in self.enum:
|
||||||
if attrib in self.XMLExclude:
|
if attrib in self.XMLExclude:
|
||||||
continue
|
continue
|
||||||
|
@ -351,72 +265,42 @@ class abstractItem():
|
||||||
if val or attrib in self.XMLForce:
|
if val or attrib in self.XMLForce:
|
||||||
item.set(attrib.name, str(val))
|
item.set(attrib.name, str(val))
|
||||||
|
|
||||||
# Saving revisions
|
|
||||||
rev = self.revisions()
|
|
||||||
for r in rev:
|
|
||||||
revItem = ET.Element("revision")
|
|
||||||
revItem.set("timestamp", str(r[0]))
|
|
||||||
revItem.set("text", r[1])
|
|
||||||
item.append(revItem)
|
|
||||||
|
|
||||||
# Saving lastPath
|
# Saving lastPath
|
||||||
item.set("lastPath", self._lastPath)
|
item.set("lastPath", self._lastPath)
|
||||||
|
|
||||||
|
# Additional stuff for subclasses
|
||||||
|
item = self.toXMLProcessItem(item)
|
||||||
|
|
||||||
for i in self.childItems:
|
for i in self.childItems:
|
||||||
item.append(ET.XML(i.toXML()))
|
item.append(ET.XML(i.toXML()))
|
||||||
|
|
||||||
return ET.tostring(item)
|
return ET.tostring(item)
|
||||||
|
|
||||||
def toXML_(self):
|
def toXMLProcessItem(self, item):
|
||||||
item = ET.Element("outlineItem")
|
"""
|
||||||
|
Subclass this to change the behavior of `toXML`.
|
||||||
for attrib in Outline:
|
"""
|
||||||
if attrib in exclude: continue
|
return item
|
||||||
val = self.data(attrib.value)
|
|
||||||
if val or attrib in force:
|
|
||||||
item.set(attrib.name, str(val))
|
|
||||||
|
|
||||||
# Saving revisions
|
|
||||||
rev = self.revisions()
|
|
||||||
for r in rev:
|
|
||||||
revItem = ET.Element("revision")
|
|
||||||
revItem.set("timestamp", str(r[0]))
|
|
||||||
revItem.set("text", r[1])
|
|
||||||
item.append(revItem)
|
|
||||||
|
|
||||||
# Saving lastPath
|
|
||||||
item.set("lastPath", self._lastPath)
|
|
||||||
|
|
||||||
for i in self.childItems:
|
|
||||||
item.append(ET.XML(i.toXML()))
|
|
||||||
|
|
||||||
return ET.tostring(item)
|
|
||||||
|
|
||||||
def setFromXML(self, xml):
|
def setFromXML(self, xml):
|
||||||
root = ET.XML(xml)
|
root = ET.XML(xml)
|
||||||
|
|
||||||
for k in root.attrib:
|
for k in self.enum:
|
||||||
if k in Outline.__members__:
|
if k.name in root.attrib:
|
||||||
# if k == Outline.compile:
|
self.setData(k, str(root.attrib[k.name]))
|
||||||
# self.setData(Outline.__members__[k], unicode(root.attrib[k]), Qt.CheckStateRole)
|
|
||||||
# else:
|
|
||||||
self.setData(Outline.__members__[k], str(root.attrib[k]))
|
|
||||||
|
|
||||||
if "lastPath" in root.attrib:
|
if "lastPath" in root.attrib:
|
||||||
self._lastPath = root.attrib["lastPath"]
|
self._lastPath = root.attrib["lastPath"]
|
||||||
|
|
||||||
# If loading from an old file format, convert to md and remove html markup
|
self.setFromXMLProcessMore(root)
|
||||||
if self.type() in ["txt", "t2t"]:
|
|
||||||
self.setData(Outline.type, "md")
|
|
||||||
|
|
||||||
elif self.type() == "html":
|
|
||||||
self.setData(Outline.type, "md")
|
|
||||||
self.setData(Outline.text, HTML2PlainText(self.data(Outline.text)))
|
|
||||||
self.setData(Outline.notes, HTML2PlainText(self.data(Outline.notes)))
|
|
||||||
|
|
||||||
for child in root:
|
for child in root:
|
||||||
if child.tag == "outlineItem":
|
if child.tag == self.name:
|
||||||
item = outlineItem(self._model, xml=ET.tostring(child), parent=self)
|
item = self.__class__(self._model, xml=ET.tostring(child), parent=self)
|
||||||
elif child.tag == "revision":
|
|
||||||
self.appendRevision(child.attrib["timestamp"], child.attrib["text"])
|
|
||||||
|
|
||||||
|
def setFromXMLProcessMore(self, root):
|
||||||
|
"""
|
||||||
|
Additional stuff that subclasses must do with the XML to restore
|
||||||
|
item.
|
||||||
|
"""
|
||||||
|
return
|
||||||
|
|
|
@ -16,7 +16,6 @@ from lxml import etree as ET
|
||||||
|
|
||||||
from manuskript.enums import Outline
|
from manuskript.enums import Outline
|
||||||
from manuskript.functions import mainWindow, toInt, wordCount
|
from manuskript.functions import mainWindow, toInt, wordCount
|
||||||
from manuskript.converters import HTML2PlainText
|
|
||||||
from manuskript.models import outlineItem
|
from manuskript.models import outlineItem
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -105,13 +104,13 @@ class abstractModel(QAbstractItemModel):
|
||||||
item = search(self.rootItem)
|
item = search(self.rootItem)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def getIndexByID(self, ID):
|
def getIndexByID(self, ID, column=0):
|
||||||
"Returns the index of item whose ID is `ID`. If none, returns QModelIndex()."
|
"Returns the index of item whose ID is `ID`. If none, returns QModelIndex()."
|
||||||
item = self.getItemByID(ID)
|
item = self.getItemByID(ID)
|
||||||
if not item:
|
if not item:
|
||||||
return QModelIndex()
|
return QModelIndex()
|
||||||
else:
|
else:
|
||||||
return self.indexFromItem(item)
|
return self.indexFromItem(item, column)
|
||||||
|
|
||||||
def parent(self, index=QModelIndex()):
|
def parent(self, index=QModelIndex()):
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
|
@ -369,6 +368,7 @@ class abstractModel(QAbstractItemModel):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
items = self.decodeMimeData(data)
|
items = self.decodeMimeData(data)
|
||||||
|
|
||||||
if items is None:
|
if items is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,24 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# --!-- coding: utf8 --!--
|
# --!-- coding: utf8 --!--
|
||||||
|
|
||||||
|
import time
|
||||||
|
import locale
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtGui import QFont, QIcon
|
||||||
|
from PyQt5.QtWidgets import qApp
|
||||||
|
from lxml import etree as ET
|
||||||
from manuskript.models.abstractItem import abstractItem
|
from manuskript.models.abstractItem import abstractItem
|
||||||
from manuskript import enums
|
from manuskript import enums
|
||||||
from manuskript.functions import mainWindow, toInt
|
from manuskript import functions as F
|
||||||
from manuskript import settings
|
from manuskript import settings
|
||||||
import time
|
from manuskript.converters import HTML2PlainText
|
||||||
|
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
except:
|
||||||
|
# Invalid locale, but not really a big deal because it's used only for
|
||||||
|
# number formating
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class outlineItem(abstractItem):
|
class outlineItem(abstractItem):
|
||||||
|
@ -20,7 +32,8 @@ class outlineItem(abstractItem):
|
||||||
abstractItem.__init__(self, model, title, _type, xml, parent, ID)
|
abstractItem.__init__(self, model, title, _type, xml, parent, ID)
|
||||||
|
|
||||||
self.defaultTextType = None
|
self.defaultTextType = None
|
||||||
self._data[self.enum.compile] = Qt.Checked
|
if not self._data.get(self.enum.compile):
|
||||||
|
self._data[self.enum.compile] = 2
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Properties
|
# Properties
|
||||||
|
@ -42,7 +55,7 @@ class outlineItem(abstractItem):
|
||||||
return self.data(self.enum.text)
|
return self.data(self.enum.text)
|
||||||
|
|
||||||
def compile(self):
|
def compile(self):
|
||||||
if self._data[self.enum.compile] in ["0", 0]:
|
if self._data.get(self.enum.compile, 1) in ["0", 0]:
|
||||||
return False
|
return False
|
||||||
elif self.parent():
|
elif self.parent():
|
||||||
return self.parent().compile()
|
return self.parent().compile()
|
||||||
|
@ -64,6 +77,87 @@ class outlineItem(abstractItem):
|
||||||
def setCustomIcon(self, customIcon):
|
def setCustomIcon(self, customIcon):
|
||||||
self.setData(self.enum.customIcon, customIcon)
|
self.setData(self.enum.customIcon, customIcon)
|
||||||
|
|
||||||
|
def wordCount(self):
|
||||||
|
return self._data.get(self.enum.wordCount, 0)
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# Data
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
def data(self, column, role=Qt.DisplayRole):
|
||||||
|
|
||||||
|
data = abstractItem.data(self, column, role)
|
||||||
|
E = self.enum
|
||||||
|
|
||||||
|
if role == Qt.DisplayRole or role == Qt.EditRole:
|
||||||
|
if column == E.revisions:
|
||||||
|
return []
|
||||||
|
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
|
||||||
|
elif role == Qt.DecorationRole and column == E.title:
|
||||||
|
if self.customIcon():
|
||||||
|
return QIcon.fromTheme(self.data(E.customIcon))
|
||||||
|
if self.isFolder():
|
||||||
|
return QIcon.fromTheme("folder")
|
||||||
|
elif self.isText():
|
||||||
|
return QIcon.fromTheme("text-x-generic")
|
||||||
|
|
||||||
|
elif role == Qt.CheckStateRole and column == E.compile:
|
||||||
|
return Qt.Checked if self.compile() else Qt.Unchecked
|
||||||
|
|
||||||
|
elif role == Qt.FontRole:
|
||||||
|
f = QFont()
|
||||||
|
if column == E.wordCount and self.isFolder():
|
||||||
|
f.setItalic(True)
|
||||||
|
elif column == E.goal and self.isFolder() and not self.data(E.setGoal):
|
||||||
|
f.setItalic(True)
|
||||||
|
if self.isFolder():
|
||||||
|
f.setBold(True)
|
||||||
|
return f
|
||||||
|
|
||||||
|
def setData(self, column, data, role=Qt.DisplayRole):
|
||||||
|
|
||||||
|
E = self.enum
|
||||||
|
|
||||||
|
if column == E.text and self.isFolder():
|
||||||
|
# Folder have no text
|
||||||
|
return
|
||||||
|
|
||||||
|
if column == E.goal:
|
||||||
|
self._data[E.setGoal] = F.toInt(data) if F.toInt(data) > 0 else ""
|
||||||
|
|
||||||
|
# Checking if we will have to recount words
|
||||||
|
updateWordCount = False
|
||||||
|
if column in [E.wordCount, E.goal, E.setGoal]:
|
||||||
|
updateWordCount = not column in self._data or self._data[column] != data
|
||||||
|
|
||||||
|
# Stuff to do before
|
||||||
|
if column == E.text:
|
||||||
|
self.addRevision()
|
||||||
|
|
||||||
|
# Calling base class implementation
|
||||||
|
abstractItem.setData(self, column, data, role)
|
||||||
|
|
||||||
|
# Stuff to do afterwards
|
||||||
|
if column == E.text:
|
||||||
|
wc = F.wordCount(data)
|
||||||
|
self.setData(E.wordCount, wc)
|
||||||
|
|
||||||
|
if column == E.compile:
|
||||||
|
# Title changes when compile changes
|
||||||
|
self.emitDataChanged(cols=[E.title, E.compile],
|
||||||
|
recursive=True)
|
||||||
|
|
||||||
|
if column == E.customIcon:
|
||||||
|
# If custom icon changed, we tell views to update title (so that
|
||||||
|
# icons will be updated as well)
|
||||||
|
self.emitDataChanged(cols=[E.title])
|
||||||
|
|
||||||
|
if updateWordCount:
|
||||||
|
self.updateWordCount()
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Wordcount
|
# Wordcount
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
@ -82,23 +176,23 @@ class outlineItem(abstractItem):
|
||||||
"""Update word count for item and parents.
|
"""Update word count for item and parents.
|
||||||
If emit is False, no signal is emitted (sometimes cause segfault)"""
|
If emit is False, no signal is emitted (sometimes cause segfault)"""
|
||||||
if not self.isFolder():
|
if not self.isFolder():
|
||||||
setGoal = toInt(self.data(self.enum.setGoal))
|
setGoal = F.toInt(self.data(self.enum.setGoal))
|
||||||
goal = toInt(self.data(self.enum.goal))
|
goal = F.toInt(self.data(self.enum.goal))
|
||||||
|
|
||||||
if goal != setGoal:
|
if goal != setGoal:
|
||||||
self._data[self.enum.goal] = setGoal
|
self._data[self.enum.goal] = setGoal
|
||||||
if setGoal:
|
if setGoal:
|
||||||
wc = toInt(self.data(self.enum.wordCount))
|
wc = F.toInt(self.data(self.enum.wordCount))
|
||||||
self.setData(self.enum.goalPercentage, wc / float(setGoal))
|
self.setData(self.enum.goalPercentage, wc / float(setGoal))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
wc = 0
|
wc = 0
|
||||||
for c in self.children():
|
for c in self.children():
|
||||||
wc += toInt(c.data(self.enum.wordCount))
|
wc += F.toInt(c.data(self.enum.wordCount))
|
||||||
self._data[self.enum.wordCount] = wc
|
self._data[self.enum.wordCount] = wc
|
||||||
|
|
||||||
setGoal = toInt(self.data(self.enum.setGoal))
|
setGoal = F.toInt(self.data(self.enum.setGoal))
|
||||||
goal = toInt(self.data(self.enum.goal))
|
goal = F.toInt(self.data(self.enum.goal))
|
||||||
|
|
||||||
if setGoal:
|
if setGoal:
|
||||||
if goal != setGoal:
|
if goal != setGoal:
|
||||||
|
@ -107,7 +201,7 @@ class outlineItem(abstractItem):
|
||||||
else:
|
else:
|
||||||
goal = 0
|
goal = 0
|
||||||
for c in self.children():
|
for c in self.children():
|
||||||
goal += toInt(c.data(self.enum.goal))
|
goal += F.toInt(c.data(self.enum.goal))
|
||||||
self._data[self.enum.goal] = goal
|
self._data[self.enum.goal] = goal
|
||||||
|
|
||||||
if goal:
|
if goal:
|
||||||
|
@ -123,9 +217,9 @@ class outlineItem(abstractItem):
|
||||||
self.parent().updateWordCount(emit)
|
self.parent().updateWordCount(emit)
|
||||||
|
|
||||||
def stats(self):
|
def stats(self):
|
||||||
wc = self.data(Outline.wordCount)
|
wc = self.data(enums.Outline.wordCount)
|
||||||
goal = self.data(Outline.goal)
|
goal = self.data(enums.Outline.goal)
|
||||||
progress = self.data(Outline.goalPercentage)
|
progress = self.data(enums.Outline.goalPercentage)
|
||||||
if not wc:
|
if not wc:
|
||||||
wc = 0
|
wc = 0
|
||||||
if goal:
|
if goal:
|
||||||
|
@ -242,7 +336,8 @@ class outlineItem(abstractItem):
|
||||||
|
|
||||||
return lst
|
return lst
|
||||||
|
|
||||||
def findItemsContaining(self, text, columns, mainWindow=mainWindow(), caseSensitive=False, recursive=True):
|
def findItemsContaining(self, text, columns, mainWindow=F.mainWindow(),
|
||||||
|
caseSensitive=False, recursive=True):
|
||||||
"""Returns a list if IDs of all subitems
|
"""Returns a list if IDs of all subitems
|
||||||
containing ``text`` in columns ``columns``
|
containing ``text`` in columns ``columns``
|
||||||
(being a list of int).
|
(being a list of int).
|
||||||
|
@ -255,7 +350,8 @@ class outlineItem(abstractItem):
|
||||||
|
|
||||||
return lst
|
return lst
|
||||||
|
|
||||||
def itemContains(self, text, columns, mainWindow=mainWindow(), caseSensitive=False):
|
def itemContains(self, text, columns, mainWindow=F.mainWindow(),
|
||||||
|
caseSensitive=False):
|
||||||
lst = []
|
lst = []
|
||||||
text = text.lower() if not caseSensitive else text
|
text = text.lower() if not caseSensitive else text
|
||||||
for c in columns:
|
for c in columns:
|
||||||
|
@ -269,10 +365,10 @@ class outlineItem(abstractItem):
|
||||||
print("Character POV not found:", self.POV())
|
print("Character POV not found:", self.POV())
|
||||||
|
|
||||||
elif c == self.enum.status:
|
elif c == self.enum.status:
|
||||||
searchIn = mainWindow.mdlStatus.item(toInt(self.status()), 0).text()
|
searchIn = mainWindow.mdlStatus.item(F.toInt(self.status()), 0).text()
|
||||||
|
|
||||||
elif c == self.enum.label:
|
elif c == self.enum.label:
|
||||||
searchIn = mainWindow.mdlLabels.item(toInt(self.label()), 0).text()
|
searchIn = mainWindow.mdlLabels.item(F.toInt(self.label()), 0).text()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
searchIn = self.data(c)
|
searchIn = self.data(c)
|
||||||
|
@ -309,7 +405,7 @@ class outlineItem(abstractItem):
|
||||||
|
|
||||||
self.appendRevision(
|
self.appendRevision(
|
||||||
time.time(),
|
time.time(),
|
||||||
self._data[self.enum.text])
|
self.text())
|
||||||
|
|
||||||
if settings.revisions["smartremove"]:
|
if settings.revisions["smartremove"]:
|
||||||
self.cleanRevisions()
|
self.cleanRevisions()
|
||||||
|
@ -358,7 +454,44 @@ class outlineItem(abstractItem):
|
||||||
self._data[self.enum.revisions] = rev2
|
self._data[self.enum.revisions] = rev2
|
||||||
self.emitDataChanged([self.enum.revisions])
|
self.emitDataChanged([self.enum.revisions])
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# XML
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
# We don't want to write some datas (computed)
|
||||||
|
XMLExclude = [enums.Outline.wordCount,
|
||||||
|
enums.Outline.goal,
|
||||||
|
enums.Outline.goalPercentage,
|
||||||
|
enums.Outline.revisions]
|
||||||
|
# We want to force some data even if they're empty
|
||||||
|
XMLForce = [enums.Outline.compile]
|
||||||
|
|
||||||
|
def toXMLProcessItem(self, item):
|
||||||
|
|
||||||
|
# Saving revisions
|
||||||
|
rev = self.revisions()
|
||||||
|
for r in rev:
|
||||||
|
revItem = ET.Element("revision")
|
||||||
|
revItem.set("timestamp", str(r[0]))
|
||||||
|
revItem.set("text", r[1])
|
||||||
|
item.append(revItem)
|
||||||
|
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def setFromXMLProcessMore(self, root):
|
||||||
|
|
||||||
|
# If loading from an old file format, convert to md and
|
||||||
|
# remove html markup
|
||||||
|
if self.type() in ["txt", "t2t"]:
|
||||||
|
self.setData(Outline.type, "md")
|
||||||
|
|
||||||
|
elif self.type() == "html":
|
||||||
|
self.setData(Outline.type, "md")
|
||||||
|
self.setData(Outline.text, HTML2PlainText(self.data(Outline.text)))
|
||||||
|
self.setData(Outline.notes, HTML2PlainText(self.data(Outline.notes)))
|
||||||
|
|
||||||
|
# Revisions
|
||||||
|
for child in root:
|
||||||
|
if child.tag == "revision":
|
||||||
|
self.appendRevision(child.attrib["timestamp"], child.attrib["text"])
|
||||||
|
|
|
@ -43,13 +43,7 @@ class chkOutlineCompile(QCheckBox):
|
||||||
|
|
||||||
def getCheckedValue(self, index):
|
def getCheckedValue(self, index):
|
||||||
item = index.internalPointer()
|
item = index.internalPointer()
|
||||||
c = item.data(Outline.compile)
|
return Qt.Checked if item.compile() else Qt.Unchecked
|
||||||
if c:
|
|
||||||
c = int(c)
|
|
||||||
else:
|
|
||||||
c = Qt.Unchecked
|
|
||||||
|
|
||||||
return c
|
|
||||||
|
|
||||||
def update(self, topLeft, bottomRight):
|
def update(self, topLeft, bottomRight):
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,10 @@ class outlineBasics(QAbstractItemView):
|
||||||
if event.button() == Qt.RightButton:
|
if event.button() == Qt.RightButton:
|
||||||
self.menu = self.makePopupMenu()
|
self.menu = self.makePopupMenu()
|
||||||
self.menu.popup(event.globalPos())
|
self.menu.popup(event.globalPos())
|
||||||
else:
|
# We don't call QAbstractItemView.mouseReleaseEvent because
|
||||||
QAbstractItemView.mouseReleaseEvent(self, event)
|
# outlineBasics is never subclassed alone. So the others views
|
||||||
|
# (outlineView, corkView, treeView) that subclass outlineBasics
|
||||||
|
# call their respective mother class.
|
||||||
|
|
||||||
def makePopupMenu(self):
|
def makePopupMenu(self):
|
||||||
index = self.currentIndex()
|
index = self.currentIndex()
|
||||||
|
|
|
@ -195,6 +195,9 @@ class outlineCompileDelegate(QStyledItemDelegate):
|
||||||
def displayText(self, value, locale):
|
def displayText(self, value, locale):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
#def createEditor(self, parent, option, index):
|
||||||
|
#return None
|
||||||
|
|
||||||
|
|
||||||
class outlineGoalPercentageDelegate(QStyledItemDelegate):
|
class outlineGoalPercentageDelegate(QStyledItemDelegate):
|
||||||
def __init__(self, rootIndex=None, parent=None):
|
def __init__(self, rootIndex=None, parent=None):
|
||||||
|
|
|
@ -39,6 +39,10 @@ class textEditView(QTextEdit):
|
||||||
self.setAcceptRichText(False)
|
self.setAcceptRichText(False)
|
||||||
# When setting up a theme, this becomes true.
|
# When setting up a theme, this becomes true.
|
||||||
self._fromTheme = False
|
self._fromTheme = False
|
||||||
|
# Sometimes we need to update index because item has changed its
|
||||||
|
# position, so we only have it's ID as reference. We store it to
|
||||||
|
# update at the propper time.
|
||||||
|
self._updateIndexFromID = None
|
||||||
|
|
||||||
self.spellcheck = spellcheck
|
self.spellcheck = spellcheck
|
||||||
self.currentDict = dict if dict else settings.dict
|
self.currentDict = dict if dict else settings.dict
|
||||||
|
@ -271,7 +275,15 @@ class textEditView(QTextEdit):
|
||||||
if self._updating:
|
if self._updating:
|
||||||
return
|
return
|
||||||
|
|
||||||
elif self._index and self._index.isValid():
|
if self._updateIndexFromID:
|
||||||
|
# We have to update to a new index
|
||||||
|
self._index = self._index.model().getIndexByID(
|
||||||
|
self._updateIndexFromID,
|
||||||
|
self._column)
|
||||||
|
self._updateIndexFromID = None
|
||||||
|
|
||||||
|
if self._index and self._index.isValid():
|
||||||
|
|
||||||
if topLeft.parent() != self._index.parent():
|
if topLeft.parent() != self._index.parent():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -294,13 +306,34 @@ class textEditView(QTextEdit):
|
||||||
self.updateText()
|
self.updateText()
|
||||||
|
|
||||||
def rowsAboutToBeRemoved(self, parent, first, last):
|
def rowsAboutToBeRemoved(self, parent, first, last):
|
||||||
if self._index:
|
if self._index and self._index.isValid():
|
||||||
|
|
||||||
|
# Has my _index just been removed?
|
||||||
if self._index.parent() == parent and \
|
if self._index.parent() == parent and \
|
||||||
first <= self._index.row() <= last:
|
first <= self._index.row() <= last:
|
||||||
self._index = None
|
self._index = None
|
||||||
self.setEnabled(False)
|
self.setEnabled(False)
|
||||||
|
return
|
||||||
# FIXME: self._indexes
|
# FIXME: self._indexes
|
||||||
|
|
||||||
|
# We check if item is a child of the row about to be removed
|
||||||
|
child = False
|
||||||
|
p = self._index.parent()
|
||||||
|
while p:
|
||||||
|
if p == parent:
|
||||||
|
child = True
|
||||||
|
p = None
|
||||||
|
elif p.isValid():
|
||||||
|
p = p.parent()
|
||||||
|
else:
|
||||||
|
p = None
|
||||||
|
if child:
|
||||||
|
# Item might have moved (so will not be valid any more)
|
||||||
|
ID = self._index.internalPointer().ID()
|
||||||
|
# We store ID, and we update it in self.update (after the
|
||||||
|
# rows have been removed).
|
||||||
|
self._updateIndexFromID = ID
|
||||||
|
|
||||||
def disconnectDocument(self):
|
def disconnectDocument(self):
|
||||||
try:
|
try:
|
||||||
self.document().contentsChanged.disconnect(self.updateTimer.start)
|
self.document().contentsChanged.disconnect(self.updateTimer.start)
|
||||||
|
|
|
@ -94,7 +94,7 @@ class treeTitleDelegate(QStyledItemDelegate):
|
||||||
# If text color is Compile and item is selected, we have
|
# If text color is Compile and item is selected, we have
|
||||||
# to change the color
|
# to change the color
|
||||||
if settings.viewSettings["Outline"]["Text"] == "Compile" and \
|
if settings.viewSettings["Outline"]["Text"] == "Compile" and \
|
||||||
item.compile() in [0, "0"]:
|
not item.compile():
|
||||||
col = mixColors(textColor, QColor(S.window))
|
col = mixColors(textColor, QColor(S.window))
|
||||||
painter.setPen(col)
|
painter.setPen(col)
|
||||||
f = QFont(opt.font)
|
f = QFont(opt.font)
|
||||||
|
@ -109,7 +109,7 @@ class treeTitleDelegate(QStyledItemDelegate):
|
||||||
extraText = item.childCount()
|
extraText = item.childCount()
|
||||||
extraText = " [{}]".format(extraText)
|
extraText = " [{}]".format(extraText)
|
||||||
elif settings.viewSettings["Tree"]["InfoFolder"] == "WC":
|
elif settings.viewSettings["Tree"]["InfoFolder"] == "WC":
|
||||||
extraText = item.data(Outline.wordCount)
|
extraText = item.wordCount()
|
||||||
extraText = " ({})".format(extraText)
|
extraText = " ({})".format(extraText)
|
||||||
elif settings.viewSettings["Tree"]["InfoFolder"] == "Progress":
|
elif settings.viewSettings["Tree"]["InfoFolder"] == "Progress":
|
||||||
extraText = int(toFloat(item.data(Outline.goalPercentage)) * 100)
|
extraText = int(toFloat(item.data(Outline.goalPercentage)) * 100)
|
||||||
|
@ -122,7 +122,7 @@ class treeTitleDelegate(QStyledItemDelegate):
|
||||||
|
|
||||||
if item.isText() and settings.viewSettings["Tree"]["InfoText"] != "Nothing":
|
if item.isText() and settings.viewSettings["Tree"]["InfoText"] != "Nothing":
|
||||||
if settings.viewSettings["Tree"]["InfoText"] == "WC":
|
if settings.viewSettings["Tree"]["InfoText"] == "WC":
|
||||||
extraText = item.data(Outline.wordCount)
|
extraText = item.wordCount()
|
||||||
extraText = " ({})".format(extraText)
|
extraText = " ({})".format(extraText)
|
||||||
elif settings.viewSettings["Tree"]["InfoText"] == "Progress":
|
elif settings.viewSettings["Tree"]["InfoText"] == "Progress":
|
||||||
extraText = int(toFloat(item.data(Outline.goalPercentage)) * 100)
|
extraText = int(toFloat(item.data(Outline.goalPercentage)) * 100)
|
||||||
|
|
Loading…
Reference in a new issue