define model before nested functions
[sface.git] / sface / rspecwindow.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     def expandMatchingText(self, txt):
24         self.collapseAll()
25         self.expandToDepth(0)
26
27         model = self.model()
28
29         def recursiveExpand(index):
30             parent = index.parent()
31             if parent and parent.isValid():
32                 recursiveExpand(parent)
33             self.expand(index)
34
35         def search(index):
36             if index.data().toString() == txt:
37                 recursiveExpand(index)
38                 self.scrollTo(index, self.PositionAtCenter)
39             
40             rows = model.rowCount(index)
41             for r in range(rows):
42                 child_index = index.child(r, 0)
43                 search(child_index)
44             
45         root_rows = model.rowCount()
46         for r in range(root_rows):
47             index = model.index(r, 0)
48             search(index)
49
50
51 class DomModel(QAbstractItemModel):
52     def __init__(self, document, parent = 0):
53         QAbstractItemModel.__init__(self, parent)
54         self.domDocument = document
55         # one of the children of the rootItem is the 'xml' thing.
56         # here I delete it.
57         childList = document.childNodes()
58         for i in range(childList.count()):
59             currElem = childList.item(i)
60             if (currElem.nodeType() == QDomNode.ProcessingInstructionNode):
61                 document.removeChild(currElem)
62                 break
63         self.rootItem = DomItem(document, 0);
64
65     def data(self, index, role):
66         if not index.isValid():
67             return QVariant()
68         if role != Qt.DisplayRole:
69             return QVariant()
70         node = index.internalPointer().node()
71         attributeMap = node.attributes()
72
73         col = index.column()
74         if col == 0:
75             if node.nodeType() == QDomNode.ElementNode:
76                 qslist = QStringList()
77                 for i in range(attributeMap.count()):
78                     attr = attributeMap.item(i)
79                     elem = '%s="%s"' % (attr.nodeName(), attr.nodeValue())
80                     qslist.append(elem)
81                 return QString("<%s %s>" % (node.nodeName(), qslist.join(' ')))
82             elif node.nodeType() == QDomNode.AttributeNode:
83                 return QString('Whozat?!')
84             elif node.nodeType() == QDomNode.TextNode:
85                 return node.nodeValue()
86             elif node.nodeType() == QDomNode.CDATASectionNode:
87                 return QString('unsupported node type')
88             elif node.nodeType() == QDomNode.EntityReferenceNode:
89                 return QString('unsupported node type')
90             elif node.nodeType() == QDomNode.EntityNode:
91                 return QString('unsupported node type')
92             elif node.nodeType() == QDomNode.ProcessingInstructionNode:
93                 return QVariant()
94                 #return node.nodeName()
95             elif node.nodeType() == QDomNode.CommentNode:
96                 return QString('#').append(node.nodeValue())
97             elif node.nodeType() == QDomNode.DocumentNode:
98                 return QString('unsupported node type')
99             elif node.nodeType() == QDomNode.DocumentTypeNode:
100                 return QString('unsupported node type')
101             elif node.nodeType() == QDomNode.DocumentFragmentNode:
102                 return QString('unsupported node type')
103             elif node.nodeType() == QDomNode.NotationNode:
104                 return QString('unsupported node type')
105             elif node.nodeType() == QDomNode.BaseNode:
106                 return QString('unsupported node type')
107             elif node.nodeType() == QDomNode.CharacterDataNode:
108                 return QString('unsupported node type')
109             else:
110                 return QVariant()
111         else:
112             return QVariant()
113
114     def flags(self, index):
115         if not index.isValid():
116             return Qt.ItemIsEnabled
117         return Qt.ItemIsEnabled | Qt.ItemIsSelectable
118         
119     def headerData(self, section, orientation, role):
120         return QVariant()
121
122     def index(self, row, column, parent=None):
123         if not parent or not parent.isValid():
124             parentItem = self.rootItem
125         else:
126             parentItem = parent.internalPointer()
127
128         childItem = parentItem.child(row)
129         # childItem would be None to say "false"?
130         if childItem:
131             return self.createIndex(row, column, childItem)
132         else:
133             return QModelIndex()
134
135     def parent(self, child):
136         if not child.isValid():
137             return QModelIndex()
138         childItem = child.internalPointer()
139         parentItem = childItem.parent()
140         
141         if not parentItem or parentItem == self.rootItem:
142             return QModelIndex()
143         return self.createIndex(parentItem.row(), 0, parentItem)
144
145     def rowCount(self, parent=None):
146         if not parent or not parent.isValid():
147             parentItem = self.rootItem
148         else:
149             parentItem = parent.internalPointer()
150
151         return parentItem.node().childNodes().count()
152
153     def columnCount(self, parent):
154         # just one column we'll print tag name (and attributes) or the
155         # tag content
156         return 1
157
158
159 class DomItem:
160     # wrapper around PyQt4.QtXml.QDomNode it keeps an hash of
161     # childrens for performance reasons
162
163     def __init__(self, node, row, parent = 0):
164         # node is of type PyQt4.QtXml.QDomNode
165         self.domNode = node
166         self.parentItem = parent
167         self.rowNumber = row
168         self.childItems = {}
169
170     def child(self, i):
171         if i in self.childItems:
172             return self.childItems[i]
173         if i >= 0 and i < self.domNode.childNodes().count():
174             childNode = self.domNode.childNodes().item(i)
175             childItem = DomItem(childNode, i, self)
176             self.childItems[i] = childItem
177             return childItem
178         return None
179             
180     def parent(self):
181         return self.parentItem
182
183     def node(self):
184         return self.domNode
185
186     def row(self):
187         return self.rowNumber
188     
189
190 class RSpecWindow(QDialog):
191     def __init__(self, parent=None):
192         QDialog.__init__(self, parent)
193         self.setWindowTitle("RSPec View")
194
195         self.document = None
196         self.model = None
197
198         self.view = RSpecView(self)
199         layout = QVBoxLayout()
200         layout.addWidget(self.view)
201         self.setLayout(layout)
202
203         self.updateView()
204
205     def show(self):
206         self.updateView()
207         QDialog.show(self)
208         
209     def showNode(self, hostname):
210         self.view.expandMatchingText(hostname)
211
212     def updateView(self):
213         del self.document
214         del self.model
215         self.document = None
216         self.model = None
217
218         rspec_file = config.getSliceRSpecFile()
219         if not os.path.exists(rspec_file):
220             return
221
222         self.document = QDomDocument("RSpec")
223         self.document.setContent(open(rspec_file,'r').read())
224         self.model = DomModel(self.document, self)
225
226         self.view.setModel(self.model)
227         self.view.expand(self.model.index(0, 0)) #expand first level only
228
229