comparison pyink/domview.py @ 1243:d5f70928e9f1

Move MBScene_domview_ui and MBScene_domview to separated modules. - domview.py is where MBScene_domview and MBScene_domview_monitor are. - domview_ui.py is where MBScene_domview_ui and MBScene_frameline_stack are. - keep modules small to make better collaberation.
author Thinker K.F. Li <thinker@codemud.net>
date Mon, 10 Jan 2011 16:32:16 +0800
parents
children b241f9768833
comparison
equal deleted inserted replaced
1242:1b1eb8f9a866 1243:d5f70928e9f1
1 import random
2 import pybInkscape
3 from tween import TweenObject
4
5 class Layer:
6 def __init__(self, node):
7 self.scenes = []
8 self.group = node
9 pass
10 pass
11
12 class ObjectWatcher(pybInkscape.PYNodeObserver):
13 def __init__(self, obj, type, func, arg):
14 self.obj = obj
15 self.type = type
16 self.func = func
17 self.arg = arg
18
19 def notifyChildAdded(self, node, child, prev):
20 if self.type == 'DOMNodeInserted':
21 self.func(node, child)
22 def notifyChildRemoved(self, node, child, prev):
23 if self.type == 'DOMNodeRemoved':
24 self.func(node, child)
25 def notifyChildOrderChanged(self,node,child,prev):
26 pass
27 def notifyContentChanged(self,node,old_content,new_content):
28 if self.type == 'DOMSubtreeModified':
29 self.func(node)
30 def notifyAttributeChanged(self,node, name, old_value, new_value):
31 if self.type == 'DOMAttrModified':
32 self.func(node, name, old_value, new_value)
33
34 def addEventListener(obj, type, func, arg):
35 obs = ObjectWatcher(obj, type, func, arg)
36 obj.addSubtreeObserver(obs)
37 pass
38
39
40 ## \brief Monitor changes of DOM-tree.
41 #
42 # This class monitors DOM-tree to maintain _maxframe and maps for node ID to
43 # node and scene group ID to scene node.
44 class MBScene_domview_monitor(object):
45 def __init__(self, *args, **kws):
46 super(MBScene_domview_monitor, self).__init__()
47
48 self._maxframe = 0
49 self._id2node = {} # map ID to the node in the DOM tree.
50 self._group2scene = {} # map ID of a group to associated scene node.
51 pass
52
53 def _start_monitor(self):
54 self._collect_node_ids()
55 self._collect_all_scenes()
56
57 doc = self._doc
58 addEventListener(doc, 'DOMNodeInserted', self._on_insert_node, None)
59 addEventListener(doc, 'DOMNodeRemoved', self._on_remove_node, None)
60 addEventListener(doc, 'DOMAttrModified', self._on_attr_modified, None)
61 pass
62
63 def _on_insert_node(self, node, child):
64 for cchild in child.childList():
65 self._on_insert_node(child, cchild)
66 pass
67
68 try:
69 child_id = child.getAttribute('id')
70 except:
71 pass
72 else:
73 if child_id not in self._id2node:
74 self._id2node[child_id] = child
75 pass
76 pass
77
78 if child.name() == 'ns0:scene':
79 try:
80 ref = child.getAttribute('ref')
81 except:
82 pass
83 else:
84 if ref not in self._group2scene:
85 self._group2scene[ref] = child
86 pass
87 pass
88
89 try:
90 start = child.getAttribute('start')
91 self._maxframe = max(int(start), self._maxframe)
92 except:
93 pass
94 try:
95 start = child.getAttribute('end')
96 self._maxframe = max(int(start), self._maxframe)
97 except:
98 pass
99 pass
100 pass
101
102 def _find_maxframe(self, scenes_node):
103 maxframe = 0
104 for child in scenes_node.childList():
105 if child.name() != 'ns0:scene':
106 continue
107
108 try:
109 start = child.getAttribute('start')
110 maxframe = max(int(start), maxframe)
111 except:
112 pass
113 try:
114 end = child.getAttribute('end')
115 maxframe = max(int(end), maxframe)
116 except:
117 pass
118 pass
119 return maxframe
120
121 def _on_remove_node(self, node, child):
122 for cchild in child.childList():
123 self._on_remove_node(child, cchild)
124 pass
125
126 try:
127 child_id = child.getAttribute('id')
128 except:
129 pass
130 else:
131 if child_id not in self._id2node:
132 raise ValueError, \
133 'remove a node that is never known (%s)' % (child_id)
134 del self._id2node[child_id]
135 pass
136
137 if child.name() == 'ns0:scene':
138 try:
139 ref = child.getAttribute('ref')
140 except:
141 pass
142 else:
143 del self._group2scene[ref]
144 pass
145
146 try:
147 if node.name() == 'ns0:scenes' and \
148 (int(child.getAttribute('start')) == self._maxframe or
149 int(child.getAttribute('end')) == self._maxframe):
150 self._maxframe = self._find_maxframe(node)
151 pass
152 except:
153 pass
154 pass
155 pass
156
157 def _on_attr_modified(self, node, name, old_value, new_value):
158 if name == 'id' and old_value != new_value:
159 if old_value and (old_value not in self._id2node):
160 raise ValueError, \
161 'old ID value of passed node is invalid one (%s)' % \
162 (old_value)
163 if (new_value in self._id2node):
164 raise ValueError, \
165 'new ID value of passed node is invalid one (%s)' % \
166 (new_value)
167
168 if old_value:
169 del self._id2node[old_value]
170 pass
171 self._id2node[new_value] = node
172 pass
173 elif name == 'ref' and node.name() == 'ns0:scene':
174 if old_value == new_value:
175 return
176 if old_value:
177 node = self._group2scene[old_value] # use old node. Binding
178 # may generate a new
179 # wrapper.
180 del self._group2scene[old_value]
181 pass
182 if new_value:
183 self._group2scene[new_value] = node
184 pass
185 pass
186 elif (name in ('start', 'end')) and node.name() == 'ns0:scene':
187 self._maxframe = max(int(new_value), self._maxframe)
188 pass
189 pass
190
191 ## \brief Collect ID of nodes in the document.
192 #
193 # It is used to implement a fast mapping from an ID to the respective node.
194 #
195 def _collect_node_ids(self):
196 self._id2node = {}
197 root = self._root
198 for n in root.childList():
199 self._collect_node_ids_recursive(n)
200 pass
201 pass
202
203 def _collect_node_ids_recursive(self, node):
204 try:
205 node_id = node.getAttribute('id')
206 except:
207 return
208
209 self._id2node[node_id] = node
210 for n in node.childList():
211 self._collect_node_ids_recursive(n)
212 pass
213 pass
214
215 def _parse_one_scene(self, scene_node):
216 assert scene_node.name() == 'ns0:scene'
217
218 start = int(scene_node.getAttribute("start"))
219 try:
220 end = int(scene_node.getAttribute("end"))
221 except:
222 end = start
223 pass
224
225 try:
226 scene_type = scene_node.getAttribute('type')
227 if scene_type == None:
228 scene_type = 'normal'
229 pass
230 except:
231 scene_type = 'normal'
232 pass
233
234 return start, end, scene_type
235
236 def _parse_one_scenes(self, scenes_node):
237 try:
238 cur = int(n.getAttribute("current"))
239 except:
240 cur = 0
241 pass
242 self.current = cur
243
244 for scene_node in scenes_node.childList():
245 if scene_node.name() != 'ns0:scene':
246 continue
247
248 try:
249 start, end, scene_type = self._parse_one_scene(scene_node)
250 except:
251 continue
252
253 group_id = scene_node.getAttribute("ref")
254 self._group2scene[group_id] = scene_node
255 pass
256 pass
257
258 ## \brief Parse all scenes node in svg:metadata subtree.
259 #
260 def _collect_all_scenes(self):
261 root = self._root
262 for child in root.childList():
263 if child.name() != 'svg:metadata':
264 continue
265
266 metadata_node = child
267 for metachild in metadata_node.childList():
268 if metachild.name() == 'ns0:scenes':
269 self._parse_one_scenes(metachild)
270 self._maxframe = self._find_maxframe(metachild)
271 pass
272 pass
273 pass
274 pass
275
276 ## \brief Return the node with given ID.
277 #
278 def get_node(self, node_id):
279 return self._id2node[node_id]
280
281 ## \brief Return a scene node corresponding to a scene group of given ID.
282 #
283 def get_scene(self, group_id):
284 return self._group2scene[group_id]
285
286 def new_id(self):
287 while True:
288 candidate = 's%d' % int(random.random()*100000)
289 if candidate not in self._id2node:
290 return candidate
291 pass
292 pass
293 pass
294
295
296 ## \brief This layer provide a data view to the DOM-tree.
297 #
298 # This class maintains layers information, and provides functions to create,
299 # change and destroy scene node and scene group. A scene node is a 'ns0:scene'
300 # in 'ns0:scenes' tag. A scene group is respective 'svg:g' for a scene.
301 #
302 class MBScene_domview(MBScene_domview_monitor):
303 # Declare variables, here, for keeping tracking
304 _doc = None
305 _root = None
306
307 def __init__(self, *args, **kws):
308 super(MBScene_domview, self).__init__()
309 pass
310
311 ## \brief Create a scenes node if not existed.
312 #
313 def _init_metadata(self):
314 for node in self._root.childList():
315 if node.name() == 'svg:metadata':
316 break
317 pass
318 else:
319 raise RuntimeError, \
320 'can not find <svg:metadata> node in the document'
321
322 for n in node.childList():
323 if n.name() == 'ns0:scenes':
324 self._scenes_node = n
325 break
326 pass
327 else:
328 ns = "http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd"
329 self._root.setAttribute("xmlns:ns0", ns)
330 scenes_node = self._doc.createElement("ns0:scenes")
331 node.appendChild(scenes_node)
332 self._scenes_node = scenes_node
333 pass
334 pass
335
336 def _parse_all_layers(self):
337 root = self._root
338 layers = self._layers
339
340 for child in root.childList():
341 if child.name() != 'svg:g':
342 continue
343
344 layer_group = child
345 layer = Layer(layer_group)
346 layer.idx = len(layers)
347 layers.append(layer)
348 self.parse_layer(layer.idx)
349 pass
350 pass
351
352 def handle_doc_root(self, doc, root):
353 self._doc = doc
354 self._root = root
355 self._layers = []
356
357 self._start_monitor() # start MBScene_domview_monitor
358 self._init_metadata()
359 self._parse_all_layers()
360 pass
361
362 def dumpattr(self, n):
363 s = ""
364 for a,v in n.attrib.items():
365 s = s + ("%s=%s" % (a,v))
366 pass
367 return s
368
369 def dump(self, node, l=0):
370 print " " * l*2,"<", node.tag, self.dumpattr(node),">"
371 for n in node:
372 self.dump(n, l+1)
373 pass
374 print " " * l * 2,"/>"
375 pass
376
377 ## \brief Create and add a ns0:scene node under ns0:scenes subtree.
378 #
379 def add_scene_node(self, start, end,
380 frame_type=TweenObject.TWEEN_TYPE_NORMAL,
381 ref=None):
382 type_names = ('normal', 'scale')
383 scenes_node = self._scenes_node
384 doc = self._doc
385
386 scene_node = doc.createElement('ns0:scene')
387 self.chg_scene_node(scene_node, start=start)
388 if start != end:
389 self.chg_scene_node(scene_node, end=end)
390 pass
391 type_name = type_names[frame_type]
392 self.chg_scene_node(scene_node, tween_type=type_name)
393 if ref:
394 self.chg_scene_node(scene_node, ref=ref)
395 pass
396
397 scenes_node.appendChild(scene_node)
398
399 return scene_node
400
401 ## \brief Change attributes of a scene node.
402 #
403 # This is here to monitor changes of scene node.
404 def chg_scene_node(self, scene_node, start=None, end=None,
405 tween_type=None, ref=None):
406 if start is not None:
407 scene_node.setAttribute('start', str(start))
408 pass
409 if end is not None:
410 scene_node.setAttribute('end', str(end))
411 pass
412 if tween_type is not None:
413 scene_node.setAttribute('type', tween_type)
414 pass
415 if ref is not None:
416 scene_node.setAttribute('ref', ref)
417 pass
418 pass
419
420 def rm_scene_node(self, scene_node):
421 self._scenes_node.removeChild(scene_node)
422 pass
423
424 def rm_scene_node_n_group(self, scene_node):
425 scene_group_id = scene_node.getAttribute('ref')
426 scene_group_node = self.get_node(scene_group_id)
427 scene_group_node.parent().removeChild(scene_group_node)
428
429 self._scenes_node.removeChild(scene_node)
430 pass
431
432 ## \brief Create and add a svg:g for a scene under a group for a layer.
433 #
434 def add_scene_group(self, layer_idx):
435 layer = self._layers[layer_idx]
436 doc = self._doc
437
438 scene_group = doc.createElement('svg:g')
439 gid = self.new_id()
440 scene_group.setAttribute("id", gid)
441 scene_group.setAttribute("inkscape:groupmode", "layer")
442
443 layer.group.appendChild(scene_group)
444
445 return scene_group
446
447 def parse_layer(self, layer_idx):
448 layer = self._layers[layer_idx]
449 layer_group = layer.group
450
451 for child in layer_group.childList():
452 if child.name() != 'svg:g':
453 continue
454 try:
455 child_id = child.getAttribute('id')
456 scene_node = self.get_scene(child_id)
457 except:
458 continue
459
460 layer.scenes.append(scene_node)
461 pass
462 pass
463
464 ## \brief Add/insert a layer at given position.
465 #
466 # \param layer_idx is the position in the layer list.
467 #
468 def insert_layer(self, layer_idx, layer_group):
469 layers = self._layers
470
471 layer = Layer(layer_group)
472 if layer_idx >= len(layers):
473 layers.append(layer)
474 else:
475 layers.insert(layer_idx, layer)
476 for idx in range(layer_idx, len(layers)):
477 layers[idx].idx = idx
478 pass
479 pass
480 pass
481
482 ## \brief Remove layer and associated scene nodes and scene groups.
483 #
484 def rm_layer(self, layer_idx):
485 layers = self._layers
486
487 for layer in layers:
488 for scene_node in layer.scenes:
489 scene_group_id = scene_node.getAttribute('ref')
490 scene_group_node = self.get_node(scene_group_id)
491 scene_group_node.parent().removeChild(scene_group_node)
492
493 scene_node.parent().removeChild(scene_node)
494 pass
495 pass
496
497 del layers[layer_idx]
498
499 for idx in range(layer_idx, len(layers)):
500 layers[idx].idx = idx
501 pass
502 pass
503
504 def get_layer_num(self):
505 return len(self._layers)
506
507 def find_layer_n_scene_of_node(self, node_id):
508 for layer_idx, layer in enumerate(self._layers):
509 for scene_node in layer.scenes:
510 scene_group_id = scene_node.getAttribute('ref')
511 if scene_group_id == node_id:
512 return layer_idx, scene_node
513 pass
514 pass
515 return -1, None
516
517 def get_layer_group(self, layer_idx):
518 layer = self._layers[layer_idx]
519 return layer.group
520
521 def get_all_scene_node_of_layer(self, layer_idx):
522 layer = self._layers[layer_idx]
523 return layer.scenes
524
525 def get_layer_data(self, layer_idx):
526 layer = self._layers[layer_idx]
527 try:
528 data = layer.data
529 except:
530 return None
531 return data
532
533 def set_layer_data(self, layer_idx, data):
534 layer = self._layers[layer_idx]
535 layer.data = data
536 pass
537
538 def create_layer_dup_group(self, layer_idx):
539 layer = self._layers[layer_idx]
540
541 dup_group = self._doc.createElement('svg:g')
542 gid = self.new_id()
543 dup_group.setAttribute('id', gid)
544 dup_group.setAttribute('inkscape:label', 'dup')
545 dup_group.setAttribute('sodipodi:insensitive', '1')
546 dup_group.setAttribute('style', '')
547
548 layer.group.appendChild(dup_group)
549
550 return dup_group
551
552 def insert_frames(self, layer_idx, frame_idx, num):
553 layer = self._layers[layer_idx]
554 for scene_node in layer.scenes:
555 start, end, tween_type = self._parse_one_scene(scene_node)
556 if start >= frame_idx:
557 self.chg_scene_node(scene_node, start=(start + num))
558 pass
559 if end >= frame_idx:
560 self.chg_scene_node(scene_node, end=(end + num))
561 pass
562 pass
563 pass
564
565 ## \brief Remove frames
566 #
567 # - Scenes covered by removing range were removed.
568 # - Scenes after removing range were shifted left.
569 #
570 def rm_frames(self, layer_idx, frame_idx, num):
571 layer = self._layers[layer_idx]
572
573 last_rm = frame_idx + num - 1 # last removed frame
574 for scene_node in layer.scenes:
575 start, end, tween_type = \
576 self._dom._parse_one_scene(scene_node)
577
578 if end < frame_idx:
579 continue
580
581 if start > last_rm: # this scene is at right side
582 self.chg_scene_node(scene_node,
583 start=(start - num),
584 end=(end - num))
585 else: # this scene is covered by removing range
586 self.rm_scene_node_n_group(scene_node)
587 pass
588 pass
589 pass
590
591 def get_max_frame(self):
592 return self._maxframe
593 pass
594