diff --git a/gui/control.c b/gui/control.c
index 2e8cf629..338fae47 100644
--- a/gui/control.c
+++ b/gui/control.c
@@ -154,11 +154,8 @@ void menu_request_new_studio(void)
}
}
-void on_load_studio(GtkWidget * item)
+void on_load_studio(const char * studio_name)
{
- const char * studio_name;
-
- studio_name = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(item))));
log_info("Load studio \"%s\"", studio_name);
if (!control_proxy_load_studio(studio_name))
@@ -167,13 +164,10 @@ void on_load_studio(GtkWidget * item)
}
}
-void on_delete_studio(GtkWidget * item)
+void on_delete_studio(const char * studio_name)
{
- const char * studio_name;
bool result;
- studio_name = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(item))));
-
if (!ask_dialog(&result, "Confirm studio delete", "Studio \"%s\" will be deleted. Are you sure?", studio_name) || !result)
{
return;
diff --git a/gui/dynmenu.c b/gui/dynmenu.c
new file mode 100644
index 00000000..a57ce9c1
--- /dev/null
+++ b/gui/dynmenu.c
@@ -0,0 +1,231 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*
+ * LADI Session Handler (ladish)
+ *
+ * Copyright (C) 2010 Nedko Arnaudov
+ *
+ **************************************************************************
+ * This file contains dynamic menu related code
+ **************************************************************************
+ *
+ * LADI Session Handler 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.
+ *
+ * LADI Session Handler 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 LADI Session Handler. If not, see
+ * or write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dynmenu.h"
+#include "gtk_builder.h"
+#include "../common/catdup.h"
+
+struct ladish_dynmenu
+{
+ int count;
+ GtkWidget * menu_item;
+ GtkWidget * menu;
+ bool
+ (* fill_callback)(
+ void
+ (* callback)(
+ void * context,
+ const char * name,
+ void * data,
+ void (* data_free)()),
+ void * context);
+ void (* item_activate_callback)(const char * name, void * data);
+ bool add_sensitive;
+ gulong activate_signal_id;
+ char * description;
+};
+
+struct ladish_dynmenu_item_data
+{
+ GtkWidget * item;
+ void * data;
+ void (* data_free)();
+ void (* item_activate_callback)(const char * name, void * data);
+};
+
+void on_activate_item(GtkMenuItem * item, struct ladish_dynmenu_item_data * data_ptr)
+{
+ //log_info("on_activate_item('%s')", gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(data_ptr->item)))));
+
+ data_ptr->item_activate_callback(
+ gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(data_ptr->item)))),
+ data_ptr->data);
+}
+
+#define data_ptr ((struct ladish_dynmenu_item_data *)data)
+
+void free_item_data(gpointer data, GClosure * closure)
+{
+ //log_info("data_ptr %p deallocate", data_ptr);
+
+ if (data_ptr->data != NULL)
+ {
+ if (data_ptr->data_free != NULL)
+ {
+ data_ptr->data_free(data_ptr->data);
+ }
+ else
+ {
+ free(data_ptr->data);
+ }
+ }
+
+ free(data_ptr);
+}
+
+#undef data_ptr
+#define dynmenu_ptr ((struct ladish_dynmenu *)context)
+
+static void ladish_dynmenu_add_entry(void * context, const char * name, void * data, void (* data_free)())
+{
+ struct ladish_dynmenu_item_data * data_ptr;
+
+ data_ptr = malloc(sizeof(struct ladish_dynmenu_item_data));
+ //log_info("data_ptr %p allocated", data_ptr);
+ data_ptr->data = data;
+ data_ptr->data_free = data_free;
+ data_ptr->item_activate_callback = dynmenu_ptr->item_activate_callback;
+
+ data_ptr->item = gtk_menu_item_new_with_label(name);
+ //log_info("refcount == %d", (unsigned int)G_OBJECT(item)->ref_count); // refcount == 2 because of the label
+ gtk_widget_set_sensitive(data_ptr->item, dynmenu_ptr->add_sensitive);
+ gtk_widget_show(data_ptr->item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(dynmenu_ptr->menu), data_ptr->item);
+ g_signal_connect_data(
+ G_OBJECT(data_ptr->item),
+ "activate",
+ G_CALLBACK(on_activate_item),
+ data_ptr,
+ free_item_data,
+ (GConnectFlags)0);
+ dynmenu_ptr->count++;
+}
+
+static void remove_dynmenu_menu_entry(GtkWidget * item, gpointer context)
+{
+ GtkWidget * label;
+
+ label = gtk_bin_get_child(GTK_BIN(item));
+
+ //log_debug("removing dynmenu item \"%s\"", gtk_menu_item_get_label(GTK_MENU_ITEM(item));
+ // gtk_menu_item_get_label() requries gtk 2.16
+ log_debug("removing dynmenu item \"%s\"", gtk_label_get_text(GTK_LABEL(label)));
+
+ gtk_container_remove(GTK_CONTAINER(item), label); /* destroy the label and drop the item refcount by one */
+ //log_info("refcount == %d", (unsigned int)G_OBJECT(item)->ref_count);
+ gtk_container_remove(GTK_CONTAINER(dynmenu_ptr->menu), item); /* drop the refcount of item by one and thus destroy it */
+ dynmenu_ptr->count--;
+}
+
+#undef dynmenu_ptr
+
+static void menu_dynmenu_clear(struct ladish_dynmenu * dynmenu_ptr)
+{
+ gtk_container_foreach(GTK_CONTAINER(dynmenu_ptr->menu), remove_dynmenu_menu_entry, dynmenu_ptr);
+ ASSERT(dynmenu_ptr->count == 0);
+ dynmenu_ptr->count = 0;
+}
+
+static void populate_dynmenu_menu(GtkMenuItem * menu_item, struct ladish_dynmenu * dynmenu_ptr)
+{
+ const char * prefix;
+ char * text;
+
+ menu_dynmenu_clear(dynmenu_ptr);
+ dynmenu_ptr->add_sensitive = true;
+ if (!dynmenu_ptr->fill_callback(ladish_dynmenu_add_entry, dynmenu_ptr))
+ {
+ menu_dynmenu_clear(dynmenu_ptr);
+ prefix = "Error obtaining ";
+ }
+ else if (dynmenu_ptr->count == 0)
+ {
+ prefix = "Empty ";
+ }
+ else
+ {
+ return;
+ }
+
+ text = catdup(prefix, dynmenu_ptr->description);
+
+ dynmenu_ptr->add_sensitive = false;
+ ladish_dynmenu_add_entry(dynmenu_ptr, text != NULL ? text : prefix, NULL, NULL);
+
+ free(text); /* free(NULL) is safe */
+}
+
+bool
+ladish_dynmenu_create(
+ const char * menu_item,
+ const char * menu,
+ bool
+ (* fill_callback)(
+ void
+ (* callback)(
+ void * context,
+ const char * name,
+ void * data,
+ void (* data_free)()),
+ void * context),
+ const char * description,
+ void (* item_activate_callback)(const char * name, void * data),
+ ladish_dynmenu_handle * dynmenu_handle_ptr)
+{
+ struct ladish_dynmenu * dynmenu_ptr;
+
+ dynmenu_ptr = malloc(sizeof(struct ladish_dynmenu));
+ if (dynmenu_ptr == NULL)
+ {
+ log_error("Allocation of ladish_dynmenu struct failed");
+ return false;
+ }
+
+ dynmenu_ptr->description = strdup(description);
+ if (dynmenu_ptr->description == NULL)
+ {
+ log_error("strdup('%s') failed for dynmenu description string", description);
+ free(dynmenu_ptr);
+ return false;
+ }
+
+ dynmenu_ptr->count = 0;
+ dynmenu_ptr->menu_item = get_gtk_builder_widget(menu_item);
+ dynmenu_ptr->menu = get_gtk_builder_widget(menu);
+ dynmenu_ptr->fill_callback = fill_callback;
+ dynmenu_ptr->item_activate_callback = item_activate_callback;
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(dynmenu_ptr->menu_item), dynmenu_ptr->menu);
+ dynmenu_ptr->activate_signal_id = g_signal_connect(G_OBJECT(dynmenu_ptr->menu_item), "activate", G_CALLBACK(populate_dynmenu_menu), dynmenu_ptr);
+
+ *dynmenu_handle_ptr = (ladish_dynmenu_handle)dynmenu_ptr;
+ return true;
+}
+
+#define dynmenu_ptr ((struct ladish_dynmenu *)dynmenu_handle)
+
+void
+ladish_dynmenu_destroy(
+ ladish_dynmenu_handle dynmenu_handle)
+{
+ if (g_signal_handler_is_connected(G_OBJECT(dynmenu_ptr->menu_item), dynmenu_ptr->activate_signal_id))
+ {
+ g_signal_handler_disconnect(G_OBJECT(dynmenu_ptr->menu_item), dynmenu_ptr->activate_signal_id);
+ }
+
+ free(dynmenu_ptr);
+}
+
+#undef dynmenu_ptr
diff --git a/gui/dynmenu.h b/gui/dynmenu.h
new file mode 100644
index 00000000..fba86f76
--- /dev/null
+++ b/gui/dynmenu.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*
+ * LADI Session Handler (ladish)
+ *
+ * Copyright (C) 2010 Nedko Arnaudov
+ *
+ **************************************************************************
+ * This file contains interface to the dynamic menu related code
+ **************************************************************************
+ *
+ * LADI Session Handler 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.
+ *
+ * LADI Session Handler 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 LADI Session Handler. If not, see
+ * or write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef DYNMENU_H__38C9F38E_2BB9_4934_9E2B_A7FC2087DDA5__INCLUDED
+#define DYNMENU_H__38C9F38E_2BB9_4934_9E2B_A7FC2087DDA5__INCLUDED
+
+#include "common.h"
+
+typedef struct ladish_dynmenu_tag { int unused; } * ladish_dynmenu_handle;
+
+bool
+ladish_dynmenu_create(
+ const char * menu_item,
+ const char * menu,
+ bool
+ (* fill_callback)(
+ void
+ (* callback)(
+ void * context,
+ const char * name,
+ void * data,
+ void (* data_free)()),
+ void * context),
+ const char * description,
+ void (* item_activate_callback)(const char * name, void * data),
+ ladish_dynmenu_handle * dynmenu_handle_ptr);
+
+void
+ladish_dynmenu_destroy(
+ ladish_dynmenu_handle dynmenu_handle);
+
+#endif /* #ifndef DYNMENU_H__38C9F38E_2BB9_4934_9E2B_A7FC2087DDA5__INCLUDED */
diff --git a/gui/internal.h b/gui/internal.h
index 287cc618..c134488c 100644
--- a/gui/internal.h
+++ b/gui/internal.h
@@ -34,10 +34,12 @@ void dbus_init(void);
void dbus_uninit(void);
/* control.c */
-void on_load_studio(GtkWidget * item);
-void on_delete_studio(GtkWidget * item);
+void on_load_studio(const char * studio_name);
+void on_delete_studio(const char * studio_name);
-void init_studio_lists(void);
+/* studio_list.c */
+bool create_studio_lists(void);
+void destroy_studio_lists(void);
void set_room_callbacks(void);
diff --git a/gui/main.c b/gui/main.c
index ab20e855..89708a8c 100644
--- a/gui/main.c
+++ b/gui/main.c
@@ -103,7 +103,10 @@ int main(int argc, char** argv)
init_dialogs();
- init_studio_lists();
+ if (!create_studio_lists())
+ {
+ return 1;
+ }
init_statusbar();
init_jack_widgets();
@@ -174,6 +177,7 @@ int main(int argc, char** argv)
control_proxy_uninit();
uninit_jack();
create_room_dialog_uninit();
+ destroy_studio_lists();
uninit_gtk_builder();
conf_proxy_uninit();
diff --git a/gui/studio_list.c b/gui/studio_list.c
index 246a9486..4a25e0d2 100644
--- a/gui/studio_list.c
+++ b/gui/studio_list.c
@@ -27,94 +27,96 @@
#include "internal.h"
#include "gtk_builder.h"
#include "../proxies/control_proxy.h"
+#include "dynmenu.h"
-struct studio_list
+static ladish_dynmenu_handle g_load_studio_list;
+static ladish_dynmenu_handle g_delete_studio_list;
+
+struct ladish_studio_list_closure
{
- int count;
- GtkWidget * menu_item;
- GtkWidget * menu;
- void (* item_activate_callback)(GtkWidget * item);
- bool add_sensitive;
+ void
+ (* callback)(
+ void * context,
+ const char * name,
+ void * data,
+ void (* data_free)());
+ void * context;
};
-static struct studio_list g_load_studio_list;
-static struct studio_list g_delete_studio_list;
-
-#define studio_list_ptr ((struct studio_list *)context)
-
-static void add_studio_list_menu_entry(void * context, const char * studio_name)
-{
- GtkWidget * item;
-
- item = gtk_menu_item_new_with_label(studio_name);
- //log_info("refcount == %d", (unsigned int)G_OBJECT(item)->ref_count); // refcount == 2 because of the label
- gtk_widget_set_sensitive(item, studio_list_ptr->add_sensitive);
- gtk_widget_show(item);
- gtk_menu_shell_append(GTK_MENU_SHELL(studio_list_ptr->menu), item);
- g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(studio_list_ptr->item_activate_callback), item);
- studio_list_ptr->count++;
-}
-
-static void remove_studio_list_menu_entry(GtkWidget * item, gpointer context)
-{
- GtkWidget * label;
-
- label = gtk_bin_get_child(GTK_BIN(item));
-
- //log_debug("removing studio menu item \"%s\"", gtk_menu_item_get_label(GTK_MENU_ITEM(item));
- // gtk_menu_item_get_label() requries gtk 2.16
- log_debug("removing studio menu item \"%s\"", gtk_label_get_text(GTK_LABEL(label)));
-
- gtk_container_remove(GTK_CONTAINER(item), label); /* destroy the label and drop the item refcount by one */
- //log_info("refcount == %d", (unsigned int)G_OBJECT(item)->ref_count);
- gtk_container_remove(GTK_CONTAINER(studio_list_ptr->menu), item); /* drop the refcount of item by one and thus destroy it */
- studio_list_ptr->count--;
-}
-
-#undef studio_list_ptr
-
-static void menu_studio_list_clear(struct studio_list * studio_list_ptr)
-{
- gtk_container_foreach(GTK_CONTAINER(studio_list_ptr->menu), remove_studio_list_menu_entry, studio_list_ptr);
- ASSERT(studio_list_ptr->count == 0);
- studio_list_ptr->count = 0;
-}
-
-static void populate_studio_list_menu(GtkMenuItem * menu_item, struct studio_list * studio_list_ptr)
-{
- menu_studio_list_clear(studio_list_ptr);
- studio_list_ptr->add_sensitive = true;
- if (!control_proxy_get_studio_list(add_studio_list_menu_entry, studio_list_ptr))
- {
- menu_studio_list_clear(studio_list_ptr);
- studio_list_ptr->add_sensitive = false;
- add_studio_list_menu_entry(studio_list_ptr, "Error obtaining studio list");
- }
- else if (studio_list_ptr->count == 0)
- {
- studio_list_ptr->add_sensitive = false;
- add_studio_list_menu_entry(studio_list_ptr, "Empty studio list");
- }
-}
+#define closure_ptr ((struct ladish_studio_list_closure * )context)
static
void
-init_studio_list(
- struct studio_list * studio_list_ptr,
- const char * menu_item,
- const char * menu,
- void (* item_activate_callback)(GtkWidget * item))
+add_item(
+ void * context,
+ const char * studio_name)
{
- studio_list_ptr->count = 0;
- studio_list_ptr->menu_item = get_gtk_builder_widget(menu_item);
- studio_list_ptr->menu = get_gtk_builder_widget(menu);
- studio_list_ptr->item_activate_callback = item_activate_callback;
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(studio_list_ptr->menu_item), studio_list_ptr->menu);
- g_signal_connect(G_OBJECT(studio_list_ptr->menu_item), "activate", G_CALLBACK(populate_studio_list_menu), studio_list_ptr);
+ closure_ptr->callback(closure_ptr->context, studio_name, NULL, NULL);
}
-void init_studio_lists(void)
+#undef closure_ptr
+
+static
+bool
+fill_callback(
+ void
+ (* callback)(
+ void * context,
+ const char * name,
+ void * data,
+ void (* data_free)()),
+ void * context)
{
- init_studio_list(&g_load_studio_list, "menu_item_load_studio", "load_studio_menu", on_load_studio);
- init_studio_list(&g_delete_studio_list, "menu_item_delete_studio", "delete_studio_menu", on_delete_studio);
+ struct ladish_studio_list_closure closure;
+
+ closure.callback = callback;
+ closure.context = context;
+
+ return control_proxy_get_studio_list(add_item, &closure);
+}
+
+static void on_load_studio_wrapper(const char * name, void * data)
+{
+ ASSERT(data == NULL);
+ on_load_studio(name);
+}
+
+static void on_delete_studio_wrapper(const char * name, void * data)
+{
+ ASSERT(data == NULL);
+ on_load_studio(name);
+}
+
+bool create_studio_lists(void)
+{
+ if (!ladish_dynmenu_create(
+ "menu_item_load_studio",
+ "load_studio_menu",
+ fill_callback,
+ "studio list",
+ on_load_studio_wrapper,
+ &g_load_studio_list))
+ {
+ return false;
+ }
+
+ if (!ladish_dynmenu_create(
+ "menu_item_delete_studio",
+ "delete_studio_menu",
+ fill_callback,
+ "studio list",
+ on_delete_studio_wrapper,
+ &g_delete_studio_list))
+ {
+ ladish_dynmenu_destroy(g_load_studio_list);
+ return false;
+ }
+
+ return true;
+}
+
+void destroy_studio_lists(void)
+{
+ ladish_dynmenu_destroy(g_delete_studio_list);
+ ladish_dynmenu_destroy(g_load_studio_list);
}
diff --git a/wscript b/wscript
index a182d486..c3ef3ca7 100644
--- a/wscript
+++ b/wscript
@@ -439,6 +439,7 @@ def build(bld):
'ask_dialog.c',
'create_room_dialog.c',
'menu.c',
+ 'dynmenu.c',
'toolbar.c',
'about.c',
'dbus.c',