diff options
Diffstat (limited to 'src/jalv_gtk.c')
-rw-r--r-- | src/jalv_gtk.c | 1944 |
1 files changed, 1023 insertions, 921 deletions
diff --git a/src/jalv_gtk.c b/src/jalv_gtk.c index f909425..33b4b48 100644 --- a/src/jalv_gtk.c +++ b/src/jalv_gtk.c @@ -49,268 +49,347 @@ static bool updating = false; /** Widget for a control. */ typedef struct { - GtkSpinButton* spin; - GtkWidget* control; + GtkSpinButton* spin; + GtkWidget* control; } Controller; static float get_float(const LilvNode* node, float fallback) { - if (lilv_node_is_float(node) || lilv_node_is_int(node)) { - return lilv_node_as_float(node); - } + if (lilv_node_is_float(node) || lilv_node_is_int(node)) { + return lilv_node_as_float(node); + } - return fallback; + return fallback; } static GtkWidget* new_box(gboolean horizontal, gint spacing) { - #if GTK_MAJOR_VERSION == 3 - return gtk_box_new( - horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, - spacing); - #else - return (horizontal - ? gtk_hbox_new(FALSE, spacing) - : gtk_vbox_new(FALSE, spacing)); - #endif +#if GTK_MAJOR_VERSION == 3 + return gtk_box_new(horizontal ? GTK_ORIENTATION_HORIZONTAL + : GTK_ORIENTATION_VERTICAL, + spacing); +#else + return (horizontal ? gtk_hbox_new(FALSE, spacing) + : gtk_vbox_new(FALSE, spacing)); +#endif } static GtkWidget* new_hscale(gdouble min, gdouble max, gdouble step) { - #if GTK_MAJOR_VERSION == 3 - return gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, min, max, step); - #else - return gtk_hscale_new_with_range(min, max, step); - #endif +#if GTK_MAJOR_VERSION == 3 + return gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, min, max, step); +#else + return gtk_hscale_new_with_range(min, max, step); +#endif } static void size_request(GtkWidget* widget, GtkRequisition* req) { - #if GTK_MAJOR_VERSION == 3 - gtk_widget_get_preferred_size(widget, NULL, req); - #else - gtk_widget_size_request(widget, req); - #endif +#if GTK_MAJOR_VERSION == 3 + gtk_widget_get_preferred_size(widget, NULL, req); +#else + gtk_widget_size_request(widget, req); +#endif } static void on_window_destroy(GtkWidget* ZIX_UNUSED(widget), gpointer ZIX_UNUSED(data)) { - gtk_main_quit(); + gtk_main_quit(); } int jalv_init(int* argc, char*** argv, JalvOptions* opts) { - GOptionEntry entries[] = { - { "load", 'l', 0, G_OPTION_ARG_STRING, &opts->load, - "Load state from save directory", "DIR" }, - { "preset", 'p', 0, G_OPTION_ARG_STRING, &opts->preset, - "Load state from preset", "URI" }, - { "dump", 'd', 0, G_OPTION_ARG_NONE, &opts->dump, - "Dump plugin <=> UI communication", NULL }, - { "ui-uri", 'U', 0, G_OPTION_ARG_STRING, &opts->ui_uri, - "Load the UI with the given URI", "URI" }, - { "trace", 't', 0, G_OPTION_ARG_NONE, &opts->trace, - "Print trace messages from plugin", NULL }, - { "show-hidden", 's', 0, G_OPTION_ARG_NONE, &opts->show_hidden, - "Show controls for ports with notOnGUI property on generic UI", NULL }, - { "no-menu", 'n', 0, G_OPTION_ARG_NONE, &opts->no_menu, - "Do not show Jalv menu on window", NULL }, - { "generic-ui", 'g', 0, G_OPTION_ARG_NONE, &opts->generic_ui, - "Use Jalv generic UI and not the plugin UI", NULL}, - { "buffer-size", 'b', 0, G_OPTION_ARG_INT, &opts->buffer_size, - "Buffer size for plugin <=> UI communication", "SIZE"}, - { "update-frequency", 'r', 0, G_OPTION_ARG_DOUBLE, &opts->update_rate, - "UI update frequency", NULL}, - { "scale-factor", 0, 0, G_OPTION_ARG_DOUBLE, &opts->scale_factor, - "UI scale factor", NULL}, - { "control", 'c', 0, G_OPTION_ARG_STRING_ARRAY, &opts->controls, - "Set control value (e.g. \"vol=1.4\")", NULL}, - { "print-controls", 'p', 0, G_OPTION_ARG_NONE, &opts->print_controls, - "Print control output changes to stdout", NULL}, - { "jack-name", 'n', 0, G_OPTION_ARG_STRING, &opts->name, - "JACK client name", NULL}, - { "exact-jack-name", 'x', 0, G_OPTION_ARG_NONE, &opts->name_exact, - "Exact JACK client name (exit if taken)", NULL }, - { 0, 0, 0, G_OPTION_ARG_NONE, 0, 0, 0 } }; - GError* error = NULL; - const int err = gtk_init_with_args( - argc, argv, - "PLUGIN_URI - Run an LV2 plugin as a Jack application", - entries, NULL, &error); - - if (!err) { - fprintf(stderr, "%s\n", error->message); - } - - return !err; + GOptionEntry entries[] = { + {"load", + 'l', + 0, + G_OPTION_ARG_STRING, + &opts->load, + "Load state from save directory", + "DIR"}, + {"preset", + 'p', + 0, + G_OPTION_ARG_STRING, + &opts->preset, + "Load state from preset", + "URI"}, + {"dump", + 'd', + 0, + G_OPTION_ARG_NONE, + &opts->dump, + "Dump plugin <=> UI communication", + NULL}, + {"ui-uri", + 'U', + 0, + G_OPTION_ARG_STRING, + &opts->ui_uri, + "Load the UI with the given URI", + "URI"}, + {"trace", + 't', + 0, + G_OPTION_ARG_NONE, + &opts->trace, + "Print trace messages from plugin", + NULL}, + {"show-hidden", + 's', + 0, + G_OPTION_ARG_NONE, + &opts->show_hidden, + "Show controls for ports with notOnGUI property on generic UI", + NULL}, + {"no-menu", + 'n', + 0, + G_OPTION_ARG_NONE, + &opts->no_menu, + "Do not show Jalv menu on window", + NULL}, + {"generic-ui", + 'g', + 0, + G_OPTION_ARG_NONE, + &opts->generic_ui, + "Use Jalv generic UI and not the plugin UI", + NULL}, + {"buffer-size", + 'b', + 0, + G_OPTION_ARG_INT, + &opts->buffer_size, + "Buffer size for plugin <=> UI communication", + "SIZE"}, + {"update-frequency", + 'r', + 0, + G_OPTION_ARG_DOUBLE, + &opts->update_rate, + "UI update frequency", + NULL}, + {"scale-factor", + 0, + 0, + G_OPTION_ARG_DOUBLE, + &opts->scale_factor, + "UI scale factor", + NULL}, + {"control", + 'c', + 0, + G_OPTION_ARG_STRING_ARRAY, + &opts->controls, + "Set control value (e.g. \"vol=1.4\")", + NULL}, + {"print-controls", + 'p', + 0, + G_OPTION_ARG_NONE, + &opts->print_controls, + "Print control output changes to stdout", + NULL}, + {"jack-name", + 'n', + 0, + G_OPTION_ARG_STRING, + &opts->name, + "JACK client name", + NULL}, + {"exact-jack-name", + 'x', + 0, + G_OPTION_ARG_NONE, + &opts->name_exact, + "Exact JACK client name (exit if taken)", + NULL}, + {0, 0, 0, G_OPTION_ARG_NONE, 0, 0, 0}}; + GError* error = NULL; + const int err = + gtk_init_with_args(argc, + argv, + "PLUGIN_URI - Run an LV2 plugin as a Jack application", + entries, + NULL, + &error); + + if (!err) { + fprintf(stderr, "%s\n", error->message); + } + + return !err; } const char* jalv_native_ui_type(void) { #if GTK_MAJOR_VERSION == 2 - return "http://lv2plug.in/ns/extensions/ui#GtkUI"; + return "http://lv2plug.in/ns/extensions/ui#GtkUI"; #elif GTK_MAJOR_VERSION == 3 - return "http://lv2plug.in/ns/extensions/ui#Gtk3UI"; + return "http://lv2plug.in/ns/extensions/ui#Gtk3UI"; #else - return NULL; + return NULL; #endif } static void on_save_activate(GtkWidget* ZIX_UNUSED(widget), void* ptr) { - Jalv* jalv = (Jalv*)ptr; - GtkWidget* dialog = gtk_file_chooser_dialog_new( - "Save State", - (GtkWindow*)jalv->window, - GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER, - "_Cancel", GTK_RESPONSE_CANCEL, - "_Save", GTK_RESPONSE_ACCEPT, - NULL); - - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - char* base = g_build_filename(path, "/", NULL); - jalv_save(jalv, base); - g_free(path); - g_free(base); - } - - gtk_widget_destroy(dialog); + Jalv* jalv = (Jalv*)ptr; + GtkWidget* dialog = + gtk_file_chooser_dialog_new("Save State", + (GtkWindow*)jalv->window, + GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER, + "_Cancel", + GTK_RESPONSE_CANCEL, + "_Save", + GTK_RESPONSE_ACCEPT, + NULL); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + char* base = g_build_filename(path, "/", NULL); + jalv_save(jalv, base); + g_free(path); + g_free(base); + } + + gtk_widget_destroy(dialog); } static void on_quit_activate(GtkWidget* ZIX_UNUSED(widget), gpointer data) { - GtkWidget* window = (GtkWidget*)data; - gtk_widget_destroy(window); + GtkWidget* window = (GtkWidget*)data; + gtk_widget_destroy(window); } typedef struct { - Jalv* jalv; - LilvNode* preset; + Jalv* jalv; + LilvNode* preset; } PresetRecord; static char* symbolify(const char* in) { - const size_t len = strlen(in); - char* out = (char*)calloc(len + 1, 1); - for (size_t i = 0; i < len; ++i) { - if (g_ascii_isalnum(in[i])) { - out[i] = in[i]; - } else { - out[i] = '_'; - } - } - return out; + const size_t len = strlen(in); + char* out = (char*)calloc(len + 1, 1); + for (size_t i = 0; i < len; ++i) { + if (g_ascii_isalnum(in[i])) { + out[i] = in[i]; + } else { + out[i] = '_'; + } + } + return out; } static void set_window_title(Jalv* jalv) { - LilvNode* name = lilv_plugin_get_name(jalv->plugin); - const char* plugin = lilv_node_as_string(name); - if (jalv->preset) { - const char* preset_label = lilv_state_get_label(jalv->preset); - char* title = g_strdup_printf("%s - %s", plugin, preset_label); - gtk_window_set_title(GTK_WINDOW(jalv->window), title); - free(title); - } else { - gtk_window_set_title(GTK_WINDOW(jalv->window), plugin); - } - lilv_node_free(name); + LilvNode* name = lilv_plugin_get_name(jalv->plugin); + const char* plugin = lilv_node_as_string(name); + if (jalv->preset) { + const char* preset_label = lilv_state_get_label(jalv->preset); + char* title = g_strdup_printf("%s - %s", plugin, preset_label); + gtk_window_set_title(GTK_WINDOW(jalv->window), title); + free(title); + } else { + gtk_window_set_title(GTK_WINDOW(jalv->window), plugin); + } + lilv_node_free(name); } static void on_preset_activate(GtkWidget* widget, gpointer data) { - if (GTK_CHECK_MENU_ITEM(widget) != active_preset_item) { - PresetRecord* record = (PresetRecord*)data; - jalv_apply_preset(record->jalv, record->preset); - if (active_preset_item) { - gtk_check_menu_item_set_active(active_preset_item, FALSE); - } - - active_preset_item = GTK_CHECK_MENU_ITEM(widget); - gtk_check_menu_item_set_active(active_preset_item, TRUE); - set_window_title(record->jalv); - } + if (GTK_CHECK_MENU_ITEM(widget) != active_preset_item) { + PresetRecord* record = (PresetRecord*)data; + jalv_apply_preset(record->jalv, record->preset); + if (active_preset_item) { + gtk_check_menu_item_set_active(active_preset_item, FALSE); + } + + active_preset_item = GTK_CHECK_MENU_ITEM(widget); + gtk_check_menu_item_set_active(active_preset_item, TRUE); + set_window_title(record->jalv); + } } static void on_preset_destroy(gpointer data, GClosure* ZIX_UNUSED(closure)) { - PresetRecord* record = (PresetRecord*)data; - lilv_node_free(record->preset); - free(record); + PresetRecord* record = (PresetRecord*)data; + lilv_node_free(record->preset); + free(record); } typedef struct { - GtkMenuItem* item; - char* label; - GtkMenu* menu; - GSequence* banks; + GtkMenuItem* item; + char* label; + GtkMenu* menu; + GSequence* banks; } PresetMenu; static PresetMenu* pset_menu_new(const char* label) { - PresetMenu* menu = (PresetMenu*)malloc(sizeof(PresetMenu)); - menu->label = g_strdup(label); - menu->item = GTK_MENU_ITEM(gtk_menu_item_new_with_label(menu->label)); - menu->menu = GTK_MENU(gtk_menu_new()); - menu->banks = NULL; - return menu; + PresetMenu* menu = (PresetMenu*)malloc(sizeof(PresetMenu)); + menu->label = g_strdup(label); + menu->item = GTK_MENU_ITEM(gtk_menu_item_new_with_label(menu->label)); + menu->menu = GTK_MENU(gtk_menu_new()); + menu->banks = NULL; + return menu; } static void pset_menu_free(PresetMenu* menu) { - if (menu->banks) { - for (GSequenceIter* i = g_sequence_get_begin_iter(menu->banks); - !g_sequence_iter_is_end(i); - i = g_sequence_iter_next(i)) { - PresetMenu* bank_menu = (PresetMenu*)g_sequence_get(i); - pset_menu_free(bank_menu); - } - g_sequence_free(menu->banks); - } - - free(menu->label); - free(menu); + if (menu->banks) { + for (GSequenceIter* i = g_sequence_get_begin_iter(menu->banks); + !g_sequence_iter_is_end(i); + i = g_sequence_iter_next(i)) { + PresetMenu* bank_menu = (PresetMenu*)g_sequence_get(i); + pset_menu_free(bank_menu); + } + g_sequence_free(menu->banks); + } + + free(menu->label); + free(menu); } static gint menu_cmp(gconstpointer a, gconstpointer b, gpointer ZIX_UNUSED(data)) { - return strcmp(((const PresetMenu*)a)->label, ((const PresetMenu*)b)->label); + return strcmp(((const PresetMenu*)a)->label, ((const PresetMenu*)b)->label); } static PresetMenu* get_bank_menu(Jalv* jalv, PresetMenu* menu, const LilvNode* bank) { - LilvNode* label = lilv_world_get( - jalv->world, bank, jalv->nodes.rdfs_label, NULL); - - const char* uri = lilv_node_as_string(bank); - const char* str = label ? lilv_node_as_string(label) : uri; - PresetMenu key = { NULL, (char*)str, NULL, NULL }; - GSequenceIter* i = g_sequence_lookup(menu->banks, &key, menu_cmp, NULL); - if (!i) { - PresetMenu* bank_menu = pset_menu_new(str); - gtk_menu_item_set_submenu(bank_menu->item, GTK_WIDGET(bank_menu->menu)); - g_sequence_insert_sorted(menu->banks, bank_menu, menu_cmp, NULL); - return bank_menu; - } - return (PresetMenu*)g_sequence_get(i); + LilvNode* label = + lilv_world_get(jalv->world, bank, jalv->nodes.rdfs_label, NULL); + + const char* uri = lilv_node_as_string(bank); + const char* str = label ? lilv_node_as_string(label) : uri; + PresetMenu key = {NULL, (char*)str, NULL, NULL}; + GSequenceIter* i = g_sequence_lookup(menu->banks, &key, menu_cmp, NULL); + if (!i) { + PresetMenu* bank_menu = pset_menu_new(str); + gtk_menu_item_set_submenu(bank_menu->item, GTK_WIDGET(bank_menu->menu)); + g_sequence_insert_sorted(menu->banks, bank_menu, menu_cmp, NULL); + return bank_menu; + } + return (PresetMenu*)g_sequence_get(i); } static int @@ -319,185 +398,190 @@ add_preset_to_menu(Jalv* jalv, const LilvNode* title, void* data) { - PresetMenu* menu = (PresetMenu*)data; - const char* label = lilv_node_as_string(title); - GtkWidget* item = gtk_check_menu_item_new_with_label(label); - gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE); - if (jalv->preset && - lilv_node_equals(lilv_state_get_uri(jalv->preset), node)) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE); - active_preset_item = GTK_CHECK_MENU_ITEM(item); - } - - LilvNode* bank = lilv_world_get( - jalv->world, node, jalv->nodes.pset_bank, NULL); - - if (bank) { - PresetMenu* bank_menu = get_bank_menu(jalv, menu, bank); - gtk_menu_shell_append(GTK_MENU_SHELL(bank_menu->menu), item); - } else { - gtk_menu_shell_append(GTK_MENU_SHELL(menu->menu), item); - } - - PresetRecord* record = (PresetRecord*)malloc(sizeof(PresetRecord)); - record->jalv = jalv; - record->preset = lilv_node_duplicate(node); - - g_signal_connect_data(G_OBJECT(item), "activate", - G_CALLBACK(on_preset_activate), - record, on_preset_destroy, - (GConnectFlags)0); - - return 0; + PresetMenu* menu = (PresetMenu*)data; + const char* label = lilv_node_as_string(title); + GtkWidget* item = gtk_check_menu_item_new_with_label(label); + gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE); + if (jalv->preset && + lilv_node_equals(lilv_state_get_uri(jalv->preset), node)) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE); + active_preset_item = GTK_CHECK_MENU_ITEM(item); + } + + LilvNode* bank = + lilv_world_get(jalv->world, node, jalv->nodes.pset_bank, NULL); + + if (bank) { + PresetMenu* bank_menu = get_bank_menu(jalv, menu, bank); + gtk_menu_shell_append(GTK_MENU_SHELL(bank_menu->menu), item); + } else { + gtk_menu_shell_append(GTK_MENU_SHELL(menu->menu), item); + } + + PresetRecord* record = (PresetRecord*)malloc(sizeof(PresetRecord)); + record->jalv = jalv; + record->preset = lilv_node_duplicate(node); + + g_signal_connect_data(G_OBJECT(item), + "activate", + G_CALLBACK(on_preset_activate), + record, + on_preset_destroy, + (GConnectFlags)0); + + return 0; } static void finish_menu(PresetMenu* menu) { - for (GSequenceIter* i = g_sequence_get_begin_iter(menu->banks); - !g_sequence_iter_is_end(i); - i = g_sequence_iter_next(i)) { - PresetMenu* bank_menu = (PresetMenu*)g_sequence_get(i); - gtk_menu_shell_append(GTK_MENU_SHELL(menu->menu), - GTK_WIDGET(bank_menu->item)); - } - g_sequence_free(menu->banks); + for (GSequenceIter* i = g_sequence_get_begin_iter(menu->banks); + !g_sequence_iter_is_end(i); + i = g_sequence_iter_next(i)) { + PresetMenu* bank_menu = (PresetMenu*)g_sequence_get(i); + gtk_menu_shell_append(GTK_MENU_SHELL(menu->menu), + GTK_WIDGET(bank_menu->item)); + } + g_sequence_free(menu->banks); } static void rebuild_preset_menu(Jalv* jalv, GtkContainer* pset_menu) { - // Clear current menu - active_preset_item = NULL; - for (GList* items = g_list_nth(gtk_container_get_children(pset_menu), 3); - items; - items = items->next) { - gtk_container_remove(pset_menu, GTK_WIDGET(items->data)); - } - - // Load presets and build new menu - PresetMenu menu = { - NULL, NULL, GTK_MENU(pset_menu), - g_sequence_new((GDestroyNotify)pset_menu_free) - }; - jalv_load_presets(jalv, add_preset_to_menu, &menu); - finish_menu(&menu); - gtk_widget_show_all(GTK_WIDGET(pset_menu)); + // Clear current menu + active_preset_item = NULL; + for (GList* items = g_list_nth(gtk_container_get_children(pset_menu), 3); + items; + items = items->next) { + gtk_container_remove(pset_menu, GTK_WIDGET(items->data)); + } + + // Load presets and build new menu + PresetMenu menu = {NULL, + NULL, + GTK_MENU(pset_menu), + g_sequence_new((GDestroyNotify)pset_menu_free)}; + jalv_load_presets(jalv, add_preset_to_menu, &menu); + finish_menu(&menu); + gtk_widget_show_all(GTK_WIDGET(pset_menu)); } static void on_save_preset_activate(GtkWidget* widget, void* ptr) { - Jalv* jalv = (Jalv*)ptr; - - GtkWidget* dialog = gtk_file_chooser_dialog_new( - "Save Preset", - (GtkWindow*)jalv->window, - GTK_FILE_CHOOSER_ACTION_SAVE, - "_Cancel", GTK_RESPONSE_REJECT, - "_Save", GTK_RESPONSE_ACCEPT, - NULL); - - char* dot_lv2 = g_build_filename(g_get_home_dir(), ".lv2", NULL); - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dot_lv2); - free(dot_lv2); - - GtkWidget* content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - GtkBox* box = GTK_BOX(new_box(true, 8)); - GtkWidget* uri_label = gtk_label_new("URI (Optional):"); - GtkWidget* uri_entry = gtk_entry_new(); - GtkWidget* add_prefix = gtk_check_button_new_with_mnemonic( - "_Prefix plugin name"); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(add_prefix), TRUE); - gtk_box_pack_start(box, uri_label, FALSE, TRUE, 2); - gtk_box_pack_start(box, uri_entry, TRUE, TRUE, 2); - gtk_box_pack_start(GTK_BOX(content), GTK_WIDGET(box), FALSE, FALSE, 6); - gtk_box_pack_start(GTK_BOX(content), add_prefix, FALSE, FALSE, 6); - - gtk_widget_show_all(GTK_WIDGET(dialog)); - gtk_entry_set_activates_default(GTK_ENTRY(uri_entry), TRUE); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - LilvNode* plug_name = lilv_plugin_get_name(jalv->plugin); - const char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - const char* uri = gtk_entry_get_text(GTK_ENTRY(uri_entry)); - const char* prefix = ""; - const char* sep = ""; - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(add_prefix))) { - prefix = lilv_node_as_string(plug_name); - sep = "_"; - } - - char* dirname = g_path_get_dirname(path); - char* basename = g_path_get_basename(path); - char* sym = symbolify(basename); - char* sprefix = symbolify(prefix); - char* bundle = g_strjoin(NULL, sprefix, sep, sym, ".preset.lv2/", NULL); - char* file = g_strjoin(NULL, sym, ".ttl", NULL); - char* dir = g_build_filename(dirname, bundle, NULL); - - jalv_save_preset(jalv, dir, (strlen(uri) ? uri : NULL), basename, file); - - // Reload bundle into the world - LilvNode* ldir = lilv_new_file_uri(jalv->world, NULL, dir); - lilv_world_unload_bundle(jalv->world, ldir); - lilv_world_load_bundle(jalv->world, ldir); - lilv_node_free(ldir); - - // Rebuild preset menu and update window title - rebuild_preset_menu(jalv, GTK_CONTAINER(gtk_widget_get_parent(widget))); - set_window_title(jalv); - - g_free(dir); - g_free(file); - g_free(bundle); - free(sprefix); - free(sym); - g_free(basename); - g_free(dirname); - lilv_node_free(plug_name); - } - - gtk_widget_destroy(GTK_WIDGET(dialog)); + Jalv* jalv = (Jalv*)ptr; + + GtkWidget* dialog = gtk_file_chooser_dialog_new("Save Preset", + (GtkWindow*)jalv->window, + GTK_FILE_CHOOSER_ACTION_SAVE, + "_Cancel", + GTK_RESPONSE_REJECT, + "_Save", + GTK_RESPONSE_ACCEPT, + NULL); + + char* dot_lv2 = g_build_filename(g_get_home_dir(), ".lv2", NULL); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dot_lv2); + free(dot_lv2); + + GtkWidget* content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkBox* box = GTK_BOX(new_box(true, 8)); + GtkWidget* uri_label = gtk_label_new("URI (Optional):"); + GtkWidget* uri_entry = gtk_entry_new(); + GtkWidget* add_prefix = + gtk_check_button_new_with_mnemonic("_Prefix plugin name"); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(add_prefix), TRUE); + gtk_box_pack_start(box, uri_label, FALSE, TRUE, 2); + gtk_box_pack_start(box, uri_entry, TRUE, TRUE, 2); + gtk_box_pack_start(GTK_BOX(content), GTK_WIDGET(box), FALSE, FALSE, 6); + gtk_box_pack_start(GTK_BOX(content), add_prefix, FALSE, FALSE, 6); + + gtk_widget_show_all(GTK_WIDGET(dialog)); + gtk_entry_set_activates_default(GTK_ENTRY(uri_entry), TRUE); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + LilvNode* plug_name = lilv_plugin_get_name(jalv->plugin); + const char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + const char* uri = gtk_entry_get_text(GTK_ENTRY(uri_entry)); + const char* prefix = ""; + const char* sep = ""; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(add_prefix))) { + prefix = lilv_node_as_string(plug_name); + sep = "_"; + } + + char* dirname = g_path_get_dirname(path); + char* basename = g_path_get_basename(path); + char* sym = symbolify(basename); + char* sprefix = symbolify(prefix); + char* bundle = g_strjoin(NULL, sprefix, sep, sym, ".preset.lv2/", NULL); + char* file = g_strjoin(NULL, sym, ".ttl", NULL); + char* dir = g_build_filename(dirname, bundle, NULL); + + jalv_save_preset(jalv, dir, (strlen(uri) ? uri : NULL), basename, file); + + // Reload bundle into the world + LilvNode* ldir = lilv_new_file_uri(jalv->world, NULL, dir); + lilv_world_unload_bundle(jalv->world, ldir); + lilv_world_load_bundle(jalv->world, ldir); + lilv_node_free(ldir); + + // Rebuild preset menu and update window title + rebuild_preset_menu(jalv, GTK_CONTAINER(gtk_widget_get_parent(widget))); + set_window_title(jalv); + + g_free(dir); + g_free(file); + g_free(bundle); + free(sprefix); + free(sym); + g_free(basename); + g_free(dirname); + lilv_node_free(plug_name); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); } static void on_delete_preset_activate(GtkWidget* widget, void* ptr) { - Jalv* jalv = (Jalv*)ptr; - if (!jalv->preset) { - return; - } - - GtkWidget* dialog = gtk_dialog_new_with_buttons( - "Delete Preset?", - (GtkWindow*)jalv->window, - (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), - "_Cancel", GTK_RESPONSE_REJECT, - "_OK", GTK_RESPONSE_ACCEPT, - NULL); - - char* msg = g_strdup_printf("Delete preset \"%s\" from the file system?", - lilv_state_get_label(jalv->preset)); - - GtkWidget* content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - GtkWidget* text = gtk_label_new(msg); - gtk_box_pack_start(GTK_BOX(content), text, TRUE, TRUE, 4); - - gtk_widget_show_all(dialog); - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - jalv_delete_current_preset(jalv); - rebuild_preset_menu(jalv, GTK_CONTAINER(gtk_widget_get_parent(widget))); - } - - lilv_state_free(jalv->preset); - jalv->preset = NULL; - set_window_title(jalv); - - g_free(msg); - gtk_widget_destroy(text); - gtk_widget_destroy(dialog); + Jalv* jalv = (Jalv*)ptr; + if (!jalv->preset) { + return; + } + + GtkWidget* dialog = gtk_dialog_new_with_buttons( + "Delete Preset?", + (GtkWindow*)jalv->window, + (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), + "_Cancel", + GTK_RESPONSE_REJECT, + "_OK", + GTK_RESPONSE_ACCEPT, + NULL); + + char* msg = g_strdup_printf("Delete preset \"%s\" from the file system?", + lilv_state_get_label(jalv->preset)); + + GtkWidget* content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkWidget* text = gtk_label_new(msg); + gtk_box_pack_start(GTK_BOX(content), text, TRUE, TRUE, 4); + + gtk_widget_show_all(dialog); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + jalv_delete_current_preset(jalv); + rebuild_preset_menu(jalv, GTK_CONTAINER(gtk_widget_get_parent(widget))); + } + + lilv_state_free(jalv->preset); + jalv->preset = NULL; + set_window_title(jalv); + + g_free(msg); + gtk_widget_destroy(text); + gtk_widget_destroy(dialog); } static void @@ -506,41 +590,41 @@ set_control(const ControlID* control, LV2_URID type, const void* body) { - if (!updating) { - jalv_set_control(control, size, type, body); - } + if (!updating) { + jalv_set_control(control, size, type, body); + } } static bool differ_enough(float a, float b) { - return fabsf(a - b) >= FLT_EPSILON; + return fabsf(a - b) >= FLT_EPSILON; } static void set_float_control(const ControlID* control, float value) { - if (control->value_type == control->jalv->forge.Int) { - const int32_t ival = lrintf(value); - set_control(control, sizeof(ival), control->jalv->forge.Int, &ival); - } else if (control->value_type == control->jalv->forge.Long) { - const int64_t lval = lrintf(value); - set_control(control, sizeof(lval), control->jalv->forge.Long, &lval); - } else if (control->value_type == control->jalv->forge.Float) { - set_control(control, sizeof(value), control->jalv->forge.Float, &value); - } else if (control->value_type == control->jalv->forge.Double) { - const double dval = value; - set_control(control, sizeof(dval), control->jalv->forge.Double, &dval); - } else if (control->value_type == control->jalv->forge.Bool) { - const int32_t ival = value; - set_control(control, sizeof(ival), control->jalv->forge.Bool, &ival); - } - - Controller* controller = (Controller*)control->widget; - if (controller && controller->spin && - differ_enough(gtk_spin_button_get_value(controller->spin), value)) { - gtk_spin_button_set_value(controller->spin, value); - } + if (control->value_type == control->jalv->forge.Int) { + const int32_t ival = lrintf(value); + set_control(control, sizeof(ival), control->jalv->forge.Int, &ival); + } else if (control->value_type == control->jalv->forge.Long) { + const int64_t lval = lrintf(value); + set_control(control, sizeof(lval), control->jalv->forge.Long, &lval); + } else if (control->value_type == control->jalv->forge.Float) { + set_control(control, sizeof(value), control->jalv->forge.Float, &value); + } else if (control->value_type == control->jalv->forge.Double) { + const double dval = value; + set_control(control, sizeof(dval), control->jalv->forge.Double, &dval); + } else if (control->value_type == control->jalv->forge.Bool) { + const int32_t ival = value; + set_control(control, sizeof(ival), control->jalv->forge.Bool, &ival); + } + + Controller* controller = (Controller*)control->widget; + if (controller && controller->spin && + differ_enough(gtk_spin_button_get_value(controller->spin), value)) { + gtk_spin_button_set_value(controller->spin, value); + } } static double @@ -549,23 +633,23 @@ get_atom_double(Jalv* jalv, LV2_URID type, const void* body) { - if (type == jalv->forge.Int || type == jalv->forge.Bool) { - return *(const int32_t*)body; - } + if (type == jalv->forge.Int || type == jalv->forge.Bool) { + return *(const int32_t*)body; + } - if (type == jalv->forge.Long) { - return *(const int64_t*)body; - } + if (type == jalv->forge.Long) { + return *(const int64_t*)body; + } - if (type == jalv->forge.Float) { - return *(const float*)body; - } + if (type == jalv->forge.Float) { + return *(const float*)body; + } - if (type == jalv->forge.Double) { - return *(const double*)body; - } + if (type == jalv->forge.Double) { + return *(const double*)body; + } - return NAN; + return NAN; } static void @@ -575,46 +659,44 @@ control_changed(Jalv* jalv, LV2_URID type, const void* body) { - GtkWidget* widget = controller->control; - const double fvalue = get_atom_double(jalv, size, type, body); - - if (!isnan(fvalue)) { - if (GTK_IS_COMBO_BOX(widget)) { - GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); - GValue value = { 0, { { 0 } } }; - GtkTreeIter i; - bool valid = gtk_tree_model_get_iter_first(model, &i); - while (valid) { - gtk_tree_model_get_value(model, &i, 0, &value); - const double v = g_value_get_float(&value); - g_value_unset(&value); - if (fabs(v - fvalue) < FLT_EPSILON) { - gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &i); - return; - } - valid = gtk_tree_model_iter_next(model, &i); - } - } else if (GTK_IS_TOGGLE_BUTTON(widget)) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), - fvalue > 0.0f); - } else if (GTK_IS_RANGE(widget)) { - gtk_range_set_value(GTK_RANGE(widget), fvalue); - } else { - fprintf(stderr, "Unknown widget type for value\n"); - } - - if (controller->spin) { - // Update spinner for numeric control - gtk_spin_button_set_value(GTK_SPIN_BUTTON(controller->spin), - fvalue); - } - } else if (GTK_IS_ENTRY(widget) && type == jalv->urids.atom_String) { - gtk_entry_set_text(GTK_ENTRY(widget), (const char*)body); - } else if (GTK_IS_FILE_CHOOSER(widget) && type == jalv->urids.atom_Path) { - gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), (const char*)body); - } else { - fprintf(stderr, "Unknown widget type for value\n"); - } + GtkWidget* widget = controller->control; + const double fvalue = get_atom_double(jalv, size, type, body); + + if (!isnan(fvalue)) { + if (GTK_IS_COMBO_BOX(widget)) { + GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); + GValue value = {0, {{0}}}; + GtkTreeIter i; + bool valid = gtk_tree_model_get_iter_first(model, &i); + while (valid) { + gtk_tree_model_get_value(model, &i, 0, &value); + const double v = g_value_get_float(&value); + g_value_unset(&value); + if (fabs(v - fvalue) < FLT_EPSILON) { + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &i); + return; + } + valid = gtk_tree_model_iter_next(model, &i); + } + } else if (GTK_IS_TOGGLE_BUTTON(widget)) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), fvalue > 0.0f); + } else if (GTK_IS_RANGE(widget)) { + gtk_range_set_value(GTK_RANGE(widget), fvalue); + } else { + fprintf(stderr, "Unknown widget type for value\n"); + } + + if (controller->spin) { + // Update spinner for numeric control + gtk_spin_button_set_value(GTK_SPIN_BUTTON(controller->spin), fvalue); + } + } else if (GTK_IS_ENTRY(widget) && type == jalv->urids.atom_String) { + gtk_entry_set_text(GTK_ENTRY(widget), (const char*)body); + } else if (GTK_IS_FILE_CHOOSER(widget) && type == jalv->urids.atom_Path) { + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), (const char*)body); + } else { + fprintf(stderr, "Unknown widget type for value\n"); + } } static int @@ -623,21 +705,23 @@ patch_set_get(Jalv* jalv, const LV2_Atom_URID** property, const LV2_Atom** value) { - lv2_atom_object_get(obj, - jalv->urids.patch_property, (const LV2_Atom*)property, - jalv->urids.patch_value, value, - 0); - if (!*property) { - fprintf(stderr, "patch:Set message with no property\n"); - return 1; - } - - if ((*property)->atom.type != jalv->forge.URID) { - fprintf(stderr, "patch:Set property is not a URID\n"); - return 1; - } - - return 0; + lv2_atom_object_get(obj, + jalv->urids.patch_property, + (const LV2_Atom*)property, + jalv->urids.patch_value, + value, + 0); + if (!*property) { + fprintf(stderr, "patch:Set message with no property\n"); + return 1; + } + + if ((*property)->atom.type != jalv->forge.URID) { + fprintf(stderr, "patch:Set property is not a URID\n"); + return 1; + } + + return 0; } static int @@ -645,20 +729,18 @@ patch_put_get(Jalv* jalv, const LV2_Atom_Object* obj, const LV2_Atom_Object** body) { - lv2_atom_object_get(obj, - jalv->urids.patch_body, (const LV2_Atom*)body, - 0); - if (!*body) { - fprintf(stderr, "patch:Put message with no body\n"); - return 1; - } - - if (!lv2_atom_forge_is_object_type(&jalv->forge, (*body)->atom.type)) { - fprintf(stderr, "patch:Put body is not an object\n"); - return 1; - } - - return 0; + lv2_atom_object_get(obj, jalv->urids.patch_body, (const LV2_Atom*)body, 0); + if (!*body) { + fprintf(stderr, "patch:Put message with no body\n"); + return 1; + } + + if (!lv2_atom_forge_is_object_type(&jalv->forge, (*body)->atom.type)) { + fprintf(stderr, "patch:Put body is not an object\n"); + return 1; + } + + return 0; } static LV2UI_Request_Value_Status @@ -667,49 +749,47 @@ on_request_value(LV2UI_Feature_Handle handle, const LV2_URID ZIX_UNUSED(type), const LV2_Feature* const* ZIX_UNUSED(features)) { - Jalv* jalv = (Jalv*)handle; - ControlID* control = get_property_control(&jalv->controls, key); + Jalv* jalv = (Jalv*)handle; + ControlID* control = get_property_control(&jalv->controls, key); - if (!control) { - return LV2UI_REQUEST_VALUE_ERR_UNKNOWN; - } + if (!control) { + return LV2UI_REQUEST_VALUE_ERR_UNKNOWN; + } - if (control->value_type != jalv->forge.Path) { - return LV2UI_REQUEST_VALUE_ERR_UNSUPPORTED; - } + if (control->value_type != jalv->forge.Path) { + return LV2UI_REQUEST_VALUE_ERR_UNSUPPORTED; + } - GtkWidget* dialog = - gtk_file_chooser_dialog_new("Choose file", - GTK_WINDOW(jalv->window), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, GTK_RESPONSE_OK, - NULL); + GtkWidget* dialog = gtk_file_chooser_dialog_new("Choose file", + GTK_WINDOW(jalv->window), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, + GTK_RESPONSE_OK, + NULL); - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { - char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { + char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - set_control(control, strlen(path) + 1, jalv->forge.Path, path); + set_control(control, strlen(path) + 1, jalv->forge.Path, path); - g_free(path); - } + g_free(path); + } - gtk_widget_destroy(dialog); + gtk_widget_destroy(dialog); - return 0; + return 0; } static void property_changed(Jalv* jalv, LV2_URID key, const LV2_Atom* value) { - ControlID* control = get_property_control(&jalv->controls, key); - if (control) { - control_changed(jalv, - (Controller*)control->widget, - value->size, - value->type, - value + 1); - } + ControlID* control = get_property_control(&jalv->controls, key); + if (control) { + control_changed( + jalv, (Controller*)control->widget, value->size, value->type, value + 1); + } } void @@ -719,334 +799,329 @@ jalv_ui_port_event(Jalv* jalv, uint32_t protocol, const void* buffer) { - if (jalv->ui_instance) { - suil_instance_port_event(jalv->ui_instance, port_index, - buffer_size, protocol, buffer); - return; - } - - if (protocol == 0 && (Controller*)jalv->ports[port_index].widget) { - control_changed(jalv, - (Controller*)jalv->ports[port_index].widget, - buffer_size, - jalv->forge.Float, - buffer); - return; - } - - if (protocol == 0) { - return; // No widget (probably notOnGUI) - } - - if (protocol != jalv->urids.atom_eventTransfer) { - fprintf(stderr, "Unknown port event protocol\n"); - return; - } - - const LV2_Atom* atom = (const LV2_Atom*)buffer; - if (lv2_atom_forge_is_object_type(&jalv->forge, atom->type)) { - updating = true; - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)buffer; - if (obj->body.otype == jalv->urids.patch_Set) { - const LV2_Atom_URID* property = NULL; - const LV2_Atom* value = NULL; - if (!patch_set_get(jalv, obj, &property, &value)) { - property_changed(jalv, property->body, value); - } - } else if (obj->body.otype == jalv->urids.patch_Put) { - const LV2_Atom_Object* body = NULL; - if (!patch_put_get(jalv, obj, &body)) { - LV2_ATOM_OBJECT_FOREACH(body, prop) { - property_changed(jalv, prop->key, &prop->value); - } - } - } else { - printf("Unknown object type?\n"); - } - updating = false; - } + if (jalv->ui_instance) { + suil_instance_port_event( + jalv->ui_instance, port_index, buffer_size, protocol, buffer); + return; + } + + if (protocol == 0 && (Controller*)jalv->ports[port_index].widget) { + control_changed(jalv, + (Controller*)jalv->ports[port_index].widget, + buffer_size, + jalv->forge.Float, + buffer); + return; + } + + if (protocol == 0) { + return; // No widget (probably notOnGUI) + } + + if (protocol != jalv->urids.atom_eventTransfer) { + fprintf(stderr, "Unknown port event protocol\n"); + return; + } + + const LV2_Atom* atom = (const LV2_Atom*)buffer; + if (lv2_atom_forge_is_object_type(&jalv->forge, atom->type)) { + updating = true; + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)buffer; + if (obj->body.otype == jalv->urids.patch_Set) { + const LV2_Atom_URID* property = NULL; + const LV2_Atom* value = NULL; + if (!patch_set_get(jalv, obj, &property, &value)) { + property_changed(jalv, property->body, value); + } + } else if (obj->body.otype == jalv->urids.patch_Put) { + const LV2_Atom_Object* body = NULL; + if (!patch_put_get(jalv, obj, &body)) { + LV2_ATOM_OBJECT_FOREACH(body, prop) + { + property_changed(jalv, prop->key, &prop->value); + } + } + } else { + printf("Unknown object type?\n"); + } + updating = false; + } } static gboolean scale_changed(GtkRange* range, gpointer data) { - set_float_control((const ControlID*)data, gtk_range_get_value(range)); - return FALSE; + set_float_control((const ControlID*)data, gtk_range_get_value(range)); + return FALSE; } static gboolean spin_changed(GtkSpinButton* spin, gpointer data) { - const ControlID* control = (const ControlID*)data; - Controller* controller = (Controller*)control->widget; - GtkRange* range = GTK_RANGE(controller->control); - const double value = gtk_spin_button_get_value(spin); - if (differ_enough(gtk_range_get_value(range), value)) { - gtk_range_set_value(range, value); - } - return FALSE; + const ControlID* control = (const ControlID*)data; + Controller* controller = (Controller*)control->widget; + GtkRange* range = GTK_RANGE(controller->control); + const double value = gtk_spin_button_get_value(spin); + if (differ_enough(gtk_range_get_value(range), value)) { + gtk_range_set_value(range, value); + } + return FALSE; } static gboolean log_scale_changed(GtkRange* range, gpointer data) { - set_float_control((const ControlID*)data, expf(gtk_range_get_value(range))); - return FALSE; + set_float_control((const ControlID*)data, expf(gtk_range_get_value(range))); + return FALSE; } static gboolean log_spin_changed(GtkSpinButton* spin, gpointer data) { - const ControlID* control = (const ControlID*)data; - Controller* controller = (Controller*)control->widget; - GtkRange* range = GTK_RANGE(controller->control); - const double value = gtk_spin_button_get_value(spin); - if (differ_enough(gtk_range_get_value(range), logf(value))) { - gtk_range_set_value(range, logf(value)); - } - return FALSE; + const ControlID* control = (const ControlID*)data; + Controller* controller = (Controller*)control->widget; + GtkRange* range = GTK_RANGE(controller->control); + const double value = gtk_spin_button_get_value(spin); + if (differ_enough(gtk_range_get_value(range), logf(value))) { + gtk_range_set_value(range, logf(value)); + } + return FALSE; } static void combo_changed(GtkComboBox* box, gpointer data) { - const ControlID* control = (const ControlID*)data; + const ControlID* control = (const ControlID*)data; - GtkTreeIter iter; - if (gtk_combo_box_get_active_iter(box, &iter)) { - GtkTreeModel* model = gtk_combo_box_get_model(box); - GValue value = { 0, { { 0 } } }; + GtkTreeIter iter; + if (gtk_combo_box_get_active_iter(box, &iter)) { + GtkTreeModel* model = gtk_combo_box_get_model(box); + GValue value = {0, {{0}}}; - gtk_tree_model_get_value(model, &iter, 0, &value); - const double v = g_value_get_float(&value); - g_value_unset(&value); + gtk_tree_model_get_value(model, &iter, 0, &value); + const double v = g_value_get_float(&value); + g_value_unset(&value); - set_float_control(control, v); - } + set_float_control(control, v); + } } static gboolean toggle_changed(GtkToggleButton* button, gpointer data) { - set_float_control((const ControlID*)data, - gtk_toggle_button_get_active(button) ? 1.0f : 0.0f); - return FALSE; + set_float_control((const ControlID*)data, + gtk_toggle_button_get_active(button) ? 1.0f : 0.0f); + return FALSE; } static void string_changed(GtkEntry* widget, gpointer data) { - ControlID* control = (ControlID*)data; - const char* string = gtk_entry_get_text(widget); + ControlID* control = (ControlID*)data; + const char* string = gtk_entry_get_text(widget); - set_control(control, strlen(string) + 1, control->jalv->forge.String, string); + set_control(control, strlen(string) + 1, control->jalv->forge.String, string); } static void -file_changed(GtkFileChooserButton* widget, - gpointer data) +file_changed(GtkFileChooserButton* widget, gpointer data) { - ControlID* control = (ControlID*)data; - Jalv* jalv = control->jalv; - const char* filename = gtk_file_chooser_get_filename( - GTK_FILE_CHOOSER(widget)); + ControlID* control = (ControlID*)data; + Jalv* jalv = control->jalv; + const char* filename = + gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)); - set_control(control, strlen(filename) + 1, jalv->forge.Path, filename); + set_control(control, strlen(filename) + 1, jalv->forge.Path, filename); } static Controller* new_controller(GtkSpinButton* spin, GtkWidget* control) { - Controller* controller = (Controller*)malloc(sizeof(Controller)); - controller->spin = spin; - controller->control = control; - return controller; + Controller* controller = (Controller*)malloc(sizeof(Controller)); + controller->spin = spin; + controller->control = control; + return controller; } static Controller* make_combo(ControlID* record, float value) { - GtkListStore* list_store = gtk_list_store_new( - 2, G_TYPE_FLOAT, G_TYPE_STRING); - int active = -1; - for (size_t i = 0; i < record->n_points; ++i) { - const ScalePoint* point = &record->points[i]; - GtkTreeIter iter; - gtk_list_store_append(list_store, &iter); - gtk_list_store_set(list_store, &iter, - 0, point->value, - 1, point->label, - -1); - if (fabsf(value - point->value) < FLT_EPSILON) { - active = i; - } - } - - GtkWidget* combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store)); - gtk_combo_box_set_active(GTK_COMBO_BOX(combo), active); - g_object_unref(list_store); - - gtk_widget_set_sensitive(combo, record->is_writable); - - GtkCellRenderer* cell = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", 1, NULL); - - if (record->is_writable) { - g_signal_connect(G_OBJECT(combo), "changed", - G_CALLBACK(combo_changed), record); - } - - return new_controller(NULL, combo); + GtkListStore* list_store = gtk_list_store_new(2, G_TYPE_FLOAT, G_TYPE_STRING); + int active = -1; + for (size_t i = 0; i < record->n_points; ++i) { + const ScalePoint* point = &record->points[i]; + GtkTreeIter iter; + gtk_list_store_append(list_store, &iter); + gtk_list_store_set(list_store, &iter, 0, point->value, 1, point->label, -1); + if (fabsf(value - point->value) < FLT_EPSILON) { + active = i; + } + } + + GtkWidget* combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store)); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), active); + g_object_unref(list_store); + + gtk_widget_set_sensitive(combo, record->is_writable); + + GtkCellRenderer* cell = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", 1, NULL); + + if (record->is_writable) { + g_signal_connect( + G_OBJECT(combo), "changed", G_CALLBACK(combo_changed), record); + } + + return new_controller(NULL, combo); } static Controller* make_log_slider(ControlID* record, float value) { - const float min = get_float(record->min, 0.0f); - const float max = get_float(record->max, 1.0f); - const float lmin = logf(min); - const float lmax = logf(max); - const float ldft = logf(value); - GtkWidget* scale = new_hscale(lmin, lmax, 0.001); - GtkWidget* spin = gtk_spin_button_new_with_range(min, max, 0.000001); - - gtk_widget_set_sensitive(scale, record->is_writable); - gtk_widget_set_sensitive(spin, record->is_writable); - - gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE); - gtk_range_set_value(GTK_RANGE(scale), ldft); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value); - - if (record->is_writable) { - g_signal_connect(G_OBJECT(scale), "value-changed", - G_CALLBACK(log_scale_changed), record); - g_signal_connect(G_OBJECT(spin), "value-changed", - G_CALLBACK(log_spin_changed), record); - } - - return new_controller(GTK_SPIN_BUTTON(spin), scale); + const float min = get_float(record->min, 0.0f); + const float max = get_float(record->max, 1.0f); + const float lmin = logf(min); + const float lmax = logf(max); + const float ldft = logf(value); + GtkWidget* scale = new_hscale(lmin, lmax, 0.001); + GtkWidget* spin = gtk_spin_button_new_with_range(min, max, 0.000001); + + gtk_widget_set_sensitive(scale, record->is_writable); + gtk_widget_set_sensitive(spin, record->is_writable); + + gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE); + gtk_range_set_value(GTK_RANGE(scale), ldft); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value); + + if (record->is_writable) { + g_signal_connect( + G_OBJECT(scale), "value-changed", G_CALLBACK(log_scale_changed), record); + g_signal_connect( + G_OBJECT(spin), "value-changed", G_CALLBACK(log_spin_changed), record); + } + + return new_controller(GTK_SPIN_BUTTON(spin), scale); } static Controller* make_slider(ControlID* record, float value) { - const float min = get_float(record->min, 0.0f); - const float max = get_float(record->max, 1.0f); - const double step = record->is_integer ? 1.0 : ((max - min) / 100.0); - GtkWidget* scale = new_hscale(min, max, step); - GtkWidget* spin = gtk_spin_button_new_with_range(min, max, step); - - gtk_widget_set_sensitive(scale, record->is_writable); - gtk_widget_set_sensitive(spin, record->is_writable); - - if (record->is_integer) { - gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 0); - } else { - gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 7); - } - - gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE); - gtk_range_set_value(GTK_RANGE(scale), value); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value); - if (record->points) { - for (size_t i = 0; i < record->n_points; ++i) { - const ScalePoint* point = &record->points[i]; - - gchar* str = g_markup_printf_escaped( - "<span font_size=\"small\">%s</span>", point->label); - gtk_scale_add_mark( - GTK_SCALE(scale), point->value, GTK_POS_TOP, str); - } - } - - if (record->is_writable) { - g_signal_connect(G_OBJECT(scale), "value-changed", - G_CALLBACK(scale_changed), record); - g_signal_connect(G_OBJECT(spin), "value-changed", - G_CALLBACK(spin_changed), record); - } - - return new_controller(GTK_SPIN_BUTTON(spin), scale); + const float min = get_float(record->min, 0.0f); + const float max = get_float(record->max, 1.0f); + const double step = record->is_integer ? 1.0 : ((max - min) / 100.0); + GtkWidget* scale = new_hscale(min, max, step); + GtkWidget* spin = gtk_spin_button_new_with_range(min, max, step); + + gtk_widget_set_sensitive(scale, record->is_writable); + gtk_widget_set_sensitive(spin, record->is_writable); + + if (record->is_integer) { + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 0); + } else { + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 7); + } + + gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE); + gtk_range_set_value(GTK_RANGE(scale), value); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value); + if (record->points) { + for (size_t i = 0; i < record->n_points; ++i) { + const ScalePoint* point = &record->points[i]; + + gchar* str = g_markup_printf_escaped( + "<span font_size=\"small\">%s</span>", point->label); + gtk_scale_add_mark(GTK_SCALE(scale), point->value, GTK_POS_TOP, str); + } + } + + if (record->is_writable) { + g_signal_connect( + G_OBJECT(scale), "value-changed", G_CALLBACK(scale_changed), record); + g_signal_connect( + G_OBJECT(spin), "value-changed", G_CALLBACK(spin_changed), record); + } + + return new_controller(GTK_SPIN_BUTTON(spin), scale); } static Controller* make_toggle(ControlID* record, float value) { - GtkWidget* check = gtk_check_button_new(); + GtkWidget* check = gtk_check_button_new(); - gtk_widget_set_sensitive(check, record->is_writable); + gtk_widget_set_sensitive(check, record->is_writable); - if (value) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE); - } + if (value) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE); + } - if (record->is_writable) { - g_signal_connect(G_OBJECT(check), "toggled", - G_CALLBACK(toggle_changed), record); - } + if (record->is_writable) { + g_signal_connect( + G_OBJECT(check), "toggled", G_CALLBACK(toggle_changed), record); + } - return new_controller(NULL, check); + return new_controller(NULL, check); } static Controller* make_entry(ControlID* control) { - GtkWidget* entry = gtk_entry_new(); + GtkWidget* entry = gtk_entry_new(); - gtk_widget_set_sensitive(entry, control->is_writable); - if (control->is_writable) { - g_signal_connect(G_OBJECT(entry), "activate", - G_CALLBACK(string_changed), control); - } + gtk_widget_set_sensitive(entry, control->is_writable); + if (control->is_writable) { + g_signal_connect( + G_OBJECT(entry), "activate", G_CALLBACK(string_changed), control); + } - return new_controller(NULL, entry); + return new_controller(NULL, entry); } static Controller* make_file_chooser(ControlID* record) { - GtkWidget* button = gtk_file_chooser_button_new( - "Open File", GTK_FILE_CHOOSER_ACTION_OPEN); + GtkWidget* button = + gtk_file_chooser_button_new("Open File", GTK_FILE_CHOOSER_ACTION_OPEN); - gtk_widget_set_sensitive(button, record->is_writable); + gtk_widget_set_sensitive(button, record->is_writable); - if (record->is_writable) { - g_signal_connect(G_OBJECT(button), "file-set", - G_CALLBACK(file_changed), record); - } + if (record->is_writable) { + g_signal_connect( + G_OBJECT(button), "file-set", G_CALLBACK(file_changed), record); + } - return new_controller(NULL, button); + return new_controller(NULL, button); } static Controller* make_controller(ControlID* control, float value) { - Controller* controller = NULL; - - if (control->is_toggle) { - controller = make_toggle(control, value); - } else if (control->is_enumeration) { - controller = make_combo(control, value); - } else if (control->is_logarithmic) { - controller = make_log_slider(control, value); - } else { - controller = make_slider(control, value); - } - - return controller; + Controller* controller = NULL; + + if (control->is_toggle) { + controller = make_toggle(control, value); + } else if (control->is_enumeration) { + controller = make_combo(control, value); + } else if (control->is_logarithmic) { + controller = make_log_slider(control, value); + } else { + controller = make_slider(control, value); + } + + return controller; } static GtkWidget* new_label(const char* text, bool title, float xalign, float yalign) { - GtkWidget* label = gtk_label_new(NULL); - const char* fmt = title ? "<span font_weight=\"bold\">%s</span>" : "%s:"; - gchar* str = g_markup_printf_escaped(fmt, text); - gtk_label_set_markup(GTK_LABEL(label), str); - g_free(str); - gtk_misc_set_alignment(GTK_MISC(label), xalign, yalign); - return label; + GtkWidget* label = gtk_label_new(NULL); + const char* fmt = title ? "<span font_weight=\"bold\">%s</span>" : "%s:"; + gchar* str = g_markup_printf_escaped(fmt, text); + gtk_label_set_markup(GTK_LABEL(label), str); + g_free(str); + gtk_misc_set_alignment(GTK_MISC(label), xalign, yalign); + return label; } static void @@ -1055,191 +1130,220 @@ add_control_row(GtkWidget* table, const char* name, Controller* controller) { - GtkWidget* label = new_label(name, false, 1.0, 0.5); - gtk_table_attach(GTK_TABLE(table), - label, - 0, 1, row, row + 1, - GTK_FILL, (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), 8, 1); - int control_left_attach = 1; - if (controller->spin) { - control_left_attach = 2; - gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(controller->spin), - 1, 2, row, row + 1, - GTK_FILL, GTK_FILL, 2, 1); - } - gtk_table_attach(GTK_TABLE(table), controller->control, - control_left_attach, 3, row, row + 1, - (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), GTK_FILL, 2, 1); + GtkWidget* label = new_label(name, false, 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), + label, + 0, + 1, + row, + row + 1, + GTK_FILL, + (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), + 8, + 1); + int control_left_attach = 1; + if (controller->spin) { + control_left_attach = 2; + gtk_table_attach(GTK_TABLE(table), + GTK_WIDGET(controller->spin), + 1, + 2, + row, + row + 1, + GTK_FILL, + GTK_FILL, + 2, + 1); + } + gtk_table_attach(GTK_TABLE(table), + controller->control, + control_left_attach, + 3, + row, + row + 1, + (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), + GTK_FILL, + 2, + 1); } static int control_group_cmp(const void* p1, const void* p2, void* ZIX_UNUSED(data)) { - const ControlID* control1 = *(const ControlID*const*)p1; - const ControlID* control2 = *(const ControlID*const*)p2; + const ControlID* control1 = *(const ControlID* const*)p1; + const ControlID* control2 = *(const ControlID* const*)p2; - const int cmp = (control1->group && control2->group) - ? strcmp(lilv_node_as_string(control1->group), - lilv_node_as_string(control2->group)) - : ((intptr_t)control1->group - (intptr_t)control2->group); + const int cmp = (control1->group && control2->group) + ? strcmp(lilv_node_as_string(control1->group), + lilv_node_as_string(control2->group)) + : ((intptr_t)control1->group - (intptr_t)control2->group); - return cmp; + return cmp; } static GtkWidget* build_control_widget(Jalv* jalv, GtkWidget* window) { - GtkWidget* port_table = gtk_table_new(jalv->num_ports, 3, false); - - /* Make an array of controls sorted by group */ - GArray* controls = g_array_new(FALSE, TRUE, sizeof(ControlID*)); - for (unsigned i = 0; i < jalv->controls.n_controls; ++i) { - g_array_append_vals(controls, &jalv->controls.controls[i], 1); - } - g_array_sort_with_data(controls, control_group_cmp, jalv); - - /* Add controls in group order */ - LilvNode* last_group = NULL; - int n_rows = 0; - for (size_t i = 0; i < controls->len; ++i) { - ControlID* record = g_array_index(controls, ControlID*, i); - Controller* controller = NULL; - LilvNode* group = record->group; - - /* Check group and add new heading if necessary */ - if (group && !lilv_node_equals(group, last_group)) { - LilvNode* group_name = lilv_world_get( - jalv->world, group, jalv->nodes.lv2_name, NULL); - GtkWidget* group_label = new_label( - lilv_node_as_string(group_name), true, 0.0f, 1.0f); - gtk_table_attach(GTK_TABLE(port_table), group_label, - 0, 2, n_rows, n_rows + 1, - GTK_FILL, GTK_FILL, 0, 6); - ++n_rows; - } - last_group = group; - - /* Make control widget */ - if (record->value_type == jalv->forge.String) { - controller = make_entry(record); - } else if (record->value_type == jalv->forge.Path) { - controller = make_file_chooser(record); - } else { - const float val = get_float(record->def, 0.0f); - controller = make_controller(record, val); - } - - record->widget = controller; - if (record->type == PORT) { - jalv->ports[record->index].widget = controller; - } - if (controller) { - /* Add row to table for this controller */ - add_control_row( - port_table, n_rows++, - (record->label - ? lilv_node_as_string(record->label) - : lilv_node_as_uri(record->node)), - controller); - - /* Set tooltip text from comment, if available */ - LilvNode* comment = lilv_world_get( - jalv->world, record->node, jalv->nodes.rdfs_comment, NULL); - if (comment) { - gtk_widget_set_tooltip_text(controller->control, - lilv_node_as_string(comment)); - } - lilv_node_free(comment); - } - } - - if (n_rows > 0) { - gtk_window_set_resizable(GTK_WINDOW(window), TRUE); - GtkWidget* alignment = gtk_alignment_new(0.5, 0.0, 1.0, 0.0); - gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 8, 8); - gtk_container_add(GTK_CONTAINER(alignment), port_table); - return alignment; - } - - gtk_widget_destroy(port_table); - GtkWidget* button = gtk_button_new_with_label("Close"); - g_signal_connect_swapped(button, "clicked", - G_CALLBACK(gtk_widget_destroy), window); - gtk_window_set_resizable(GTK_WINDOW(window), FALSE); - return button; + GtkWidget* port_table = gtk_table_new(jalv->num_ports, 3, false); + + /* Make an array of controls sorted by group */ + GArray* controls = g_array_new(FALSE, TRUE, sizeof(ControlID*)); + for (unsigned i = 0; i < jalv->controls.n_controls; ++i) { + g_array_append_vals(controls, &jalv->controls.controls[i], 1); + } + g_array_sort_with_data(controls, control_group_cmp, jalv); + + /* Add controls in group order */ + LilvNode* last_group = NULL; + int n_rows = 0; + for (size_t i = 0; i < controls->len; ++i) { + ControlID* record = g_array_index(controls, ControlID*, i); + Controller* controller = NULL; + LilvNode* group = record->group; + + /* Check group and add new heading if necessary */ + if (group && !lilv_node_equals(group, last_group)) { + LilvNode* group_name = + lilv_world_get(jalv->world, group, jalv->nodes.lv2_name, NULL); + GtkWidget* group_label = + new_label(lilv_node_as_string(group_name), true, 0.0f, 1.0f); + gtk_table_attach(GTK_TABLE(port_table), + group_label, + 0, + 2, + n_rows, + n_rows + 1, + GTK_FILL, + GTK_FILL, + 0, + 6); + ++n_rows; + } + last_group = group; + + /* Make control widget */ + if (record->value_type == jalv->forge.String) { + controller = make_entry(record); + } else if (record->value_type == jalv->forge.Path) { + controller = make_file_chooser(record); + } else { + const float val = get_float(record->def, 0.0f); + controller = make_controller(record, val); + } + + record->widget = controller; + if (record->type == PORT) { + jalv->ports[record->index].widget = controller; + } + if (controller) { + /* Add row to table for this controller */ + add_control_row(port_table, + n_rows++, + (record->label ? lilv_node_as_string(record->label) + : lilv_node_as_uri(record->node)), + controller); + + /* Set tooltip text from comment, if available */ + LilvNode* comment = lilv_world_get( + jalv->world, record->node, jalv->nodes.rdfs_comment, NULL); + if (comment) { + gtk_widget_set_tooltip_text(controller->control, + lilv_node_as_string(comment)); + } + lilv_node_free(comment); + } + } + + if (n_rows > 0) { + gtk_window_set_resizable(GTK_WINDOW(window), TRUE); + GtkWidget* alignment = gtk_alignment_new(0.5, 0.0, 1.0, 0.0); + gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 8, 8); + gtk_container_add(GTK_CONTAINER(alignment), port_table); + return alignment; + } + + gtk_widget_destroy(port_table); + GtkWidget* button = gtk_button_new_with_label("Close"); + g_signal_connect_swapped( + button, "clicked", G_CALLBACK(gtk_widget_destroy), window); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + return button; } static void build_menu(Jalv* jalv, GtkWidget* window, GtkWidget* vbox) { - GtkWidget* menu_bar = gtk_menu_bar_new(); - GtkWidget* file = gtk_menu_item_new_with_mnemonic("_File"); - GtkWidget* file_menu = gtk_menu_new(); - - GtkAccelGroup* ag = gtk_accel_group_new(); - gtk_window_add_accel_group(GTK_WINDOW(window), ag); - - GtkWidget* save = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, ag); - GtkWidget* quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, ag); - - gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), file_menu); - gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), save); - gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit); - gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file); - - GtkWidget* pset_item = gtk_menu_item_new_with_mnemonic("_Presets"); - GtkWidget* pset_menu = gtk_menu_new(); - GtkWidget* save_preset = gtk_menu_item_new_with_mnemonic( - "_Save Preset..."); - GtkWidget* delete_preset = gtk_menu_item_new_with_mnemonic( - "_Delete Current Preset..."); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(pset_item), pset_menu); - gtk_menu_shell_append(GTK_MENU_SHELL(pset_menu), save_preset); - gtk_menu_shell_append(GTK_MENU_SHELL(pset_menu), delete_preset); - gtk_menu_shell_append(GTK_MENU_SHELL(pset_menu), - gtk_separator_menu_item_new()); - gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), pset_item); - - PresetMenu menu = { - NULL, NULL, GTK_MENU(pset_menu), - g_sequence_new((GDestroyNotify)pset_menu_free) - }; - jalv_load_presets(jalv, add_preset_to_menu, &menu); - finish_menu(&menu); - - g_signal_connect(G_OBJECT(quit), "activate", - G_CALLBACK(on_quit_activate), window); - - g_signal_connect(G_OBJECT(save), "activate", - G_CALLBACK(on_save_activate), jalv); - - g_signal_connect(G_OBJECT(save_preset), "activate", - G_CALLBACK(on_save_preset_activate), jalv); - - g_signal_connect(G_OBJECT(delete_preset), "activate", - G_CALLBACK(on_delete_preset_activate), jalv); - - gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0); + GtkWidget* menu_bar = gtk_menu_bar_new(); + GtkWidget* file = gtk_menu_item_new_with_mnemonic("_File"); + GtkWidget* file_menu = gtk_menu_new(); + + GtkAccelGroup* ag = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(window), ag); + + GtkWidget* save = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, ag); + GtkWidget* quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, ag); + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), file_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), save); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit); + gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file); + + GtkWidget* pset_item = gtk_menu_item_new_with_mnemonic("_Presets"); + GtkWidget* pset_menu = gtk_menu_new(); + GtkWidget* save_preset = gtk_menu_item_new_with_mnemonic("_Save Preset..."); + GtkWidget* delete_preset = + gtk_menu_item_new_with_mnemonic("_Delete Current Preset..."); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(pset_item), pset_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(pset_menu), save_preset); + gtk_menu_shell_append(GTK_MENU_SHELL(pset_menu), delete_preset); + gtk_menu_shell_append(GTK_MENU_SHELL(pset_menu), + gtk_separator_menu_item_new()); + gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), pset_item); + + PresetMenu menu = {NULL, + NULL, + GTK_MENU(pset_menu), + g_sequence_new((GDestroyNotify)pset_menu_free)}; + jalv_load_presets(jalv, add_preset_to_menu, &menu); + finish_menu(&menu); + + g_signal_connect( + G_OBJECT(quit), "activate", G_CALLBACK(on_quit_activate), window); + + g_signal_connect( + G_OBJECT(save), "activate", G_CALLBACK(on_save_activate), jalv); + + g_signal_connect(G_OBJECT(save_preset), + "activate", + G_CALLBACK(on_save_preset_activate), + jalv); + + g_signal_connect(G_OBJECT(delete_preset), + "activate", + G_CALLBACK(on_delete_preset_activate), + jalv); + + gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0); } bool jalv_discover_ui(Jalv* ZIX_UNUSED(jalv)) { - return TRUE; + return TRUE; } float jalv_ui_refresh_rate(Jalv* ZIX_UNUSED(jalv)) { #if GTK_MAJOR_VERSION == 2 - return 30.0f; + return 30.0f; #else - GdkDisplay* const display = gdk_display_get_default(); - GdkMonitor* const monitor = gdk_display_get_primary_monitor(display); + GdkDisplay* const display = gdk_display_get_default(); + GdkMonitor* const monitor = gdk_display_get_primary_monitor(display); - const float rate = (float)gdk_monitor_get_refresh_rate(monitor); + const float rate = (float)gdk_monitor_get_refresh_rate(monitor); - return rate < 30.0f ? 30.0f : rate; + return rate < 30.0f ? 30.0f : rate; #endif } @@ -1247,94 +1351,92 @@ float jalv_ui_scale_factor(Jalv* ZIX_UNUSED(jalv)) { #if GTK_MAJOR_VERSION == 2 - return 1.0f; + return 1.0f; #else - GdkDisplay* const display = gdk_display_get_default(); - GdkMonitor* const monitor = gdk_display_get_primary_monitor(display); + GdkDisplay* const display = gdk_display_get_default(); + GdkMonitor* const monitor = gdk_display_get_primary_monitor(display); - return (float)gdk_monitor_get_scale_factor(monitor); + return (float)gdk_monitor_get_scale_factor(monitor); #endif } int jalv_open_ui(Jalv* jalv) { - GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - jalv->window = window; - - g_signal_connect(window, "destroy", - G_CALLBACK(on_window_destroy), jalv); - - set_window_title(jalv); - - GtkWidget* vbox = new_box(false, 0); - gtk_window_set_role(GTK_WINDOW(window), "plugin_ui"); - gtk_container_add(GTK_CONTAINER(window), vbox); - - if (!jalv->opts.no_menu) { - build_menu(jalv, window, vbox); - } - - /* Create/show alignment to contain UI (whether custom or generic) */ - GtkWidget* alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0); - gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0); - gtk_widget_show(alignment); - - /* Attempt to instantiate custom UI if necessary */ - if (jalv->ui && !jalv->opts.generic_ui) { - jalv_ui_instantiate(jalv, jalv_native_ui_type(), alignment); - } - - jalv->features.request_value.request = on_request_value; - - if (jalv->ui_instance) { - GtkWidget* widget = (GtkWidget*)suil_instance_get_widget( - jalv->ui_instance); - - gtk_container_add(GTK_CONTAINER(alignment), widget); - gtk_window_set_resizable(GTK_WINDOW(window), jalv_ui_is_resizable(jalv)); - gtk_widget_show_all(vbox); - gtk_widget_grab_focus(widget); - } else { - GtkWidget* controls = build_control_widget(jalv, window); - GtkWidget* scroll_win = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_add_with_viewport( - GTK_SCROLLED_WINDOW(scroll_win), controls); - gtk_scrolled_window_set_policy( - GTK_SCROLLED_WINDOW(scroll_win), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(alignment), scroll_win); - gtk_widget_show_all(vbox); - - GtkRequisition controls_size = {0, 0}; - GtkRequisition box_size = {0, 0}; - size_request(GTK_WIDGET(controls), &controls_size); - size_request(GTK_WIDGET(vbox), &box_size); - - gtk_window_set_default_size( - GTK_WINDOW(window), - MAX(MAX(box_size.width, controls_size.width) + 24, 640), - box_size.height + controls_size.height); - } - - jalv_init_ui(jalv); - - g_timeout_add(1000 / jalv->ui_update_hz, (GSourceFunc)jalv_update, jalv); - - gtk_window_present(GTK_WINDOW(window)); - - gtk_main(); - suil_instance_free(jalv->ui_instance); - jalv->ui_instance = NULL; - zix_sem_post(&jalv->done); - return 0; + GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + jalv->window = window; + + g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), jalv); + + set_window_title(jalv); + + GtkWidget* vbox = new_box(false, 0); + gtk_window_set_role(GTK_WINDOW(window), "plugin_ui"); + gtk_container_add(GTK_CONTAINER(window), vbox); + + if (!jalv->opts.no_menu) { + build_menu(jalv, window, vbox); + } + + /* Create/show alignment to contain UI (whether custom or generic) */ + GtkWidget* alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0); + gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0); + gtk_widget_show(alignment); + + /* Attempt to instantiate custom UI if necessary */ + if (jalv->ui && !jalv->opts.generic_ui) { + jalv_ui_instantiate(jalv, jalv_native_ui_type(), alignment); + } + + jalv->features.request_value.request = on_request_value; + + if (jalv->ui_instance) { + GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(jalv->ui_instance); + + gtk_container_add(GTK_CONTAINER(alignment), widget); + gtk_window_set_resizable(GTK_WINDOW(window), jalv_ui_is_resizable(jalv)); + gtk_widget_show_all(vbox); + gtk_widget_grab_focus(widget); + } else { + GtkWidget* controls = build_control_widget(jalv, window); + GtkWidget* scroll_win = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll_win), + controls); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(alignment), scroll_win); + gtk_widget_show_all(vbox); + + GtkRequisition controls_size = {0, 0}; + GtkRequisition box_size = {0, 0}; + size_request(GTK_WIDGET(controls), &controls_size); + size_request(GTK_WIDGET(vbox), &box_size); + + gtk_window_set_default_size( + GTK_WINDOW(window), + MAX(MAX(box_size.width, controls_size.width) + 24, 640), + box_size.height + controls_size.height); + } + + jalv_init_ui(jalv); + + g_timeout_add(1000 / jalv->ui_update_hz, (GSourceFunc)jalv_update, jalv); + + gtk_window_present(GTK_WINDOW(window)); + + gtk_main(); + suil_instance_free(jalv->ui_instance); + jalv->ui_instance = NULL; + zix_sem_post(&jalv->done); + return 0; } int jalv_close_ui(Jalv* ZIX_UNUSED(jalv)) { - gtk_main_quit(); - return 0; + gtk_main_quit(); + return 0; } LV2_RESTORE_WARNINGS |