comparison pyink/MBScene.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 1b1eb8f9a866
children b241f9768833
comparison
equal deleted inserted replaced
1242:1b1eb8f9a866 1243:d5f70928e9f1
2 # -*- indent-tabs-mode: t; tab-width: 8; python-indent: 4; fill-column: 79 -*- 2 # -*- indent-tabs-mode: t; tab-width: 8; python-indent: 4; fill-column: 79 -*-
3 # vim: sw=4:ts=8:sts=4:textwidth=79 3 # vim: sw=4:ts=8:sts=4:textwidth=79
4 import pygtk 4 import pygtk
5 import gtk 5 import gtk
6 import glib 6 import glib
7 from copy import deepcopy
8 from lxml import etree
9 import random
10 import traceback 7 import traceback
11 import time
12 import pybInkscape 8 import pybInkscape
13 import math
14 from tween import TweenObject 9 from tween import TweenObject
15 from frameline import frameline, frameruler 10 from domview_ui import MBScene_domview_ui
16 11
17 # Please refer to 12 # Please refer to
18 # http://www.assembla.com/wiki/show/MadButterfly/Inkscape_extention 13 # http://www.assembla.com/wiki/show/MadButterfly/Inkscape_extention
19 # for the designed document. 14 # for the designed document.
20 15
40 # scene is 10, we will set the scene field as "8-10". 35 # scene is 10, we will set the scene field as "8-10".
41 # - 5. If this scene are filled screne, we will split the existing 36 # - 5. If this scene are filled screne, we will split the existing
42 # scene into two scenes with the same content. 37 # scene into two scenes with the same content.
43 # 38 #
44 39
45 class Layer:
46 def __init__(self, node):
47 self.scenes = []
48 self.group = node
49 pass
50 pass
51
52 class ObjectWatcher(pybInkscape.PYNodeObserver):
53 def __init__(self, obj, type, func, arg):
54 self.obj = obj
55 self.type = type
56 self.func = func
57 self.arg = arg
58
59 def notifyChildAdded(self, node, child, prev):
60 if self.type == 'DOMNodeInserted':
61 self.func(node, child)
62 def notifyChildRemoved(self, node, child, prev):
63 if self.type == 'DOMNodeRemoved':
64 self.func(node, child)
65 def notifyChildOrderChanged(self,node,child,prev):
66 pass
67 def notifyContentChanged(self,node,old_content,new_content):
68 if self.type == 'DOMSubtreeModified':
69 self.func(node)
70 def notifyAttributeChanged(self,node, name, old_value, new_value):
71 if self.type == 'DOMAttrModified':
72 self.func(node, name, old_value, new_value)
73
74 def addEventListener(obj, type, func, arg):
75 obs = ObjectWatcher(obj, type, func, arg)
76 obj.addSubtreeObserver(obs)
77 pass
78
79 ## \brief Iterator to travel a sub-tree of DOM. 40 ## \brief Iterator to travel a sub-tree of DOM.
80 # 41 #
81 def _DOM_iterator(node): 42 def _DOM_iterator(node):
82 nodes = [node] 43 nodes = [node]
83 while nodes: 44 while nodes:
89 pass 50 pass
90 yield node 51 yield node
91 pass 52 pass
92 pass 53 pass
93 54
94
95 ## \brief Monitor changes of DOM-tree.
96 #
97 # This class monitors DOM-tree to maintain _maxframe and maps for node ID to
98 # node and scene group ID to scene node.
99 class MBScene_domview_monitor(object):
100 def __init__(self, *args, **kws):
101 super(MBScene_domview_monitor, self).__init__()
102
103 self._maxframe = 0
104 self._id2node = {} # map ID to the node in the DOM tree.
105 self._group2scene = {} # map ID of a group to associated scene node.
106 pass
107
108 def _start_monitor(self):
109 self._collect_node_ids()
110 self._collect_all_scenes()
111
112 doc = self._doc
113 addEventListener(doc, 'DOMNodeInserted', self._on_insert_node, None)
114 addEventListener(doc, 'DOMNodeRemoved', self._on_remove_node, None)
115 addEventListener(doc, 'DOMAttrModified', self._on_attr_modified, None)
116 pass
117
118 def _on_insert_node(self, node, child):
119 for cchild in child.childList():
120 self._on_insert_node(child, cchild)
121 pass
122
123 try:
124 child_id = child.getAttribute('id')
125 except:
126 pass
127 else:
128 if child_id not in self._id2node:
129 self._id2node[child_id] = child
130 pass
131 pass
132
133 if child.name() == 'ns0:scene':
134 try:
135 ref = child.getAttribute('ref')
136 except:
137 pass
138 else:
139 if ref not in self._group2scene:
140 self._group2scene[ref] = child
141 pass
142 pass
143
144 try:
145 start = child.getAttribute('start')
146 self._maxframe = max(int(start), self._maxframe)
147 except:
148 pass
149 try:
150 start = child.getAttribute('end')
151 self._maxframe = max(int(start), self._maxframe)
152 except:
153 pass
154 pass
155 pass
156
157 def _find_maxframe(self, scenes_node):
158 maxframe = 0
159 for child in scenes_node.childList():
160 if child.name() != 'ns0:scene':
161 continue
162
163 try:
164 start = child.getAttribute('start')
165 maxframe = max(int(start), maxframe)
166 except:
167 pass
168 try:
169 end = child.getAttribute('end')
170 maxframe = max(int(end), maxframe)
171 except:
172 pass
173 pass
174 return maxframe
175
176 def _on_remove_node(self, node, child):
177 for cchild in child.childList():
178 self._on_remove_node(child, cchild)
179 pass
180
181 try:
182 child_id = child.getAttribute('id')
183 except:
184 pass
185 else:
186 if child_id not in self._id2node:
187 raise ValueError, \
188 'remove a node that is never known (%s)' % (child_id)
189 del self._id2node[child_id]
190 pass
191
192 if child.name() == 'ns0:scene':
193 try:
194 ref = child.getAttribute('ref')
195 except:
196 pass
197 else:
198 del self._group2scene[ref]
199 pass
200
201 try:
202 if node.name() == 'ns0:scenes' and \
203 (int(child.getAttribute('start')) == self._maxframe or
204 int(child.getAttribute('end')) == self._maxframe):
205 self._maxframe = self._find_maxframe(node)
206 pass
207 except:
208 pass
209 pass
210 pass
211
212 def _on_attr_modified(self, node, name, old_value, new_value):
213 if name == 'id' and old_value != new_value:
214 if old_value and (old_value not in self._id2node):
215 raise ValueError, \
216 'old ID value of passed node is invalid one (%s)' % \
217 (old_value)
218 if (new_value in self._id2node):
219 raise ValueError, \
220 'new ID value of passed node is invalid one (%s)' % \
221 (new_value)
222
223 if old_value:
224 del self._id2node[old_value]
225 pass
226 self._id2node[new_value] = node
227 pass
228 elif name == 'ref' and node.name() == 'ns0:scene':
229 if old_value == new_value:
230 return
231 if old_value:
232 node = self._group2scene[old_value] # use old node. Binding
233 # may generate a new
234 # wrapper.
235 del self._group2scene[old_value]
236 pass
237 if new_value:
238 self._group2scene[new_value] = node
239 pass
240 pass
241 elif (name in ('start', 'end')) and node.name() == 'ns0:scene':
242 self._maxframe = max(int(new_value), self._maxframe)
243 pass
244 pass
245
246 ## \brief Collect ID of nodes in the document.
247 #
248 # It is used to implement a fast mapping from an ID to the respective node.
249 #
250 def _collect_node_ids(self):
251 self._id2node = {}
252 root = self._root
253 for n in root.childList():
254 self._collect_node_ids_recursive(n)
255 pass
256 pass
257
258 def _collect_node_ids_recursive(self, node):
259 try:
260 node_id = node.getAttribute('id')
261 except:
262 return
263
264 self._id2node[node_id] = node
265 for n in node.childList():
266 self._collect_node_ids_recursive(n)
267 pass
268 pass
269
270 def _parse_one_scene(self, scene_node):
271 assert scene_node.name() == 'ns0:scene'
272
273 start = int(scene_node.getAttribute("start"))
274 try:
275 end = int(scene_node.getAttribute("end"))
276 except:
277 end = start
278 pass
279
280 try:
281 scene_type = scene_node.getAttribute('type')
282 if scene_type == None:
283 scene_type = 'normal'
284 pass
285 except:
286 scene_type = 'normal'
287 pass
288
289 return start, end, scene_type
290
291 def _parse_one_scenes(self, scenes_node):
292 try:
293 cur = int(n.getAttribute("current"))
294 except:
295 cur = 0
296 pass
297 self.current = cur
298
299 for scene_node in scenes_node.childList():
300 if scene_node.name() != 'ns0:scene':
301 continue
302
303 try:
304 start, end, scene_type = self._parse_one_scene(scene_node)
305 except:
306 continue
307
308 group_id = scene_node.getAttribute("ref")
309 self._group2scene[group_id] = scene_node
310 pass
311 pass
312
313 ## \brief Parse all scenes node in svg:metadata subtree.
314 #
315 def _collect_all_scenes(self):
316 root = self._root
317 for child in root.childList():
318 if child.name() != 'svg:metadata':
319 continue
320
321 metadata_node = child
322 for metachild in metadata_node.childList():
323 if metachild.name() == 'ns0:scenes':
324 self._parse_one_scenes(metachild)
325 self._maxframe = self._find_maxframe(metachild)
326 pass
327 pass
328 pass
329 pass
330
331 ## \brief Return the node with given ID.
332 #
333 def get_node(self, node_id):
334 return self._id2node[node_id]
335
336 ## \brief Return a scene node corresponding to a scene group of given ID.
337 #
338 def get_scene(self, group_id):
339 return self._group2scene[group_id]
340
341 def new_id(self):
342 while True:
343 candidate = 's%d' % int(random.random()*100000)
344 if candidate not in self._id2node:
345 return candidate
346 pass
347 pass
348 pass
349
350
351 ## \brief This layer provide a data view to the DOM-tree.
352 #
353 # This class maintains layers information, and provides functions to create,
354 # change and destroy scene node and scene group. A scene node is a 'ns0:scene'
355 # in 'ns0:scenes' tag. A scene group is respective 'svg:g' for a scene.
356 #
357 class MBScene_domview(MBScene_domview_monitor):
358 # Declare variables, here, for keeping tracking
359 _doc = None
360 _root = None
361
362 def __init__(self, *args, **kws):
363 super(MBScene_domview, self).__init__()
364 pass
365
366 ## \brief Create a scenes node if not existed.
367 #
368 def _init_metadata(self):
369 for node in self._root.childList():
370 if node.name() == 'svg:metadata':
371 break
372 pass
373 else:
374 raise RuntimeError, \
375 'can not find <svg:metadata> node in the document'
376
377 for n in node.childList():
378 if n.name() == 'ns0:scenes':
379 self._scenes_node = n
380 break
381 pass
382 else:
383 ns = "http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd"
384 self._root.setAttribute("xmlns:ns0", ns)
385 scenes_node = self._doc.createElement("ns0:scenes")
386 node.appendChild(scenes_node)
387 self._scenes_node = scenes_node
388 pass
389 pass
390
391 def _parse_all_layers(self):
392 root = self._root
393 layers = self._layers
394
395 for child in root.childList():
396 if child.name() != 'svg:g':
397 continue
398
399 layer_group = child
400 layer = Layer(layer_group)
401 layer.idx = len(layers)
402 layers.append(layer)
403 self.parse_layer(layer.idx)
404 pass
405 pass
406
407 def handle_doc_root(self, doc, root):
408 self._doc = doc
409 self._root = root
410 self._layers = []
411
412 self._start_monitor() # start MBScene_domview_monitor
413 self._init_metadata()
414 self._parse_all_layers()
415 pass
416
417 def dumpattr(self, n):
418 s = ""
419 for a,v in n.attrib.items():
420 s = s + ("%s=%s" % (a,v))
421 pass
422 return s
423
424 def dump(self, node, l=0):
425 print " " * l*2,"<", node.tag, self.dumpattr(node),">"
426 for n in node:
427 self.dump(n, l+1)
428 pass
429 print " " * l * 2,"/>"
430 pass
431
432 ## \brief Create and add a ns0:scene node under ns0:scenes subtree.
433 #
434 def add_scene_node(self, start, end,
435 frame_type=TweenObject.TWEEN_TYPE_NORMAL,
436 ref=None):
437 type_names = ('normal', 'scale')
438 scenes_node = self._scenes_node
439 doc = self._doc
440
441 scene_node = doc.createElement('ns0:scene')
442 self.chg_scene_node(scene_node, start=start)
443 if start != end:
444 self.chg_scene_node(scene_node, end=end)
445 pass
446 type_name = type_names[frame_type]
447 self.chg_scene_node(scene_node, tween_type=type_name)
448 if ref:
449 self.chg_scene_node(scene_node, ref=ref)
450 pass
451
452 scenes_node.appendChild(scene_node)
453
454 return scene_node
455
456 ## \brief Change attributes of a scene node.
457 #
458 # This is here to monitor changes of scene node.
459 def chg_scene_node(self, scene_node, start=None, end=None,
460 tween_type=None, ref=None):
461 if start is not None:
462 scene_node.setAttribute('start', str(start))
463 pass
464 if end is not None:
465 scene_node.setAttribute('end', str(end))
466 pass
467 if tween_type is not None:
468 scene_node.setAttribute('type', tween_type)
469 pass
470 if ref is not None:
471 scene_node.setAttribute('ref', ref)
472 pass
473 pass
474
475 def rm_scene_node(self, scene_node):
476 self._scenes_node.removeChild(scene_node)
477 pass
478
479 def rm_scene_node_n_group(self, scene_node):
480 scene_group_id = scene_node.getAttribute('ref')
481 scene_group_node = self.get_node(scene_group_id)
482 scene_group_node.parent().removeChild(scene_group_node)
483
484 self._scenes_node.removeChild(scene_node)
485 pass
486
487 ## \brief Create and add a svg:g for a scene under a group for a layer.
488 #
489 def add_scene_group(self, layer_idx):
490 layer = self._layers[layer_idx]
491 doc = self._doc
492
493 scene_group = doc.createElement('svg:g')
494 gid = self.new_id()
495 scene_group.setAttribute("id", gid)
496 scene_group.setAttribute("inkscape:groupmode", "layer")
497
498 layer.group.appendChild(scene_group)
499
500 return scene_group
501
502 def parse_layer(self, layer_idx):
503 layer = self._layers[layer_idx]
504 layer_group = layer.group
505
506 for child in layer_group.childList():
507 if child.name() != 'svg:g':
508 continue
509 try:
510 child_id = child.getAttribute('id')
511 scene_node = self.get_scene(child_id)
512 except:
513 continue
514
515 layer.scenes.append(scene_node)
516 pass
517 pass
518
519 ## \brief Add/insert a layer at given position.
520 #
521 # \param layer_idx is the position in the layer list.
522 #
523 def insert_layer(self, layer_idx, layer_group):
524 layers = self._layers
525
526 layer = Layer(layer_group)
527 if layer_idx >= len(layers):
528 layers.append(layer)
529 else:
530 layers.insert(layer_idx, layer)
531 for idx in range(layer_idx, len(layers)):
532 layers[idx].idx = idx
533 pass
534 pass
535 pass
536
537 ## \brief Remove layer and associated scene nodes and scene groups.
538 #
539 def rm_layer(self, layer_idx):
540 layers = self._layers
541
542 for layer in layers:
543 for scene_node in layer.scenes:
544 scene_group_id = scene_node.getAttribute('ref')
545 scene_group_node = self.get_node(scene_group_id)
546 scene_group_node.parent().removeChild(scene_group_node)
547
548 scene_node.parent().removeChild(scene_node)
549 pass
550 pass
551
552 del layers[layer_idx]
553
554 for idx in range(layer_idx, len(layers)):
555 layers[idx].idx = idx
556 pass
557 pass
558
559 def get_layer_num(self):
560 return len(self._layers)
561
562 def find_layer_n_scene_of_node(self, node_id):
563 for layer_idx, layer in enumerate(self._layers):
564 for scene_node in layer.scenes:
565 scene_group_id = scene_node.getAttribute('ref')
566 if scene_group_id == node_id:
567 return layer_idx, scene_node
568 pass
569 pass
570 return -1, None
571
572 def get_layer_group(self, layer_idx):
573 layer = self._layers[layer_idx]
574 return layer.group
575
576 def get_all_scene_node_of_layer(self, layer_idx):
577 layer = self._layers[layer_idx]
578 return layer.scenes
579
580 def get_layer_data(self, layer_idx):
581 layer = self._layers[layer_idx]
582 try:
583 data = layer.data
584 except:
585 return None
586 return data
587
588 def set_layer_data(self, layer_idx, data):
589 layer = self._layers[layer_idx]
590 layer.data = data
591 pass
592
593 def create_layer_dup_group(self, layer_idx):
594 layer = self._layers[layer_idx]
595
596 dup_group = self._doc.createElement('svg:g')
597 gid = self.new_id()
598 dup_group.setAttribute('id', gid)
599 dup_group.setAttribute('inkscape:label', 'dup')
600 dup_group.setAttribute('sodipodi:insensitive', '1')
601 dup_group.setAttribute('style', '')
602
603 layer.group.appendChild(dup_group)
604
605 return dup_group
606
607 def insert_frames(self, layer_idx, frame_idx, num):
608 layer = self._layers[layer_idx]
609 for scene_node in layer.scenes:
610 start, end, tween_type = self._parse_one_scene(scene_node)
611 if start >= frame_idx:
612 self.chg_scene_node(scene_node, start=(start + num))
613 pass
614 if end >= frame_idx:
615 self.chg_scene_node(scene_node, end=(end + num))
616 pass
617 pass
618 pass
619
620 ## \brief Remove frames
621 #
622 # - Scenes covered by removing range were removed.
623 # - Scenes after removing range were shifted left.
624 #
625 def rm_frames(self, layer_idx, frame_idx, num):
626 layer = self._layers[layer_idx]
627
628 last_rm = frame_idx + num - 1 # last removed frame
629 for scene_node in layer.scenes:
630 start, end, tween_type = \
631 self._dom._parse_one_scene(scene_node)
632
633 if end < frame_idx:
634 continue
635
636 if start > last_rm: # this scene is at right side
637 self.chg_scene_node(scene_node,
638 start=(start - num),
639 end=(end - num))
640 else: # this scene is covered by removing range
641 self.rm_scene_node_n_group(scene_node)
642 pass
643 pass
644 pass
645
646 def get_max_frame(self):
647 return self._maxframe
648 pass
649
650 ## \brief Maintain a stack of frameline UI component.
651 #
652 # Every layer is assocated with a frameline. Framelines are showed/stacked in
653 # virtical. Framelines of lower layers are placed at lower position on the
654 # screen. This class provide a set of API to access framelines with layer and
655 # frame index number. You access/set content of frameline by specifing layer
656 # index and frame index.
657 #
658 class MBScene_frameline_stack(object):
659 _frameline_tween_types = (frameline.TWEEN_TYPE_NONE,
660 frameline.TWEEN_TYPE_SHAPE)
661 _num_frames_of_line = 100
662
663 _framelines = None
664
665 def __init__(self, *args, **kws):
666 super(MBScene_frameline_stack, self).__init__(*args, **kws)
667
668 self._last_mouse_over_frameline = None
669 self._last_active_frameline = None
670 self._active_frame_callback = None
671 pass
672
673 def _change_hover_frameline(self, widget, event):
674 """
675 Hide all hover frames. This is a hack. We should use the lost focus
676 event instead in the future to reduce the overhead.
677 """
678 if self._last_mouse_over_frameline and \
679 widget != self._last_mouse_over_frameline:
680 self._last_mouse_over_frameline.mouse_leave()
681 pass
682 self._last_mouse_over_frameline = widget
683 pass
684
685 ## \brief Switch to new active frameline.
686 #
687 # Hide active frame mark for the active frame of old active frameline. It
688 # always shows at most one active frame mark. When a frame is activated,
689 # all active frame mark of other frameline should be hidden.
690 #
691 def _active_frameline(self, frameline):
692 last = self._last_active_frameline
693
694 if last and last != frameline:
695 last.deactive()
696 pass
697
698 self._last_active_frameline = frameline
699 pass
700
701 ## \brief Called for changing of active frame.
702 #
703 # This handle deactive previous frameline that owns an active frame when a
704 # frame in another frameline is activated.
705 #
706 def _change_active_frame(self, frameline, frame_idx, button):
707 frameline.active_frame(frame_idx)
708 self._active_frameline(frameline)
709
710 if self._active_frame_callback:
711 layer_idx = frameline.layer_idx
712 self._active_frame_callback(layer_idx, frame_idx)
713 pass
714 pass
715
716 ## \brief Add a frameline into the frameline box for the given layer.
717 #
718 def _add_frameline(self, layer_idx):
719 if layer_idx > len(self._framelines):
720 raise ValueError, 'layer number should be a consequence'
721
722 vbox = self._frameline_vbox
723
724 line = frameline(self._num_frames_of_line)
725 line.set_size_request(self._num_frames_of_line * 10, 20)
726
727 hbox = gtk.HBox()
728 label = gtk.Label('')
729 label.set_size_request(100,0)
730 hbox.pack_start(label,expand=False, fill=True)
731 hbox.pack_start(line)
732 vbox.pack_start(hbox, False)
733
734 # Put later one on the top of earier one, but after the ruler.
735 position = len(self._framelines) - layer_idx + 1
736 vbox.reorder_child(hbox, position)
737
738 self._framelines[layer_idx: layer_idx] = [line]
739
740 for idx in range(layer_idx, len(self._framelines)):
741 self._framelines[idx].layer_idx = idx
742 pass
743
744 line.label = label
745 line.connect('motion-notify-event', self._change_hover_frameline)
746 line.connect(frameline.FRAME_BUT_PRESS, self._change_active_frame)
747 pass
748
749 ## \brief Remove the given frameline from the frameline box.
750 #
751 def _remove_frameline(self, layer_idx):
752 vbox = self._frameline_vbox
753 line = self._framelines[layer_idx]
754
755 hbox = line.parent
756 vbox.remove(hbox)
757 del self._framelines[layer_idx]
758
759 for idx in range(layer_idx, len(self._framelines)):
760 self._framelines[idx].layer_idx = idx
761 pass
762 pass
763
764 def _init_framelines(self):
765 self._framelines = []
766
767 box = gtk.ScrolledWindow()
768 self._frameline_box = box
769 box.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
770 box.set_size_request(-1, 150)
771 vbox = gtk.VBox()
772 self._frameline_vbox = vbox
773 box.add_with_viewport(vbox)
774
775 nframes = self._num_frames_of_line
776
777 #
778 # Set up a ruler
779 #
780 ruler = frameruler(nframes)
781 ruler.set_size_request(nframes * 10, 20)
782 ruler.show()
783 hbox = gtk.HBox()
784 label=gtk.Label('')
785 label.set_size_request(100,0)
786 hbox.pack_start(label,expand=False,fill=True)
787 hbox.pack_start(ruler)
788 vbox.pack_start(hbox, False)
789 pass
790
791 ## \brief Show framelines on the screen.
792 #
793 # When a frameline was inserted or removed, it would not be showed
794 # immediately. This function is used to notify toolkit to update the
795 # screen and drawing framelines.
796 def _show_framelines(self):
797 self._frameline_vbox.show_all()
798 pass
799
800 ## \brief Make given frame as current active frame.
801 #
802 def active_frame(self, layer_idx, frame_idx):
803 frameline = self._framelines[layer_idx]
804 self._active_frameline(frameline)
805 frameline.active_frame(frame_idx)
806 pass
807
808 ## \brief Get layer and frame index of current active frame.
809 #
810 # \return (-1, -1) for no active, (layer_idx, frame_idx) for current
811 # active.
812 def get_active_layer_frame(self):
813 if self._last_active_frameline:
814 layer_idx = self._last_active_frameline.layer_idx
815 frame_idx = self._last_active_frameline.get_active_frame()
816 if frame_idx != -1:
817 return layer_idx, frame_idx
818 pass
819 return -1, -1
820
821 ## \brief Get information of the key frame at left-side.
822 #
823 # The key frame, returned, is at the place of the give frame or its
824 # left-side.
825 def get_left_key_tween(self, layer_idx, frame_idx):
826 frameline = self._framelines[layer_idx]
827 start, end, fl_tween_type = frameline.get_frame_block_floor(frame_idx)
828 tween_type = self._frameline_tween_types.index(fl_tween_type)
829 return start, end, tween_type
830
831 ## \brief Return information of a key frame and its tweening.
832 #
833 # This method return the key frame that the given frame is, or is in its
834 # tween.
835 #
836 # \return (start, end, tween_type)
837 def get_key_tween(self, layer_idx, frame_idx):
838 frameline = self._framelines[layer_idx]
839 start, end, fl_tween_type = frameline.get_frame_block(frame_idx)
840
841 tween_type = self._frameline_tween_types.index(fl_tween_type)
842 return start, end, tween_type
843
844 def get_all_key_tween_of_layer(self, layer_idx):
845 frameline = self._framelines[layer_idx]
846 info = frameline.get_frame_blocks()
847 tweens = [(tween[0], tween[1],
848 self._frameline_tween_types.index(tween[2]))
849 for tween in info]
850 return tweens
851
852 ## \brief Tweening key frame to a give size
853 #
854 # The tween can be changed by tweening it again.
855 def tween(self, layer_idx, key_frame_idx, tween_len,
856 tween_type=TweenObject.TWEEN_TYPE_NORMAL):
857 assert tween_len > 0
858 frameline = self._framelines[layer_idx]
859 right_frame_idx = key_frame_idx + tween_len - 1
860 fl_tween_type = self._frameline_tween_types[tween_type]
861
862 start, end, old_fl_tween_type = \
863 frameline.get_frame_block(key_frame_idx)
864 if start != key_frame_idx:
865 ValueError, 'invalid key frame (%d)' % (key_frame_idx)
866 if start < end:
867 frameline.unmark_keyframe(end)
868 pass
869 frameline.mark_keyframe(right_frame_idx)
870 frameline.tween(start, fl_tween_type)
871 pass
872
873 ## \brief Unmark a key frame.
874 #
875 # Once a key frame was unmark, the associated tween was also removed
876 # totally.
877 #
878 def unmark_keyframe(self, layer_idx, frame_idx):
879 frameline = self._framelines[layer_idx]
880 start, end, fl_tween_type = frameline.get_frame_block(frame_idx)
881 if start != frame_idx:
882 raise ValueError, 'no such key (%d, %d)' % (layer_idx, frame_idx)
883
884 frameline.unmark_keyframe(frame_idx)
885 if start < end:
886 frameline.unmark_keyframe(end)
887 pass
888 pass
889
890 ## \brief Makr a key frame.
891 #
892 # Make a frame as a key frame.
893 #
894 def mark_keyframe(self, layer_idx, frame_idx):
895 frameline = self._framelines[layer_idx]
896 frameline.mark_keyframe(frame_idx)
897 pass
898
899 ## \brief Get data associated with the given key frame.
900 #
901 # The given frame index must be exactly a key frame.
902 #
903 def get_keyframe_data(self, layer_idx, frame_idx):
904 frameline = self._framelines[layer_idx]
905 data = frameline.get_frame_data(frame_idx)
906 return data
907
908 ## \brief Set/associate data with the given key frame.
909 #
910 def set_keyframe_data(self, layer_idx, frame_idx, data):
911 frameline = self._framelines[layer_idx]
912 frameline.set_frame_data(frame_idx, data)
913 pass
914
915 ## \brief Insert frames before specified frame.
916 #
917 # Specified frame and frames after it are shift right for \ref num
918 # positions to make a space for new frames.
919 #
920 def insert_frames(self, layer_idx, frame_idx, num):
921 assert num > 0
922 assert frame_idx >= 0
923 frameline = self._framelines[layer_idx]
924 for i in range(num):
925 frameline.add_frame(frame_idx)
926 pass
927 pass
928
929 ## \brief Remove a number of frames from the frameline.
930 #
931 # All key frames and associated tween info covered by removing range would
932 # be removed.
933 #
934 def rm_frames(self, layer_idx, frame_idx, num):
935 assert num > 0
936 assert frame_idx >= 0
937
938 frameline = self._framelines[layer_idx]
939
940 #
941 # Remove right key frame of last tween which left one will be removed.
942 #
943 last_rm = frame_idx + num - 1 # last removed frame
944 try:
945 start, end, tween_type = frameline.get_frame_block(last_rm)
946 except ValueError: # last removed frame is not in any tween
947 pass
948 else:
949 if start >= frame_idx and end > last_rm:
950 # Left key frame of the tween was removed, but not right one.
951 frameline.untween(start)
952 frameline.unmark_keyframe(end)
953 pass
954 pass
955
956 #
957 # Remove left key of the tween that right key frame is in removing
958 # range.
959 #
960 try:
961 start, end, tween_type = frameline.get_frame_block(frame_idx)
962 except ValueError:
963 pass
964 else:
965 if start < frame_idx and end <= last_rm:
966 # right key frame is in removing range but left one.
967 frameline.untween(start)
968 frameline.unmark_keyframe(start)
969 frameline.unmark_keyframe(end)
970 pass
971 pass
972
973 for i in range(num):
974 frameline.rm_frame(frame_idx)
975 pass
976 pass
977
978 ## \brief Set label for a layer.
979 #
980 def set_layer_label(self, layer_idx, txt):
981 frameline = self._framelines[layer_idx]
982 frameline.label.set_text(txt)
983 pass
984
985 ## \brief Register a callback for active frame event.
986 #
987 # The callback would be called when a frame is activated.
988 #
989 def register_active_frame_callback(self, cb):
990 self._active_frame_callback = cb
991 pass
992 pass
993
994 ## \brief Bridge of DOM-tree to syncrhonize data-model and UI.
995 #
996 # This class is a wrapper
997 class MBScene_domview_ui(object):
998 _tween_type_names = ('normal', 'scale')
999
1000 def __init__(self):
1001 super(MBScene_domview_ui, self).__init__()
1002 self._fl_stack = MBScene_frameline_stack()
1003 self._dom = MBScene_domview()
1004 pass
1005
1006 ## \brief Update content of a frameline from scenes of respective layer.
1007 #
1008 def _update_frameline_content(self, layer_idx):
1009 fl_stack = self._fl_stack
1010 scene_nodes = self._dom.get_all_scene_node_of_layer(layer_idx)
1011 for scene_node in scene_nodes:
1012 start, end, tween_name = self._dom._parse_one_scene(scene_node)
1013
1014 fl_stack.mark_keyframe(layer_idx, start)
1015 fl_stack.set_keyframe_data(layer_idx, start, scene_node)
1016 if start != end:
1017 tween_type = self._tween_type_names.index(tween_name)
1018 tween_len = end - start + 1
1019 fl_stack.tween(layer_idx, start, tween_len, tween_type)
1020 pass
1021 pass
1022 pass
1023
1024 ## \brief Add a frameline for every found layer.
1025 #
1026 # This method is called to create a frameline for every layer found when
1027 # loading a document.
1028 #
1029 def _add_frameline_for_every_layer(self):
1030 for layer_idx in range(self._dom.get_layer_num()):
1031 layer_group_node = self._dom.get_layer_group(layer_idx)
1032 label = layer_group_node.getAttribute('inkscape:label')
1033
1034 self._fl_stack._add_frameline(layer_idx)
1035 self._fl_stack.set_layer_label(layer_idx, label)
1036
1037 self._update_frameline_content(layer_idx)
1038 pass
1039 pass
1040
1041 ## \brief This method is called to handle a new document.
1042 #
1043 def handle_doc_root(self, doc, root):
1044 self._dom.handle_doc_root(doc, root)
1045 self._fl_stack._init_framelines()
1046 self._add_frameline_for_every_layer()
1047 self._fl_stack._show_framelines()
1048 pass
1049
1050 ## \brief Mark given frame as a key frame.
1051 #
1052 def mark_key(self, layer_idx, key_idx):
1053 scene_group = self._dom.add_scene_group(layer_idx)
1054 scene_group_id = scene_group.getAttribute('id')
1055
1056 scene_node = self._dom.add_scene_node(key_idx, key_idx)
1057 self._dom.chg_scene_node(scene_node, ref=scene_group_id)
1058
1059 self._fl_stack.mark_keyframe(layer_idx, key_idx)
1060 self._fl_stack.set_keyframe_data(layer_idx, key_idx, scene_node)
1061 pass
1062
1063 ## \brief Tweening a key frame.
1064 #
1065 # To tween a key spanning several frames at right-side.
1066 # The tween of a key frame can be changed by tweening it again.
1067 #
1068 def tween(self, layer_idx, key_frame_idx, tween_len,
1069 tween_type=TweenObject.TWEEN_TYPE_NORMAL):
1070 self._fl_stack.tween(layer_idx, key_frame_idx, tween_len, tween_type)
1071
1072 end_frame_idx = key_frame_idx + tween_len - 1
1073 scene_node = self._fl_stack.get_keyframe_data(layer_idx, key_frame_idx)
1074 tween_name = self._tween_type_names[tween_type]
1075 self._dom.chg_scene_node(scene_node, end=end_frame_idx,
1076 tween_type=tween_name)
1077 pass
1078
1079 ## \brief Change tween info of a key frame
1080 #
1081 def chg_tween(self, layer_idx, key_frame_idx,
1082 tween_len=None, tween_type=None):
1083 scene_node = self._fl_stack.get_keyframe_data(layer_idx, key_frame_idx)
1084 start, end, old_tween_type = \
1085 self._fl_stack.get_key_tween(layer_idx, key_frame_idx)
1086
1087 if tween_len is not None:
1088 end = start + tween_len - 1
1089 self._dom.chg_scene_node(scene_node, end=end)
1090 pass
1091 if tween_type is not None:
1092 tween_name = self._tween_type_names[tween_type]
1093 self._dom.chg_scene_node(scene_node, tween_type=tween_name)
1094 pass
1095
1096 if tween_type is None:
1097 tween_type = old_tween_type
1098 pass
1099
1100 tween_len = end - start + 1
1101 self._fl_stack.tween(layer_idx, start, tween_len, tween_type)
1102 pass
1103
1104 ## \brief Unmark a frame from a key frame.
1105 #
1106 def unmark_key(self, layer_idx, key_frame_idx):
1107 scene_node = self._fl_stack.get_keyframe_data(layer_idx, key_frame_idx)
1108 self._dom.rm_scene_node_n_group(scene_node)
1109
1110 self._fl_stack.unmark_keyframe(layer_idx, key_frame_idx)
1111 pass
1112
1113 ## \brief Insert frames at specified position.
1114 #
1115 # All frame at and after given position will shift right.
1116 #
1117 def insert_frames(self, layer_idx, frame_idx, num):
1118 self._fl_stack.insert_frames(layer_idx, frame_idx, num)
1119 self._dom.insert_frames(layer_idx, frame_idx, num)
1120 pass
1121
1122 ## \brief Insert frames at specified position.
1123 #
1124 # All frame at and after given position will shift left, except nearest
1125 # \ref num frames are removed.
1126 #
1127 def rm_frames(self, layer_idx, frame_idx, num):
1128 self._fl_stack.insert_frames(layer_idx, frame_idx, num)
1129 self._dom.rm_frames(layer_idx, frame_idx, num)
1130 pass
1131
1132 ## \brief Insert a layer at given position.
1133 #
1134 # Original layer \ref layer_idx and later ones would be shifted to make a
1135 # space for the new layer.
1136 #
1137 def insert_layer(self, layer_idx):
1138 self._dom.insert_layer(layer_idx)
1139 self._fl_stack._add_frameline(layer_idx)
1140 self._fl_stack._show_framelines()
1141 pass
1142
1143 ## \brief Remove given layer.
1144 #
1145 def rm_layer(self, layer_idx):
1146 self._dom.rm_layer(layer_idx)
1147 self._fl_stack._remove_frameline(layer_idx)
1148 self._fl_stack._show_framelines()
1149 pass
1150
1151 def set_active_layer_frame(self, layer_idx, frame_idx):
1152 self._fl_stack.active_frame(layer_idx, frame_idx)
1153 pass
1154
1155 ## \bref Return current active frame and its layer.
1156 #
1157 # \return (layer_idx, frame_idx) of active frame, or (-1, -1) when no
1158 # active one.
1159 def get_active_layer_frame(self):
1160 layer_idx, frame_idx = self._fl_stack.get_active_layer_frame()
1161 return layer_idx, frame_idx
1162
1163 def get_layer_num(self):
1164 return self._dom.get_layer_num()
1165
1166 ## \brief Return associated group node for a key frame.
1167 #
1168 # The given frame index must be exactly a key frame.
1169 #
1170 def get_key_group(self, layer_idx, frame_idx):
1171 scene_node = self._fl_stack.get_keyframe_data(layer_idx, frame_idx)
1172 scene_group_id = scene_node.getAttribute('ref')
1173 scene_group_node = self._dom.get_node(scene_group_id)
1174 return scene_group_node
1175
1176 ## \brief Find an associated key frame and tween info for a group ID.
1177 #
1178 def find_key_from_group(self, scene_group_id):
1179 layer_idx, scene_node = \
1180 self._dom.find_layer_n_scene_of_node(scene_group_id)
1181 start, end, tween_name = self._dom._parse_one_scene(scene_node)
1182 tween_type = self._tween_type_names.index(tween_name)
1183 return layer_idx, (start, end, tween_type)
1184
1185 ## \brief Return key and tween info for given frame index.
1186 #
1187 # The return key is at given frame, or its tween covers given frame.
1188 #
1189 def get_key(self, layer_idx, frame_idx):
1190 start, end, tween_type = \
1191 self._fl_stack.get_key_tween(layer_idx, frame_idx)
1192 return start, end, tween_type
1193
1194 def get_left_key(self, layer_idx, frame_idx):
1195 start, end, tween_type = \
1196 self._fl_stack.get_left_key_tween(layer_idx, frame_idx)
1197 return start, end, tween_type
1198
1199 ## \brief Return information of key frames in the given layer.
1200 #
1201 def get_layer_keys(self, layer_idx):
1202 key_tweens = self._fl_stack.get_all_key_tween_of_layer(layer_idx)
1203 return key_tweens
1204
1205 ## \brief Return widget showing frames and layers.
1206 #
1207 def get_frame_ui_widget(self):
1208 return self._fl_stack._frameline_box
1209
1210 ## \brief Register a callback for activating a frame event.
1211 #
1212 # The callback function is called when a frame is activated.
1213 #
1214 def register_active_frame_callback(self, cb):
1215 self._fl_stack.register_active_frame_callback(cb)
1216 pass
1217
1218 ## \brief Get duplicate group of a layer.
1219 #
1220 def get_layer_dup_group(self, layer_idx):
1221 data = self._dom.get_layer_data(layer_idx)
1222 if not data:
1223 data = dict()
1224 self._dom.set_layer_data(layer_idx, data)
1225 pass
1226
1227 dup_group = None
1228 if data.has_key('dup_group_id'):
1229 try:
1230 dup_group = self._dom.get_node(data['dup_group_id'])
1231 except KeyError:
1232 pass
1233 pass
1234
1235 if not dup_group:
1236 # Search dup group from children of the layer group
1237 layer_group = self._dom.get_layer_group(layer_idx)
1238 for child in layer_group.childList():
1239 try:
1240 label = child.getAttribute('inkscape:label')
1241 except:
1242 pass
1243 else:
1244 if label == 'dup':
1245 data['dup_group_id'] = child
1246 return child
1247 pass
1248 pass
1249
1250 # Or create a new dup group for the layer
1251 dup_group = self._dom.create_layer_dup_group(layer_idx)
1252 data['dup_group_id'] = dup_group.getAttribute('id')
1253 pass
1254
1255 return dup_group
1256
1257 def get_max_frame(self):
1258 max_frame = self._dom.get_max_frame()
1259 return max_frame
1260 pass
1261 55
1262 ## \brief MBScene connect GUI and DOM-tree 56 ## \brief MBScene connect GUI and DOM-tree
1263 # 57 #
1264 # This method accepts user actions and involves MBScene_domview_ui to update 58 # This method accepts user actions and involves MBScene_domview_ui to update
1265 # data on the document. 59 # data on the document.