From: Richard Kettlewell Date: Mon, 4 Aug 2008 20:16:06 +0000 (+0100) Subject: Disobedience now keeps track of known playlists and has a (not yet X-Git-Tag: 5.0~86^2~4^2~1^2~3 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/fc36ecb7248f60d2dfc1736ccaeb93d57da57f01 Disobedience now keeps track of known playlists and has a (not yet very functional) menu to launch them. --- diff --git a/disobedience/Makefile.am b/disobedience/Makefile.am index 08e465c..db74563 100644 --- a/disobedience/Makefile.am +++ b/disobedience/Makefile.am @@ -29,7 +29,7 @@ disobedience_SOURCES=disobedience.h disobedience.c client.c queue.c \ recent.c added.c queue-generic.c queue-generic.h queue-menu.c \ choose.c choose-menu.c choose-search.c popup.c misc.c \ control.c properties.c menu.c log.c progress.c login.c rtp.c \ - help.c ../lib/memgc.c settings.c users.c lookup.c + help.c ../lib/memgc.c settings.c users.c lookup.c playlists.c disobedience_LDADD=../lib/libdisorder.a $(LIBPCRE) $(LIBGC) $(LIBGCRYPT) \ $(LIBASOUND) $(COREAUDIO) $(LIBDB) disobedience_LDFLAGS=$(GTK_LIBS) diff --git a/disobedience/disobedience.c b/disobedience/disobedience.c index ce94e96..dd052ad 100644 --- a/disobedience/disobedience.c +++ b/disobedience/disobedience.c @@ -242,6 +242,7 @@ static gboolean periodic_slow(gpointer attribute((unused)) data) { /* Update everything to be sure that the connection to the server hasn't * mysteriously gone stale on us. */ all_update(); + event_raise("periodic-slow", 0); /* Recheck RTP status too */ check_rtp_address(0, 0, 0); return TRUE; /* don't remove me */ @@ -284,6 +285,7 @@ static gboolean periodic_fast(gpointer attribute((unused)) data) { recheck_rights = 0; if(recheck_rights) check_rights(); + event_raise("periodic-fast", 0); return TRUE; } @@ -481,6 +483,7 @@ int main(int argc, char **argv) { disorder_eclient_version(client, version_completed, 0); event_register("log-connected", check_rtp_address, 0); suppress_actions = 0; + playlists_init(); /* If no password is set yet pop up a login box */ if(!config->password) login_box(); diff --git a/disobedience/disobedience.h b/disobedience/disobedience.h index 91d07de..bd7b60c 100644 --- a/disobedience/disobedience.h +++ b/disobedience/disobedience.h @@ -252,6 +252,12 @@ void load_settings(void); void set_tool_colors(GtkWidget *w); void popup_settings(void); +/* Playlists */ + +void playlists_init(void); +extern char **playlists; +extern int nplaylists; + #endif /* DISOBEDIENCE_H */ /* diff --git a/disobedience/log.c b/disobedience/log.c index 6779cbd..9a94e06 100644 --- a/disobedience/log.c +++ b/disobedience/log.c @@ -43,6 +43,12 @@ static void log_state(void *v, unsigned long state); static void log_volume(void *v, int l, int r); static void log_rescanned(void *v); static void log_rights_changed(void *v, rights_type r); +static void log_playlist_created(void *v, + const char *playlist, const char *sharing); +static void log_playlist_modified(void *v, + const char *playlist, const char *sharing); +static void log_playlist_deleted(void *v, + const char *playlist); /** @brief Callbacks for server state monitoring */ const disorder_eclient_log_callbacks log_callbacks = { @@ -59,7 +65,10 @@ const disorder_eclient_log_callbacks log_callbacks = { .state = log_state, .volume = log_volume, .rescanned = log_rescanned, - .rights_changed = log_rights_changed + .rights_changed = log_rights_changed, + .playlist_created = log_playlist_created, + .playlist_modified = log_playlist_modified, + .playlist_deleted = log_playlist_deleted, }; /** @brief Update everything */ @@ -204,6 +213,23 @@ static void log_rights_changed(void attribute((unused)) *v, --suppress_actions; } +static void log_playlist_created(void attribute((unused)) *v, + const char *playlist, + const char attribute((unused)) *sharing) { + event_raise("playlist-created", (void *)playlist); +} + +static void log_playlist_modified(void attribute((unused)) *v, + const char *playlist, + const char attribute((unused)) *sharing) { + event_raise("playlist-modified", (void *)playlist); +} + +static void log_playlist_deleted(void attribute((unused)) *v, + const char *playlist) { + event_raise("playlist-deleted", (void *)playlist); +} + /* Local Variables: c-basic-offset:2 diff --git a/disobedience/menu.c b/disobedience/menu.c index 142857c..63d89be 100644 --- a/disobedience/menu.c +++ b/disobedience/menu.c @@ -26,6 +26,8 @@ static GtkWidget *selectall_widget; static GtkWidget *selectnone_widget; static GtkWidget *properties_widget; +static GtkWidget *playlists_widget; +static GtkWidget *playlists_menu; /** @brief Main menu widgets */ GtkItemFactory *mainmenufactory; @@ -115,7 +117,7 @@ static void edit_menu_show(GtkWidget attribute((unused)) *widget, && t->selectnone_sensitive(t->extra)); } } - + /** @brief Fetch version in order to display the about... popup */ static void about_popup(gpointer attribute((unused)) callback_data, guint attribute((unused)) callback_action, @@ -215,6 +217,41 @@ static void menu_rights_changed(const char attribute((unused)) *event, users_set_sensitive(!!(last_rights & RIGHT_ADMIN)); } +/** @brief Called to activate a playlist */ +static void menu_activate_playlist(GtkMenuItem *menuitem, + gpointer attribute((unused)) user_data) { + GtkLabel *label = GTK_LABEL(GTK_BIN(menuitem)->child); + const char *playlist = gtk_label_get_text(label); + + fprintf(stderr, "activate playlist %s\n", playlist); /* TODO */ +} + +/** @brief Called when the playlists change */ +static void menu_playlists_changed(const char attribute((unused)) *event, + void attribute((unused)) *eventdata, + void attribute((unused)) *callbackdata) { + GtkMenuShell *menu = GTK_MENU_SHELL(playlists_menu); + /* TODO: we could be more sophisticated and only insert/remove widgets as + * needed. For now that's too much effort. */ + while(menu->children) + gtk_container_remove(GTK_CONTAINER(menu), GTK_WIDGET(menu->children->data)); + /* NB nplaylists can be -1 as well as 0 */ + for(int n = 0; n < nplaylists; ++n) { + GtkWidget *w = gtk_menu_item_new_with_label(playlists[n]); + g_signal_connect(w, "activate", G_CALLBACK(menu_activate_playlist), 0); + gtk_widget_show(w); + gtk_menu_shell_append(menu, w); + } + gtk_widget_set_sensitive(playlists_widget, + nplaylists > 0); +} + +static void edit_playlists(gpointer attribute((unused)) callback_data, + guint attribute((unused)) callback_action, + GtkWidget attribute((unused)) *menu_item) { + fprintf(stderr, "edit playlists\n"); /* TODO */ +} + /** @brief Create the menu bar widget */ GtkWidget *menubar(GtkWidget *w) { GtkWidget *m; @@ -295,6 +332,15 @@ GtkWidget *menubar(GtkWidget *w) { 0, /* item_type */ 0 /* extra_data */ }, + { + (char *)"/Edit/Edit playlists", /* path */ + 0, /* accelerator */ + edit_playlists, /* callback */ + 0, /* callback_action */ + 0, /* item_type */ + 0 /* extra_data */ + }, + { (char *)"/Control", /* path */ @@ -336,6 +382,14 @@ GtkWidget *menubar(GtkWidget *w) { (char *)"", /* item_type */ 0 /* extra_data */ }, + { + (char *)"/Control/Activate playlist", /* path */ + 0, /* accelerator */ + 0, /* callback */ + 0, /* callback_action */ + (char *)"", /* item_type */ + 0 /* extra_data */ + }, { (char *)"/Help", /* path */ @@ -380,16 +434,22 @@ GtkWidget *menubar(GtkWidget *w) { "/Edit/Deselect all tracks"); properties_widget = gtk_item_factory_get_widget(mainmenufactory, "/Edit/Track properties"); + playlists_widget = gtk_item_factory_get_item(mainmenufactory, + "/Control/Activate playlist"); + playlists_menu = gtk_item_factory_get_widget(mainmenufactory, + "/Control/Activate playlist"); assert(selectall_widget != 0); assert(selectnone_widget != 0); assert(properties_widget != 0); + assert(playlists_widget != 0); + assert(playlists_menu != 0); - GtkWidget *edit_widget = gtk_item_factory_get_widget(mainmenufactory, "/Edit"); g_signal_connect(edit_widget, "show", G_CALLBACK(edit_menu_show), 0); - + event_register("rights-changed", menu_rights_changed, 0); + event_register("playlists-updated", menu_playlists_changed, 0); users_set_sensitive(0); m = gtk_item_factory_get_widget(mainmenufactory, ""); diff --git a/disobedience/playlists.c b/disobedience/playlists.c new file mode 100644 index 0000000..c37f1a2 --- /dev/null +++ b/disobedience/playlists.c @@ -0,0 +1,107 @@ +/* + * This file is part of DisOrder + * Copyright (C) 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +/** @file disobedience/playlists.c + * @brief Playlist for Disobedience + * + */ +#include "disobedience.h" + +static void playlists_updated(void *v, + const char *err, + int nvec, char **vec); + +/** @brief Current list of playlists or NULL */ +char **playlists; + +/** @brief Count of playlists */ +int nplaylists; + +/** @brief Schedule an update to the list of playlists */ +static void playlists_update(const char attribute((unused)) *event, + void attribute((unused)) *eventdata, + void attribute((unused)) *callbackdata) { + disorder_eclient_playlists(client, 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; + const char *ad = strchr(a, '.'), *bd = strchr(b, '.'); + int c; + + /* Group owned playlists by owner */ + if(ad && bd) { + const int adn = ad - a, bdn = bd - b; + if((c = strncmp(a, b, adn < bdn ? adn : bdn))) + return c; + /* Lexical order within playlists of a single owner */ + return strcmp(ad + 1, bd + 1); + } + + /* Owned playlists after shared ones */ + if(ad) { + return 1; + } else if(bd) { + return -1; + } + + /* Lexical order of shared playlists */ + return strcmp(a, b); +} + +/** @brief Called with a new list of playlists */ +static void playlists_updated(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 Initialize playlist support */ +void playlists_init(void) { + /* We re-get all playlists upon any change... */ + event_register("playlist-created", playlists_update, 0); + event_register("playlist-modified", playlists_update, 0); + event_register("playlist-deleted", playlists_update, 0); + /* ...and on reconnection */ + event_register("log-connected", playlists_update, 0); + /* ...and from time to time */ + event_register("periodic-slow", playlists_update, 0); + /* ...and at startup */ + playlists_update(0, 0, 0); +} + +/* +Local Variables: +c-basic-offset:2 +comment-column:40 +fill-column:79 +indent-tabs-mode:nil +End: +*/