chiark / gitweb /
Disobedience now keeps track of known playlists and has a (not yet
authorRichard Kettlewell <rjk@greenend.org.uk>
Mon, 4 Aug 2008 20:16:06 +0000 (21:16 +0100)
committerRichard Kettlewell <rjk@greenend.org.uk>
Mon, 4 Aug 2008 20:16:06 +0000 (21:16 +0100)
very functional) menu to launch them.

disobedience/Makefile.am
disobedience/disobedience.c
disobedience/disobedience.h
disobedience/log.c
disobedience/menu.c
disobedience/playlists.c [new file with mode: 0644]

index 08e465c8660a3c99a91a37df361876a749b8f5c3..db74563d23d455b60e48fc2e808391fa080f3fa5 100644 (file)
@@ -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)
index ce94e960256258e6537f9185baa703777e30be8d..dd052adaf0e56364ef1295ce32d06442d38a5472 100644 (file)
@@ -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();
index 91d07de7fa671fd4f0e695bc11da2cb7954a6e9f..bd7b60cf43058ff0505e5929eb071dfc09a8abb3 100644 (file)
@@ -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 */
 
 /*
index 6779cbd3cc4c081a64ba8fb4a8c9693d734ef0a1..9a94e0630cfe3ef9fa13f8dbc177ad8faf681c21 100644 (file)
@@ -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
index 142857c742295197791a7420ad48f6edbf0fb953..63d89bee1ae08a9dcb8aaee5539224f56abf56e2 100644 (file)
@@ -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 *)"<CheckItem>",            /* item_type */
       0                                 /* extra_data */
     },
+    {
+      (char *)"/Control/Activate playlist", /* path */
+      0,                                /* accelerator */
+      0,                                /* callback */
+      0,                                /* callback_action */
+      (char *)"<Branch>",               /* item_type */
+      0                                 /* extra_data */
+    },
     
     {
       (char *)"/Help",                  /* path */
@@ -380,16 +434,22 @@ GtkWidget *menubar(GtkWidget *w) {
                                                 "<GdisorderMain>/Edit/Deselect all tracks");
   properties_widget = gtk_item_factory_get_widget(mainmenufactory,
                                                  "<GdisorderMain>/Edit/Track properties");
+  playlists_widget = gtk_item_factory_get_item(mainmenufactory,
+                                               "<GdisorderMain>/Control/Activate playlist");
+  playlists_menu = gtk_item_factory_get_widget(mainmenufactory,
+                                               "<GdisorderMain>/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,
                                                        "<GdisorderMain>/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,
                                   "<GdisorderMain>");
diff --git a/disobedience/playlists.c b/disobedience/playlists.c
new file mode 100644 (file)
index 0000000..c37f1a2
--- /dev/null
@@ -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:
+*/