manuskript/src/models/outlineModel.py
2015-06-04 20:22:37 +02:00

416 lines
12 KiB
Python

#!/usr/bin/env python
#--!-- coding: utf8 --!--
from __future__ import print_function
from __future__ import unicode_literals
from qt import *
from enums import *
from enum import Enum
from lxml import etree as ET
from functions import *
class outlineModel(QAbstractItemModel):
newStatuses = pyqtSignal()
def __init__(self):
QAbstractItemModel.__init__(self)
self.rootItem = outlineItem("root", "folder")
self.generateStatuses()
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()
def parent(self, index=QModelIndex()):
if not index.isValid():
return QModelIndex()
childItem = index.internalPointer()
parentItem = childItem.parent()
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()
item.setData(index.column(), value, role)
if index.column() == Outline.status.value:
self.generateStatuses()
self.dataChanged.emit(index, index)
return True
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return [i.name for i in Outline][section]
else:
return QVariant()
return True
#################### DRAG AND DROP ########################
# http://doc.qt.io/qt-5/model-view-programming.html#using-drag-and-drop-with-item-views
def flags(self, index):
#FIXME when dragging folders, sometimes flags is not called
flags = QAbstractItemModel.flags(self, index) | Qt.ItemIsEditable
if index.isValid() and index.internalPointer().isFolder():
flags |= Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled
elif index.isValid():
flags |= Qt.ItemIsDragEnabled
else:
flags |= Qt.ItemIsDropEnabled
if index.isValid() and index.column() == Outline.compile.value:
flags |= Qt.ItemIsUserCheckable
return flags
def mimeTypes(self):
return ["application/xml"]
def mimeData(self, indexes):
mimeData = QMimeData()
encodedData = ""
root = ET.Element("outlineItems")
for index in indexes:
if index.isValid() and index.column() == 0:
item = ET.XML(index.internalPointer().toXML())
root.append(item)
encodedData = ET.tostring(root)
mimeData.setData("application/xml", encodedData)
return mimeData
def supportedDropActions(self):
#return Qt.MoveAction # Qt.CopyAction |
return Qt.CopyAction | Qt.MoveAction
def canDropMomeData(data, action, row, column, parent):
if not data.hasFormat("application/xml"):
return False
if column > 0:
return False
return True
def dropMimeData(self, data, action, row, column, parent):
if action == Qt.IgnoreAction:
return True # What is that?
if not data.hasFormat("application/xml"):
return False
if column > 0:
column = 0
if row <> -1:
beginRow = row
elif parent.isValid():
beginRow = self.rowCount(parent) + 1
else:
beginRow = self.rowCount() + 1
encodedData = str(data.data("application/xml"))
root = ET.XML(encodedData)
if root.tag <> "outlineItems":
return False
items = []
for child in root:
if child.tag == "outlineItem":
items.append(outlineItem(xml=ET.tostring(child)))
if not items:
return False
self.insertItems(items, beginRow, parent)
return True
################# ADDING AND REMOVING #################
def insertItem(self, item, row, parent=QModelIndex()):
return self.insertItems([item], row, parent)
def insertItems(self, items, row, parent=QModelIndex()):
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
# Insert only if parent is folder
if parentItem.isFolder():
self.beginInsertRows(parent, row, row + len(items) - 1)
for i in items:
parentItem.insertChild(row + items.index(i), i)
self.endInsertRows()
return True
def appendItem(self, item, parent=QModelIndex()):
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
# 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())
def removeIndex(self, index):
item = index.internalPointer()
self.removeRow(item.row(), index.parent())
def removeRow(self, row, parent=QModelIndex()):
return self.removeRows(row, 1, parent)
def removeRows(self, row, count, parent=QModelIndex()):
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
self.beginRemoveRows(parent, row, row + count - 1)
for i in range(count):
parentItem.removeChild(row)
self.endRemoveRows()
return True
#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()
################# XML #################
def saveToXML(self, xml):
root = ET.XML(self.rootItem.toXML())
ET.ElementTree(root).write(xml, encoding="UTF-8", xml_declaration=True, pretty_print=True)
def loadFromXML(self, xml):
try:
root = ET.parse(xml)
self.rootItem = outlineItem(xml=ET.tostring(root))
self.generateStatuses()
except:
print("N'arrive pas à ouvrir {}".format(xml))
return
################# DIVERS #################
def generateStatuses(self, item=None):
if item == None:
self.statuses = [
"TODO",
"First draft",
"Second draft",
"Final"
]
item = self.rootItem
val = item.data(Outline.status.value)
if val and not val in self.statuses:
self.statuses.append(val)
self.newStatuses.emit()
for c in item.children():
self.generateStatuses(c)
class outlineItem():
def __init__(self, title="", type="folder", xml=None):
self._data = {}
self.childItems = []
if title: self._data[Outline.title] = title
self._data[Outline.type] = type
if xml is not None:
self.setFromXML(xml)
def child(self, row):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def children(self):
return self.childItems
def columnCount(self):
return len(Outline)
def data(self, column, role=Qt.DisplayRole):
#print("Data: ", column, role)
if role == Qt.DisplayRole or role == Qt.EditRole:
if column == Outline.compile.value:
return self.data(column, Qt.CheckStateRole)
elif Outline(column) in self._data:
return self._data[Outline(column)]
else:
return None
elif role == Qt.DecorationRole and column == Outline.title.value:
if self.isFolder():
return QIcon.fromTheme("folder")
elif self.isScene():
return QIcon.fromTheme("document-new")
elif role == Qt.ForegroundRole and column == Outline.title.value:
if not self.isCompile():
return QBrush(Qt.gray)
elif role == Qt.CheckStateRole and column == Outline.compile.value:
if Outline(column) in self._data and self._data[Outline(column)]:
return Qt.Checked
else:
return Qt.Unchecked
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
if column == Outline.text.value:
wc = wordCount(data)
self.setData(Outline.wordCount.value, wc)
if column in [Outline.wordCount.value, Outline.goal.value]:
wc = self.data(Outline.wordCount.value)
goal = self.data(Outline.goal.value)
if goal and wc:
self.setData(Outline.goalPercentage.value, int(wc) / float(goal))
else:
self.setData(Outline.goalPercentage.value, "0")
self._data[Outline(column)] = data
def row(self):
if self.parent:
return self.parent().childItems.index(self)
def appendChild(self, child):
self.insertChild(self.childCount(), child)
def insertChild(self, row, child):
self.childItems.insert(row, child)
child._parent = self
def removeChild(self, row):
self.childItems.pop(row)
def parent(self):
return self._parent
def isFolder(self):
return self._data[Outline.type] == "folder"
def isCompile(self):
return Outline.compile in self._data and self._data[Outline.compile]
def isScene(self):
return self._data[Outline.type] == "scene"
def toXML(self):
item = ET.Element("outlineItem")
for attrib in Outline:
val = self.data(attrib.value)
if val:
item.set(attrib.name, unicode(val))
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__:
self.setData(Outline.__members__[k].value, unicode(root.attrib[k]))
for child in root:
item = outlineItem(xml=ET.tostring(child))
self.appendChild(item)