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