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