-/** @brief Called when we enter, or move within, a drop zone */
-static gboolean queue_drag_motion(GtkWidget attribute((unused)) *widget,
- GdkDragContext *drag_context,
- gint attribute((unused)) x,
- gint attribute((unused)) y,
- guint when,
- gpointer user_data) {
- struct queuelike *ql = &ql_queue;
- const char *id = user_data;
- int row;
- struct queue_entry *q = findentry(ql, id, &row);
-
- if(!id || q) {
- if(!ql->dragmark) {
- NW(event_box);
- ql->dragmark = gtk_event_box_new();
- g_signal_connect(ql->dragmark, "destroy",
- G_CALLBACK(gtk_widget_destroyed), &ql->dragmark);
- gtk_widget_set_size_request(ql->dragmark, 10240, row ? 4 : 2);
- gtk_widget_set_name(ql->dragmark, "queue-drag");
- gtk_layout_put(GTK_LAYOUT(ql->mainlayout), ql->dragmark, 0,
- (row + 1) * ql->mainrowheight - !!row);
- } else
- gtk_layout_move(GTK_LAYOUT(ql->mainlayout), ql->dragmark, 0,
- (row + 1) * ql->mainrowheight - !!row);
- gtk_widget_show(ql->dragmark);
- gdk_drag_status(drag_context, GDK_ACTION_MOVE, when);
- return TRUE;
- } else
- /* ID has gone AWOL */
- return FALSE;
-}
-
-/** @brief Called when we leave a drop zone */
-static void queue_drag_leave(GtkWidget attribute((unused)) *widget,
- GdkDragContext attribute((unused)) *drag_context,
- guint attribute((unused)) when,
- gpointer attribute((unused)) user_data) {
- struct queuelike *ql = &ql_queue;
-
- if(ql->dragmark)
- gtk_widget_hide(ql->dragmark);
-}
-
-/** @brief Add a drag target at position @p y
- *
- * @p id is the track to insert the moved tracks after, and might be 0 to
- * insert before the start. */
-static void add_drag_target(struct queuelike *ql, int y, int row,
- const char *id) {
- GtkWidget *eventbox;
-
- assert(ql->dropzones[row] == 0);
- NW(event_box);
- eventbox = gtk_event_box_new();
- /* Make the target zone invisible */
- gtk_event_box_set_visible_window(GTK_EVENT_BOX(eventbox), FALSE);
- /* Make it large enough */
- gtk_widget_set_size_request(eventbox, 10240,
- y ? ql->mainrowheight : ql->mainrowheight / 2);
- /* Position it */
- gtk_layout_put(GTK_LAYOUT(ql->mainlayout), eventbox, 0,
- y ? y - ql->mainrowheight / 2 : 0);
- /* Mark it as capable of receiving drops */
- gtk_drag_dest_set(eventbox,
- 0,
- dragtargets, NDRAGTARGETS, GDK_ACTION_MOVE);
- g_signal_connect(eventbox, "drag-drop",
- G_CALLBACK(queue_drag_drop), (char *)id);
- /* Monitor drag motion */
- g_signal_connect(eventbox, "drag-motion",
- G_CALLBACK(queue_drag_motion), (char *)id);
- g_signal_connect(eventbox, "drag-leave",
- G_CALLBACK(queue_drag_leave), (char *)id);
- /* The widget needs to be shown to receive drags */
- gtk_widget_show(eventbox);
- /* Remember the drag targets */
- ql->dropzones[row] = eventbox;
- g_signal_connect(eventbox, "destroy",
- G_CALLBACK(gtk_widget_destroyed), &ql->dropzones[row]);
-}
-
-/** @brief Create dropzones for dragging into */
-static void add_drag_targets(struct queuelike *ql) {
- int row, y;
- struct queue_entry *q;
-
- /* Create an array to store the widgets */
- ql->dropzones = xcalloc(ql->nrows, sizeof (GtkWidget *));
- y = 0;
- /* Add a drag target before the first row provided it's not the playing
- * track */
- if(!playing_track || ql->q != playing_track)
- add_drag_target(ql, 0, 0, 0);
- /* Put a drag target at the bottom of every row */
- for(q = ql->q, row = 0; q; q = q->next, ++row) {
- y += ql->mainrowheight;
- add_drag_target(ql, y, row, q->id);
- }
-}
-
-/** @brief Remove the dropzones */
-static void remove_drag_targets(struct queuelike *ql) {
- int row;
-
- for(row = 0; row < ql->nrows; ++row) {
- if(ql->dropzones[row]) {
- DW(event_box);
- gtk_widget_destroy(ql->dropzones[row]);
- }
- assert(ql->dropzones[row] == 0);
- }
-}
-
-/* Layout ------------------------------------------------------------------ */
-
-/** @brief Redisplay a queue */
-static void redisplay_queue(struct queuelike *ql) {
- struct queue_entry *q;
- int row, col;
- GList *c, *children;
- const char *name;
- GtkRequisition req;
- GtkWidget *w;
- int maxwidths[NCOLUMNS], x, y, titlerowheight;
- int totalwidth = 10240; /* TODO: can we be less blunt */
-
- D(("redisplay_queue"));
- /* Eliminate all the existing widgets and start from scratch */
- for(c = children = gtk_container_get_children(GTK_CONTAINER(ql->mainlayout));
- c;
- c = c->next) {
- /* Destroy both the label and the eventbox */
- if(GTK_BIN(c->data)->child) {
- DW(label);
- gtk_widget_destroy(GTK_BIN(c->data)->child);
- }
- DW(event_box);
- gtk_widget_destroy(GTK_WIDGET(c->data));
- }
- g_list_free(children);
- /* Adjust the row count */
- for(q = ql->q, ql->nrows = 0; q; q = q->next)
- ++ql->nrows;
- /* We need to create all the widgets before we can position them */
- ql->cells = xcalloc(ql->nrows * (NCOLUMNS + 1), sizeof *ql->cells);
- /* Minimum width is given by the column headings */
- for(col = 0; col < NCOLUMNS; ++col) {
- /* Reset size so we don't inherit last iteration's maximum size */
- gtk_widget_set_size_request(GTK_BIN(ql->titlecells[col])->child, -1, -1);
- gtk_widget_size_request(GTK_BIN(ql->titlecells[col])->child, &req);
- maxwidths[col] = req.width;
- }
- /* Find the vertical size of the title bar */
- gtk_widget_size_request(GTK_BIN(ql->titlecells[0])->child, &req);
- titlerowheight = req.height;
- y = 0;
- if(ql->nrows) {
- /* Construct the widgets */
- for(q = ql->q, row = 0; q; q = q->next, ++row) {
- /* Figure out the widget name for this row */
- if(q == playing_track) name = "row-playing";
- else name = row % 2 ? "row-even" : "row-odd";
- /* Make the widget for each column */
- for(col = 0; col <= NCOLUMNS; ++col) {
- /* Create and store the widget */
- if(col < NCOLUMNS)
- w = get_queue_cell(ql, q, row, col, name, &maxwidths[col]);
- else
- w = get_padding_cell(name);
- ql->cells[row * (NCOLUMNS + 1) + col] = w;
- /* Maybe mark it draggable */
- if(draggable_row(q)) {
- gtk_drag_source_set(w, GDK_BUTTON1_MASK,
- dragtargets, NDRAGTARGETS, GDK_ACTION_MOVE);
- g_signal_connect(w, "drag-begin", G_CALLBACK(queue_drag_begin), q);
- }
- /* Catch button presses */
- g_signal_connect(w, "button-release-event",
- G_CALLBACK(queuelike_button_released), q);
- g_signal_connect(w, "button-press-event",
- G_CALLBACK(queuelike_button_released), q);
- }
- }
- /* ...and of each row in the main layout */
- gtk_widget_size_request(GTK_BIN(ql->cells[0])->child, &req);
- ql->mainrowheight = req.height;
- /* Now we know the maximum width of each column we can set the size of
- * everything and position it */
- for(row = 0, q = ql->q; row < ql->nrows; ++row, q = q->next) {
- x = 0;
- for(col = 0; col < NCOLUMNS; ++col) {
- w = ql->cells[row * (NCOLUMNS + 1) + col];
- gtk_widget_set_size_request(GTK_BIN(w)->child,
- maxwidths[col], -1);
- gtk_layout_put(GTK_LAYOUT(ql->mainlayout), w, x, y);
- x += maxwidths[col];
- }
- w = ql->cells[row * (NCOLUMNS + 1) + col];
- gtk_widget_set_size_request(GTK_BIN(w)->child,
- totalwidth - x, -1);
- gtk_layout_put(GTK_LAYOUT(ql->mainlayout), w, x, y);
- y += ql->mainrowheight;
- }
- }
- /* Titles */
- x = 0;
- for(col = 0; col < NCOLUMNS; ++col) {
- gtk_widget_set_size_request(GTK_BIN(ql->titlecells[col])->child,
- maxwidths[col], -1);
- gtk_layout_move(GTK_LAYOUT(ql->titlelayout), ql->titlecells[col], x, 0);
- x += maxwidths[col];