/** @brief When we last got the playing track */
time_t last_playing;
+static void queue_completed(void *v,
+ const char *error,
+ struct queue_entry *q);
+static void playing_completed(void *v,
+ const char *error,
+ struct queue_entry *q);
+
/** @brief Called when either the actual queue or the playing track change */
static void queue_playing_changed(void) {
- struct queue_entry *q = xmalloc(sizeof *q);
- *q = *actual_playing_track;
- q->next = actual_queue;
- playing_track = q;
+ /* Check that the playing track isn't in the queue. There's a race here due
+ * to the fact that we issue the two commands at slightly different times.
+ * If it goes wrong we re-issue and try again, so that we never offer up an
+ * inconsistent state. */
+ if(actual_playing_track) {
+ struct queue_entry *q;
+ for(q = actual_queue; q; q = q->next)
+ if(!strcmp(q->id, actual_playing_track->id))
+ break;
+ if(q) {
+ disorder_eclient_playing(client, playing_completed, 0);
+ disorder_eclient_queue(client, queue_completed, 0);
+ return;
+ }
+ }
+
+ struct queue_entry *q = xmalloc(sizeof *q);
+ if(actual_playing_track) {
+ *q = *actual_playing_track;
+ q->next = actual_queue;
+ playing_track = q;
+ } else {
+ playing_track = NULL;
+ q = actual_queue;
+ }
time(&last_playing); /* for column_length() */
ql_new_queue(&ql_queue, q);
/* Tell anyone who cares */
static void queue_init(void) {
/* Arrange a callback whenever the playing state changes */
event_register("playing-changed", playing_changed, 0);
+ /* We reget both playing track and queue at pause/resume so that start times
+ * can be computed correctly */
event_register("pause-changed", playing_changed, 0);
- /* ...and when the queue changes */
+ event_register("pause-changed", queue_changed, 0);
+ /* Reget the queue whenever it changes */
event_register("queue-changed", queue_changed, 0);
/* ...and once a second anyway */
g_timeout_add(1000/*ms*/, playing_periodic, 0);
/** @brief Columns for the queue */
static const struct queue_column queue_columns[] = {
- { "When", column_when, 0, 1 },
+ { "When", column_when, 0, COL_RIGHT },
{ "Who", column_who, 0, 0 },
- { "Artist", column_namepart, "artist", 0 },
- { "Album", column_namepart, "album", 0 },
- { "Title", column_namepart, "title", 0 },
- { "Length", column_length, 0, 1 }
+ { "Artist", column_namepart, "artist", COL_EXPAND|COL_ELLIPSIZE },
+ { "Album", column_namepart, "album", COL_EXPAND|COL_ELLIPSIZE },
+ { "Title", column_namepart, "title", COL_EXPAND|COL_ELLIPSIZE },
+ { "Length", column_length, 0, COL_RIGHT }
};
/** @brief Pop-up menu for queue */
{ "Track properties", ql_properties_activate, ql_properties_sensitive, 0, 0 },
{ "Select all tracks", ql_selectall_activate, ql_selectall_sensitive, 0, 0 },
{ "Deselect all tracks", ql_selectnone_activate, ql_selectnone_sensitive, 0, 0 },
- { "Scratch track", ql_scratch_activate, ql_scratch_sensitive, 0, 0 },
+ { "Scratch playing track", ql_scratch_activate, ql_scratch_sensitive, 0, 0 },
{ "Remove track from queue", ql_remove_activate, ql_remove_sensitive, 0, 0 },
};
struct queuelike ql_queue = {
+ .name = "queue",
.init = queue_init,
.columns = queue_columns,
.ncolumns = sizeof queue_columns / sizeof *queue_columns,
.menuitems = queue_menuitems,
- .nmenuitems = sizeof queue_menuitems / sizeof *queue_menuitems,
+ .nmenuitems = sizeof queue_menuitems / sizeof *queue_menuitems
};
GtkWidget *queue_widget(void) {