changeset 1150:6586cd10c92f

Refactory frameline.py
author Thinker K.F. Li <thinker@codemud.net>
date Sun, 26 Dec 2010 19:17:12 +0800
parents 0ffef2df6201
children 71c72e8d6755
files pyink/MBScene.py pyink/frameline.py pyink/pyink.py pyink/tween.py
diffstat 4 files changed, 407 insertions(+), 372 deletions(-) [+]
line wrap: on
line diff
--- a/pyink/MBScene.py	Fri Dec 24 15:40:16 2010 +0800
+++ b/pyink/MBScene.py	Sun Dec 26 19:17:12 2010 +0800
@@ -1,6 +1,6 @@
 #!/usr/bin/python
-# -*- indent-tabs-mode: t; tab-width: 8; python-indent: 4; -*-
-# vim: sw=4:ts=8:sts=4
+# -*- indent-tabs-mode: t; tab-width: 8; python-indent: 4; fill-column: 79 -*-
+# vim: sw=4:ts=8:sts=4:textwidth=79
 import pygtk
 import gtk
 import glib
@@ -12,6 +12,7 @@
 import pybInkscape
 import math
 from tween import TweenObject
+from frameline import frameline, frameruler
 
 # Please refer to
 # http://www.assembla.com/wiki/show/MadButterfly/Inkscape_extention
@@ -141,6 +142,11 @@
 	pass
 
 class MBScene():
+    _tween_types = (frameline.TWEEN_TYPE_NONE,
+		    frameline.TWEEN_TYPE_MOVE,
+		    frameline.TWEEN_TYPE_SHAPE)
+    _tween_type_names = ('normal', 'relocate', 'scale')
+    
     def __init__(self, desktop, win, root=None):
 	self.desktop = desktop
 	self.window = win
@@ -157,6 +163,7 @@
 	self.root = root
 	self.framerate=12
 	self.maxframe=0
+	self._disable_tween_type_selector = False
 	pass
 
     def show_selection(self,w,obj):
@@ -267,7 +274,7 @@
 			node.appendChild(ns)
 			for layer in range(0,len(self._framelines)):
 			    lobj = self._framelines[layer]
-			    lobj.addScenes(rdoc,ns)
+			    self.addScenes(lobj, ns)
 			    pass
 			pass
 		    pass
@@ -285,9 +292,6 @@
 	self.scenemap = None
 	doc = self.root
 
-        #obs = pybInkscape.PYNodeObserver()
-        #obs = LayerAddRemoveWatcher(self)
-        #doc.addObserver(obs)
 	addEventListener(doc,'DOMNodeInserted',self.updateUI,None)
 	addEventListener(doc,'DOMNodeRemoved',self.updateUI,None)
 	doc.childList()
@@ -413,8 +417,7 @@
 	print 'Add key ', x
 	self.last_line.add_keyframe(x,ns)
 	self.update()
-	self.last_line.update()
-    
+	pass
 
     def removeKeyScene(self):
 	nth = self.last_frame
@@ -552,8 +555,7 @@
 
 	    # Check the duplicated scene group and create it if it is not available
 	    try:
-	        if layer.duplicateGroup:
-	            layer.duplicateGroup.setAttribute("style","display:none")
+		layer.duplicateGroup.setAttribute("style","display:none")
 	    except:
 	        print "*"*40
 	        layer.duplicateGroup = self.document.createElement("svg:g")
@@ -574,23 +576,22 @@
 		        s.ref.setAttribute("style","display:none")
 		    i = i + 1
 		    continue
+		
 		if nth == s.idx + 1:
 		    s.ref.setAttribute("style","")
 		else:
 		    if nth > (s.idx+1) and nth <= (layer._keys[i+1].idx+1):
 			if i+2 < len(layer._keys):
-			    #s.ref.parent().appendChild(layer.duplicateGroup)
 			    s.ref.setAttribute("style","display:none")
 	                    layer.duplicateGroup.setAttribute("style","")
-			    self.tween.updateTweenContent(layer.duplicateGroup, layer.get_tween_type(s.idx),s, layer._keys[i+2], nth)
+			    d = layer._keys[i + 2]
+			    self.tween.\
+				updateTweenContent(layer.duplicateGroup,
+						   layer.get_tween_type(s.idx),
+						   s, d, nth)
 			else:
 	                    layer.duplicateGroup.setAttribute("style","")
-			    #layer.duplicateGroup = s.ref.duplicate(self.document)
-			    #layer.duplicateGroup.setAttribute("style","")
-			    #layer.duplicateGroup.setAttribute("inkscape:label","dup")
-			    #layer.duplicateGroup.setAttribute("sodipodi:insensitive","1")
 			    s.ref.setAttribute("style","display:none")
-			    #s.ref.parent().appendChild(layer.duplicateGroup)
 			    pass
 		    else:
 		        s.ref.setAttribute("style","display:none")
@@ -599,21 +600,6 @@
 	    pass
 	pass
 
