chiark / gitweb /
Disobedience: basic support for required/prohibited tags.
authorRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 30 Jul 2011 17:51:36 +0000 (18:51 +0100)
committerRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 30 Jul 2011 17:51:36 +0000 (18:51 +0100)
disobedience/Makefile.am
disobedience/disobedience.h
disobedience/filter.c [new file with mode: 0644]
disobedience/log.c
disobedience/menu.c

index 0f6bcaa4de6fddf5bd602808ca087256a653e004..49a0c4eabeac06b0e6f1a0e74e6c9d6864cf917c 100644 (file)
@@ -28,7 +28,7 @@ disobedience_SOURCES=disobedience.h disobedience.c client.c queue.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 choose.h      \
        popup.h playlists.c multidrag.c multidrag.h autoscroll.c        \
-       autoscroll.h
+       autoscroll.h filter.c
 disobedience_LDADD=../lib/libdisorder.a $(LIBPCRE) $(LIBGC) $(LIBGCRYPT) \
        $(LIBASOUND) $(COREAUDIO) $(LIBDB) $(LIBICONV)
 disobedience_LDFLAGS=$(GTK_LIBS)
index 719eea40dc98931e603f22c27c483e00cb42ec38..81de1f635788615c06257e1e9fa3cc26cdfd0550 100644 (file)
@@ -237,6 +237,11 @@ void manage_users(void);
 
 void popup_help(const char *what);
 
+/* Filtering */
+
+void popup_filtering(void);
+void init_filtering(void);
+
 /* RTP */
 
 int rtp_running(void);
diff --git a/disobedience/filter.c b/disobedience/filter.c
new file mode 100644 (file)
index 0000000..12f6cea
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+/** @file disobedience/filter.c
+ * @brief Track filtering
+ */
+
+#include "disobedience.h"
+
+static GtkWidget *filtering_window;
+
+static struct filter_row {
+  const char *label;
+  const char *pref;
+  GtkWidget *entry;
+} filter_rows[] = {
+  { "Required tags", "required-tags", NULL },
+  { "Prohibited tags", "prohibited-tags", NULL },
+};
+#define NFILTER (sizeof filter_rows / sizeof *filter_rows)
+
+/* Getting values */
+
+/** @brief Called with the latest setting for a row */
+static void filter_get_completed(void *v, const char *err,
+                                const char *value) {
+  if(err)
+    popup_protocol_error(0, err);
+  else if(filtering_window) {
+    struct filter_row *row = v;
+    /* Identify unset and empty lists */
+    if(!value)
+      value = "";
+    /* Skip trivial updates (we'll see one as a consequence of each
+     * update we make...) */
+    if(strcmp(gtk_entry_get_text(GTK_ENTRY(row->entry)), value))
+      gtk_entry_set_text(GTK_ENTRY(row->entry), value);
+  }
+}
+
+/** @brief Retrieve the latest setting for @p row */
+static void filter_get(struct filter_row *row) {
+  disorder_eclient_get_global(client, filter_get_completed, row->pref, row);
+}
+
+/** @brief Called when the user changes the contents of some entry */
+static void filter_entry_changed(GtkEditable *editable, gpointer user_data) {
+  struct filter_row *row = user_data;
+  const char *new_value = gtk_entry_get_text(GTK_ENTRY(editable));
+  if(*new_value)
+    disorder_eclient_set_global(client, NULL, row->pref, new_value, row);
+  else
+    disorder_eclient_unset_global(client, NULL, row->pref, row);
+}
+
+/** @brief Display the filtering window */
+void popup_filtering(void) {
+  GtkWidget *label, *table;
+  /* Pop up the window if it already exists */
+  if(filtering_window) {
+    gtk_window_present(GTK_WINDOW(filtering_window));
+    return;
+  }
+  /* Create the window */
+  /* TODO loads of this is very similar to (copied from!) users.c - can we
+   * de-dupe? */
+  filtering_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_style(filtering_window, tool_style);
+  gtk_window_set_title(GTK_WINDOW(filtering_window), "Filtering");
+  g_signal_connect(filtering_window, "destroy",
+                  G_CALLBACK(gtk_widget_destroyed), &filtering_window);
+  table = gtk_table_new(NFILTER/*rows*/, 2/*cols*/, FALSE/*homogeneous*/);
+  gtk_widget_set_style(table, tool_style);\
+
+  for(size_t n = 0; n < NFILTER; ++n) {
+    label = gtk_label_new(filter_rows[n].label);
+    gtk_widget_set_style(label, tool_style);
+    gtk_misc_set_alignment(GTK_MISC(label), 1/*right*/, 0/*bottom*/);
+    gtk_table_attach(GTK_TABLE(table), label,
+                    0, 1,                /* left/right_attach */
+                    n, n+1,              /* top/bottom_attach */
+                    GTK_FILL, 0,         /* x/yoptions */
+                    1, 1);               /* x/ypadding */
+    filter_rows[n].entry = gtk_entry_new();
+    gtk_widget_set_style(filter_rows[n].entry, tool_style);
+    gtk_table_attach(GTK_TABLE(table), filter_rows[n].entry,
+                    1, 2,                /* left/right_attach */
+                    n, n+1,              /* top/bottom_attach */
+                    GTK_FILL, 0,         /* x/yoptions */
+                    1, 1);               /* x/ypadding */
+    g_signal_connect(filter_rows[n].entry, "changed",
+                    G_CALLBACK(filter_entry_changed), &filter_rows[n]);
+    filter_get(&filter_rows[n]);
+  }
+
+  gtk_container_add(GTK_CONTAINER(filtering_window), frame_widget(table, NULL));
+  gtk_widget_show_all(filtering_window);
+}
+
+/** @brief Called when any global pref changes */
+static void filtering_global_pref_changed(const char *event,
+                                         void *eventdata,
+                                         void *callbackdata) {
+  const char *pref = eventdata;
+  if(!filtering_window)
+    return;                    /* not paying attention */
+  for(size_t n = 0; n < NFILTER; ++n) {
+    if(!strcmp(pref, filter_rows[n].pref))
+      filter_get(&filter_rows[n]);
+  }
+}
+
+/** @brief Initialize filtering infrastructure */
+void init_filtering() {
+  event_register("global-pref", NULL, filtering_global_pref_changed);
+}
index 71d2af04bf32c006e91a4dff7642a251be0d1265..2fd04a10c339acf2aeedabff2aef8295e12a2907 100644 (file)
@@ -48,6 +48,8 @@ static void log_playlist_modified(void *v,
                                   const char *playlist, const char *sharing);
 static void log_playlist_deleted(void *v,
                                  const char *playlist);
