aboutsummaryrefslogtreecommitdiffstats
path: root/src/jalv_gtk2.c
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-04-09 21:30:50 +0000
committerDavid Robillard <d@drobilla.net>2012-04-09 21:30:50 +0000
commit9ca650bafe893d3285c7e27d4e3447bf9921105b (patch)
treef4bcd546edbb1f58ae71a88a2937b49e182c5e08 /src/jalv_gtk2.c
parentba65388a9f7e790acf8edb2a591084d16e8382c4 (diff)
downloadjalv-9ca650bafe893d3285c7e27d4e3447bf9921105b.tar.gz
jalv-9ca650bafe893d3285c7e27d4e3447bf9921105b.tar.bz2
jalv-9ca650bafe893d3285c7e27d4e3447bf9921105b.zip
Generic UI for jalv.gtk from Nick Lanham.
git-svn-id: http://svn.drobilla.net/lad/trunk/jalv@4152 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/jalv_gtk2.c')
-rw-r--r--src/jalv_gtk2.c243
1 files changed, 228 insertions, 15 deletions
diff --git a/src/jalv_gtk2.c b/src/jalv_gtk2.c
index 0eb3fa4..155f890 100644
--- a/src/jalv_gtk2.c
+++ b/src/jalv_gtk2.c
@@ -38,6 +38,8 @@ jalv_init(int* argc, char*** argv, JalvOptions* opts)
"Load state from save directory", "DIR" },
{ "dump", 'd', 0, G_OPTION_ARG_NONE, &opts->dump,
"Dump plugin <=> UI communication", NULL },
+ { "generic-ui", 'g', 0, G_OPTION_ARG_NONE, &opts->generic_ui,
+ "Use Jalv generic UI and not the plugin UI", NULL},
{ 0, 0, 0, 0, 0, 0, 0 } };
GError* error = NULL;
const int err = gtk_init_with_args(
@@ -189,6 +191,205 @@ add_preset_to_menu(Jalv* jalv,
return 0;
}
+static gboolean
+slider_changed(GtkRange* range, gpointer data)
+{
+ ((struct Port*)data)->control = gtk_range_get_value(range);
+ return FALSE;
+}
+
+static void
+combo_changed(GtkComboBox* box, gpointer data)
+{
+ float fval = (float)gtk_combo_box_get_active(GTK_COMBO_BOX(box));
+ ((struct Port*)data)->control = fval;
+}
+
+static gboolean
+toggle_changed(GtkToggleButton* button, gpointer data)
+{
+ float fval = gtk_toggle_button_get_active(button) ? 1.0f : 0.0f;
+ ((struct Port*)data)->control = fval;
+ return FALSE;
+}
+
+static gchar*
+scale_format(GtkScale* scale, gdouble value, gpointer user_data)
+{
+ gpointer hval = g_hash_table_lookup(user_data, &value);
+ return hval ? g_strdup(hval) :
+ g_strdup_printf("%0.*f", gtk_scale_get_digits(scale), value);
+}
+
+static gint
+dcmp(gconstpointer a, gconstpointer b)
+{
+ double y = *(double*)a;
+ double z = *(double*)b;
+ return y < z ? -1 : z < y ? 1 : 0;
+}
+
+static GtkWidget*
+make_combo(struct Port* port, GHashTable* points, int deft)
+{
+ GList* list = g_hash_table_get_keys(points);
+ GtkListStore* list_store = gtk_list_store_new(1, G_TYPE_STRING);
+ for (GList* cur = g_list_sort(list, dcmp); cur; cur = cur->next) {
+ GtkTreeIter iter;
+ gtk_list_store_append(list_store, &iter);
+ gtk_list_store_set(list_store, &iter,
+ 0, g_hash_table_lookup(points, cur->data), -1);
+ }
+ g_list_free(list);
+
+ GtkWidget* combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store));
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo), deft);
+ g_object_unref(list_store);
+
+ 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", 0, NULL);
+
+ g_signal_connect(G_OBJECT(combo),
+ "changed", G_CALLBACK(combo_changed), port);
+
+ return combo;
+}
+
+static GtkWidget*
+make_slider(struct Port* port, GHashTable* points,
+ bool is_int, float min, float max, float deft)
+{
+ const double step = is_int ? 1.0 : ((max - min) / 100.0);
+ GtkWidget* slider = gtk_hscale_new_with_range(min, max, step);
+ gtk_range_set_value(GTK_RANGE(slider), deft);
+ if (points) {
+ g_signal_connect(G_OBJECT(slider),
+ "format-value", G_CALLBACK(scale_format), points);
+ }
+
+ g_signal_connect(G_OBJECT(slider),
+ "value-changed", G_CALLBACK(slider_changed), port);
+
+ return slider;
+}
+
+static GtkWidget*
+make_toggle(struct Port* port, float deft)
+{
+ GtkWidget* check = gtk_check_button_new();
+ if (deft) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
+ }
+ g_signal_connect(G_OBJECT(check),
+ "toggled", G_CALLBACK(toggle_changed), port);
+ return check;
+}
+
+static GtkWidget*
+build_control_widget(Jalv* jalv, GtkWidget* window)
+{
+ LilvNode* lv2_integer = lilv_new_uri(jalv->world, LV2_CORE__integer);
+ LilvNode* lv2_toggled = lilv_new_uri(jalv->world, LV2_CORE__toggled);
+ LilvNode* lv2_enum = lilv_new_uri(jalv->world, LV2_CORE__enumeration);
+ LilvNode* rdfs_comment = lilv_new_uri(jalv->world, LILV_NS_RDFS "comment");
+ GtkWidget* port_table = gtk_table_new(jalv->num_ports, 2, false);
+ float* defaults = calloc(jalv->num_ports, sizeof(float));
+ float* mins = calloc(jalv->num_ports, sizeof(float));
+ float* maxs = calloc(jalv->num_ports, sizeof(float));
+ int num_controls = 0;
+ lilv_plugin_get_port_ranges_float(jalv->plugin, mins, maxs, defaults);
+ for (unsigned i = 0; i < jalv->num_ports; i++) {
+ if (jalv->ports[i].type != TYPE_CONTROL) {
+ continue;
+ }
+ ++num_controls;
+
+ const LilvPort* port = jalv->ports[i].lilv_port;
+ LilvNode* name = lilv_port_get_name(jalv->plugin, port);
+
+ /* Get scale points */
+ LilvScalePoints* sp = lilv_port_get_scale_points(jalv->plugin, port);
+ GHashTable* points = NULL;
+ if (sp) {
+ points = g_hash_table_new(g_double_hash, g_double_equal);
+ gdouble* values = malloc(lilv_scale_points_size(sp) * sizeof(gdouble));
+ int idx = 0;
+ LILV_FOREACH(scale_points, i, sp) {
+ const LilvScalePoint* p = lilv_scale_points_get(sp, i);
+ values[idx++] = lilv_node_as_float(lilv_scale_point_get_value(p));
+ char* label = g_strdup(
+ lilv_node_as_string(lilv_scale_point_get_label(p)));
+ g_hash_table_insert(points, values + idx, label);
+ }
+ lilv_scale_points_free(sp);
+ }
+
+ /* Make control */
+ GtkWidget* control = NULL;
+ if (lilv_port_has_property(jalv->plugin, port, lv2_toggled)) {
+ control = make_toggle(&jalv->ports[i], defaults[i]);
+ } else if (lilv_port_has_property(jalv->plugin, port, lv2_enum)
+ || (lilv_port_has_property(jalv->plugin, port, lv2_integer)
+ && points)) {
+ control = make_combo(&jalv->ports[i], points, defaults[i]);
+ } else {
+ control = make_slider(
+ &jalv->ports[i], points,
+ lilv_port_has_property(jalv->plugin, port, lv2_integer),
+ mins[i], maxs[i], defaults[i]);
+ }
+
+ /* Set tooltip text */
+ LilvNodes* comments = lilv_port_get_value(
+ jalv->plugin, port, rdfs_comment);
+ if (comments) {
+ gtk_widget_set_tooltip_text(
+ control, lilv_node_as_string(lilv_nodes_get_first(comments)));
+ }
+
+ /* Add control table row */
+ GtkWidget* label = gtk_label_new(NULL);
+ gchar* markup = g_markup_printf_escaped(
+ "<span font_weight=\"bold\">%s:</span>",
+ lilv_node_as_string(name));
+ gtk_label_set_markup(GTK_LABEL(label), markup);
+ g_free(markup);
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+ gtk_table_attach(GTK_TABLE(port_table),
+ label,
+ 0, 1, i, i + 1,
+ GTK_FILL, GTK_FILL | GTK_EXPAND, 15, 15);
+ gtk_table_attach(GTK_TABLE(port_table), control,
+ 1, 2, i, i + 1,
+ GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ lilv_node_free(name);
+ }
+
+ free(defaults);
+ free(mins);
+ free(maxs);
+ lilv_node_free(lv2_integer);
+ lilv_node_free(lv2_toggled);
+ lilv_node_free(lv2_enum);
+ lilv_node_free(rdfs_comment);
+
+ if (num_controls > 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;
+ } else {
+ 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;
+ }
+}
+
int
jalv_open_ui(Jalv* jalv,
SuilInstance* instance)
@@ -231,6 +432,7 @@ jalv_open_ui(Jalv* jalv,
jalv_load_presets(jalv, add_preset_to_menu, presets_menu);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(quit), "activate",
@@ -242,29 +444,40 @@ jalv_open_ui(Jalv* jalv,
g_signal_connect(G_OBJECT(save_preset), "activate",
G_CALLBACK(on_save_preset_activate), jalv);
- GtkWidget* alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
- gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
- if (instance) {
- GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(instance);
+ if (instance && !jalv->opts.generic_ui) {
+ GtkWidget* alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
+ GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(instance);
+
+ gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(alignment), widget);
g_timeout_add(1000 / JALV_UI_UPDATE_HZ,
(GSourceFunc)jalv_emit_ui_events, jalv);
+ gtk_window_set_resizable(GTK_WINDOW(window), jalv_ui_is_resizable(jalv));
+ gtk_widget_show_all(vbox);
} else {
- GtkWidget* button = gtk_button_new_with_label("Close");
-
- g_signal_connect_swapped(button, "clicked",
- G_CALLBACK(gtk_widget_destroy),
- window);
-
- gtk_container_add(GTK_CONTAINER(alignment), button);
+ 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_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0);
+ gtk_widget_show_all(vbox);
+
+ GtkRequisition controls_size, box_size;
+ gtk_widget_size_request(GTK_WIDGET(controls), &controls_size);
+ gtk_widget_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);
}
- gtk_window_set_resizable(GTK_WINDOW(window), jalv_ui_is_resizable(jalv));
-
- gtk_container_add(GTK_CONTAINER(window), vbox);
- gtk_widget_show_all(window);
+ gtk_window_present(GTK_WINDOW(window));
gtk_main();
zix_sem_post(jalv->done);