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',