-    def DOMtoItem(self,obj):
-	"""
-	Find the corresponding PYSPObject object for a DOM object.
-	"""
-	return self.DOMtoItem_recursive(self.desktop.doc().root(),obj)
-
-    def DOMtoItem_recursive(self,tree,obj):
-	nodes = tree.childList()
-	for s in nodes:
-	    if s.getId() == obj.getAttribute('id'):
-	        return s
-	    d = self.DOMtoItem_recursive(s,obj)
-	    if d != None: return d
-	     
-
     def enterGroup(self,obj):
         for l in self.layers:
 	    for s in l.node.childList():
@@ -648,14 +634,11 @@
 	    pass
         pass
 
-    def setTweenType(self,typ):
-        if typ == 'normal':
-	    self.tweenTypeSelector.set_active(0)
-        elif typ == 'relocate':
-	    self.tweenTypeSelector.set_active(1)
-        elif typ == 'scale':
-	    self.tweenTypeSelector.set_active(2)
-	    pass
+    def setTweenType(self, tween_type):
+	sel_type = MBScene._tween_types.index(tween_type)
+	self._disable_tween_type_selector = True
+	self.tweenTypeSelector.set_active(sel_type)
+	self._disable_tween_type_selector = False
 	pass
 	
     def newCell(self,file):
@@ -689,7 +672,6 @@
 	pass
 	    
     def _create_framelines(self):
-	import frameline
 	self.scrollwin = gtk.ScrolledWindow()
 	self.scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 	self.scrollwin.set_size_request(-1,150)
@@ -700,7 +682,7 @@
 	vbox.show()
 	self.scrollwin.add_with_viewport(vbox)
 	
-	ruler = frameline.frameruler(nframes)
+	ruler = frameruler(nframes)
 	ruler.set_size_request(nframes * 10, 20)
 	ruler.show()
 	hbox = gtk.HBox()
@@ -715,7 +697,7 @@
 	#
 	self._framelines = []
 	for i in range(len(self.layers)-1,-1,-1):
-	    line = frameline.frameline(nframes)
+	    line = frameline(nframes)
 	    hbox = gtk.HBox()
 	    label = gtk.Label(self.layers[i].node.getAttribute("inkscape:label"))
 	    label.set_size_request(100,0)
@@ -746,7 +728,9 @@
 		frameline.add_keyframe(scene.start-1,scene.node)
 		if scene.start != scene.end:
 		    frameline.add_keyframe(scene.end-1,scene.node)
-		    frameline.tween(scene.start-1,scene.type)
+		    tween_type_idx = self._tween_type_names.index(scene.type)
+		    tween_type = self._tween_types[tween_type_idx]
+		    frameline.tween(scene.start-1, tween_type)
 		pass
 	    pass
 	pass
@@ -895,39 +879,25 @@
 	hbox.pack_start(btn,expand=False,fill=False)
 	self.addNameEditor(hbox)
 	self.addTweenTypeSelector(hbox)
-
 	pass
 
-    def onTweenTypeChange(self,w):
-	n = self.tweenTypeSelector.get_active()
+    def onTweenTypeChange(self, w):
+	if self._disable_tween_type_selector:
+	    return
+	
 	if self.last_line == None:
 	    return
 	frameline = self.last_line
         i = 0
 	found = -1
-        while i < len(frameline._keys):
-	    s = frameline._keys[i]
-	    if s.right_tween is False:
-	        if self.last_frame == s.idx:
-		    found = s.idx
-		    break
-		else:
-		    pass
-		i = i + 1
-		continue
-
-	    if self.last_frame >= s.idx and self.last_frame <= frameline._keys[i+1].idx:
-		found = s.idx
+	for start_idx, stop_idx, tween_type in frameline.get_frame_blocks():
+	    if start_idx < stop_idx:
+		n = self.tweenTypeSelector.get_active()
+		new_tween_type = MBScene._tween_types[n]
+		self.last_line.set_tween_type(start_idx, new_tween_type)
+		self.update()
 		break
-	    else:
-	        pass
-	    i = i + 2
 	    pass
-        pass
-	if found == -1: return
-	self.last_line.set_tween_type(found,self.tweenTypeSelector.get_active_text())
-	self.last_line.update()
-	self.update()
 	pass
 
     def addTweenTypeSelector(self,hbox):
@@ -955,6 +925,24 @@
 	gtk.main_quit()
 	pass
 
+    def addScenes(self, frameline, scenes_node):
+	doc = self.document
+	for start_idx, stop_idx, tween_type in frameline.get_frame_blocks():
+	    ref = frameline.get_frame_data(start_idx)
+	    tween_type_idx = self._tween_types.index(tween_type)
+	    tween_type_name = self._tween_type_names[tween_type_idx]
+	    
+	    scene_node = doc.createElement("ns0:scene")
+	    scenes_node.appendChild(scene_node)
+	    scene_node.setAttribute("start", str(start_idx + 1))
+	    if start_idx != stop_idx:
+		scene_node.setAttribute("end", str(stop_idx + 1))
+		pass
+	    scene_node.setAttribute("ref", ref.attribute("id"))
+	    scene_node.setAttribute("type", tween_type_name)
+	    pass
+	pass
+
     def updateUI(self,node=None,arg=None):
         if self.lockui: return
         self.lockui = True
