rework project menu. Fix for #128 and #130

This commit is contained in:
Nedko Arnaudov 2010-12-19 18:32:02 +02:00
parent 06f37b47d4
commit eaf2b0bd50
8 changed files with 183 additions and 196 deletions

View File

@ -29,6 +29,8 @@
#include "gtk_builder.h"
#include "jack.h"
#include "zoom.h"
#include "menu.h"
#include "studio.h"
GtkAction * g_clear_xruns_and_max_dsp_action;
GtkAction * g_zoom_100_action;
@ -36,6 +38,44 @@ GtkAction * g_zoom_fit_action;
GtkAction * g_zoom_in_action;
GtkAction * g_zoom_out_action;
static
gboolean
load_project_accelerator_activated(
GtkAccelGroup * accel_group,
GObject * acceleratable,
guint keyval,
GdkModifierType modifier)
{
graph_view_handle view;
view = get_current_view();
if (get_studio_state() == STUDIO_STATE_STARTED && view != NULL && is_room_view(view))
{
menu_request_load_project();
}
return TRUE;
}
static
gboolean
unload_project_accelerator_activated(
GtkAccelGroup * accel_group,
GObject * acceleratable,
guint keyval,
GdkModifierType modifier)
{
graph_view_handle view;
view = get_current_view();
if (get_studio_state() == STUDIO_STATE_STARTED && view != NULL && is_room_view(view))
{
menu_request_unload_project();
}
return TRUE;
}
void init_actions_and_accelerators(void)
{
static GtkActionGroup * action_group_ptr;
@ -91,6 +131,20 @@ void init_actions_and_accelerators(void)
gtk_action_connect_accelerator(descriptor_ptr->action_ptr);
}
gtk_accel_group_connect(
accel_group_ptr,
gdk_keyval_from_name("o"),
GDK_CONTROL_MASK,
GTK_ACCEL_VISIBLE,
g_cclosure_new((GCallback)load_project_accelerator_activated, NULL, NULL));
gtk_accel_group_connect(
accel_group_ptr,
gdk_keyval_from_name("u"),
GDK_CONTROL_MASK,
GTK_ACCEL_VISIBLE,
g_cclosure_new((GCallback)unload_project_accelerator_activated, NULL, NULL));
gtk_window_add_accel_group(GTK_WINDOW(g_main_win), accel_group_ptr);
}

View File

