Make Analyzer UI require instance-access
[calf.git] / src / gtk_main_win.cpp
blob5ac34a38210cc82c549ce394e853ab89dd9519fe
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>
29 #include <iostream>
30 #include <fstream>
32 using namespace calf_plugins;
33 using namespace std;
35 gtk_main_window::gtk_main_window()
37 toplevel = NULL;
38 owner = NULL;
39 notifier = NULL;
40 is_closed = true;
41 progress_window = NULL;
42 images = image_factory();
45 static const char *ui_xml =
46 "<ui>\n"
47 " <menubar>\n"
48 " <menu action=\"FileMenuAction\">\n"
49 " <menuitem action=\"FileOpen\"/>\n"
50 " <menuitem action=\"FileSave\"/>\n"
51 " <menuitem action=\"FileSaveAs\"/>\n"
52 " <separator/>\n"
53 " <menuitem action=\"FileReorder\"/>\n"
54 " <separator/>\n"
55 " <menuitem action=\"FilePreferences\"/>\n"
56 " <separator/>\n"
57 " <menuitem action=\"FileQuit\"/>\n"
58 " </menu>\n"
59 " <menu action=\"AddPluginMenuAction\" />\n"
60 " </menubar>\n"
61 "</ui>\n"
64 const GtkActionEntry gtk_main_window::actions[] = {
65 { "FileMenuAction", NULL, "_File", NULL, "File-related operations", NULL },
66 { "FileOpen", GTK_STOCK_OPEN, "_Open", "<Ctrl>O", "Open a rack file", (GCallback)on_open_action },
67 { "FileSave", GTK_STOCK_SAVE, "_Save", "<Ctrl>S", "Save a rack file", (GCallback)on_save_action },
68 { "FileSaveAs", GTK_STOCK_SAVE_AS, "Save _as...", NULL, "Save a rack file as", (GCallback)on_save_as_action },
69 { "HostMenuAction", NULL, "_Host", NULL, "Host-related operations", NULL },
70 { "AddPluginMenuAction", NULL, "_Add plugin", NULL, "Add a plugin to the rack", NULL },
71 { "FileReorder", NULL, "_Reorder plugins", NULL, "Reorder plugins to minimize latency (experimental)", (GCallback)on_reorder_action },
72 { "FilePreferences", GTK_STOCK_PREFERENCES, "_Preferences...", NULL, "Adjust preferences", (GCallback)on_preferences_action },
73 { "FileQuit", GTK_STOCK_QUIT, "_Quit", "<Ctrl>Q", "Exit application", (GCallback)on_exit_action },
76 void gtk_main_window::on_open_action(GtkWidget *widget, gtk_main_window *main)
78 main->open_file();
81 void gtk_main_window::on_save_action(GtkWidget *widget, gtk_main_window *main)
83 main->save_file();
86 void gtk_main_window::on_save_as_action(GtkWidget *widget, gtk_main_window *main)
88 main->save_file_as();
91 void gtk_main_window::on_reorder_action(GtkWidget *widget, gtk_main_window *main)
93 main->owner->reorder_plugins();
96 void gtk_main_window::on_preferences_action(GtkWidget *widget, gtk_main_window *main)
98 GtkBuilder *prefs_builder = gtk_builder_new();
99 GError *error = NULL;
100 const gchar *objects[] = { "preferences", NULL };
101 if (!gtk_builder_add_objects_from_file(prefs_builder, PKGLIBDIR "/calf-gui.xml", (gchar **)objects, &error))
103 g_warning("Cannot load preferences dialog: %s", error->message);
104 g_error_free(error);
105 g_object_unref(G_OBJECT(prefs_builder));
106 return;
109 // styles selector
110 GtkCellRenderer *cell;
111 GtkListStore *styles = main->get_styles();
112 GtkComboBox *cb = GTK_COMBO_BOX(gtk_builder_get_object(prefs_builder, "rcstyles"));
113 gtk_combo_box_set_model(cb, GTK_TREE_MODEL(styles));
114 cell = gtk_cell_renderer_text_new();
115 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(cb), cell, TRUE);
116 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(cb), cell, "text", 0, NULL);
117 GtkTreeIter iter;
118 gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(styles), &iter);
119 while (valid) {
120 GValue path = {0,};
121 gtk_tree_model_get_value(GTK_TREE_MODEL(styles), &iter, 1, &path);
122 if (main->get_config()->style.compare(g_value_get_string(&path)) == 0) {
123 gtk_combo_box_set_active_iter(cb, &iter);
124 break;
126 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(styles), &iter);
127 g_value_unset(&path);
130 GtkWidget *preferences_dlg = GTK_WIDGET(gtk_builder_get_object(prefs_builder, "preferences"));
131 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(prefs_builder, "show-rack-ears")), main->get_config()->rack_ears);
132 gtk_spin_button_set_range(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "rack-float")), 0, 1);
133 gtk_spin_button_set_range(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "float-size")), 1, 32);
134 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "rack-float")), 1, 1);
135 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "float-size")), 1, 1);
136 gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "rack-float")), main->get_config()->rack_float);
137 gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "float-size")), main->get_config()->float_size);
138 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(prefs_builder, "show-vu-meters")), main->get_config()->vu_meters);
139 int response = gtk_dialog_run(GTK_DIALOG(preferences_dlg));
140 if (response == GTK_RESPONSE_OK)
142 GValue path_ = {0,};
143 GtkTreeIter iter;
144 gtk_combo_box_get_active_iter(cb, &iter);
145 gtk_tree_model_get_value(GTK_TREE_MODEL(styles), &iter, 1, &path_);
146 main->get_config()->rack_ears = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(prefs_builder, "show-rack-ears")));
147 main->get_config()->rack_float = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "rack-float")));
148 main->get_config()->float_size = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(prefs_builder, "float-size")));
149 main->get_config()->vu_meters = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(prefs_builder, "show-vu-meters")));
150 main->get_config()->style = g_value_get_string(&path_);
151 main->get_config()->save(main->get_config_db());
152 //main->load_style(g_value_get_string(&path_));
154 gtk_widget_destroy(preferences_dlg);
155 g_object_unref(G_OBJECT(prefs_builder));
158 void gtk_main_window::on_exit_action(GtkWidget *widget, gtk_main_window *main)
160 gtk_widget_destroy(GTK_WIDGET(main->toplevel));
163 void gtk_main_window::add_plugin(jack_host *plugin)
165 if (toplevel)
167 plugin_strip *strip = create_strip(plugin);
168 plugins[plugin] = strip;
169 update_strip(plugin);
170 sort_strips();
172 else {
173 plugin_queue.push_back(plugin);
174 //plugins[plugin] = NULL;
178 void gtk_main_window::del_plugin(plugin_ctl_iface *plugin)
180 if (!plugins.count(plugin))
181 return;
182 plugin_strip *strip = plugins[plugin];
183 if (strip->gui_win)
184 strip->gui_win->close();
185 vector<GtkWidget *> to_destroy;
186 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
188 if (i->second == strip)
189 to_destroy.push_back(i->second->strip_table);
190 else if(i->second->id > strip->id)
191 i->second->id--;
193 for (unsigned int i = 0; i < to_destroy.size(); i++)
194 gtk_container_remove(GTK_CONTAINER(strips_table), to_destroy[i]);
195 plugins.erase(plugin);
196 sort_strips();
199 void gtk_main_window::set_window(plugin_ctl_iface *plugin, plugin_gui_window *gui_win)
201 if (!plugins.count(plugin))
202 return;
203 plugin_strip *strip = plugins[plugin];
204 if (!strip)
205 return;
206 strip->gui_win = gui_win;
207 if (!is_closed)
208 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(strip->button), gui_win != NULL);
211 void gtk_main_window::refresh_all_presets(bool builtin_too)
213 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
215 if (i->second && i->second->gui_win) {
216 char ch = '0';
217 i->second->gui_win->fill_gui_presets(true, ch);
218 i->second->gui_win->fill_gui_presets(false, ch);
223 static gboolean
224 gui_button_pressed(GtkWidget *button, plugin_strip *strip)
226 GtkToggleButton *tb = GTK_TOGGLE_BUTTON(button);
227 if ((gtk_toggle_button_get_active(tb) != 0) == (strip->gui_win != NULL))
228 return FALSE;
229 if (strip->gui_win) {
230 strip->gui_win->close();
231 strip->gui_win = NULL;
232 } else {
233 strip->main_win->open_gui(strip->plugin);
235 return TRUE;
237 static gboolean
238 connect_button_pressed(GtkWidget *button, plugin_strip *strip)
240 //GtkToggleButton *tb = GTK_TOGGLE_BUTTON(button);
241 if (strip->connector) {
242 strip->connector->close();
243 strip->connector = NULL;
244 } else {
245 strip->connector = new calf_connector(strip);
247 return TRUE;
249 static gboolean
250 extra_button_pressed(GtkWidget *button, plugin_strip *strip)
252 if (strip->connector)
253 strip->connector->close();
254 strip->main_win->owner->remove_plugin(strip->plugin);
255 return TRUE;
258 void gtk_main_window::show_rack_ears(bool show)
260 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
262 if (show)
264 gtk_widget_show(i->second->leftBG);
265 gtk_widget_show(i->second->rightBG);
267 else
269 gtk_widget_hide(i->second->leftBG);
270 gtk_widget_hide(i->second->rightBG);
275 void gtk_main_window::show_vu_meters(bool show)
277 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
279 if (show)
281 if (i->second->inBox)
282 gtk_widget_show(i->second->inBox);
283 if (i->second->outBox)
284 gtk_widget_show(i->second->outBox);
286 else
288 if (i->second->inBox)
289 gtk_widget_hide(i->second->inBox);
290 if (i->second->outBox)
291 gtk_widget_hide(i->second->outBox);
296 plugin_strip *gtk_main_window::create_strip(jack_host *plugin)
298 plugin_strip *strip = new plugin_strip;
299 strip->main_win = this;
300 strip->plugin = plugin;
301 strip->gui_win = NULL;
302 strip->connector = NULL;
303 strip->id = plugins.size();
305 GtkAttachOptions ao = (GtkAttachOptions)(GTK_EXPAND | GTK_FILL);
307 strip->strip_table = gtk_table_new(6, 4, FALSE);
308 gtk_table_set_col_spacings(GTK_TABLE(strip->strip_table), 0);
309 gtk_table_set_row_spacings(GTK_TABLE(strip->strip_table), 0);
311 int row = 0;
312 // g_object_get(G_OBJECT(strips_table), "n-rows", &row, "n-columns", &cols, NULL);
313 // gtk_table_resize(GTK_TABLE(strips_table), row + 4, cols);
314 // printf("%03d %03d", row, cols);
315 // images for left side
316 GtkWidget *nwImg = gtk_image_new_from_pixbuf(images.get("side_d_nw"));
317 GtkWidget *swImg = gtk_image_new_from_pixbuf(images.get("side_d_sw"));
318 GtkWidget *wImg = gtk_image_new_from_pixbuf(images.get("side_d_w"));
319 gtk_widget_set_size_request(GTK_WIDGET(wImg), 56, 1);
321 // images for right side
322 GtkWidget *neImg = gtk_image_new_from_pixbuf(images.get("side_d_ne"));
323 GtkWidget *seImg = gtk_image_new_from_pixbuf(images.get("side_d_se"));
324 GtkWidget *eImg = gtk_image_new_from_pixbuf(images.get("side_d_e"));
325 gtk_widget_set_size_request(GTK_WIDGET(eImg), 56, 1);
327 // pack left box
328 GtkWidget *leftBG = gtk_event_box_new();
329 GtkWidget *leftBox = gtk_vbox_new(FALSE, 0);
330 gtk_container_add(GTK_CONTAINER(leftBG), leftBox);
331 gtk_widget_set_name(leftBG, "CalfMainLeft");
332 gtk_box_pack_start(GTK_BOX(leftBox), GTK_WIDGET(nwImg), FALSE, FALSE, 0);
333 gtk_box_pack_end(GTK_BOX(leftBox), GTK_WIDGET(swImg), FALSE, FALSE, 0);
334 gtk_widget_show_all(GTK_WIDGET(leftBG));
335 if (!get_config()->rack_ears)
336 gtk_widget_hide(GTK_WIDGET(leftBG));
337 gtk_table_attach(GTK_TABLE(strip->strip_table), leftBG, 0, 1, row, row + 4, (GtkAttachOptions)(0), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
339 // pack right box
340 GtkWidget *rightBG = gtk_event_box_new();
341 GtkWidget *rightBox = gtk_vbox_new(FALSE, 0);
342 gtk_container_add(GTK_CONTAINER(rightBG), rightBox);
343 gtk_widget_set_name(rightBG, "CalfMainRight");
344 gtk_box_pack_start(GTK_BOX(rightBox), GTK_WIDGET(neImg), FALSE, FALSE, 0);
345 gtk_box_pack_end(GTK_BOX(rightBox), GTK_WIDGET(seImg), FALSE, FALSE, 0);
346 gtk_widget_show_all(GTK_WIDGET(rightBG));
347 if (!get_config()->rack_ears)
348 gtk_widget_hide(GTK_WIDGET(rightBG));
349 gtk_table_attach(GTK_TABLE(strip->strip_table), rightBG, 5, 6, row, row + 4, (GtkAttachOptions)(0), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
351 strip->leftBG = leftBG;
352 strip->rightBG = rightBG;
355 // top light
356 GtkWidget *topImg = gtk_image_new_from_pixbuf(images.get("light_top"));
357 gtk_widget_set_size_request(GTK_WIDGET(topImg), 1, 1);
358 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);
359 gtk_widget_show(topImg);
360 strip->name = topImg;
361 row ++;
363 // title @ 1, 1
364 const plugin_metadata_iface *metadata = plugin->get_metadata_iface();
365 GtkWidget *title = gtk_label_new(NULL);
366 gtk_widget_set_name(GTK_WIDGET(title), "Calf-Rack-Title");
367 gtk_label_set_markup(GTK_LABEL(title), metadata->get_label());
368 gtk_label_set_justify(GTK_LABEL(title), GTK_JUSTIFY_RIGHT);
369 GtkWidget * align = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
370 gtk_container_add(GTK_CONTAINER(align), title);
371 gtk_table_attach(GTK_TABLE(strip->strip_table), align, 1, 2, row, row + 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL) , 10, 0);
372 gtk_widget_show_all(align);
374 // open button
375 GtkWidget *label = calf_toggle_button_new("Edit");
376 strip->button = label;
377 gtk_widget_set_size_request(GTK_WIDGET(label), 80, -1);
378 g_signal_connect(GTK_OBJECT(label), "toggled", G_CALLBACK(gui_button_pressed),
379 (plugin_ctl_iface *)strip);
380 gtk_widget_show(strip->button);
382 // connect button
383 GtkWidget *con = calf_toggle_button_new("Connect");
384 strip->con = con;
385 gtk_widget_set_size_request(GTK_WIDGET(con), -1, -1);
386 g_signal_connect(GTK_OBJECT(con), "toggled", G_CALLBACK(connect_button_pressed),
387 (plugin_ctl_iface *)strip);
388 gtk_widget_show(strip->con);
390 // delete buton
391 GtkWidget *extra = calf_button_new("Remove");
392 strip->extra = extra;
393 //gtk_widget_set_size_request(GTK_WIDGET(extra), 100, -1);
394 g_signal_connect(GTK_OBJECT(extra), "clicked", G_CALLBACK(extra_button_pressed),
395 (plugin_ctl_iface *)strip);
396 gtk_widget_show(strip->extra);
398 // button box @ 1, 2
399 GtkWidget *buttonBox = gtk_hbox_new(FALSE, 5);
400 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->button), FALSE, FALSE, 0);
401 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->con), FALSE, FALSE, 0);
402 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->extra), FALSE, FALSE, 0);
403 gtk_table_attach(GTK_TABLE(strip->strip_table), buttonBox, 1, 2, row + 1, row + 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 10, 10);
404 gtk_widget_show(buttonBox);
406 // midi box
407 if (metadata->get_midi()) {
408 GtkWidget *led = calf_led_new();
409 GtkWidget *midiBox = gtk_vbox_new(FALSE, 1);
410 gtk_box_pack_start(GTK_BOX(midiBox), GTK_WIDGET(gtk_label_new("Midi")), FALSE, FALSE, 0);
411 gtk_box_pack_start(GTK_BOX(midiBox), GTK_WIDGET(led), FALSE, FALSE, 0);
412 gtk_table_attach(GTK_TABLE(strip->strip_table), midiBox, 2, 3, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 5, 3);
413 gtk_widget_set_size_request(GTK_WIDGET(led), 25, 25);
414 strip->midi_in = led;
415 gtk_widget_show_all(midiBox);
416 } else {
417 GtkWidget *led = gtk_label_new("");
418 gtk_table_attach(GTK_TABLE(strip->strip_table), led, 2, 3, row, row + 1, GTK_FILL, GTK_EXPAND, 5, 3);
419 gtk_widget_set_size_request(GTK_WIDGET(led), 25, 25);
420 strip->midi_in = led;
421 gtk_widget_show(strip->midi_in);
424 strip->inBox = NULL;
425 strip->outBox = NULL;
426 strip->audio_in.clear();
427 strip->audio_out.clear();
429 if (metadata->get_input_count()) {
431 GtkWidget *inBox = gtk_vbox_new(FALSE, 1);
433 gtk_box_pack_start(GTK_BOX(inBox), gtk_label_new("Audio In"),FALSE, FALSE, 0);
435 for (int i = 0; i < metadata->get_input_count(); i++)
437 label = calf_vumeter_new();
438 calf_vumeter_set_falloff(CALF_VUMETER(label), 2.5);
439 calf_vumeter_set_hold(CALF_VUMETER(label), 1.5);
440 calf_vumeter_set_width(CALF_VUMETER(label), 100);
441 calf_vumeter_set_height(CALF_VUMETER(label), 12);
442 calf_vumeter_set_position(CALF_VUMETER(label), 2);
443 gtk_box_pack_start(GTK_BOX(inBox), label, TRUE, TRUE, 0);
444 strip->audio_in.push_back(label);
447 strip->inBox = gtk_alignment_new(0.f, 0.f, 1.f, 0.f);
448 gtk_container_add(GTK_CONTAINER(strip->inBox), inBox);
450 gtk_table_attach(GTK_TABLE(strip->strip_table), strip->inBox, 3, 4, row, row + 1, ao, ao, 5, 3);
452 if (get_config()->vu_meters)
453 gtk_widget_show_all(strip->inBox);
455 gtk_widget_set_size_request(GTK_WIDGET(strip->inBox), 120, -1);
458 if (metadata->get_output_count()) {
460 GtkWidget *outBox = gtk_vbox_new(FALSE, 1);
462 gtk_box_pack_start(GTK_BOX(outBox), gtk_label_new("Audio Out"),TRUE, TRUE, 0);
464 for (int i = 0; i < metadata->get_output_count(); i++)
466 label = calf_vumeter_new();
467 calf_vumeter_set_falloff(CALF_VUMETER(label), 2.5);
468 calf_vumeter_set_hold(CALF_VUMETER(label), 1.5);
469 calf_vumeter_set_width(CALF_VUMETER(label), 100);
470 calf_vumeter_set_height(CALF_VUMETER(label), 12);
471 calf_vumeter_set_position(CALF_VUMETER(label), 2);
472 gtk_box_pack_start(GTK_BOX(outBox), label,FALSE, FALSE, 0);
473 strip->audio_out.push_back(label);
476 strip->outBox = gtk_alignment_new(0.f, 0.f, 1.f, 0.f);
477 gtk_container_add(GTK_CONTAINER(strip->outBox), outBox);
479 gtk_table_attach(GTK_TABLE(strip->strip_table), strip->outBox, 4, 5, row, row + 1, ao, ao, 5, 3);
481 if (get_config()->vu_meters)
482 gtk_widget_show_all(strip->outBox);
484 gtk_widget_set_size_request(GTK_WIDGET(strip->outBox), 120, -1);
487 // other stuff bottom right
488 GtkWidget *paramBox = gtk_hbox_new(FALSE, 0);
490 GtkWidget *logoImg = gtk_image_new_from_pixbuf(images.get("logo_button"));
491 gtk_misc_set_alignment(GTK_MISC(logoImg), 1, 1);
492 gtk_box_pack_end(GTK_BOX(paramBox), GTK_WIDGET(logoImg), TRUE, TRUE, 0);
494 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);
495 gtk_widget_show_all(GTK_WIDGET(paramBox));
497 row += 2;
499 // bottom light
500 GtkWidget *botImg = gtk_image_new_from_file(PKGLIBDIR "/light_bottom.png");
501 gtk_widget_set_size_request(GTK_WIDGET(botImg), 1, 1);
502 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);
503 gtk_widget_show(botImg);
505 gtk_widget_show(GTK_WIDGET(strip->strip_table));
507 return strip;
510 void gtk_main_window::sort_strips()
512 if(plugins.size() <= 0) return;
513 int rack_float = get_config()->rack_float; // 0=horiz, 1=vert
514 int float_size = get_config()->float_size; // amout of rows/cols before line break
515 int posx, posy;
516 gtk_table_resize(GTK_TABLE(strips_table), (int)(plugins.size() / float_size + 1), float_size);
517 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
519 switch (rack_float) {
520 case 0:
521 default:
522 posx = i->second->id % float_size;
523 posy = (int)(i->second->id / float_size);
524 break;
525 case 1:
526 posy = i->second->id % float_size;
527 posx = (int)(i->second->id / float_size);
528 break;
530 bool rem = false;
531 if(i->second->strip_table->parent != NULL) {
532 rem = true;
533 g_object_ref(i->second->strip_table);
534 gtk_container_remove(GTK_CONTAINER(strips_table), GTK_WIDGET(i->second->strip_table));
536 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);
537 if(rem) g_object_unref(i->second->strip_table);
541 void gtk_main_window::update_strip(plugin_ctl_iface *plugin)
543 // plugin_strip *strip = plugins[plugin];
544 // assert(strip);
548 void gtk_main_window::open_gui(plugin_ctl_iface *plugin)
550 plugin_gui_window *gui_win = new plugin_gui_window(this, this);
551 std::string title = "Calf - ";
552 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());
553 gtk_widget_show(GTK_WIDGET(gui_win->toplevel));
554 plugins[plugin]->gui_win = gui_win;
557 static const char *plugin_pre_xml =
558 "<ui>\n"
559 " <menubar>\n"
560 " <menu action=\"AddPluginMenuAction\">\n"
561 " <placeholder name=\"plugin\">\n";
563 static const char *plugin_post_xml =
564 " </placeholder>\n"
565 " </menu>\n"
566 " </menubar>\n"
567 "</ui>\n"
569 #define countof(X) ( (size_t) ( sizeof(X)/sizeof*(X) ) )
570 void gtk_main_window::register_icons()
572 const char *names[]={"Allpass", "Amplifier", "Analyser",
573 "Bandpass", "Chorus", "Comb", "Compressor",
574 "Constant", "Converter", "Delay", "Distortion",
575 "Dynamics", "Envelope", "EQ", "Expander",
576 "Filter", "Flanger", "Function", "Gate",
577 "Generator", "Highpass", "Instrument",
578 "Limiter", "Mixer", "Modulator", "MultiEQ",
579 "Oscillator", "ParaEQ", "Phaser", "Pitch",
580 "Reverb", "Simulator", "Spatial", "Spectral",
581 "Utility", "Waveshaper"};
582 factory = gtk_icon_factory_new ();
583 for (size_t i = 0; i < countof(names); i++) {
584 char name[1024];
585 strcpy(name, "LV2-");
586 strcat(name, names[i]);
587 if (!gtk_icon_factory_lookup(factory, name)) {
588 std::string iname = std::string(PKGLIBDIR) + "icons/LV2/" + names[i] + ".svg";
589 GdkPixbuf *buf = gdk_pixbuf_new_from_file_at_size(iname.c_str(), 64, 64, NULL);
590 GtkIconSet *icon = gtk_icon_set_new_from_pixbuf(buf);
591 gtk_icon_factory_add (factory, name, icon);
592 gtk_icon_set_unref(icon);
593 g_object_unref(buf);
596 gtk_icon_factory_add_default(factory);
599 void gtk_main_window::add_plugin_action(GtkWidget *src, gpointer data)
601 add_plugin_params *app = (add_plugin_params *)data;
602 app->main_win->new_plugin(app->name.c_str());
605 static void action_destroy_notify(gpointer data)
607 delete (gtk_main_window::add_plugin_params *)data;
610 std::string gtk_main_window::make_plugin_list(GtkActionGroup *actions)
612 string s = plugin_pre_xml;
613 const plugin_registry::plugin_vector &plugins = plugin_registry::instance().get_all();
614 std::string type = "";
615 std::string tmp = "";
616 std::string last = "";
617 unsigned int count = 0;
618 unsigned int size = plugins.size();
620 const plugin_metadata_iface *p = plugins[0];
622 for(unsigned int i = 0; i <= size; i++)
624 if (i < size) {
625 p = plugins[i];
626 type = (p->get_plugin_info()).plugin_type;
627 type = type.substr(0, type.length() - 6);
629 if (type != last or i >= size or !i) {
631 if (i) {
632 if (count > 1) {
633 s += "<menu action='" + last + "'>" + tmp + "</menu>";
634 GtkAction *a = gtk_action_new(last.c_str(), last.c_str(), NULL, ("LV2-" + last).c_str());
635 gtk_action_group_add_action(actions, a);
636 } else {
637 s += tmp;
640 tmp = "";
641 last = type;
642 count = 0;
644 if (i < size) {
645 std::string action = "Add" + string(p->get_id()) + "Action";
646 std::string stock = "LV2-" + type;
647 // TODO:
648 // add lv2 stock icons to plug-ins and not just to menus
649 // GTK_STOCK_OPEN -> ("LV2_" + type).c_str()
650 GtkActionEntry ae = { action.c_str(), stock.c_str(), p->get_label(), NULL, NULL, (GCallback)add_plugin_action };
651 gtk_action_group_add_actions_full(actions, &ae, 1, (gpointer)new add_plugin_params(this, p->get_id()), action_destroy_notify);
652 tmp += string("<menuitem always-show-image=\"true\" action=\"") + action + "\" />";
653 count += 1;
656 return s + plugin_post_xml;
659 static void window_destroy_cb(GtkWindow *window, gpointer data)
661 ((gtk_main_window *)data)->owner->on_main_window_destroy();
664 void gtk_main_window::create()
666 register_icons();
667 toplevel = GTK_WINDOW(gtk_window_new (GTK_WINDOW_TOPLEVEL));
668 std::string title = "Calf JACK Host";
669 gtk_window_set_title(toplevel, title.c_str()); //(owner->get_client_name() + " - Calf JACK Host").c_str());
670 gtk_window_set_icon_name(toplevel, "calf");
671 gtk_window_set_role(toplevel, "calf_rack");
674 load_style((PKGLIBDIR "styles/" + get_config()->style).c_str());
676 is_closed = false;
677 gtk_window_set_resizable(toplevel, false);
679 all_vbox = gtk_vbox_new(0, FALSE);
681 ui_mgr = gtk_ui_manager_new();
682 std_actions = gtk_action_group_new("default");
683 gtk_action_group_add_actions(std_actions, actions, sizeof(actions)/sizeof(actions[0]), this);
684 GError *error = NULL;
685 gtk_ui_manager_insert_action_group(ui_mgr, std_actions, 0);
686 gtk_ui_manager_add_ui_from_string(ui_mgr, ui_xml, -1, &error);
687 gtk_box_pack_start(GTK_BOX(all_vbox), gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar"), false, false, 0);
689 gtk_widget_set_size_request(GTK_WIDGET(gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar")), 640, -1);
691 gtk_widget_set_name(GTK_WIDGET(gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar")), "Calf-Menu");
693 plugin_actions = gtk_action_group_new("plugins");
694 string plugin_xml = make_plugin_list(plugin_actions);
695 gtk_ui_manager_insert_action_group(ui_mgr, plugin_actions, 0);
696 gtk_ui_manager_add_ui_from_string(ui_mgr, plugin_xml.c_str(), -1, &error);
698 strips_table = gtk_table_new(0, 1, FALSE);
699 gtk_table_set_col_spacings(GTK_TABLE(strips_table), 0);
700 gtk_table_set_row_spacings(GTK_TABLE(strips_table), 0);
702 for(GList *p = GTK_TABLE(strips_table)->children; p != NULL; p = p->next)
704 GtkTableChild *c = (GtkTableChild *)p->data;
705 if (c->top_attach == 0) {
706 gtk_misc_set_alignment(GTK_MISC(c->widget), 0.5, 0);
709 for (std::vector<jack_host *>::iterator i = plugin_queue.begin(); i != plugin_queue.end(); ++i)
711 plugin_strip *st = create_strip(*i);
712 plugins[*i] = st;
713 update_strip(*i);
715 sort_strips();
717 GtkWidget *evbox = gtk_event_box_new();
718 gtk_widget_set_name(evbox, "Calf-Rack");
719 gtk_container_add(GTK_CONTAINER(evbox), strips_table);
720 gtk_container_add(GTK_CONTAINER(all_vbox), evbox);
721 gtk_container_add(GTK_CONTAINER(toplevel), all_vbox);
723 gtk_widget_set_name(GTK_WIDGET(strips_table), "Calf-Container");
725 gtk_window_add_accel_group(toplevel, gtk_ui_manager_get_accel_group(ui_mgr));
726 gtk_widget_show_all(GTK_WIDGET(toplevel));
727 source_id = g_timeout_add_full(G_PRIORITY_DEFAULT, 1000/30, on_idle, this, NULL); // 30 fps should be enough for everybody
729 notifier = get_config_db()->add_listener(this);
730 on_config_change();
731 g_signal_connect(GTK_OBJECT(toplevel), "destroy", G_CALLBACK(window_destroy_cb), this);
734 void gtk_main_window::on_config_change()
736 get_config()->load(get_config_db());
737 show_rack_ears(get_config()->rack_ears);
738 show_vu_meters(get_config()->vu_meters);
739 sort_strips();
742 void gtk_main_window::refresh_plugin(plugin_ctl_iface *plugin)
744 if (plugins[plugin]->gui_win)
745 plugins[plugin]->gui_win->gui->refresh();
748 void gtk_main_window::on_closed()
750 if (notifier)
752 delete notifier;
753 notifier = NULL;
755 if (source_id)
756 g_source_remove(source_id);
757 is_closed = true;
758 toplevel = NULL;
760 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
762 if (i->second && i->second->gui_win) {
763 i->second->gui_win->close();
766 plugins.clear();
769 static inline float LVL(float value)
771 return value; //sqrt(value) * 0.75;
774 gboolean gtk_main_window::on_idle(void *data)
776 gtk_main_window *self = (gtk_main_window *)data;
778 self->owner->on_idle();
780 if (!self->refresh_controller.check_redraw(GTK_WIDGET(self->toplevel)))
781 return TRUE;
783 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = self->plugins.begin(); i != self->plugins.end(); ++i)
785 if (i->second)
787 plugin_ctl_iface *plugin = i->first;
788 plugin_strip *strip = i->second;
789 int idx = 0;
790 if (strip->inBox && gtk_widget_is_drawable (strip->inBox)) {
791 for (int i = 0; i < (int)strip->audio_in.size(); i++) {
792 calf_vumeter_set_value(CALF_VUMETER(strip->audio_in[i]), LVL(plugin->get_level(idx++)));
795 else
796 idx += strip->audio_in.size();
797 if (strip->outBox && gtk_widget_is_drawable (strip->outBox)) {
798 for (int i = 0; i < (int)strip->audio_out.size(); i++) {
799 calf_vumeter_set_value(CALF_VUMETER(strip->audio_out[i]), LVL(plugin->get_level(idx++)));
802 else
803 idx += strip->audio_out.size();
804 if (plugin->get_metadata_iface()->get_midi()) {
805 calf_led_set_value (CALF_LED (strip->midi_in), plugin->get_level(idx++));
809 return TRUE;
812 void gtk_main_window::open_file()
814 GtkWidget *dialog;
815 dialog = gtk_file_chooser_dialog_new ("Open File",
816 toplevel,
817 GTK_FILE_CHOOSER_ACTION_OPEN,
818 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
819 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
820 NULL);
821 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
823 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
824 char *error = owner->open_file(filename);
825 if (error)
826 display_error(error, filename);
827 else
828 owner->set_current_filename(filename);
829 g_free (filename);
830 free (error);
832 gtk_widget_destroy (dialog);
835 bool gtk_main_window::save_file()
837 if (owner->get_current_filename().empty())
838 return save_file_as();
840 const char *error = owner->save_file(owner->get_current_filename().c_str());
841 if (error)
843 display_error(error, owner->get_current_filename().c_str());
844 return false;
846 return true;
849 bool gtk_main_window::save_file_as()
851 GtkWidget *dialog;
852 bool success = false;
853 dialog = gtk_file_chooser_dialog_new ("Save File",
854 toplevel,
855 GTK_FILE_CHOOSER_ACTION_SAVE,
856 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
857 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
858 NULL);
859 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
861 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
862 char *error = owner->save_file(filename);
863 if (error)
864 display_error(error, filename);
865 else
867 owner->set_current_filename(filename);
868 success = true;
870 g_free (filename);
871 free(error);
873 gtk_widget_destroy (dialog);
874 return success;
877 void gtk_main_window::display_error(const char *error, const char *filename)
879 GtkWidget *dialog;
880 dialog = gtk_message_dialog_new_with_markup (toplevel, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, error, filename, NULL);
881 gtk_dialog_run (GTK_DIALOG (dialog));
882 gtk_widget_destroy (dialog);
885 GtkWidget *gtk_main_window::create_progress_window()
887 GtkWidget *tlw = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
888 gtk_window_set_type_hint (GTK_WINDOW (tlw), GDK_WINDOW_TYPE_HINT_DIALOG);
889 GtkWidget *pbar = gtk_progress_bar_new();
890 gtk_container_add (GTK_CONTAINER(tlw), pbar);
891 gtk_widget_show_all (pbar);
892 return tlw;
895 void gtk_main_window::report_progress(float percentage, const std::string &message)
897 if (percentage < 100)
899 if (!progress_window) {
900 progress_window = create_progress_window();
901 gtk_window_set_modal (GTK_WINDOW (progress_window), TRUE);
902 if (toplevel)
903 gtk_window_set_transient_for (GTK_WINDOW (progress_window), toplevel);
904 gtk_widget_show(progress_window);
906 GtkWidget *pbar = gtk_bin_get_child (GTK_BIN (progress_window));
907 if (!message.empty())
908 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pbar), message.c_str());
909 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (pbar), percentage / 100.0);
911 else
913 if (progress_window) {
914 gtk_window_set_modal (GTK_WINDOW (progress_window), FALSE);
915 gtk_widget_destroy (progress_window);
916 progress_window = NULL;
920 while (gtk_events_pending ())
921 gtk_main_iteration ();
924 void gtk_main_window::add_condition(const std::string &name)
926 conditions.insert(name);
929 void gtk_main_window::show_error(const std::string &text)
931 GtkWidget *widget = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", text.c_str());
932 gtk_dialog_run (GTK_DIALOG (widget));
933 gtk_widget_destroy (widget);
936 GtkListStore *gtk_main_window::get_styles()
938 std::vector <calf_utils::direntry> list = calf_utils::list_directory(PKGLIBDIR"styles");
939 GtkListStore *store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
940 for (std::vector<calf_utils::direntry>::iterator i = list.begin(); i != list.end(); i++) {
941 string title = i->name;
942 std::string rcf = i->full_path + "/gtk.rc";
943 ifstream infile(rcf.c_str());
944 if (infile.good()) {
945 string line;
946 getline(infile, line);
947 title = line.substr(1);
949 gtk_list_store_insert_with_values(store, NULL, -1,
950 0, title.c_str(),
951 1, i->name.c_str(),
952 -1);
954 return store;
956 void gtk_main_window::load_style(std::string path) {
957 gtk_rc_parse((path + "/gtk.rc").c_str());
958 gtk_rc_reset_styles(gtk_settings_get_for_screen(gdk_screen_get_default()));
959 images.set_path(path);