@@ -984,6 +972,8 @@
 	    self.desktop.getToplevel().child.child.pack_end(self.top,expand=False)
 	else:
 	    self.top.remove(self.startWindow)
+	    pass
+	
 	vbox = gtk.VBox(False,0)
 	self.startWindow = vbox
 	self.top.pack_start(vbox,expand=False)
@@ -992,10 +982,6 @@
 	self.addButtons(hbox)
 	vbox.pack_start(hbox,expand=False)
 
-	# self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-	# self.window.connect("destroy", gtk.main_quit)
-	# self.window.set_position(gtk.WIN_POS_MOUSE)
-
 	self.top.show_all()
 	self.last_update = None
 	return False
--- a/pyink/frameline.py	Fri Dec 24 15:40:16 2010 +0800
+++ b/pyink/frameline.py	Sun Dec 26 19:17:12 2010 +0800
@@ -1,3 +1,5 @@
+# -*- indent-tabs-mode: t; tab-width: 8; python-indent: 4; fill-column: 79 -*-
+# vim: sw=4:ts=8:sts=4:textwidth=79
 import pygtk
 pygtk.require("2.0")
 import gtk
@@ -16,7 +18,7 @@
         self.left_tween = False
         self.right_tween = False
         self.right_tween_type = 0
-	self.ref=''
+	self.ref = ''
         pass
     pass
 
@@ -109,6 +111,9 @@
 # - 'frame-button-pree' for user press on a frame.
 #   - callback(widget, frame_idx, button)
 #
+# All methos that change state of the frameline, must call methods to update
+# the screen.
+#
 class frameline(gtk.DrawingArea):
     _type = 0
     _frame_width = 10           # Width for each frame is 10 pixels
@@ -116,17 +121,18 @@
     _key_mark_color = 0x000000  # color of marks for key frames.
     _key_mark_sz = 4            # width and height of a key frame mark
     _tween_color = 0x808080     # color of tween line
-    _tween_bgcolors = [0x80ff80, 0xff8080,0xffff80] # bg colors of tween frames
+    # bg colors of tween frames
+    _tween_bgcolors = [0x80ff80, 0xff8080, 0xffff80]
     # Colors for normal frames
     _normal_bgcolors = [0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xcccccc]
     _normal_border = 0xaaaaaa   # border color of normal frames.
     _active_border = 0xff3030   # border color of an active frame
     _hover_border_color = 0xa0a0a0 # border when the pointer over a frame
+    
     # tween types
-    _tween_type_none=0
-    _tween_type_move=1
-    _tween_type_shape=2
-    
+    TWEEN_TYPE_NONE = 0
+    TWEEN_TYPE_MOVE = 1
+    TWEEN_TYPE_SHAPE = 2    
 
     FRAME_BUT_PRESS = 'frame-button-press'
     
@@ -155,20 +161,100 @@
         self._drawing = False
         pass
 
+    def __len__(self):
+        return self._num_frames
+    
+    def _find_keyframe(self, idx):
+	key_indic = [key.idx for key in self._keys]
+	key_pos = key_indic.index(idx)
+	return key_pos
+
+    def _find_keyframe_floor(self, frame_idx):
+	pos = 0
+        keys = [key.idx for key in self._keys]
+	keys.append(frame_idx)
+	keys.sort()
+	keys.reverse()
+	pos = (len(keys) - 1) - keys.index(frame_idx) - 1
+	return pos
+
+    ## \brief Find the range a continous tween.
+    #
+    def _find_tween_range(self, key_pos):
+	key = self._keys[key_pos]
+	if not (key.left_tween or key.right_tween):
+	    raise ValueError, 'the keyframe is not in a tween'
+	
+	#
+	# Initialize tween type and first_pos
+	#
+	if key.right_tween:
+	    tween_type = key.right_tween_type
+	    first_pos = key_pos
+	else:
+	    # key.left_tween is True since the key is in a tween.
+	    first_pos = key_pos -1
+	    key = self._keys[first_pos]
+	    tween_type = key.right_tween_type
+	    pass
+	
+	#
+	# Find first_pos
+	#
+        while first_pos and key.left_tween:
+	    right_pos = first_pos - 1
+	    right_key = self._keys[right_pos]
+	    if right_key.right_tween_type != tween_type:
+		break
+            first_pos = right_pos
+	    key = right_key
+            pass
+        
+	#
+	# Find last_pos
+	#
+        max_pos = len(self._keys) - 1
+        last_pos = key_pos
+	key = self._keys[last_pos]
+        while last_pos < max_pos and self._keys[last_pos].right_tween:
+	    if key.right_tween_type != tween_type:
+		break
+            last_pos = last_pos + 1
+	    key = self._keys[last_pos]
+            pass
+        
+        return first_pos, last_pos
+
     def _press_hdl(self, widget, event):
         frame = event.x / self._frame_width
         but = event.button
         self.emit(frameline.FRAME_BUT_PRESS, frame, but)
         pass
