X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/7fe2807d48006b2191dfe8b1985176cd378e80c3..dba3eb8e520cf879da73c34ad8d4372eab2c50b6:/disobedience/playlists.c diff --git a/disobedience/playlists.c b/disobedience/playlists.c index 05de973..2673e1c 100644 --- a/disobedience/playlists.c +++ b/disobedience/playlists.c @@ -48,6 +48,12 @@ static void playlist_list_received_playlists(void *v, static void playlist_editor_fill(const char *event, void *eventdata, void *callbackdata); +static int playlist_playall_sensitive(void *extra); +static void playlist_playall_activate(GtkMenuItem *menuitem, + gpointer user_data); +static int playlist_remove_sensitive(void *extra) ; +static void playlist_remove_activate(GtkMenuItem *menuitem, + gpointer user_data); /** @brief Playlist editing window */ static GtkWidget *playlist_window; @@ -60,13 +66,19 @@ static const struct queue_column playlist_columns[] = { { "Length", column_length, 0, COL_RIGHT } }; -/** @brief Pop-up menu for playlist editor */ -// TODO some of these may not be generic enough yet - check! +/** @brief Pop-up menu for playlist editor + * + * Status: + * - track properties works but, bizarrely, raises the main window + * - play track works + * - play playlist works + * - select/deselect all work + */ static struct menuitem playlist_menuitems[] = { { "Track properties", ql_properties_activate, ql_properties_sensitive, 0, 0 }, { "Play track", ql_play_activate, ql_play_sensitive, 0, 0 }, - //{ "Play playlist", ql_playall_activate, ql_playall_sensitive, 0, 0 }, - { "Remove track from queue", ql_remove_activate, ql_remove_sensitive, 0, 0 }, + { "Play playlist", playlist_playall_activate, playlist_playall_sensitive, 0, 0 }, + { "Remove track from queue", playlist_remove_activate, playlist_remove_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 }, }; @@ -144,6 +156,12 @@ static void playlist_list_received_playlists(void attribute((unused)) *v, /* Playlists menu ----------------------------------------------------------- */ +static void playlist_menu_playing(void attribute((unused)) *v, + const char *err) { + if(err) + popup_protocol_error(0, err); +} + /** @brief Play received playlist contents * * Passed as a completion callback by menu_activate_playlist(). @@ -156,7 +174,7 @@ static void playlist_menu_received_content(void attribute((unused)) *v, return; } for(int n = 0; n < nvec; ++n) - disorder_eclient_play(client, vec[n], NULL, NULL); + disorder_eclient_play(client, vec[n], playlist_menu_playing, NULL); } /** @brief Called to activate a playlist @@ -506,6 +524,8 @@ static void playlist_picker_fill(const char attribute((unused)) *event, if(was_selected && !strcmp(was_selected, playlists[n])) gtk_tree_selection_select_iter(playlist_picker_selection, iter); } + /* TODO deselecting then reselecting the current playlist resets the playlist + * editor, which trashes the user's selection. */ } /** @brief Called when the selection might have changed */ @@ -638,13 +658,21 @@ static GtkWidget *playlist_picker_create(void) { /* Playlist editor ---------------------------------------------------------- */ /** @brief Called with new tracks for the playlist */ -static void playlists_editor_received_tracks(void attribute((unused)) *v, +static void playlists_editor_received_tracks(void *v, const char *err, int nvec, char **vec) { + const char *playlist = v; if(err) { popup_protocol_error(0, err); return; } + if(!playlist_picker_selected + || strcmp(playlist, playlist_picker_selected)) { + /* The tracks are for the wrong playlist - something must have changed + * while the fetch command was in flight. We just ignore this callback, + * the right answer will be requested and arrive in due course. */ + return; + } if(nvec == -1) /* No such playlist, presumably we'll get a deleted event shortly */ return; @@ -657,10 +685,8 @@ static void playlists_editor_received_tracks(void attribute((unused)) *v, /* Synthesize a unique ID so that the selection survives updates. Tracks * can appear more than once in the queue so we can't use raw track names, * so we add a serial number to the start. */ - /* TODO but this doesn't work for some reason */ int *serialp = hash_find(h, vec[n]), serial = serialp ? *serialp : 0; byte_xasprintf((char **)&q->id, "%d-%s", serial++, vec[n]); - fprintf(stderr, "%s\n", q->id); hash_add(h, vec[0], &serial, HASH_INSERT_OR_REPLACE); *qq = q; qq = &q->next; @@ -680,7 +706,8 @@ static void playlist_editor_fill(const char attribute((unused)) *event, return; if(!strcmp(playlist_picker_selected, modified_playlist)) disorder_eclient_playlist_get(client, playlists_editor_received_tracks, - playlist_picker_selected, NULL); + playlist_picker_selected, + (void *)playlist_picker_selected); } static GtkWidget *playlists_editor_create(void) { @@ -690,6 +717,57 @@ static GtkWidget *playlists_editor_create(void) { return w; } +/* Playlist editor right-click menu ---------------------------------------- */ + +/** @brief Called to determine whether the playlist is playable */ +static int playlist_playall_sensitive(void attribute((unused)) *extra) { + /* If there's no playlist obviously we can't play it */ + if(!playlist_picker_selected) + return FALSE; + /* If it's empty we can't play it */ + if(!ql_playlist.q) + return FALSE; + /* Otherwise we can */ + return TRUE; +} + +/** @brief Called to play the selected playlist */ +static void playlist_playall_activate(GtkMenuItem attribute((unused)) *menuitem, + gpointer attribute((unused)) user_data) { + if(!playlist_picker_selected) + return; + /* Re-use the menu-based activation callback */ + disorder_eclient_playlist_get(client, playlist_menu_received_content, + playlist_picker_selected, NULL); +} + +/** @brief Called to determine whether the playlist is playable */ +static int playlist_remove_sensitive(void attribute((unused)) *extra) { + /* If there's no playlist obviously we can't remove from it */ + if(!playlist_picker_selected) + return FALSE; + /* If no tracks are selected we cannot remove them */ + if(!gtk_tree_selection_count_selected_rows(ql_playlist.selection)) + return FALSE; + /* We're good to go */ + return TRUE; +} + +/** @brief Called to play the selected playlist */ +static void playlist_remove_activate(GtkMenuItem attribute((unused)) *menuitem, + gpointer attribute((unused)) user_data) { + if(!playlist_picker_selected) + return; + /* To safely remove rows we must: + * - take a lock + * - fetch the playlist + * - delete the selected rows + * - store the playlist + * - release the lock + */ + fprintf(stderr, "remove tracks\n"); /* TODO */ +} + /* Playlists window --------------------------------------------------------- */ /** @brief Keypress handler */ @@ -718,9 +796,9 @@ static void playlist_window_destroyed(GtkWidget attribute((unused)) *widget, * * Called when the playlists menu item is selected */ -void edit_playlists(gpointer attribute((unused)) callback_data, - guint attribute((unused)) callback_action, - GtkWidget attribute((unused)) *menu_item) { +void playlist_window_create(gpointer attribute((unused)) callback_data, + guint attribute((unused)) callback_action, + GtkWidget attribute((unused)) *menu_item) { /* If the window already exists, raise it */ if(playlist_window) { gtk_window_present(GTK_WINDOW(playlist_window)); @@ -756,7 +834,6 @@ void edit_playlists(gpointer attribute((unused)) callback_data, void playlists_init(void) { /* We re-get all playlists upon any change... */ event_register("playlist-created", playlist_list_update, 0); - event_register("playlist-modified", playlist_list_update, 0); /* TODO why? */ event_register("playlist-deleted", playlist_list_update, 0); /* ...and on reconnection */ event_register("log-connected", playlist_list_update, 0);