LV2: HACK: forbid unloading the .so until lifecycle issues get resolved.
[calf.git] / src / gtk_main_win.cpp
blob3487e566aa5f7f09a9b8fd631c521b8ceb67902f
1 /* Calf DSP Library
2 * GUI main window.
3 * Copyright (C) 2007-2011 Krzysztof Foltman
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
21 #include <calf/ctl_led.h>
22 #include <calf/ctl_vumeter.h>
23 #include <calf/giface.h>
24 #include <calf/gui.h>
25 #include <calf/custom_ctl.h>
26 #include <calf/preset.h>
27 #include <calf/gtk_main_win.h>
28 #include <calf/jackhost.h>
30 using namespace calf_plugins;
31 using namespace std;
33 gtk_main_window::gtk_main_window()
35 toplevel = NULL;
36 owner = NULL;
37 notifier = NULL;
38 is_closed = true;
39 progress_window = NULL;
42 static const char *ui_xml =
43 "<ui>\n"
44 " <menubar>\n"
45 " <menu action=\"FileMenuAction\">\n"
46 " <menuitem action=\"FileOpen\"/>\n"
47 " <menuitem action=\"FileSave\"/>\n"
48 " <menuitem action=\"FileSaveAs\"/>\n"
49 " <separator/>\n"
50 " <menuitem action=\"FileReorder\"/>\n"
51 " <separator/>\n"
52 " <menuitem action=\"FilePreferences\"/>\n"
53 " <separator/>\n"
54 " <menuitem action=\"FileQuit\"/>\n"
55 " </menu>\n"
56 " <menu action=\"AddPluginMenuAction\" />\n"
57 " </menubar>\n"
58 "</ui>\n"
61 const GtkActionEntry gtk_main_window::actions[] = {
62 { "FileMenuAction", NULL, "_File", NULL, "File-related operations", NULL },
63 { "FileOpen", GTK_STOCK_OPEN, "_Open", "<Ctrl>O", "Open a rack file", (GCallback)on_open_action },
64 { "FileSave", GTK_STOCK_SAVE, "_Save", "<Ctrl>S", "Save a rack file", (GCallback)on_save_action },
65 { "FileSaveAs", GTK_STOCK_SAVE_AS, "Save _as...", NULL, "Save a rack file as", (GCallback)on_save_as_action },
66 { "HostMenuAction", NULL, "_Host", NULL, "Host-related operations", NULL },
67 { "AddPluginMenuAction", NULL, "_Add plugin", NULL, "Add a plugin to the rack", NULL },
68 { "FileReorder", NULL, "_Reorder plugins", NULL, "Reorder plugins to minimize latency (experimental)", (GCallback)on_reorder_action },
69 { "FilePreferences", GTK_STOCK_PREFERENCES, "_Preferences...", NULL, "Adjust preferences", (GCallback)on_preferences_action },
70 { "FileQuit", GTK_STOCK_QUIT, "_Quit", "<Ctrl>Q", "Exit application", (GCallback)on_exit_action },
73 void gtk_main_window::on_open_action(GtkWidget *widget, gtk_main_window *main)
75 main->open_file();
78 void gtk_main_window::on_save_action(GtkWidget *widget, gtk_main_window *main)
80 main->save_file();
83 void gtk_main_window::on_save_as_action(GtkWidget *widget, gtk_main_window *main)
85 main->save_file_as();
88 void gtk_main_window::on_reorder_action(GtkWidget *widget, gtk_main_window *main)
90 main->owner->reorder_plugins();
93 void gtk_main_window::on_preferences_action(GtkWidget *widget, gtk_main_window *main)
95 GtkBuilder *prefs_builder = gtk_builder_new();
96 GError *error = NULL;
97 const gchar *objects[] = { "preferences", NULL };
98 if (!gtk_builder_add_objects_from_file(prefs_builder, PKGLIBDIR "/calf-gui.xml", (gchar **)objects, &error))
100 g_warning("Cannot load preferences dialog: %s", error->message);
101 g_error_free(error);
102 g_object_unref(G_OBJECT(prefs_builder));
103 return;
105 GtkWidget *preferences_dlg = GTK_WIDGET(gtk_builder_get_object(prefs_builder, "preferences"));
106 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(prefs_builder, "show-rack-ears")), main->get_config()->rack_ears);
107 gtk_spin_button_set_range(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "rack-float")), 0, 1);
108 gtk_spin_button_set_range(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "float-size")), 1, 32);
109 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "rack-float")), 1, 1);
110 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "float-size")), 1, 1);
111 gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "rack-float")), main->get_config()->rack_float);
112 gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "float-size")), main->get_config()->float_size);
113 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(prefs_builder, "show-vu-meters")), main->get_config()->vu_meters);
114 int response = gtk_dialog_run(GTK_DIALOG(preferences_dlg));
115 if (response == GTK_RESPONSE_OK)
117 main->get_config()->rack_ears = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(prefs_builder, "show-rack-ears")));
118 main->get_config()->rack_float = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "rack-float")));
119 main->get_config()->float_size = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "float-size")));
120 main->get_config()->vu_meters = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(prefs_builder, "show-vu-meters")));
121 main->get_config()->save(main->get_config_db());
123 gtk_widget_destroy(preferences_dlg);
124 g_object_unref(G_OBJECT(prefs_builder));
127 void gtk_main_window::on_exit_action(GtkWidget *widget, gtk_main_window *main)
129 gtk_widget_destroy(GTK_WIDGET(main->toplevel));
132 void gtk_main_window::add_plugin(jack_host *plugin)
134 if (toplevel)
136 plugin_strip *strip = create_strip(plugin);
137 plugins[plugin] = strip;
138 update_strip(plugin);
139 sort_strips();
141 else {
142 plugin_queue.push_back(plugin);
143 //plugins[plugin] = NULL;
147 void gtk_main_window::del_plugin(plugin_ctl_iface *plugin)
149 if (!plugins.count(plugin))
150 return;
151 plugin_strip *strip = plugins[plugin];
152 if (strip->gui_win)
153 strip->gui_win->close();
154 vector<GtkWidget *> to_destroy;
155 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); i++)
157 if (i->second == strip)
158 to_destroy.push_back(i->second->strip_table);
159 else if(i->second->id > strip->id)
160 i->second->id--;
162 for (unsigned int i = 0; i < to_destroy.size(); i++)
163 gtk_container_remove(GTK_CONTAINER(strips_table), to_destroy[i]);
164 plugins.erase(plugin);
165 sort_strips();
168 void gtk_main_window::set_window(plugin_ctl_iface *plugin, plugin_gui_window *gui_win)
170 if (!plugins.count(plugin))
171 return;
172 plugin_strip *strip = plugins[plugin];
173 if (!strip)
174 return;
175 strip->gui_win = gui_win;
176 if (!is_closed)
177 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(strip->button), gui_win != NULL);
180 void gtk_main_window::refresh_all_presets(bool builtin_too)
182 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); i++)
184 if (i->second && i->second->gui_win) {
185 char ch = '0';
186 i->second->gui_win->fill_gui_presets(true, ch);
187 i->second->gui_win->fill_gui_presets(false, ch);
192 static gboolean
193 gui_button_pressed(GtkWidget *button, plugin_strip *strip)
195 GtkToggleButton *tb = GTK_TOGGLE_BUTTON(button);
196 if ((gtk_toggle_button_get_active(tb) != 0) == (strip->gui_win != NULL))
197 return FALSE;
198 if (strip->gui_win) {
199 strip->gui_win->close();
200 strip->gui_win = NULL;
201 } else {
202 strip->main_win->open_gui(strip->plugin);
204 return TRUE;
206 static gboolean
207 connect_button_pressed(GtkWidget *button, plugin_strip *strip)
209 //GtkToggleButton *tb = GTK_TOGGLE_BUTTON(button);
210 if (strip->connector) {
211 strip->connector->close();
212 strip->connector = NULL;
213 } else {
214 strip->connector = new calf_connector(strip);
216 return TRUE;
218 static gboolean
219 extra_button_pressed(GtkWidget *button, plugin_strip *strip)
221 if (strip->connector)
222 strip->connector->close();
223 strip->main_win->owner->remove_plugin(strip->plugin);
224 return TRUE;
227 void gtk_main_window::show_rack_ears(bool show)
229 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
231 if (show)
233 gtk_widget_show(i->second->leftBox);
234 gtk_widget_show(i->second->rightBox);
236 else
238 gtk_widget_hide(i->second->leftBox);
239 gtk_widget_hide(i->second->rightBox);
244 void gtk_main_window::show_vu_meters(bool show)
246 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
248 if (show)
250 if (i->second->inBox)
251 gtk_widget_show(i->second->inBox);
252 if (i->second->outBox)
253 gtk_widget_show(i->second->outBox);
255 else
257 if (i->second->inBox)
258 gtk_widget_hide(i->second->inBox);
259 if (i->second->outBox)
260 gtk_widget_hide(i->second->outBox);
265 plugin_strip *gtk_main_window::create_strip(jack_host *plugin)
267 plugin_strip *strip = new plugin_strip;
268 strip->main_win = this;
269 strip->plugin = plugin;
270 strip->gui_win = NULL;
271 strip->connector = NULL;
272 strip->id = plugins.size();
274 GtkAttachOptions ao = (GtkAttachOptions)(GTK_EXPAND | GTK_FILL);
276 strip->strip_table = gtk_table_new(6, 4, FALSE);
277 gtk_table_set_col_spacings(GTK_TABLE(strip->strip_table), 0);
278 gtk_table_set_row_spacings(GTK_TABLE(strip->strip_table), 0);
280 int row = 0;
281 // g_object_get(G_OBJECT(strips_table), "n-rows", &row, "n-columns", &cols, NULL);
282 // gtk_table_resize(GTK_TABLE(strips_table), row + 4, cols);
283 // printf("%03d %03d", row, cols);
284 // images for left side
285 GtkWidget *nwImg = gtk_image_new_from_file(PKGLIBDIR "/side_d_nw.png");
286 GtkWidget *swImg = gtk_image_new_from_file(PKGLIBDIR "/side_d_sw.png");
287 GtkWidget *wImg = gtk_image_new_from_file(PKGLIBDIR "/side_d_w.png");
288 gtk_widget_set_size_request(GTK_WIDGET(wImg), 56, 1);
290 // images for right side
291 GtkWidget *neImg = gtk_image_new_from_file(PKGLIBDIR "/side_d_ne.png");
292 GtkWidget *seImg = gtk_image_new_from_file(PKGLIBDIR "/side_d_se.png");
293 GtkWidget *eImg = gtk_image_new_from_file(PKGLIBDIR "/side_d_e.png");
294 gtk_widget_set_size_request(GTK_WIDGET(eImg), 56, 1);
296 // pack left box
297 GtkWidget *leftBox = gtk_vbox_new(FALSE, 0);
298 gtk_box_pack_start(GTK_BOX(leftBox), GTK_WIDGET(nwImg), FALSE, FALSE, 0);
299 gtk_box_pack_start(GTK_BOX(leftBox), GTK_WIDGET(wImg), TRUE, TRUE, 0);
300 gtk_box_pack_end(GTK_BOX(leftBox), GTK_WIDGET(swImg), FALSE, FALSE, 0);
301 gtk_widget_show_all(GTK_WIDGET(leftBox));
302 if (!get_config()->rack_ears)
303 gtk_widget_hide(GTK_WIDGET(leftBox));
304 gtk_table_attach(GTK_TABLE(strip->strip_table), leftBox, 0, 1, row, row + 4, (GtkAttachOptions)(0), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
306 // pack right box
307 GtkWidget *rightBox = gtk_vbox_new(FALSE, 0);
308 gtk_box_pack_start(GTK_BOX(rightBox), GTK_WIDGET(neImg), FALSE, FALSE, 0);
309 gtk_box_pack_start(GTK_BOX(rightBox), GTK_WIDGET(eImg), TRUE, TRUE, 0);
310 gtk_box_pack_end(GTK_BOX(rightBox), GTK_WIDGET(seImg), FALSE, FALSE, 0);
311 gtk_widget_show_all(GTK_WIDGET(rightBox));
312 if (!get_config()->rack_ears)
313 gtk_widget_hide(GTK_WIDGET(rightBox));
314 gtk_table_attach(GTK_TABLE(strip->strip_table), rightBox, 5, 6, row, row + 4, (GtkAttachOptions)(0), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
316 strip->leftBox = leftBox;
317 strip->rightBox = rightBox;
320 // top light
321 GtkWidget *topImg = gtk_image_new_from_file(PKGLIBDIR "/light_top.png");
322 gtk_widget_set_size_request(GTK_WIDGET(topImg), 1, 1);
323 gtk_table_attach(GTK_TABLE(strip->strip_table), topImg, 1, 5, row, row + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL | GTK_SHRINK), (GtkAttachOptions)(0), 0, 0);
324 gtk_widget_show(topImg);
325 strip->name = topImg;
326 row ++;
328 // title @ 1, 1
329 const plugin_metadata_iface *metadata = plugin->get_metadata_iface();
330 GtkWidget *title = gtk_label_new(NULL);
331 gtk_widget_set_name(GTK_WIDGET(title), "Calf-Rack-Title");
332 gtk_label_set_markup(GTK_LABEL(title), metadata->get_label());
333 gtk_label_set_justify(GTK_LABEL(title), GTK_JUSTIFY_RIGHT);
334 gtk_table_attach(GTK_TABLE(strip->strip_table), title, 1, 2, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0 , 10, 0);
335 gtk_widget_show(title);
337 // open button
338 GtkWidget *label = calf_toggle_button_new("Edit");
339 strip->button = label;
340 gtk_widget_set_size_request(GTK_WIDGET(label), 80, -1);
341 g_signal_connect(GTK_OBJECT(label), "toggled", G_CALLBACK(gui_button_pressed),
342 (plugin_ctl_iface *)strip);
343 gtk_widget_show(strip->button);
345 // connect button
346 GtkWidget *con = calf_toggle_button_new("Connect");
347 strip->con = con;
348 gtk_widget_set_size_request(GTK_WIDGET(con), -1, -1);
349 g_signal_connect(GTK_OBJECT(con), "toggled", G_CALLBACK(connect_button_pressed),
350 (plugin_ctl_iface *)strip);
351 gtk_widget_show(strip->con);
353 // delete buton
354 GtkWidget *extra = calf_button_new("Remove");
355 strip->extra = extra;
356 //gtk_widget_set_size_request(GTK_WIDGET(extra), 100, -1);
357 g_signal_connect(GTK_OBJECT(extra), "clicked", G_CALLBACK(extra_button_pressed),
358 (plugin_ctl_iface *)strip);
359 gtk_widget_show(strip->extra);
361 // button box @ 1, 2
362 GtkWidget *buttonBox = gtk_hbox_new(FALSE, 5);
363 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->button), FALSE, FALSE, 0);
364 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->con), FALSE, FALSE, 0);
365 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->extra), FALSE, FALSE, 0);
366 gtk_table_attach(GTK_TABLE(strip->strip_table), buttonBox, 1, 2, row + 1, row + 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 10, 10);
367 gtk_widget_show(buttonBox);
369 // midi box
370 if (metadata->get_midi()) {
371 label = calf_led_new();
372 GtkWidget *midiBox = gtk_vbox_new(FALSE, 1);
373 gtk_box_pack_start(GTK_BOX(midiBox), GTK_WIDGET(gtk_label_new("MIDI")), FALSE, FALSE, 0);
374 gtk_box_pack_start(GTK_BOX(midiBox), GTK_WIDGET(label), FALSE, FALSE, 0);
375 gtk_table_attach(GTK_TABLE(strip->strip_table), midiBox, 2, 3, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 5, 3);
376 gtk_widget_set_size_request(GTK_WIDGET(label), 25, 25);
377 strip->midi_in = label;
378 gtk_widget_show_all(midiBox);
379 } else {
380 label = gtk_label_new("");
381 gtk_table_attach(GTK_TABLE(strip->strip_table), label, 2, 3, row, row + 1, GTK_FILL, GTK_EXPAND, 5, 3);
382 gtk_widget_set_size_request(GTK_WIDGET(label), 25, 25);
383 strip->midi_in = label;
384 gtk_widget_show(strip->midi_in);
386 strip->midi_in = label;
388 strip->inBox = NULL;
389 strip->outBox = NULL;
390 strip->audio_in.clear();
391 strip->audio_out.clear();
393 if (metadata->get_input_count()) {
395 GtkWidget *inBox = gtk_vbox_new(FALSE, 1);
397 gtk_box_pack_start(GTK_BOX(inBox), gtk_label_new("AUDIO IN"),FALSE, FALSE, 0);
399 for (int i = 0; i < metadata->get_input_count(); i++)
401 label = calf_vumeter_new();
402 calf_vumeter_set_falloff(CALF_VUMETER(label), 2.5);
403 calf_vumeter_set_hold(CALF_VUMETER(label), 1.5);
404 calf_vumeter_set_width(CALF_VUMETER(label), 100);
405 calf_vumeter_set_height(CALF_VUMETER(label), 12);
406 calf_vumeter_set_position(CALF_VUMETER(label), 2);
407 gtk_box_pack_start(GTK_BOX(inBox), label, TRUE, TRUE, 0);
408 strip->audio_in.push_back(label);
411 strip->inBox = gtk_alignment_new(0.f, 0.f, 1.f, 0.f);
412 gtk_container_add(GTK_CONTAINER(strip->inBox), inBox);
414 gtk_table_attach(GTK_TABLE(strip->strip_table), strip->inBox, 3, 4, row, row + 1, ao, ao, 5, 3);
416 if (get_config()->vu_meters)
417 gtk_widget_show_all(strip->inBox);
419 gtk_widget_set_size_request(GTK_WIDGET(strip->inBox), 120, -1);
422 if (metadata->get_output_count()) {
424 GtkWidget *outBox = gtk_vbox_new(FALSE, 1);
426 gtk_box_pack_start(GTK_BOX(outBox), gtk_label_new("AUDIO OUT"),TRUE, TRUE, 0);
428 for (int i = 0; i < metadata->get_output_count(); i++)
430 label = calf_vumeter_new();
431 calf_vumeter_set_falloff(CALF_VUMETER(label), 2.5);
432 calf_vumeter_set_hold(CALF_VUMETER(label), 1.5);
433 calf_vumeter_set_width(CALF_VUMETER(label), 100);
434 calf_vumeter_set_height(CALF_VUMETER(label), 12);
435 calf_vumeter_set_position(CALF_VUMETER(label), 2);
436 gtk_box_pack_start(GTK_BOX(outBox), label,FALSE, FALSE, 0);
437 strip->audio_out.push_back(label);
440 strip->outBox = gtk_alignment_new(0.f, 0.f, 1.f, 0.f);
441 gtk_container_add(GTK_CONTAINER(strip->outBox), outBox);
443 gtk_table_attach(GTK_TABLE(strip->strip_table), strip->outBox, 4, 5, row, row + 1, ao, ao, 5, 3);
445 if (get_config()->vu_meters)
446 gtk_widget_show_all(strip->outBox);
448 gtk_widget_set_size_request(GTK_WIDGET(strip->outBox), 120, -1);
451 // other stuff bottom right
452 GtkWidget *paramBox = gtk_hbox_new(FALSE, 0);
454 GtkWidget *logoImg = gtk_image_new_from_file(PKGLIBDIR "/logo_button.png");
455 gtk_box_pack_start(GTK_BOX(paramBox), GTK_WIDGET(logoImg), TRUE, TRUE, 0);
457 gtk_table_attach(GTK_TABLE(strip->strip_table), paramBox, 3, 5, row + 1, row + 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 10, 0);
458 gtk_widget_show_all(GTK_WIDGET(paramBox));
460 row += 2;
462 // bottom light
463 GtkWidget *botImg = gtk_image_new_from_file(PKGLIBDIR "/light_bottom.png");
464 gtk_widget_set_size_request(GTK_WIDGET(botImg), 1, 1);
465 gtk_table_attach(GTK_TABLE(strip->strip_table), botImg, 1, 5, row, row + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL | GTK_SHRINK), (GtkAttachOptions)(0), 0, 0);
466 gtk_widget_show(botImg);
468 gtk_widget_show(GTK_WIDGET(strip->strip_table));
470 return strip;
473 void gtk_main_window::sort_strips()
475 if(plugins.size() <= 0) return;
476 int rack_float = get_config()->rack_float; // 0=horiz, 1=vert
477 int float_size = get_config()->float_size; // amout of rows/cols before line break
478 int posx, posy;
479 gtk_table_resize(GTK_TABLE(strips_table), (int)(plugins.size() / float_size + 1), float_size);
480 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); i++)
482 switch (rack_float) {
483 case 0:
484 default:
485 posx = i->second->id % float_size;
486 posy = (int)(i->second->id / float_size);
487 break;
488 case 1:
489 posy = i->second->id % float_size;
490 posx = (int)(i->second->id / float_size);
491 break;
493 bool rem = false;
494 if(i->second->strip_table->parent != NULL) {
495 rem = true;
496 g_object_ref(i->second->strip_table);
497 gtk_container_remove(GTK_CONTAINER(strips_table), GTK_WIDGET(i->second->strip_table));
499 gtk_table_attach(GTK_TABLE(strips_table), i->second->strip_table, posx, posx + 1, posy, posy + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL | GTK_SHRINK), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL | GTK_SHRINK), 0, 0);
500 if(rem) g_object_unref(i->second->strip_table);
504 void gtk_main_window::update_strip(plugin_ctl_iface *plugin)
506 // plugin_strip *strip = plugins[plugin];
507 // assert(strip);
511 void gtk_main_window::open_gui(plugin_ctl_iface *plugin)
513 plugin_gui_window *gui_win = new plugin_gui_window(this, this);
514 std::string title = "Calf - ";
515 gui_win->create(plugin, (title + plugin->get_metadata_iface()->get_label()).c_str(), plugin->get_metadata_iface()->get_id()); //(owner->get_client_name() + " - " + plugin->get_metadata_iface()->get_label()).c_str(), plugin->get_metadata_iface()->get_id());
516 gtk_widget_show(GTK_WIDGET(gui_win->toplevel));
517 plugins[plugin]->gui_win = gui_win;
520 static const char *plugin_pre_xml =
521 "<ui>\n"
522 " <menubar>\n"
523 " <menu action=\"AddPluginMenuAction\">\n"
524 " <placeholder name=\"plugin\">\n";
526 static const char *plugin_post_xml =
527 " </placeholder>\n"
528 " </menu>\n"
529 " </menubar>\n"
530 "</ui>\n"
532 #define countof(X) ( (size_t) ( sizeof(X)/sizeof*(X) ) )
533 void gtk_main_window::register_icons()
535 const char *names[]={"Allpass", "Amplifier", "Analyser",
536 "Bandpass", "Chorus", "Comb", "Compressor",
537 "Constant", "Converter", "Delay", "Distortion",
538 "Dynamics", "Envelope", "EQ", "Expander",
539 "Filter", "Flanger", "Function", "Gate",
540 "Generator", "Highpass", "Instrument",
541 "Limiter", "Mixer", "Modulator", "MultiEQ",
542 "Oscillator", "ParaEQ", "Phaser", "Pitch",
543 "Reverb", "Simulator", "Spatial", "Spectral",
544 "Utility", "Waveshaper"};
545 factory = gtk_icon_factory_new ();
546 for (size_t i = 0; i < countof(names); i++) {
547 char name[1024];
548 strcpy(name, "LV2-");
549 strcat(name, names[i]);
550 if (!gtk_icon_factory_lookup(factory, name)) {
551 std::string iname = std::string(PKGLIBDIR) + "icons/LV2/" + names[i] + ".svg";
552 GdkPixbuf *buf = gdk_pixbuf_new_from_file_at_size(iname.c_str(), 64, 64, NULL);
553 GtkIconSet *icon = gtk_icon_set_new_from_pixbuf(buf);
554 gtk_icon_factory_add (factory, name, icon);
555 gtk_icon_set_unref(icon);
556 g_object_unref(buf);
559 gtk_icon_factory_add_default(factory);
562 void gtk_main_window::add_plugin_action(GtkWidget *src, gpointer data)
564 add_plugin_params *app = (add_plugin_params *)data;
565 app->main_win->new_plugin(app->name.c_str());
568 static void action_destroy_notify(gpointer data)
570 delete (gtk_main_window::add_plugin_params *)data;
573 std::string gtk_main_window::make_plugin_list(GtkActionGroup *actions)
575 string s = plugin_pre_xml;
576 const plugin_registry::plugin_vector &plugins = plugin_registry::instance().get_all();
577 std::string type = "";
578 std::string tmp = "";
579 std::string last = "";
580 unsigned int count = 0;
581 unsigned int size = plugins.size();
583 const plugin_metadata_iface *p = plugins[0];
585 for(unsigned int i = 0; i <= size; i++)
587 if (i < size) {
588 p = plugins[i];
589 type = (p->get_plugin_info()).plugin_type;
590 type = type.substr(0, type.length() - 6);
592 if (type != last or i >= size or !i) {
594 if (i) {
595 if (count > 1) {
596 s += "<menu action='" + last + "'>" + tmp + "</menu>";
597 GtkAction *a = gtk_action_new(last.c_str(), last.c_str(), NULL, ("LV2-" + last).c_str());
598 gtk_action_group_add_action(actions, a);
599 } else {
600 s += tmp;
603 tmp = "";
604 last = type;
605 count = 0;
607 if (i < size) {
608 std::string action = "Add" + string(p->get_id()) + "Action";
609 std::string stock = "LV2-" + type;
610 // TODO:
611 // add lv2 stock icons to plug-ins and not just to menus
612 // GTK_STOCK_OPEN -> ("LV2_" + type).c_str()
613 GtkActionEntry ae = { action.c_str(), stock.c_str(), p->get_label(), NULL, NULL, (GCallback)add_plugin_action };
614 gtk_action_group_add_actions_full(actions, &ae, 1, (gpointer)new add_plugin_params(this, p->get_id()), action_destroy_notify);
615 tmp += string("<menuitem always-show-image=\"true\" action=\"") + action + "\" />";
616 count += 1;
619 return s + plugin_post_xml;
622 static void window_destroy_cb(GtkWindow *window, gpointer data)
624 ((gtk_main_window *)data)->owner->on_main_window_destroy();
627 void gtk_main_window::create()
629 register_icons();
630 toplevel = GTK_WINDOW(gtk_window_new (GTK_WINDOW_TOPLEVEL));
631 std::string title = "Calf JACK Host";
632 gtk_window_set_title(toplevel, title.c_str()); //(owner->get_client_name() + " - Calf JACK Host").c_str());
633 gtk_widget_set_name(GTK_WIDGET(toplevel), "Calf-Rack");
634 gtk_window_set_icon_name(toplevel, "calf");
635 gtk_window_set_role(toplevel, "calf_rack");
637 is_closed = false;
638 gtk_window_set_resizable(toplevel, false);
640 all_vbox = gtk_vbox_new(0, FALSE);
642 ui_mgr = gtk_ui_manager_new();
643 std_actions = gtk_action_group_new("default");
644 gtk_action_group_add_actions(std_actions, actions, sizeof(actions)/sizeof(actions[0]), this);
645 GError *error = NULL;
646 gtk_ui_manager_insert_action_group(ui_mgr, std_actions, 0);
647 gtk_ui_manager_add_ui_from_string(ui_mgr, ui_xml, -1, &error);
648 gtk_box_pack_start(GTK_BOX(all_vbox), gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar"), false, false, 0);
650 gtk_widget_set_size_request(GTK_WIDGET(gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar")), 640, -1);
652 gtk_widget_set_name(GTK_WIDGET(gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar")), "Calf-Menu");
654 plugin_actions = gtk_action_group_new("plugins");
655 string plugin_xml = make_plugin_list(plugin_actions);
656 gtk_ui_manager_insert_action_group(ui_mgr, plugin_actions, 0);
657 gtk_ui_manager_add_ui_from_string(ui_mgr, plugin_xml.c_str(), -1, &error);
659 strips_table = gtk_table_new(0, 1, FALSE);
660 gtk_table_set_col_spacings(GTK_TABLE(strips_table), 0);
661 gtk_table_set_row_spacings(GTK_TABLE(strips_table), 0);
663 for(GList *p = GTK_TABLE(strips_table)->children; p != NULL; p = p->next)
665 GtkTableChild *c = (GtkTableChild *)p->data;
666 if (c->top_attach == 0) {
667 gtk_misc_set_alignment(GTK_MISC(c->widget), 0.5, 0);
670 for (std::vector<jack_host *>::iterator i = plugin_queue.begin(); i != plugin_queue.end(); i++)
672 plugin_strip *st = create_strip(*i);
673 plugins[*i] = st;
674 update_strip(*i);
676 sort_strips();
678 gtk_container_add(GTK_CONTAINER(all_vbox), strips_table);
679 gtk_container_add(GTK_CONTAINER(toplevel), all_vbox);
681 gtk_widget_set_name(GTK_WIDGET(strips_table), "Calf-Container");
683 gtk_window_add_accel_group(toplevel, gtk_ui_manager_get_accel_group(ui_mgr));
684 gtk_widget_show_all(GTK_WIDGET(toplevel));
685 source_id = g_timeout_add_full(G_PRIORITY_DEFAULT, 1000/30, on_idle, this, NULL); // 30 fps should be enough for everybody
687 notifier = get_config_db()->add_listener(this);
688 on_config_change();
689 g_signal_connect(GTK_OBJECT(toplevel), "destroy", G_CALLBACK(window_destroy_cb), this);
692 void gtk_main_window::on_config_change()
694 get_config()->load(get_config_db());
695 show_rack_ears(get_config()->rack_ears);
696 show_vu_meters(get_config()->vu_meters);
697 sort_strips();
700 void gtk_main_window::refresh_plugin(plugin_ctl_iface *plugin)
702 if (plugins[plugin]->gui_win)
703 plugins[plugin]->gui_win->gui->refresh();
706 void gtk_main_window::on_closed()
708 if (notifier)
710 delete notifier;
711 notifier = NULL;
713 if (source_id)
714 g_source_remove(source_id);
715 is_closed = true;
716 toplevel = NULL;
718 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); i++)
720 if (i->second && i->second->gui_win) {
721 i->second->gui_win->close();
724 plugins.clear();
727 static inline float LVL(float value)
729 return value; //sqrt(value) * 0.75;
732 gboolean gtk_main_window::on_idle(void *data)
734 gtk_main_window *self = (gtk_main_window *)data;
736 self->owner->on_idle();
738 if (!self->refresh_controller.check_redraw(GTK_WIDGET(self->toplevel)))
739 return TRUE;
741 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = self->plugins.begin(); i != self->plugins.end(); i++)
743 if (i->second)
745 plugin_ctl_iface *plugin = i->first;
746 plugin_strip *strip = i->second;
747 int idx = 0;
748 if (strip->inBox && gtk_widget_is_drawable (strip->inBox)) {
749 for (int i = 0; i < (int)strip->audio_in.size(); i++) {
750 calf_vumeter_set_value(CALF_VUMETER(strip->audio_in[i]), LVL(plugin->get_level(idx++)));
753 else
754 idx += strip->audio_in.size();
755 if (strip->outBox && gtk_widget_is_drawable (strip->outBox)) {
756 for (int i = 0; i < (int)strip->audio_out.size(); i++) {
757 calf_vumeter_set_value(CALF_VUMETER(strip->audio_out[i]), LVL(plugin->get_level(idx++)));
760 else
761 idx += strip->audio_out.size();
762 if (plugin->get_metadata_iface()->get_midi()) {
763 calf_led_set_value (CALF_LED (strip->midi_in), plugin->get_level(idx++));
767 return TRUE;
770 void gtk_main_window::open_file()
772 GtkWidget *dialog;
773 dialog = gtk_file_chooser_dialog_new ("Open File",
774 toplevel,
775 GTK_FILE_CHOOSER_ACTION_OPEN,
776 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
777 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
778 NULL);
779 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
781 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
782 char *error = owner->open_file(filename);
783 if (error)
784 display_error(error, filename);
785 else
786 owner->set_current_filename(filename);
787 g_free (filename);
788 free (error);
790 gtk_widget_destroy (dialog);
793 bool gtk_main_window::save_file()
795 if (owner->get_current_filename().empty())
796 return save_file_as();
798 const char *error = owner->save_file(owner->get_current_filename().c_str());
799 if (error)
801 display_error(error, owner->get_current_filename().c_str());
802 return false;
804 return true;
807 bool gtk_main_window::save_file_as()
809 GtkWidget *dialog;
810 bool success = false;
811 dialog = gtk_file_chooser_dialog_new ("Save File",
812 toplevel,
813 GTK_FILE_CHOOSER_ACTION_SAVE,
814 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
815 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
816 NULL);
817 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
819 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
820 char *error = owner->save_file(filename);
821 if (error)
822 display_error(error, filename);
823 else
825 owner->set_current_filename(filename);
826 success = true;
828 g_free (filename);
829 free(error);
831 gtk_widget_destroy (dialog);
832 return success;
835 void gtk_main_window::display_error(const char *error, const char *filename)
837 GtkWidget *dialog;
838 dialog = gtk_message_dialog_new_with_markup (toplevel, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, error, filename, NULL);
839 gtk_dialog_run (GTK_DIALOG (dialog));
840 gtk_widget_destroy (dialog);
843 GtkWidget *gtk_main_window::create_progress_window()
845 GtkWidget *tlw = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
846 gtk_window_set_type_hint (GTK_WINDOW (tlw), GDK_WINDOW_TYPE_HINT_DIALOG);
847 GtkWidget *pbar = gtk_progress_bar_new();
848 gtk_container_add (GTK_CONTAINER(tlw), pbar);
849 gtk_widget_show_all (pbar);
850 return tlw;
853 void gtk_main_window::report_progress(float percentage, const std::string &message)
855 if (percentage < 100)
857 if (!progress_window) {
858 progress_window = create_progress_window();
859 gtk_window_set_modal (GTK_WINDOW (progress_window), TRUE);
860 if (toplevel)
861 gtk_window_set_transient_for (GTK_WINDOW (progress_window), toplevel);
862 gtk_widget_show(progress_window);
864 GtkWidget *pbar = gtk_bin_get_child (GTK_BIN (progress_window));
865 if (!message.empty())
866 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pbar), message.c_str());
867 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (pbar), percentage / 100.0);
869 else
871 if (progress_window) {
872 gtk_window_set_modal (GTK_WINDOW (progress_window), FALSE);
873 gtk_widget_destroy (progress_window);
874 progress_window = NULL;
878 while (gtk_events_pending ())
879 gtk_main_iteration ();
882 void gtk_main_window::add_condition(const std::string &name)
884 conditions.insert(name);
887 void gtk_main_window::show_error(const std::string &text)
889 GtkWidget *widget = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", text.c_str());
890 gtk_dialog_run (GTK_DIALOG (widget));
891 gtk_widget_destroy (widget);