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