static int playlist_remove_sensitive(void *extra) ;
static void playlist_remove_activate(GtkMenuItem *menuitem,
gpointer user_data);
+static void playlist_new_locked(void *v, const char *err);
+static void playlist_new_retrieved(void *v, const char *err,
+ int nvec,
+ char **vec);
+static void playlist_new_created(void *v, const char *err);
+static void playlist_new_unlocked(void *v, const char *err);
+static void playlist_new_entry_edited(GtkEditable *editable,
+ gpointer user_data);
+static void playlist_new_button_toggled(GtkToggleButton *tb,
+ gpointer userdata);
+static void playlist_new_changed(const char *event,
+ void *eventdata,
+ void *callbackdata);
+static const char *playlist_new_valid(void);
+static void playlist_new_details(char **namep,
+ char **fullnamep,
+ gboolean *sharedp,
+ gboolean *publicp,
+ gboolean *privatep);
+static void playlist_new_ok(GtkButton *button,
+ gpointer userdata);
+static void playlist_new_cancel(GtkButton *button,
+ gpointer userdata);
+static void playlists_editor_received_tracks(void *v,
+ const char *err,
+ int nvec, char **vec);
+static void playlist_window_destroyed(GtkWidget *widget,
+ GtkWidget **widget_pointer);
+static gboolean playlist_window_keypress(GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data);
+static int playlistcmp(const void *ap, const void *bp);
/** @brief Playlist editing window */
static GtkWidget *playlist_window;
disorder_eclient_playlists(client, playlist_list_received_playlists, 0);
}
+/** @brief Called with a new list of playlists */
+static void playlist_list_received_playlists(void attribute((unused)) *v,
+ const char *err,
+ int nvec, char **vec) {
+ if(err) {
+ playlists = 0;
+ nplaylists = -1;
+ /* Probably means server does not support playlists */
+ } else {
+ playlists = vec;
+ nplaylists = nvec;
+ qsort(playlists, nplaylists, sizeof (char *), playlistcmp);
+ }
+ /* Tell our consumers */
+ event_raise("playlists-updated", 0);
+}
+
/** @brief qsort() callback for playlist name comparison */
static int playlistcmp(const void *ap, const void *bp) {
const char *a = *(char **)ap, *b = *(char **)bp;
return strcmp(a, b);
}
-/** @brief Called with a new list of playlists */
-static void playlist_list_received_playlists(void attribute((unused)) *v,
- const char *err,
- int nvec, char **vec) {
- if(err) {
- playlists = 0;
- nplaylists = -1;
- /* Probably means server does not support playlists */
- } else {
- playlists = vec;
- nplaylists = nvec;
- qsort(playlists, nplaylists, sizeof (char *), playlistcmp);
- }
- /* Tell our consumers */
- event_raise("playlists-updated", 0);
-}
-
/* Playlists menu ----------------------------------------------------------- */
static void playlist_menu_playing(void attribute((unused)) *v,
/** @brief "Private" radio button */
static GtkWidget *playlist_new_private;
-/** @brief Get entered new-playlist details
- * @param namep Where to store entered name (or NULL)
- * @param fullnamep Where to store computed full name (or NULL)
- * @param sharep Where to store 'shared' flag (or NULL)
- * @param publicp Where to store 'public' flag (or NULL)
- * @param privatep Where to store 'private' flag (or NULL)
- */
-static void playlist_new_details(char **namep,
- char **fullnamep,
- gboolean *sharedp,
- gboolean *publicp,
- gboolean *privatep) {
- gboolean shared, public, private;
- g_object_get(playlist_new_shared, "active", &shared, (char *)NULL);
- g_object_get(playlist_new_public, "active", &public, (char *)NULL);
- g_object_get(playlist_new_private, "active", &private, (char *)NULL);
- char *gname = gtk_editable_get_chars(GTK_EDITABLE(playlist_new_entry),
- 0, -1); /* name owned by calle */
- char *name = xstrdup(gname);
- g_free(gname);
- if(sharedp) *sharedp = shared;
- if(publicp) *publicp = public;
- if(privatep) *privatep = private;
- if(namep) *namep = name;
- if(fullnamep) {
- if(*sharedp) *fullnamep = *namep;
- else byte_xasprintf(fullnamep, "%s.%s", config->username, name);
+/** @brief Buttons for new-playlist popup */
+static struct button playlist_new_buttons[] = {
+ {
+ .stock = GTK_STOCK_OK,
+ .clicked = playlist_new_ok,
+ .tip = "Create new playlist"
+ },
+ {
+ .stock = GTK_STOCK_CANCEL,
+ .clicked = playlist_new_cancel,
+ .tip = "Do not create new playlist"
}
+};
+#define NPLAYLIST_NEW_BUTTONS (sizeof playlist_new_buttons / sizeof *playlist_new_buttons)
+
+/** @brief Pop up a new window to enter the playlist name and details */
+static void playlist_new_playlist(void) {
+ assert(playlist_new_window == NULL);
+ playlist_new_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ g_signal_connect(playlist_new_window, "destroy",
+ G_CALLBACK(gtk_widget_destroyed), &playlist_new_window);
+ gtk_window_set_title(GTK_WINDOW(playlist_new_window), "Create new playlist");
+ /* Window will be modal, suppressing access to other windows */
+ gtk_window_set_modal(GTK_WINDOW(playlist_new_window), TRUE);
+ gtk_window_set_transient_for(GTK_WINDOW(playlist_new_window),
+ GTK_WINDOW(playlist_window));
+
+ /* Window contents will use a table (grid) layout */
+ GtkWidget *table = gtk_table_new(3, 3, FALSE/*!homogeneous*/);
+
+ /* First row: playlist name */
+ gtk_table_attach_defaults(GTK_TABLE(table),
+ gtk_label_new("Playlist name"),
+ 0, 1, 0, 1);
+ playlist_new_entry = gtk_entry_new();
+ g_signal_connect(playlist_new_entry, "changed",
+ G_CALLBACK(playlist_new_entry_edited), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(table),
+ playlist_new_entry,
+ 1, 3, 0, 1);
+
+ /* Second row: radio buttons to choose type */
+ playlist_new_shared = gtk_radio_button_new_with_label(NULL, "shared");
+ playlist_new_public
+ = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(playlist_new_shared),
+ "public");
+ playlist_new_private
+ = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(playlist_new_shared),
+ "private");
+ g_signal_connect(playlist_new_shared, "toggled",
+ G_CALLBACK(playlist_new_button_toggled), NULL);
+ g_signal_connect(playlist_new_public, "toggled",
+ G_CALLBACK(playlist_new_button_toggled), NULL);
+ g_signal_connect(playlist_new_private, "toggled",
+ G_CALLBACK(playlist_new_button_toggled), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_shared, 0, 1, 1, 2);
+ gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_public, 1, 2, 1, 2);
+ gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_private, 2, 3, 1, 2);
+
+ /* Third row: info bar saying why not */
+ playlist_new_info = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_info,
+ 0, 3, 2, 3);
+
+ /* Fourth row: ok/cancel buttons */
+ GtkWidget *hbox = create_buttons_box(playlist_new_buttons,
+ NPLAYLIST_NEW_BUTTONS,
+ gtk_hbox_new(FALSE, 0));
+ gtk_table_attach_defaults(GTK_TABLE(table), hbox, 0, 3, 3, 4);
+
+ gtk_container_add(GTK_CONTAINER(playlist_new_window),
+ frame_widget(table, NULL));
+
+ /* Set initial state of OK button */
+ playlist_new_changed(0,0,0);
+
+ /* TODO: return should = OK, escape should = cancel */
+
+ /* Display the window */
+ gtk_widget_show_all(playlist_new_window);
}
-/** @brief Called when the newly created playlist has unlocked */
-static void playlist_new_unlocked(void attribute((unused)) *v, const char *err) {
- if(err)
- popup_protocol_error(0, err);
- /* Pop down the creation window */
- gtk_widget_destroy(playlist_new_window);
+/** @brief Called when 'ok' is clicked in new-playlist popup */
+static void playlist_new_ok(GtkButton attribute((unused)) *button,
+ gpointer attribute((unused)) userdata) {
+ gboolean shared, public, private;
+ char *name, *fullname;
+ playlist_new_details(&name, &fullname, &shared, &public, &private);
+
+ /* We need to:
+ * - lock the playlist
+ * - check it doesn't exist
+ * - set sharing (which will create it empty
+ * - unlock it
+ *
+ * TODO we should freeze the window while this is going on to stop a second
+ * click.
+ */
+ disorder_eclient_playlist_lock(client, playlist_new_locked, fullname,
+ fullname);
}
-/** @brief Called when the new playlist has been created */
-static void playlist_new_created(void attribute((unused)) *v, const char *err) {
+/** @brief Called when the proposed new playlist has been locked */
+static void playlist_new_locked(void *v, const char *err) {
+ char *fullname = v;
if(err) {
popup_protocol_error(0, err);
return;
}
- disorder_eclient_playlist_unlock(client, playlist_new_unlocked, NULL);
- // TODO arrange for the new playlist to be selected
+ disorder_eclient_playlist_get(client, playlist_new_retrieved,
+ fullname, fullname);
}
/** @brief Called when the proposed new playlist's contents have been retrieved
fullname);
}
-/** @brief Called when the proposed new playlist has been locked */
-static void playlist_new_locked(void *v, const char *err) {
- char *fullname = v;
+/** @brief Called when the new playlist has been created */
+static void playlist_new_created(void attribute((unused)) *v, const char *err) {
if(err) {
popup_protocol_error(0, err);
return;
}
- disorder_eclient_playlist_get(client, playlist_new_retrieved,
- fullname, fullname);
+ disorder_eclient_playlist_unlock(client, playlist_new_unlocked, NULL);
+ // TODO arrange for the new playlist to be selected
}
-/** @brief Called when 'ok' is clicked in new-playlist popup */
-static void playlist_new_ok(GtkButton attribute((unused)) *button,
- gpointer attribute((unused)) userdata) {
- gboolean shared, public, private;
- char *name, *fullname;
- playlist_new_details(&name, &fullname, &shared, &public, &private);
-
- /* We need to:
- * - lock the playlist
- * - check it doesn't exist
- * - set sharing (which will create it empty
- * - unlock it
- *
- * TODO we should freeze the window while this is going on to stop a second
- * click.
- */
- disorder_eclient_playlist_lock(client, playlist_new_locked, fullname,
- fullname);
+/** @brief Called when the newly created playlist has unlocked */
+static void playlist_new_unlocked(void attribute((unused)) *v, const char *err) {
+ if(err)
+ popup_protocol_error(0, err);
+ /* Pop down the creation window */
+ gtk_widget_destroy(playlist_new_window);
}
/** @brief Called when 'cancel' is clicked in new-playlist popup */
gtk_widget_destroy(playlist_new_window);
}
-/** @brief Buttons for new-playlist popup */
-static struct button playlist_new_buttons[] = {
- {
- .stock = GTK_STOCK_OK,
- .clicked = playlist_new_ok,
- .tip = "Create new playlist"
- },
- {
- .stock = GTK_STOCK_CANCEL,
- .clicked = playlist_new_cancel,
- .tip = "Do not create new playlist"
- }
-};
-#define NPLAYLIST_NEW_BUTTONS (sizeof playlist_new_buttons / sizeof *playlist_new_buttons)
+/** @brief Called when some radio button in the new-playlist popup changes */
+static void playlist_new_button_toggled(GtkToggleButton attribute((unused)) *tb,
+ gpointer attribute((unused)) userdata) {
+ playlist_new_changed(0,0,0);
+}
+
+/** @brief Called when the text entry field in the new-playlist popup changes */
+static void playlist_new_entry_edited(GtkEditable attribute((unused)) *editable,
+ gpointer attribute((unused)) user_data) {
+ playlist_new_changed(0,0,0);
+}
+
+/** @brief Called to update new playlist window state
+ *
+ * This is called whenever one the text entry or radio buttons changed, and
+ * also when the set of known playlists changes. It determines whether the new
+ * playlist would be creatable and sets the sensitivity of the OK button
+ * and info display accordingly.
+ */
+static void playlist_new_changed(const char attribute((unused)) *event,
+ void attribute((unused)) *eventdata,
+ void attribute((unused)) *callbackdata) {
+ if(!playlist_new_window)
+ return;
+ const char *reason = playlist_new_valid();
+ gtk_widget_set_sensitive(playlist_new_buttons[0].widget,
+ !reason);
+ gtk_label_set_text(GTK_LABEL(playlist_new_info), reason);
+}
/** @brief Test whether the new-playlist window settings are valid
* @return NULL on success or an error string if not
return NULL;
}
-/** @brief Called to update new playlist window state
- *
- * This is called whenever one the text entry or radio buttons changed, and
- * also when the set of known playlists changes. It determines whether the new
- * playlist would be creatable and sets the sensitivity of the OK button
- * and info display accordingly.
+/** @brief Get entered new-playlist details
+ * @param namep Where to store entered name (or NULL)
+ * @param fullnamep Where to store computed full name (or NULL)
+ * @param sharep Where to store 'shared' flag (or NULL)
+ * @param publicp Where to store 'public' flag (or NULL)
+ * @param privatep Where to store 'private' flag (or NULL)
*/
-static void playlist_new_changed(const char attribute((unused)) *event,
- void attribute((unused)) *eventdata,
- void attribute((unused)) *callbackdata) {
- if(!playlist_new_window)
- return;
- const char *reason = playlist_new_valid();
- gtk_widget_set_sensitive(playlist_new_buttons[0].widget,
- !reason);
- gtk_label_set_text(GTK_LABEL(playlist_new_info), reason);
-}
-
-/** @brief Called when some radio button in the new-playlist popup changes */
-static void playlist_new_button_toggled(GtkToggleButton attribute((unused)) tb,
- gpointer attribute((unused)) userdata) {
- playlist_new_changed(0,0,0);
-}
-
-/** @brief Called when the text entry field in the new-playlist popup changes */
-static void playlist_new_entry_edited(GtkEditable attribute((unused)) *editable,
- gpointer attribute((unused)) user_data) {
- playlist_new_changed(0,0,0);
-}
-
-/** @brief Pop up a new window to enter the playlist name and details */
-static void playlist_new_playlist(void) {
- assert(playlist_new_window == NULL);
- playlist_new_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- g_signal_connect(playlist_new_window, "destroy",
- G_CALLBACK(gtk_widget_destroyed), &playlist_new_window);
- gtk_window_set_title(GTK_WINDOW(playlist_new_window), "Create new playlist");
- /* Window will be modal, suppressing access to other windows */
- gtk_window_set_modal(GTK_WINDOW(playlist_new_window), TRUE);
- gtk_window_set_transient_for(GTK_WINDOW(playlist_new_window),
- GTK_WINDOW(playlist_window));
-
- /* Window contents will use a table (grid) layout */
- GtkWidget *table = gtk_table_new(3, 3, FALSE/*!homogeneous*/);
-
- /* First row: playlist name */
- gtk_table_attach_defaults(GTK_TABLE(table),
- gtk_label_new("Playlist name"),
- 0, 1, 0, 1);
- playlist_new_entry = gtk_entry_new();
- g_signal_connect(playlist_new_entry, "changed",
- G_CALLBACK(playlist_new_entry_edited), NULL);
- gtk_table_attach_defaults(GTK_TABLE(table),
- playlist_new_entry,
- 1, 3, 0, 1);
-
- /* Second row: radio buttons to choose type */
- playlist_new_shared = gtk_radio_button_new_with_label(NULL, "shared");
- playlist_new_public
- = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(playlist_new_shared),
- "public");
- playlist_new_private
- = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(playlist_new_shared),
- "private");
- g_signal_connect(playlist_new_shared, "toggled",
- G_CALLBACK(playlist_new_button_toggled), NULL);
- g_signal_connect(playlist_new_public, "toggled",
- G_CALLBACK(playlist_new_button_toggled), NULL);
- g_signal_connect(playlist_new_private, "toggled",
- G_CALLBACK(playlist_new_button_toggled), NULL);
- gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_shared, 0, 1, 1, 2);
- gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_public, 1, 2, 1, 2);
- gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_private, 2, 3, 1, 2);
-
- /* Third row: info bar saying why not */
- playlist_new_info = gtk_label_new("");
- gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_info,
- 0, 3, 2, 3);
-
- /* Fourth row: ok/cancel buttons */
- GtkWidget *hbox = create_buttons_box(playlist_new_buttons,
- NPLAYLIST_NEW_BUTTONS,
- gtk_hbox_new(FALSE, 0));
- gtk_table_attach_defaults(GTK_TABLE(table), hbox, 0, 3, 3, 4);
-
- gtk_container_add(GTK_CONTAINER(playlist_new_window),
- frame_widget(table, NULL));
-
- /* Set initial state of OK button */
- playlist_new_changed(0,0,0);
-
- /* TODO: return should = OK, escape should = cancel */
-
- /* Display the window */
- gtk_widget_show_all(playlist_new_window);
+static void playlist_new_details(char **namep,
+ char **fullnamep,
+ gboolean *sharedp,
+ gboolean *publicp,
+ gboolean *privatep) {
+ gboolean shared, public, private;
+ g_object_get(playlist_new_shared, "active", &shared, (char *)NULL);
+ g_object_get(playlist_new_public, "active", &public, (char *)NULL);
+ g_object_get(playlist_new_private, "active", &private, (char *)NULL);
+ char *gname = gtk_editable_get_chars(GTK_EDITABLE(playlist_new_entry),
+ 0, -1); /* name owned by calle */
+ char *name = xstrdup(gname);
+ g_free(gname);
+ if(sharedp) *sharedp = shared;
+ if(publicp) *publicp = public;
+ if(privatep) *privatep = private;
+ if(namep) *namep = name;
+ if(fullnamep) {
+ if(*sharedp) *fullnamep = *namep;
+ else byte_xasprintf(fullnamep, "%s.%s", config->username, name);
+ }
}
/* Playlist picker ---------------------------------------------------------- */
/* Playlist editor ---------------------------------------------------------- */
+static GtkWidget *playlists_editor_create(void) {
+ assert(ql_playlist.view == NULL); /* better not be set up already */
+ GtkWidget *w = init_queuelike(&ql_playlist);
+ /* Initially empty */
+ return w;
+}
+
+/** @brief (Re-)populate the playlist tree model */
+static void playlist_editor_fill(const char attribute((unused)) *event,
+ void *eventdata,
+ void attribute((unused)) *callbackdata) {
+ const char *modified_playlist = eventdata;
+ if(!playlist_window)
+ return;
+ if(!playlist_picker_selected)
+ return;
+ if(!strcmp(playlist_picker_selected, modified_playlist))
+ disorder_eclient_playlist_get(client, playlists_editor_received_tracks,
+ playlist_picker_selected,
+ (void *)playlist_picker_selected);
+}
+
/** @brief Called with new tracks for the playlist */
static void playlists_editor_received_tracks(void *v,
const char *err,
ql_new_queue(&ql_playlist, newq);
}
-/** @brief (Re-)populate the playlist tree model */
-static void playlist_editor_fill(const char attribute((unused)) *event,
- void *eventdata,
- void attribute((unused)) *callbackdata) {
- const char *modified_playlist = eventdata;
- if(!playlist_window)
- return;
- if(!playlist_picker_selected)
- return;
- if(!strcmp(playlist_picker_selected, modified_playlist))
- disorder_eclient_playlist_get(client, playlists_editor_received_tracks,
- playlist_picker_selected,
- (void *)playlist_picker_selected);
-}
-
-static GtkWidget *playlists_editor_create(void) {
- assert(ql_playlist.view == NULL); /* better not be set up already */
- GtkWidget *w = init_queuelike(&ql_playlist);
- /* Initially empty */
- return w;
-}
-
/* Playlist editor right-click menu ---------------------------------------- */
/** @brief Called to determine whether the playlist is playable */
/* Playlists window --------------------------------------------------------- */
-/** @brief Keypress handler */
-static gboolean playlist_window_keypress(GtkWidget attribute((unused)) *widget,
- GdkEventKey *event,
- gpointer attribute((unused)) user_data) {
- if(event->state)
- return FALSE;
- switch(event->keyval) {
- case GDK_Escape:
- gtk_widget_destroy(playlist_window);
- return TRUE;
- default:
- return FALSE;
- }
-}
-
-/** @brief Called when the playlist window is destroyed */
-static void playlist_window_destroyed(GtkWidget attribute((unused)) *widget,
- GtkWidget **widget_pointer) {
- destroy_queuelike(&ql_playlist);
- *widget_pointer = NULL;
-}
-
/** @brief Pop up the playlists window
*
* Called when the playlists menu item is selected
gtk_widget_show_all(playlist_window);
}
+/** @brief Keypress handler */
+static gboolean playlist_window_keypress(GtkWidget attribute((unused)) *widget,
+ GdkEventKey *event,
+ gpointer attribute((unused)) user_data) {
+ if(event->state)
+ return FALSE;
+ switch(event->keyval) {
+ case GDK_Escape:
+ gtk_widget_destroy(playlist_window);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/** @brief Called when the playlist window is destroyed */
+static void playlist_window_destroyed(GtkWidget attribute((unused)) *widget,
+ GtkWidget **widget_pointer) {
+ destroy_queuelike(&ql_playlist);
+ *widget_pointer = NULL;
+}
+
/** @brief Initialize playlist support */
void playlists_init(void) {
/* We re-get all playlists upon any change... */