/*
* This file is part of DisOrder
- * Copyright (C) 2006, 2007 Richard Kettlewell
+ * Copyright (C) 2006-2008 Richard Kettlewell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* These only exist while the drag proceeds, as otherwise they steal events
* from more deserving widgets. (It might work to hide them when not in use
* too but this way around the d+d code is a bit more self-contained.)
+ *
+ * NB that while in the server the playing track is not in the queue, in
+ * Disobedience, the playing does live in @c ql_queue.q, despite its different
+ * status to everything else found in that list.
*/
#include "disobedience.h"
struct queue_menuitem *menuitems; /**< @brief menu items */
GtkWidget *dragmark; /**< @brief drag destination marker */
GtkWidget **dropzones; /**< @brief drag targets */
+ int ndropzones; /**< @brief number of drag targets */
/* State */
struct queue_entry *q; /**< @brief head of queue */
set_widget_states(ql);
}
+/** @brief Deselect all entries in a queue */
+void queue_select_none(struct queuelike *ql) {
+ struct queue_entry *qq;
+
+ for(qq = ql->q; qq; qq = qq->next)
+ selection_set(ql->selection, qq->id, 0);
+ ql->last_click = 0;
+ set_widget_states(ql);
+}
+
/** @brief Pop up properties for selected tracks */
void queue_properties(struct queuelike *ql) {
struct vector v;
gtk_widget_hide(ql->dragmark);
}
-/** @brief Add a drag target at position @p y
+/** @brief Add a drag target
+ * @param ql The queue-like (in practice this is always @ref ql_queue)
+ * @param y The Y coordinate to place the drag target
+ * @param id Track to insert moved tracks after, or NULL
+ *
+ * Adds a drop zone at Y coordinate @p y, which is assumed to lie between two
+ * tracks (or before the start of the queue or after the end of the queue). If
+ * tracks are dragged into this dropzone then they will be moved @em after
+ * track @p id, or to the start of the queue if @p id is NULL.
*
- * @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,
+ * We remember all the dropzones in @c ql->dropzones so they can be destroyed
+ * later.
+ */
+static void add_drag_target(struct queuelike *ql, int y,
const char *id) {
GtkWidget *eventbox;
- assert(ql->dropzones[row] == 0);
NW(event_box);
eventbox = gtk_event_box_new();
/* Make the target zone invisible */
/* The widget needs to be shown to receive drags */
gtk_widget_show(eventbox);
/* Remember the drag targets */
- ql->dropzones[row] = eventbox;
+ ql->dropzones[ql->ndropzones] = eventbox;
g_signal_connect(eventbox, "destroy",
- G_CALLBACK(gtk_widget_destroyed), &ql->dropzones[row]);
+ G_CALLBACK(gtk_widget_destroyed),
+ &ql->dropzones[ql->ndropzones]);
+ ++ql->ndropzones;
}
/** @brief Create dropzones for dragging into */
static void add_drag_targets(struct queuelike *ql) {
- int row, y;
+ int y;
struct queue_entry *q;
/* Create an array to store the widgets */
ql->dropzones = xcalloc(ql->nrows, sizeof (GtkWidget *));
+ ql->ndropzones = 0;
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);
+ add_drag_target(ql, 0, 0);
/* Put a drag target at the bottom of every row */
- for(q = ql->q, row = 0; q; q = q->next, ++row) {
+ for(q = ql->q; q; q = q->next) {
y += ql->mainrowheight;
- add_drag_target(ql, y, row, q->id);
+ add_drag_target(ql, y, q->id);
}
}
/** @brief Remove the dropzones */
static void remove_drag_targets(struct queuelike *ql) {
- int row;
+ int n;
- for(row = 0; row < ql->nrows; ++row) {
- if(ql->dropzones[row]) {
+ for(n = 0; n < ql->ndropzones; ++n) {
+ if(ql->dropzones[n]) {
DW(event_box);
- gtk_widget_destroy(ql->dropzones[row]);
+ gtk_widget_destroy(ql->dropzones[n]);
}
- assert(ql->dropzones[row] == 0);
+ assert(ql->dropzones[n] == 0);
}
}
queue_select_all(mii->ql);
}
+/** @brief Determine whether the select none menu option should be sensitive */
+static int selectnone_sensitive(struct queuelike *ql,
+ struct queue_menuitem attribute((unused)) *m,
+ struct queue_entry attribute((unused)) *q) {
+ /* Sensitive if there is anything selected */
+ return hash_count(ql->selection) != 0;
+}
+
+/** @brief Select no tracks */
+static void selectnone_activate(GtkMenuItem attribute((unused)) *menuitem,
+ gpointer user_data) {
+ const struct menuiteminfo *mii = user_data;
+ queue_select_none(mii->ql);
+}
+
/** @brief Determine whether the play menu option should be sensitive */
static int play_sensitive(struct queuelike *ql,
struct queue_menuitem attribute((unused)) *m,
static struct queue_menuitem queue_menu[] = {
{ "Track properties", properties_activate, properties_sensitive, 0, 0 },
{ "Select all tracks", selectall_activate, selectall_sensitive, 0, 0 },
+ { "Deselect all tracks", selectnone_activate, selectnone_sensitive, 0, 0 },
{ "Scratch track", scratch_activate, scratch_sensitive, 0, 0 },
{ "Remove track from queue", remove_activate, remove_sensitive, 0, 0 },
{ 0, 0, 0, 0, 0 }
static struct queue_menuitem recent_menu[] = {
{ "Track properties", properties_activate, properties_sensitive,0, 0 },
{ "Select all tracks", selectall_activate, selectall_sensitive, 0, 0 },
+ { "Deselect all tracks", selectnone_activate, selectnone_sensitive, 0, 0 },
{ 0, 0, 0, 0, 0 }
};
{ "Track properties", properties_activate, properties_sensitive, 0, 0 },
{ "Play track", play_activate, play_sensitive, 0, 0 },
{ "Select all tracks", selectall_activate, selectall_sensitive, 0, 0 },
+ { "Deselect all tracks", selectnone_activate, selectnone_sensitive, 0, 0 },
{ 0, 0, 0, 0, 0 }
};
return !!queue_count_entries(g_object_get_data(G_OBJECT(w), "queue"));
}
+static int queue_selectnone_sensitive(GtkWidget *w) {
+ struct queuelike *const ql = g_object_get_data(G_OBJECT(w), "queue");
+
+ return hash_count(ql->selection) != 0;
+}
+
static void queue_properties_activate(GtkWidget *w) {
queue_properties(g_object_get_data(G_OBJECT(w), "queue"));
}
queue_select_all(g_object_get_data(G_OBJECT(w), "queue"));
}
+static void queue_selectnone_activate(GtkWidget *w) {
+ queue_select_none(g_object_get_data(G_OBJECT(w), "queue"));
+}
+
static const struct tabtype tabtype_queue = {
queue_properties_sensitive,
queue_selectall_sensitive,
+ queue_selectnone_sensitive,
queue_properties_activate,
queue_selectall_activate,
+ queue_selectnone_activate,
};
/* Other entry points ------------------------------------------------------ */