2015-06-02 10:06:17 +12:00
|
|
|
#!/usr/bin/env python
|
|
|
|
#--!-- coding: utf8 --!--
|
|
|
|
|
2015-06-04 04:40:19 +12:00
|
|
|
from qt import *
|
2015-06-04 12:04:47 +12:00
|
|
|
from enums import *
|
2015-06-02 10:06:17 +12:00
|
|
|
from enum import Enum
|
2015-06-03 00:40:48 +12:00
|
|
|
from lxml import etree as ET
|
2015-06-05 06:22:37 +12:00
|
|
|
from functions import *
|
2015-07-04 09:00:54 +12:00
|
|
|
import settings
|
2015-06-30 22:27:43 +12:00
|
|
|
import locale
|
|
|
|
locale.setlocale(locale.LC_ALL, '')
|
2015-07-04 04:41:18 +12:00
|
|
|
import time
|
2015-06-05 06:22:37 +12:00
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
class outlineModel(QAbstractItemModel):
|
2015-06-04 12:51:37 +12:00
|
|
|
|
2015-06-25 06:32:50 +12:00
|
|
|
def __init__(self, parent):
|
|
|
|
QAbstractItemModel.__init__(self, parent)
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-27 03:33:55 +12:00
|
|
|
self.rootItem = outlineItem(self, title="root", ID="0")
|
2015-06-02 10:06:17 +12:00
|
|
|
|
|
|
|
def index(self, row, column, parent):
|
|
|
|
|
|
|
|
if not self.hasIndex(row, column, parent):
|
|
|
|
return QModelIndex()
|
|
|
|
|
|
|
|
if not parent.isValid():
|
|
|
|
parentItem = self.rootItem
|
|
|
|
else:
|
|
|
|
parentItem = parent.internalPointer()
|
|
|
|
|
|
|
|
childItem = parentItem.child(row)
|
|
|
|
if childItem:
|
|
|
|
return self.createIndex(row, column, childItem)
|
|
|
|
else:
|
|
|
|
return QModelIndex()
|
|
|
|
|
2015-06-05 21:37:01 +12:00
|
|
|
def indexFromItem(self, item, column=0):
|
|
|
|
if item == self.rootItem:
|
2015-06-16 06:30:18 +12:00
|
|
|
return QModelIndex()
|
2015-06-05 21:37:01 +12:00
|
|
|
|
|
|
|
parent = item.parent()
|
|
|
|
if not parent:
|
|
|
|
parent = self.rootItem
|
|
|
|
|
|
|
|
if len(parent.children()) == 0:
|
|
|
|
return None
|
|
|
|
|
|
|
|
#print(item.title(), [i.title() for i in parent.children()])
|
|
|
|
|
|
|
|
row = parent.children().index(item)
|
|
|
|
col = column
|
|
|
|
return self.createIndex(row, col, item)
|
|
|
|
|
2015-06-30 01:34:03 +12:00
|
|
|
def ID(self, index):
|
|
|
|
if index.isValid():
|
|
|
|
item = index.internalPointer()
|
|
|
|
return item.ID()
|
|
|
|
|
2015-07-01 00:01:32 +12:00
|
|
|
def findItemsByPOV(self, POV):
|
|
|
|
"Returns a list of IDs of all items whose POV is ``POV``."
|
|
|
|
return self.rootItem.findItemsByPOV(POV)
|
|
|
|
|
2015-07-01 01:38:14 +12:00
|
|
|
def findItemsContaining(self, text, columns, caseSensitive=False):
|
|
|
|
"""Returns a list of IDs of all items containing ``text``
|
|
|
|
in columns ``columns`` (being a list of int)."""
|
|
|
|
return self.rootItem.findItemsContaining(text, columns, mainWindow(), caseSensitive)
|
2015-07-01 00:01:32 +12:00
|
|
|
|
2015-06-27 10:22:46 +12:00
|
|
|
def getIndexByID(self, ID):
|
|
|
|
"Returns the index of item whose ID is ``ID``. If none, returns QModelIndex()."
|
|
|
|
def search(item):
|
|
|
|
if item.ID() == ID:
|
|
|
|
return item
|
|
|
|
for c in item.children():
|
|
|
|
r = search(c)
|
|
|
|
if r:
|
|
|
|
return r
|
|
|
|
item = search(self.rootItem)
|
|
|
|
if not item:
|
|
|
|
return QModelIndex()
|
|
|
|
else:
|
|
|
|
return self.indexFromItem(item)
|
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
def parent(self, index=QModelIndex()):
|
|
|
|
if not index.isValid():
|
|
|
|
return QModelIndex()
|
|
|
|
|
|
|
|
childItem = index.internalPointer()
|
2015-07-03 06:10:25 +12:00
|
|
|
try:
|
|
|
|
parentItem = childItem.parent()
|
|
|
|
except AttributeError:
|
|
|
|
import traceback, sys
|
|
|
|
print(traceback.print_exc())
|
|
|
|
print(sys.exc_info()[0])
|
|
|
|
return QModelIndex()
|
2015-06-02 10:06:17 +12:00
|
|
|
|
|
|
|
if parentItem == self.rootItem:
|
|
|
|
return QModelIndex()
|
|
|
|
|
|
|
|
return self.createIndex(parentItem.row(), 0, parentItem)
|
|
|
|
|
|
|
|
def rowCount(self, parent=QModelIndex()):
|
|
|
|
if parent.column() > 0:
|
|
|
|
return 0
|
|
|
|
|
|
|
|
if not parent.isValid():
|
|
|
|
parentItem = self.rootItem
|
|
|
|
else:
|
|
|
|
parentItem = parent.internalPointer()
|
|
|
|
|
|
|
|
return parentItem.childCount()
|
|
|
|
|
|
|
|
def columnCount(self, parent=QModelIndex()):
|
|
|
|
if parent.isValid():
|
|
|
|
return parent.internalPointer().columnCount()
|
|
|
|
else:
|
|
|
|
return self.rootItem.columnCount()
|
|
|
|
|
|
|
|
def data(self, index, role=Qt.DisplayRole):
|
|
|
|
if not index.isValid():
|
|
|
|
return QVariant()
|
|
|
|
|
|
|
|
item = index.internalPointer()
|
|
|
|
return item.data(index.column(), role)
|
|
|
|
|
|
|
|
def setData(self, index, value, role=Qt.EditRole):
|
|
|
|
item = index.internalPointer()
|
2015-06-18 04:40:55 +12:00
|
|
|
if item.data(index.column(), role) != value:
|
2015-06-25 10:05:01 +12:00
|
|
|
|
2015-06-18 04:40:55 +12:00
|
|
|
item.setData(index.column(), value, role)
|
2015-06-04 12:51:37 +12:00
|
|
|
|
2015-06-25 04:39:38 +12:00
|
|
|
#self.dataChanged.emit(index.sibling(index.row(), 0),
|
|
|
|
#index.sibling(index.row(), max([i.value for i in Outline])))
|
2015-06-25 10:05:01 +12:00
|
|
|
#print("Model emit", index.row(), index.column())
|
2015-06-25 20:50:30 +12:00
|
|
|
self.dataChanged.emit(index, index)
|
|
|
|
|
|
|
|
if index.column() == Outline.type.value:
|
|
|
|
# If type changed, then the icon of title changed.
|
|
|
|
# Some views might be glad to know it.
|
|
|
|
self.dataChanged.emit(index.sibling(index.row(), Outline.title.value),
|
|
|
|
index.sibling(index.row(), Outline.title.value))
|
2015-06-25 04:39:38 +12:00
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
return True
|
2015-06-03 00:40:48 +12:00
|
|
|
|
|
|
|
|
|
|
|
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
2015-06-18 04:40:55 +12:00
|
|
|
if orientation == Qt.Horizontal and role in [Qt.DisplayRole, Qt.ToolTipRole]:
|
2015-06-06 11:13:27 +12:00
|
|
|
if section == Outline.title.value:
|
2015-06-18 04:40:55 +12:00
|
|
|
return self.tr("Title")
|
|
|
|
elif section == Outline.POV.value:
|
|
|
|
return self.tr("POV")
|
|
|
|
elif section == Outline.label.value:
|
|
|
|
return self.tr("Label")
|
|
|
|
elif section == Outline.status.value:
|
|
|
|
return self.tr("Status")
|
|
|
|
elif section == Outline.compile.value:
|
|
|
|
return self.tr("Compile")
|
2015-06-06 11:13:27 +12:00
|
|
|
elif section == Outline.wordCount.value:
|
2015-06-18 04:40:55 +12:00
|
|
|
return self.tr("Word count")
|
2015-06-06 11:13:27 +12:00
|
|
|
elif section == Outline.goal.value:
|
2015-06-18 04:40:55 +12:00
|
|
|
return self.tr("Goal")
|
2015-06-06 11:13:27 +12:00
|
|
|
elif section == Outline.goalPercentage.value:
|
|
|
|
return "%"
|
|
|
|
else:
|
|
|
|
return [i.name for i in Outline][section]
|
|
|
|
|
|
|
|
elif role == Qt.SizeHintRole:
|
|
|
|
if section == Outline.compile.value:
|
|
|
|
return QSize(40, 30)
|
|
|
|
elif section == Outline.goalPercentage.value:
|
|
|
|
return QSize(100, 30)
|
|
|
|
else:
|
|
|
|
return QVariant()
|
2015-06-03 00:40:48 +12:00
|
|
|
else:
|
|
|
|
return QVariant()
|
2015-06-06 11:13:27 +12:00
|
|
|
|
|
|
|
|
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
return True
|
|
|
|
|
|
|
|
#################### DRAG AND DROP ########################
|
|
|
|
# http://doc.qt.io/qt-5/model-view-programming.html#using-drag-and-drop-with-item-views
|
2015-06-02 10:06:17 +12:00
|
|
|
|
|
|
|
def flags(self, index):
|
2015-06-04 12:04:47 +12:00
|
|
|
#FIXME when dragging folders, sometimes flags is not called
|
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
flags = QAbstractItemModel.flags(self, index) | Qt.ItemIsEditable
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-08 23:53:33 +12:00
|
|
|
if index.isValid() and index.internalPointer().isFolder() and index.column() == 0:
|
2015-06-04 12:04:47 +12:00
|
|
|
flags |= Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled
|
2015-06-03 00:40:48 +12:00
|
|
|
|
2015-06-08 23:53:33 +12:00
|
|
|
elif index.isValid() and index.column() == 0:
|
2015-06-03 00:40:48 +12:00
|
|
|
flags |= Qt.ItemIsDragEnabled
|
|
|
|
|
2015-06-08 23:53:33 +12:00
|
|
|
elif not index.isValid():
|
2015-06-02 10:06:17 +12:00
|
|
|
flags |= Qt.ItemIsDropEnabled
|
2015-06-04 12:04:47 +12:00
|
|
|
|
|
|
|
if index.isValid() and index.column() == Outline.compile.value:
|
|
|
|
flags |= Qt.ItemIsUserCheckable
|
|
|
|
|
2015-06-05 21:37:01 +12:00
|
|
|
if index.column() in [i.value for i in [Outline.wordCount, Outline.goalPercentage]]:
|
|
|
|
flags &= ~ Qt.ItemIsEditable
|
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
return flags
|
2015-06-03 00:40:48 +12:00
|
|
|
|
|
|
|
def mimeTypes(self):
|
|
|
|
return ["application/xml"]
|
|
|
|
|
|
|
|
def mimeData(self, indexes):
|
|
|
|
mimeData = QMimeData()
|
|
|
|
encodedData = ""
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
root = ET.Element("outlineItems")
|
2015-06-04 12:04:47 +12:00
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
for index in indexes:
|
2015-06-04 12:04:47 +12:00
|
|
|
if index.isValid() and index.column() == 0:
|
2015-06-03 00:40:48 +12:00
|
|
|
item = ET.XML(index.internalPointer().toXML())
|
|
|
|
root.append(item)
|
|
|
|
|
|
|
|
encodedData = ET.tostring(root)
|
|
|
|
|
|
|
|
mimeData.setData("application/xml", encodedData)
|
|
|
|
return mimeData
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
def supportedDropActions(self):
|
|
|
|
|
2015-06-09 22:32:43 +12:00
|
|
|
return Qt.CopyAction | Qt.MoveAction
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-08 23:53:33 +12:00
|
|
|
#def canDropMimeData(self, data, action, row, column, parent):
|
|
|
|
#if not data.hasFormat("application/xml"):
|
|
|
|
#return False
|
2015-06-04 04:40:19 +12:00
|
|
|
|
2015-06-08 23:53:33 +12:00
|
|
|
#if column > 0:
|
|
|
|
#return False
|
2015-06-04 04:40:19 +12:00
|
|
|
|
2015-06-08 23:53:33 +12:00
|
|
|
#return True
|
2015-06-04 04:40:19 +12:00
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
def dropMimeData(self, data, action, row, column, parent):
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
if action == Qt.IgnoreAction:
|
|
|
|
return True # What is that?
|
|
|
|
|
|
|
|
if not data.hasFormat("application/xml"):
|
|
|
|
return False
|
|
|
|
|
|
|
|
if column > 0:
|
|
|
|
column = 0
|
|
|
|
|
2015-06-08 08:06:57 +12:00
|
|
|
if row != -1:
|
2015-06-03 00:40:48 +12:00
|
|
|
beginRow = row
|
|
|
|
elif parent.isValid():
|
|
|
|
beginRow = self.rowCount(parent) + 1
|
|
|
|
else:
|
|
|
|
beginRow = self.rowCount() + 1
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-08 23:53:33 +12:00
|
|
|
encodedData = bytes(data.data("application/xml")).decode()
|
2015-06-04 12:04:47 +12:00
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
root = ET.XML(encodedData)
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-08 08:06:57 +12:00
|
|
|
if root.tag != "outlineItems":
|
2015-06-03 00:40:48 +12:00
|
|
|
return False
|
|
|
|
|
|
|
|
items = []
|
|
|
|
for child in root:
|
|
|
|
if child.tag == "outlineItem":
|
2015-06-30 01:34:03 +12:00
|
|
|
item = outlineItem(xml=ET.tostring(child))
|
|
|
|
items.append(item)
|
2015-06-03 00:40:48 +12:00
|
|
|
|
|
|
|
if not items:
|
|
|
|
return False
|
|
|
|
|
2015-06-30 01:34:03 +12:00
|
|
|
r = self.insertItems(items, beginRow, parent)
|
|
|
|
|
|
|
|
if action == Qt.CopyAction:
|
|
|
|
for item in items:
|
|
|
|
item.getUniqueID()
|
|
|
|
|
|
|
|
return r
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
################# ADDING AND REMOVING #################
|
|
|
|
|
|
|
|
def insertItem(self, item, row, parent=QModelIndex()):
|
|
|
|
return self.insertItems([item], row, parent)
|
|
|
|
|
|
|
|
def insertItems(self, items, row, parent=QModelIndex()):
|
2015-06-02 10:06:17 +12:00
|
|
|
if not parent.isValid():
|
|
|
|
parentItem = self.rootItem
|
|
|
|
else:
|
|
|
|
parentItem = parent.internalPointer()
|
2015-06-03 00:40:48 +12:00
|
|
|
|
2015-06-08 08:06:57 +12:00
|
|
|
if parent.isValid() and parent.column() != 0:
|
2015-06-05 21:37:01 +12:00
|
|
|
parent = parentItem.index()
|
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
# Insert only if parent is folder
|
2015-06-02 10:06:17 +12:00
|
|
|
if parentItem.isFolder():
|
2015-06-03 00:40:48 +12:00
|
|
|
self.beginInsertRows(parent, row, row + len(items) - 1)
|
|
|
|
|
|
|
|
for i in items:
|
|
|
|
parentItem.insertChild(row + items.index(i), i)
|
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
self.endInsertRows()
|
2015-06-03 00:40:48 +12:00
|
|
|
|
2015-06-06 11:13:27 +12:00
|
|
|
return True
|
|
|
|
|
|
|
|
else:
|
|
|
|
return False
|
2015-06-03 00:40:48 +12:00
|
|
|
|
|
|
|
def appendItem(self, item, parent=QModelIndex()):
|
|
|
|
if not parent.isValid():
|
|
|
|
parentItem = self.rootItem
|
|
|
|
else:
|
|
|
|
parentItem = parent.internalPointer()
|
|
|
|
|
2015-06-08 08:06:57 +12:00
|
|
|
if parent.isValid() and parent.column() != 0:
|
2015-06-05 21:37:01 +12:00
|
|
|
parent = parentItem.index()
|
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
# If parent is folder, write into
|
|
|
|
if parentItem.isFolder():
|
|
|
|
self.insertItem(item, self.rowCount(parent), parent)
|
|
|
|
|
|
|
|
# If parent is not folder, write next to
|
|
|
|
else:
|
|
|
|
self.insertItem(item, parent.row()+1, parent.parent())
|
2015-06-02 10:06:17 +12:00
|
|
|
|
|
|
|
def removeIndex(self, index):
|
|
|
|
item = index.internalPointer()
|
2015-06-03 00:40:48 +12:00
|
|
|
self.removeRow(item.row(), index.parent())
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-07-03 06:10:25 +12:00
|
|
|
def removeIndexes(self, indexes):
|
|
|
|
levels = {}
|
|
|
|
for i in indexes:
|
|
|
|
item = i.internalPointer()
|
|
|
|
level = item.level()
|
|
|
|
if not level in levels:
|
|
|
|
levels[level] = []
|
|
|
|
levels[level].append([i.row(), i])
|
|
|
|
|
|
|
|
# Sort by level then by row
|
|
|
|
for l in reversed(sorted(levels.keys())):
|
|
|
|
rows = levels[l]
|
|
|
|
|
|
|
|
rows = list(reversed(sorted(rows, key=lambda x:x[0])))
|
|
|
|
for r in rows:
|
|
|
|
self.removeIndex(r[1])
|
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
def removeRow(self, row, parent=QModelIndex()):
|
|
|
|
return self.removeRows(row, 1, parent)
|
|
|
|
|
|
|
|
def removeRows(self, row, count, parent=QModelIndex()):
|
2015-06-02 10:06:17 +12:00
|
|
|
if not parent.isValid():
|
|
|
|
parentItem = self.rootItem
|
|
|
|
else:
|
|
|
|
parentItem = parent.internalPointer()
|
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
self.beginRemoveRows(parent, row, row + count - 1)
|
|
|
|
for i in range(count):
|
|
|
|
parentItem.removeChild(row)
|
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
self.endRemoveRows()
|
|
|
|
return True
|
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
#def insertRow(self, row, item, parent=QModelIndex()):
|
|
|
|
#self.beginInsertRows(parent, row, row)
|
|
|
|
|
|
|
|
#if not parent.isValid():
|
|
|
|
#parentItem = self.rootItem
|
|
|
|
#else:
|
|
|
|
#parentItem = parent.internalPointer()
|
|
|
|
|
|
|
|
#parentItem.insertChild(row, item)
|
|
|
|
|
|
|
|
#self.endInsertRows()
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
|
2015-06-16 06:30:18 +12:00
|
|
|
################# XML / saving / loading #################
|
2015-06-03 00:40:48 +12:00
|
|
|
|
2015-06-17 23:25:46 +12:00
|
|
|
def saveToXML(self, xml=None):
|
|
|
|
"If xml (filename) is given, saves the items to xml. Otherwise returns as string."
|
2015-06-03 00:40:48 +12:00
|
|
|
root = ET.XML(self.rootItem.toXML())
|
2015-06-17 23:25:46 +12:00
|
|
|
if xml:
|
|
|
|
ET.ElementTree(root).write(xml, encoding="UTF-8", xml_declaration=True, pretty_print=True)
|
|
|
|
else:
|
|
|
|
return ET.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True)
|
2015-06-03 00:40:48 +12:00
|
|
|
|
2015-06-17 23:25:46 +12:00
|
|
|
def loadFromXML(self, xml, fromString=False):
|
|
|
|
"Load from xml. Assume that xml is a filename. If fromString=True, xml is the content."
|
|
|
|
if not fromString:
|
2015-06-04 04:40:19 +12:00
|
|
|
root = ET.parse(xml)
|
2015-06-17 23:25:46 +12:00
|
|
|
else:
|
|
|
|
root = ET.fromstring(xml)
|
|
|
|
|
2015-06-27 03:33:55 +12:00
|
|
|
self.rootItem = outlineItem(model=self, xml=ET.tostring(root), ID="0")
|
|
|
|
self.rootItem.checkIDs()
|
2015-06-16 06:30:18 +12:00
|
|
|
|
|
|
|
def pathToIndex(self, index, path=""):
|
2015-06-27 10:22:46 +12:00
|
|
|
# FIXME: Use item's ID instead of rows
|
2015-06-16 06:30:18 +12:00
|
|
|
if not index.isValid():
|
|
|
|
return ""
|
|
|
|
if index.parent().isValid():
|
|
|
|
path = self.pathToIndex(index.parent())
|
|
|
|
if path:
|
|
|
|
path = "{},{}".format(path, str(index.row()))
|
|
|
|
else:
|
|
|
|
path = str(index.row())
|
|
|
|
|
|
|
|
return path
|
2015-06-04 12:51:37 +12:00
|
|
|
|
2015-06-16 06:30:18 +12:00
|
|
|
def indexFromPath(self, path):
|
|
|
|
path = path.split(",")
|
|
|
|
item = self.rootItem
|
|
|
|
for p in path:
|
2015-06-30 01:34:03 +12:00
|
|
|
if p != "" and int(p) < item.childCount():
|
2015-06-16 06:30:18 +12:00
|
|
|
item = item.child(int(p))
|
|
|
|
return self.indexFromItem(item)
|
2015-06-03 00:40:48 +12:00
|
|
|
|
|
|
|
class outlineItem():
|
|
|
|
|
2015-06-27 03:33:55 +12:00
|
|
|
def __init__(self, model=None, title="", _type="folder", xml=None, parent=None, ID=None):
|
2015-06-02 10:06:17 +12:00
|
|
|
|
|
|
|
self._data = {}
|
|
|
|
self.childItems = []
|
2015-06-05 21:37:01 +12:00
|
|
|
self._parent = None
|
|
|
|
self._model = model
|
2015-06-25 01:43:35 +12:00
|
|
|
self.defaultTextType = None
|
2015-06-27 03:33:55 +12:00
|
|
|
self.IDs = [] # used by root item to store unique IDs
|
2015-06-25 01:43:35 +12:00
|
|
|
|
|
|
|
if title:
|
|
|
|
self._data[Outline.title] = title
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-25 01:43:35 +12:00
|
|
|
self._data[Outline.type] = _type
|
2015-06-05 21:37:01 +12:00
|
|
|
self._data[Outline.compile] = Qt.Checked
|
2015-06-25 01:43:35 +12:00
|
|
|
|
2015-06-04 04:40:19 +12:00
|
|
|
if xml is not None:
|
2015-06-03 00:40:48 +12:00
|
|
|
self.setFromXML(xml)
|
2015-06-27 03:33:55 +12:00
|
|
|
|
|
|
|
if parent:
|
|
|
|
parent.appendChild(self)
|
|
|
|
|
|
|
|
if ID:
|
|
|
|
self._data[Outline.ID] = ID
|
2015-06-03 00:40:48 +12:00
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
|
|
|
|
def child(self, row):
|
|
|
|
return self.childItems[row]
|
|
|
|
|
|
|
|
def childCount(self):
|
|
|
|
return len(self.childItems)
|
|
|
|
|
2015-06-04 12:51:37 +12:00
|
|
|
def children(self):
|
|
|
|
return self.childItems
|
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
def columnCount(self):
|
|
|
|
return len(Outline)
|
|
|
|
|
|
|
|
def data(self, column, role=Qt.DisplayRole):
|
2015-06-05 06:22:37 +12:00
|
|
|
|
|
|
|
#print("Data: ", column, role)
|
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
if role == Qt.DisplayRole or role == Qt.EditRole:
|
2015-06-27 01:55:34 +12:00
|
|
|
#if column == Outline.compile.value:
|
|
|
|
#return self.data(column, Qt.CheckStateRole)
|
2015-06-05 06:22:37 +12:00
|
|
|
|
2015-06-27 01:55:34 +12:00
|
|
|
if Outline(column) in self._data:
|
2015-06-02 10:06:17 +12:00
|
|
|
return self._data[Outline(column)]
|
2015-06-05 21:37:01 +12:00
|
|
|
|
2015-07-04 04:41:18 +12:00
|
|
|
elif column == Outline.revisions.value:
|
|
|
|
return []
|
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
else:
|
2015-06-25 10:05:01 +12:00
|
|
|
return ""
|
2015-06-04 17:08:49 +12:00
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
elif role == Qt.DecorationRole and column == Outline.title.value:
|
|
|
|
if self.isFolder():
|
|
|
|
return QIcon.fromTheme("folder")
|
2015-06-16 01:46:31 +12:00
|
|
|
elif self.isText():
|
2015-06-21 21:32:42 +12:00
|
|
|
return QIcon.fromTheme("text-x-generic")
|
2015-06-25 01:43:35 +12:00
|
|
|
elif self.isT2T():
|
|
|
|
return QIcon.fromTheme("text-x-script")
|
|
|
|
elif self.isHTML():
|
|
|
|
return QIcon.fromTheme("text-html")
|
2015-06-04 12:04:47 +12:00
|
|
|
|
2015-06-18 03:15:13 +12:00
|
|
|
#elif role == Qt.ForegroundRole:
|
|
|
|
#if self.isCompile() in [0, "0"]:
|
|
|
|
#return QBrush(Qt.gray)
|
2015-06-04 17:08:49 +12:00
|
|
|
|
2015-06-04 12:04:47 +12:00
|
|
|
elif role == Qt.CheckStateRole and column == Outline.compile.value:
|
2015-06-27 01:55:34 +12:00
|
|
|
#print(self.title(), self.compile())
|
|
|
|
#if self._data[Outline(column)] and not self.compile():
|
|
|
|
#return Qt.PartiallyChecked
|
|
|
|
#else:
|
|
|
|
return self._data[Outline(column)]
|
2015-06-06 11:13:27 +12:00
|
|
|
|
|
|
|
elif role == Qt.FontRole:
|
|
|
|
f = QFont()
|
|
|
|
if column == Outline.wordCount.value and self.isFolder():
|
|
|
|
f.setItalic(True)
|
|
|
|
elif column == Outline.goal.value and self.isFolder() and self.data(Outline.setGoal) == None:
|
|
|
|
f.setItalic(True)
|
|
|
|
if self.isFolder():
|
|
|
|
f.setBold(True)
|
|
|
|
return f
|
2015-06-02 10:06:17 +12:00
|
|
|
|
2015-06-05 06:22:37 +12:00
|
|
|
def setData(self, column, data, role=Qt.DisplayRole):
|
|
|
|
if role not in [Qt.DisplayRole, Qt.EditRole, Qt.CheckStateRole]:
|
|
|
|
print(column, column == Outline.text.value, data, role)
|
|
|
|
return
|
|
|
|
|
2015-06-05 21:37:01 +12:00
|
|
|
if column == Outline.text.value and self.isFolder():
|
|
|
|
# Folder have no text
|
|
|
|
return
|
2015-06-05 06:22:37 +12:00
|
|
|
|
2015-06-05 21:37:01 +12:00
|
|
|
if column == Outline.goal.value:
|
|
|
|
self._data[Outline.setGoal] = toInt(data) if toInt(data) > 0 else ""
|
|
|
|
|
2015-06-25 01:43:35 +12:00
|
|
|
# Checking if we will have to recount words
|
2015-06-05 21:37:01 +12:00
|
|
|
updateWordCount = False
|
|
|
|
if column in [Outline.wordCount.value, Outline.goal.value, Outline.setGoal.value]:
|
2015-06-08 08:06:57 +12:00
|
|
|
updateWordCount = not Outline(column) in self._data or self._data[Outline(column)] != data
|
2015-06-25 01:43:35 +12:00
|
|
|
|
2015-06-25 20:50:30 +12:00
|
|
|
# Stuff to do before
|
2015-06-25 01:43:35 +12:00
|
|
|
if column == Outline.type.value:
|
|
|
|
oldType = self._data[Outline.type]
|
|
|
|
if oldType == "html" and data in ["txt", "t2t"]:
|
|
|
|
# Resource inneficient way to convert HTML to plain text
|
|
|
|
e = QTextEdit()
|
|
|
|
e.setHtml(self._data[Outline.text])
|
|
|
|
self._data[Outline.text] = e.toPlainText()
|
2015-07-02 00:23:52 +12:00
|
|
|
elif oldType in ["txt", "t2t"] and data == "html" and Outline.text in self._data:
|
2015-06-25 23:41:55 +12:00
|
|
|
self._data[Outline.text] = self._data[Outline.text].replace("\n", "<br>")
|
2015-06-07 05:10:44 +12:00
|
|
|
|
2015-07-04 04:41:18 +12:00
|
|
|
elif column == Outline.text.value:
|
|
|
|
self.addRevision()
|
|
|
|
|
2015-06-25 20:50:30 +12:00
|
|
|
# Setting data
|
|
|
|
self._data[Outline(column)] = data
|
|
|
|
|
|
|
|
# Stuff to do afterwards
|
|
|
|
if column == Outline.text.value:
|
|
|
|
wc = wordCount(data)
|
|
|
|
self.setData(Outline.wordCount.value, wc)
|
2015-06-27 01:55:34 +12:00
|
|
|
|
|
|
|
if column == Outline.compile.value:
|
|
|
|
self.emitDataChanged(cols=[Outline.title.value, Outline.compile.value], recursive=True)
|
2015-06-25 20:50:30 +12:00
|
|
|
|
2015-06-05 21:37:01 +12:00
|
|
|
if updateWordCount:
|
|
|
|
self.updateWordCount()
|
|
|
|
|
|
|
|
def updateWordCount(self):
|
|
|
|
if not self.isFolder():
|
|
|
|
setGoal = toInt(self.data(Outline.setGoal.value))
|
|
|
|
goal = toInt(self.data(Outline.goal.value))
|
|
|
|
|
2015-06-08 08:06:57 +12:00
|
|
|
if goal != setGoal:
|
2015-06-05 21:37:01 +12:00
|
|
|
self._data[Outline.goal] = setGoal
|
|
|
|
if setGoal:
|
|
|
|
wc = toInt(self.data(Outline.wordCount.value))
|
|
|
|
self.setData(Outline.goalPercentage.value, wc / float(setGoal))
|
|
|
|
|
|
|
|
else:
|
|
|
|
wc = 0
|
|
|
|
for c in self.children():
|
|
|
|
wc += toInt(c.data(Outline.wordCount.value))
|
|
|
|
self._data[Outline.wordCount] = wc
|
|
|
|
|
|
|
|
setGoal = toInt(self.data(Outline.setGoal.value))
|
|
|
|
goal = toInt(self.data(Outline.goal.value))
|
|
|
|
|
|
|
|
if setGoal:
|
2015-06-08 08:06:57 +12:00
|
|
|
if goal != setGoal:
|
2015-06-05 21:37:01 +12:00
|
|
|
self._data[Outline.goal] = setGoal
|
2015-06-16 08:26:23 +12:00
|
|
|
goal = setGoal
|
2015-06-05 21:37:01 +12:00
|
|
|
else:
|
|
|
|
goal = 0
|
|
|
|
for c in self.children():
|
|
|
|
goal += toInt(c.data(Outline.goal.value))
|
|
|
|
self._data[Outline.goal] = goal
|
|
|
|
|
|
|
|
if goal:
|
|
|
|
self.setData(Outline.goalPercentage.value, wc / float(goal))
|
2015-06-21 20:44:11 +12:00
|
|
|
else:
|
|
|
|
self.setData(Outline.goalPercentage.value, "")
|
2015-06-05 21:37:01 +12:00
|
|
|
|
2015-06-25 04:39:38 +12:00
|
|
|
self.emitDataChanged([Outline.goal.value, Outline.setGoal.value,
|
|
|
|
Outline.wordCount.value, Outline.goalPercentage.value])
|
2015-06-05 21:37:01 +12:00
|
|
|
|
|
|
|
if self.parent():
|
|
|
|
self.parent().updateWordCount()
|
2015-06-02 10:06:17 +12:00
|
|
|
|
|
|
|
def row(self):
|
|
|
|
if self.parent:
|
|
|
|
return self.parent().childItems.index(self)
|
|
|
|
|
|
|
|
def appendChild(self, child):
|
2015-06-03 00:40:48 +12:00
|
|
|
self.insertChild(self.childCount(), child)
|
2015-06-02 10:06:17 +12:00
|
|
|
|
|
|
|
def insertChild(self, row, child):
|
|
|
|
self.childItems.insert(row, child)
|
2015-06-03 00:40:48 +12:00
|
|
|
child._parent = self
|
2015-06-06 11:13:27 +12:00
|
|
|
child.setModel(self._model)
|
2015-06-27 03:33:55 +12:00
|
|
|
if not child.data(Outline.ID.value):
|
|
|
|
child.getUniqueID()
|
2015-06-05 21:37:01 +12:00
|
|
|
self.updateWordCount()
|
|
|
|
|
2015-06-06 11:13:27 +12:00
|
|
|
def setModel(self, model):
|
|
|
|
self._model = model
|
|
|
|
for c in self.children():
|
|
|
|
c.setModel(model)
|
|
|
|
|
2015-06-05 21:37:01 +12:00
|
|
|
def index(self, column=0):
|
2015-06-06 11:13:27 +12:00
|
|
|
if self._model:
|
|
|
|
return self._model.indexFromItem(self, column)
|
|
|
|
else:
|
|
|
|
return QModelIndex()
|
2015-06-05 21:37:01 +12:00
|
|
|
|
2015-06-27 01:55:34 +12:00
|
|
|
def emitDataChanged(self, cols=None, recursive=False):
|
2015-06-05 21:37:01 +12:00
|
|
|
idx = self.index()
|
2015-06-06 11:13:27 +12:00
|
|
|
if idx and self._model:
|
2015-06-25 04:39:38 +12:00
|
|
|
if not cols:
|
|
|
|
# Emit data changed for the whole item (all columns)
|
|
|
|
self._model.dataChanged.emit(idx, self.index(len(Outline)))
|
2015-06-27 01:55:34 +12:00
|
|
|
|
2015-06-25 04:39:38 +12:00
|
|
|
else:
|
|
|
|
# Emit only for the specified columns
|
|
|
|
for c in cols:
|
|
|
|
self._model.dataChanged.emit(self.index(c), self.index(c))
|
2015-06-27 01:55:34 +12:00
|
|
|
|
|
|
|
if recursive:
|
|
|
|
for c in self.children():
|
|
|
|
c.emitDataChanged(cols, recursive=True)
|
2015-06-02 10:06:17 +12:00
|
|
|
|
|
|
|
def removeChild(self, row):
|
|
|
|
self.childItems.pop(row)
|
2015-06-30 01:34:03 +12:00
|
|
|
self.updateWordCount()
|
2015-06-02 10:06:17 +12:00
|
|
|
|
|
|
|
def parent(self):
|
|
|
|
return self._parent
|
|
|
|
|
2015-06-25 01:43:35 +12:00
|
|
|
def type(self):
|
|
|
|
return self._data[Outline.type]
|
|
|
|
|
2015-06-02 10:06:17 +12:00
|
|
|
def isFolder(self):
|
|
|
|
return self._data[Outline.type] == "folder"
|
|
|
|
|
2015-06-25 01:43:35 +12:00
|
|
|
def isT2T(self):
|
|
|
|
return self._data[Outline.type] == "t2t"
|
|
|
|
|
|
|
|
def isHTML(self):
|
|
|
|
return self._data[Outline.type] == "html"
|
|
|
|
|
|
|
|
def isText(self):
|
|
|
|
return self._data[Outline.type] == "txt"
|
|
|
|
|
2015-07-01 23:14:03 +12:00
|
|
|
def text(self):
|
|
|
|
return self.data(Outline.text.value)
|
|
|
|
|
2015-06-27 01:55:34 +12:00
|
|
|
def compile(self):
|
|
|
|
if self._data[Outline.compile] in ["0", 0]:
|
|
|
|
return False
|
|
|
|
elif self.parent():
|
|
|
|
return self.parent().compile()
|
|
|
|
else:
|
|
|
|
return True # rootItem always compile
|
2015-06-04 17:08:49 +12:00
|
|
|
|
2015-06-05 21:37:01 +12:00
|
|
|
def title(self):
|
|
|
|
if Outline.title in self._data:
|
|
|
|
return self._data[Outline.title]
|
|
|
|
else:
|
|
|
|
return ""
|
2015-06-27 10:22:46 +12:00
|
|
|
|
|
|
|
def ID(self):
|
|
|
|
return self.data(Outline.ID.value)
|
2015-06-05 21:37:01 +12:00
|
|
|
|
2015-06-30 22:27:43 +12:00
|
|
|
def POV(self):
|
|
|
|
return self.data(Outline.POV.value)
|
|
|
|
|
2015-07-01 00:01:32 +12:00
|
|
|
def status(self):
|
|
|
|
return self.data(Outline.status.value)
|
|
|
|
|
|
|
|
def label(self):
|
|
|
|
return self.data(Outline.label.value)
|
|
|
|
|
2015-06-06 11:32:52 +12:00
|
|
|
def path(self):
|
2015-06-30 22:27:43 +12:00
|
|
|
"Returns path to item as string."
|
2015-06-06 11:32:52 +12:00
|
|
|
if self.parent().parent():
|
|
|
|
return "{} > {}".format(self.parent().path(), self.title())
|
|
|
|
else:
|
|
|
|
return self.title()
|
|
|
|
|
2015-06-30 22:27:43 +12:00
|
|
|
def pathID(self):
|
|
|
|
"Returns path to item as list of (ID, title)."
|
|
|
|
if self.parent().parent():
|
|
|
|
return self.parent().pathID() + [(self.ID(), self.title())]
|
|
|
|
else:
|
|
|
|
return [(self.ID(), self.title())]
|
|
|
|
|
2015-06-05 21:37:01 +12:00
|
|
|
def level(self):
|
|
|
|
if self.parent():
|
|
|
|
return self.parent().level() + 1
|
|
|
|
else:
|
|
|
|
return -1
|
|
|
|
|
2015-06-30 22:27:43 +12:00
|
|
|
def stats(self):
|
|
|
|
wc = self.data(Outline.wordCount.value)
|
|
|
|
goal = self.data(Outline.goal.value)
|
|
|
|
progress = self.data(Outline.goalPercentage.value)
|
|
|
|
if not wc:
|
|
|
|
wc = 0
|
|
|
|
if goal:
|
|
|
|
return qApp.translate("outlineModel", "{} words / {} ({})").format(
|
|
|
|
locale.format("%d", wc, grouping=True),
|
|
|
|
locale.format("%d", goal, grouping=True),
|
|
|
|
"{}%".format(str(int(progress * 100))))
|
|
|
|
else:
|
|
|
|
return qApp.translate("outlineModel", "{} words").format(
|
|
|
|
locale.format("%d", wc, grouping=True))
|
|
|
|
|
2015-07-04 04:41:18 +12:00
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
# XML
|
|
|
|
###############################################################################
|
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
def toXML(self):
|
|
|
|
item = ET.Element("outlineItem")
|
|
|
|
|
2015-06-06 11:13:27 +12:00
|
|
|
# We don't want to write some datas (computed)
|
2015-07-04 04:41:18 +12:00
|
|
|
exclude = [Outline.wordCount, Outline.goal, Outline.goalPercentage, Outline.revisions]
|
2015-06-06 11:13:27 +12:00
|
|
|
# We want to force some data even if they're empty
|
|
|
|
force = [Outline.compile]
|
2015-06-05 21:37:01 +12:00
|
|
|
|
2015-06-03 00:40:48 +12:00
|
|
|
for attrib in Outline:
|
2015-06-05 21:37:01 +12:00
|
|
|
if attrib in exclude: continue
|
2015-06-05 06:22:37 +12:00
|
|
|
val = self.data(attrib.value)
|
2015-06-06 11:13:27 +12:00
|
|
|
if val or attrib in force:
|
2015-06-08 08:06:57 +12:00
|
|
|
item.set(attrib.name, str(val))
|
2015-07-04 04:41:18 +12:00
|
|
|
|
|
|
|
# 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)
|
2015-06-03 00:40:48 +12:00
|
|
|
|
|
|
|
for i in self.childItems:
|
|
|
|
item.append(ET.XML(i.toXML()))
|
|
|
|
|
|
|
|
return ET.tostring(item)
|
|
|
|
|
|
|
|
def setFromXML(self, xml):
|
|
|
|
root = ET.XML(xml)
|
|
|
|
|
|
|
|
for k in root.attrib:
|
|
|
|
if k in Outline.__members__:
|
2015-06-06 11:13:27 +12:00
|
|
|
#if k == Outline.compile:
|
|
|
|
#self.setData(Outline.__members__[k].value, unicode(root.attrib[k]), Qt.CheckStateRole)
|
|
|
|
#else:
|
2015-06-08 08:06:57 +12:00
|
|
|
self.setData(Outline.__members__[k].value, str(root.attrib[k]))
|
2015-06-03 00:40:48 +12:00
|
|
|
|
|
|
|
for child in root:
|
2015-07-04 04:41:18 +12:00
|
|
|
if child.tag == "outlineItem":
|
|
|
|
item = outlineItem(self._model, xml=ET.tostring(child), parent=self)
|
|
|
|
elif child.tag == "revision":
|
|
|
|
self.appendRevision(child.attrib["timestamp"], child.attrib["text"])
|
|
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
# IDS
|
|
|
|
###############################################################################
|
2015-06-27 03:33:55 +12:00
|
|
|
|
|
|
|
def getUniqueID(self):
|
|
|
|
self.setData(Outline.ID.value, self._model.rootItem.findUniqueID())
|
|
|
|
|
|
|
|
def checkIDs(self):
|
|
|
|
"""This is called when a model is loaded.
|
|
|
|
|
|
|
|
Makes a list of all sub-items IDs, that is used to generate unique IDs afterwards.
|
|
|
|
"""
|
|
|
|
self.IDs = self.listAllIDs()
|
|
|
|
|
|
|
|
if max([self.IDs.count(i) for i in self.IDs if i]) != 1:
|
|
|
|
print("There are some doublons:", [i for i in self.IDs if i and self.IDs.count(i) != 1])
|
|
|
|
|
|
|
|
def checkChildren(item):
|
|
|
|
for c in item.children():
|
|
|
|
_id = c.data(Outline.ID.value)
|
|
|
|
if not _id or _id == "0":
|
|
|
|
c.getUniqueID()
|
|
|
|
checkChildren(c)
|
|
|
|
|
|
|
|
checkChildren(self)
|
|
|
|
|
|
|
|
|
|
|
|
def listAllIDs(self):
|
|
|
|
IDs = [self.data(Outline.ID.value)]
|
|
|
|
for c in self.children():
|
|
|
|
IDs.extend(c.listAllIDs())
|
|
|
|
return IDs
|
|
|
|
|
|
|
|
def findUniqueID(self):
|
|
|
|
k = 0
|
|
|
|
while str(k) in self.IDs:
|
|
|
|
k += 1
|
|
|
|
self.IDs.append(str(k))
|
2015-06-27 10:22:46 +12:00
|
|
|
return str(k)
|
|
|
|
|
|
|
|
def pathToItem(self):
|
|
|
|
path = self.data(Outline.ID.value)
|
|
|
|
if self.parent().parent():
|
|
|
|
path = "{}:{}".format(self.parent().pathToItem(), path)
|
2015-07-01 00:01:32 +12:00
|
|
|
return path
|
|
|
|
|
|
|
|
def findItemsByPOV(self, POV):
|
|
|
|
"Returns a list of IDs of all subitems whose POV is ``POV``."
|
|
|
|
lst = []
|
|
|
|
if self.POV() == POV:
|
|
|
|
lst.append(self.ID())
|
|
|
|
|
|
|
|
for c in self.children():
|
|
|
|
lst.extend(c.findItemsByPOV(POV))
|
|
|
|
|
|
|
|
return lst
|
|
|
|
|
2015-07-01 01:38:14 +12:00
|
|
|
def findItemsContaining(self, text, columns, mainWindow, caseSensitive=False):
|
2015-07-01 00:01:32 +12:00
|
|
|
"""Returns a list if IDs of all subitems
|
2015-07-01 01:38:14 +12:00
|
|
|
containing ``text`` in columns ``columns``
|
|
|
|
(being a list of int).
|
2015-07-01 00:01:32 +12:00
|
|
|
"""
|
|
|
|
lst = []
|
2015-07-01 01:38:14 +12:00
|
|
|
text = text.lower() if not caseSensitive else text
|
|
|
|
for c in columns:
|
|
|
|
|
|
|
|
if c == Outline.POV.value:
|
|
|
|
searchIn = mainWindow.mdlPersos.getPersoNameByID(self.POV())
|
|
|
|
|
|
|
|
elif c == Outline.status.value:
|
|
|
|
searchIn = mainWindow.mdlStatus.item(toInt(self.status()), 0).text()
|
|
|
|
|
|
|
|
elif c == Outline.label.value:
|
|
|
|
searchIn = mainWindow.mdlLabels.item(toInt(self.label()), 0).text()
|
|
|
|
|
|
|
|
else:
|
|
|
|
searchIn = self.data(c)
|
|
|
|
|
|
|
|
searchIn = searchIn.lower() if not caseSensitive else searchIn
|
|
|
|
|
|
|
|
if text in searchIn:
|
|
|
|
if not self.ID() in lst:
|
|
|
|
lst.append(self.ID())
|
2015-07-01 00:01:32 +12:00
|
|
|
|
|
|
|
for c in self.children():
|
2015-07-01 01:38:14 +12:00
|
|
|
lst.extend(c.findItemsContaining(text, columns, mainWindow, caseSensitive))
|
2015-07-01 00:01:32 +12:00
|
|
|
|
2015-07-04 04:41:18 +12:00
|
|
|
return lst
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
# REVISIONS
|
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
def revisions(self):
|
|
|
|
return self.data(Outline.revisions.value)
|
|
|
|
|
|
|
|
def appendRevision(self, ts, text):
|
|
|
|
if not Outline.revisions in self._data:
|
|
|
|
self._data[Outline.revisions] = []
|
|
|
|
|
|
|
|
self._data[Outline.revisions].append((
|
|
|
|
int(ts),
|
|
|
|
text))
|
|
|
|
|
|
|
|
def addRevision(self):
|
|
|
|
# FIXME: only add if significantly different, or enough time span
|
|
|
|
|
2015-07-04 09:00:54 +12:00
|
|
|
if not settings.revisions["keep"]:
|
|
|
|
return
|
|
|
|
|
2015-07-04 04:41:18 +12:00
|
|
|
if not Outline.text in self._data:
|
|
|
|
return
|
|
|
|
|
|
|
|
self.appendRevision(
|
|
|
|
time.time(),
|
|
|
|
self._data[Outline.text])
|
|
|
|
|
2015-07-04 09:00:54 +12:00
|
|
|
if settings.revisions["smartremove"]:
|
|
|
|
self.cleanRevisions()
|
2015-07-04 04:41:18 +12:00
|
|
|
|
|
|
|
def cleanRevisions(self):
|
|
|
|
"Keep only one some the revisions."
|
|
|
|
rev = self.revisions()
|
|
|
|
rev2 = []
|
|
|
|
now = time.time()
|
|
|
|
|
2015-07-04 09:00:54 +12:00
|
|
|
rule = settings.revisions["rules"]
|
2015-07-04 04:41:18 +12:00
|
|
|
|
|
|
|
revs = {}
|
|
|
|
for i in rule:
|
|
|
|
revs[i] = []
|
|
|
|
|
|
|
|
for r in rev:
|
|
|
|
for span in rule:
|
|
|
|
if not span or now - r[0] < span:
|
|
|
|
revs[span].append(r)
|
|
|
|
break
|
|
|
|
|
|
|
|
for span in revs:
|
|
|
|
sortedRev = sorted(revs[span], key=lambda x:x[0])
|
|
|
|
last = None
|
|
|
|
for r in sortedRev:
|
|
|
|
if not last:
|
|
|
|
rev2.append(r)
|
|
|
|
last = r[0]
|
|
|
|
elif r[0] - last >= rule[span]:
|
|
|
|
rev2.append(r)
|
|
|
|
last = r[0]
|
|
|
|
|
|
|
|
if rev2 != rev:
|
|
|
|
self._data[Outline.revisions] = rev2
|
|
|
|
self.emitDataChanged([Outline.revisions.value])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|