+    
     def hide_hover(self):
         if self._active_frame != self._last_hover:
             self._draw_normal_frame(self._last_hover)
+	    pass
+	pass
     
     def _motion_hdl(self, widget, event):
-        frame = int(event.x / self._frame_width)
-	    
-        if frame < self._num_frames and frame >= 0:
-            self._draw_hover(frame)
+        frame_idx = int(event.x / self._frame_width)
+        if self._last_hover != -1:
+            self._draw_frame(self._last_hover)
+	    if self._last_hover == self._active_frame:
+		self._draw_active_frame()
+		pass
+            pass
+	
+        if frame_idx < self._num_frames and frame_idx >= 0:
+            self._draw_hover_frame(frame_idx)
+	    if self._last_hover == self._active_frame:
+		self._draw_active_frame()
+		pass
+	    self._last_hover = frame_idx
+	else:
+	    self._last_hover = -1
+	    pass
         pass
 
     def _fl_expose(self, widget, event):
@@ -188,12 +274,11 @@
         self.update()
         pass
 
-    def _draw_tween(self, first_key, last_key):
-        if not self._drawing:
-            return
-        
+    def _draw_tween(self, first_pos, last_pos):
         win = self.window
         w_x, w_y, w_w, w_h, depth = win.get_geometry()
+	first_key = self._keys[first_pos]
+	last_key = self._keys[last_pos]
         
         #
         # Get background color of a tween
@@ -229,9 +314,6 @@
         pass
 
     def _draw_normal_frame(self, idx):
-        if not self._drawing:
-            return
-        
         win = self.window
         w_x, w_y, w_w, w_h, depth = win.get_geometry()
         
@@ -255,10 +337,7 @@
 
     ## \brief Draw a bottom line from start to the point before stop frame.
     #
-    def _draw_bottom_line(self, start, stop):
-        if not self._drawing:
-            return
-        
+    def _draw_bottom_line(self, start_idx, stop_idx):
         win = self.window
         w_x, w_y, w_w, w_h, depth = win.get_geometry()
         gc = self._gc
@@ -266,66 +345,41 @@
         border_rgb = color_to_rgb(self._normal_border)
         border_color = gtk.gdk.Color(*border_rgb)
         gc.set_rgb_fg_color(border_color)
-        start_x = start * self._frame_width
-        stop_x = stop * self._frame_width
+        start_x = start_idx * self._frame_width
+        stop_x = stop_idx * self._frame_width
         win.draw_line(gc, start_x, w_h - 1, stop_x, w_h - 1)
         pass
     
-    def _draw_all_frames(self):
-        if not self._drawing:
-            return
-        
+    def _draw_active(self, idx):
         win = self.window
         w_x, w_y, w_w, w_h, depth = win.get_geometry()
+
+        color_v = self._active_border
+        color_rgb = color_to_rgb(color_v)
+        color = gtk.gdk.Color(*color_rgb)
+
         gc = self._gc
+        gc.set_rgb_fg_color(color)
         
-        i = 0
-        key_i = 0
-        try:
-            key = self._keys[key_i]
-        except IndexError:
-            key = keyframe(self._num_frames)
-            pass
-        num_frames = self._num_frames
-        while i < num_frames:
-            if key.idx == i and key.right_tween:
-                #
-                # Skip tween keys
-                #
-                first_tween_key = key
-                while key.idx == i or key.left_tween:
-                    last_tween_key = key
-                    key_i = key_i + 1
-                    try:
-                        key = self._keys[key_i]
-                    except IndexError:
-                        key = keyframe(self._num_frames)
-                        pass
-                    pass
+        line_x1 = idx * self._frame_width
+        line_x2 = line_x1 + self._frame_width
 
-                if first_tween_key != last_tween_key:
-                    self._draw_tween(first_tween_key, last_tween_key)
-                
-                i = last_tween_key.idx + 1
-                pass
-            else:
-	        if key.idx == i:
-		    key_i=key_i+1
-		    try:
-		        key = self._keys[key_i]
-		    except:
-		        key = keyframe(self._num_frames)
-                self._draw_normal_frame(i)
-                i = i + 1
-                pass
-            pass
-
-        self._draw_bottom_line(0, num_frames)
-        pass
+        win.draw_line(gc, line_x1, 0, line_x1, w_h)
+        win.draw_line(gc, line_x2, 0, line_x2, w_h)
+        win.draw_line(gc, line_x1, w_h - 1, line_x2, w_h - 1)
+        win.draw_line(gc, line_x1, 0, line_x2, 0)
+	pass
 
     def _draw_keyframe(self, frame_idx):
