[pitivi] Drag and drop support from the media library.



commit b9cb45cd75379b8a085a0b17fdfd68e9c8ed0c88
Author: Mathieu Duponchelle <mathieu duponchelle epitech eu>
Date:   Thu Apr 18 00:45:07 2013 +0200

    Drag and drop support from the media library.

 pitivi/medialibrary.py      |   16 +++++
 pitivi/timeline/elements.py |   10 +++-
 pitivi/timeline/timeline.py |  147 ++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 171 insertions(+), 2 deletions(-)
---
diff --git a/pitivi/medialibrary.py b/pitivi/medialibrary.py
index 7485909..9d9a311 100644
--- a/pitivi/medialibrary.py
+++ b/pitivi/medialibrary.py
@@ -306,12 +306,23 @@ class MediaLibraryWidget(Gtk.VBox, Loggable):
         self.pack_start(self.treeview_scrollwin, True, True, 0)
         self.pack_start(self._progressbar, False, True, 0)
 
+    def getAssetForUri(self, uri):
+        # Sanitization
+        uri = filter(lambda c: c != '\n' and c != '\r', uri)
+        for path in self.modelFilter:
+            asset = path[COL_ASSET]
+            info = asset.get_info()
+            asset_uri = info.get_uri()
+            if asset_uri == uri:
+                return asset
+
     def _setup_view_for_drag_and_drop(self, view, target_entries):
         view.drag_source_set(0, [], Gdk.DragAction.COPY)
         view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, target_entries, Gdk.DragAction.COPY)
         view.drag_source_set_target_list([])
         view.drag_source_add_uri_targets()
         view.drag_source_add_text_targets()
+        view.connect("drag-data-get", self._dndDragDataGetCb)
         view.connect("drag_begin", self._dndDragBeginCb)
         view.connect("drag-end", self._dndDragEndCb)
 
@@ -984,6 +995,11 @@ class MediaLibraryWidget(Gtk.VBox, Loggable):
             self.app.current.addUris(filenames)
 
     #used with TreeView and IconView
+    def _dndDragDataGetCb(self, unused_view, context, data, info, timestamp):
+        paths = self.getSelectedPaths()
+        uris = [self.modelFilter[path][COL_URI] for path in paths]
+        data.set_uris(uris)
+
     def _dndDragBeginCb(self, unused_view, context):
         self.info("Drag operation begun")
         self.dragged = True
diff --git a/pitivi/timeline/elements.py b/pitivi/timeline/elements.py
index fd18449..3b70a7b 100644
--- a/pitivi/timeline/elements.py
+++ b/pitivi/timeline/elements.py
@@ -131,6 +131,7 @@ class Ghostclip(Clutter.Actor):
         self.props.width = width
 
     def update(self, priority, y, isControlledByBrother):
+        self.priority = priority
         # Only tricky part of the code, can be called by the linked track element.
         if priority < 0:
             return
@@ -156,7 +157,7 @@ class Ghostclip(Clutter.Actor):
             self.props.visible = True
         else:
             # No need to mockup on the same layer
-            if priority == self.bElement.get_parent().get_layer().get_priority():
+            if self.bElement and priority == self.bElement.get_parent().get_layer().get_priority():
                 self.props.visible = False
             # We would be moving to an existing layer.
             elif priority < self.nbrLayers:
@@ -166,6 +167,13 @@ class Ghostclip(Clutter.Actor):
                     self.props.y += self.nbrLayers * (EXPANDED_SIZE + SPACING)
                 self.props.visible = True
 
+    def getLayerForY(self, y):
+        if self.track_type == GES.TrackType.AUDIO:
+            y -= self.nbrLayers * (EXPANDED_SIZE + SPACING)
+        priority = int(y / (EXPANDED_SIZE + SPACING))
+
+        return priority
+
 
 class TrimHandle(Clutter.Texture):
     def __init__(self, timelineElement, isLeft):
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index 8d0bddf..268b7f6 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -3,6 +3,8 @@ GtkClutter.init([])
 
 from gi.repository import Gst, GES, GObject, Clutter, Gtk, GLib, Gdk
 
