chiark / gitweb /
Work on playlist right-click menu
[disorder] / disobedience / playlists.c
index 05de973a9577d057bfc1c1175f4269c8dfff3db5..2673e1c526ddb325ab6ced81a212790dc6a00a35 100644 (file)
@@ -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);