-        if not self._drawing:
-            return
+	# Only keyframes that is not right-side of NONE type tween should be
+	# draw.
+	pos = self._find_keyframe(frame_idx)
+	key = self._keys[pos]
+	if key.left_tween and not key.right_tween:
+	    left_key = self._keys[pos - 1]
+	    if left_key.right_tween_type == 0:
+		return
+	    pass
         
         win = self.window
         w_x, w_y, w_w, w_h, depth = win.get_geometry()
@@ -345,157 +399,14 @@
         pass
 
     def _draw_keyframes(self):
-        if not self._drawing:
-            return
-        
-        win = self.window
-        w_x, w_y, w_w, w_h, depth = win.get_geometry()
-        
-        color_v = self._key_mark_color
-        color_rgb = color_to_rgb(color_v)
-        color = gtk.gdk.Color(*color_rgb)
-        
-        gc = self._gc
-        gc.set_rgb_fg_color(color)
-        
         for key in self._keys:
-	    if key.left_tween is True and lastkey.right_tween_type == frameline._tween_type_none:
-	        continue
-	        
-            mark_sz = self._key_mark_sz
-            mark_x = int((key.idx + 0.5) * self._frame_width - mark_sz / 2)
-            mark_y = w_h * 2 / 3 - mark_sz / 2
-
-            win.draw_rectangle(gc, True, mark_x, mark_y, mark_sz, mark_sz)
-	    lastkey = key
+	    self._draw_keyframe(key.idx)
             pass
         pass
 
-    def _draw_active(self):
-        if not self._drawing:
-            return
-        
-        if self._active_frame == -1:
-            return
-        
-        win = self.window
-        w_x, w_y, w_w, w_h, depth = win.get_geometry()
-
-        color_v = self._active_border
-        color_rgb = color_to_rgb(color_v)
-        color = gtk.gdk.Color(*color_rgb)
-
-        gc = self._gc
-        gc.set_rgb_fg_color(color)
-        
-        idx = self._active_frame
-        line_x1 = idx * self._frame_width
-        line_x2 = line_x1 + self._frame_width
-
-        win.draw_line(gc, line_x1, 0, line_x1, w_h)
-        win.draw_line(gc, line_x2, 0, line_x2, w_h)
-        win.draw_line(gc, line_x1, w_h - 1, line_x2, w_h - 1)
-        win.draw_line(gc, line_x1, 0, line_x2, 0)
-        pass
-
-    ## \brief Find the range a continous tween.
-    #
-    def _find_tween_range(self, key_pos):
-        first_pos = key_pos
-        while first_pos and self._keys[first_pos].left_tween:
-            first_pos = first_pos - 1
-            pass
-        
-        max_pos = len(self._keys) - 1
-        
-        last_pos = key_pos
-        while last_pos < max_pos and self._keys[last_pos].right_tween:
-            last_pos = last_pos + 1
-            pass
-        
-        return first_pos, last_pos
-
-    ## \brief Redraw a frame specified by an index.
-    #
-    def _redraw_frame(self, frame_idx):
-        if not self._drawing:
-            return
-        
-        keys = [key.idx for key in self._keys]
-        if len(keys):
-            try:
-                pos = keys.index(frame_idx)
-            except ValueError:
-                keys.append(frame_idx)
-                keys.sort()
-                pos = keys.index(frame_idx) - 1
-                pass
-            if pos < 0:
-                pos = 0
-                pass
-            key = self._keys[pos]
-        else:
-            key = None
-            pass
-        
-        if key and (key.right_tween or \
-                (key.left_tween and key.idx == frame_idx)):
-            #
-            # in tween
-            #
-            first_pos, last_pos = self._find_tween_range(pos)
-            first_key = self._keys[first_pos]
-            last_key = self._keys[last_pos]
-            
-            self._draw_tween(first_key, last_key)
-            self._draw_bottom_line(first_key.idx, last_key.idx + 1)
-
-            for i in range(first_pos, last_pos + 1):
-                key = self._keys[i]
-		if key.left_tween is False or lastkey.right_tween_type != frameline._tween_type_none:
-                    self._draw_keyframe(key.idx)
-		lastkey = key
-                pass
-            pass
-        else:                   # not in tween
-            self._draw_normal_frame(frame_idx)
-            self._draw_bottom_line(frame_idx, frame_idx + 1)
-            if key and (key.idx == frame_idx):
-                self._draw_keyframe(frame_idx)
-                pass
-            pass
-        pass
-    def set_tween_type(self,frame_idx,typ):
-        found=False
-	for i in range(0,len(self._keys)):
-	    if self._keys[i].idx == frame_idx:
-	        idx = i
-		found = True
-		break
-	if not found: return
-        key = self._keys[idx]
-	if typ == 'normal':
-	    type = self._tween_type_none
-	elif typ == 'relocate':
-	    type = self._tween_type_move
-	elif typ == 'scale':
-	    type = self._tween_type_shape
-	if key.left_tween is False and key.right_tween is True:
-	    key.right_tween_type = type
-    	
-
     ## \brief Show a mark for the pointer for a frame.
     #
     def _draw_hover(self, frame_idx):
