introduce RSpecView, inherited from QTreeView
[sface.git] / sface / screens / rspecscreen.py
1 import os
2 import sys
3
4 from PyQt4.QtCore import *
5 from PyQt4.QtGui import *
6 from PyQt4.QtXml import *
7
8 from sface.config import config
9 from sface.screens.sfascreen import SfaScreen
10
11
12 class RSpecView(QTreeView):
13     def __init__(self, parent):
14         QTreeView.__init__(self, parent)
15
16         self.setAnimated(True)
17         self.setItemsExpandable(True)
18         self.setRootIsDecorated(True)
19         self.setHeaderHidden(True)
20         self.setAttribute(Qt.WA_MacShowFocusRect, 0)
21         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
22
23
24 class DomModel(QAbstractItemModel):
25     def __init__(self, document, parent = 0):
26         QAbstractItemModel.__init__(self, parent)
27         self.domDocument = document
28         self.rootItem = DomItem(document, 0);
29
30     def data(self, index, role):
31         if not index.isValid():
32             return QVariant()
33         if role != Qt.DisplayRole:
34             return QVariant()
35         node = index.internalPointer().node()
36         attributeMap = node.attributes()
37
38         col = index.column()
39         if col == 0:
40             if node.nodeType() == QDomNode.ElementNode:
41                 qslist = QStringList()
42                 for i in range(attributeMap.count()):
43                     attr = attributeMap.item(i)
44                     elem = (attr.nodeName()
45                             .append(QString('="'))
46                             .append(attr.nodeValue())
47                             .append(QString('"')))
48                     qslist.append(elem)
49                 return (QString('<').
50                         append(node.nodeName()).
51                         append(' ').
52                         append(qslist.join(' ')).
53                         append('>'))
54             elif node.nodeType() == QDomNode.AttributeNode:
55                 return QString('Whozat?!')
56             elif node.nodeType() == QDomNode.TextNode:
57                 return node.nodeValue()
58             elif node.nodeType() == QDomNode.CDATASectionNode:
59                 return QString('unsupported node type')
60             elif node.nodeType() == QDomNode.EntityReferenceNode:
61                 return QString('unsupported node type')
62             elif node.nodeType() == QDomNode.EntityNode:
63                 return QString('unsupported node type')
64             elif node.nodeType() == QDomNode.ProcessingInstructionNode:
65                 return node.nodeName()
66             elif node.nodeType() == QDomNode.CommentNode:
67                 return QString('#').append(node.nodeValue())
68             elif node.nodeType() == QDomNode.DocumentNode:
69                 return QString('unsupported node type')
70             elif node.nodeType() == QDomNode.DocumentTypeNode:
71                 return QString('unsupported node type')
72             elif node.nodeType() == QDomNode.DocumentFragmentNode:
73                 return QString('unsupported node type')
74             elif node.nodeType() == QDomNode.NotationNode:
75                 return QString('unsupported node type')
76             elif node.nodeType() == QDomNode.BaseNode:
77                 return QString('unsupported node type')
78             elif node.nodeType() == QDomNode.CharacterDataNode:
79                 return QString('unsupported node type')
80             else:
81                 return QVariant()
82         else:
83             return QVariant()
84
85     def flags(self, index):
86         if not index.isValid():
87             return Qt.ItemIsEnabled
88         # does this `|` thing hold?
89         return Qt.ItemIsEnabled | Qt.ItemIsSelectable
90         
91     def headerData(self, section, orientation, role):
92         return QVariant()
93
94     def index(self, row, column, parent):
95         if not parent.isValid():
96             parentItem = self.rootItem
97         else:
98             parentItem = parent.internalPointer()
99
100         childItem = parentItem.child(row)
101         # childItem would be None to say "false"?
102         if childItem:
103             return self.createIndex(row, column, childItem)
104         else:
105             return QModelIndex()
106
107     def parent(self, child):
108         if not child.isValid():
109             return QModelIndex()
110         childItem = child.internalPointer()
111         parentItem = childItem.parent()
112         
113         if not parentItem or parentItem == self.rootItem:
114             return QModelIndex()
115         return self.createIndex(parentItem.row(), 0, parentItem)
116
117     def rowCount(self, parent):
118         if not parent.isValid():
119             parentItem = self.rootItem
120         else:
121             parentItem = parent.internalPointer()
122
123         return parentItem.node().childNodes().count()
124
125     def columnCount(self, parent):
126         # just one column
127         # we'll print tag name (and attributes)
128         # or the tag content
129         return 1
130
131
132 class DomItem:
133     # wrapper around PyQt4.QtXml.QDomNode
134     # it keeps an hash of childrens for
135     # performance reasons
136
137     def __init__(self, node, row, parent = 0):
138         # node is of type PyQt4.QtXml.QDomNode
139         self.domNode = node
140         self.parentItem = parent
141         self.rowNumber = row
142         self.childItems = {}
143
144     def child(self, i):
145         if i in self.childItems:
146             return self.childItems[i]
147         if i >= 0 and i < self.domNode.childNodes().count():
148             childNode = self.domNode.childNodes().item(i)
149             childItem = DomItem(childNode, i, self)
150             self.childItems[i] = childItem
151             return childItem
152         return None
153             
154     def parent(self):
155         return self.parentItem
156
157     def node(self):
158         return self.domNode
159
160     def row(self):
161         return self.rowNumber
162     
163
164
165 class RSpecWidget(QWidget):
166     def __init__(self, parent=None):
167         QWidget.__init__(self, parent)
168         self.updateView()
169
170     def updateView(self):
171         document = QDomDocument("RSpec")
172         rspec_file = config.getSliceRSpecFile()
173         if not os.path.exists(rspec_file):
174             return
175
176         document.setContent(open(rspec_file,'r').read())
177         model = DomModel(document, self)
178
179         view = RSpecView(self)
180         view.setModel(model)
181         view.expandAll()
182
183         layout = QVBoxLayout()
184         layout.addWidget(view)
185         self.setLayout(layout)
186
187
188 class RSpecScreen(SfaScreen):
189     def __init__(self, parent):
190         SfaScreen.__init__(self, parent)
191         
192         widget = RSpecWidget(self)
193         self.init(widget, "RSpec Window", "RSPec View")
194
195     def rspecUpdated(self):
196         self.widget.updateView()