#include "popup.h"
#include "queue-generic.h"
-
static const GtkTargetEntry queuelike_targets[] = {
{
- (char *)"text/x-disorder-track-data", /* drag type */
- GTK_TARGET_SAME_WIDGET, /* rearrangement only for now */
+ (char *)"text/x-disorder-queued-tracks", /* drag type */
+ GTK_TARGET_SAME_WIDGET, /* rearrangement within a widget */
0 /* ID value */
},
+ {
+ (char *)"text/x-disorder-playable-tracks", /* drag type */
+ GTK_TARGET_SAME_APP|GTK_TARGET_OTHER_WIDGET, /* copying between widgets */
+ 1 /* ID value */
+ },
};
/* Track detail lookup ----------------------------------------------------- */
// then the lowest-numbered member of the intersection is chosen.
// 3) otherwise no member is chosen and gdk_drag_status() is called
// with action=0 to refuse the drop.
- // Currently we can only accept _MOVE. But in the future we will
- // need to accept _COPY in some cases.
if(dc->suggested_action) {
- if(dc->suggested_action == GDK_ACTION_MOVE)
+ if(dc->suggested_action & (GDK_ACTION_MOVE|GDK_ACTION_COPY))
action = dc->suggested_action;
} else if(dc->actions & GDK_ACTION_MOVE)
- action = dc->actions;
+ action = GDK_ACTION_MOVE;
+ else if(dc->actions & GDK_ACTION_COPY)
+ action = GDK_ACTION_COPY;
/*fprintf(stderr, "suggested %#x actions %#x result %#x\n",
dc->suggested_action, dc->actions, action);*/
if(action) {
// If the action is acceptable then we see if this widget is acceptable
- if(!gtk_drag_dest_find_target(w, dc, NULL))
+ if(gtk_drag_dest_find_target(w, dc, NULL) == GDK_NONE)
action = 0;
}
// Report the status
gtk_tree_selection_selected_foreach(ql->selection,
ql_drag_data_get_collect,
result);
+ // TODO must not be able to drag playing track!
//fprintf(stderr, "drag-data-get: %.*s\n",
// result->nvec, result->vec);
/* gtk_selection_data_set_text() insists that data->target is one of a
struct vector ids[1], tracks[1];
int parity = 0;
- //fprintf(stderr, "drag-data-received: %d,%d\n", x, y);
+ //fprintf(stderr, "drag-data-received: %d,%d info_=%u\n", x, y, info_);
/* Get the selection string */
p = result = (char *)gtk_selection_data_get_text(data);
if(!result) {
break;
}
}
+ /* Guarantee we never drop an empty list */
+ if(!tracks->nvec)
+ return;
/* Note that q->id can match one of ids[]. This doesn't matter for
* moveafter but TODO may matter for playlist support. */
- ql->drop(ql, tracks->nvec, tracks->vec, ids->vec, q);
+ switch(info_) {
+ case 0:
+ /* Rearrangement. Send ID and track data. */
+ ql->drop(ql, tracks->nvec, tracks->vec, ids->vec, q);
+ break;
+ case 1:
+ /* Copying between widgets. IDs mean nothing so don't send them. */
+ ql->drop(ql, tracks->nvec, tracks->vec, NULL, q);
+ break;
+ }
}
/** @brief Initialize a @ref queuelike */
GTK_DEST_DEFAULT_HIGHLIGHT|GTK_DEST_DEFAULT_DROP,
queuelike_targets,
sizeof queuelike_targets / sizeof *queuelike_targets,
- GDK_ACTION_MOVE);
+ GDK_ACTION_MOVE|GDK_ACTION_COPY);
g_signal_connect(ql->view, "drag-begin",
G_CALLBACK(ql_drag_begin), ql);
g_signal_connect(ql->view, "drag-motion",
G_CALLBACK(ql_drag_data_received), ql);
make_treeview_multidrag(ql->view);
} else {
- /* TODO: support copy-dragging out of non-rearrangeable queues. Will need
- * to support copy dropping into the rearrangeable ones. */
+ /* For queues that cannot accept a drop we still accept a copy out */
+ gtk_drag_source_set(ql->view,
+ GDK_BUTTON1_MASK,
+ queuelike_targets,
+ sizeof queuelike_targets / sizeof *queuelike_targets,
+ GDK_ACTION_COPY);
+ g_signal_connect(ql->view, "drag-begin",
+ G_CALLBACK(ql_drag_begin), ql);
+ g_signal_connect(ql->view, "drag-data-get",
+ G_CALLBACK(ql_drag_data_get), ql);
+ make_treeview_multidrag(ql->view);
}
/* TODO style? */
g_timeout_add(1000/*ms*/, playing_periodic, 0);
}
-static void queue_move_completed(void attribute((unused)) *v,
+static void queue_drop_completed(void attribute((unused)) *v,
const char *err) {
if(err) {
popup_protocol_error(0, err);
/** @brief Called when drag+drop completes */
static void queue_drop(struct queuelike attribute((unused)) *ql,
int ntracks,
- char attribute((unused)) **tracks, char **ids,
+ char **tracks, char **ids,
struct queue_entry *after_me) {
int n;
-
- if(playing_track) {
- /* If there's a playing track then you can't drag it anywhere */
- for(n = 0; n < ntracks; ++n) {
- if(!strcmp(playing_track->id, ids[n])) {
- fprintf(stderr, "cannot drag playing track\n");
- return;
+
+ if(ids) {
+ /* Rearrangement */
+ if(playing_track) {
+ /* If there's a playing track then you can't drag it anywhere */
+ for(n = 0; n < ntracks; ++n) {
+ if(!strcmp(playing_track->id, ids[n])) {
+ fprintf(stderr, "cannot drag playing track\n");
+ return;
+ }
}
+ /* You can't tell the server to drag after the playing track by ID, you
+ * have to send "". */
+ if(after_me == playing_track)
+ after_me = NULL;
+ /* If you try to drag before the playing track (i.e. after_me=NULL on
+ * input) then the effect is just to drag after it, although there's no
+ * longer code to explicitly implement this. */
}
- /* You can't tell the server to drag after the playing track by ID, you
+ /* Tell the server to move them. The log will tell us about the change (if
+ * indeed it succeeds!), so no need to rearrange the model now. */
+ disorder_eclient_moveafter(client,
+ after_me ? after_me->id : "",
+ ntracks, (const char **)ids,
+ queue_drop_completed, NULL);
+ } else {
+ /* You can't tell the server to insert after the playing track by ID, you
* have to send "". */
if(after_me == playing_track)
after_me = NULL;
- /* If you try to drag before the playing track (i.e. after_me=NULL on
- * input) then the effect is just to drag after it, although there's no
- * longer code to explicitly implement this. */
+ /* Play the tracks */
+ disorder_eclient_playafter(client,
+ after_me ? after_me->id : "",
+ ntracks, (const char **)tracks,
+ queue_drop_completed, NULL);
}
- /* Tell the server to move them. The log will tell us about the change (if
- * indeed it succeeds!), so no need to rearrange the model now. */
- disorder_eclient_moveafter(client,
- after_me ? after_me->id : "",
- ntracks, (const char **)ids,
- queue_move_completed, NULL);
}
/** @brief Columns for the queue */