diff pyink/domview_ui.py @ 1243:d5f70928e9f1

Move MBScene_domview_ui and MBScene_domview to separated modules. - domview.py is where MBScene_domview and MBScene_domview_monitor are. - domview_ui.py is where MBScene_domview_ui and MBScene_frameline_stack are. - keep modules small to make better collaberation.
author Thinker K.F. Li <thinker@codemud.net>
date Mon, 10 Jan 2011 16:32:16 +0800
parents
children b241f9768833
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pyink/domview_ui.py	Mon Jan 10 16:32:16 2011 +0800
@@ -0,0 +1,618 @@
+import gtk
+from tween import TweenObject
+from frameline import frameline, frameruler
+from domview import MBScene_domview
+
+
+## \brief Maintain a stack of frameline UI component.
+#
+# Every layer is assocated with a frameline.  Framelines are showed/stacked in
+# virtical.  Framelines of lower layers are placed at lower position on the
+# screen.  This class provide a set of API to access framelines with layer and
+# frame index number.  You access/set content of frameline by specifing layer
+# index and frame index.
+#
+class MBScene_frameline_stack(object):
+    _frameline_tween_types = (frameline.TWEEN_TYPE_NONE,
+			      frameline.TWEEN_TYPE_SHAPE)
+    _num_frames_of_line = 100
+    
+    _framelines = None
+    
+    def __init__(self, *args, **kws):
+	super(MBScene_frameline_stack, self).__init__(*args, **kws)
+	
+	self._last_mouse_over_frameline = None
+	self._last_active_frameline = None
+	self._active_frame_callback = None
+	pass
+
+    def _change_hover_frameline(self, widget, event):
+        """
+	Hide all hover frames. This is a hack. We should use the lost focus
+	event instead in the future to reduce the overhead.
+	"""
+	if self._last_mouse_over_frameline and \
+		widget != self._last_mouse_over_frameline:
+	    self._last_mouse_over_frameline.mouse_leave()
+	    pass
+	self._last_mouse_over_frameline = widget
+	pass
+
+    ## \brief Switch to new active frameline.
+    #
+    # Hide active frame mark for the active frame of old active frameline.  It
+    # always shows at most one active frame mark.  When a frame is activated,
+    # all active frame mark of other frameline should be hidden.
+    #
+    def _active_frameline(self, frameline):
+	last = self._last_active_frameline
+	
+	if last and last != frameline:
+	    last.deactive()
+	    pass
+	
+	self._last_active_frameline = frameline
+	pass
+
+    ## \brief Called for changing of active frame.
+    #
+    # This handle deactive previous frameline that owns an active frame when a
+    # frame in another frameline is activated.
+    #
+    def _change_active_frame(self, frameline, frame_idx, button):
+	frameline.active_frame(frame_idx)
+	self._active_frameline(frameline)
+	
+	if self._active_frame_callback:
+	    layer_idx = frameline.layer_idx
+	    self._active_frame_callback(layer_idx, frame_idx)
+	    pass
+	pass
+
+    ## \brief Add a frameline into the frameline box for the given layer.
+    #
+    def _add_frameline(self, layer_idx):
+	if layer_idx > len(self._framelines):
+	    raise ValueError, 'layer number should be a consequence'
+
+	vbox = self._frameline_vbox
+	
+	line = frameline(self._num_frames_of_line)
+	line.set_size_request(self._num_frames_of_line * 10, 20)
+	
+	hbox = gtk.HBox()
+	label = gtk.Label('')
+	label.set_size_request(100,0)
+	hbox.pack_start(label,expand=False, fill=True)
+	hbox.pack_start(line)
+	vbox.pack_start(hbox, False)
+
+	# Put later one on the top of earier one, but after the ruler.
+	position = len(self._framelines) - layer_idx + 1
+	vbox.reorder_child(hbox, position)
+	
+	self._framelines[layer_idx: layer_idx] = [line]
+	
+	for idx in range(layer_idx, len(self._framelines)):
+	    self._framelines[idx].layer_idx = idx
+	    pass
+	
+	line.label = label
+	line.connect('motion-notify-event', self._change_hover_frameline)
+	line.connect(frameline.FRAME_BUT_PRESS, self._change_active_frame)
+	pass
+    
+    ## \brief Remove the given frameline from the frameline box.
+    #
+    def _remove_frameline(self, layer_idx):
+	vbox = self._frameline_vbox
+	line = self._framelines[layer_idx]
+	
+	hbox = line.parent
+	vbox.remove(hbox)
+	del self._framelines[layer_idx]
+	
+	for idx in range(layer_idx, len(self._framelines)):
+	    self._framelines[idx].layer_idx = idx
+	    pass
+	pass
+
+    def _init_framelines(self):
+	self._framelines = []
+	
+	box = gtk.ScrolledWindow()
+	self._frameline_box = box
+	box.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+	box.set_size_request(-1, 150)
+	vbox = gtk.VBox()
+	self._frameline_vbox = vbox
+	box.add_with_viewport(vbox)
+	
+	nframes = self._num_frames_of_line
+	
+	#
+	# Set up a ruler
+	#
+	ruler = frameruler(nframes)
+	ruler.set_size_request(nframes * 10, 20)
+	ruler.show()
+	hbox = gtk.HBox()
+	label=gtk.Label('')
+	label.set_size_request(100,0)
+	hbox.pack_start(label,expand=False,fill=True)
+	hbox.pack_start(ruler)
+	vbox.pack_start(hbox, False)
+	pass
+
+    ## \brief Show framelines on the screen.
+    #
+    # When a frameline was inserted or removed, it would not be showed
+    # immediately.  This function is used to notify toolkit to update the
+    # screen and drawing framelines.
+    def _show_framelines(self):
+	self._frameline_vbox.show_all()
+	pass
+
+    ## \brief Make given frame as current active frame.
+    #
+    def active_frame(self, layer_idx, frame_idx):
+	frameline = self._framelines[layer_idx]
+	self._active_frameline(frameline)
+	frameline.active_frame(frame_idx)
+	pass
+
+    ## \brief Get layer and frame index of current active frame.
+    #
+    # \return (-1, -1) for no active, (layer_idx, frame_idx) for current
+    #		active.
+    def get_active_layer_frame(self):
+	if self._last_active_frameline:
+	    layer_idx = self._last_active_frameline.layer_idx
+	    frame_idx = self._last_active_frameline.get_active_frame()
+	    if frame_idx != -1:
+		return layer_idx, frame_idx
+	    pass
+	return -1, -1
+
+    ## \brief Get information of the key frame at left-side.
+    #
+    # The key frame, returned, is at the place of the give frame or its
+    # left-side.
+    def get_left_key_tween(self, layer_idx, frame_idx):
+	frameline = self._framelines[layer_idx]
+	start, end, fl_tween_type = frameline.get_frame_block_floor(frame_idx)
+	tween_type = self._frameline_tween_types.index(fl_tween_type)
+	return start, end, tween_type
+
+    ## \brief Return information of a key frame and its tweening.
+    #
+    # This method return the key frame that the given frame is, or is in its
+    # tween.
+    #
+    # \return (start, end, tween_type)
+    def get_key_tween(self, layer_idx, frame_idx):
+	frameline = self._framelines[layer_idx]
+	start, end, fl_tween_type = frameline.get_frame_block(frame_idx)
+
+	tween_type = self._frameline_tween_types.index(fl_tween_type)
+	return start, end, tween_type
+
+    def get_all_key_tween_of_layer(self, layer_idx):
+	frameline = self._framelines[layer_idx]
+	info = frameline.get_frame_blocks()
+	tweens = [(tween[0], tween[1],
+		   self._frameline_tween_types.index(tween[2]))
+		  for tween in info]
+	return tweens
+    
+    ## \brief Tweening key frame to a give size
+    #
+    # The tween can be changed by tweening it again.
+    def tween(self, layer_idx, key_frame_idx, tween_len,
+	      tween_type=TweenObject.TWEEN_TYPE_NORMAL):
+	assert tween_len > 0
+	frameline = self._framelines[layer_idx]
+	right_frame_idx = key_frame_idx + tween_len - 1
+	fl_tween_type = self._frameline_tween_types[tween_type]
+
+	start, end, old_fl_tween_type = \
+	    frameline.get_frame_block(key_frame_idx)
+	if start != key_frame_idx:
+	    ValueError, 'invalid key frame (%d)' % (key_frame_idx)
+	if start < end:
+	    frameline.unmark_keyframe(end)
+	    pass
+	frameline.mark_keyframe(right_frame_idx)
+	frameline.tween(start, fl_tween_type)
+	pass
+
+    ## \brief Unmark a key frame.
+    #
+    # Once a key frame was unmark, the associated tween was also removed
+    # totally.
+    #
+    def unmark_keyframe(self, layer_idx, frame_idx):
+	frameline = self._framelines[layer_idx]
+	start, end, fl_tween_type = frameline.get_frame_block(frame_idx)
+	if start != frame_idx:
+	    raise ValueError, 'no such key (%d, %d)' % (layer_idx, frame_idx)
+
+	frameline.unmark_keyframe(frame_idx)
+	if start < end:
+	    frameline.unmark_keyframe(end)
+	    pass
+	pass
+
+    ## \brief Makr a key frame.
+    #
+    # Make a frame as a key frame.
+    #
+    def mark_keyframe(self, layer_idx, frame_idx):
+	frameline = self._framelines[layer_idx]
+	frameline.mark_keyframe(frame_idx)
+	pass
+
+    ## \brief Get data associated with the given key frame.
+    #
+    # The given frame index must be exactly a key frame.
+    #
+    def get_keyframe_data(self, layer_idx, frame_idx):
+	frameline = self._framelines[layer_idx]
+	data = frameline.get_frame_data(frame_idx)
+	return data
+
+    ## \brief Set/associate data with the given key frame.
+    #
+    def set_keyframe_data(self, layer_idx, frame_idx, data):
+	frameline = self._framelines[layer_idx]
+	frameline.set_frame_data(frame_idx, data)
+	pass
+
+    ## \brief Insert frames before specified frame.
+    #
+    # Specified frame and frames after it are shift right for \ref num
+    # positions to make a space for new frames.
+    #
+    def insert_frames(self, layer_idx, frame_idx, num):
+	assert num > 0
+	assert frame_idx >= 0
+	frameline = self._framelines[layer_idx]
+	for i in range(num):
+	    frameline.add_frame(frame_idx)
+	    pass
+	pass
+
+    ## \brief Remove a number of frames from the frameline.
+    #
+    # All key frames and associated tween info covered by removing range would
+    # be removed.
+    #
+    def rm_frames(self, layer_idx, frame_idx, num):
+	assert num > 0
+	assert frame_idx >= 0
+	
+	frameline = self._framelines[layer_idx]
+	
+	#
+	# Remove right key frame of last tween which left one will be removed.
+	#
+	last_rm = frame_idx + num - 1 # last removed frame
+	try:
+	    start, end, tween_type = frameline.get_frame_block(last_rm)
+	except ValueError:	# last removed frame is not in any tween
+	    pass
+	else:
+	    if start >= frame_idx and end > last_rm:
+		# Left key frame of the tween was removed, but not right one.
+		frameline.untween(start)
+		frameline.unmark_keyframe(end)
+		pass
+	    pass
+
+	#
+	# Remove left key of the tween that right key frame is in removing
+	# range.
+	#
+	try:
+	    start, end, tween_type = frameline.get_frame_block(frame_idx)
+	except ValueError:
+	    pass
+	else:
+	    if start < frame_idx and end <= last_rm:
+		# right key frame is in removing range but left one.
+		frameline.untween(start)
+		frameline.unmark_keyframe(start)
+		frameline.unmark_keyframe(end)
+		pass
+	    pass
+	
+	for i in range(num):
+	    frameline.rm_frame(frame_idx)
+	    pass
+	pass
+
+    ## \brief Set label for a layer.
+    #
+    def set_layer_label(self, layer_idx, txt):
+	frameline = self._framelines[layer_idx]
+	frameline.label.set_text(txt)
+	pass
+
+    ## \brief Register a callback for active frame event.
+    #
+    # The callback would be called when a frame is activated.
+    #
+    def register_active_frame_callback(self, cb):
+	self._active_frame_callback = cb
+	pass
+    pass
+
+## \brief Bridge of DOM-tree to syncrhonize data-model and UI.
+#
+# This class is a wrapper
+class MBScene_domview_ui(object):
+    _tween_type_names = ('normal', 'scale')
+    
+    def __init__(self):
+	super(MBScene_domview_ui, self).__init__()
+	self._fl_stack = MBScene_frameline_stack()
+	self._dom = MBScene_domview()
+	pass
+
+    ## \brief Update content of a frameline from scenes of respective layer.
+    #
+    def _update_frameline_content(self, layer_idx):
+	fl_stack = self._fl_stack
+	scene_nodes = self._dom.get_all_scene_node_of_layer(layer_idx)
+	for scene_node in scene_nodes:
+	    start, end, tween_name = self._dom._parse_one_scene(scene_node)
+
+	    fl_stack.mark_keyframe(layer_idx, start)
+	    fl_stack.set_keyframe_data(layer_idx, start, scene_node)
+	    if start != end:
+		tween_type = self._tween_type_names.index(tween_name)
+		tween_len = end - start + 1
+		fl_stack.tween(layer_idx, start, tween_len, tween_type)
+		pass
+	    pass
+	pass
+    
+    ## \brief Add a frameline for every found layer.
+    #
+    # This method is called to create a frameline for every layer found when
+    # loading a document.
+    #
+    def _add_frameline_for_every_layer(self):
+	for layer_idx in range(self._dom.get_layer_num()):
+	    layer_group_node = self._dom.get_layer_group(layer_idx)
+	    label = layer_group_node.getAttribute('inkscape:label')
+	    
+	    self._fl_stack._add_frameline(layer_idx)
+	    self._fl_stack.set_layer_label(layer_idx, label)
+
+	    self._update_frameline_content(layer_idx)
+	    pass
+	pass
+    
+    ## \brief This method is called to handle a new document.
+    #
+    def handle_doc_root(self, doc, root):
+	self._dom.handle_doc_root(doc, root)
+	self._fl_stack._init_framelines()
+	self._add_frameline_for_every_layer()
+	self._fl_stack._show_framelines()
+	pass
+
+    ## \brief Mark given frame as a key frame.
+    #
+    def mark_key(self, layer_idx, key_idx):
+	scene_group = self._dom.add_scene_group(layer_idx)
+	scene_group_id = scene_group.getAttribute('id')
+	
+	scene_node = self._dom.add_scene_node(key_idx, key_idx)
+	self._dom.chg_scene_node(scene_node, ref=scene_group_id)
+	
+	self._fl_stack.mark_keyframe(layer_idx, key_idx)
+	self._fl_stack.set_keyframe_data(layer_idx, key_idx, scene_node)
+	pass
+
+    ## \brief Tweening a key frame.
+    #
+    # To tween a key spanning several frames at right-side.
+    # The tween of a key frame can be changed by tweening it again.
+    #
+    def tween(self, layer_idx, key_frame_idx, tween_len,
+	      tween_type=TweenObject.TWEEN_TYPE_NORMAL):
+	self._fl_stack.tween(layer_idx, key_frame_idx, tween_len, tween_type)
+	
+	end_frame_idx = key_frame_idx + tween_len - 1
+	scene_node = self._fl_stack.get_keyframe_data(layer_idx, key_frame_idx)
+	tween_name = self._tween_type_names[tween_type]
+	self._dom.chg_scene_node(scene_node, end=end_frame_idx,
+				 tween_type=tween_name)
+	pass
+
+    ## \brief Change tween info of a key frame
+    #
+    def chg_tween(self, layer_idx, key_frame_idx,
+		  tween_len=None, tween_type=None):
+	scene_node = self._fl_stack.get_keyframe_data(layer_idx, key_frame_idx)
+	start, end, old_tween_type = \
+	    self._fl_stack.get_key_tween(layer_idx, key_frame_idx)
+	
+	if tween_len is not None:
+	    end = start + tween_len - 1	    
+	    self._dom.chg_scene_node(scene_node, end=end)
+	    pass
+	if tween_type is not None:
+	    tween_name = self._tween_type_names[tween_type]
+	    self._dom.chg_scene_node(scene_node, tween_type=tween_name)
+	    pass
+
+	if tween_type is None:
+	    tween_type = old_tween_type
+	    pass
+
+	tween_len = end - start + 1
+	self._fl_stack.tween(layer_idx, start, tween_len, tween_type)
+	pass
+
+    ## \brief Unmark a frame from a key frame.
+    #
+    def unmark_key(self, layer_idx, key_frame_idx):
+	scene_node = self._fl_stack.get_keyframe_data(layer_idx, key_frame_idx)
+	self._dom.rm_scene_node_n_group(scene_node)
+	
+	self._fl_stack.unmark_keyframe(layer_idx, key_frame_idx)
+	pass
+
+    ## \brief Insert frames at specified position.
+    #
+    # All frame at and after given position will shift right.
+    #
+    def insert_frames(self, layer_idx, frame_idx, num):
+	self._fl_stack.insert_frames(layer_idx, frame_idx, num)
+	self._dom.insert_frames(layer_idx, frame_idx, num)
+	pass
+
+    ## \brief Insert frames at specified position.
+    #
+    # All frame at and after given position will shift left, except nearest
+    # \ref num frames are removed.
+    #
+    def rm_frames(self, layer_idx, frame_idx, num):
+	self._fl_stack.insert_frames(layer_idx, frame_idx, num)
+	self._dom.rm_frames(layer_idx, frame_idx, num)
+	pass
+
+    ## \brief Insert a layer at given position.
+    #
+    # Original layer \ref layer_idx and later ones would be shifted to make a
+    # space for the new layer.
+    #
+    def insert_layer(self, layer_idx):
+	self._dom.insert_layer(layer_idx)
+	self._fl_stack._add_frameline(layer_idx)
+	self._fl_stack._show_framelines()
+	pass
+
+    ## \brief Remove given layer.
+    #
+    def rm_layer(self, layer_idx):
+	self._dom.rm_layer(layer_idx)
+	self._fl_stack._remove_frameline(layer_idx)
+	self._fl_stack._show_framelines()
+	pass
+    
+    def set_active_layer_frame(self, layer_idx, frame_idx):
+	self._fl_stack.active_frame(layer_idx, frame_idx)
+	pass
+    
+    ## \bref Return current active frame and its layer.
+    #
+    # \return (layer_idx, frame_idx) of active frame, or (-1, -1) when no
+    #	      active one.
+    def get_active_layer_frame(self):
+	layer_idx, frame_idx = self._fl_stack.get_active_layer_frame()
+	return layer_idx, frame_idx
+
+    def get_layer_num(self):
+	return self._dom.get_layer_num()
+
+    ## \brief Return associated group node for a key frame.
+    #
+    # The given frame index must be exactly a key frame.
+    #
+    def get_key_group(self, layer_idx, frame_idx):
+	scene_node = self._fl_stack.get_keyframe_data(layer_idx, frame_idx)
+	scene_group_id = scene_node.getAttribute('ref')
+	scene_group_node = self._dom.get_node(scene_group_id)
+	return scene_group_node
+
+    ## \brief Find an associated key frame and tween info for a group ID.
+    #
+    def find_key_from_group(self, scene_group_id):
+	layer_idx, scene_node = \
+	    self._dom.find_layer_n_scene_of_node(scene_group_id)
+	start, end, tween_name = self._dom._parse_one_scene(scene_node)
+	tween_type = self._tween_type_names.index(tween_name)
+	return layer_idx, (start, end, tween_type)
+    
+    ## \brief Return key and tween info for given frame index.
+    #
+    # The return key is at given frame, or its tween covers given frame.
+    #
+    def get_key(self, layer_idx, frame_idx):
+	start, end, tween_type = \
+	    self._fl_stack.get_key_tween(layer_idx, frame_idx)
+	return start, end, tween_type
+
+    def get_left_key(self, layer_idx, frame_idx):
+	start, end, tween_type = \
+	    self._fl_stack.get_left_key_tween(layer_idx, frame_idx)
+	return start, end, tween_type
+
+    ## \brief Return information of key frames in the given layer.
+    #
+    def get_layer_keys(self, layer_idx):
+	key_tweens = self._fl_stack.get_all_key_tween_of_layer(layer_idx)
+	return key_tweens
+
+    ## \brief Return widget showing frames and layers.
+    #
+    def get_frame_ui_widget(self):
+	return self._fl_stack._frameline_box
+
+    ## \brief Register a callback for activating a frame event.
+    #
+    # The callback function is called when a frame is activated.
+    #
+    def register_active_frame_callback(self, cb):
+	self._fl_stack.register_active_frame_callback(cb)
+	pass
+
+    ## \brief Get duplicate group of a layer.
+    #
+    def get_layer_dup_group(self, layer_idx):
+	data = self._dom.get_layer_data(layer_idx)
+	if not data:
+	    data = dict()
+	    self._dom.set_layer_data(layer_idx, data)
+	    pass
+
+	dup_group = None
+	if data.has_key('dup_group_id'):
+	    try:
+		dup_group = self._dom.get_node(data['dup_group_id'])
+	    except KeyError:
+		pass
+	    pass
+	
+	if not dup_group:
+	    # Search dup group from children of the layer group
+	    layer_group = self._dom.get_layer_group(layer_idx)
+	    for child in layer_group.childList():
+		try:
+		    label = child.getAttribute('inkscape:label')
+		except:
+		    pass
+		else:
+		    if label == 'dup':
+			data['dup_group_id'] = child
+			return child
+		    pass
+		pass
+	    
+	    # Or create a new dup group for the layer
+	    dup_group = self._dom.create_layer_dup_group(layer_idx)
+	    data['dup_group_id'] = dup_group.getAttribute('id')
+	    pass
+
+	return dup_group
+
+    def get_max_frame(self):
+	max_frame = self._dom.get_max_frame()
+	return max_frame
+    pass
+