New mini toggles
[calf.git] / src / gtk_main_win.cpp
blob4243719667265c2a1a4e1e495008ffe30c386595
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 GtkWidget * align = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
335 gtk_container_add(GTK_CONTAINER(align), title);
336 gtk_table_attach(GTK_TABLE(strip->strip_table), align, 1, 2, row, row + 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL) , 10, 0);
337 gtk_widget_show_all(align);
339 // open button
340 GtkWidget *label = calf_toggle_button_new("Edit");
341 strip->button = label;
342 gtk_widget_set_size_request(GTK_WIDGET(label), 80, -1);
343 g_signal_connect(GTK_OBJECT(label), "toggled", G_CALLBACK(gui_button_pressed),
344 (plugin_ctl_iface *)strip);
345 gtk_widget_show(strip->button);
347 // connect button
348 GtkWidget *con = calf_toggle_button_new("Connect");
349 strip->con = con;
350 gtk_widget_set_size_request(GTK_WIDGET(con), -1, -1);
351 g_signal_connect(GTK_OBJECT(con), "toggled", G_CALLBACK(connect_button_pressed),
352 (plugin_ctl_iface *)strip);
353 gtk_widget_show(strip->con);
355 // delete buton
356 GtkWidget *extra = calf_button_new("Remove");
357 strip->extra = extra;
358 //gtk_widget_set_size_request(GTK_WIDGET(extra), 100, -1);
359 g_signal_connect(GTK_OBJECT(extra), "clicked", G_CALLBACK(extra_button_pressed),
360 (plugin_ctl_iface *)strip);
361 gtk_widget_show(strip->extra);
363 // button box @ 1, 2
364 GtkWidget *buttonBox = gtk_hbox_new(FALSE, 5);
365 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->button), FALSE, FALSE, 0);
366 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->con), FALSE, FALSE, 0);
367 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->extra), FALSE, FALSE, 0);
368 gtk_table_attach(GTK_TABLE(strip->strip_table), buttonBox, 1, 2, row + 1, row + 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 10, 10);
369 gtk_widget_show(buttonBox);
371 // midi box
372 if (metadata->get_midi()) {
373 label = calf_led_new();
374 GtkWidget *midiBox = gtk_vbox_new(FALSE, 1);
375 gtk_box_pack_start(GTK_BOX(midiBox), GTK_WIDGET(gtk_label_new("Midi")), FALSE, FALSE, 0);
376 gtk_box_pack_start(GTK_BOX(midiBox), GTK_WIDGET(label), FALSE, FALSE, 0);
377 gtk_table_attach(GTK_TABLE(strip->strip_table), midiBox, 2, 3, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 5, 3);
378 gtk_widget_set_size_request(GTK_WIDGET(label), 25, 25);
379 strip->midi_in = label;
380 gtk_widget_show_all(midiBox);
381 } else {
382 label = gtk_label_new("");
383 gtk_table_attach(GTK_TABLE(strip->strip_table), label, 2, 3, row, row + 1, GTK_FILL, GTK_EXPAND, 5, 3);
384 gtk_widget_set_size_request(GTK_WIDGET(label), 25, 25);
385 strip->midi_in = label;
386 gtk_widget_show(strip->midi_in);
388 strip->midi_in = label;
390 strip->inBox = NULL;
391 strip->outBox = NULL;
392 strip->audio_in.clear();
393 strip->audio_out.clear();
395 if (metadata->get_input_count()) {
397 GtkWidget *inBox = gtk_vbox_new(FALSE, 1);
399 gtk_box_pack_start(GTK_BOX(inBox), gtk_label_new("Audio In"),FALSE, FALSE, 0);
401 for (int i = 0; i < metadata->get_input_count(); i++)
403 label = calf_vumeter_new();
404 calf_vumeter_set_falloff(CALF_VUMETER(label), 2.5);
405 calf_vumeter_set_hold(CALF_VUMETER(label), 1.5);
406 calf_vumeter_set_width(CALF_VUMETER(label), 100);
407 calf_vumeter_set_height(CALF_VUMETER(label), 12);
408 calf_vumeter_set_position(CALF_VUMETER(label), 2);
409 gtk_box_pack_start(GTK_BOX(inBox), label, TRUE, TRUE, 0);
410 strip->audio_in.push_back(label);
413 strip->inBox = gtk_alignment_new(0.f, 0.f, 1.f, 0.f);
414 gtk_container_add(GTK_CONTAINER(strip->inBox), inBox);
416 gtk_table_attach(GTK_TABLE(strip->strip_table), strip->inBox, 3, 4, row, row + 1, ao, ao, 5, 3);
418 if (get_config()->vu_meters)
419 gtk_widget_show_all(strip->inBox);
421 gtk_widget_set_size_request(GTK_WIDGET(strip->inBox), 120, -1);
424 if (metadata->get_output_count()) {
426 GtkWidget *outBox = gtk_vbox_new(FALSE, 1);
428 gtk_box_pack_start(GTK_BOX(outBox), gtk_label_new("Audio Out"),TRUE, TRUE, 0);
430 for (int i = 0; i < metadata->get_output_count(); i++)
432 label = calf_vumeter_new();
433 calf_vumeter_set_falloff(CALF_VUMETER(label), 2.5);
434 calf_vumeter_set_hold(CALF_VUMETER(label), 1.5);
435 calf_vumeter_set_width(CALF_VUMETER(label), 100);
436 calf_vumeter_set_height(CALF_VUMETER(label), 12);
437 calf_vumeter_set_position(CALF_VUMETER(label), 2);
438 gtk_box_pack_start(GTK_BOX(outBox), label,FALSE, FALSE, 0);
439 strip->audio_out.push_back(label);
442 strip->outBox = gtk_alignment_new(0.f, 0.f, 1.f, 0.f);
443 gtk_container_add(GTK_CONTAINER(strip->outBox), outBox);
445 gtk_table_attach(GTK_TABLE(strip->strip_table), strip->outBox, 4, 5, row, row + 1, ao, ao, 5, 3);
447 if (get_config()->vu_meters)
448 gtk_widget_show_all(strip->outBox);
450 gtk_widget_set_size_request(GTK_WIDGET(strip->outBox), 120, -1);
453 // other stuff bottom right
454 GtkWidget *paramBox = gtk_hbox_new(FALSE, 0);
456 GtkWidget *logoImg = gtk_image_new_from_file(PKGLIBDIR "/logo_button.png");
457 gtk_box_pack_end(GTK_BOX(paramBox), GTK_WIDGET(logoImg), TRUE, TRUE, 0);
459 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);
460 gtk_widget_show_all(GTK_WIDGET(paramBox));
462 row += 2;
464 // bottom light
465 GtkWidget *botImg = gtk_image_new_from_file(PKGLIBDIR "/light_bottom.png");
466 gtk_widget_set_size_request(GTK_WIDGET(botImg), 1, 1);
467 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);
468 gtk_widget_show(botImg);
470 gtk_widget_show(GTK_WIDGET(strip->strip_table));
472 return strip;
475 void gtk_main_window::sort_strips()
477 if(plugins.size() <= 0) return;
478 int rack_float = get_config()->rack_float; // 0=horiz, 1=vert
479 int float_size = get_config()->float_size; // amout of rows/cols before line break
480 int posx, posy;
481 gtk_table_resize(GTK_TABLE(strips_table), (int)(plugins.size() / float_size + 1), float_size);
482 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
484 switch (rack_float) {
485 case 0:
486 default:
487 posx = i->second->id % float_size;
488 posy = (int)(i->second->id / float_size);
489 break;
490 case 1:
491 posy = i->second->id % float_size;
492 posx = (int)(i->second->id / float_size);
493 break;
495 bool rem = false;
496 if(i->second->strip_table->parent != NULL) {
497 rem = true;
498 g_object_ref(i->second->strip_table);
499 gtk_container_remove(GTK_CONTAINER(strips_table), GTK_WIDGET(i->second->strip_table));
501 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);
502 if(rem) g_object_unref(i->second->strip_table);
506 void gtk_main_window::update_strip(plugin_ctl_iface *plugin)
508 // plugin_strip *strip = plugins[plugin];
509 // assert(strip);
513 void gtk_main_window::open_gui(plugin_ctl_iface *plugin)
515 plugin_gui_window *gui_win = new plugin_gui_window(this, this);
516 std::string title = "Calf - ";
517 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());
518 gtk_widget_show(GTK_WIDGET(gui_win->toplevel));
519 plugins[plugin]->gui_win = gui_win;
522 static const char *plugin_pre_xml =
523 "<ui>\n"
524 " <menubar>\n"
525 " <menu action=\"AddPluginMenuAction\">\n"
526 " <placeholder name=\"plugin\">\n";
528 static const char *plugin_post_xml =
529 " </placeholder>\n"
530 " </menu>\n"
531 " </menubar>\n"
532 "</ui>\n"
534 #define countof(X) ( (size_t) ( sizeof(X)/sizeof*(X) ) )
535 void gtk_main_window::register_icons()
537 const char *names[]={"Allpass", "Amplifier", "Analyser",
538 "Bandpass", "Chorus", "Comb", "Compressor",
539 "Constant", "Converter", "Delay", "Distortion",
540 "Dynamics", "Envelope", "EQ", "Expander",
541 "Filter", "Flanger", "Function", "Gate",
542 "Generator", "Highpass", "Instrument",
543 "Limiter", "Mixer", "Modulator", "MultiEQ",
544 "Oscillator", "ParaEQ", "Phaser", "Pitch",
545 "Reverb", "Simulator", "Spatial", "Spectral",
546 "Utility", "Waveshaper"};
547 factory = gtk_icon_factory_new ();
548 for (size_t i = 0; i < countof(names); i++) {
549 char name[1024];
550 strcpy(name, "LV2-");
551 strcat(name, names[i]);
552 if (!gtk_icon_factory_lookup(factory, name)) {
553 std::string iname = std::string(PKGLIBDIR) + "icons/LV2/" + names[i] + ".svg";
554 GdkPixbuf *buf = gdk_pixbuf_new_from_file_at_size(iname.c_str(), 64, 64, NULL);
555 GtkIconSet *icon = gtk_icon_set_new_from_pixbuf(buf);
556 gtk_icon_factory_add (factory, name, icon);
557 gtk_icon_set_unref(icon);
558 g_object_unref(buf);
561 gtk_icon_factory_add_default(factory);
564 void gtk_main_window::add_plugin_action(GtkWidget *src, gpointer data)
566 add_plugin_params *app = (add_plugin_params *)data;
567 app->main_win->new_plugin(app->name.c_str());
570 static void action_destroy_notify(gpointer data)
572 delete (gtk_main_window::add_plugin_params *)data;
575 std::string gtk_main_window::make_plugin_list(GtkActionGroup *actions)
577 string s = plugin_pre_xml;
578 const plugin_registry::plugin_vector &plugins = plugin_registry::instance().get_all();
579 std::string type = "";
580 std::string tmp = "";
581 std::string last = "";
582 unsigned int count = 0;
583 unsigned int size = plugins.size();
585 const plugin_metadata_iface *p = plugins[0];
587 for(unsigned int i = 0; i <= size; i++)
589 if (i < size) {
590 p = plugins[i];
591 type = (p->get_plugin_info()).plugin_type;
592 type = type.substr(0, type.length() - 6);
594 if (type != last or i >= size or !i) {
596 if (i) {
597 if (count > 1) {
598 s += "<menu action='" + last + "'>" + tmp + "</menu>";
599 GtkAction *a = gtk_action_new(last.c_str(), last.c_str(), NULL, ("LV2-" + last).c_str());
600 gtk_action_group_add_action(actions, a);
601 } else {
602 s += tmp;
605 tmp = "";
606 last = type;
607 count = 0;
609 if (i < size) {
610 std::string action = "Add" + string(p->get_id()) + "Action";
611 std::string stock = "LV2-" + type;
612 // TODO:
613 // add lv2 stock icons to plug-ins and not just to menus
614 // GTK_STOCK_OPEN -> ("LV2_" + type).c_str()
615 GtkActionEntry ae = { action.c_str(), stock.c_str(), p->get_label(), NULL, NULL, (GCallback)add_plugin_action };
616 gtk_action_group_add_actions_full(actions, &ae, 1, (gpointer)new add_plugin_params(this, p->get_id()), action_destroy_notify);
617 tmp += string("<menuitem always-show-image=\"true\" action=\"") + action + "\" />";
618 count += 1;
621 return s + plugin_post_xml;
624 static void window_destroy_cb(GtkWindow *window, gpointer data)
626 ((gtk_main_window *)data)->owner->on_main_window_destroy();
629 void gtk_main_window::create()
631 register_icons();
632 toplevel = GTK_WINDOW(gtk_window_new (GTK_WINDOW_TOPLEVEL));
633 std::string title = "Calf JACK Host";
634 gtk_window_set_title(toplevel, title.c_str()); //(owner->get_client_name() + " - Calf JACK Host").c_str());
635 gtk_widget_set_name(GTK_WIDGET(toplevel), "Calf-Rack");
636 gtk_window_set_icon_name(toplevel, "calf");
637 gtk_window_set_role(toplevel, "calf_rack");
639 is_closed = false;
640 gtk_window_set_resizable(toplevel, false);
642 all_vbox = gtk_vbox_new(0, FALSE);
644 ui_mgr = gtk_ui_manager_new();
645 std_actions = gtk_action_group_new("default");
646 gtk_action_group_add_actions(std_actions, actions, sizeof(actions)/sizeof(actions[0]), this);
647 GError *error = NULL;
648 gtk_ui_manager_insert_action_group(ui_mgr, std_actions, 0);
649 gtk_ui_manager_add_ui_from_string(ui_mgr, ui_xml, -1, &error);
650 gtk_box_pack_start(GTK_BOX(all_vbox), gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar"), false, false, 0);
652 gtk_widget_set_size_request(GTK_WIDGET(gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar")), 640, -1);
654 gtk_widget_set_name(GTK_WIDGET(gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar")), "Calf-Menu");
656 plugin_actions = gtk_action_group_new("plugins");
657 string plugin_xml = make_plugin_list(plugin_actions);
658 gtk_ui_manager_insert_action_group(ui_mgr, plugin_actions, 0);
659 gtk_ui_manager_add_ui_from_string(ui_mgr, plugin_xml.c_str(), -1, &error);
661 strips_table = gtk_table_new(0, 1, FALSE);
662 gtk_table_set_col_spacings(GTK_TABLE(strips_table), 0);
663 gtk_table_set_row_spacings(GTK_TABLE(strips_table), 0);
665 for(GList *p = GTK_TABLE(strips_table)->children; p != NULL; p = p->next)
667 GtkTableChild *c = (GtkTableChild *)p->data;
668 if (c->top_attach == 0) {
669 gtk_misc_set_alignment(GTK_MISC(c->widget), 0.5, 0);
672 for (std::vector<jack_host *>::iterator i = plugin_queue.begin(); i != plugin_queue.end(); ++i)
674 plugin_strip *st = create_strip(*i);
675 plugins[*i] = st;
676 update_strip(*i);
678 sort_strips();
680 gtk_container_add(GTK_CONTAINER(all_vbox), strips_table);
681 gtk_container_add(GTK_CONTAINER(toplevel), all_vbox);
683 gtk_widget_set_name(GTK_WIDGET(strips_table), "Calf-Container");
685 gtk_window_add_accel_group(toplevel, gtk_ui_manager_get_accel_group(ui_mgr));
686 gtk_widget_show_all(GTK_WIDGET(toplevel));
687 source_id = g_timeout_add_full(G_PRIORITY_DEFAULT, 1000/30, on_idle, this, NULL); // 30 fps should be enough for everybody
689 notifier = get_config_db()->add_listener(this);
690 on_config_change();
691 g_signal_connect(GTK_OBJECT(toplevel), "destroy", G_CALLBACK(window_destroy_cb), this);
694 void gtk_main_window::on_config_change()
696 get_config()->load(get_config_db());
697 show_rack_ears(get_config()->rack_ears);
698 show_vu_meters(get_config()->vu_meters);
699 sort_strips();
702 void gtk_main_window::refresh_plugin(plugin_ctl_iface *plugin)
704 if (plugins[plugin]->gui_win)
705 plugins[plugin]->gui_win->gui->refresh();
708 void gtk_main_window::on_closed()
710 if (notifier)
712 delete notifier;
713 notifier = NULL;
715 if (source_id)
716 g_source_remove(source_id);
717 is_closed = true;
718 toplevel = NULL;
720 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
722 if (i->second && i->second->gui_win) {
723 i->second->gui_win->close();
726 plugins.clear();
729 static inline float LVL(float value)
731 return value; //sqrt(value) * 0.75;
734 gboolean gtk_main_window::on_idle(void *data)
736 gtk_main_window *self = (gtk_main_window *)data;
738 self->owner->on_idle();
740 if (!self->refresh_controller.check_redraw(GTK_WIDGET(self->toplevel)))
741 return TRUE;
743 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = self->plugins.begin(); i != self->plugins.end(); ++i)
745 if (i->second)
747 plugin_ctl_iface *plugin = i->first;
748 plugin_strip *strip = i->second;
749 int idx = 0;
750 if (strip->inBox && gtk_widget_is_drawable (strip->inBox)) {
751 for (int i = 0; i < (int)strip->audio_in.size(); i++) {
752 calf_vumeter_set_value(CALF_VUMETER(strip->audio_in[i]), LVL(plugin->get_level(idx++)));
755 else
756 idx += strip->audio_in.size();
757 if (strip->outBox && gtk_widget_is_drawable (strip->outBox)) {
758 for (int i = 0; i < (int)strip->audio_out.size(); i++) {
759 calf_vumeter_set_value(CALF_VUMETER(strip->audio_out[i]), LVL(plugin->get_level(idx++)));
762 else
763 idx += strip->audio_out.size();
764 if (plugin->get_metadata_iface()->get_midi()) {
765 calf_led_set_value (CALF_LED (strip->midi_in), plugin->get_level(idx++));
769 return TRUE;
772 void gtk_main_window::open_file()
774 GtkWidget *dialog;
775 dialog = gtk_file_chooser_dialog_new ("Open File",
776 toplevel,
777 GTK_FILE_CHOOSER_ACTION_OPEN,
778 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
779 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
780 NULL);
781 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
783 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
784 char *error = owner->open_file(filename);
785 if (error)
786 display_error(error, filename);
787 else
788 owner->set_current_filename(filename);
789 g_free (filename);
790 free (error);
792 gtk_widget_destroy (dialog);
795 bool gtk_main_window::save_file()
797 if (owner->get_current_filename().empty())
798 return save_file_as();
800 const char *error = owner->save_file(owner->get_current_filename().c_str());
801 if (error)
803 display_error(error, owner->get_current_filename().c_str());
804 return false;
806 return true;
809 bool gtk_main_window::save_file_as()
811 GtkWidget *dialog;
812 bool success = false;
813 dialog = gtk_file_chooser_dialog_new ("Save File",
814 toplevel,
815 GTK_FILE_CHOOSER_ACTION_SAVE,
816 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
817 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
818 NULL);
819 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
821 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
822 char *error = owner->save_file(filename);
823 if (error)
824 display_error(error, filename);
825 else
827 owner->set_current_filename(filename);
828 success = true;
830 g_free (filename);
831 free(error);
833 gtk_widget_destroy (dialog);
834 return success;
837 void gtk_main_window::display_error(const char *error, const char *filename)
839 GtkWidget *dialog;
840 dialog = gtk_message_dialog_new_with_markup (toplevel, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, error, filename, NULL);
841 gtk_dialog_run (GTK_DIALOG (dialog));
842 gtk_widget_destroy (dialog);
845 GtkWidget *gtk_main_window::create_progress_window()
847 GtkWidget *tlw = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
848 gtk_window_set_type_hint (GTK_WINDOW (tlw), GDK_WINDOW_TYPE_HINT_DIALOG);
849 GtkWidget *pbar = gtk_progress_bar_new();
850 gtk_container_add (GTK_CONTAINER(tlw), pbar);
851 gtk_widget_show_all (pbar);
852 return tlw;
855 void gtk_main_window::report_progress(float percentage, const std::string &message)
857 if (percentage < 100)
859 if (!progress_window) {
860 progress_window = create_progress_window();
861 gtk_window_set_modal (GTK_WINDOW (progress_window), TRUE);
862 if (toplevel)
863 gtk_window_set_transient_for (GTK_WINDOW (progress_window), toplevel);
864 gtk_widget_show(progress_window);
866 GtkWidget *pbar = gtk_bin_get_child (GTK_BIN (progress_window));
867 if (!message.empty())
868 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pbar), message.c_str());
869 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (pbar), percentage / 100.0);
871 else
873 if (progress_window) {
874 gtk_window_set_modal (GTK_WINDOW (progress_window), FALSE);
875 gtk_widget_destroy (progress_window);
876 progress_window = NULL;
880 while (gtk_events_pending ())
881 gtk_main_iteration ();
884 void gtk_main_window::add_condition(const std::string &name)
886 conditions.insert(name);
889 void gtk_main_window::show_error(const std::string &text)
891 GtkWidget *widget = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", text.c_str());
892 gtk_dialog_run (GTK_DIALOG (widget));
893 gtk_widget_destroy (widget);