@ -40,9 +40,10 @@ struct ladish_dynmenu
void * context,
const char * name,
void * data,
ladish_dynmenu_item_activate_callback item_activate_callback,
void (* data_free)()),
void * context);
void (* item_activate_callback)(const char * name, void * data);
ladish_dynmenu_item_activate_callback item_activate_callback;
bool add_sensitive;
gulong activate_signal_id;
char * description;
@ -53,7 +54,7 @@ struct ladish_dynmenu_item_data
GtkWidget * item;
void * data;
void (* data_free)();
void (* item_activate_callback)(const char * name, void * data);
ladish_dynmenu_item_activate_callback item_activate_callback;
};
void on_activate_item(GtkMenuItem * item, struct ladish_dynmenu_item_data * data_ptr)
@ -89,7 +90,14 @@ void free_item_data(gpointer data, GClosure * closure)
#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)())
static
void
ladish_dynmenu_add_entry(
void * context,
const char * name,
void * data,
ladish_dynmenu_item_activate_callback item_activate_callback,
void (* data_free)())
{
struct ladish_dynmenu_item_data * data_ptr;
@ -97,20 +105,30 @@ static void ladish_dynmenu_add_entry(void * context, const char * name, void * d
//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_activate_callback = item_activate_callback != NULL ? item_activate_callback : dynmenu_ptr->item_activate_callback;
if (name == NULL)
{
data_ptr->item = gtk_separator_menu_item_new(); /* separator */
gtk_widget_show(data_ptr->item);
gtk_menu_shell_append(GTK_MENU_SHELL(dynmenu_ptr->menu), data_ptr->item);
}
else
{
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);
}
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++;
}
@ -149,7 +167,7 @@ static void populate_dynmenu_menu(GtkMenuItem * menu_item, struct ladish_dynmenu
if (!dynmenu_ptr->fill_callback(ladish_dynmenu_add_entry, dynmenu_ptr))
{
menu_dynmenu_clear(dynmenu_ptr);
prefix = _("Error obtaining ");
prefix = _("Error filling ");
}
else if (dynmenu_ptr->count == 0)
{
@ -163,7 +181,7 @@ static void populate_dynmenu_menu(GtkMenuItem * menu_item, struct ladish_dynmenu
text = catdup(prefix, dynmenu_ptr->description);
dynmenu_ptr->add_sensitive = false;
ladish_dynmenu_add_entry(dynmenu_ptr, text != NULL ? text : prefix, NULL, NULL);
ladish_dynmenu_add_entry(dynmenu_ptr, text != NULL ? text : prefix, NULL, NULL, NULL);
free(text); /* free(NULL) is safe */
}
@ -179,10 +197,11 @@ ladish_dynmenu_create(
void * context,
const char * name,
void * data,
ladish_dynmenu_item_activate_callback item_activate_callback,
void (* data_free)()),
void * context),
const char * description,
void (* item_activate_callback)(const char * name, void * data),
ladish_dynmenu_item_activate_callback item_activate_callback,
ladish_dynmenu_handle * dynmenu_handle_ptr)
{
struct ladish_dynmenu * dynmenu_ptr;
@ -216,6 +235,26 @@ ladish_dynmenu_create(
#define dynmenu_ptr ((struct ladish_dynmenu *)dynmenu_handle)
void
ladish_dynmenu_fill_external(
ladish_dynmenu_handle dynmenu_handle,
GtkMenu * menu)
{
GtkWidget * menu_backup;
int count_backup;
menu_backup = dynmenu_ptr->menu;
count_backup = dynmenu_ptr->count;
dynmenu_ptr->menu = GTK_WIDGET(menu);
dynmenu_ptr->add_sensitive = true;
dynmenu_ptr->fill_callback(ladish_dynmenu_add_entry, dynmenu_ptr);
dynmenu_ptr->menu = menu_backup;
dynmenu_ptr->count = count_backup;
}
void
ladish_dynmenu_destroy(
ladish_dynmenu_handle dynmenu_handle)

View File

@ -31,6 +31,8 @@
typedef struct ladish_dynmenu_tag { int unused; } * ladish_dynmenu_handle;
typedef void (* ladish_dynmenu_item_activate_callback)(const char * name, void * data);
bool
ladish_dynmenu_create(
const char * menu_item,
@ -42,12 +44,18 @@ ladish_dynmenu_create(
void * context,
const char * name,
void * data,
ladish_dynmenu_item_activate_callback item_activate_callback,
void (* data_free)()),
void * context),
const char * description,
void (* item_activate_callback)(const char * name, void * data),
ladish_dynmenu_item_activate_callback item_activate_callback,
ladish_dynmenu_handle * dynmenu_handle_ptr);
void
ladish_dynmenu_fill_external(
ladish_dynmenu_handle dynmenu_handle,
GtkMenu * menu);
void
ladish_dynmenu_destroy(
ladish_dynmenu_handle dynmenu_handle);

View File

@ -187,72 +187,6 @@
<child type="submenu">
<object class="GtkMenu" id="project_menu">
<property name="visible">True</property>
<child>
<object class="GtkImageMenuItem" id="menu_item_load_project">
<property name="label" translatable="yes">_Load Project...</property>
<property name="image">image_load_project</property>
<property name="sensitive">False</property>
<property name="use_stock">False</property>
<property name="use_underline">True</property>
<property name="visible">True</property>
<accelerator key="O" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="menu_item_unload_project">
<property name="label" translatable="yes">_Unload Project</property>
<property name="image">image_unload_project</property>
<property name="sensitive">False</property>
<property name="use_stock">False</property>
<property name="use_underline">True</property>
<property name="visible">True</property>
<accelerator key="U" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="menu_item_rename_project">
<property name="label" translatable="yes">_Rename Project...</property>
<property name="image">image_rename_project</property>
<property name="sensitive">False</property>
<property name="use_stock">False</property>
<property name="use_underline">True</property>
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="menu_item_save_project">
<property name="label" translatable="yes">_Save Project</property>
<property name="image">image_save_project</property>
<property name="sensitive">False</property>
<property name="use_stock">False</property>
<property name="use_underline">True</property>
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="menu_item_save_as_project">
<property name="label" translatable="yes">Save Project _As...</property>
<property name="image">image_save_as_project</property>
<property name="sensitive">False</property>
<property name="use_stock">False</property>
<property name="use_underline">True</property>
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="project_menu_separator1">
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="menu_item_recently_loaded_projects">
<property name="label" translatable="yes">R_ecently Loaded</property>
<property name="sensitive">False</property>
<property name="use_stock">False</property>
<property name="use_underline">True</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>
</object>

View File

@ -477,3 +477,8 @@ ladish_room_proxy_handle graph_view_get_room(graph_view_handle view)
{
return view_ptr->room;
}
bool room_has_project(graph_view_handle view)
{
return view_ptr->project_name != NULL;
}

View File

@ -54,6 +54,7 @@ graph_view_handle get_current_view(void);
canvas_handle get_current_canvas(void);
const char * get_current_view_room_name(void);
bool is_room_view(graph_view_handle view);
bool room_has_project(graph_view_handle view);
ladish_app_supervisor_proxy_handle graph_view_get_app_supervisor(graph_view_handle view);
bool app_run_custom(graph_view_handle view, const char * command, const char * name, bool run_in_terminal, uint8_t level);

View File

@ -40,10 +40,7 @@ static GtkWidget * g_menu_item_unload_studio;
static GtkWidget * g_menu_item_rename_studio;
static GtkWidget * g_menu_item_create_room;
static GtkWidget * g_menu_item_destroy_room;
static GtkWidget * g_menu_item_load_project;
static GtkWidget * g_menu_item_unload_project;
static GtkWidget * g_menu_item_save_project;
static GtkWidget * g_menu_item_save_as_project;
static GtkWidget * g_menu_item_project;
static GtkWidget * g_menu_item_daemon_exit;
static GtkWidget * g_menu_item_jack_configure;
static GtkWidget * g_menu_item_settings;
@ -62,7 +59,7 @@ static GtkWidget * g_menu_item_start_app;
static bool g_latency_changing;
static ladish_dynmenu_handle g_recent_projects_list;
static ladish_dynmenu_handle g_project_dynmenu;
typedef void (* menu_request_toggle_func)(bool visible);
@ -88,26 +85,17 @@ static void buffer_size_change_request(GtkCheckMenuItem * item_ptr, gpointer use
struct ladish_recent_projects_list_closure
{
GtkMenu * menu;
void
(* callback)(
void * context,
const char * name,
void * data,
void (* item_activate_callback)(const char * name, void * data),
void (* data_free)());
void * context;
};
static void on_load_project_main_menu(const char * name, void * data)
{
log_info("Request to load project \"%s\":\"%s\"", name, (const char *)data);
if (!ladish_room_proxy_load_project(graph_view_get_room(get_current_view()), data))
{
error_message_box(_("Project load failed, please inspect logs."));
}
}
static void on_load_project_popup_menu(const char * name, void * data)
static void on_load_project(const char * name, void * data)
{
log_info("Request to load project \"%s\":\"%s\"", name, (const char *)data);
if (!ladish_room_proxy_load_project(graph_view_get_room(get_current_view()), data))
@ -125,46 +113,57 @@ add_recent_project(
const char * project_name,
const char * project_dir)
{
GtkWidget * menuitem;
if (closure_ptr->menu != NULL)
{
menuitem = gtk_menu_item_new_with_label(project_name);
g_signal_connect_data(
menuitem,
"activate",
(GCallback)on_load_project_popup_menu,
strdup(project_dir),
(GClosureNotify)free,
(GConnectFlags)0);
gtk_menu_shell_append(GTK_MENU_SHELL(closure_ptr->menu), menuitem);
}
else
{
closure_ptr->callback(closure_ptr->context, project_name, strdup(project_dir), NULL);
}
closure_ptr->callback(closure_ptr->context, project_name, strdup(project_dir), NULL, free);
}
#undef closure_ptr
static
bool
get_recent_projects_list(
fill_project_dynmenu(
void (* callback)(
void * context,
const char * name,
void * data,
void (* item_activate_callback)(const char * name, void * data),
void (* data_free)()),
void * context)
{
struct ladish_recent_projects_list_closure closure;
bool has_project;
graph_view_handle view;
closure.menu = NULL;
closure.callback = callback;
closure.context = context;
return ladish_room_proxy_get_recent_projects(graph_view_get_room(get_current_view()), 10, add_recent_project, &closure);
view = get_current_view();
if (ladish_room_proxy_get_recent_projects(graph_view_get_room(view), 10, add_recent_project, &closure))
{
callback(context, NULL, NULL, NULL, NULL); /* add separator */
}
callback(context, _("Load Project..."), NULL, (ladish_dynmenu_item_activate_callback)menu_request_load_project, NULL);
has_project = room_has_project(view);
if (!has_project)
{
callback(context, _("Create Project..."), NULL, (ladish_dynmenu_item_activate_callback)menu_request_save_as_project, NULL);
}
callback(context, has_project ? _("Unload Project") : _("Clear Room"), NULL, (ladish_dynmenu_item_activate_callback)menu_request_unload_project, NULL);
if (has_project)
{
callback(context, _("Save Project"), NULL, (ladish_dynmenu_item_activate_callback)menu_request_save_project, NULL);
callback(context, _("Save Project As..."), NULL, (ladish_dynmenu_item_activate_callback)menu_request_save_as_project, NULL);
callback(context, _("Project Properties..."), NULL, (ladish_dynmenu_item_activate_callback)ladish_project_properties_dialog_run, NULL);
}
return true;
}
#undef closure_ptr
bool menu_init(void)
{
g_menu_item_new_studio = get_gtk_builder_widget("menu_item_new_studio");
@ -177,10 +176,7 @@ bool menu_init(void)
g_menu_item_rename_studio = get_gtk_builder_widget("menu_item_rename_studio");
g_menu_item_create_room = get_gtk_builder_widget("menu_item_create_room");
g_menu_item_destroy_room = get_gtk_builder_widget("menu_item_destroy_room");
g_menu_item_load_project = get_gtk_builder_widget("menu_item_load_project");
g_menu_item_unload_project = get_gtk_builder_widget("menu_item_unload_project");
g_menu_item_save_project = get_gtk_builder_widget("menu_item_save_project");
g_menu_item_save_as_project = get_gtk_builder_widget("menu_item_save_as_project");
g_menu_item_project = get_gtk_builder_widget("project_menu_item");
g_menu_item_daemon_exit = get_gtk_builder_widget("menu_item_daemon_exit");
g_menu_item_jack_configure = get_gtk_builder_widget("menu_item_jack_configure");
g_menu_item_settings = get_gtk_builder_widget("menu_item_settings");
@ -213,10 +209,6 @@ bool menu_init(void)
g_signal_connect(G_OBJECT(g_menu_item_start_app), "activate", G_CALLBACK(menu_request_start_app), NULL);
g_signal_connect(G_OBJECT(g_menu_item_create_room), "activate", G_CALLBACK(menu_request_create_room), NULL);
g_signal_connect(G_OBJECT(g_menu_item_destroy_room), "activate", G_CALLBACK(menu_request_destroy_room), NULL);
g_signal_connect(G_OBJECT(g_menu_item_load_project), "activate", G_CALLBACK(menu_request_load_project), NULL);
g_signal_connect(G_OBJECT(g_menu_item_unload_project), "activate", G_CALLBACK(menu_request_unload_project), NULL);
g_signal_connect(G_OBJECT(g_menu_item_save_project), "activate", G_CALLBACK(menu_request_save_project), NULL);
g_signal_connect(G_OBJECT(g_menu_item_save_as_project), "activate", G_CALLBACK(menu_request_save_as_project), NULL);
g_signal_connect(G_OBJECT(g_menu_item_jack_latency_32), "toggled", G_CALLBACK(buffer_size_change_request), (gpointer)32);
g_signal_connect(G_OBJECT(g_menu_item_jack_latency_64), "toggled", G_CALLBACK(buffer_size_change_request), (gpointer)64);
@ -229,12 +221,12 @@ bool menu_init(void)
g_signal_connect(G_OBJECT(g_menu_item_jack_latency_8192), "toggled", G_CALLBACK(buffer_size_change_request), (gpointer)8192);
if (!ladish_dynmenu_create(
"menu_item_recently_loaded_projects",
"recently_loaded_projects_menu",
get_recent_projects_list,
"recent projects list",
on_load_project_main_menu,
&g_recent_projects_list))
"project_menu_item",
"project_menu",
fill_project_dynmenu,
"project menu",
on_load_project,
&g_project_dynmenu))
{
return false;
}
@ -244,11 +236,13 @@ bool menu_init(void)
void menu_uninit(void)
{
ladish_dynmenu_destroy(g_recent_projects_list);
ladish_dynmenu_destroy(g_project_dynmenu);
}
void menu_studio_state_changed(unsigned int studio_state)
{
graph_view_handle view;
gtk_widget_set_sensitive(g_menu_item_start_studio, studio_state == STUDIO_STATE_STOPPED);
gtk_widget_set_sensitive(g_menu_item_stop_studio, studio_state == STUDIO_STATE_STARTED);
gtk_widget_set_sensitive(g_menu_item_save_studio, studio_state == STUDIO_STATE_STARTED);
@ -257,6 +251,9 @@ void menu_studio_state_changed(unsigned int studio_state)
gtk_widget_set_sensitive(g_menu_item_rename_studio, studio_state == STUDIO_STATE_STOPPED || studio_state == STUDIO_STATE_STARTED);
gtk_widget_set_sensitive(g_menu_item_start_app, studio_state == STUDIO_STATE_STOPPED || studio_state == STUDIO_STATE_STARTED);
gtk_widget_set_sensitive(g_menu_item_create_room, studio_state == STUDIO_STATE_STOPPED || studio_state == STUDIO_STATE_STARTED);
view = get_current_view();
gtk_widget_set_sensitive(g_menu_item_project, studio_state == STUDIO_STATE_STARTED && view != NULL && is_room_view(view));
}
void menu_set_jack_latency_items_sensivity(bool sensitive)
@ -329,11 +326,7 @@ void menu_set_toolbar_visibility(bool visible)
void menu_view_activated(bool room)
{
gtk_widget_set_sensitive(g_menu_item_destroy_room, room);
gtk_widget_set_sensitive(g_menu_item_load_project, room);
gtk_widget_set_sensitive(g_menu_item_unload_project, room);
gtk_widget_set_sensitive(g_menu_item_save_project, room);
gtk_widget_set_sensitive(g_menu_item_save_as_project, room);
gtk_widget_set_sensitive(get_gtk_builder_widget("menu_item_recently_loaded_projects"), room);
gtk_widget_set_sensitive(g_menu_item_project, room && get_studio_state() == STUDIO_STATE_STARTED);
}
static void on_popup_menu_action_start_app(GtkWidget * menuitem, gpointer userdata)
@ -351,30 +344,9 @@ static void on_popup_menu_action_destroy_room(GtkWidget * menuitem, gpointer use
menu_request_destroy_room();
}
static void on_popup_menu_action_load_project(GtkWidget * menuitem, gpointer userdata)
{
menu_request_load_project();
}
static void on_popup_menu_action_unload_project(GtkWidget * menuitem, gpointer userdata)
{
menu_request_unload_project();
}
static void on_popup_menu_action_save_project(GtkWidget * menuitem, gpointer userdata)
{
menu_request_save_project();
}
static void on_popup_menu_action_save_project_as(GtkWidget * menuitem, gpointer userdata)
{
menu_request_save_as_project();
}
void fill_view_popup_menu(GtkMenu * menu, graph_view_handle view)
{
GtkWidget * menuitem;
struct ladish_recent_projects_list_closure closure;
log_info("filling view menu...");
@ -387,38 +359,10 @@ void fill_view_popup_menu(GtkMenu * menu, graph_view_handle view)
if (is_room_view(view))
{
closure.menu = menu;
closure.callback = NULL;
closure.context = NULL;
menuitem = gtk_separator_menu_item_new(); /* separator */
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
if (ladish_room_proxy_get_recent_projects(graph_view_get_room(get_current_view()), 10, add_recent_project, &closure))
{
menuitem = gtk_separator_menu_item_new(); /* separator */
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
}
menuitem = gtk_menu_item_new_with_label(_("Load Project..."));
g_signal_connect(menuitem, "activate", (GCallback)on_popup_menu_action_load_project, NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
menuitem = gtk_menu_item_new_with_label(_("Unload Project"));
g_signal_connect(menuitem, "activate", (GCallback)on_popup_menu_action_unload_project, NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
menuitem = gtk_menu_item_new_with_label(_("Save Project..."));
g_signal_connect(menuitem, "activate", (GCallback)on_popup_menu_action_save_project, NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
menuitem = gtk_menu_item_new_with_label(_("Save Project As..."));
g_signal_connect(menuitem, "activate", (GCallback)on_popup_menu_action_save_project_as, NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
menuitem = gtk_menu_item_new_with_label(_("Project Properties..."));
g_signal_connect(menuitem, "activate", (GCallback)ladish_project_properties_dialog_run, NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
ladish_dynmenu_fill_external(g_project_dynmenu, menu);
menuitem = gtk_separator_menu_item_new(); /* separator */
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);

View File

@ -39,6 +39,7 @@ struct ladish_studio_list_closure
void * context,
const char * name,
void * data,
ladish_dynmenu_item_activate_callback item_activate_callback,
void (* data_free)());
void * context;
};
@ -51,7 +52,7 @@ add_item(
void * context,
const char * studio_name)
{
closure_ptr->callback(closure_ptr->context, studio_name, NULL, NULL);
closure_ptr->callback(closure_ptr->context, studio_name, NULL, NULL, NULL);
}
#undef closure_ptr
@ -64,6 +65,7 @@ fill_callback(
void * context,
const char * name,
void * data,
ladish_dynmenu_item_activate_callback item_activate_callback,
void (* data_free)()),
void * context)
{