Check-in [8c7de37e5e]
Overview
| Comment: | Implement basic exporting and conversion for drag-and-drop. Needs to buffer implicit playlist file, because data_get() gets called excessively. Still support direct M3U/PLS/XSPF transfers (should any other application ever understand it), and direct URL transmission. No import functionality yet, but internal JSON format prepared as target type. |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA1: |
8c7de37e5ee837f7e11dd169067f03b0 |
| User & Date: | mario on 2015-04-19 16:36:22 |
| Other Links: | manifest | tags |
Context
|
2015-04-19
| ||
| 19:35 | Add plugin defaults. check-in: 6a17061df0 user: mario tags: trunk | |
| 16:36 | Implement basic exporting and conversion for drag-and-drop. Needs to buffer implicit playlist file, because data_get() gets called excessively. Still support direct M3U/PLS/XSPF transfers (should any other application ever understand it), and direct URL transmission. No import functionality yet, but internal JSON format prepared as target type. check-in: 8c7de37e5e user: mario tags: trunk | |
| 16:33 | Semi-fix for brand-new initialization. Set default category from existing categories[] list. Setting the displayed path as well doesn't work yet. (It's just half-way selected after the next restart.) check-in: 1cfacd1296 user: mario tags: trunk | |
Changes
Modified channels/dnd.py from [fc493ef928] to [2bdcd36106].
| ︙ | ︙ | |||
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# Implements Gtk/X11 drag and drop support for station lists.
# Should allow to export either just stream URLs, or complete
# PLS, XSPF collections.
#
# Also used by the bookmarks tab to move favourites around.
from config import *
from uikit import *
# Drag and Drop support
class dnd(object):
module = "dnd"
meta = plugin_meta()
# Keeps selected row on starting DND event
| > > | > > > > < | | < > > > > > > > > > | | | | | > > > | | > > | < | | | | > | > > > | | | > > > | > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > | | | | > > | | | > > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# Implements Gtk/X11 drag and drop support for station lists.
# Should allow to export either just stream URLs, or complete
# PLS, XSPF collections.
#
# Also used by the bookmarks tab to move favourites around.
import copy
from config import *
from uikit import *
import action
# Drag and Drop support
class dnd(object):
module = "dnd"
meta = plugin_meta()
# Keeps selected row on starting DND event
row = None
# Buffer converted types
buf = {}
# Supported type map
drag_types = [
("json/vnd.streamtuner2.station", 0, 51),
("audio/x-mpegurl", 0, 20),
("application/x-scpls", 0, 21),
("application/xspf+xml", 0, 22),
("FILE_NAME", 0, 3),
("text/uri-list", 0, 4),
("STRING", 0, 5),
("text/plain", 0, 5),
]
cnv_types = {
20: "m3u",
21: "pls",
22: "xspf",
4: "temp",
5: "srv",
51: "json",
}
# Hook to main, and extend channel tabs
def __init__(self, parent):
self.parent = parent
parent.hooks["init"].append(self.add_dnd)
# Attach drag and drop handlers to each channels“ station TreeView
def add_dnd(self, parent):
# visit each module
for cn,module in parent.channels.items():
w = module.gtk_list
# bind SOURCE events
w.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, self.drag_types, gtk.gdk.ACTION_DEFAULT|gtk.gdk.ACTION_COPY|gtk.gdk.ACTION_MOVE)
w.connect('drag-begin', self.begin)
w.connect('drag-data-get', self.data_get)
# bind DESTINATION events
w.enable_model_drag_dest(self.drag_types, gtk.gdk.ACTION_DEFAULT|gtk.gdk.ACTION_COPY)
w.connect('drag-drop', self.drop)#self.drag_types
w.connect('drag-data-received', self.data_received)
# -- SOURCE, drag'n'drop from ST2 to elsewhere --
# Starting to drag a row
def begin(self, widget, context):
__print__(dbg.UI, "dndāsource: begin-drag, store current row")
self.row = self.treelist_row()
self.buf = {}
#context.set_icon_stock("gtk-add", 2, 2)
return "url" in self.row
# Keep currently selected row when source dragging starts
def treelist_row(self):
cn = self.parent.channel()
row = copy.copy(cn.row())
row.setdefault("format", cn.audioformat)
row.setdefault("listformat", cn.listformat)
return row
# Target window/app requests data for offered drop
def data_get(self, widget, context, selection, info, time):
__print__(dbg.UI, "dndāsource: data-get, send and convert to requested target type", info)
# Start new converter if not buffered (because `data_get` gets called mercilessly along the dragging path)
if not info in self.buf:
r = self.row
cnv = action.save_playlist(source=r["listformat"], multiply=False)
# Pass M3U/PLS/XSPF as direct content, or internal JSON even
if info >= 20:
buf = 'set_text', cnv.export(urls=[r["url"]], row=r, dest=self.cnv_types[info])
# Create temporary PLS file, because "text/uri-list" is widely misunderstood and just implemented for file:// IRLs
elif info <= 4:
fn = "{}/{}.pls".format(conf.tmp, re.sub("[^\w-]+", " ", r["title"]))
cnv.file(rows=[r], dest="pls", fn=fn)
if info == 4:
fn = ["file://localhost{}".format(fn)]
buf = 'set_uris', fn
# Text sources are assumed to understand the literal URL, or expect a description
else:
buf = 'set_text', "{url}\n# Title: {title}\n# Homepage: {homepage}".format(**r)
# Buffer
self.buf[info] = buf
# Return prepared data
func, data = self.buf[info]
if func in ('set_text'):
selection.set_text(data)
else:
selection.set_uris(data)
return True
# -- DESTINATION, when playlist/url gets dragged in from other app --
# Just a notification for incoming drop
def drop(self, widget, context, x, y, time):
__print__(dbg.UI, "dndādest: drop-probing", context.targets, x, y, time, context.drag_get_selection())
widget.drag_get_data(context, context.targets[0], time)
return True
# Actual data is being passed,
# now has to be converted and patched into stream rows and channel liststore
def data_received(self, widget, context, x, y, selection, info, time):
__print__(dbg.UI, "dndādest: data-receival", x,y,selection, info, time, selection.get_uris(), selection.get_text())
context.finish(True, False, time)
return True
|