X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/a59bf4da6cdf7b4c5f3ddf359cb69676554ee222..02d50cbd5926b4ef1f88c6f9ca28486fa45508c0:/disobedience/queue-generic.c diff --git a/disobedience/queue-generic.c b/disobedience/queue-generic.c index 05feb5b..333187a 100644 --- a/disobedience/queue-generic.c +++ b/disobedience/queue-generic.c @@ -43,19 +43,6 @@ #include "multidrag.h" #include "autoscroll.h" -static const GtkTargetEntry queuelike_targets[] = { - { - (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 ----------------------------------------------------- */ static void queue_lookups_completed(const char attribute((unused)) *event, @@ -336,6 +323,8 @@ void ql_new_queue(struct queuelike *ql, ql->ncolumns + QUEUEPOINTER_COLUMN, nqd->new, -1); it = gtk_tree_model_iter_next(GTK_TREE_MODEL(ql->store), iter); + /* We'll need the new start time */ + nqd->new->when = q->when; ++kept; } else { /* Delete this row (and move iter to the next one) */ @@ -426,6 +415,8 @@ void ql_new_queue(struct queuelike *ql, nit = gtk_tree_model_iter_next(GTK_TREE_MODEL(ql->store), next); ++searches; } + /* Note that this assertion will fail in the face of duplicate IDs. + * q->id really does need to be unique. */ assert(nit); gtk_list_store_swap(ql->store, iter, next); *iter = *next; @@ -516,6 +507,28 @@ static GtkTreePath *ql_drop_path(GtkWidget *w, return path; } +#if 0 +static const char *act(GdkDragAction action) { + struct dynstr d[1]; + + dynstr_init(d); + if(action & GDK_ACTION_DEFAULT) + dynstr_append_string(d, "|DEFAULT"); + if(action & GDK_ACTION_COPY) + dynstr_append_string(d, "|COPY"); + if(action & GDK_ACTION_MOVE) + dynstr_append_string(d, "|MOVE"); + if(action & GDK_ACTION_LINK) + dynstr_append_string(d, "|LINK"); + if(action & GDK_ACTION_PRIVATE) + dynstr_append_string(d, "|PRIVATE"); + if(action & GDK_ACTION_ASK) + dynstr_append_string(d, "|ASK"); + dynstr_terminate(d); + return d->nvec ? d->vec + 1 : ""; +} +#endif + /** @brief Called when a drag moves within a candidate destination * @param w Destination widget * @param dc Drag context @@ -550,8 +563,13 @@ static gboolean ql_drag_motion(GtkWidget *w, 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);*/ + /* TODO this comes up with the wrong answer sometimes. If we are in the + * middle of a rearrange then the suggested action will be COPY, which we'll + * take, even though MOVE would actually be appropriate. The drag still + * seems to work, but it _is_ wrong. The answer is to take the target into + * account. */ + /*fprintf(stderr, "suggested %s actions %s result %s\n", + act(dc->suggested_action), act(dc->actions), act(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) == GDK_NONE) @@ -634,12 +652,13 @@ static void ql_drag_data_get_collect(GtkTreeModel *model, static void ql_drag_data_get(GtkWidget attribute((unused)) *w, GdkDragContext attribute((unused)) *dc, GtkSelectionData *data, - guint attribute((unused)) info_, + guint attribute((unused)) info, guint attribute((unused)) time_, gpointer user_data) { struct queuelike *const ql = user_data; struct dynstr result[1]; + //fprintf(stderr, "ql_drag_data_get %s info=%d\n", ql->name, info); dynstr_init(result); gtk_tree_selection_selected_foreach(ql->selection, ql_drag_data_get_collect, @@ -672,7 +691,7 @@ static void ql_drag_data_received(GtkWidget attribute((unused)) *w, gint x, gint y, GtkSelectionData *data, - guint attribute((unused)) info_, + guint info_, guint attribute((unused)) time_, gpointer user_data) { struct queuelike *const ql = user_data; @@ -680,7 +699,7 @@ static void ql_drag_data_received(GtkWidget attribute((unused)) *w, struct vector ids[1], tracks[1]; int parity = 0; - //fprintf(stderr, "drag-data-received: %d,%d info_=%u\n", x, y, info_); + //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) { @@ -713,18 +732,21 @@ static void ql_drag_data_received(GtkWidget attribute((unused)) *w, GtkTreePath *path = ql_drop_path(w, GTK_TREE_MODEL(ql->store), x, y, &pos); if(path) { q = ql_path_to_q(GTK_TREE_MODEL(ql->store), path); + //fprintf(stderr, " drop path: %s q=%p pos=%d\n", + // gtk_tree_path_to_string(path), q, pos); } else { /* This generally means a drop past the end of the queue. We find the last * element in the queue and ask to move after that. */ for(q = ql->q; q && q->next; q = q->next) ; + //fprintf(stderr, " after end. q=%p. pos=%d\n", q, pos); } switch(pos) { case GTK_TREE_VIEW_DROP_BEFORE: case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: if(q) { q = q->prev; - //fprintf(stderr, " ...but we like to drop near %s\n", + //fprintf(stderr, " but we like to drop near %s\n", // q ? q->id : "NULL"); } break; @@ -737,11 +759,12 @@ static void ql_drag_data_received(GtkWidget attribute((unused)) *w, /* Note that q->id can match one of ids[]. This doesn't matter for * moveafter but TODO may matter for playlist support. */ switch(info_) { - case 0: - /* Rearrangement. Send ID and track data. */ + case QUEUED_TRACKS_ID: + case PLAYLIST_TRACKS_ID: + /* Rearrangement within some widget. Send ID and track data. */ ql->drop(ql, tracks->nvec, tracks->vec, ids->vec, q); break; - case 1: + case PLAYABLE_TRACKS_ID: /* Copying between widgets. IDs mean nothing so don't send them. */ ql->drop(ql, tracks->nvec, tracks->vec, NULL, q); break; @@ -750,6 +773,14 @@ static void ql_drag_data_received(GtkWidget attribute((unused)) *w, gtk_tree_path_free(path); } +static int count_drag_targets(const GtkTargetEntry *targets) { + const GtkTargetEntry *t = targets; + + while(t->target) + ++t; + return t - targets; +} + /** @brief Initialize a @ref queuelike */ GtkWidget *init_queuelike(struct queuelike *ql) { D(("init_queuelike")); @@ -777,7 +808,7 @@ GtkWidget *init_queuelike(struct queuelike *ql) { (ql->columns[n].name, r, "text", n, - "background", ql->ncolumns + BACKGROUND_COLUMN, + "cell-background", ql->ncolumns + BACKGROUND_COLUMN, "foreground", ql->ncolumns + FOREGROUND_COLUMN, (char *)0); gtk_tree_view_column_set_resizable(c, TRUE); @@ -818,15 +849,15 @@ GtkWidget *init_queuelike(struct queuelike *ql) { /* This view will act as a drag source */ gtk_drag_source_set(ql->view, GDK_BUTTON1_MASK, - queuelike_targets, - sizeof queuelike_targets / sizeof *queuelike_targets, - GDK_ACTION_MOVE); + ql->drag_source_targets, + count_drag_targets(ql->drag_source_targets), + ql->drag_dest_actions); /* This view will act as a drag destination */ gtk_drag_dest_set(ql->view, GTK_DEST_DEFAULT_HIGHLIGHT|GTK_DEST_DEFAULT_DROP, - queuelike_targets, - sizeof queuelike_targets / sizeof *queuelike_targets, - GDK_ACTION_MOVE|GDK_ACTION_COPY); + ql->drag_dest_targets, + count_drag_targets(ql->drag_dest_targets), + ql->drag_dest_actions); g_signal_connect(ql->view, "drag-motion", G_CALLBACK(ql_drag_motion), ql); g_signal_connect(ql->view, "drag-leave", @@ -841,9 +872,9 @@ GtkWidget *init_queuelike(struct queuelike *ql) { /* 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); + ql->drag_source_targets, + count_drag_targets(ql->drag_source_targets), + ql->drag_source_actions); g_signal_connect(ql->view, "drag-data-get", G_CALLBACK(ql_drag_data_get), ql); make_treeview_multidrag(ql->view, NULL);