1 /*
2 * Copyright (C) 2020-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 *
19 * This file incorporates work covered by the following copyright and
20 * permission notice:
21 *
22 Copyright 2007-2016 David Robillard <http://drobilla.net>
23
24 Permission to use, copy, modify, and/or distribute this software for any
25 purpose with or without fee is hereby granted, provided that the above
26 copyright notice and this permission notice appear in all copies.
27
28 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
29 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
30 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
31 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
32 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
33 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
34 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35 */
36
37 #include "zrythm-config.h"
38
39 #include <math.h>
40
41 #include "audio/engine.h"
42 #include "audio/track.h"
43 #include "gui/backend/event.h"
44 #include "gui/backend/event_manager.h"
45 #include "gui/widgets/main_window.h"
46 #include "plugins/carla_native_plugin.h"
47 #include "plugins/lv2_plugin.h"
48 #include "plugins/lv2/lv2_gtk.h"
49 #include "plugins/lv2/lv2_state.h"
50 #include "plugins/lv2/lv2_ui.h"
51 #include "plugins/lv2/lv2_urid.h"
52 #include "plugins/plugin.h"
53 #include "plugins/plugin_gtk.h"
54 #include "plugins/plugin_manager.h"
55 #include "settings/settings.h"
56 #include "project.h"
57 #include "utils/error.h"
58 #include "utils/flags.h"
59 #include "utils/gtk.h"
60 #include "utils/math.h"
61 #include "utils/objects.h"
62 #include "utils/string.h"
63 #include "zrythm.h"
64 #include "zrythm_app.h"
65
66 #include <glib/gi18n.h>
67
68 #define MIN_SCALE_WIDTH 120
69
70 static void
on_quit_activate(GtkWidget * widget,gpointer data)71 on_quit_activate (
72 GtkWidget* widget, gpointer data)
73 {
74 GtkWidget* window = (GtkWidget*)data;
75 gtk_widget_destroy (window);
76 }
77
78 static void
on_save_activate(GtkWidget * widget,Plugin * plugin)79 on_save_activate (
80 GtkWidget* widget,
81 Plugin * plugin)
82 {
83 switch (plugin->setting->descr->protocol)
84 {
85 case PROT_LV2:
86 g_return_if_fail (plugin->lv2);
87 lv2_gtk_on_save_activate (plugin->lv2);
88 break;
89 case PROT_VST:
90 break;
91 default:
92 break;
93 }
94 #if 0
95 GtkWidget* dialog = gtk_file_chooser_dialog_new(
96 _("Save State"),
97 (GtkWindow*)plugin->window,
98 GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER,
99 _("_Cancel"), GTK_RESPONSE_CANCEL,
100 _("_Save"), GTK_RESPONSE_ACCEPT,
101 NULL);
102
103 if (gtk_dialog_run (
104 GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
105 {
106 char* path =
107 gtk_file_chooser_get_filename (
108 GTK_FILE_CHOOSER(dialog));
109 char* base =
110 g_build_filename (path, "/", NULL);
111
112 switch (plugin->descr->protocol)
113 {
114 case PROT_LV2:
115 lv2_state_save_to_file (plugin->lv2, base);
116 break;
117 case PROT_VST:
118 break;
119 default:
120 break;
121 }
122 g_free(path);
123 g_free(base);
124 }
125 gtk_widget_destroy(dialog);
126 #endif
127 }
128
129 void
plugin_gtk_set_window_title(Plugin * plugin,GtkWindow * window)130 plugin_gtk_set_window_title (
131 Plugin * plugin,
132 GtkWindow * window)
133 {
134 g_return_if_fail (
135 plugin && plugin->setting->descr && window);
136
137 char * title =
138 plugin_generate_window_title (plugin);
139
140 gtk_window_set_title (window, title);
141
142 g_free (title);
143 }
144
145 void
plugin_gtk_on_preset_activate(GtkWidget * widget,PluginGtkPresetRecord * record)146 plugin_gtk_on_preset_activate (
147 GtkWidget* widget,
148 PluginGtkPresetRecord * record)
149 {
150 g_return_if_fail (record && record->plugin);
151 Plugin * plugin = record->plugin;
152
153 if (GTK_CHECK_MENU_ITEM (widget) !=
154 plugin->active_preset_item)
155 {
156 if (plugin->setting->descr->protocol ==
157 PROT_LV2)
158 {
159 g_return_if_fail (plugin->lv2);
160 GError * err = NULL;
161 bool applied =
162 lv2_state_apply_preset (
163 plugin->lv2,
164 (LilvNode *) record->preset, NULL,
165 &err);
166 if (!applied)
167 {
168 HANDLE_ERROR (
169 err, "%s",
170 _("Failed to apply preset"));
171 return;
172 }
173 }
174 if (plugin->active_preset_item)
175 {
176 gtk_check_menu_item_set_active (
177 plugin->active_preset_item,
178 FALSE);
179 }
180
181 plugin->active_preset_item =
182 GTK_CHECK_MENU_ITEM (widget);
183 gtk_check_menu_item_set_active (
184 plugin->active_preset_item, TRUE);
185
186 EVENTS_PUSH (ET_PLUGIN_PRESET_LOADED, plugin);
187 }
188 }
189
190 void
plugin_gtk_on_preset_destroy(PluginGtkPresetRecord * record,GClosure * closure)191 plugin_gtk_on_preset_destroy (
192 PluginGtkPresetRecord * record,
193 GClosure* closure)
194 {
195 free (record);
196 }
197
198 PluginGtkPresetMenu*
plugin_gtk_preset_menu_new(const char * label)199 plugin_gtk_preset_menu_new (
200 const char* label)
201 {
202 PluginGtkPresetMenu* menu =
203 object_new (PluginGtkPresetMenu);
204
205 menu->label = g_strdup (label);
206 menu->item =
207 GTK_MENU_ITEM (
208 gtk_menu_item_new_with_label(menu->label));
209 menu->menu = GTK_MENU (gtk_menu_new());
210 menu->banks = NULL;
211
212 return menu;
213 }
214
215 static void
preset_menu_free(PluginGtkPresetMenu * menu)216 preset_menu_free (
217 PluginGtkPresetMenu* menu)
218 {
219 if (menu->banks)
220 {
221 for (GSequenceIter* i =
222 g_sequence_get_begin_iter(menu->banks);
223 !g_sequence_iter_is_end(i);
224 i = g_sequence_iter_next(i))
225 {
226 PluginGtkPresetMenu* bank_menu =
227 (PluginGtkPresetMenu*)g_sequence_get(i);
228 preset_menu_free (bank_menu);
229 }
230 g_sequence_free(menu->banks);
231 }
232
233 free(menu->label);
234 free(menu);
235 }
236
237 gint
plugin_gtk_menu_cmp(gconstpointer a,gconstpointer b,gpointer data)238 plugin_gtk_menu_cmp (
239 gconstpointer a, gconstpointer b, gpointer data)
240 {
241 return strcmp(((PluginGtkPresetMenu*)a)->label,
242 ((PluginGtkPresetMenu*)b)->label);
243 }
244
245 static void
finish_menu(PluginGtkPresetMenu * menu)246 finish_menu (PluginGtkPresetMenu* menu)
247 {
248 for (GSequenceIter* i =
249 g_sequence_get_begin_iter(menu->banks);
250 !g_sequence_iter_is_end(i);
251 i = g_sequence_iter_next(i))
252 {
253 PluginGtkPresetMenu* bank_menu =
254 (PluginGtkPresetMenu*)g_sequence_get(i);
255 gtk_menu_shell_append (
256 GTK_MENU_SHELL(menu->menu),
257 GTK_WIDGET(bank_menu->item));
258 }
259
260 g_sequence_free(menu->banks);
261 }
262
263 void
plugin_gtk_rebuild_preset_menu(Plugin * plugin,GtkContainer * pset_menu)264 plugin_gtk_rebuild_preset_menu (
265 Plugin* plugin,
266 GtkContainer* pset_menu)
267 {
268 // Clear current menu
269 plugin->active_preset_item = NULL;
270 for (GList* items =
271 g_list_nth (
272 gtk_container_get_children (pset_menu),
273 3);
274 items;
275 items = items->next)
276 {
277 gtk_container_remove (
278 pset_menu, GTK_WIDGET(items->data));
279 }
280
281 // Load presets and build new menu
282 PluginGtkPresetMenu menu =
283 {
284 NULL, NULL, GTK_MENU(pset_menu),
285 g_sequence_new (
286 (GDestroyNotify)preset_menu_free),
287 };
288 if (plugin->lv2)
289 {
290 lv2_state_load_presets (
291 plugin->lv2, lv2_gtk_add_preset_to_menu,
292 &menu);
293 }
294 finish_menu (&menu);
295 gtk_widget_show_all (GTK_WIDGET(pset_menu));
296 }
297
298 void
plugin_gtk_on_save_preset_activate(GtkWidget * widget,Plugin * plugin)299 plugin_gtk_on_save_preset_activate (
300 GtkWidget * widget,
301 Plugin * plugin)
302 {
303 const PluginSetting * setting = plugin->setting;
304 const PluginDescriptor * descr = setting->descr;
305 bool open_with_carla = setting->open_with_carla;
306 bool is_lv2 = descr->protocol == PROT_LV2;
307
308 GtkWidget* dialog =
309 gtk_file_chooser_dialog_new (
310 _("Save Preset"),
311 plugin->window ?
312 plugin->window : GTK_WINDOW (MAIN_WINDOW),
313 GTK_FILE_CHOOSER_ACTION_SAVE,
314 _("_Cancel"), GTK_RESPONSE_REJECT,
315 _("_Save"), GTK_RESPONSE_ACCEPT,
316 NULL);
317
318 if (open_with_carla)
319 {
320 #ifdef HAVE_CARLA
321 char * homedir =
322 g_build_filename (
323 g_get_home_dir(), NULL);
324 gtk_file_chooser_set_current_folder (
325 GTK_FILE_CHOOSER (dialog), homedir);
326 g_free (homedir);
327 #else
328 g_return_if_reached ();
329 #endif
330 }
331 else if (is_lv2)
332 {
333 char * dot_lv2 =
334 g_build_filename (
335 g_get_home_dir(), ".lv2", NULL);
336 gtk_file_chooser_set_current_folder (
337 GTK_FILE_CHOOSER (dialog), dot_lv2);
338 g_free (dot_lv2);
339 }
340
341 /* add additional inputs */
342 GtkWidget* content =
343 gtk_dialog_get_content_area (
344 GTK_DIALOG (dialog));
345 GtkWidget * uri_entry = NULL;
346 GtkWidget * add_prefix =
347 add_prefix =
348 gtk_check_button_new_with_mnemonic (
349 _("_Prefix plugin name"));
350 gtk_toggle_button_set_active (
351 GTK_TOGGLE_BUTTON (add_prefix), TRUE);
352 GtkBox* box =
353 GTK_BOX (
354 gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8));
355 if (open_with_carla)
356 {
357 }
358 else if (is_lv2)
359 {
360 GtkWidget* uri_label =
361 gtk_label_new (_("URI (Optional):"));
362 uri_entry = gtk_entry_new();
363
364 gtk_box_pack_start (
365 box, uri_label, FALSE, TRUE, 2);
366 gtk_box_pack_start (
367 box, uri_entry, TRUE, TRUE, 2);
368 gtk_box_pack_start (
369 GTK_BOX(content), GTK_WIDGET(box),
370 FALSE, FALSE, 6);
371 gtk_entry_set_activates_default (
372 GTK_ENTRY(uri_entry), TRUE);
373 }
374 gtk_box_pack_start (
375 GTK_BOX (content), add_prefix,
376 FALSE, FALSE, 6);
377
378 gtk_widget_show_all (GTK_WIDGET (dialog));
379 gtk_dialog_set_default_response (
380 GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
381 if (gtk_dialog_run (GTK_DIALOG(dialog)) ==
382 GTK_RESPONSE_ACCEPT)
383 {
384 const char* path =
385 gtk_file_chooser_get_filename (
386 GTK_FILE_CHOOSER (dialog));
387 bool add_prefix_active =
388 gtk_toggle_button_get_active (
389 GTK_TOGGLE_BUTTON (add_prefix));
390 if (open_with_carla)
391 {
392 #ifdef HAVE_CARLA
393 const char * prefix = "";
394 const char * sep = "";
395 char * dirname =
396 g_path_get_dirname(path);
397 char * basename =
398 g_path_get_basename(path);
399 char * sym =
400 string_symbolify (basename);
401 if (add_prefix_active)
402 {
403 prefix = descr->name;
404 sep = "_";
405 }
406 char * sprefix =
407 string_symbolify (prefix);
408 char * bundle =
409 g_strjoin (
410 NULL, sprefix, sep, sym,
411 ".preset.carla", NULL);
412 char * dir =
413 g_build_filename (
414 dirname, bundle, NULL);
415 carla_native_plugin_save_state (
416 plugin->carla, false, dir);
417 g_free (dirname);
418 g_free (basename);
419 g_free (sym);
420 g_free (sprefix);
421 g_free (bundle);
422 g_free (dir);
423 #endif
424 }
425 else if (is_lv2)
426 {
427 const char * uri =
428 gtk_entry_get_text (
429 GTK_ENTRY (uri_entry));
430 lv2_gtk_on_save_preset_activate (
431 widget, plugin->lv2, path, uri,
432 add_prefix_active);
433 }
434 }
435
436 gtk_widget_destroy (GTK_WIDGET (dialog));
437
438 EVENTS_PUSH (ET_PLUGIN_PRESET_SAVED, plugin);
439 }
440
441 static void
on_delete_preset_activate(GtkWidget * widget,Plugin * plugin)442 on_delete_preset_activate (
443 GtkWidget * widget,
444 Plugin * plugin)
445 {
446 switch (plugin->setting->descr->protocol)
447 {
448 case PROT_LV2:
449 lv2_gtk_on_delete_preset_activate (
450 widget, plugin->lv2);
451 break;
452 default:
453 break;
454 }
455 }
456
457 /**
458 * Creates a label for a control.
459 *
460 * @param title Whether this is a title text (makes
461 * it bold).
462 * @param preformatted Whether the text is
463 * preformatted.
464 */
465 GtkWidget*
plugin_gtk_new_label(const char * text,bool title,bool preformatted,float xalign,float yalign)466 plugin_gtk_new_label (
467 const char * text,
468 bool title,
469 bool preformatted,
470 float xalign,
471 float yalign)
472 {
473 GtkWidget * label = gtk_label_new(NULL);
474 const char * fmt =
475 title ?
476 "<b>%s</b>" :
477 "%s: ";
478 gchar * str;
479 if (preformatted)
480 {
481 str = g_strdup_printf (fmt, text);
482 }
483 else
484 {
485 str = g_markup_printf_escaped (fmt, text);
486 }
487 gtk_label_set_markup (GTK_LABEL(label), str);
488 g_free (str);
489 gtk_label_set_xalign (
490 GTK_LABEL (label), xalign);
491 gtk_label_set_yalign (
492 GTK_LABEL (label), yalign);
493 return label;
494 }
495
496 void
plugin_gtk_add_control_row(GtkWidget * table,int row,const char * _name,PluginGtkController * controller)497 plugin_gtk_add_control_row (
498 GtkWidget* table,
499 int row,
500 const char* _name,
501 PluginGtkController* controller)
502 {
503 char name[600];
504 strcpy (name, _name);
505 int preformatted = false;
506
507 if (controller && controller->port)
508 {
509 PortIdentifier id = controller->port->id;
510
511 #define FORMAT_UNIT(caps) \
512 case PORT_UNIT_##caps: \
513 sprintf ( \
514 name, "%s <small>(%s)</small>", \
515 _name, port_unit_strings[id.unit].str); \
516 break
517
518 switch (id.unit)
519 {
520 FORMAT_UNIT (HZ);
521 FORMAT_UNIT (DB);
522 FORMAT_UNIT (DEGREES);
523 FORMAT_UNIT (SECONDS);
524 FORMAT_UNIT (MS);
525 default:
526 break;
527 }
528
529 #undef FORMAT_UNIT
530
531 preformatted = true;
532 }
533
534 GtkWidget* label =
535 plugin_gtk_new_label (
536 name, false, preformatted, 1.0, 0.5);
537 gtk_grid_attach (
538 GTK_GRID (table), label,
539 0, row, 1, 1);
540 int control_left_attach = 1;
541 if (controller->spin)
542 {
543 control_left_attach = 2;
544 gtk_grid_attach (
545 GTK_GRID (table),
546 GTK_WIDGET (controller->spin),
547 1, row, 1, 1);
548 }
549 gtk_grid_attach (
550 GTK_GRID (table), controller->control,
551 control_left_attach, row, 3 - control_left_attach, 1);
552 }
553
554 void
plugin_gtk_build_menu(Plugin * plugin,GtkWidget * window,GtkWidget * vbox)555 plugin_gtk_build_menu (
556 Plugin* plugin,
557 GtkWidget* window,
558 GtkWidget* vbox)
559 {
560 GtkWidget* menu_bar = gtk_menu_bar_new();
561 GtkWidget* file = gtk_menu_item_new_with_mnemonic("_File");
562 GtkWidget* file_menu = gtk_menu_new();
563
564 GtkAccelGroup* ag = gtk_accel_group_new();
565 gtk_window_add_accel_group(GTK_WINDOW(window), ag);
566
567 GtkWidget* save =
568 gtk_menu_item_new_with_mnemonic ("_Save");
569 GtkWidget* quit =
570 gtk_menu_item_new_with_mnemonic ("_Quit");
571
572 gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), file_menu);
573 gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), save);
574 gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit);
575 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file);
576
577 GtkWidget* pset_item = gtk_menu_item_new_with_mnemonic("_Presets");
578 GtkWidget* pset_menu = gtk_menu_new();
579 GtkWidget* save_preset = gtk_menu_item_new_with_mnemonic(
580 "_Save Preset...");
581 GtkWidget* delete_preset = gtk_menu_item_new_with_mnemonic(
582 "_Delete Current Preset...");
583 gtk_menu_item_set_submenu(GTK_MENU_ITEM(pset_item), pset_menu);
584 gtk_menu_shell_append(GTK_MENU_SHELL(pset_menu), save_preset);
585 gtk_menu_shell_append(GTK_MENU_SHELL(pset_menu), delete_preset);
586 gtk_menu_shell_append(GTK_MENU_SHELL(pset_menu),
587 gtk_separator_menu_item_new());
588 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), pset_item);
589
590 PluginGtkPresetMenu menu = {
591 NULL, NULL, GTK_MENU (pset_menu),
592 g_sequence_new (
593 (GDestroyNotify) preset_menu_free)
594 };
595 if (plugin->lv2)
596 {
597 lv2_state_load_presets (
598 plugin->lv2, lv2_gtk_add_preset_to_menu,
599 &menu);
600 }
601 finish_menu (&menu);
602
603 /* connect signals */
604 g_signal_connect (
605 G_OBJECT(quit), "activate",
606 G_CALLBACK(on_quit_activate), window);
607 g_signal_connect (
608 G_OBJECT(save), "activate",
609 G_CALLBACK(on_save_activate), plugin);
610 g_signal_connect (
611 G_OBJECT(save_preset), "activate",
612 G_CALLBACK(plugin_gtk_on_save_preset_activate), plugin);
613 g_signal_connect (
614 G_OBJECT(delete_preset), "activate",
615 G_CALLBACK(on_delete_preset_activate), plugin);
616
617 gtk_box_pack_start (
618 GTK_BOX (vbox), menu_bar, FALSE, FALSE, 0);
619 }
620
621 /**
622 * Called when the plugin window is destroyed.
623 */
624 static void
on_window_destroy(GtkWidget * widget,Plugin * pl)625 on_window_destroy (
626 GtkWidget * widget,
627 Plugin * pl)
628 {
629 g_return_if_fail (IS_PLUGIN_AND_NONNULL (pl));
630 pl->window = NULL;
631 g_message (
632 "destroying window for %s",
633 pl->setting->descr->name);
634
635 /* reinit widget in plugin ports/parameters */
636 for (int i = 0; i < pl->num_in_ports; i++)
637 {
638 Port * port = pl->in_ports[i];
639 port->widget = NULL;
640 }
641
642 if (pl->lv2)
643 {
644 object_free_w_func_and_null (
645 suil_instance_free,
646 pl->lv2->suil_instance);
647 }
648 }
649
650 static gboolean
on_delete_event(GtkWidget * widget,GdkEvent * event,Plugin * plugin)651 on_delete_event (
652 GtkWidget *widget,
653 GdkEvent *event,
654 Plugin * plugin)
655 {
656 plugin->visible = 0;
657 plugin->window = NULL;
658 EVENTS_PUSH (
659 ET_PLUGIN_VISIBILITY_CHANGED, plugin);
660
661 char pl_str[700];
662 plugin_print (plugin, pl_str, 700);
663 g_message (
664 "%s: deleted plugin [%s] window",
665 __func__, pl_str);
666
667 return FALSE;
668 }
669
670 /**
671 * Creates a new GtkWindow that will be used to
672 * either wrap plugin UIs or create generic UIs in.
673 */
674 void
plugin_gtk_create_window(Plugin * plugin)675 plugin_gtk_create_window (
676 Plugin * plugin)
677 {
678 if (plugin->lv2 &&
679 plugin->lv2->has_external_ui &&
680 plugin->lv2->external_ui_widget)
681 {
682 g_message (
683 "plugin has external UI, skipping window "
684 "creation");
685 return;
686 }
687
688 g_message (
689 "creating GTK window for %s",
690 plugin->setting->descr->name);
691
692 /* create window */
693 plugin->window =
694 GTK_WINDOW (
695 gtk_window_new (GTK_WINDOW_TOPLEVEL));
696 plugin_gtk_set_window_title (
697 plugin, plugin->window);
698 gtk_window_set_icon_name (
699 plugin->window, "zrythm");
700 gtk_window_set_role (
701 plugin->window, "plugin_ui");
702
703 if (g_settings_get_boolean (
704 S_P_PLUGINS_UIS, "stay-on-top"))
705 {
706 gtk_window_set_transient_for (
707 plugin->window,
708 GTK_WINDOW (MAIN_WINDOW));
709 }
710
711 /* add vbox for stacking elements */
712 plugin->vbox =
713 GTK_BOX (
714 gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
715 gtk_container_add (
716 GTK_CONTAINER (plugin->window),
717 GTK_WIDGET (plugin->vbox));
718
719 /* add menu bar */
720 plugin_gtk_build_menu (
721 plugin, GTK_WIDGET (plugin->window),
722 GTK_WIDGET (plugin->vbox));
723
724 /* Create/show alignment to contain UI (whether
725 * custom or generic) */
726 plugin->ev_box =
727 GTK_EVENT_BOX (
728 gtk_event_box_new ());
729 gtk_box_pack_start (
730 plugin->vbox, GTK_WIDGET (plugin->ev_box),
731 TRUE, TRUE, 0);
732 gtk_widget_show_all (GTK_WIDGET (plugin->vbox));
733
734 /* connect signals */
735 plugin->destroy_window_id =
736 g_signal_connect (
737 plugin->window, "destroy",
738 G_CALLBACK (on_window_destroy), plugin);
739 plugin->delete_event_id =
740 g_signal_connect (
741 G_OBJECT (plugin->window), "delete-event",
742 G_CALLBACK (on_delete_event), plugin);
743 }
744
745 /**
746 * Eventually called by the callbacks when the user
747 * changes a widget value (e.g., the slider changed
748 * callback) and updates the port values if plugin
749 * is not updating.
750 */
751 static void
set_lv2_control(Lv2Plugin * lv2_plugin,Port * port,uint32_t size,LV2_URID type,const void * body)752 set_lv2_control (
753 Lv2Plugin * lv2_plugin,
754 Port * port,
755 uint32_t size,
756 LV2_URID type,
757 const void * body)
758 {
759 #if 0
760 g_debug (
761 "%s (%s) %s - updating %d",
762 __FILE__, __func__,
763 lilv_node_as_string (control->symbol),
764 control->plugin->updating);
765 #endif
766
767 if (lv2_plugin->updating)
768 return;
769
770 bool is_property =
771 port->id.flags & PORT_FLAG_IS_PROPERTY;
772
773 LV2_Atom_Forge * forge =
774 &lv2_plugin->main_forge;
775 LV2_Atom_Forge_Frame frame;
776
777 if (is_property)
778 {
779 g_debug (
780 "setting property control '%s' to '%s'",
781 port->id.sym,
782 (char *) body);
783
784 uint8_t buf[1024];
785
786 /* forge patch set atom */
787 lv2_atom_forge_set_buffer (
788 forge, buf, sizeof (buf));
789 lv2_atom_forge_object (
790 forge, &frame, 0, PM_URIDS.patch_Set);
791 lv2_atom_forge_key (
792 forge, PM_URIDS.patch_property);
793 lv2_atom_forge_urid (
794 forge,
795 lv2_urid_map_uri (
796 lv2_plugin, port->id.uri));
797 lv2_atom_forge_key (
798 forge, PM_URIDS.patch_value);
799 lv2_atom_forge_atom (
800 forge, size, type);
801 lv2_atom_forge_write (
802 forge, body, size);
803
804 const LV2_Atom* atom =
805 lv2_atom_forge_deref (
806 forge, frame.ref);
807 g_return_if_fail (
808 lv2_plugin->control_in >= 0 &&
809 lv2_plugin->control_in < 400000);
810 lv2_ui_send_event_from_ui_to_plugin (
811 lv2_plugin,
812 (uint32_t) lv2_plugin->control_in,
813 lv2_atom_total_size (atom),
814 PM_URIDS.atom_eventTransfer, atom);
815 }
816 else if (port->value_type == forge->Float)
817 {
818 g_debug (
819 "setting float control '%s' to '%f'",
820 port->id.sym,
821 (double) * (float *) body);
822
823 port->control = *(float*) body;
824 port->unsnapped_control = *(float*)body;
825 }
826 else
827 {
828 g_warning ("control change not handled");
829 }
830 }
831
832 /**
833 * Called by generic UI callbacks when e.g. a slider
834 * changes value.
835 */
836 static void
set_float_control(Plugin * pl,Port * port,float value)837 set_float_control (
838 Plugin * pl,
839 Port * port,
840 float value)
841 {
842 if (pl->lv2)
843 {
844 Lv2Plugin * lv2_plugin = pl->lv2;
845 LV2_URID type = port->value_type;
846 LV2_Atom_Forge * forge =
847 &lv2_plugin->main_forge;
848
849 if (type == forge->Int)
850 {
851 const int32_t ival = lrint (value);
852 set_lv2_control (
853 lv2_plugin, port, sizeof (ival), type,
854 &ival);
855 }
856 else if (type == forge->Long)
857 {
858 const int64_t lval = lrint(value);
859 set_lv2_control (
860 lv2_plugin, port, sizeof (lval), type,
861 &lval);
862 }
863 else if (type == forge->Float)
864 {
865 set_lv2_control (
866 lv2_plugin, port, sizeof (value),
867 type, &value);
868 }
869 else if (type == forge->Double)
870 {
871 const double dval = value;
872 set_lv2_control (
873 lv2_plugin, port, sizeof (dval), type,
874 &dval);
875 }
876 else if (type == forge->Bool)
877 {
878 const int32_t ival = (int32_t) value;
879 set_lv2_control (
880 lv2_plugin, port, sizeof (ival), type,
881 &ival);
882 }
883 else if (port->id.flags &
884 PORT_FLAG_GENERIC_PLUGIN_PORT)
885 {
886 port_set_control_value (
887 port, value, F_NOT_NORMALIZED,
888 F_PUBLISH_EVENTS);
889 }
890 }
891 else if (pl->setting->open_with_carla)
892 {
893 port_set_control_value (
894 port, value, F_NOT_NORMALIZED,
895 F_PUBLISH_EVENTS);
896 }
897
898 PluginGtkController * controller =
899 (PluginGtkController *) port->widget;
900 if (controller && controller->spin &&
901 !math_floats_equal (
902 (float)
903 gtk_spin_button_get_value (
904 controller->spin),
905 value))
906 {
907 gtk_spin_button_set_value (
908 controller->spin, value);
909 }
910 }
911
912 static gboolean
scale_changed(GtkRange * range,Port * port)913 scale_changed (
914 GtkRange * range,
915 Port * port)
916 {
917 /*g_message ("scale changed");*/
918 g_return_val_if_fail (
919 IS_PORT_AND_NONNULL (port), false);
920 PluginGtkController * controller = port->widget;
921 Plugin * pl = controller->plugin;
922 g_return_val_if_fail (
923 IS_PLUGIN_AND_NONNULL (pl), false);
924
925 set_float_control (
926 pl, port, (float) gtk_range_get_value (range));
927
928 return FALSE;
929 }
930
931 static gboolean
spin_changed(GtkSpinButton * spin,Port * port)932 spin_changed (
933 GtkSpinButton * spin,
934 Port * port)
935 {
936 PluginGtkController* controller =
937 port->widget;
938 GtkRange* range =
939 GTK_RANGE (controller->control);
940 const double value =
941 gtk_spin_button_get_value (spin);
942 if (!math_doubles_equal (
943 gtk_range_get_value (range), value))
944 {
945 gtk_range_set_value (range, value);
946 }
947
948 return FALSE;
949 }
950
951 static gboolean
log_scale_changed(GtkRange * range,Port * port)952 log_scale_changed (
953 GtkRange* range,
954 Port * port)
955 {
956 /*g_message ("log scale changed");*/
957 g_return_val_if_fail (
958 IS_PORT_AND_NONNULL (port), false);
959 PluginGtkController * controller = port->widget;
960 Plugin * pl = controller->plugin;
961 g_return_val_if_fail (
962 IS_PLUGIN_AND_NONNULL (pl), false);
963
964 set_float_control (
965 pl, port,
966 expf ((float) gtk_range_get_value (range)));
967
968 return FALSE;
969 }
970
971 static gboolean
log_spin_changed(GtkSpinButton * spin,Port * port)972 log_spin_changed (
973 GtkSpinButton * spin,
974 Port * port)
975 {
976 PluginGtkController * controller =
977 port->widget;
978 GtkRange * range =
979 GTK_RANGE (controller->control);
980 const double value =
981 gtk_spin_button_get_value (spin);
982 if (!math_floats_equal (
983 (float) gtk_range_get_value (range),
984 logf ((float) value)))
985 {
986 gtk_range_set_value (
987 range, (double) logf ((float) value));
988 }
989
990 return FALSE;
991 }
992
993 static void
combo_changed(GtkComboBox * box,Port * port)994 combo_changed (
995 GtkComboBox * box,
996 Port * port)
997 {
998 GtkTreeIter iter;
999 if (gtk_combo_box_get_active_iter (box, &iter))
1000 {
1001 GtkTreeModel* model =
1002 gtk_combo_box_get_model (box);
1003 GValue value = { 0, { { 0 } } };
1004
1005 gtk_tree_model_get_value (
1006 model, &iter, 0, &value);
1007 const double v = g_value_get_float(&value);
1008 g_value_unset(&value);
1009
1010 g_return_if_fail (
1011 IS_PORT_AND_NONNULL (port));
1012 Plugin * pl = port_get_plugin (port, true);
1013 g_return_if_fail (
1014 IS_PLUGIN_AND_NONNULL (pl));
1015
1016 set_float_control (pl, port, (float) v);
1017 }
1018 }
1019
1020 static gboolean
switch_state_set(GtkSwitch * button,gboolean state,Port * port)1021 switch_state_set (
1022 GtkSwitch * button,
1023 gboolean state,
1024 Port * port)
1025 {
1026 /*g_message ("toggle_changed");*/
1027 g_return_val_if_fail (
1028 IS_PORT_AND_NONNULL (port), false);
1029 PluginGtkController * controller = port->widget;
1030 Plugin * pl = controller->plugin;
1031 g_return_val_if_fail (
1032 IS_PLUGIN_AND_NONNULL (pl), false);
1033
1034 set_float_control (
1035 pl, port, state ? 1.0f : 0.0f);
1036
1037 return FALSE;
1038 }
1039
1040 static void
string_changed(GtkEntry * widget,Port * port)1041 string_changed (
1042 GtkEntry * widget,
1043 Port * port)
1044 {
1045 const char* string =
1046 gtk_entry_get_text (widget);
1047
1048 g_return_if_fail (
1049 IS_PORT_AND_NONNULL (port));
1050 PluginGtkController * controller = port->widget;
1051 Plugin * pl = controller->plugin;
1052 g_return_if_fail (
1053 IS_PLUGIN_AND_NONNULL (pl));
1054
1055 if (pl->lv2)
1056 {
1057 set_lv2_control (
1058 pl->lv2, port,
1059 strlen (string) + 1,
1060 pl->lv2->main_forge.String,
1061 string);
1062 }
1063 else
1064 {
1065 /* TODO carla */
1066 }
1067 }
1068
1069 static void
file_changed(GtkFileChooserButton * widget,Port * port)1070 file_changed (
1071 GtkFileChooserButton * widget,
1072 Port * port)
1073 {
1074 const char* filename = gtk_file_chooser_get_filename(
1075 GTK_FILE_CHOOSER (widget));
1076
1077 g_return_if_fail (
1078 IS_PORT_AND_NONNULL (port));
1079 PluginGtkController * controller = port->widget;
1080 Plugin * pl = controller->plugin;
1081 g_return_if_fail (
1082 IS_PLUGIN_AND_NONNULL (pl));
1083
1084 if (pl->lv2)
1085 {
1086 set_lv2_control (
1087 pl->lv2, port, strlen (filename),
1088 pl->lv2->main_forge.Path, filename);
1089 }
1090 }
1091
1092 static PluginGtkController *
new_controller(GtkSpinButton * spin,GtkWidget * control)1093 new_controller (
1094 GtkSpinButton * spin,
1095 GtkWidget * control)
1096 {
1097 PluginGtkController * controller =
1098 object_new (PluginGtkController);
1099
1100 controller->spin = spin;
1101 controller->control = control;
1102
1103 return controller;
1104 }
1105
1106 static PluginGtkController *
make_combo(Port * port,float value)1107 make_combo (
1108 Port * port,
1109 float value)
1110 {
1111 GtkListStore* list_store =
1112 gtk_list_store_new (
1113 2, G_TYPE_FLOAT, G_TYPE_STRING);
1114 int active = -1;
1115 for (int i = 0; i < port->num_scale_points; i++)
1116 {
1117 const PortScalePoint * point =
1118 port->scale_points[i];
1119
1120 GtkTreeIter iter;
1121 gtk_list_store_append (list_store, &iter);
1122 gtk_list_store_set (
1123 list_store, &iter,
1124 0, (double) point->val,
1125 1, point->label,
1126 -1);
1127 if (fabsf (value - point->val) < FLT_EPSILON)
1128 {
1129 active = i;
1130 }
1131 }
1132
1133 GtkWidget* combo =
1134 gtk_combo_box_new_with_model (
1135 GTK_TREE_MODEL (list_store));
1136 gtk_combo_box_set_active (
1137 GTK_COMBO_BOX (combo), active);
1138 g_object_unref (list_store);
1139
1140 bool is_input = port->id.flow == FLOW_INPUT;
1141 gtk_widget_set_sensitive (combo, is_input);
1142
1143 GtkCellRenderer * cell =
1144 gtk_cell_renderer_text_new ();
1145 gtk_cell_layout_pack_start (
1146 GTK_CELL_LAYOUT (combo), cell, TRUE);
1147 gtk_cell_layout_set_attributes (
1148 GTK_CELL_LAYOUT (combo), cell, "text", 1, NULL);
1149
1150 if (is_input)
1151 {
1152 g_signal_connect (
1153 G_OBJECT (combo), "changed",
1154 G_CALLBACK (combo_changed), port);
1155 }
1156
1157 return new_controller (NULL, combo);
1158 }
1159
1160 static PluginGtkController *
make_log_slider(Port * port,float value)1161 make_log_slider (
1162 Port * port,
1163 float value)
1164 {
1165 const float min = port->minf;
1166 const float max = port->maxf;
1167 const float lmin = logf (min);
1168 const float lmax = logf (max);
1169 const float ldft = logf (value);
1170 GtkWidget* scale =
1171 gtk_scale_new_with_range (
1172 GTK_ORIENTATION_HORIZONTAL,
1173 lmin, lmax, 0.001);
1174 gtk_widget_set_size_request (
1175 scale, MIN_SCALE_WIDTH, -1);
1176 GtkWidget* spin =
1177 gtk_spin_button_new_with_range (
1178 min, max, 0.000001);
1179
1180 bool is_input = port->id.flow == FLOW_INPUT;
1181 gtk_widget_set_sensitive (scale, is_input);
1182 gtk_widget_set_sensitive (spin, is_input);
1183
1184 gtk_widget_set_hexpand (scale, 1);
1185
1186 gtk_scale_set_draw_value (
1187 GTK_SCALE (scale), FALSE);
1188 gtk_range_set_value (
1189 GTK_RANGE (scale), ldft);
1190 gtk_spin_button_set_value (
1191 GTK_SPIN_BUTTON (spin), value);
1192
1193 if (is_input)
1194 {
1195 g_signal_connect (
1196 G_OBJECT (scale), "value-changed",
1197 G_CALLBACK (log_scale_changed), port);
1198 g_signal_connect (
1199 G_OBJECT (spin), "value-changed",
1200 G_CALLBACK (log_spin_changed), port);
1201 }
1202
1203 return
1204 new_controller (
1205 GTK_SPIN_BUTTON (spin), scale);
1206 }
1207
1208 static PluginGtkController*
make_slider(Port * port,float value)1209 make_slider (
1210 Port * port,
1211 float value)
1212 {
1213 const float min = port->minf;
1214 const float max = port->maxf;
1215 bool is_integer =
1216 port->id.flags & PORT_FLAG_INTEGER;
1217 const double step =
1218 is_integer ?
1219 1.0 : ((double) (max - min) / 100.0);
1220 GtkWidget * scale =
1221 gtk_scale_new_with_range (
1222 GTK_ORIENTATION_HORIZONTAL, min, max, step);
1223 gtk_widget_set_size_request (
1224 scale, MIN_SCALE_WIDTH, -1);
1225 GtkWidget * spin =
1226 gtk_spin_button_new_with_range (min, max, step);
1227
1228 bool is_input = port->id.flow == FLOW_INPUT;
1229 gtk_widget_set_sensitive (scale, is_input);
1230 gtk_widget_set_sensitive (spin, is_input);
1231
1232 gtk_widget_set_hexpand (scale, 1);
1233
1234 if (is_integer)
1235 {
1236 gtk_spin_button_set_digits (
1237 GTK_SPIN_BUTTON (spin), 0);
1238 }
1239 else
1240 {
1241 gtk_spin_button_set_digits (
1242 GTK_SPIN_BUTTON (spin), 7);
1243 }
1244
1245 gtk_scale_set_draw_value (
1246 GTK_SCALE (scale), FALSE);
1247 gtk_range_set_value (GTK_RANGE(scale), value);
1248 gtk_spin_button_set_value (
1249 GTK_SPIN_BUTTON (spin), value);
1250 if (port->num_scale_points > 0)
1251 {
1252 for (int i = 0; i < port->num_scale_points;
1253 i++)
1254 {
1255 const PortScalePoint * point =
1256 port->scale_points[i];
1257
1258 char * str =
1259 g_markup_printf_escaped (
1260 "<span font_size=\"small\">"
1261 "%s</span>", point->label);
1262 gtk_scale_add_mark (
1263 GTK_SCALE (scale), point->val,
1264 GTK_POS_TOP, str);
1265 }
1266 }
1267
1268 if (is_input)
1269 {
1270 g_signal_connect (
1271 G_OBJECT (scale), "value-changed",
1272 G_CALLBACK(scale_changed), port);
1273 g_signal_connect (
1274 G_OBJECT (spin), "value-changed",
1275 G_CALLBACK (spin_changed), port);
1276 }
1277
1278 return
1279 new_controller (GTK_SPIN_BUTTON (spin), scale);
1280 }
1281
1282 static PluginGtkController*
make_toggle(Port * port,float value)1283 make_toggle (
1284 Port * port,
1285 float value)
1286 {
1287 GtkWidget * check = gtk_switch_new ();
1288 gtk_widget_set_halign (check, GTK_ALIGN_START);
1289
1290 bool is_input = port->id.flow == FLOW_INPUT;
1291 gtk_widget_set_sensitive (check, is_input);
1292
1293 if (!math_floats_equal (value, 0))
1294 {
1295 gtk_switch_set_active (
1296 GTK_SWITCH (check), TRUE);
1297 }
1298
1299 if (is_input)
1300 {
1301 g_signal_connect (
1302 G_OBJECT (check), "state-set",
1303 G_CALLBACK (switch_state_set), port);
1304 }
1305
1306 return new_controller (NULL, check);
1307 }
1308
1309 static PluginGtkController*
make_entry(Port * port)1310 make_entry (
1311 Port * port)
1312 {
1313 GtkWidget* entry = gtk_entry_new();
1314
1315 bool is_input = port->id.flow == FLOW_INPUT;
1316 gtk_widget_set_sensitive (entry, is_input);
1317 if (is_input)
1318 {
1319 g_signal_connect (
1320 G_OBJECT (entry), "activate",
1321 G_CALLBACK (string_changed), port);
1322 }
1323
1324 return new_controller (NULL, entry);
1325 }
1326
1327 static PluginGtkController*
make_file_chooser(Port * port)1328 make_file_chooser (
1329 Port * port)
1330 {
1331 GtkWidget * button =
1332 gtk_file_chooser_button_new (
1333 _("Open File"), GTK_FILE_CHOOSER_ACTION_OPEN);
1334
1335 bool is_input = port->id.flow == FLOW_INPUT;
1336 gtk_widget_set_sensitive (button, is_input);
1337
1338 if (is_input)
1339 {
1340 g_signal_connect (
1341 G_OBJECT (button), "file-set",
1342 G_CALLBACK (file_changed), port);
1343 }
1344
1345 return new_controller (NULL, button);
1346 }
1347
1348 static PluginGtkController *
make_controller(Plugin * pl,Port * port,float value)1349 make_controller (
1350 Plugin * pl,
1351 Port * port,
1352 float value)
1353 {
1354 PluginGtkController * controller = NULL;
1355
1356 if (port->id.flags & PORT_FLAG_TOGGLE)
1357 {
1358 controller = make_toggle (port, value);
1359 }
1360 else if (port->id.flags2 & PORT_FLAG2_ENUMERATION)
1361 {
1362 controller = make_combo (port, value);
1363 }
1364 else if (port->id.flags & PORT_FLAG_LOGARITHMIC)
1365 {
1366 controller = make_log_slider (port, value);
1367 }
1368 else
1369 {
1370 controller = make_slider (port, value);
1371 }
1372
1373 if (controller)
1374 {
1375 controller->port = port;
1376 controller->plugin = pl;
1377 }
1378
1379 return controller;
1380 }
1381
1382 static GtkWidget*
build_control_widget(Plugin * pl,GtkWindow * window)1383 build_control_widget (
1384 Plugin * pl,
1385 GtkWindow * window)
1386 {
1387 GtkWidget * port_table = gtk_grid_new ();
1388
1389 /* Make an array of ports sorted by group */
1390 GArray * controls =
1391 g_array_new (false, true, sizeof (Port *));
1392 for (int i = 0; i < pl->num_in_ports; i++)
1393 {
1394 Port * port = pl->in_ports[i];
1395 if (port->id.type != TYPE_CONTROL)
1396 continue;
1397
1398 g_array_append_val (controls, port);
1399 }
1400 g_array_sort (
1401 controls, port_identifier_port_group_cmp);
1402
1403 /* Add controls in group order */
1404 const char * last_group = NULL;
1405 int n_rows = 0;
1406 int num_ctrls = (int) controls->len;
1407 for (int i = 0; i < num_ctrls; ++i)
1408 {
1409 Port * port =
1410 g_array_index (controls, Port *, i);
1411 PluginGtkController * controller = NULL;
1412 const char * group = port->id.port_group;
1413
1414 /* Check group and add new heading if
1415 * necessary */
1416 if (group &&
1417 !string_is_equal (group, last_group))
1418 {
1419 const char * group_name = group;
1420 GtkWidget * group_label =
1421 plugin_gtk_new_label (
1422 group_name, true, false, 0.0f, 1.0f);
1423 gtk_grid_attach (
1424 GTK_GRID (port_table), group_label,
1425 0, n_rows, 2, 1);
1426 ++n_rows;
1427 }
1428 last_group = group;
1429
1430 /* Make control widget */
1431 if (pl->lv2)
1432 {
1433 LV2_Atom_Forge * forge =
1434 &pl->lv2->main_forge;
1435 if (port->value_type == forge->String)
1436 {
1437 controller = make_entry (port);
1438 }
1439 else if (port->value_type == forge->Path)
1440 {
1441 controller = make_file_chooser (port);
1442 }
1443 else
1444 {
1445 controller =
1446 make_controller (
1447 pl, port, port->deff);
1448 }
1449 }
1450 else
1451 {
1452 /* TODO handle non-float carla params */
1453 controller =
1454 make_controller (
1455 pl, port, port->deff);
1456 }
1457
1458 port->widget = controller;
1459 if (controller)
1460 {
1461 controller->port = port;
1462 controller->plugin = pl;
1463
1464 /* Add row to table for this controller */
1465 plugin_gtk_add_control_row (
1466 port_table, n_rows++,
1467 (port->id.label ?
1468 port->id.label : port->id.sym),
1469 controller);
1470
1471 /* Set tooltip text from comment,
1472 * if available */
1473 if (port->id.comment)
1474 {
1475 gtk_widget_set_tooltip_text (
1476 controller->control,
1477 port->id.comment);
1478 }
1479 }
1480 }
1481
1482 if (n_rows > 0)
1483 {
1484 gtk_window_set_resizable (
1485 GTK_WINDOW (window), TRUE);
1486 GtkWidget * box =
1487 gtk_box_new (
1488 GTK_ORIENTATION_HORIZONTAL, 0.0);
1489 /*gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 8, 8);*/
1490 gtk_widget_set_margin_start (
1491 GTK_WIDGET (port_table), 8);
1492 gtk_widget_set_margin_end (
1493 GTK_WIDGET (port_table), 8);
1494 gtk_container_add (
1495 GTK_CONTAINER (box), port_table);
1496 return box;
1497 }
1498 else
1499 {
1500 gtk_widget_destroy (port_table);
1501 GtkWidget* button =
1502 gtk_button_new_with_label (_("Close"));
1503 g_signal_connect_swapped (
1504 button, "clicked",
1505 G_CALLBACK (gtk_widget_destroy), window);
1506 gtk_window_set_resizable (
1507 GTK_WINDOW (window), FALSE);
1508 return button;
1509 }
1510 }
1511
1512 static double
get_atom_double(Lv2Plugin * plugin,uint32_t size,LV2_URID type,const void * body,bool * is_nan)1513 get_atom_double (
1514 Lv2Plugin * plugin,
1515 uint32_t size,
1516 LV2_URID type,
1517 const void * body,
1518 bool * is_nan)
1519 {
1520 *is_nan = false;
1521
1522 LV2_Atom_Forge * forge = &plugin->main_forge;
1523 if (type == forge->Int || type == forge->Bool)
1524 return *(const int32_t*)body;
1525 else if (type == forge->Long)
1526 return *(const int64_t*)body;
1527 else if (type == forge->Float)
1528 return *(const float*)body;
1529 else if (type == forge->Double)
1530 return *(const double*)body;
1531
1532 *is_nan = true;
1533
1534 return NAN;
1535 }
1536
1537 /**
1538 * Called when a property changed or when there is a
1539 * UI port event to set (update) the widget's value.
1540 */
1541 void
plugin_gtk_generic_set_widget_value(Plugin * pl,PluginGtkController * controller,uint32_t size,LV2_URID type,const void * body)1542 plugin_gtk_generic_set_widget_value (
1543 Plugin * pl,
1544 PluginGtkController * controller,
1545 uint32_t size,
1546 LV2_URID type,
1547 const void * body)
1548 {
1549 GtkWidget * widget = controller->control;
1550 bool is_nan = false;
1551 double fvalue;
1552 if (pl->lv2)
1553 {
1554 fvalue =
1555 get_atom_double (
1556 pl->lv2, size, type, body, &is_nan);
1557 }
1558 else
1559 {
1560 fvalue = (double) *(const float*)body;
1561 }
1562
1563 if (!is_nan)
1564 {
1565 if (GTK_IS_COMBO_BOX (widget))
1566 {
1567 GtkTreeModel* model =
1568 gtk_combo_box_get_model (
1569 GTK_COMBO_BOX (widget));
1570 GValue value = { 0, { { 0 } } };
1571 GtkTreeIter i;
1572 bool valid =
1573 gtk_tree_model_get_iter_first (model, &i);
1574 while (valid)
1575 {
1576 gtk_tree_model_get_value (
1577 model, &i, 0, &value);
1578 const double v =
1579 g_value_get_float (&value);
1580 g_value_unset (&value);
1581 if (fabs (v - fvalue) < DBL_EPSILON)
1582 {
1583 gtk_combo_box_set_active_iter (
1584 GTK_COMBO_BOX (widget), &i);
1585 return;
1586 }
1587 valid =
1588 gtk_tree_model_iter_next(model, &i);
1589 }
1590 }
1591 else if (GTK_IS_TOGGLE_BUTTON (widget))
1592 {
1593 gtk_toggle_button_set_active (
1594 GTK_TOGGLE_BUTTON (widget),
1595 fvalue > 0.0);
1596 }
1597 else if (GTK_IS_RANGE (widget))
1598 {
1599 gtk_range_set_value (
1600 GTK_RANGE (widget), fvalue);
1601 }
1602 else if (GTK_IS_SWITCH (widget))
1603 {
1604 gtk_switch_set_active (
1605 GTK_SWITCH (widget), fvalue > 0.0);
1606 }
1607 else
1608 {
1609 g_warning (
1610 _("Unknown widget type for value"));
1611 }
1612
1613 if (controller->spin)
1614 {
1615 // Update spinner for numeric control
1616 gtk_spin_button_set_value (
1617 GTK_SPIN_BUTTON (controller->spin),
1618 fvalue);
1619 }
1620 }
1621 else if (GTK_IS_ENTRY(widget) &&
1622 type == PM_URIDS.atom_String)
1623 {
1624 gtk_entry_set_text (
1625 GTK_ENTRY(widget), (const char*)body);
1626 }
1627 else if (GTK_IS_FILE_CHOOSER (widget) &&
1628 type == PM_URIDS.atom_Path)
1629 {
1630 gtk_file_chooser_set_filename (
1631 GTK_FILE_CHOOSER(widget),
1632 (const char*)body);
1633 }
1634 else
1635 g_warning (
1636 _("Unknown widget type for value\n"));
1637 }
1638
1639 /**
1640 * Called on each GUI frame to update the GTK UI.
1641 *
1642 * @note This is a GSourceFunc.
1643 */
1644 int
plugin_gtk_update_plugin_ui(Plugin * pl)1645 plugin_gtk_update_plugin_ui (
1646 Plugin * pl)
1647 {
1648 /* Emit UI events (for LV2 plugins). */
1649 if (!pl->setting->open_with_carla &&
1650 pl->lv2 && pl->visible &&
1651 (pl->window ||
1652 pl->lv2->external_ui_widget))
1653 {
1654 Lv2Plugin * lv2_plugin = pl->lv2;
1655
1656 Lv2ControlChange ev;
1657 const size_t space =
1658 zix_ring_read_space (
1659 lv2_plugin->plugin_to_ui_events);
1660 for (size_t i = 0;
1661 i + sizeof(ev) < space;
1662 i += sizeof(ev) + ev.size)
1663 {
1664 /* Read event header to get the size */
1665 zix_ring_read (
1666 lv2_plugin->plugin_to_ui_events,
1667 (char*)&ev, sizeof(ev));
1668
1669 /* Resize read buffer if necessary */
1670 lv2_plugin->ui_event_buf =
1671 g_realloc (
1672 lv2_plugin->ui_event_buf, ev.size);
1673 void* const buf =
1674 lv2_plugin->ui_event_buf;
1675
1676 /* Read event body */
1677 zix_ring_read (
1678 lv2_plugin->plugin_to_ui_events,
1679 (char*)buf, ev.size);
1680
1681 #if 0
1682 if (ev.protocol ==
1683 PM_URIDS.atom_eventTransfer)
1684 {
1685 /* Dump event in Turtle to the
1686 * console */
1687 LV2_Atom * atom = (LV2_Atom *) buf;
1688 char * str =
1689 sratom_to_turtle (
1690 plugin->ui_sratom,
1691 &plugin->unmap,
1692 "plugin:", NULL, NULL,
1693 atom->type, atom->size,
1694 LV2_ATOM_BODY(atom));
1695 g_message (
1696 "Event from plugin to its UI "
1697 "(%u bytes): %s",
1698 atom->size, str);
1699 free(str);
1700 }
1701 #endif
1702
1703 lv2_gtk_ui_port_event (
1704 lv2_plugin, ev.index,
1705 ev.size, ev.protocol,
1706 ev.protocol == 0 ?
1707 (void *)
1708 &pl->lilv_ports[ev.index]->control :
1709 buf);
1710
1711 #if 0
1712 if (ev.protocol == 0)
1713 {
1714 float val = * (float *) buf;
1715 g_message (
1716 "%s = %f",
1717 lv2_port_get_symbol_as_string (
1718 plugin,
1719 &plugin->ports[ev.index]),
1720 (double) val);
1721 }
1722 #endif
1723 }
1724
1725 if (lv2_plugin->has_external_ui &&
1726 lv2_plugin->external_ui_widget)
1727 {
1728 lv2_plugin->external_ui_widget->run (
1729 lv2_plugin->external_ui_widget);
1730 }
1731 }
1732
1733 if (pl->setting->open_with_carla)
1734 {
1735 /* fetch port values */
1736 for (int i = 0; i < pl->num_in_ports; i++)
1737 {
1738 Port * port = pl->in_ports[i];
1739 if (port->id.type != TYPE_CONTROL ||
1740 !port->widget)
1741 {
1742 continue;
1743 }
1744
1745 plugin_gtk_generic_set_widget_value (
1746 pl, port->widget, 0, 0, &port->control);
1747 }
1748 }
1749
1750 return G_SOURCE_CONTINUE;
1751 }
1752
1753 /**
1754 * Opens the generic UI of the plugin.
1755 *
1756 * Assumes plugin_gtk_create_window() has been
1757 * called.
1758 */
1759 void
plugin_gtk_open_generic_ui(Plugin * plugin,bool fire_events)1760 plugin_gtk_open_generic_ui (
1761 Plugin * plugin,
1762 bool fire_events)
1763 {
1764 g_message (
1765 "opening generic GTK window..");
1766 GtkWidget* controls =
1767 build_control_widget (
1768 plugin, plugin->window);
1769 GtkWidget* scroll_win =
1770 gtk_scrolled_window_new (NULL, NULL);
1771 gtk_container_add (
1772 GTK_CONTAINER (scroll_win), controls);
1773 gtk_scrolled_window_set_policy (
1774 GTK_SCROLLED_WINDOW (scroll_win),
1775 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1776 gtk_container_add (
1777 GTK_CONTAINER (plugin->ev_box),
1778 scroll_win);
1779 gtk_widget_show_all (
1780 GTK_WIDGET (plugin->vbox));
1781
1782 GtkRequisition controls_size, box_size;
1783 gtk_widget_get_preferred_size (
1784 GTK_WIDGET (controls), NULL,
1785 &controls_size);
1786 gtk_widget_get_preferred_size (
1787 GTK_WIDGET (plugin->vbox), NULL,
1788 &box_size);
1789
1790 gtk_window_set_default_size (
1791 GTK_WINDOW (plugin->window),
1792 MAX (
1793 MAX (
1794 box_size.width,
1795 controls_size.width) + 24,
1796 640),
1797 box_size.height + controls_size.height);
1798 gtk_window_present (GTK_WINDOW (plugin->window));
1799
1800 if (plugin->setting->descr->protocol ==
1801 PROT_LV2 &&
1802 !plugin->setting->open_with_carla)
1803 {
1804 lv2_ui_init (plugin->lv2);
1805 }
1806
1807 plugin->ui_instantiated = true;
1808 if (fire_events)
1809 {
1810 EVENTS_PUSH (
1811 ET_PLUGIN_VISIBILITY_CHANGED, plugin);
1812 }
1813
1814 g_message (
1815 "plugin window shown, adding idle timeout. "
1816 "Update frequency (Hz): %.01f",
1817 (double) plugin->ui_update_hz);
1818 g_return_if_fail (
1819 plugin->ui_update_hz >=
1820 PLUGIN_MIN_REFRESH_RATE);
1821
1822 plugin->update_ui_source_id =
1823 g_timeout_add (
1824 (guint)
1825 (1000.f / plugin->ui_update_hz),
1826 (GSourceFunc) plugin_gtk_update_plugin_ui,
1827 plugin);
1828 }
1829
1830 /**
1831 * Closes the plugin's UI (either LV2 wrapped with
1832 * suil, generic or LV2 external).
1833 */
1834 int
plugin_gtk_close_ui(Plugin * pl)1835 plugin_gtk_close_ui (
1836 Plugin * pl)
1837 {
1838 g_return_val_if_fail (ZRYTHM_HAVE_UI, -1);
1839
1840 if (pl->update_ui_source_id)
1841 {
1842 g_source_remove (pl->update_ui_source_id);
1843 pl->update_ui_source_id = 0;
1844 }
1845
1846 g_message ("%s called", __func__);
1847 if (pl->window)
1848 {
1849 if (pl->destroy_window_id)
1850 {
1851 g_signal_handler_disconnect (
1852 pl->window, pl->destroy_window_id);
1853 pl->destroy_window_id = 0;
1854 }
1855 if (pl->delete_event_id)
1856 {
1857 g_signal_handler_disconnect (
1858 pl->window, pl->delete_event_id);
1859 pl->delete_event_id = 0;
1860 }
1861 gtk_widget_set_sensitive (
1862 GTK_WIDGET (pl->window), 0);
1863 /*gtk_window_close (*/
1864 /*GTK_WINDOW (pl->window));*/
1865 gtk_widget_destroy (
1866 GTK_WIDGET (pl->window));
1867 pl->window = NULL;
1868 }
1869
1870 if (pl->lv2 &&
1871 pl->lv2->has_external_ui &&
1872 pl->lv2->external_ui_widget &&
1873 pl->external_ui_visible)
1874 {
1875 g_message ("hiding external LV2 UI");
1876 pl->lv2->external_ui_widget->hide (
1877 pl->lv2->external_ui_widget);
1878 pl->external_ui_visible = false;
1879 }
1880
1881 return 0;
1882 }
1883
1884 typedef struct PresetInfo
1885 {
1886 char * name;
1887 GtkComboBoxText * cb;
1888 } PresetInfo;
1889
1890 /**
1891 * Sets up the combo box with all the banks the
1892 * plugin has.
1893 *
1894 * @return Whether any banks were added.
1895 */
1896 bool
plugin_gtk_setup_plugin_banks_combo_box(GtkComboBoxText * cb,Plugin * plugin)1897 plugin_gtk_setup_plugin_banks_combo_box (
1898 GtkComboBoxText * cb,
1899 Plugin * plugin)
1900 {
1901 bool ret = false;
1902 gtk_combo_box_text_remove_all (cb);
1903
1904 if (!plugin)
1905 {
1906 return false;
1907 }
1908
1909 for (int i = 0; i < plugin->num_banks; i++)
1910 {
1911 PluginBank * bank = plugin->banks[i];
1912 gtk_combo_box_text_append (
1913 cb, bank->uri, bank->name);
1914 ret = true;
1915 }
1916
1917 gtk_combo_box_set_active (
1918 GTK_COMBO_BOX (cb),
1919 plugin->selected_bank.bank_idx);
1920
1921 return ret;
1922 }
1923
1924 /**
1925 * Sets up the combo box with all the presets the
1926 * plugin has in the given bank, or all the presets
1927 * if NULL is given.
1928 *
1929 * @return Whether any presets were added.
1930 */
1931 bool
plugin_gtk_setup_plugin_presets_list_box(GtkListBox * box,Plugin * plugin)1932 plugin_gtk_setup_plugin_presets_list_box (
1933 GtkListBox * box,
1934 Plugin * plugin)
1935 {
1936 g_debug ("%s: setting up...", __func__);
1937
1938 z_gtk_container_destroy_all_children (
1939 GTK_CONTAINER (box));
1940
1941 if (!plugin ||
1942 plugin->selected_bank.bank_idx == -1)
1943 {
1944 g_debug (
1945 "%s: no plugin (%p) or selected bank (%d)",
1946 __func__, plugin,
1947 plugin ?
1948 plugin->selected_bank.bank_idx : -100);
1949 return false;
1950 }
1951
1952 char pl_str[800];
1953 plugin_print (plugin, pl_str, 800);
1954 if (!plugin->instantiated)
1955 {
1956 g_message (
1957 "plugin %s not instantiated", pl_str);
1958 return false;
1959 }
1960
1961 bool ret = false;
1962 PluginBank * bank =
1963 plugin->banks[
1964 plugin->selected_bank.bank_idx];
1965 for (int j = 0; j < bank->num_presets;
1966 j++)
1967 {
1968 PluginPreset * preset =
1969 bank->presets[j];
1970 GtkWidget * label =
1971 gtk_label_new (preset->name);
1972 gtk_widget_set_visible (label, true);
1973 gtk_list_box_insert (
1974 box, label, -1);
1975 ret = true;
1976 }
1977
1978 GtkListBoxRow * row =
1979 gtk_list_box_get_row_at_index (
1980 box, plugin->selected_preset.idx);
1981 gtk_list_box_select_row (box, row);
1982
1983 g_debug ("%s: done", __func__);
1984
1985 return ret;
1986 }
1987