+static void log_global_pref(void *v,
+                            const char *name, const char *value);
 
 /** @brief Callbacks for server state monitoring */
 const disorder_eclient_log_callbacks log_callbacks = {
@@ -69,6 +71,7 @@ const disorder_eclient_log_callbacks log_callbacks = {
   .playlist_created = log_playlist_created,
   .playlist_modified = log_playlist_modified,
   .playlist_deleted = log_playlist_deleted,
+  .global_pref = log_global_pref,
 };
 
 /** @brief Update everything */
@@ -238,6 +241,12 @@ static void log_playlist_deleted(void attribute((unused)) *v,
   event_raise("playlist-deleted", (void *)playlist);
 }
 
+static void log_global_pref(void attribute((unused)) *v,
+                            const char *name,
+                            const char attribute((unused)) *value) {
+  event_raise("global-pref", (void *)name);
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index f54c2a39c8eaf69f136ed0b7060db441c2039f19..946361d8c3f5ae898245df2960bfca13645d46c1 100644 (file)
@@ -360,6 +360,14 @@ GtkWidget *menubar(GtkWidget *w) {
       (char *)"<CheckItem>",            /* item_type */
       0                                 /* extra_data */
     },
+    {
+      (char *)"/Control/Filtering",     /* path */
+      (char *)"<CTRL>F",                /* accelerator */
+      popup_filtering,                  /* callback */
+      0,                                /* callback_action */
+      0,                                /* item_type */
+      0                                 /* extra_data */
+    },
     {
       (char *)"/Control/Activate playlist", /* path */
       0,                                /* accelerator */