+from datetime import datetime
+
 from pitivi.utils.timeline import Zoomable, Selection, UNSELECT
 from pitivi.settings import GlobalSettings
 from pitivi.dialogs.prefs import PreferencesDialog
@@ -12,7 +14,7 @@ from pitivi.utils.widgets import ZoomBox
 from ruler import ScaleRuler
 from gettext import gettext as _
 from pitivi.utils.pipeline import Pipeline
-from elements import ClipElement, TransitionElement
+from elements import ClipElement, TransitionElement, Ghostclip
 from controls import ControlContainer
 
 GlobalSettings.addConfigOption('edgeSnapDeadband',
@@ -40,6 +42,8 @@ PreferencesDialog.addNumericPreference('imageClipLength',
     description=_("Default clip length (in miliseconds) of images when inserting on the timeline."),
     lower=1)
 
+TARGET_TYPE_URI_LIST = 80
+
 # tooltip text for toolbar
 DELETE = _("Delete Selected")
 SPLIT = _("Split clip at playhead position")
@@ -122,6 +126,7 @@ class TimelineStage(Clutter.ScrollActor, Zoomable):
         self.bTimeline = None
         self._container = container
         self.elements = []
+        self.ghostClips = []
         self.selection = Selection()
         self._scroll_point = Clutter.Point()
         self.lastPosition = 0  # Saved for redrawing when paused
@@ -177,8 +182,71 @@ class TimelineStage(Clutter.ScrollActor, Zoomable):
                 return elem
         return None
 
+    # drag and drop from the medialibrary
+
+    def resetGhostClips(self):
+        for ghostCouple in self.ghostClips:
+            for ghostclip in ghostCouple:
+                del ghostclip
+        self.ghostClips = []
+
+    def addGhostClip(self, asset, x, y):
+        ghostAudio = ghostVideo = None
+
+        if asset.get_supported_formats() & GES.TrackType.VIDEO:
+            ghostVideo = self._createGhostclip(GES.TrackType.VIDEO, asset)
+        if asset.get_supported_formats() & GES.TrackType.AUDIO:
+            ghostAudio = self._createGhostclip(GES.TrackType.AUDIO, asset)
+
+        self.ghostClips.append([ghostVideo, ghostAudio])
+
+    def updateGhostClips(self, x, y):
+        for ghostCouple in self.ghostClips:
+            for ghostclip in ghostCouple:
+                if ghostclip is not None:
+                    priority = int(y / (EXPANDED_SIZE + SPACING))
+                    ghostclip.update(priority, y, False)
+                    if x >= 0:
+                        ghostclip.props.x = x
+
+    def convertGhostClips(self):
+        for ghostCouple in self.ghostClips:
+            ghostclip = ghostCouple[0]
+            if not ghostclip:
+                ghostclip = ghostCouple[1]
+
+            layer = None
+            target = None
+            for layer in self.bTimeline.get_layers():
+                if layer.get_priority() == ghostclip.priority:
+                    target = layer
+                    break
+            if target is None:
+                layer = self.bTimeline.append_layer()
+
+            layer.add_asset(ghostclip.asset,
+                            Zoomable.pixelToNs(ghostclip.props.x),
+                            0,
+                            ghostclip.asset.get_duration(),
+                            1.0,
+                            ghostclip.asset.get_supported_formats())
+
+    def removeGhostClips(self):
+        for ghostCouple in self.ghostClips:
+            for ghostclip in ghostCouple:
+                if ghostclip is not None and ghostclip.get_parent():
+                    self.remove_child(ghostclip)
+
     # Internal API
 
+    def _createGhostclip(self, trackType, asset):
+        ghostclip = Ghostclip(trackType)
+        ghostclip.asset = asset
+        ghostclip.setNbrLayers(len(self.bTimeline.get_layers()))
+        ghostclip.setWidth(Zoomable.nsToPixel(asset.get_duration()))
+        self.add_child(ghostclip)
+        return ghostclip
+
     def _connectTrack(self, track):
         track.connect("track-element-added", self._trackElementAddedCb)
         track.connect("track-element-removed", self._trackElementRemovedCb)
@@ -408,6 +476,8 @@ class Timeline(Gtk.VBox, Zoomable):
         self._createUi()
         self._createActions()
 
+        self._setUpDragAndDrop()
+
         self._settings.connect("edgeSnapDeadbandChanged",
                 self._snapDistanceChangedCb)
 
@@ -532,6 +602,21 @@ class Timeline(Gtk.VBox, Zoomable):
         self._packScrollbars(self)
         self.stage.show()
 
+    def _setUpDragAndDrop(self):
+        self.dropHighlight = False
+        self.dropOccured = False
+        self.dropDataReady = False
+        self.dropData = None
+        dnd_list = [Gtk.TargetEntry.new('text/uri-list', Gtk.TargetFlags.OTHER_APP, TARGET_TYPE_URI_LIST)]
+
+        self.drag_dest_set(0, dnd_list, Gdk.DragAction.COPY)
+        self.drag_dest_add_uri_targets()
+
+        self.connect('drag-motion', self._dragMotionCb)
+        self.connect('drag-data-received', self._dragDataReceivedCb)
+        self.connect('drag-drop', self._dragDropCb)
+        self.connect('drag-leave', self._dragLeaveCb)
+
     def _ensureLayer(self):
         """
         Make sure we have a layer in our timeline
@@ -819,6 +904,11 @@ class Timeline(Gtk.VBox, Zoomable):
     def _playPause(self, unused_action):
         self.app.current.pipeline.togglePlayback()
 
+    def _transposeXY(self, x, y):
+        height = self.ruler.get_allocation().height
+        x += self.timeline.get_scroll_point().x
+        return x - CONTROL_WIDTH, y - height
+
     # Interface
 
     # Zoomable
@@ -969,6 +1059,61 @@ class Timeline(Gtk.VBox, Zoomable):
         else:
             self.selection_actions.set_sensitive(False)
 
+    # drag and drop
+
+    def _dragDataReceivedCb(self, widget, context, x, y, data, info, time):
+        if not self.dropDataReady:
+            if data.get_length() > 0:
+                if not self.dropOccured:
+                    self.timeline.resetGhostClips()
+                self.dropData = data.get_data()
+                self.dropDataReady = True
+
+        if self.dropOccured:
+            self.dropOccured = False
+            Gtk.drag_finish(context, True, False, time)
+            self._dragLeaveCb(widget, context, time)
+
+    def _dragDropCb(self, widget, context, x, y, time):
+        target = widget.drag_dest_find_target(context, None)
+        if target.name() == "text/uri-list":
+            self.dropOccured = True
+            widget.drag_get_data(context, target, time)
+            self.timeline.convertGhostClips()
+            return True
+        else:
+            return False
+
+    def _dragMotionCb(self, widget, context, x, y, time):
+        target = widget.drag_dest_find_target(context, None)
+        if target.name() != "text/uri-list":
+            return False
+        if not self.dropDataReady:
+            widget.drag_get_data(context, target, time)
+            Gdk.drag_status(context, 0, time)
+        else:
+            x, y = self._transposeXY(x, y)
+            if not self.timeline.ghostClips:
+                asset = self.app.gui.medialibrary.getAssetForUri(self.dropData)
+                self.timeline.addGhostClip(asset, x, y)
+            self.timeline.updateGhostClips(x, y)
+            Gdk.drag_status(context, Gdk.DragAction.COPY, time)
+            if not self.dropHighlight:
+                widget.drag_highlight()
+                self.dropHighlight = True
+#        Gtk.drag_set_icon_pixbuf(context, self.pixbuf, 0, 0)
+        return True
+
+    def _dragLeaveCb(self, widget, context, time):
+        if self.dropDataReady:
+            self.dropData = None
+            self.dropDataReady = False
+        if self.dropHighlight:
+            widget.drag_unhighlight()
+            self.dropHighlight = False
+
+        self.timeline.removeGhostClips()
+
     # Standalone
 
     # Standalone public API


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]