4 from PyQt4.QtCore import *
5 from PyQt4.QtGui import *
6 from PyQt4.QtXml import *
8 from sface.config import config
9 from sface.screens.sfascreen import SfaScreen
15 def QVarMapAccess(qv, key):
16 # helper function. qv is a dict wrapped into a QVariant
18 #print "DICT:", qv.toMap()
19 # if len(qv.toMap().keys()) == 0:
22 # traceback.print_stack()
25 return qv.toMap()[QString(key)].toString()
27 class nodeData(QVariant):
28 def __init__(self, *args, **kws):
29 QVariant.__init__(self, *args, **kws)
32 def setType(self, typ):
38 class DomModel(QAbstractItemModel):
39 def __init__(self, document, parent = 0):
40 QAbstractItemModel.__init__(self, parent)
41 self.domDocument = document
42 # one of the children of the rootItem is the 'xml' thing.
44 childList = document.childNodes()
45 for i in range(childList.count()):
46 currElem = childList.item(i)
47 if (currElem.nodeType() == QDomNode.ProcessingInstructionNode):
48 document.removeChild(currElem)
51 self.rootItem = DomItem(document, 0);
53 def data(self, index, role = Qt.DisplayRole):
54 # for interesting nodes, returns a dict wrapped into a QVariant.
55 if not index.isValid():
57 if role != Qt.DisplayRole:
59 node = index.internalPointer().node()
60 attributeMap = node.attributes()
64 if node.nodeType() == QDomNode.ElementNode:
65 qslist = QStringList()
66 for i in range(attributeMap.count()):
67 attr = attributeMap.item(i)
68 elem = ' %s="%s"' % (attr.nodeName(), attr.nodeValue())
70 ElemNameAndAtts = '%s%s'% (node.nodeName(), qslist.join(' '))
72 answer = nodeData(ElemNameAndAtts)
73 answer.setType('element')
75 elif node.nodeType() == QDomNode.AttributeNode:
78 elif node.nodeType() == QDomNode.TextNode:
80 answer = nodeData(node.nodeValue())
81 answer.setType('text')
83 elif node.nodeType() == QDomNode.CDATASectionNode:
85 return QString('unsupported node type')
86 elif node.nodeType() == QDomNode.EntityReferenceNode:
88 return QString('unsupported node type')
89 elif node.nodeType() == QDomNode.EntityNode:
91 return QString('unsupported node type')
92 elif node.nodeType() == QDomNode.ProcessingInstructionNode:
95 elif node.nodeType() == QDomNode.CommentNode:
96 answer = nodeData(node.nodeValue())
97 answer.setType('comment')
99 elif node.nodeType() == QDomNode.DocumentNode:
101 return QString('unsupported node type')
102 elif node.nodeType() == QDomNode.DocumentTypeNode:
104 return QString('unsupported node type')
105 elif node.nodeType() == QDomNode.DocumentFragmentNode:
107 return QString('unsupported node type')
108 elif node.nodeType() == QDomNode.NotationNode:
110 return QString('unsupported node type')
111 elif node.nodeType() == QDomNode.BaseNode:
113 return QString('unsupported node type')
114 elif node.nodeType() == QDomNode.CharacterDataNode:
116 return QString('unsupported node type')
124 def flags(self, index):
125 if not index.isValid():
126 return Qt.ItemIsEnabled
127 return Qt.ItemIsEnabled | Qt.ItemIsSelectable
129 def headerData(self, section, orientation, role):
132 def index(self, row, column, parent=None):
133 if not parent or not parent.isValid():
134 parentItem = self.rootItem
136 parentItem = parent.internalPointer()
138 childItem = parentItem.child(row)
139 # childItem would be None to say "false"?
141 return self.createIndex(row, column, childItem)
145 def parent(self, child):
146 if not child.isValid():
148 childItem = child.internalPointer()
149 parentItem = childItem.parent()
151 if not parentItem or parentItem == self.rootItem:
153 return self.createIndex(parentItem.row(), 0, parentItem)
155 def rowCount(self, parent=None):
156 if not parent or not parent.isValid():
157 parentItem = self.rootItem
159 parentItem = parent.internalPointer()
161 return parentItem.node().childNodes().count()
163 def columnCount(self, parent):
164 # just one column we'll print tag name (and attributes) or the
170 # wrapper around PyQt4.QtXml.QDomNode it keeps an hash of
171 # childrens for performance reasons
173 def __init__(self, node, row, parent = 0):
174 # node is of type PyQt4.QtXml.QDomNode
176 self.parentItem = parent
181 if i in self.childItems:
182 return self.childItems[i]
183 if i >= 0 and i < self.domNode.childNodes().count():
184 childNode = self.domNode.childNodes().item(i)
185 childItem = DomItem(childNode, i, self)
186 self.childItems[i] = childItem
191 return self.parentItem
197 return self.rowNumber
199 class XmlView(QTreeView):
200 def __init__(self, parent=None):
201 QTreeView.__init__(self, parent)
203 delegate = XmlDelegate(self)
204 delegate.insertNodeDelegate('element', ElemNodeDelegate())
205 delegate.insertNodeDelegate('text', TextNodeDelegate())
206 delegate.insertNodeDelegate('comment', CommentNodeDelegate())
207 self.setItemDelegate(delegate)
209 self.setAnimated(True)
210 self.setItemsExpandable(True)
211 self.setRootIsDecorated(True)
212 self.setHeaderHidden(True)
213 self.setAttribute(Qt.WA_MacShowFocusRect, 0)
214 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
216 class XmlDelegate(QItemDelegate):
218 def __init__(self, parent=None):
219 QAbstractItemDelegate.__init__(self, parent)
222 def insertNodeDelegate(self, nodeType, delegate):
223 delegate.setParent(self)
224 self.delegates[nodeType] = delegate
226 def removeNodeDelegate(self, nodeType, delegate):
227 if nodeType in self.delegates:
228 del self.delegates[nodeType]
230 def paint(self, painter, option, index):
232 #print "ASKING FOR DATA"
233 nodeData = index.model().data(index)
235 nodeType = nodeData.getType()
236 delegate = self.delegates.get(nodeType)
237 #print "DELEGS DICT:", self.delegates
238 #print "NODETYPE:", nodeType.toString()
239 if delegate is not None:
240 #print "WOW DELEG ISNT NONE"
241 delegate.paint(painter, option, index)
244 # not sure this will ever work. this delegate
245 # doesn't know about my QMap strategy.
246 QItemDelegate.paint(self, painter, option, index)
247 #print time.strftime('%M:%S'),"[%.3f s]"%(time.time()-start), 'paint' ,'Done'
249 def sizeHint(self, option, index):
251 fm = option.fontMetrics
252 nodeData = index.model().data(index)
253 nodeType = nodeData.getType()
254 text = nodeData.toString()
255 if nodeType == 'element' or nodeType == 'comment':
257 elif nodeType == 'text':
258 nl = text.count('\n')
259 numlines = nl if nl > 0 else 1
260 sys.__stdout__.write("TEXT: \n" + text)
263 document = QTextDocument()
264 document.setDefaultFont(option.font)
265 document.setHtml(text)
266 # the +5 is for margin. The +4 is voodoo;
267 # fm.height just give it too small.
268 #print time.strftime('%M:%S'),"[%.3f s]"%(time.time()-start), 'hint' ,'Done'
269 return QSize(document.idealWidth() + 5, (fm.height() + 4) * numlines)
271 class ElemNodeDelegate(QAbstractItemDelegate):
272 def paint(self, painter, option, index):
273 palette = QApplication.palette()
274 document = QTextDocument()
275 document.setDefaultFont(option.font)
276 nonHighGlobPattern = '<<b><font color="#b42be2">%s</font></b>%s>'
277 nonHighAttPattern = ' <b>%s</b>="<font color="#1e90ff">%s</font>"'
278 highGlobPattern = '<<b>%s</b>%s>'
279 highAttPattern = ' <b>%s</b>="%s"'
280 def getHtmlText(plainText, globPattern, attPattern):
281 tmp = plainText.split(' ', 1)
285 # many elems don't have atts...
286 attList = tmp[1].split()
290 attValue = tmp[1][1:-1]
291 AttListHtml += (attPattern % (attName, attValue))
292 html = (globPattern % (elemName, AttListHtml))
294 def colorize(color, text):
295 return '<font color=' + color + '>' + text + '</font>'
296 nodeData = index.model().data(index)
297 nodeType = nodeData.getType()
298 # Uff... QString Vs string...
299 text = str(nodeData.toString())
300 if option.state & QStyle.State_Selected:
301 htmlText = colorize(palette.highlightedText().color().name(),
302 getHtmlText(text, highGlobPattern, highAttPattern))
304 #print getHtmlText(text, highGlobPattern, highAttPattern)
305 document.setHtml(QString(htmlText))
307 htmlText = getHtmlText(text, nonHighGlobPattern, nonHighAttPattern)
308 document.setHtml(QString(htmlText))
309 color = palette.highlight().color() \
310 if option.state & QStyle.State_Selected \
311 else palette.base().color()
313 # voodoo: if not highlighted, filling the rect
314 # with the base color makes no difference
315 painter.fillRect(option.rect, color)
316 painter.translate(option.rect.x(), option.rect.y())
317 document.drawContents(painter)
320 class TextNodeDelegate(QAbstractItemDelegate):
321 def paint(self, painter, option, index):
322 palette = QApplication.palette()
323 document = QTextDocument()
324 document.setDefaultFont(option.font)
325 def verbatimize(text):
326 text.replace('\n', '<br>')
327 return '<pre>' + text + '</pre'
328 def colorize(color, text):
329 return '<font color=' + color + '>' + text + '</font>'
330 nodeData = index.model().data(index)
331 nodeType = nodeData.getType()
332 text = nodeData.toString()
333 if option.state & QStyle.State_Selected:
334 htmlText = colorize(palette.highlightedText().color().name(),
336 document.setHtml(QString(htmlText))
338 htmlText = verbatimize(text)
339 document.setHtml(QString(htmlText))
340 color = palette.highlight().color() \
341 if option.state & QStyle.State_Selected \
342 else palette.base().color()
344 # voodoo: if not highlighted, filling the rect
345 # with the base color makes no difference
346 painter.fillRect(option.rect, color)
347 painter.translate(option.rect.x(), option.rect.y())
348 document.drawContents(painter)
351 class CommentNodeDelegate(QAbstractItemDelegate):
352 def paint(self, painter, option, index):
353 paint(self, painter, option, index)
355 def paint(self, painter, option, index):
356 text = index.model().data(index).property('content').toString()
357 palette = QApplication.palette()
358 document = QTextDocument()
359 document.setDefaultFont(option.font)
360 if option.state & QStyle.State_Selected:
361 rx = QRegExp(QString('<font .*>'))
363 # If selected, I remove the <font color="..."> by hand,
364 # and give the highlight color
365 document.setHtml(QString("<font color=%1>%2</font>") \
366 .arg(palette.highlightedText().color().name())\
367 .arg(text.replace(rx, QString('')).
368 replace(QString('</font>'),QString(''))))
370 document.setHtml(text)
371 color = palette.highlight().color() \
372 if option.state & QStyle.State_Selected \
373 else palette.base().color()
375 # voodoo: if not highlighted, filling the rect
376 # with the base color makes no difference
377 painter.fillRect(option.rect, color)
378 painter.translate(option.rect.x(), option.rect.y())
379 document.drawContents(painter)