* New: selectable styles
[calf.git] / src / gtk_main_win.cpp
blob67770905bdfcd79223f485e3f210159f252a0207
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->leftBox);
265 gtk_widget_show(i->second->rightBox);
267 else
269 gtk_widget_hide(i->second->leftBox);
270 gtk_widget_hide(i->second->rightBox);
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 *leftBox = gtk_vbox_new(FALSE, 0);
329 gtk_box_pack_start(GTK_BOX(leftBox), GTK_WIDGET(nwImg), FALSE, FALSE, 0);
330 gtk_box_pack_start(GTK_BOX(leftBox), GTK_WIDGET(wImg), TRUE, TRUE, 0);
331 gtk_box_pack_end(GTK_BOX(leftBox), GTK_WIDGET(swImg), FALSE, FALSE, 0);
332 gtk_widget_show_all(GTK_WIDGET(leftBox));
333 if (!get_config()->rack_ears)
334 gtk_widget_hide(GTK_WIDGET(leftBox));
335 gtk_table_attach(GTK_TABLE(strip->strip_table), leftBox, 0, 1, row, row + 4, (GtkAttachOptions)(0), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
337 // pack right box
338 GtkWidget *rightBox = gtk_vbox_new(FALSE, 0);
339 gtk_box_pack_start(GTK_BOX(rightBox), GTK_WIDGET(neImg), FALSE, FALSE, 0);
340 gtk_box_pack_start(GTK_BOX(rightBox), GTK_WIDGET(eImg), TRUE, TRUE, 0);
341 gtk_box_pack_end(GTK_BOX(rightBox), GTK_WIDGET(seImg), FALSE, FALSE, 0);
342 gtk_widget_show_all(GTK_WIDGET(rightBox));
343 if (!get_config()->rack_ears)
344 gtk_widget_hide(GTK_WIDGET(rightBox));
345 gtk_table_attach(GTK_TABLE(strip->strip_table), rightBox, 5, 6, row, row + 4, (GtkAttachOptions)(0), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
347 strip->leftBox = leftBox;
348 strip->rightBox = rightBox;
351 // top light
352 GtkWidget *topImg = gtk_image_new_from_pixbuf(images.get("light_top"));
353 gtk_widget_set_size_request(GTK_WIDGET(topImg), 1, 1);
354 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);
355 gtk_widget_show(topImg);
356 strip->name = topImg;
357 row ++;
359 // title @ 1, 1
360 const plugin_metadata_iface *metadata = plugin->get_metadata_iface();
361 GtkWidget *title = gtk_label_new(NULL);
362 gtk_widget_set_name(GTK_WIDGET(title), "Calf-Rack-Title");
363 gtk_label_set_markup(GTK_LABEL(title), metadata->get_label());
364 gtk_label_set_justify(GTK_LABEL(title), GTK_JUSTIFY_RIGHT);
365 GtkWidget * align = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
366 gtk_container_add(GTK_CONTAINER(align), title);
367 gtk_table_attach(GTK_TABLE(strip->strip_table), align, 1, 2, row, row + 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL) , 10, 0);
368 gtk_widget_show_all(align);
370 // open button
371 GtkWidget *label = calf_toggle_button_new("Edit");
372 strip->button = label;
373 gtk_widget_set_size_request(GTK_WIDGET(label), 80, -1);
374 g_signal_connect(GTK_OBJECT(label), "toggled", G_CALLBACK(gui_button_pressed),
375 (plugin_ctl_iface *)strip);
376 gtk_widget_show(strip->button);
378 // connect button
379 GtkWidget *con = calf_toggle_button_new("Connect");
380 strip->con = con;
381 gtk_widget_set_size_request(GTK_WIDGET(con), -1, -1);
382 g_signal_connect(GTK_OBJECT(con), "toggled", G_CALLBACK(connect_button_pressed),
383 (plugin_ctl_iface *)strip);
384 gtk_widget_show(strip->con);
386 // delete buton
387 GtkWidget *extra = calf_button_new("Remove");
388 strip->extra = extra;
389 //gtk_widget_set_size_request(GTK_WIDGET(extra), 100, -1);
390 g_signal_connect(GTK_OBJECT(extra), "clicked", G_CALLBACK(extra_button_pressed),
391 (plugin_ctl_iface *)strip);
392 gtk_widget_show(strip->extra);
394 // button box @ 1, 2
395 GtkWidget *buttonBox = gtk_hbox_new(FALSE, 5);
396 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->button), FALSE, FALSE, 0);
397 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->con), FALSE, FALSE, 0);
398 gtk_box_pack_start(GTK_BOX(buttonBox), GTK_WIDGET(strip->extra), FALSE, FALSE, 0);
399 gtk_table_attach(GTK_TABLE(strip->strip_table), buttonBox, 1, 2, row + 1, row + 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 10, 10);
400 gtk_widget_show(buttonBox);
402 // midi box
403 if (metadata->get_midi()) {
404 label = calf_led_new();
405 GtkWidget *midiBox = gtk_vbox_new(FALSE, 1);
406 gtk_box_pack_start(GTK_BOX(midiBox), GTK_WIDGET(gtk_label_new("Midi")), FALSE, FALSE, 0);
407 gtk_box_pack_start(GTK_BOX(midiBox), GTK_WIDGET(label), FALSE, FALSE, 0);
408 gtk_table_attach(GTK_TABLE(strip->strip_table), midiBox, 2, 3, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 5, 3);
409 gtk_widget_set_size_request(GTK_WIDGET(label), 25, 25);
410 strip->midi_in = label;
411 gtk_widget_show_all(midiBox);
412 } else {
413 label = gtk_label_new("");
414 gtk_table_attach(GTK_TABLE(strip->strip_table), label, 2, 3, row, row + 1, GTK_FILL, GTK_EXPAND, 5, 3);
415 gtk_widget_set_size_request(GTK_WIDGET(label), 25, 25);
416 strip->midi_in = label;
417 gtk_widget_show(strip->midi_in);
419 strip->midi_in = label;
421 strip->inBox = NULL;
422 strip->outBox = NULL;
423 strip->audio_in.clear();
424 strip->audio_out.clear();
426 if (metadata->get_input_count()) {
428 GtkWidget *inBox = gtk_vbox_new(FALSE, 1);
430 gtk_box_pack_start(GTK_BOX(inBox), gtk_label_new("Audio In"),FALSE, FALSE, 0);
432 for (int i = 0; i < metadata->get_input_count(); i++)
434 label = calf_vumeter_new();
435 calf_vumeter_set_falloff(CALF_VUMETER(label), 2.5);
436 calf_vumeter_set_hold(CALF_VUMETER(label), 1.5);
437 calf_vumeter_set_width(CALF_VUMETER(label), 100);
438 calf_vumeter_set_height(CALF_VUMETER(label), 12);
439 calf_vumeter_set_position(CALF_VUMETER(label), 2);
440 gtk_box_pack_start(GTK_BOX(inBox), label, TRUE, TRUE, 0);
441 strip->audio_in.push_back(label);
444 strip->inBox = gtk_alignment_new(0.f, 0.f, 1.f, 0.f);
445 gtk_container_add(GTK_CONTAINER(strip->inBox), inBox);
447 gtk_table_attach(GTK_TABLE(strip->strip_table), strip->inBox, 3, 4, row, row + 1, ao, ao, 5, 3);
449 if (get_config()->vu_meters)
450 gtk_widget_show_all(strip->inBox);
452 gtk_widget_set_size_request(GTK_WIDGET(strip->inBox), 120, -1);
455 if (metadata->get_output_count()) {
457 GtkWidget *outBox = gtk_vbox_new(FALSE, 1);
459 gtk_box_pack_start(GTK_BOX(outBox), gtk_label_new("Audio Out"),TRUE, TRUE, 0);
461 for (int i = 0; i < metadata->get_output_count(); i++)
463 label = calf_vumeter_new();
464 calf_vumeter_set_falloff(CALF_VUMETER(label), 2.5);
465 calf_vumeter_set_hold(CALF_VUMETER(label), 1.5);
466 calf_vumeter_set_width(CALF_VUMETER(label), 100);
467 calf_vumeter_set_height(CALF_VUMETER(label), 12);
468 calf_vumeter_set_position(CALF_VUMETER(label), 2);
469 gtk_box_pack_start(GTK_BOX(outBox), label,FALSE, FALSE, 0);
470 strip->audio_out.push_back(label);
473 strip->outBox = gtk_alignment_new(0.f, 0.f, 1.f, 0.f);
474 gtk_container_add(GTK_CONTAINER(strip->outBox), outBox);
476 gtk_table_attach(GTK_TABLE(strip->strip_table), strip->outBox, 4, 5, row, row + 1, ao, ao, 5, 3);
478 if (get_config()->vu_meters)
479 gtk_widget_show_all(strip->outBox);
481 gtk_widget_set_size_request(GTK_WIDGET(strip->outBox), 120, -1);
484 // other stuff bottom right
485 GtkWidget *paramBox = gtk_hbox_new(FALSE, 0);
487 GtkWidget *logoImg = gtk_image_new_from_pixbuf(images.get("logo_button"));
488 gtk_box_pack_end(GTK_BOX(paramBox), GTK_WIDGET(logoImg), TRUE, TRUE, 0);
490 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);
491 gtk_widget_show_all(GTK_WIDGET(paramBox));
493 row += 2;
495 // bottom light
496 GtkWidget *botImg = gtk_image_new_from_file(PKGLIBDIR "/light_bottom.png");
497 gtk_widget_set_size_request(GTK_WIDGET(botImg), 1, 1);
498 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);
499 gtk_widget_show(botImg);
501 gtk_widget_show(GTK_WIDGET(strip->strip_table));
503 return strip;
506 void gtk_main_window::sort_strips()
508 if(plugins.size() <= 0) return;
509 int rack_float = get_config()->rack_float; // 0=horiz, 1=vert
510 int float_size = get_config()->float_size; // amout of rows/cols before line break
511 int posx, posy;
512 gtk_table_resize(GTK_TABLE(strips_table), (int)(plugins.size() / float_size + 1), float_size);
513 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
515 switch (rack_float) {
516 case 0:
517 default:
518 posx = i->second->id % float_size;
519 posy = (int)(i->second->id / float_size);
520 break;
521 case 1:
522 posy = i->second->id % float_size;
523 posx = (int)(i->second->id / float_size);
524 break;
526 bool rem = false;
527 if(i->second->strip_table->parent != NULL) {
528 rem = true;
529 g_object_ref(i->second->strip_table);
530 gtk_container_remove(GTK_CONTAINER(strips_table), GTK_WIDGET(i->second->strip_table));
532 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);
533 if(rem) g_object_unref(i->second->strip_table);
537 void gtk_main_window::update_strip(plugin_ctl_iface *plugin)
539 // plugin_strip *strip = plugins[plugin];
540 // assert(strip);
544 void gtk_main_window::open_gui(plugin_ctl_iface *plugin)
546 plugin_gui_window *gui_win = new plugin_gui_window(this, this);
547 std::string title = "Calf - ";
548 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());
549 gtk_widget_show(GTK_WIDGET(gui_win->toplevel));
550 plugins[plugin]->gui_win = gui_win;
553 static const char *plugin_pre_xml =
554 "<ui>\n"
555 " <menubar>\n"
556 " <menu action=\"AddPluginMenuAction\">\n"
557 " <placeholder name=\"plugin\">\n";
559 static const char *plugin_post_xml =
560 " </placeholder>\n"
561 " </menu>\n"
562 " </menubar>\n"
563 "</ui>\n"
565 #define countof(X) ( (size_t) ( sizeof(X)/sizeof*(X) ) )
566 void gtk_main_window::register_icons()
568 const char *names[]={"Allpass", "Amplifier", "Analyser",
569 "Bandpass", "Chorus", "Comb", "Compressor",
570 "Constant", "Converter", "Delay", "Distortion",
571 "Dynamics", "Envelope", "EQ", "Expander",
572 "Filter", "Flanger", "Function", "Gate",
573 "Generator", "Highpass", "Instrument",
574 "Limiter", "Mixer", "Modulator", "MultiEQ",
575 "Oscillator", "ParaEQ", "Phaser", "Pitch",
576 "Reverb", "Simulator", "Spatial", "Spectral",
577 "Utility", "Waveshaper"};
578 factory = gtk_icon_factory_new ();
579 for (size_t i = 0; i < countof(names); i++) {
580 char name[1024];
581 strcpy(name, "LV2-");
582 strcat(name, names[i]);
583 if (!gtk_icon_factory_lookup(factory, name)) {
584 std::string iname = std::string(PKGLIBDIR) + "icons/LV2/" + names[i] + ".svg";
585 GdkPixbuf *buf = gdk_pixbuf_new_from_file_at_size(iname.c_str(), 64, 64, NULL);
586 GtkIconSet *icon = gtk_icon_set_new_from_pixbuf(buf);
587 gtk_icon_factory_add (factory, name, icon);
588 gtk_icon_set_unref(icon);
589 g_object_unref(buf);
592 gtk_icon_factory_add_default(factory);
595 void gtk_main_window::add_plugin_action(GtkWidget *src, gpointer data)
597 add_plugin_params *app = (add_plugin_params *)data;
598 app->main_win->new_plugin(app->name.c_str());
601 static void action_destroy_notify(gpointer data)
603 delete (gtk_main_window::add_plugin_params *)data;
606 std::string gtk_main_window::make_plugin_list(GtkActionGroup *actions)
608 string s = plugin_pre_xml;
609 const plugin_registry::plugin_vector &plugins = plugin_registry::instance().get_all();
610 std::string type = "";
611 std::string tmp = "";
612 std::string last = "";
613 unsigned int count = 0;
614 unsigned int size = plugins.size();
616 const plugin_metadata_iface *p = plugins[0];
618 for(unsigned int i = 0; i <= size; i++)
620 if (i < size) {
621 p = plugins[i];
622 type = (p->get_plugin_info()).plugin_type;
623 type = type.substr(0, type.length() - 6);
625 if (type != last or i >= size or !i) {
627 if (i) {
628 if (count > 1) {
629 s += "<menu action='" + last + "'>" + tmp + "</menu>";
630 GtkAction *a = gtk_action_new(last.c_str(), last.c_str(), NULL, ("LV2-" + last).c_str());
631 gtk_action_group_add_action(actions, a);
632 } else {
633 s += tmp;
636 tmp = "";
637 last = type;
638 count = 0;
640 if (i < size) {
641 std::string action = "Add" + string(p->get_id()) + "Action";
642 std::string stock = "LV2-" + type;
643 // TODO:
644 // add lv2 stock icons to plug-ins and not just to menus
645 // GTK_STOCK_OPEN -> ("LV2_" + type).c_str()
646 GtkActionEntry ae = { action.c_str(), stock.c_str(), p->get_label(), NULL, NULL, (GCallback)add_plugin_action };
647 gtk_action_group_add_actions_full(actions, &ae, 1, (gpointer)new add_plugin_params(this, p->get_id()), action_destroy_notify);
648 tmp += string("<menuitem always-show-image=\"true\" action=\"") + action + "\" />";
649 count += 1;
652 return s + plugin_post_xml;
655 static void window_destroy_cb(GtkWindow *window, gpointer data)
657 ((gtk_main_window *)data)->owner->on_main_window_destroy();
660 void gtk_main_window::create()
662 register_icons();
663 toplevel = GTK_WINDOW(gtk_window_new (GTK_WINDOW_TOPLEVEL));
664 std::string title = "Calf JACK Host";
665 gtk_window_set_title(toplevel, title.c_str()); //(owner->get_client_name() + " - Calf JACK Host").c_str());
666 gtk_widget_set_name(GTK_WIDGET(toplevel), "Calf-Rack");
667 gtk_window_set_icon_name(toplevel, "calf");
668 gtk_window_set_role(toplevel, "calf_rack");
671 load_style((PKGLIBDIR "styles/" + get_config()->style).c_str());
673 is_closed = false;
674 gtk_window_set_resizable(toplevel, false);
676 all_vbox = gtk_vbox_new(0, FALSE);
678 ui_mgr = gtk_ui_manager_new();
679 std_actions = gtk_action_group_new("default");
680 gtk_action_group_add_actions(std_actions, actions, sizeof(actions)/sizeof(actions[0]), this);
681 GError *error = NULL;
682 gtk_ui_manager_insert_action_group(ui_mgr, std_actions, 0);
683 gtk_ui_manager_add_ui_from_string(ui_mgr, ui_xml, -1, &error);
684 gtk_box_pack_start(GTK_BOX(all_vbox), gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar"), false, false, 0);
686 gtk_widget_set_size_request(GTK_WIDGET(gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar")), 640, -1);
688 gtk_widget_set_name(GTK_WIDGET(gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar")), "Calf-Menu");
690 plugin_actions = gtk_action_group_new("plugins");
691 string plugin_xml = make_plugin_list(plugin_actions);
692 gtk_ui_manager_insert_action_group(ui_mgr, plugin_actions, 0);
693 gtk_ui_manager_add_ui_from_string(ui_mgr, plugin_xml.c_str(), -1, &error);
695 strips_table = gtk_table_new(0, 1, FALSE);
696 gtk_table_set_col_spacings(GTK_TABLE(strips_table), 0);
697 gtk_table_set_row_spacings(GTK_TABLE(strips_table), 0);
699 for(GList *p = GTK_TABLE(strips_table)->children; p != NULL; p = p->next)
701 GtkTableChild *c = (GtkTableChild *)p->data;
702 if (c->top_attach == 0) {
703 gtk_misc_set_alignment(GTK_MISC(c->widget), 0.5, 0);
706 for (std::vector<jack_host *>::iterator i = plugin_queue.begin(); i != plugin_queue.end(); ++i)
708 plugin_strip *st = create_strip(*i);
709 plugins[*i] = st;
710 update_strip(*i);
712 sort_strips();
714 gtk_container_add(GTK_CONTAINER(all_vbox), strips_table);
715 gtk_container_add(GTK_CONTAINER(toplevel), all_vbox);
717 gtk_widget_set_name(GTK_WIDGET(strips_table), "Calf-Container");
719 gtk_window_add_accel_group(toplevel, gtk_ui_manager_get_accel_group(ui_mgr));
720 gtk_widget_show_all(GTK_WIDGET(toplevel));
721 source_id = g_timeout_add_full(G_PRIORITY_DEFAULT, 1000/30, on_idle, this, NULL); // 30 fps should be enough for everybody
723 notifier = get_config_db()->add_listener(this);
724 on_config_change();
725 g_signal_connect(GTK_OBJECT(toplevel), "destroy", G_CALLBACK(window_destroy_cb), this);
728 void gtk_main_window::on_config_change()
730 get_config()->load(get_config_db());
731 show_rack_ears(get_config()->rack_ears);
732 show_vu_meters(get_config()->vu_meters);
733 sort_strips();
736 void gtk_main_window::refresh_plugin(plugin_ctl_iface *plugin)
738 if (plugins[plugin]->gui_win)
739 plugins[plugin]->gui_win->gui->refresh();
742 void gtk_main_window::on_closed()
744 if (notifier)
746 delete notifier;
747 notifier = NULL;
749 if (source_id)
750 g_source_remove(source_id);
751 is_closed = true;
752 toplevel = NULL;
754 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); ++i)
756 if (i->second && i->second->gui_win) {
757 i->second->gui_win->close();
760 plugins.clear();
763 static inline float LVL(float value)
765 return value; //sqrt(value) * 0.75;
768 gboolean gtk_main_window::on_idle(void *data)
770 gtk_main_window *self = (gtk_main_window *)data;
772 self->owner->on_idle();
774 if (!self->refresh_controller.check_redraw(GTK_WIDGET(self->toplevel)))
775 return TRUE;
777 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = self->plugins.begin(); i != self->plugins.end(); ++i)
779 if (i->second)
781 plugin_ctl_iface *plugin = i->first;
782 plugin_strip *strip = i->second;
783 int idx = 0;
784 if (strip->inBox && gtk_widget_is_drawable (strip->inBox)) {
785 for (int i = 0; i < (int)strip->audio_in.size(); i++) {
786 calf_vumeter_set_value(CALF_VUMETER(strip->audio_in[i]), LVL(plugin->get_level(idx++)));
789 else
790 idx += strip->audio_in.size();
791 if (strip->outBox && gtk_widget_is_drawable (strip->outBox)) {
792 for (int i = 0; i < (int)strip->audio_out.size(); i++) {
793 calf_vumeter_set_value(CALF_VUMETER(strip->audio_out[i]), LVL(plugin->get_level(idx++)));
796 else
797 idx += strip->audio_out.size();
798 if (plugin->get_metadata_iface()->get_midi()) {
799 calf_led_set_value (CALF_LED (strip->midi_in), plugin->get_level(idx++));
803 return TRUE;
806 void gtk_main_window::open_file()
808 GtkWidget *dialog;
809 dialog = gtk_file_chooser_dialog_new ("Open File",
810 toplevel,
811 GTK_FILE_CHOOSER_ACTION_OPEN,
812 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
813 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
814 NULL);
815 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
817 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
818 char *error = owner->open_file(filename);
819 if (error)
820 display_error(error, filename);
821 else
822 owner->set_current_filename(filename);
823 g_free (filename);
824 free (error);
826 gtk_widget_destroy (dialog);
829 bool gtk_main_window::save_file()
831 if (owner->get_current_filename().empty())
832 return save_file_as();
834 const char *error = owner->save_file(owner->get_current_filename().c_str());
835 if (error)
837 display_error(error, owner->get_current_filename().c_str());
838 return false;
840 return true;
843 bool gtk_main_window::save_file_as()
845 GtkWidget *dialog;
846 bool success = false;
847 dialog = gtk_file_chooser_dialog_new ("Save File",
848 toplevel,
849 GTK_FILE_CHOOSER_ACTION_SAVE,
850 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
851 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
852 NULL);
853 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
855 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
856 char *error = owner->save_file(filename);
857 if (error)
858 display_error(error, filename);
859 else
861 owner->set_current_filename(filename);
862 success = true;
864 g_free (filename);
865 free(error);
867 gtk_widget_destroy (dialog);
868 return success;
871 void gtk_main_window::display_error(const char *error, const char *filename)
873 GtkWidget *dialog;
874 dialog = gtk_message_dialog_new_with_markup (toplevel, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, error, filename, NULL);
875 gtk_dialog_run (GTK_DIALOG (dialog));
876 gtk_widget_destroy (dialog);
879 GtkWidget *gtk_main_window::create_progress_window()
881 GtkWidget *tlw = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
882 gtk_window_set_type_hint (GTK_WINDOW (tlw), GDK_WINDOW_TYPE_HINT_DIALOG);
883 GtkWidget *pbar = gtk_progress_bar_new();
884 gtk_container_add (GTK_CONTAINER(tlw), pbar);
885 gtk_widget_show_all (pbar);
886 return tlw;
889 void gtk_main_window::report_progress(float percentage, const std::string &message)
891 if (percentage < 100)
893 if (!progress_window) {
894 progress_window = create_progress_window();
895 gtk_window_set_modal (GTK_WINDOW (progress_window), TRUE);
896 if (toplevel)
897 gtk_window_set_transient_for (GTK_WINDOW (progress_window), toplevel);
898 gtk_widget_show(progress_window);
900 GtkWidget *pbar = gtk_bin_get_child (GTK_BIN (progress_window));
901 if (!message.empty())
902 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pbar), message.c_str());
903 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (pbar), percentage / 100.0);
905 else
907 if (progress_window) {
908 gtk_window_set_modal (GTK_WINDOW (progress_window), FALSE);
909 gtk_widget_destroy (progress_window);
910 progress_window = NULL;
914 while (gtk_events_pending ())
915 gtk_main_iteration ();
918 void gtk_main_window::add_condition(const std::string &name)
920 conditions.insert(name);
923 void gtk_main_window::show_error(const std::string &text)
925 GtkWidget *widget = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", text.c_str());
926 gtk_dialog_run (GTK_DIALOG (widget));
927 gtk_widget_destroy (widget);
930 GtkListStore *gtk_main_window::get_styles()
932 std::vector <calf_utils::direntry> list = calf_utils::list_directory(PKGLIBDIR"styles");
933 GtkListStore *store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
934 for (std::vector<calf_utils::direntry>::iterator i = list.begin(); i != list.end(); i++) {
935 string title = i->name;
936 std::string rcf = i->full_path + "/gtk.rc";
937 ifstream infile(rcf.c_str());
938 if (infile.good()) {
939 string line;
940 getline(infile, line);
941 title = line.substr(1);
943 gtk_list_store_insert_with_values(store, NULL, -1,
944 0, title.c_str(),
945 1, i->name.c_str(),
946 -1);
948 return store;
950 void gtk_main_window::load_style(std::string path) {
951 gtk_rc_parse((path + "/gtk.rc").c_str());
952 gtk_rc_reset_styles(gtk_settings_get_for_screen(gdk_screen_get_default()));
953 images.set_path(path);