Merge commit 'origin/caching'
[calf.git] / src / main_win.cpp
blob7bb84f726ac32d8867d134b249742a66105a3899
1 /* Calf DSP Library
2 * GUI main window.
3 * Copyright (C) 2007 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., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
21 #include <assert.h>
22 #include <config.h>
23 #include <calf/ctl_led.h>
24 #include <calf/giface.h>
25 #include <calf/gui.h>
26 #include <calf/preset.h>
27 #include <calf/preset_gui.h>
28 #include <calf/main_win.h>
30 using namespace calf_plugins;
31 using namespace std;
33 static const char *ui_xml =
34 "<ui>\n"
35 " <menubar>\n"
36 " <menu action=\"HostMenuAction\">\n"
37 " <menu action=\"AddPluginMenuAction\">\n"
38 " </menu>\n"
39 " <separator/>\n"
40 " <menuitem action=\"exit\"/>\n"
41 " </menu>\n"
42 " </menubar>\n"
43 "</ui>\n"
46 static void exit_action(GtkWidget *widget, main_window *main)
48 gtk_widget_destroy(GTK_WIDGET(main->toplevel));
51 static const GtkActionEntry actions[] = {
52 { "HostMenuAction", "", "_Host", NULL, "Host-related operations", NULL },
53 { "AddPluginMenuAction", "", "_Add plugin", NULL, "Add a plugin to the rack", NULL },
54 { "exit", "", "_Exit", NULL, "Exit application", (GCallback)exit_action },
57 main_window::main_window()
59 toplevel = NULL;
60 owner = NULL;
61 is_closed = true;
64 void main_window::add_plugin(plugin_ctl_iface *plugin)
66 if (toplevel)
68 plugin_strip *strip = create_strip(plugin);
69 plugins[plugin] = strip;
70 update_strip(plugin);
72 else {
73 plugin_queue.push_back(plugin);
74 plugins[plugin] = NULL;
78 void main_window::del_plugin(plugin_ctl_iface *plugin)
80 if (!plugins.count(plugin))
81 return;
82 plugin_strip *strip = plugins[plugin];
83 if (strip->gui_win)
84 strip->gui_win->close();
86 int row = -1;
87 for(GList *p = GTK_TABLE(strips_table)->children; p != NULL; p = p->next)
89 GtkTableChild *c = (GtkTableChild *)p->data;
90 if (c->widget == strip->name)
92 row = c->top_attach - 1;
93 break;
96 g_assert(row != -1);
98 vector<GtkWidget *> to_destroy;
99 for(GList *p = GTK_TABLE(strips_table)->children; p != NULL; p = p->next)
101 GtkTableChild *c = (GtkTableChild *)p->data;
102 if (c->top_attach >= row && c->top_attach < row + 3)
103 to_destroy.push_back(c->widget);
104 if (c->top_attach >= row + 3)
106 c->top_attach -= 3;
107 c->bottom_attach -= 3;
111 for (unsigned int i = 0; i < to_destroy.size(); i++)
112 gtk_container_remove(GTK_CONTAINER(strips_table), to_destroy[i]);
113 //for (unsigned int i = 0; i < to_destroy.size(); i++)
114 // gtk_widget_destroy(to_destroy[i]);
116 plugins.erase(plugin);
117 int rows = 0, cols = 0;
118 g_object_get(G_OBJECT(strips_table), "n-rows", &rows, "n-columns", &cols, NULL);
119 gtk_table_resize(GTK_TABLE(strips_table), rows - 3, cols);
121 // a hack to remove unneeded vertical space from the window
122 // not perfect, as it undoes user's vertical resize
123 // only needed when window is resizable though
124 int width, height;
125 gtk_window_get_size(toplevel, &width, &height);
126 gtk_window_resize(toplevel, width, 1);
130 void main_window::set_window(plugin_ctl_iface *plugin, plugin_gui_window *gui_win)
132 if (!plugins.count(plugin))
133 return;
134 plugin_strip *strip = plugins[plugin];
135 if (!strip)
136 return;
137 strip->gui_win = gui_win;
138 if (!is_closed)
139 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(strip->name), gui_win != NULL);
142 void main_window::refresh_all_presets(bool builtin_too)
144 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); i++)
146 if (i->second && i->second->gui_win) {
147 char ch = '0';
148 i->second->gui_win->fill_gui_presets(true, ch);
149 i->second->gui_win->fill_gui_presets(false, ch);
154 static gboolean
155 gui_button_pressed(GtkWidget *button, main_window::plugin_strip *strip)
157 GtkToggleButton *tb = GTK_TOGGLE_BUTTON(button);
158 if ((gtk_toggle_button_get_active(tb) != 0) == (strip->gui_win != NULL))
159 return FALSE;
160 if (strip->gui_win) {
161 strip->gui_win->close();
162 strip->gui_win = NULL;
163 } else {
164 strip->main_win->open_gui(strip->plugin);
166 return TRUE;
169 static gboolean
170 extra_button_pressed(GtkWidget *button, main_window::plugin_strip *strip)
172 strip->main_win->owner->remove_plugin(strip->plugin);
173 return TRUE;
176 main_window::plugin_strip *main_window::create_strip(plugin_ctl_iface *plugin)
178 plugin_strip *strip = new plugin_strip;
179 strip->main_win = this;
180 strip->plugin = plugin;
181 strip->gui_win = NULL;
183 GtkAttachOptions ao = (GtkAttachOptions)(GTK_EXPAND | GTK_FILL);
185 int row = 0, cols = 0;
186 g_object_get(G_OBJECT(strips_table), "n-rows", &row, "n-columns", &cols, NULL);
187 gtk_table_resize(GTK_TABLE(strips_table), row + 3, cols);
189 GtkWidget *sep = gtk_hseparator_new();
190 gtk_table_attach(GTK_TABLE(strips_table), sep, 0, 5, row, row + 1, ao, GTK_SHRINK, 0, 0);
191 gtk_widget_show(sep);
192 row++;
194 GtkWidget *label = gtk_toggle_button_new_with_label(plugin->get_label());
195 gtk_table_attach(GTK_TABLE(strips_table), label, 0, 1, row, row + 2, ao, GTK_SHRINK, 0, 0);
196 strip->name = label;
197 gtk_signal_connect(GTK_OBJECT(label), "toggled", G_CALLBACK(gui_button_pressed),
198 (plugin_ctl_iface *)strip);
199 gtk_widget_show(strip->name);
201 if (plugin->get_midi())
202 label = calf_led_new();
203 else
204 label = gtk_label_new("");
205 gtk_table_attach(GTK_TABLE(strips_table), label, 1, 2, row, row + 2, GTK_FILL, GTK_SHRINK, 0, 0);
206 strip->midi_in = label;
207 gtk_widget_show(strip->midi_in);
209 for (int i = 0; i < 2; i++)
210 strip->audio_in[i] = strip->audio_out[i] = NULL;
212 if (plugin->get_input_count() == 2) {
213 label = calf_vumeter_new();
214 gtk_table_attach(GTK_TABLE(strips_table), label, 2, 3, row, row + 1, ao, GTK_SHRINK, 0, 0);
215 strip->audio_in[0] = label;
216 label = calf_vumeter_new();
217 gtk_table_attach(GTK_TABLE(strips_table), label, 2, 3, row + 1, row + 2, ao, GTK_SHRINK, 0, 0);
218 strip->audio_in[1] = label;
219 gtk_widget_show(strip->audio_in[0]);
220 gtk_widget_show(strip->audio_in[1]);
223 if (plugin->get_output_count() == 2) {
224 label = calf_vumeter_new();
225 gtk_table_attach(GTK_TABLE(strips_table), label, 3, 4, row, row + 1, ao, GTK_SHRINK, 0, 0);
226 strip->audio_out[0] = label;
227 label = calf_vumeter_new();
228 gtk_table_attach(GTK_TABLE(strips_table), label, 3, 4, row + 1, row + 2, ao, GTK_SHRINK, 0, 0);
229 strip->audio_out[1] = label;
230 gtk_widget_show(strip->audio_out[0]);
231 gtk_widget_show(strip->audio_out[1]);
233 GtkWidget *extra = gtk_button_new_with_label("Delete");
234 gtk_table_attach(GTK_TABLE(strips_table), extra, 4, 5, row, row + 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
235 strip->extra = extra;
236 gtk_signal_connect(GTK_OBJECT(extra), "clicked", G_CALLBACK(extra_button_pressed),
237 (plugin_ctl_iface *)strip);
238 gtk_widget_show(strip->extra);
240 return strip;
243 void main_window::update_strip(plugin_ctl_iface *plugin)
245 // plugin_strip *strip = plugins[plugin];
246 // assert(strip);
250 void main_window::open_gui(plugin_ctl_iface *plugin)
252 plugin_gui_window *gui_win = new plugin_gui_window(this);
253 gui_win->create(plugin, (prefix + plugin->get_label()).c_str(), plugin->get_id());
254 gtk_widget_show_all(GTK_WIDGET(gui_win->toplevel));
255 plugins[plugin]->gui_win = gui_win;
258 static const char *plugin_pre_xml =
259 "<ui>\n"
260 " <menubar>\n"
261 " <menu action=\"AddPluginMenuAction\">\n"
262 " <placeholder name=\"plugin\">\n";
264 static const char *plugin_post_xml =
265 " </placeholder>\n"
266 " </menu>\n"
267 " </menubar>\n"
268 "</ui>\n"
271 void main_window::add_plugin_action(GtkWidget *src, gpointer data)
273 add_plugin_params *app = (add_plugin_params *)data;
274 app->main_win->new_plugin(app->name.c_str());
277 static void action_destroy_notify(gpointer data)
279 delete (main_window::add_plugin_params *)data;
283 void main_window::new_plugin(const char *plugin)
285 printf("new plugin %s\n", plugin);
289 std::string main_window::make_plugin_list(GtkActionGroup *actions)
291 string s = plugin_pre_xml;
292 std::vector<plugin_metadata_iface *> plugins;
293 calf_plugins::get_all_plugins(plugins);
294 for(unsigned int i = 0; i < plugins.size(); i++)
296 plugin_metadata_iface *p = plugins[i];
297 string action_name = "Add" + string(p->get_id())+"Action";
298 s += string("<menuitem action=\"") + action_name + "\" />";
299 GtkActionEntry ae = { action_name.c_str(), NULL, p->get_label(), NULL, NULL, (GCallback)add_plugin_action };
300 gtk_action_group_add_actions_full(actions, &ae, 1, (gpointer)new add_plugin_params(this, p->get_id()), action_destroy_notify);
301 delete p;
303 plugins.clear();
304 return s + plugin_post_xml;
307 void main_window::create()
309 toplevel = GTK_WINDOW(gtk_window_new (GTK_WINDOW_TOPLEVEL));
310 gtk_window_set_default_icon_name("calf");
311 is_closed = false;
312 gtk_window_set_resizable(toplevel, false);
314 all_vbox = gtk_vbox_new(0, FALSE);
316 ui_mgr = gtk_ui_manager_new();
317 std_actions = gtk_action_group_new("default");
318 gtk_action_group_add_actions(std_actions, actions, sizeof(actions)/sizeof(actions[0]), this);
319 GError *error = NULL;
320 gtk_ui_manager_insert_action_group(ui_mgr, std_actions, 0);
321 gtk_ui_manager_add_ui_from_string(ui_mgr, ui_xml, -1, &error);
322 gtk_box_pack_start(GTK_BOX(all_vbox), gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar"), false, false, 0);
324 plugin_actions = gtk_action_group_new("plugins");
325 string plugin_xml = make_plugin_list(plugin_actions);
326 gtk_ui_manager_insert_action_group(ui_mgr, plugin_actions, 0);
327 gtk_ui_manager_add_ui_from_string(ui_mgr, plugin_xml.c_str(), -1, &error);
330 strips_table = gtk_table_new(1, 6, FALSE);
331 gtk_table_set_col_spacings(GTK_TABLE(strips_table), 10);
332 gtk_table_set_row_spacings(GTK_TABLE(strips_table), 5);
334 gtk_table_attach(GTK_TABLE(strips_table), gtk_label_new("Module"), 0, 1, 0, 1, GTK_FILL, GTK_SHRINK, 0, 0);
335 gtk_table_attach(GTK_TABLE(strips_table), gtk_label_new("MIDI In"), 1, 2, 0, 1, GTK_FILL, GTK_SHRINK, 0, 0);
336 gtk_table_attach(GTK_TABLE(strips_table), gtk_label_new("Audio In"), 2, 3, 0, 1, GTK_FILL, GTK_SHRINK, 0, 0);
337 gtk_table_attach(GTK_TABLE(strips_table), gtk_label_new("Audio Out"), 3, 4, 0, 1, GTK_FILL, GTK_SHRINK, 0, 0);
338 for(GList *p = GTK_TABLE(strips_table)->children; p != NULL; p = p->next)
340 GtkTableChild *c = (GtkTableChild *)p->data;
341 if (c->top_attach == 0) {
342 gtk_misc_set_alignment(GTK_MISC(c->widget), 0.5, 0);
345 for (std::vector<plugin_ctl_iface *>::iterator i = plugin_queue.begin(); i != plugin_queue.end(); i++)
347 plugins[*i] = create_strip(*i);
348 update_strip(*i);
351 gtk_container_add(GTK_CONTAINER(all_vbox), strips_table);
352 gtk_container_add(GTK_CONTAINER(toplevel), all_vbox);
354 gtk_widget_show_all(GTK_WIDGET(toplevel));
355 source_id = g_timeout_add_full(G_PRIORITY_LOW, 1000/30, on_idle, this, NULL); // 30 fps should be enough for everybody
358 void main_window::refresh_plugin(plugin_ctl_iface *plugin)
360 if (plugins[plugin]->gui_win)
361 plugins[plugin]->gui_win->gui->refresh();
364 void main_window::close_guis()
366 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); i++)
368 if (i->second && i->second->gui_win) {
369 i->second->gui_win->close();
372 plugins.clear();
375 void main_window::on_closed()
377 if (source_id)
378 g_source_remove(source_id);
379 is_closed = true;
380 toplevel = NULL;
383 static inline float LVL(float value)
385 return sqrt(value) * 0.75;
388 gboolean main_window::on_idle(void *data)
390 main_window *self = (main_window *)data;
391 for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = self->plugins.begin(); i != self->plugins.end(); i++)
393 if (i->second)
395 plugin_ctl_iface *plugin = i->first;
396 plugin_strip *strip = i->second;
397 int idx = 0;
398 if (plugin->get_input_count() == 2) {
399 calf_vumeter_set_value(CALF_VUMETER(strip->audio_in[0]), LVL(plugin->get_level(idx++)));
400 calf_vumeter_set_value(CALF_VUMETER(strip->audio_in[1]), LVL(plugin->get_level(idx++)));
402 if (plugin->get_output_count() == 2) {
403 calf_vumeter_set_value(CALF_VUMETER(strip->audio_out[0]), LVL(plugin->get_level(idx++)));
404 calf_vumeter_set_value(CALF_VUMETER(strip->audio_out[1]), LVL(plugin->get_level(idx++)));
406 if (plugin->get_midi()) {
407 calf_led_set_state (CALF_LED (strip->midi_in), plugin->get_level(idx++) > 0.f);
411 return TRUE;