[pitivi] Drag and drop support from the media library.
- From: Jean-François Fortin Tam <jfft src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi] Drag and drop support from the media library.
- Date: Wed, 24 Apr 2013 18:02:51 +0000 (UTC)
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]