Follow cursor.
[dom-editor.git] / Dome / Display2.py
blobc1139fe50a49e636ced95fa0ab6d081243b2a8fb
1 from __future__ import generators
3 import rox
4 from rox import g
5 from xml.dom import Node
7 def calc_node(display, node, pos):
8 if node.nodeType == Node.TEXT_NODE:
9 text = node.nodeValue.strip()
10 elif node.nodeType == Node.ELEMENT_NODE:
11 text = node.nodeName
12 for key in node.attributes:
13 a = node.attributes[key]
14 text += ' %s=%s' % (unicode(a.name), unicode(a.value))
15 elif node.nodeType == Node.COMMENT_NODE:
16 text = node.nodeValue.strip()
17 elif node.nodeName:
18 text = node.nodeName
19 elif node.nodeValue:
20 text = '<noname>' + node.nodeValue
21 else:
22 text = '<unknown>'
24 layout = display.create_pango_layout(text)
25 width, height = layout.get_pixel_size()
26 x, y = pos
28 def draw_fn():
29 surface = display.pm
30 style = display.style
31 fg = display.style.fg_gc
32 bg = display.style.bg_gc
34 surface.draw_rectangle(fg[g.STATE_NORMAL], True,
35 x, y, 8, height - 1)
36 surface.draw_rectangle(display.style.white_gc, True,
37 x + 1, y + 1, 6, height - 3)
39 if node in display.selection:
40 surface.draw_rectangle(bg[g.STATE_SELECTED], True,
41 x + 12, y, width - 1, height - 1)
42 surface.draw_layout(fg[g.STATE_SELECTED], x + 12, y, layout)
43 else:
44 if node.nodeType == Node.TEXT_NODE:
45 gc = style.text_gc[g.STATE_NORMAL]
46 else:
47 gc = style.fg_gc[g.STATE_NORMAL]
48 surface.draw_layout(gc, x + 12, y, layout)
50 bbox = (x, y, x + 12 + width, y + height)
51 return bbox, draw_fn
53 class Display(g.EventBox):
54 def __init__(self, window, view):
55 g.EventBox.__init__(self)
56 self.set_app_paintable(True)
57 self.set_double_buffered(False)
58 self.update_timeout = 0
60 self.view = None
61 self.parent_window = window
62 self.pm = None
64 s = self.get_style().copy()
65 s.bg[g.STATE_NORMAL] = g.gdk.color_parse('old lace')
66 s.text[g.STATE_NORMAL] = g.gdk.color_parse('blue')
67 self.set_style(s)
69 #self.connect('destroy', self.destroyed)
70 self.connect('button-press-event', self.bg_event)
71 self.connect('button-release-event', self.bg_event)
73 #self.set_view(view)
75 #rox.app_options.add_notify(self.options_changed)
77 # Display is relative to this node, which is the highest displayed node (possibly
78 # off the top of the screen)
79 self.ref_node = view.root
80 self.ref_pos = (0, 0)
82 self.last_alloc = None
83 self.connect('size-allocate', lambda w, a: self.size_allocate(a))
84 self.connect('size-request', lambda w, r: self.size_request(r))
85 self.connect('expose-event', lambda w, e: 1)
87 self.pan_timeout = None
88 self.h_limits = (0, 0)
89 self.set_view(view)
91 def size_allocate(self, alloc):
92 new = (alloc.width, alloc.height)
93 if self.last_alloc == new:
94 return
95 self.last_alloc = new
96 assert self.window
97 #print "Alloc", alloc.width, alloc.height
98 pm = g.gdk.Pixmap(self.window, alloc.width, alloc.height, -1)
99 self.window.set_back_pixmap(pm, False)
100 self.pm = pm
101 self.update()
103 def update(self):
104 if not self.pm: return
105 #print "update"
107 self.update_timeout = 0
109 self.pm.draw_rectangle(self.style.bg_gc[g.STATE_NORMAL], True,
110 0, 0, self.last_alloc[0], self.last_alloc[1])
112 self.drawn = {} # xmlNode -> (x1, y1, y2, y2)
114 n = self.ref_node
115 p = self.view.root.parentNode
116 while n is not p:
117 n = n.parentNode
118 if not n:
119 print "(lost root)"
120 self.ref_node = self.view.root
121 self.ref_pos = (0, 0)
122 break
124 self.selection = {}
125 for n in self.view.current_nodes:
126 self.selection[n] = None
128 pos = list(self.ref_pos)
129 self.h_limits = (self.ref_pos[0], self.ref_pos[0]) # Left, Right
130 node = self.ref_node
131 for node, bbox, draw_fn in self.walk_tree(self.ref_node, self.ref_pos):
132 if bbox[1] > self.last_alloc[1]: break # Off-screen
134 draw_fn()
135 self.drawn[node] = bbox
136 if bbox[1] < 0:
137 self.ref_node = node
138 self.ref_pos = bbox[:2]
139 self.h_limits = (min(self.h_limits[0], bbox[0]),
140 max(self.h_limits[1], bbox[2]))
142 self.window.clear()
144 return 0
146 def walk_tree(self, node, pos):
147 """Yield this (node, bbox), and all following ones in document order."""
148 pos = list(pos)
149 while node:
150 bbox, draw_fn = calc_node(self, node, pos)
151 yield (node, bbox, draw_fn)
152 pos[1] = bbox[3] + 2
153 if node.childNodes:
154 node = node.childNodes[0]
155 pos[0] += 16
156 else:
157 while not node.nextSibling:
158 node = node.parentNode
159 if not node: return
160 pos[0] -= 16
161 node = node.nextSibling
163 def size_request(self, req):
164 req.width = 4
165 req.height = 4
167 def do_update_now(self):
168 # Update now, if we need to
169 if self.update_timeout:
170 g.timeout_remove(self.update_timeout)
171 self.update()
173 def update_all(self, node = None):
174 if self.update_timeout:
175 return # Going to update anyway...
177 if self.view.running():
178 self.update_timeout = g.timeout_add(2000, self.update)
179 else:
180 self.update_timeout = g.timeout_add(10, self.update)
182 def move_from(self, old = []):
183 if self.view.current_nodes:
184 selection = {}
185 for n in self.view.current_nodes:
186 selection[n] = None
187 shown = False
188 for node, bbox, draw_fn in self.walk_tree(self.ref_node, self.ref_pos):
189 if bbox[1] > self.last_alloc[1]: break # Off-screen
190 if bbox[3] > 0 and node in selection:
191 shown = True
192 break # A selected node is shown
193 if not shown:
194 print "(selected nodes not shown)"
195 self.ref_node = self.view.current_nodes[0]
196 self.ref_pos = (40, self.last_alloc[1] / 2)
197 self.backup_ref_node()
198 self.update_all()
200 def set_view(self, view):
201 if self.view:
202 self.view.remove_display(self)
203 self.view = view
204 self.view.add_display(self)
205 self.update_all()
207 def show_menu(self, bev):
208 pass
210 def node_clicked(self, node, event):
211 pass
213 def xy_to_node(self, x, y):
214 for (n, (x1, y1, x2, y2)) in self.drawn.iteritems():
215 if x >= x1 and x <= x2 and y >= y1 and y <= y2:
216 return n
218 def pan(self):
219 def scale(x):
220 val = (float(abs(x)) ** 1.4)
221 if x < 0:
222 return -val
223 else:
224 return val
225 def chop(x):
226 if x > 10: return x - 10
227 if x < -10: return x + 10
228 return 0
229 x, y, mask = self.window.get_pointer()
230 sx, sy = self.pan_start
231 dx, dy = scale(chop(x - sx)) / 20, scale(chop(y - sy))
232 dx = max(dx, 10 - self.h_limits[1])
233 dx = min(dx, self.last_alloc[0] - 10 - self.h_limits[0])
234 new = [self.ref_pos[0] + dx, self.ref_pos[1] + dy]
236 if new == self.ref_pos:
237 return 1
239 self.ref_pos = new
241 self.backup_ref_node()
243 self.update()
245 return 1
247 def backup_ref_node(self):
248 self.ref_pos = list(self.ref_pos)
249 # Walk up the parents until we get a ref node above the start of the screen
250 # (redraw will come back down)
251 while self.ref_pos[1] > 0:
252 src = self.ref_node
254 if self.ref_node.previousSibling:
255 self.ref_node = self.ref_node.previousSibling
256 elif self.ref_node.parentNode:
257 self.ref_node = self.ref_node.parentNode
258 else:
259 break
261 # Walk from the parent node to find how far it is to this node...
262 for node, bbox, draw_fn in self.walk_tree(self.ref_node, (0, 0)):
263 if node is src: break
264 else:
265 assert 0
267 self.ref_pos[0] -= bbox[0]
268 self.ref_pos[1] -= bbox[1]
270 #print "(start from %s at (%d,%d))" % (self.ref_node, self.ref_pos[0], self.ref_pos[1])
272 if self.ref_pos[1] > 10:
273 self.ref_pos[1] = 10
274 elif self.ref_pos[1] < -100:
275 for node, bbox, draw_fn in self.walk_tree(self.ref_node, self.ref_pos):
276 if bbox[3] > 10: break # Something is visible
277 else:
278 self.ref_pos[1] = -100
280 def bg_event(self, widget, event):
281 if event.type == g.gdk.BUTTON_PRESS and event.button == 3:
282 self.show_menu(event)
283 elif event.type == g.gdk.BUTTON_PRESS or event.type == g.gdk._2BUTTON_PRESS:
284 self.do_update_now()
285 node = self.xy_to_node(event.x, event.y)
286 if event.button == 1:
287 if node:
288 self.node_clicked(node, event)
289 elif event.button == 2:
290 assert self.pan_timeout is None
291 self.pan_start = (event.x, event.y)
292 self.pan_timeout = g.timeout_add(100, self.pan)
293 elif event.type == g.gdk.BUTTON_RELEASE and event.button == 2:
294 assert self.pan_timeout is not None
295 g.timeout_remove(self.pan_timeout)
296 self.pan_timeout = None
297 else:
298 return 0
299 return 1
301 def marked_changed(self, nodes):
302 "nodes is a list of nodes to be rechecked."
303 self.update_all()