chiark / gitweb /
Start on popup menu for Disobedience choose tab. Mostly this is
authorRichard Kettlewell <rjk@greenend.org.uk>
Thu, 12 Jun 2008 10:18:39 +0000 (11:18 +0100)
committerRichard Kettlewell <rjk@greenend.org.uk>
Thu, 12 Jun 2008 10:18:39 +0000 (11:18 +0100)
restructing; the per-tab callbacks for the edit menu are now shared
with the popup menu, eliminating a lot of fiddly shims, and various
things are split out into their own files.

14 files changed:
disobedience/Makefile.am
disobedience/added.c
disobedience/choose-menu.c [new file with mode: 0644]
disobedience/choose.c
disobedience/choose.h [new file with mode: 0644]
disobedience/disobedience.h
disobedience/menu.c
disobedience/popup.c [new file with mode: 0644]
disobedience/popup.h [new file with mode: 0644]
disobedience/queue-generic.c
disobedience/queue-generic.h
disobedience/queue-menu.c
disobedience/queue.c
disobedience/recent.c

index 93d7096..ab085f2 100644 (file)
@@ -27,8 +27,9 @@ PNGS:=$(shell export LC_COLLATE=C;echo ${top_srcdir}/images/*.png)
 
 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 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
+       choose.c choose-menu.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
 disobedience_LDADD=../lib/libdisorder.a $(LIBPCRE) $(LIBGC) $(LIBGCRYPT) \
        $(LIBASOUND) $(COREAUDIO) $(LIBDB)
 disobedience_LDFLAGS=$(GTK_LIBS)
index 038c439..e3c41c2 100644 (file)
@@ -18,6 +18,7 @@
  * USA
  */
 #include "disobedience.h"
+#include "popup.h"
 #include "queue-generic.h"
 
 /** @brief Called with an updated list of newly-added tracks
@@ -77,7 +78,7 @@ static const struct queue_column added_columns[] = {
 };
 
 /** @brief Pop-up menu for new tracks list */
-static struct queue_menuitem added_menuitems[] = {
+static struct menuitem added_menuitems[] = {
   { "Track properties", ql_properties_activate, ql_properties_sensitive, 0, 0 },
   { "Play track", ql_play_activate, ql_play_sensitive, 0, 0 },
   { "Select all tracks", ql_selectall_activate, ql_selectall_sensitive, 0, 0 },
diff --git a/disobedience/choose-menu.c b/disobedience/choose-menu.c
new file mode 100644 (file)
index 0000000..8ba78e7
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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
+ */
+#include "disobedience.h"
+#include "popup.h"
+#include "choose.h"
+
+/** @brief Popup menu */
+static GtkWidget *choose_menu;
+
+static int choose_selectall_sensitive(void attribute((unused)) *extra) {
+  return TRUE;
+}
+  
+static void choose_selectall_activate(GtkMenuItem attribute((unused)) *item,
+                                      gpointer attribute((unused)) userdata) {
+  gtk_tree_selection_select_all(choose_selection);
+}
+  
+static int choose_selectnone_sensitive(void attribute((unused)) *extra) {
+  return gtk_tree_selection_count_selected_rows(choose_selection) > 0;
+}
+  
+static void choose_selectnone_activate(GtkMenuItem attribute((unused)) *item,
+                                       gpointer attribute((unused)) userdata) {
+  gtk_tree_selection_unselect_all(choose_selection);
+}
+  
+static int choose_play_sensitive(void attribute((unused)) *extra) {
+  return FALSE;                 /* TODO */
+}
+  
+static void choose_play_activate(GtkMenuItem attribute((unused)) *item,
+                                 gpointer attribute((unused)) userdata) {
+  /* TODO */
+}
+  
+static int choose_properties_sensitive(void attribute((unused)) *extra) {
+  return FALSE;                 /* TODO */
+}
+  
+static void choose_properties_activate(GtkMenuItem attribute((unused)) *item,
+                                       gpointer attribute((unused)) userdata) {
+  /* TODO */
+}
+
+/** @brief Pop-up menu for choose */
+static struct menuitem choose_menuitems[] = {
+  {
+    "Play track",
+    choose_play_activate,
+    choose_play_sensitive,
+    0,
+    0
+  },
+  {
+    "Track properties",
+    choose_properties_activate,
+    choose_properties_sensitive,
+    0,
+    0
+  },
+  {
+    "Select all tracks",
+    choose_selectall_activate,
+    choose_selectall_sensitive,
+    0,
+    0
+  },
+  {
+    "Deselect all tracks",
+    choose_selectnone_activate,
+    choose_selectnone_sensitive,
+    0,
+    0
+  },
+};
+
+const struct tabtype choose_tabtype = {
+  choose_properties_sensitive,
+  choose_selectall_sensitive,
+  choose_selectnone_sensitive,
+  choose_properties_activate,
+  choose_selectall_activate,
+  choose_selectnone_activate,
+  0,
+  0
+};
+
+/** @brief Called when a mouse button is pressed or released */
+gboolean choose_button_event(GtkWidget attribute((unused)) *widget,
+                             GdkEventButton *event,
+                             gpointer attribute((unused)) user_data) {
+  if(event->type == GDK_BUTTON_RELEASE && event->button == 2) {
+    /* Middle click release - play track */
+    //ensure_selected(choose_view, event);
+    /* TODO */
+  } else if(event->type == GDK_BUTTON_PRESS && event->button == 3) {
+    /* Right click press - pop up the menu */
+    ensure_selected(GTK_TREE_VIEW(choose_view), event);
+    popup(&choose_menu, event,
+          choose_menuitems, sizeof choose_menuitems / sizeof *choose_menuitems,
+          0);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 569b8b7..1a51a06 100644 (file)
  * TODO:
  * - sweep up contracted nodes
  * - update when content may have changed (e.g. after a rescan)
- * - popup menu
+ * - popup menu (partially implemented now)
  * - playing state
  * - display length of tracks
  */
 
 #include "disobedience.h"
-
-/** @brief Extra data at each node */
-struct choosedata {
-  /** @brief Node type */
-  int type;
-
-  /** @brief Full track or directory name */
-  gchar *track;
-
-  /** @brief Sort key */
-  gchar *sort;
-};
-
-/** @brief Track name column number */
-#define NAME_COLUMN 0
-
-/** @brief Hidden column number */
-#define CHOOSEDATA_COLUMN 1
-
-/** @brief @ref choosedata node is a file */
-#define CHOOSE_FILE 0
-
-/** @brief @ref choosedata node is a directory */
-#define CHOOSE_DIRECTORY 1
+#include "choose.h"
 
 /** @brief The current selection tree */
-static GtkTreeStore *choose_store;
+GtkTreeStore *choose_store;
 
 /** @brief The view onto the selection tree */
-static GtkWidget *choose_view;
+GtkWidget *choose_view;
 
 /** @brief The selection tree's selection */
-static GtkTreeSelection *choose_selection;
-
-/** @brief Popup menu */
-//static GtkWidget *choose_menu;
+GtkTreeSelection *choose_selection;
 
 /** @brief Map choosedata types to names */
 static const char *const choose_type_map[] = { "track", "dir" };
 
 /** @brief Return the choosedata given an interator */
-static struct choosedata *choose_iter_to_data(GtkTreeIter *iter) {
+struct choosedata *choose_iter_to_data(GtkTreeIter *iter) {
   GValue v[1];
   memset(v, 0, sizeof v);
   gtk_tree_model_get_value(GTK_TREE_MODEL(choose_store), iter, CHOOSEDATA_COLUMN, v);
@@ -275,41 +249,6 @@ static void choose_row_expanded(GtkTreeView attribute((unused)) *treeview,
   /* The row references are destroyed in the _completed handlers. */
 }
 
-static int choose_tab_selectall_sensitive(void attribute((unused)) *extra) {
-  return TRUE;
-}
-  
-static void choose_tab_selectall_activate(void attribute((unused)) *extra) {
-  gtk_tree_selection_select_all(choose_selection);
-}
-  
-static int choose_tab_selectnone_sensitive(void attribute((unused)) *extra) {
-  return gtk_tree_selection_count_selected_rows(choose_selection) > 0;
-}
-  
-static void choose_tab_selectnone_activate(void attribute((unused)) *extra) {
-  gtk_tree_selection_unselect_all(choose_selection);
-}
-  
-static int choose_tab_properties_sensitive(void attribute((unused)) *extra) {
-  return TRUE;
-}
-  
-static void choose_tab_properties_activate(void attribute((unused)) *extra) {
-  fprintf(stderr, "TODO choose_tab_properties_activate\n");
-}
-
-static const struct tabtype choose_tabtype = {
-  choose_tab_properties_sensitive,
-  choose_tab_selectall_sensitive,
-  choose_tab_selectnone_sensitive,
-  choose_tab_properties_activate,
-  choose_tab_selectall_activate,
-  choose_tab_selectnone_activate,
-  0,
-  0
-};
-
 /** @brief Create the choose tab */
 GtkWidget *choose_widget(void) {
   /* Create the tree store. */
@@ -334,8 +273,10 @@ GtkWidget *choose_widget(void) {
   gtk_tree_selection_set_mode(choose_selection, GTK_SELECTION_MULTIPLE);
 
   /* Catch button presses */
-  /*g_signal_connect(choose_view, "button-press-event",
-    G_CALLBACK(choose_button_release), 0);*/
+  g_signal_connect(choose_view, "button-press-event",
+                   G_CALLBACK(choose_button_event), 0);
+  g_signal_connect(choose_view, "button-release-event",
+                   G_CALLBACK(choose_button_event), 0);
   /* Catch row expansions so we can fill in placeholders */
   g_signal_connect(choose_view, "row-expanded",
                    G_CALLBACK(choose_row_expanded), 0);
diff --git a/disobedience/choose.h b/disobedience/choose.h
new file mode 100644 (file)
index 0000000..cb968c5
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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
+ */
+#ifndef CHOOSE_H
+#define CHOOSE_H
+
+/** @brief Extra data at each node */
+struct choosedata {
+  /** @brief Node type */
+  int type;
+
+  /** @brief Full track or directory name */
+  gchar *track;
+
+  /** @brief Sort key */
+  gchar *sort;
+};
+
+/** @brief Track name column number */
+#define NAME_COLUMN 0
+
+/** @brief Hidden column number */
+#define CHOOSEDATA_COLUMN 1
+
+/** @brief @ref choosedata node is a file */
+#define CHOOSE_FILE 0
+
+/** @brief @ref choosedata node is a directory */
+#define CHOOSE_DIRECTORY 1
+
+extern GtkTreeStore *choose_store;
+extern GtkWidget *choose_view;
+extern GtkTreeSelection *choose_selection;
+extern const struct tabtype choose_tabtype;
+
+struct choosedata *choose_iter_to_data(GtkTreeIter *iter);
+gboolean choose_button_event(GtkWidget *widget,
+                             GdkEventButton *event,
+                             gpointer user_data);
+
+#endif /* CHOOSE_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 21f3bf6..e18c082 100644 (file)
@@ -90,9 +90,12 @@ struct tabtype {
   int (*properties_sensitive)(void *extra);
   int (*selectall_sensitive)(void *extra);
   int (*selectnone_sensitive)(void *extra);
-  void (*properties_activate)(void *extra);
-  void (*selectall_activate)(void *extra);
-  void (*selectnone_activate)(void *extra);
+  void (*properties_activate)(GtkMenuItem *menuitem,
+                              gpointer user_data);
+  void (*selectall_activate)(GtkMenuItem *menuitem,
+                             gpointer user_data);
+  void (*selectnone_activate)(GtkMenuItem *menuitem,
+                              gpointer user_data);
   void (*selected)(void);
   void *extra;
 };
index 8dcae3c..7cf65c0 100644 (file)
@@ -59,7 +59,7 @@ static void select_all(gpointer attribute((unused)) callback_data,
   const struct tabtype *t = g_object_get_data(G_OBJECT(tab), "type");
 
   if(t->selectall_activate)
-    t->selectall_activate(t->extra);
+    t->selectall_activate(NULL, t->extra);
 }
 
 /** @brief Called when the select none option is activated
@@ -74,7 +74,7 @@ static void select_none(gpointer attribute((unused)) callback_data,
   const struct tabtype *t = g_object_get_data(G_OBJECT(tab), "type");
 
   if(t->selectnone_activate)
-    t->selectnone_activate(t->extra);
+    t->selectnone_activate(NULL, t->extra);
 }
 
 /** @brief Called when the track properties option is activated
@@ -89,7 +89,7 @@ static void properties_item(gpointer attribute((unused)) callback_data,
   const struct tabtype *t = g_object_get_data(G_OBJECT(tab), "type");
 
   if(t->properties_activate)
-    t->properties_activate(t->extra);
+    t->properties_activate(NULL, t->extra);
 }
 
 /** @brief Called when the login option is activated */
diff --git a/disobedience/popup.c b/disobedience/popup.c
new file mode 100644 (file)
index 0000000..ce17a73
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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
+ */
+#include "disobedience.h"
+#include "popup.h"
+
+void popup(GtkWidget **menup,
+           GdkEventButton *event,
+           struct menuitem *items,
+           int nitems,
+           void *extra) {
+  GtkWidget *menu = *menup;
+
+  /* Create the menu if it does not yet exist */
+  if(!menu) {
+    menu = *menup = gtk_menu_new();
+    g_signal_connect(menu, "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), menup);
+    for(int n = 0; n < nitems; ++n) {
+      items[n].w = gtk_menu_item_new_with_label(items[n].name);
+      gtk_menu_attach(GTK_MENU(menu), items[n].w, 0, 1, n, n + 1);
+    }
+    set_tool_colors(menu);
+  }
+  /* Configure item sensitivity */
+  for(int n = 0; n < nitems; ++n) {
+    if(items[n].handlerid)
+      g_signal_handler_disconnect(items[n].w,
+                                  items[n].handlerid);
+    gtk_widget_set_sensitive(items[n].w,
+                             items[n].sensitive(extra));
+    items[n].handlerid = g_signal_connect(items[n].w,
+                                          "activate",
+                                          G_CALLBACK(items[n].activate),
+                                          extra);
+  }
+  /* Pop up the menu */
+  gtk_widget_show_all(menu);
+  gtk_menu_popup(GTK_MENU(menu), 0, 0, 0, 0,
+                 event->button, event->time);
+}
+
+/** @brief Make sure that something is selected
+ * @param widget Tree view
+ * @param event Mouse event (the hovered row will be selected)
+ */
+void ensure_selected(GtkTreeView *treeview,
+                     GdkEventButton *event) {
+  GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
+  if(gtk_tree_selection_count_selected_rows(selection) == 0) {
+    GtkTreePath *path;
+    if(gtk_tree_view_get_path_at_pos(treeview,
+                                     event->x, event->y,
+                                     &path,
+                                     NULL,
+                                     NULL, NULL)) 
+      gtk_tree_selection_select_path(selection, path);
+    gtk_tree_path_free(path);
+  }
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/disobedience/popup.h b/disobedience/popup.h
new file mode 100644 (file)
index 0000000..165f69b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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
+ */
+#ifndef POPUP_H
+#define POPUP_H
+
+/** @brief A popup menu item */
+struct menuitem {
+  /** @brief Menu item name */
+  const char *name;
+
+  /** @brief Called to activate the menu item */
+  void (*activate)(GtkMenuItem *menuitem,
+                   gpointer user_data);
+  
+  /** @brief Called to determine whether the menu item is usable.
+   *
+   * Returns @c TRUE if it should be sensitive and @c FALSE otherwise.
+   */
+  int (*sensitive)(void *extra);
+
+  /** @brief Signal handler ID */
+  gulong handlerid;
+
+  /** @brief Widget for menu item */
+  GtkWidget *w;
+};
+
+void popup(GtkWidget **menup,
+           GdkEventButton *event,
+           struct menuitem *items,
+           int nitems,
+           void *extra);
+void ensure_selected(GtkTreeView *treeview,
+                     GdkEventButton *event);
+
+#endif /* POPUP_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 633b174..e6971ab 100644 (file)
@@ -40,6 +40,7 @@
  * - display playing row in a different color?
  */
 #include "disobedience.h"
+#include "popup.h"
 #include "queue-generic.h"
 
 static struct queuelike *const queuelikes[] = {
index 8b2acaf..8f5b59f 100644 (file)
@@ -45,28 +45,6 @@ struct queue_column {
 /** @brief Right-algin column */
 #define COL_RIGHT 0x0004
 
-/** @brief An item in the queue's popup menu */
-struct queue_menuitem {
-  /** @brief Menu item name */
-  const char *name;
-
-  /** @brief Called to activate the menu item */
-  void (*activate)(GtkMenuItem *menuitem,
-                   gpointer user_data);
-  
-  /** @brief Called to determine whether the menu item is usable.
-   *
-   * Returns @c TRUE if it should be sensitive and @c FALSE otherwise.
-   */
-  int (*sensitive)(struct queuelike *ql);
-
-  /** @brief Signal handler ID */
-  gulong handlerid;
-
-  /** @brief Widget for menu item */
-  GtkWidget *w;
-};
-
 /** @brief Definition of a queue-like window */
 struct queuelike {
 
@@ -85,7 +63,7 @@ struct queuelike {
   int ncolumns;
 
   /** @brief Items for popup menu */
-  struct queue_menuitem *menuitems;
+  struct menuitem *menuitems;
 
   /** @brief Number of menu items */
   int nmenuitems;
@@ -119,22 +97,22 @@ extern struct queuelike ql_added;
 
 extern time_t last_playing;
 
-int ql_selectall_sensitive(struct queuelike *ql);
+int ql_selectall_sensitive(void *extra);
 void ql_selectall_activate(GtkMenuItem *menuitem,
                            gpointer user_data);
-int ql_selectnone_sensitive(struct queuelike *ql);
+int ql_selectnone_sensitive(void *extra);
 void ql_selectnone_activate(GtkMenuItem *menuitem,
                             gpointer user_data);
-int ql_properties_sensitive(struct queuelike *ql);
+int ql_properties_sensitive(void *extra);
 void ql_properties_activate(GtkMenuItem *menuitem,
                             gpointer user_data);
-int ql_scratch_sensitive(struct queuelike *ql);
+int ql_scratch_sensitive(void *extra);
 void ql_scratch_activate(GtkMenuItem *menuitem,
                          gpointer user_data);
-int ql_remove_sensitive(struct queuelike *ql);
+int ql_remove_sensitive(void *extra);
 void ql_remove_activate(GtkMenuItem *menuitem,
                         gpointer user_data);
-int ql_play_sensitive(struct queuelike *ql);
+int ql_play_sensitive(void *extra);
 void ql_play_activate(GtkMenuItem *menuitem,
                       gpointer user_data);
 gboolean ql_button_release(GtkWidget *widget,
index ac3479a..fcc34bf 100644 (file)
  * USA
  */
 #include "disobedience.h"
+#include "popup.h"
 #include "queue-generic.h"
 
 /* Select All */
 
-int ql_selectall_sensitive(struct queuelike *ql) {
+int ql_selectall_sensitive(void *extra) {
+  struct queuelike *ql = extra;
   return !!ql->q;
 }
 
@@ -35,7 +37,8 @@ void ql_selectall_activate(GtkMenuItem attribute((unused)) *menuitem,
 
 /* Select None */
 
-int ql_selectnone_sensitive(struct queuelike *ql) {
+int ql_selectnone_sensitive(void *extra) {
+  struct queuelike *ql = extra;
   return gtk_tree_selection_count_selected_rows(ql->selection) > 0;
 }
 
@@ -48,7 +51,8 @@ void ql_selectnone_activate(GtkMenuItem attribute((unused)) *menuitem,
 
 /* Properties */
 
-int ql_properties_sensitive(struct queuelike *ql) {
+int ql_properties_sensitive(void *extra) {
+  struct queuelike *ql = extra;
   return gtk_tree_selection_count_selected_rows(ql->selection) > 0;
 }
 
@@ -71,7 +75,7 @@ void ql_properties_activate(GtkMenuItem attribute((unused)) *menuitem,
 
 /* Scratch */
 
-int ql_scratch_sensitive(struct queuelike attribute((unused)) *ql) {
+int ql_scratch_sensitive(void attribute((unused)) *extra) {
   return !!(last_state & DISORDER_PLAYING)
     && right_scratchable(last_rights, config->username, playing_track);
 }
@@ -100,7 +104,8 @@ static void ql_remove_sensitive_callback(GtkTreeModel *model,
   ++counts[removable];
 }
 
-int ql_remove_sensitive(struct queuelike *ql) {
+int ql_remove_sensitive(void *extra) {
+  struct queuelike *ql = extra;
   int counts[2] = { 0, 0 };
   gtk_tree_selection_selected_foreach(ql->selection,
                                       ql_remove_sensitive_callback,
@@ -135,7 +140,8 @@ void ql_remove_activate(GtkMenuItem attribute((unused)) *menuitem,
 
 /* Play */
 
-int ql_play_sensitive(struct queuelike *ql) {
+int ql_play_sensitive(void *extra) {
+  struct queuelike *ql = extra;
   return (last_rights & RIGHT_PLAY)
     && gtk_tree_selection_count_selected_rows(ql->selection) > 0;
 }
@@ -162,38 +168,8 @@ void ql_play_activate(GtkMenuItem attribute((unused)) *menuitem,
                                       0);
 }
 
-/** @brief Create @c ql->menu if it does not already exist */
-static void ql_create_menu(struct queuelike *ql) {
-  if(ql->menu)
-    return;
-  ql->menu = gtk_menu_new();
-  g_signal_connect(ql->menu, "destroy",
-                   G_CALLBACK(gtk_widget_destroyed), &ql->menu);
-  for(int n = 0; n < ql->nmenuitems; ++n) {
-    ql->menuitems[n].w = gtk_menu_item_new_with_label(ql->menuitems[n].name);
-    gtk_menu_attach(GTK_MENU(ql->menu), ql->menuitems[n].w, 0, 1, n, n + 1);
-  }
-  set_tool_colors(ql->menu);
-}
-
-/** @brief Configure @c ql->menu */
-static void ql_configure_menu(struct queuelike *ql) {
-  /* Set the sensitivity of each menu item and (re-)establish the signal
-   * handlers */
-  for(int n = 0; n < ql->nmenuitems; ++n) {
-    if(ql->menuitems[n].handlerid)
-      g_signal_handler_disconnect(ql->menuitems[n].w,
-                                  ql->menuitems[n].handlerid);
-    gtk_widget_set_sensitive(ql->menuitems[n].w,
-                             ql->menuitems[n].sensitive(ql));
-    ql->menuitems[n].handlerid = g_signal_connect
-      (ql->menuitems[n].w, "activate",
-       G_CALLBACK(ql->menuitems[n].activate), ql);
-  }
-}
-
 /** @brief Called when a button is released over a queuelike */
-gboolean ql_button_release(GtkWidget*widget,
+gboolean ql_button_release(GtkWidget *widget,
                            GdkEventButton *event,
                            gpointer user_data) {
   struct queuelike *ql = user_data;
@@ -201,59 +177,22 @@ gboolean ql_button_release(GtkWidget*widget,
   if(event->type == GDK_BUTTON_PRESS
      && event->button == 3) {
     /* Right button click. */
-    if(gtk_tree_selection_count_selected_rows(ql->selection) == 0) {
-      /* Nothing is selected, select whatever is under the pointer */
-      GtkTreePath *path;
-      if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
-                                       event->x, event->y,
-                                       &path,
-                                       NULL,
-                                       NULL, NULL)) 
-        gtk_tree_selection_select_path(ql->selection, path);
-    }
-    ql_create_menu(ql);
-    ql_configure_menu(ql);
-    gtk_widget_show_all(ql->menu);
-    gtk_menu_popup(GTK_MENU(ql->menu), 0, 0, 0, 0,
-                   event->button, event->time);
+    ensure_selected(GTK_TREE_VIEW(widget), event);
+    popup(&ql->menu, event, ql->menuitems, ql->nmenuitems, ql);
     return TRUE;                        /* hide the click from other widgets */
   }
 
   return FALSE;
 }
 
-static int ql_tab_selectall_sensitive(void *extra) {
-  return ql_selectall_sensitive(extra);
-}
-  
-static void ql_tab_selectall_activate(void *extra) {
-  ql_selectall_activate(NULL, extra);
-}
-  
-static int ql_tab_selectnone_sensitive(void *extra) {
-  return ql_selectnone_sensitive(extra);
-}
-  
-static void ql_tab_selectnone_activate(void *extra) {
-  ql_selectnone_activate(NULL, extra);
-}
-  
-static int ql_tab_properties_sensitive(void *extra) {
-  return ql_properties_sensitive(extra);
-}
-  
-static void ql_tab_properties_activate(void *extra) {
-  ql_properties_activate(NULL, extra);
-}
-
 struct tabtype *ql_tabtype(struct queuelike *ql) {
   static const struct tabtype ql_tabtype = {
-    ql_tab_properties_sensitive,
-    ql_tab_selectall_sensitive,
-    ql_tab_selectnone_sensitive,
-    ql_tab_properties_activate,
-    ql_tab_selectall_activate,
-    ql_tab_selectnone_activate,
+    ql_properties_sensitive,
+    ql_selectall_sensitive,
+    ql_selectnone_sensitive,
+    ql_properties_activate,
+    ql_selectall_activate,
+    ql_selectnone_activate,
     0,
     0
   };
index 2c68a68..fe9e707 100644 (file)
@@ -18,6 +18,7 @@
  * USA
  */
 #include "disobedience.h"
+#include "popup.h"
 #include "queue-generic.h"
 
 /** @brief The actual queue */
@@ -156,7 +157,7 @@ static const struct queue_column queue_columns[] = {
 };
 
 /** @brief Pop-up menu for queue */
-static struct queue_menuitem queue_menuitems[] = {
+static struct menuitem queue_menuitems[] = {
   { "Track properties", ql_properties_activate, ql_properties_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 },
index 01647ab..49333e7 100644 (file)
@@ -18,6 +18,7 @@
  * USA
  */
 #include "disobedience.h"
+#include "popup.h"
 #include "queue-generic.h"
 
 /** @brief Update the recently played list */
@@ -75,7 +76,7 @@ static const struct queue_column recent_columns[] = {
 };
 
 /** @brief Pop-up menu for recently played list */
-static struct queue_menuitem recent_menuitems[] = {
+static struct menuitem recent_menuitems[] = {
   { "Track properties", ql_properties_activate, ql_properties_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 },