-        if not self._drawing:
-            return
-        
-        if self._last_hover != -1:
-            self._redraw_frame(self._last_hover)
-            pass
-
-        self._draw_active()
-        
         win = self.window
         w_x, w_y, w_w, w_h, depth = win.get_geometry()
         gc = self._gc
@@ -511,10 +422,125 @@
         win.draw_line(gc, line_x2, 1, line_x2, w_h - 2)
         win.draw_line(gc, line_x1, 1, line_x2, 1)
         win.draw_line(gc, line_x1, w_h - 2, line_x2, w_h - 2)
+        pass
+    
+    ## \brief Redraw a frame specified by an index.
+    #
+    def _draw_frame(self, frame_idx):
+        if not self._drawing:
+            return
 
-        self._last_hover = frame_idx
+	pos = self._find_keyframe_floor(frame_idx)
+	try:
+	    key = self._keys[pos]
+	except IndexError:
+	    key = None
+	    pass
+	
+	if key and (key.right_tween or
+		    (key.left_tween and key.idx == frame_idx)):
+	    #
+            # in tween
+            #
+            first_pos, last_pos = self._find_tween_range(pos)
+            first_key = self._keys[first_pos]
+            last_key = self._keys[last_pos]
+            
+            self._draw_tween_of_key(first_pos)
+        else:                   # not in tween
+            self._draw_normal_frame(frame_idx)
+            self._draw_bottom_line(frame_idx, frame_idx + 1)
+            if key and (key.idx == frame_idx):
+                self._draw_keyframe(frame_idx)
+                pass
+            pass
         pass
     
+    def _draw_all_frames(self):
+        if not self._drawing:
+            return
+        
+        i = 0
+        key_pos = 0
+        try:
+            key = self._keys[key_pos]
+        except IndexError:
+            key = keyframe(self._num_frames)
+            pass
+        num_frames = self._num_frames
+        while i < num_frames:
+            if key.idx == i and key.right_tween:
+                #
+                # Skip tween keys
+                #
+		first_tween_pos, last_tween_pos = \
+		    self._find_tween_range(key_pos)
+		self._draw_tween(first_tween_pos, last_tween_pos)
+		last_tween_key = self._keys[last_tween_pos]
+                i = last_tween_key.idx + 1
+	    else:
+                self._draw_normal_frame(i)
+	        if key.idx == i:
+		    key_pos = key_pos+1
+		    try:
+		        key = self._keys[key_pos]
+		    except:
+		        key = keyframe(self._num_frames)
+			pass
+		    pass
+                i = i + 1
+                pass
+            pass
+
+        self._draw_bottom_line(0, num_frames)
+
+	self._draw_keyframes()
+        pass
+
+    def _draw_tween_of_key(self, key_pos):
+        if not self._drawing:
+            return
+
+	first_pos, last_pos = self._find_tween_range(key_pos)
+	first_key = self._keys[first_pos]
+	last_key = self._keys[last_pos]
+	
+	self._draw_tween(first_pos, last_pos)
+	self._draw_bottom_line(first_key.idx, last_key.idx + 1)
+
+	for i in range(first_pos, last_pos + 1):
+	    key = self._keys[i]
+	    self._draw_keyframe(key.idx)
+            pass
+	pass
+    
+    def _draw_active_frame(self):
+        if not self._drawing:
+            return
+        
+        if self._active_frame == -1:
+            return
+
+	self._draw_active(self._active_frame)
+        pass
+
+    def _draw_hover_frame(self, frame_idx):
+	if not self._drawing:
+	    return
+	self._draw_hover(frame_idx)
+	pass
+
+    def set_tween_type(self, frame_idx, tween_type):
+	pos = self._find_keyframe(frame_idx)
+	key = self._keys[pos]
+	assert key.right_tween
+
+	key.right_tween_type = tween_type
+	self._draw_tween_of_key(pos)
+
+	self._draw_active_frame()
+	pass
+
     def update(self):
         if not self._drawing:
             return
@@ -522,21 +548,22 @@
         win = self.window
         x, y, w, h, depth = win.get_geometry()
         self._draw_all_frames()
-        self._draw_keyframes()
-        if self._active_frame != -1:
-            self._draw_active()
-            pass
+	self._draw_active_frame()
         pass
 
     ## Add a key frame
     #
     # A key frame is the frame that user specify actions.  For
     # example, move a object or add new objects at the frame.
-    def add_keyframe(self, idx,ref=None):
-        key_indic = [key.idx for key in self._keys]
-        if idx in key_indic:
-            return
-
+    def add_keyframe(self, idx, ref=None):
+	try:
+	    pos = self._find_keyframe(idx) # it is not already a keyframe.
+	except ValueError:
+	    pass
+	else:
+	    raise ValueError, 'the frame is already a key frame'
+	
+	key_indic = [key.idx for key in self._keys]
         key_indic.append(idx)
         key_indic.sort()
         insert_pos = key_indic.index(idx)
