Mercurial > MadButterfly
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. |