Merge branch 'master' of https://github.com/calf-studio-gear/calf
[calf.git] / src / calf / gui.h
bloba3d02662362d74fd366c15e670ac2e30cf3b069c
1 /* Calf DSP Library
2 * Universal GUI module
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 02111-1307, USA.
20 #ifndef __CALF_GUI_H
21 #define __CALF_GUI_H
23 #include <expat.h>
24 #include <map>
25 #include <set>
26 #include <string>
27 #include <vector>
28 #include <gtk/gtk.h>
29 #include "giface.h"
30 #include "gui_config.h"
31 #include "jackhost.h"
33 namespace calf_plugins {
35 class window_update_controller
37 int refresh_counter;
38 public:
39 window_update_controller() : refresh_counter() {}
40 bool check_redraw(GtkWidget *toplevel);
44 struct image_factory
46 std::string path;
47 std::map<std::string, GdkPixbuf*> i;
48 GdkPixbuf *create_image (std::string image);
49 void recreate_images ();
50 void set_path (std::string p);
51 GdkPixbuf *get (std::string image);
52 gboolean available (std::string image);
53 image_factory (std::string p = "");
54 ~image_factory();
57 class plugin_gui;
58 class jack_host;
60 class control_base
62 public:
63 typedef std::map<std::string, std::string> xml_attribute_map;
65 GtkWidget *widget;
66 std::string control_name;
67 xml_attribute_map attribs;
68 plugin_gui *gui;
69 public:
70 void require_attribute(const char *name);
71 void require_int_attribute(const char *name);
72 int get_int(const char *name, int def_value = 0);
73 float get_float(const char *name, float def_value = 0.f);
74 std::vector<double> get_vector(const char *name, std::string &value);
76 public:
77 virtual GtkWidget *create(plugin_gui *_gui) { return NULL; }
78 virtual bool is_container() { return GTK_IS_CONTAINER(widget); };
79 virtual void set_visibilty(bool state);
80 virtual void add(control_base *ctl) { gtk_container_add(GTK_CONTAINER(widget), ctl->widget); }
81 /// called from created() to set all the properties
82 virtual void set_std_properties();
83 /// called after the control is created
84 virtual void created();
85 virtual ~control_base() {}
88 #define _GUARD_CHANGE_ if (in_change) return; guard_change __gc__(this);
90 class param_control: public control_base
92 protected:
93 GtkWidget *entrywin;
94 public:
95 int param_no;
96 std::string param_variable;
97 int in_change;
98 bool has_entry;
99 float old_displayed_value;
101 struct guard_change {
102 param_control *pc;
103 guard_change(param_control *_pc) : pc(_pc) { pc->in_change++; }
104 ~guard_change() { pc->in_change--; }
107 param_control();
108 inline const parameter_properties &get_props();
110 virtual GtkWidget *create(plugin_gui *_gui);
111 /// called to create a widget for a control
112 virtual GtkWidget *create(plugin_gui *_gui, int _param_no)=0;
113 /// called to transfer the value from control to parameter(s)
114 virtual void get()=0;
115 /// called to transfer the value from parameter(s) to control
116 virtual void set()=0;
117 /// called after the control is created
118 virtual void created();
119 /// called on DSSI configure()
120 virtual void configure(const char *key, const char *value) {}
121 /// called from created() to add hooks for parameters
122 virtual void hook_params();
123 /// called from created() to add context menu handlers
124 virtual void add_context_menu_handler();
125 virtual void on_idle() {}
126 virtual ~param_control();
127 virtual void do_popup_menu();
128 static gboolean on_button_press_event(GtkWidget *widget, GdkEventButton *event, void *user_data);
129 static gboolean on_popup_menu(GtkWidget *widget, void *user_data);
130 virtual void create_value_entry(GtkWidget *widget, int x, int y);
131 virtual void destroy_value_entry();
132 static gboolean value_entry_action(GtkEntry *widget, GdkEvent *event, void *user_data);
133 static gboolean value_entry_unfocus(GtkWidget *widget, GdkEventFocus *event, void *user_data);
134 static gboolean value_entry_click(GtkWidget *widget, GdkEventButton *event, void *user_data);
138 class plugin_gui_window;
140 class plugin_gui: public send_configure_iface, public send_updates_iface
142 protected:
143 int param_count;
144 std::multimap<int, param_control *> par2ctl;
145 XML_Parser parser;
146 control_base *top_container;
147 std::map<std::string, int> param_name_map;
148 int ignore_stack;
149 int last_status_serial_no;
150 std::map<int, GSList *> param_radio_groups;
151 GtkWidget *leftBG, *rightBG;
152 int context_menu_param_no;
153 uint32_t context_menu_last_designator;
154 std::vector<control_base *> stack;
156 struct automation_menu_entry {
157 plugin_gui *gui;
158 int source;
159 automation_menu_entry(plugin_gui *_gui, int _source)
160 : gui(_gui), source(_source) {}
162 std::vector<automation_menu_entry *> automation_menu_callback_data;
164 static void on_automation_add(GtkWidget *widget, void *user_data);
165 static void on_automation_delete(GtkWidget *widget, void *user_data);
166 static void on_automation_set_lower(GtkWidget *widget, void *user_data);
167 static void on_automation_set_upper(GtkWidget *widget, void *user_data);
168 void on_automation_set_lower_or_upper(automation_menu_entry *ame, bool is_upper);
169 public:
170 plugin_gui_window *window;
171 GtkWidget *container;
172 const char *effect_name;
173 plugin_ctl_iface *plugin;
174 preset_access_iface *preset_access;
175 std::vector<param_control *> params;
176 std::vector<int> read_serials;
178 /* For optional lv2ui:show interface. */
179 bool optclosed;
180 GtkWidget* optwidget;
181 GtkWidget* optwindow;
182 const char* opttitle;
184 plugin_gui(plugin_gui_window *_window);
185 GtkWidget *create_from_xml(plugin_ctl_iface *_plugin, const char *xml);
186 control_base *create_widget_from_xml(const char *element, const char *attributes[]);
188 void add_param_ctl(int param, param_control *ctl) { par2ctl.insert(std::pair<int, param_control *>(param, ctl)); }
189 void remove_param_ctl(int param, param_control *ctl);
190 void refresh();
191 void refresh(int param_no, param_control *originator = NULL);
192 int get_param_no_by_name(std::string param_name);
193 void xml_element_start(const char *element, const char *attributes[]);
194 void set_param_value(int param_no, float value, param_control *originator = NULL);
195 /// Called on change of configure variable
196 void send_configure(const char *key, const char *value);
197 /// Called on change of status variable
198 void send_status(const char *key, const char *value);
199 void on_idle();
200 /// Get a radio button group (if it exists) for a parameter
201 GSList *get_radio_group(int param);
202 /// Set a radio button group for a parameter
203 void set_radio_group(int param, GSList *group);
204 /// Show rack ear widgets
205 void show_rack_ears(bool show);
206 /// Pop-up a context menu for a control
207 void on_control_popup(param_control *ctl, int param_no);
208 /// Clean up callback data allocated for the automation pop-up menu
209 void cleanup_automation_entries();
210 /// Destroy all the widgets in the container
211 void destroy_child_widgets(GtkWidget *parent);
212 ~plugin_gui();
213 static void xml_element_start(void *data, const char *element, const char *attributes[]);
214 static void xml_element_end(void *data, const char *element);
217 class main_window_iface;
218 class main_window_owner_iface;
220 /// A class used to inform the plugin GUIs about the environment they run in
221 /// (currently: what plugin features are accessible)
222 struct gui_environment_iface
224 virtual bool check_condition(const char *name) = 0;
225 virtual calf_utils::config_db_iface *get_config_db() = 0;
226 virtual calf_utils::gui_config *get_config() = 0;
227 virtual calf_plugins::image_factory *get_image_factory() = 0;
228 virtual ~gui_environment_iface() {}
231 /// An interface that wraps UI-dependent elements of the session
232 struct session_environment_iface
234 /// Called to initialise the UI libraries
235 virtual void init_gui(int &argc, char **&argv) = 0;
236 /// Create an appropriate version of the main window
237 virtual main_window_iface *create_main_window() = 0;
238 /// Called to start the UI loop
239 virtual void start_gui_loop() = 0;
240 /// Called from within event handlers to finish the UI loop
241 virtual void quit_gui_loop() = 0;
242 virtual ~session_environment_iface() {}
245 /// Trivial implementation of gui_environment_iface
246 class gui_environment: public gui_environment_iface
248 private:
249 GKeyFile *keyfile;
250 calf_utils::config_db_iface *config_db;
251 calf_utils::gui_config gui_config;
252 public:
253 std::set<std::string> conditions;
254 image_factory images;
255 gui_environment();
256 virtual bool check_condition(const char *name) { return conditions.count(name) != 0; }
257 virtual calf_utils::config_db_iface *get_config_db() { return config_db; }
258 virtual calf_utils::gui_config *get_config() { return &gui_config; }
259 virtual calf_plugins::image_factory *get_image_factory() { return &images; }
260 ~gui_environment();
263 /// Interface used by the plugin to communicate with the main hosting window
264 struct main_window_iface: public progress_report_iface
266 /// Set owner pointer
267 virtual void set_owner(main_window_owner_iface *owner) = 0;
268 /// Add a condition to the list of conditions supported by the host
269 virtual void add_condition(const std::string &name) = 0;
270 /// Create the actual window associated with this interface
271 virtual void create() = 0;
272 /// Add the plugin to the window
273 virtual void add_plugin(jack_host *plugin) = 0;
274 /// Remove the plugin from the window
275 virtual void del_plugin(plugin_ctl_iface *plugin) = 0;
276 /// Refresh the plugin UI
277 virtual void refresh_plugin(plugin_ctl_iface *plugin) = 0;
278 /// Bind the plugin window to the plugin
279 virtual void set_window(plugin_ctl_iface *plugin, plugin_gui_window *window) = 0;
280 /// Refresh preset lists on all windows (if, for example, a new preset has been created)
281 virtual void refresh_all_presets(bool builtin_too) = 0;
282 /// Default open file operation
283 virtual void open_file() = 0;
284 /// Default save file operation
285 virtual bool save_file() = 0;
286 /// Called to clean up when host quits
287 virtual void on_closed() = 0;
288 /// Show an error message
289 virtual void show_error(const std::string &text) = 0;
292 virtual ~main_window_iface() {}
295 struct main_window_owner_iface
297 virtual void new_plugin(const char *name) = 0;
298 virtual void remove_plugin(plugin_ctl_iface *plugin) = 0;
299 virtual char *open_file(const char *name) = 0;
300 virtual char *save_file(const char *name) = 0;
301 virtual void reorder_plugins() = 0;
302 /// Return JACK client name (or its counterpart) to put in window title bars
303 virtual std::string get_client_name() const = 0;
304 /// Called on 'destroy' event of the main window
305 virtual void on_main_window_destroy() = 0;
306 /// Called from idle handler
307 virtual void on_idle() = 0;
308 /// Get the file name of the current rack
309 virtual std::string get_current_filename() const = 0;
310 /// Set the file name of the current rack
311 virtual void set_current_filename(const std::string &name) = 0;
312 virtual ~main_window_owner_iface() {}
315 class plugin_gui_window: public calf_utils::config_listener_iface
317 private:
318 void cleanup();
319 window_update_controller refresh_controller;
320 public:
321 plugin_gui *gui;
322 GtkWindow *toplevel;
323 GtkUIManager *ui_mgr;
324 GtkActionGroup *std_actions, *builtin_preset_actions, *user_preset_actions, *command_actions;
325 gui_environment_iface *environment;
326 main_window_iface *main;
327 int source_id;
328 calf_utils::config_notifier_iface *notifier;
330 plugin_gui_window(gui_environment_iface *_env, main_window_iface *_main);
331 std::string make_gui_preset_list(GtkActionGroup *grp, bool builtin, char &ch);
332 std::string make_gui_command_list(GtkActionGroup *grp, const plugin_metadata_iface *metadata);
333 void fill_gui_presets(bool builtin, char &ch);
334 void create(plugin_ctl_iface *_plugin, const char *title, const char *effect);
335 void close();
336 virtual void on_config_change();
337 static gboolean on_idle(void *data);
338 static void on_window_destroyed(GtkWidget *window, gpointer data);
339 ~plugin_gui_window();
343 inline const parameter_properties &param_control::get_props()
345 return *gui->plugin->get_metadata_iface()->get_param_props(param_no);
348 class null_audio_module;
350 struct activate_command_params
352 typedef void (*CommandFunc)(null_audio_module *);
353 plugin_gui *gui;
354 int function_idx;
356 activate_command_params(plugin_gui *_gui, int _idx)
357 : gui(_gui), function_idx(_idx)
362 void activate_command(GtkAction *action, activate_command_params *params);
364 class cairo_impl: public calf_plugins::cairo_iface
366 public:
367 cairo_t *context;
368 virtual void set_source_rgba(float r, float g, float b, float a = 1.f) { cairo_set_source_rgba(context, r, g, b, a); }
369 virtual void set_line_width(float width) { cairo_set_line_width(context, width); }
370 virtual void set_dash(const double *dash, int length) { cairo_set_dash(context, dash, length, 0); }
371 virtual void draw_label(const char *label, float x, float y, int pos, float margin, float align) {
372 cairo_text_extents_t extents;
373 cairo_text_extents(context, label, &extents);
374 switch(pos) {
375 case 0:
376 default:
377 // top
378 cairo_move_to(context, x - extents.width / 2, y - margin);
379 break;
380 case 1:
381 // right
382 cairo_move_to(context, x + margin, y + 2);
383 break;
384 case 2:
385 // bottom
386 cairo_move_to(context, x - extents.width / 2, y + margin + extents.height);
387 break;
388 case 3:
389 // left
390 cairo_move_to(context, x - margin - extents.width, y + 2);
391 break;
393 cairo_show_text(context, label);
401 #endif