@@ -552,78 +579,99 @@
             key.right_tween = True
             pass
 
-        self._draw_keyframe(idx)
+	if self._drawing:
+	    self._draw_keyframe(idx)
+	    pass
         pass
 
     def rm_keyframe(self, idx):
-        found=False
-	for i in range(0,len(self._keys)):
-	    if self._keys[i].idx == idx:
-	        idx = i
-		found = True
-		break
-	if not found: return
-        key = self._keys[idx]
+	key_pos = self._find_keyframe(idx)
+        key = self._keys[key_pos]
+	del self._keys[key_pos]
         
         if key.right_tween ^ key.left_tween:
             #
             # tween in one side
             #
             if key.right_tween:
-                right_key = self._keys[idx]
+		right_key = self._keys[key_pos]
                 right_key.left_tween = False
                 redraw_range = (right_key.idx, idx + 1)
             else:
-                left_key = self._keys[idx - 1]
+                left_key = self._keys[key_pos - 1]
                 left_key.right_key = False
                 redraw_range = (idx, left_key.idx + 1)
                 pass
             for i in range(*redraw_range):
-                self._redraw_frame(i)
+                self._draw_frame(i)
                 pass
         else:
-            self._redraw_frame(idx)
+            self._draw_frame(idx)
             pass
 
-        del self._keys[idx]
-        self._draw_active()
+        self._draw_active_frame()
         pass
 
     ## Tween the key frame specified by an index and the key frame at right.
     #
     # \see http://www.entheosweb.com/Flash/shape_tween.asp
-    def tween(self, idx, _type='normal'):
-        key_indic = [key.idx for key in self._keys]
-        pos = key_indic.index(idx)
+    def tween(self, idx, tween_type=TWEEN_TYPE_NONE):
+	pos = self._find_keyframe(idx)
         key = self._keys[pos]
         
         try:
             right_key = self._keys[pos + 1]
         except IndexError:
-            raise ValueError, 'No right key frame'
+            raise ValueError, 'no right key frame'
 
         key.right_tween = True
         right_key.left_tween = True
-	if _type == 'normal':
-            key.right_tween_type = self._tween_type_none
-	elif _type == 'relocate':
-            key.right_tween_type = self._tween_type_move
-	elif _type == 'scale':
-            key.right_tween_type = self._tween_type_shape
+	key.right_tween_type = tween_type
+	
+	self._draw_tween_of_key(pos)
+	self._draw_active_frame()
         pass
-    def get_tween_type(self,idx):
+    
+    def get_tween_type(self, idx):
         for i in range(0,len(self._keys)):
 	    key = self._keys[i]
 	    if key.left_tween is True: continue
 	    if key.idx == idx:
-	    	if key.right_tween_type == self._tween_type_none:
-		    return 'normal'
-	    	elif key.right_tween_type == self._tween_type_move:
-		    return 'relocate'
-	    	elif key.right_tween_type == self._tween_type_shape:
-		    return 'scale'
-        
+		return key.right_tween_type
+	    pass
+	pass
+
+    def get_frame_blocks(self):
+	blocks = []
+	for pos, key in enumerate(self._keys):
+	    if key.right_tween:
+		next_key = self._keys[pos + 1]
+		block = (key.idx, next_key.idx, key.right_tween_type)
+	    elif not key.left_tween:
+		block = (key.idx, key.idx, 0)
+		pass
+	    blocks.append(block)
+	    pass
+	return blocks
 
+    def get_frame_block(self, idx):
+	pos = self._find_keyframe_floor(idx)
+	if pos != -1:
+	    key = self._keys[pos]
+	    if key.idx == idx:
+		return key.idx, key.idx, 0
+	    elif key.right_tween:
+		next_key = self._keys[pos + 1]
+		return key.idx, next_key.idx, key.right_tween_type
+	    pass
+	raise ValueError, \
+	    'the frame specified by idx is not in any tween or a key frame'
+    
+    def get_frame_data(self, idx):
+	pos = self._find_keyframe(idx)
+	key = self._keys[pos]
+	return key.ref
+    
     ## Set active frame
     #
     # The active frame is the frame that is working on.
@@ -633,49 +681,28 @@
             raise IndexError, 'value of index (%d) is out of range' % (idx)
 
         if self._active_frame != -1:
-            self._redraw_frame(self._active_frame)
+            self._draw_frame(self._active_frame)
             pass
         self._active_frame = idx
-        self._draw_active()
+        self._draw_active_frame()
         pass
 
     def deactive(self):
-        self._redraw_frame(self._active_frame)
+        self._draw_frame(self._active_frame)
         self._active_frame = -1
         pass
 
     def set_num_frames(self, num):
         self._num_frames = num
+	self._
         pass
 
     def reset(self):
         self._keys = []
+	self._active_frame = -1
+	self._draw_all_frames()
         pass
 
