[pitivi/ges: 78/287] timeline: rivive the Selection class and use it
- From: Jean-FranÃois Fortin Tam <jfft src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi/ges: 78/287] timeline: rivive the Selection class and use it
- Date: Thu, 15 Mar 2012 16:32:23 +0000 (UTC)
commit 55d0afeac5da5c7398a48c9f09f9fc91ade6e4c5
Author: Thibault Saunier <thibault saunier collabora com>
Date: Wed Dec 7 18:45:49 2011 -0300
timeline: rivive the Selection class and use it
+ Port it to GES.
+ Modify the current code to use it
+ Handle various clip selection using the code from old PiTiVI
+ Some cleanup all around
Also makes triming working again
pitivi/project.py | 7 ++-
pitivi/timeline/timeline.py | 127 ++++++++++++++++++++++++++++++++++++++--
pitivi/ui/timeline.py | 41 +++++---------
pitivi/ui/timelinecanvas.py | 4 +-
pitivi/ui/trackobject.py | 135 ++++++++++++++++++++++--------------------
5 files changed, 214 insertions(+), 100 deletions(-)
---
diff --git a/pitivi/project.py b/pitivi/project.py
index d58c699..92ab35c 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -26,11 +26,12 @@ Project class
import gst
import ges
+from pitivi.utils import Seeker
from pitivi.log.loggable import Loggable
from pitivi.sourcelist import SourceList
from pitivi.settings import ExportSettings
from pitivi.signalinterface import Signallable
-from pitivi.utils import Seeker
+from pitivi.timeline.timeline import Selection
class ProjectError(Exception):
@@ -86,7 +87,9 @@ class Project(Signallable, Loggable):
self._dirty = False
self.timeline = ges.timeline_new_audio_video()
- self.timeline.selected = []
+ # We add a Selection to the timeline as there is currently
+ # no such feature in GES
+ self.timeline.selection = Selection()
self.layer = ges.TimelineLayer()
self.layer.set_property("auto-transition", True)
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index bbb41a1..b70a912 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -23,6 +23,7 @@
import ges
from pitivi.utils import infinity
+from pitivi.signalinterface import Signallable
from pitivi.timeline.gap import Gap, SmallestGapsFinder, invalid_gap
#from pitivi.timeline.align import AutoAligner
@@ -38,6 +39,11 @@ SELECT_BETWEEN = 3
"""Select a range of clips"""
+class TimelineError(Exception):
+ """Base Exception for errors happening in L{Timeline}s or L{TimelineObject}s"""
+ pass
+
+
class EditingContext(object):
DEFAULT = 0
@@ -511,8 +517,8 @@ class TrimStartContext(EditingContext):
timeline_objects = [self.focus_timeline_object]
EditingContext.finish(self)
- left_gap, right_gap = self._getGapsForLayer(obj,
- timeline_objects, self.tracks)
+ left_gap, right_gap = self._getGapsForLayer(timeline_objects,
+ self.tracks)
if left_gap is invalid_gap:
self._defaultTo(initial_position, obj.priority)
@@ -547,7 +553,7 @@ class TrimEndContext(EditingContext):
if obj.props.start > reference]
self.ripple_originals = self._saveValues(ripple)
- self.ripple_offsets = self._getOffsets(reference, self.focus.get_priority(),
+ self.ripple_offsets = self._getOffsets(reference, self.focus.props.priority,
ripple)
def _rollTo(self, position, priority):
@@ -599,11 +605,122 @@ class TrimEndContext(EditingContext):
timeline_objects = [self.focus_timeline_object]
- left_gap, right_gap = self._getGapsForLayer(obj.priority,
- timeline_objects, self.tracks)
+ left_gap, right_gap = self._getGapsForLayer(timeline_objects,
+ self.tracks)
if right_gap is invalid_gap:
self._defaultTo(absolute_initial_duration, obj.priority)
left_gap, right_gap = Gap.findAroundObject(self.focus_timeline_object)
duration = absolute_initial_duration + right_gap.duration
self._defaultTo(duration, obj.priority)
+
+
+class Selection(Signallable):
+ """
+ A collection of L{ges.TimelineObject}.
+
+ Signals:
+ - C{selection-changed} : The contents of the L{ges.Selection} changed.
+
+ @ivar selected: Set of selected L{ges.TrackObject}
+ @type selected: C{list}
+ """
+
+ __signals__ = {
+ "selection-changed": []}
+
+ def __init__(self):
+ self.selected = set([])
+ self.last_single_obj = None
+
+ def setToObj(self, obj, mode):
+ """
+ Convenience method for calling L{setSelection} with a single L{ges.TimelineObject}
+
+ @see: L{setSelection}
+ """
+ self.setSelection(set([obj]), mode)
+
+ def addTimelineObject(self, timeline_object):
+ """
+ Add the given timeline_object to the selection.
+
+ @param timeline_object: The object to add
+ @type timeline_object: L{ges.TimelineObject}
+ @raises TimelineError: If the object is already controlled by this
+ Selection.
+ """
+ if timeline_object in self.timeline_objects:
+ raise TimelineError("TrackObject already in this selection")
+
+ def setSelection(self, objs, mode):
+ """
+ Update the current selection.
+
+ Depending on the value of C{mode}, the selection will be:
+ - L{SELECT} : set to the provided selection.
+ - L{UNSELECT} : the same minus the provided selection.
+ - L{SELECT_ADD} : extended with the provided selection.
+
+ @param selection: The list of timeline objects to update the selection with.
+ @param mode: The type of update to apply. Can be C{SELECT},C{UNSELECT} or C{SELECT_ADD}
+
+ @see: L{setToObj}
+ """
+ # get a list of timeline objects
+ selection = set()
+ for obj in objs:
+ # FIXME GES break, handle the fact that we have unlinked objects in GES
+ if isinstance(obj, ges.TrackObject):
+ selection.add(obj.get_timeline_object())
+ else:
+ selection.add(obj)
+
+ old_selection = self.selected
+ if mode == SELECT_ADD:
+ selection = self.selected | selection
+ elif mode == UNSELECT:
+ selection = self.selected - selection
+ self.selected = selection
+
+ if len(self.selected) == 1:
+ self.last_single_obj = iter(selection).next()
+
+ for obj in self.selected - old_selection:
+ for tckobj in obj.get_track_objects():
+ tckobj.selected.selected = True
+
+ for obj in old_selection - self.selected:
+ for tckobj in obj.get_track_objects():
+ tckobj.selected.selected = False
+
+ # FIXME : shouldn't we ONLY emit this IFF the selection has changed ?
+ self.emit("selection-changed")
+
+ def getSelectedTrackObjs(self):
+ """
+ Returns the list of L{TrackObject} contained in this selection.
+ """
+ objects = []
+ for timeline_object in self.selected:
+ objects.extend(timeline_object.get_track_objects())
+
+ return set(objects)
+
+ def getSelectedTrackEffects(self):
+ """
+ Returns the list of L{TrackEffect} contained in this selection.
+ """
+ track_effects = []
+ for timeline_object in self.selected:
+ for track in timeline_object.track_objects:
+ if isinstance(track, ges.TrackEffect):
+ track_effects.append(track)
+
+ return track_effects
+
+ def __len__(self):
+ return len(self.selected)
+
+ def __iter__(self):
+ return iter(self.selected)
diff --git a/pitivi/ui/timeline.py b/pitivi/ui/timeline.py
index ed8f0f3..7049e07 100644
--- a/pitivi/ui/timeline.py
+++ b/pitivi/ui/timeline.py
@@ -738,18 +738,11 @@ class Timeline(gtk.Table, Loggable, Zoomable):
def deleteSelected(self, unused_action):
if self.timeline:
self.app.action_log.begin("delete clip")
- for track_object in self.timeline.selected:
- obj = track_object.get_timeline_object()
- obj.release_track_object(track_object)
- track = track_object.get_track()
- track.remove_object(track_object)
- remove = True
- for tck_obj in obj.get_track_objects():
- if isinstance(tck_obj, ges.TrackSource):
- remove = False
- if remove:
- lyr = obj.get_layer()
- lyr.remove_object(obj)
+ #FIXME GES port: Handle unlocked TrackObject-s
+ for obj in self.timeline.selection:
+ layer = obj.get_layer()
+ layer.remove_object(obj)
+
self.app.action_log.commit()
def unlinkSelected(self, unused_action):
@@ -763,14 +756,14 @@ class Timeline(gtk.Table, Loggable, Zoomable):
def ungroupSelected(self, unused_action):
if self.timeline:
self.app.action_log.begin("ungroup")
- for track_object in self.timeline.selected:
- track_object.set_locked(False)
+ for tlobj in self.timeline.selection:
+ tlobj.objects_set_locked(False)
self.app.action_log.commit()
def groupSelected(self, unused_action):
if self.timeline:
- for track_object in self.timeline.selected:
- track_object.set_locked(True)
+ for tlobj in self.timeline.selection:
+ tlobj.set_locked(True)
def alignSelected(self, unused_action):
if "NumPy" in soft_deps:
@@ -792,23 +785,17 @@ class Timeline(gtk.Table, Loggable, Zoomable):
def split(self, action):
self.timeline.enable_update(False)
- tl_objs_dict = {}
+
+ #Splitting the objects at the current position
for track in self.timeline.get_tracks():
for tck_obj in track.get_objects():
start = tck_obj.props.start
end = start + tck_obj.props.duration
if start < self._position and end > self._position:
obj = tck_obj.get_timeline_object()
- if obj in tl_objs_dict.keys():
- tl_objs_dict[obj] = "both"
- elif tck_obj.get_track().get_caps().to_string() == "audio/x-raw-int; audio/x-raw-float":
- tl_objs_dict[obj] = "audio"
- else:
- tl_objs_dict[obj] = "video"
-
- for src in tl_objs_dict:
- src.split(self._position)
- self.timeline.enable_update(True)
+ obj.split(self._position)
+
+ self.timeline.enable_update(True)
def keyframe(self, action):
timeline_position = self._position
diff --git a/pitivi/ui/timelinecanvas.py b/pitivi/ui/timelinecanvas.py
index 31a5165..778d13a 100644
--- a/pitivi/ui/timelinecanvas.py
+++ b/pitivi/ui/timelinecanvas.py
@@ -243,7 +243,7 @@ class TimelineCanvas(goocanvas.Canvas, Zoomable, Loggable):
self._selecting = False
self._marquee.props.visibility = goocanvas.ITEM_INVISIBLE
if not self._got_motion_notify:
- #self._timeline.setSelectionTo(set(), 0)
+ self._timeline.selection.setSelection([], 0)
seeker.seek(Zoomable.pixelToNs(event.x))
else:
self._got_motion_notify = False
@@ -254,7 +254,7 @@ class TimelineCanvas(goocanvas.Canvas, Zoomable, Loggable):
mode = 2
selected = self._objectsUnderMarquee()
self.app.projectManager.current.emit("selected-changed", selected)
- #self._timeline.setSelectionTo(self._objectsUnderMarquee(), mode)
+ self._timeline.selection.setSelection(self._objectsUnderMarquee(), mode)
return True
def _objectsUnderMarquee(self):
diff --git a/pitivi/ui/trackobject.py b/pitivi/ui/trackobject.py
index 3bb5780..fad5ac4 100644
--- a/pitivi/ui/trackobject.py
+++ b/pitivi/ui/trackobject.py
@@ -14,12 +14,14 @@ from zoominterface import Zoomable
from common import LAYER_HEIGHT_EXPANDED, LAYER_HEIGHT_COLLAPSED
from common import LAYER_SPACING, unpack_cairo_pattern, unpack_cairo_gradient
+from pitivi.ui.point import Point
from pitivi.log.loggable import Loggable
from pitivi.settings import GlobalSettings
from pitivi.receiver import receiver, handler
from pitivi.ui.prefs import PreferencesDialog
-from pitivi.timeline.timeline import MoveContext, TrimStartContext,\
- TrimEndContext
+from pitivi.signalinterface import Signallable
+from pitivi.timeline.timeline import SELECT, SELECT_ADD, UNSELECT, \
+ SELECT_BETWEEN, MoveContext, TrimStartContext, TrimEndContext
LEFT_SIDE = gtk.gdk.Cursor(gtk.gdk.LEFT_SIDE)
RIGHT_SIDE = gtk.gdk.Cursor(gtk.gdk.RIGHT_SIDE)
@@ -109,6 +111,31 @@ def get_next_track_source(track, tckobj):
return None
+class Selected (Signallable):
+ """
+ A simple class that let us emit a selected-changed signal
+ when need
+ """
+
+ __signals__ = {
+ "selected-changed": []}
+
+ def __init__(self):
+ self._selected = False
+
+ def __nonzero__(self):
+ return self._selected
+
+ def getSelected(self):
+ return self._selected
+
+ def setSelected(self, selected):
+ self._selected = selected
+ self.emit("selected-changed", selected)
+
+ selected = property(getSelected, setSelected)
+
+
class TimelineController(controller.Controller):
_cursor = ARROW
@@ -125,8 +152,9 @@ class TimelineController(controller.Controller):
self._view.unfocus()
def drag_start(self, item, target, event):
- #if not self._view.element.selected:
- #self._view.timeline.selection.setToObj(self._view.element, SELECT)
+ self.debug("Drag started")
+ if not self._view.element.selected:
+ self._view.timeline.selection.setToObj(self._view.element, SELECT)
if self.previous_x != None:
ratio = float(self.ref / Zoomable.pixelToNs(10000000000))
self.previous_x = self.previous_x * ratio
@@ -136,16 +164,17 @@ class TimelineController(controller.Controller):
# store y offset for later priority calculation
self._y_offset = tx[5]
# zero y component of mousdown coordiante
- #self._mousedown = Point(self._mousedown[0], 0)
+ self._mousedown = Point(self._mousedown[0], 0)
def drag_end(self, item, target, event):
+ self.debug("Drag end")
self._context.finish()
self._context = None
self._view.app.projectManager.current.timeline.enable_update(True)
self._view.app.action_log.commit()
- self._view.element.starting_start = self._view.element.get_property("start")
+ self._view.element.starting_start = self._view.element.props.start
obj = self._view.element.get_timeline_object()
- obj.starting_start = obj.get_property("start")
+ obj.starting_start = obj.props.start
self.previous_x = self.next_previous_x
def set_pos(self, item, pos):
@@ -153,10 +182,11 @@ class TimelineController(controller.Controller):
x = x + self._hadj.get_value()
priority = int((y - self._y_offset + self._vadj.get_value()) //
(LAYER_HEIGHT_EXPANDED + LAYER_SPACING))
- #self._context.setMode(self._getMode())
+ self._context.setMode(self._getMode())
track = self._view.element.get_track()
start = self._view.element.get_start()
duration = self._view.element.get_duration()
+
if self.previous_x:
position = self._view.element.get_start() + Zoomable.pixelToNs(x - self.previous_x)
else:
@@ -251,14 +281,14 @@ class StartHandle(TrimHandle):
_cursor = LEFT_SIDE
def drag_start(self, item, target, event):
+ self.debug("Trim start %s" % target)
TimelineController.drag_start(self, item, target, event)
if self._view.element.is_locked():
elem = self._view.element.get_timeline_object()
else:
elem = self._view.element
self._context = TrimStartContext(self._view.timeline,
- elem,
- set([]))
+ elem, set([]))
self._view.app.action_log.begin("trim object")
@@ -271,14 +301,14 @@ class EndHandle(TrimHandle):
_cursor = RIGHT_SIDE
def drag_start(self, item, target, event):
+ self.debug("Trim end %s" % target)
TimelineController.drag_start(self, item, target, event)
if self._view.element.is_locked():
elem = self._view.element.get_timeline_object()
else:
elem = self._view.element
self._context = TrimEndContext(self._view.timeline,
- elem,
- set([]))
+ elem, set([]))
self._view.app.action_log.begin("trim object")
@@ -303,27 +333,21 @@ class TrackObject(View, goocanvas.Group, Zoomable):
return self._context.DEFAULT
def click(self, pos):
- #timeline = self._view.timeline
+ timeline = self._view.timeline
element = self._view.element
- if element.is_locked():
- elem = set(element.get_timeline_object().get_track_objects())
+ if self._last_event.get_state() & gtk.gdk.SHIFT_MASK:
+ timeline.selection.setToObj(element, SELECT_BETWEEN)
+ elif self._last_event.get_state() & gtk.gdk.CONTROL_MASK:
+ if element.selected:
+ mode = UNSELECT
+ else:
+ mode = SELECT_ADD
+ timeline.selection.setToObj(element, mode)
else:
- elem = element
- self._view.app.projectManager.current.emit("selected-changed", elem)
- element_end = element.get_property("start") + element.get_property("duration")
- #if self._last_event.get_state() & gtk.gdk.SHIFT_MASK:
- #timeline.setSelectionToObj(element, SELECT_BETWEEN)
- #elif self._last_event.get_state() & gtk.gdk.CONTROL_MASK:
- #if element.selected:
- #mode = UNSELECT
- #else:
- #mode = SELECT_ADD
- #timeline.setSelectionToObj(element, mode)
- #else:
- x, y = pos
- x += self._hadj.get_value()
- #self._view.app.current.seeker.seek(Zoomable.pixelToNs(x))
- #timeline.setSelectionToObj(element, SELECT)
+ x, y = pos
+ x += self._hadj.get_value()
+ self._view.app.current.seeker.seek(Zoomable.pixelToNs(x))
+ timeline.selection.setToObj(element, SELECT)
def __init__(self, instance, element, track, timeline, uTrack, is_transition=False):
goocanvas.Group.__init__(self)
@@ -337,7 +361,7 @@ class TrackObject(View, goocanvas.Group, Zoomable):
self.namewidth = 0
self.nameheight = 0
self.is_transition = is_transition
- self.app.projectManager.current.connect("selected-changed", self.selected_changed)
+
self.snapped_before = False
self.snapped_after = False
@@ -362,13 +386,13 @@ class TrackObject(View, goocanvas.Group, Zoomable):
self.end_handle = EndHandle(self.app, element, timeline,
height=self.height)
- self.selection_indicator = goocanvas.Rect(
+ self._selec_indic = goocanvas.Rect(
visibility=goocanvas.ITEM_INVISIBLE,
line_width=0.0,
height=self.height)
if not self.is_transition:
- for thing in (self.bg, self.selection_indicator,
+ for thing in (self.bg, self._selec_indic,
self.start_handle, self.end_handle, self.namebg, self.name):
self.add_child(thing)
else:
@@ -376,12 +400,15 @@ class TrackObject(View, goocanvas.Group, Zoomable):
self.add_child(thing)
self.element = element
- self.element.get_timeline_object().max_duration = self.element.get_timeline_object().get_property("duration")
- self.element.max_duration = self.element.get_property("duration")
- self.element.selected = False
- self.element.starting_start = self.element.get_property("start")
+ element.max_duration = element.props.duration
+ element.starting_start = element.props.start
+ element.selected = Selected()
+ element.selected.connect("selected-changed", self.selectedChangedCb)
+
obj = self.element.get_timeline_object()
obj.starting_start = obj.get_property("start")
+ obj.max_duration = obj.props.duration
+
self.settings = instance.settings
self.unfocus()
@@ -463,7 +490,7 @@ class TrackObject(View, goocanvas.Group, Zoomable):
self.namebg.props.fill_pattern = pattern
- self.selection_indicator.props.fill_pattern = unpack_cairo_pattern(
+ self._selec_indic.props.fill_pattern = unpack_cairo_pattern(
self.settings.selectedColor)
self.name.props.font = self.settings.clipFontDesc
@@ -492,31 +519,11 @@ class TrackObject(View, goocanvas.Group, Zoomable):
def startChangedCb(self, track_object, start):
self._update()
- def selected_changed(self, unused_project, element):
- self.timeline.selected = []
- if isinstance(element, set):
- for elem in element:
- elem.selected = True
- self.timeline.selected.append(elem)
- for elem in element:
- if elem == self.element:
- self.selection_indicator.props.visibility = goocanvas.ITEM_VISIBLE
- elem.selected = True
- elif self.element.selected == False:
- self.selection_indicator.props.visibility = \
- goocanvas.ITEM_INVISIBLE
- for elem in element:
- elem.selected = False
- return
-
- else:
- self.timeline.selected.append(element)
-
- if element == self.element:
- self.selection_indicator.props.visibility = goocanvas.ITEM_VISIBLE
+ def selectedChangedCb(self, element, selected):
+ if element.selected:
+ self._selec_indic.props.visibility = goocanvas.ITEM_VISIBLE
else:
- self.selection_indicator.props.visibility = \
- goocanvas.ITEM_INVISIBLE
+ self._selec_indic.props.visibility = goocanvas.ITEM_INVISIBLE
def _update(self):
try:
@@ -536,7 +543,7 @@ class TrackObject(View, goocanvas.Group, Zoomable):
self.name.props.clip_path = "M%g,%g h%g v%g h-%g z" % (
0, 0, w, self.height, w)
self.bg.props.width = width
- self.selection_indicator.props.width = width
+ self._selec_indic.props.width = width
self.end_handle.props.x = w
if self.expanded:
if w - NAME_HOFFSET > 0:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]