-    def addScenes(self,rdoc,node):
-        for i in range(0,len(self._keys)):
-	    key = self._keys[i]
-	    if key.left_tween is True: continue
-	    if key.right_tween is True:
-	        ss = rdoc.createElement("ns0:scene")
-		node.appendChild(ss)
-		ss.setAttribute("start", str(key.idx+1))
-		ss.setAttribute("ref",key.ref.attribute("id"))
-		ss.setAttribute("end", str(self._keys[i+1].idx+1))
-		if self._keys[i].right_tween_type == self._tween_type_none:
-		    ss.setAttribute("type", "normal")
-		elif self._keys[i].right_tween_type == self._tween_type_move:
-		    ss.setAttribute("type", "relocate")
-		elif self._keys[i].right_tween_type == self._tween_type_shape:
-		    ss.setAttribute("type", "scale")
-	    else:
-	        ss = rdoc.createElement("ns0:scene")
-		node.appendChild(ss)
-		ss.setAttribute("start", str(key.idx+1))
-		ss.setAttribute("ref",key.ref.attribute("id"))
-	        ss.setAttribute("type", "normal")
-
-	        
     ## \brief Start future drawing actions
     #
     def start_drawing(self):
@@ -691,9 +718,6 @@
     def stop_drawing(self):
         self._drawing = False
         pass
-
-    def __len__(self):
-        return self._num_frames
     pass
 
 if __name__ == '__main__':
--- a/pyink/pyink.py	Fri Dec 24 15:40:16 2010 +0800
+++ b/pyink/pyink.py	Sun Dec 26 19:17:12 2010 +0800
@@ -1,3 +1,14 @@
+import os
+
+try:
+    if os.environ['PYINK_DBG_ENABLE'] == 'yes':
+        import pdb
+        pdb.set_trace()
+        pass
+    pass
+except:
+    pass
+
 import pybInkscape
 import pygtk
 import gtk
--- a/pyink/tween.py	Fri Dec 24 15:40:16 2010 +0800
+++ b/pyink/tween.py	Sun Dec 26 19:17:12 2010 +0800
@@ -26,8 +26,10 @@
 	        pass
     def updateTweenContent(self,obj, typ, source,dest,cur):
 	"""
-	    Update the content of the duplicate scene group. We will use the (start,end) and cur to calculate the percentage of
-	    the tween motion effect and then use it to update the transform matrix of the duplicated scene group.
+	    Update the content of the duplicate scene group. We will
+	    use the (start,end) and cur to calculate the percentage of
+	    the tween motion effect and then use it to update the
+	    transform matrix of the duplicated scene group.
 	"""
 
 	start = source.idx
@@ -76,8 +78,9 @@
 		label = s.getAttribute("inkscape:label")
 		# Use i8nkscape:label to identidy the equipvalent objects
 		if label:
-		    if dests.hasattr(label.value()):
-			self.updateTweenObject(obj,typ,s,dests[label.value()],percent,o)
+		    if dests.has_key(label):
+			self.updateTweenObject(obj, typ, s,
+					       dests[label], percent, o)
 			s = s.next()
 			continue
 	    except:
@@ -168,18 +171,29 @@
 	    Generate tweened object in the @obj by using s and d in the @p percent
 	    http://lists.w3.org/Archives/Public/www-style/2010Jun/0602.html
 	"""
-	if typ == 'relocate':
-	    newobj = s.duplicate(self.document)
-	    top = self.document.createElement("svg:g")
-	    top.setAttribute("ref", s.getAttribute("id"))
-	    top.appendChild(newobj)
-	    obj.appendChild(top)
+	if typ == 1:
 	    if s.name() == 'svg:g':
+		if not newobj:
+		    newobj = s.duplicate(self.document)
+		    top = self.document.createElement("svg:g")
+		    top.setAttribute("ref", s.getAttribute("id"))
+		    top.appendChild(newobj)
+		    obj.appendChild(top)
+		else:
+		    top = newobj
+		    pass
 		# Parse the translate or matrix
 		sm = self.parseTransform(s)
 		dm = self.parseTransform(d)
 		top.setAttribute("transform","translate(%g,%g)" % ((dm[2]-sm[2])*p,(dm[5]-sm[5])*p))
 	    else:
+		if not newobj:
+		    top = s.duplicate(self.document)
+		    top.setAttribute('ref', s.getAttribute('id'))
+		    obj.appendChild(top)
+		else:
+		    top = newobj
+		    pass
 		try:
 		    sx = float(s.getAttribute("x"))
 		    sy = float(s.getAttribute("y"))
@@ -193,10 +207,10 @@
 		    traceback.print_exc()
 		    pass
 	    pass
-	elif typ == 'scale':
+	elif typ == 2:
 	    self.updateTweenObjectScale(obj,s,d,p,newobj)
 	    pass
-	elif typ == 'normal':
+	elif typ == 0:
 	    newobj = s.duplicate(self.document)
 	    newobj.setAttribute("ref", s.getAttribute("id"))
 	    top = self.document.createElement("svg:g")