1 /*
2 * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
3 * Copyright (C) 2011 Pete Shorthose
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * ---------------------------------------------------------------------------
19 *
20 * This is the gx_head GUI main class
21 *
22 * ----------------------------------------------------------------------------
23 */
24
25 #include "guitarix.h" // NOLINT
26
27 #include <iomanip> // NOLINT
28 #include <cstring> // NOLINT
29 #include <string> // NOLINT
30 #include <boost/algorithm/string/replace.hpp>
31 #include <gxw/GxControlParameter.h>
32
33 namespace gx_gui {
34
35 /* ----- load a top level window from gtk builder file ------ */
36
load_toplevel(GtkBuilder * builder,const char * filename,const char * windowname)37 GtkWidget *load_toplevel(GtkBuilder *builder, const char* filename, const char* windowname) {
38 string fname = gx_system::get_options().get_builder_filepath(filename);
39 GError *err = NULL;
40 if (!gtk_builder_add_from_file(builder, fname.c_str(), &err)) {
41 g_object_unref(G_OBJECT(builder));
42 gx_print_fatal(_("gtk builder"), err->message);
43 g_error_free(err);
44 return NULL;
45 }
46 GtkWidget *w = GTK_WIDGET(gtk_builder_get_object(builder, windowname));
47 if (!w) {
48 g_object_unref(G_OBJECT(builder));
49 gx_print_fatal(_("gtk builder"), string(windowname)+_(" not found in ")+fname);
50 return NULL;
51 }
52 gtk_builder_connect_signals(builder, 0);
53 return w;
54 }
55
56 /****************************************************************
57 ** UiBuilder implementation
58 */
59
60 StackBoxBuilder *UiBuilderImpl::intf = 0;
61
UiBuilderImpl(PluginDict * i,StackBoxBuilder * b,std::vector<PluginUI * > * pl,PluginUI * pluginui)62 UiBuilderImpl::UiBuilderImpl(PluginDict *i, StackBoxBuilder *b, std::vector<PluginUI*> *pl, PluginUI *pluginui)
63 : UiBuilderBase(), plugin_dict(*i), pluginlist(pl) {
64 intf = b;
65 intf->set_current_plugin(pluginui);
66 openTabBox = openTabBox_;
67 openVerticalBox = openVerticalBox_;
68 openVerticalBox1 = openVerticalBox1_;
69 openVerticalBox2 = openVerticalBox2_;
70 openHorizontalBox = openHorizontalBox_;
71 openHorizontalhideBox = openHorizontalhideBox_;
72 openHorizontalTableBox = openHorizontalTableBox_;
73 openFrameBox = openFrameBox_;
74 openFlipLabelBox = openFlipLabelBox_;
75 openpaintampBox = openpaintampBox_;
76 closeBox = closeBox_;
77 load_glade = load_glade_;
78 load_glade_file = load_glade_file_;
79 create_master_slider = create_master_slider_;
80 create_feedback_slider = create_feedback_slider_;
81 create_mid_rackknob = create_mid_rackknob_;
82 create_small_rackknob = create_small_rackknob_;
83 create_small_rackknobr = create_small_rackknobr_;
84 create_big_rackknob = create_big_rackknob_;
85 create_spin_value = create_spin_value_;
86 create_switch = create_switch_;
87 create_switch_no_caption = create_switch_no_caption_;
88 create_feedback_switch = create_feedback_switch_;
89 create_fload_switch = create_fload_switch_;
90 create_wheel = create_wheel_;
91 create_selector = create_selector_;
92 create_simple_meter = create_simple_meter_;
93 create_simple_c_meter = create_simple_c_meter_;
94 create_selector_no_caption = create_selector_no_caption_;
95 create_port_display = create_port_display_;
96 create_p_display = create_p_display_;
97 create_simple_spin_value = create_simple_spin_value_;
98 create_eq_rackslider_no_caption = create_eq_rackslider_no_caption_;
99 set_next_flags = set_next_flags_;
100 insertSpacer = insertSpacer_;
101 };
102
~UiBuilderImpl()103 UiBuilderImpl::~UiBuilderImpl() {
104 intf->set_current_plugin(nullptr);
105 }
106
load_unit(PluginDef * pd)107 bool UiBuilderImpl::load_unit(PluginDef *pd) {
108 if (!pd->load_ui) {
109 return false;
110 }
111 intf->prepare();
112 plugin = pd;
113 int form = UI_FORM_GLADE|UI_FORM_STACK;
114 #ifndef NDEBUG
115 if (getenv("GUITARIX_FORM_STACK")) {
116 form = UI_FORM_STACK;
117 }
118 #endif
119 pd->load_ui(*this, form);
120 return true;
121 }
122
openTabBox_(const char * label)123 void UiBuilderImpl::openTabBox_(const char* label) {
124 intf->openTabBox(label);
125 }
126
openVerticalBox_(const char * label)127 void UiBuilderImpl::openVerticalBox_(const char* label) {
128 intf->openVerticalBox(label);
129 }
130
openVerticalBox1_(const char * label)131 void UiBuilderImpl::openVerticalBox1_(const char* label) {
132 intf->openVerticalBox1(label);
133 }
134
openVerticalBox2_(const char * label)135 void UiBuilderImpl::openVerticalBox2_(const char* label) {
136 intf->openVerticalBox2(label);
137 }
138
openHorizontalhideBox_(const char * label)139 void UiBuilderImpl::openHorizontalhideBox_(const char* label) {
140 intf->openHorizontalhideBox(label);
141 }
142
openHorizontalTableBox_(const char * label)143 void UiBuilderImpl::openHorizontalTableBox_(const char* label) {
144 intf->openHorizontalTableBox(label);
145 }
146
openHorizontalBox_(const char * label)147 void UiBuilderImpl::openHorizontalBox_(const char* label) {
148 intf->openHorizontalBox(label);
149 }
150
openFrameBox_(const char * label)151 void UiBuilderImpl::openFrameBox_(const char* label) {
152 intf->openFrameBox(label);
153 }
154
openFlipLabelBox_(const char * label)155 void UiBuilderImpl::openFlipLabelBox_(const char* label) {
156 intf->openFlipLabelBox(label);
157 }
158
openpaintampBox_(const char * label)159 void UiBuilderImpl::openpaintampBox_(const char* label) {
160 intf->openpaintampBox(label);
161 }
162
insertSpacer_()163 void UiBuilderImpl::insertSpacer_() {
164 intf->insertSpacer();
165 }
166
set_next_flags_(int flags)167 void UiBuilderImpl::set_next_flags_(int flags) {
168 intf->set_next_flags(flags);
169 }
170
create_mid_rackknob_(const char * id,const char * label)171 void UiBuilderImpl::create_mid_rackknob_(const char *id, const char *label) {
172 intf->create_mid_rackknob(id, label);
173 }
174
create_small_rackknob_(const char * id,const char * label)175 void UiBuilderImpl::create_small_rackknob_(const char *id, const char *label) {
176 intf->create_small_rackknob(id, label);
177 }
178
create_small_rackknobr_(const char * id,const char * label)179 void UiBuilderImpl::create_small_rackknobr_(const char *id, const char *label) {
180 intf->create_small_rackknobr(id, label);
181 }
182
create_big_rackknob_(const char * id,const char * label)183 void UiBuilderImpl::create_big_rackknob_(const char *id, const char *label) {
184 intf->create_big_rackknob(id, label);
185 }
186
create_master_slider_(const char * id,const char * label)187 void UiBuilderImpl::create_master_slider_(const char *id, const char *label) {
188 intf->create_master_slider(id, label);
189 }
190
create_feedback_slider_(const char * id,const char * label)191 void UiBuilderImpl::create_feedback_slider_(const char *id, const char *label) {
192 intf->create_feedback_slider(id, label);
193 }
194
create_selector_no_caption_(const char * id)195 void UiBuilderImpl::create_selector_no_caption_(const char *id) {
196 intf->create_selector(id, "");
197 }
198
create_selector_(const char * id,const char * label)199 void UiBuilderImpl::create_selector_(const char *id, const char *label) {
200 intf->create_selector_with_caption(id, label);
201 }
202
create_simple_meter_(const char * id)203 void UiBuilderImpl::create_simple_meter_(const char *id) {
204 intf->create_simple_meter(id);
205 }
206
create_simple_c_meter_(const char * id,const char * idl,const char * label)207 void UiBuilderImpl::create_simple_c_meter_(const char *id, const char *idl, const char *label) {
208 intf->create_simple_c_meter(id, idl, label);
209 }
210
create_spin_value_(const char * id,const char * label)211 void UiBuilderImpl::create_spin_value_(const char *id, const char *label) {
212 intf->create_spin_value(id, label);
213 }
214
create_switch_no_caption_(const char * sw_type,const char * id)215 void UiBuilderImpl::create_switch_no_caption_(const char *sw_type, const char * id) {
216 intf->create_switch_no_caption(sw_type, id);
217 }
218
create_feedback_switch_(const char * sw_type,const char * id)219 void UiBuilderImpl::create_feedback_switch_(const char *sw_type, const char * id) {
220 intf->create_feedback_switch(sw_type, id);
221 }
222
create_fload_switch_(const char * sw_type,const char * id,const char * idf)223 void UiBuilderImpl::create_fload_switch_(const char *sw_type, const char * id, const char * idf) {
224 intf->create_fload_switch(sw_type, id, idf);
225 }
226
create_switch_(const char * sw_type,const char * id,const char * label)227 void UiBuilderImpl::create_switch_(const char *sw_type, const char * id, const char *label) {
228 intf->create_v_switch(sw_type, id, label);
229 }
230
create_wheel_(const char * id,const char * label)231 void UiBuilderImpl::create_wheel_(const char * id, const char *label) {
232 intf->create_wheel(id, label);
233 }
234
create_port_display_(const char * id,const char * label)235 void UiBuilderImpl::create_port_display_(const char *id, const char *label) {
236 intf->create_port_display(id, label);
237 }
238
create_p_display_(const char * id,const char * idl,const char * idh)239 void UiBuilderImpl::create_p_display_(const char *id, const char *idl, const char *idh) {
240 intf->create_p_display(id, idl, idh);
241 }
242
create_simple_spin_value_(const char * id)243 void UiBuilderImpl::create_simple_spin_value_(const char *id) {
244 intf->create_simple_spin_value(id);
245 }
246
create_eq_rackslider_no_caption_(const char * id)247 void UiBuilderImpl::create_eq_rackslider_no_caption_(const char *id) {
248 intf->create_eq_rackslider_no_caption(id);
249 }
250
closeBox_()251 void UiBuilderImpl::closeBox_() {
252 intf->closeBox();
253 }
254
load_glade_(const char * data)255 void UiBuilderImpl::load_glade_(const char *data) {
256 intf->loadRackFromGladeData(data);
257 }
258
load_glade_file_(const char * fname)259 void UiBuilderImpl::load_glade_file_(const char *fname) {
260 intf->loadRackFromGladeFile(fname);
261 }
262
load(gx_engine::Plugin * p)263 bool UiBuilderImpl::load(gx_engine::Plugin *p) {
264 PluginDef *pd = p->get_pdef();
265 if (!(pd->flags & PGN_GUI) || !(pd->flags & gx_engine::PGNI_DYN_POSITION)) {
266 return false;
267 }
268 plugin_dict.add_plugin(*pluginlist, pd->id, "");
269 return true;
270 }
271
272 } /* end of gx_gui namespace */
273
274 /****************************************************************
275 ** class GxBuilder
276 */
277
278 // GList(GObject*) helper class must be defined in other namespace
279 namespace Glib { namespace Container_Helpers {
280 template <>
281 struct TypeTraits<GObject*> {
282 typedef GObject *CppType;
283 typedef GObject *CType;
284 typedef GObject *CTypeNonConst;
285
to_c_typeGlib::Container_Helpers::TypeTraits286 static CType to_c_type(CppType item) { return item; }
to_cpp_typeGlib::Container_Helpers::TypeTraits287 static CppType to_cpp_type(CType item) { return item; }
release_c_typeGlib::Container_Helpers::TypeTraits288 static void release_c_type(CType) {}
289 };
290 }}
291
292 namespace gx_gui {
293
294 bool GxBuilder::show_tooltips = true;
295
296 //static
create_from_file(const std::string & filename,gx_engine::GxMachineBase * pmach,const char * object_id,sigc::signal<void (bool)> * out_ctr)297 Glib::RefPtr<GxBuilder> GxBuilder::create_from_file(
298 const std::string& filename, gx_engine::GxMachineBase* pmach, const char* object_id, sigc::signal<void(bool)> *out_ctr) {
299 Glib::RefPtr<GxBuilder> builder = GxBuilder::create();
300 try {
301 if (object_id) {
302 builder->add_from_file(filename, object_id);
303 } else {
304 builder->add_from_file(filename);
305 }
306 } catch(const Glib::FileError& ex) {
307 gx_print_fatal("FileError", ex.what());
308 } catch(const Gtk::BuilderError& ex) {
309 gx_print_fatal("Builder Error", ex.what());
310 }
311 if (pmach) {
312 builder->fixup_controlparameters(*pmach, out_ctr);
313 }
314 return builder;
315 }
316
317 //static
create_from_file(const std::string & filename,gx_engine::GxMachineBase * pmach,const Glib::StringArrayHandle & object_ids,sigc::signal<void (bool)> * out_ctr)318 Glib::RefPtr<GxBuilder> GxBuilder::create_from_file(
319 const std::string& filename, gx_engine::GxMachineBase* pmach, const Glib::StringArrayHandle& object_ids, sigc::signal<void(bool)> *out_ctr) {
320 Glib::RefPtr<GxBuilder> builder = GxBuilder::create();
321 try {
322 builder->add_from_file(filename, object_ids);
323 } catch(const Glib::FileError& ex) {
324 gx_print_fatal("FileError", ex.what());
325 } catch(const Gtk::BuilderError& ex) {
326 gx_print_fatal("Builder Error", ex.what());
327 }
328 if (pmach) {
329 builder->fixup_controlparameters(*pmach, out_ctr);
330 }
331 return builder;
332 }
333
334 //static
create_from_string(const Glib::ustring & buffer,gx_engine::GxMachineBase * pmach,const char * object_id,sigc::signal<void (bool)> * out_ctr)335 Glib::RefPtr<GxBuilder> GxBuilder::create_from_string(
336 const Glib::ustring& buffer, gx_engine::GxMachineBase* pmach, const char* object_id, sigc::signal<void(bool)> *out_ctr) {
337 Glib::RefPtr<GxBuilder> builder = GxBuilder::create();
338 try {
339 if (object_id) {
340 builder->add_from_string(buffer, object_id);
341 } else {
342 builder->add_from_string(buffer);
343 }
344 } catch(const Gtk::BuilderError& ex) {
345 gx_print_fatal("Builder Error", ex.what());
346 }
347 if (pmach) {
348 builder->fixup_controlparameters(*pmach, out_ctr);
349 }
350 return builder;
351 }
352
353 //static
create_from_string(const Glib::ustring & buffer,gx_engine::GxMachineBase * pmach,const Glib::StringArrayHandle & object_ids,sigc::signal<void (bool)> * out_ctr)354 Glib::RefPtr<GxBuilder> GxBuilder::create_from_string(
355 const Glib::ustring& buffer, gx_engine::GxMachineBase* pmach, const Glib::StringArrayHandle& object_ids, sigc::signal<void(bool)> *out_ctr) {
356 Glib::RefPtr<GxBuilder> builder = GxBuilder::create();
357 try {
358 builder->add_from_string(buffer, object_ids);
359 } catch(const Gtk::BuilderError& ex) {
360 gx_print_fatal("Builder Error", ex.what());
361 }
362 if (pmach) {
363 builder->fixup_controlparameters(*pmach, out_ctr);
364 }
365 return builder;
366 }
367
get_first_window()368 Gtk::Window *GxBuilder::get_first_window() {
369 Glib::SListHandle<GObject*> objs = Glib::SListHandle<GObject*>(
370 gtk_builder_get_objects(gobj()), Glib::OWNERSHIP_DEEP);
371 for (Glib::SListHandle<GObject*>::iterator i = objs.begin(); i != objs.end(); ++i) {
372 if (g_type_is_a(G_OBJECT_TYPE(*i), GTK_TYPE_WINDOW)) {
373 return Glib::wrap(GTK_WINDOW(*i), false);
374 }
375 }
376 assert(false);
377 return 0;
378 }
379
get_cobject(const Glib::ustring & name)380 GObject* GxBuilder::get_cobject(const Glib::ustring& name)
381 {
382 GObject *cobject = gtk_builder_get_object (gobj(), name.c_str());
383 if(!cobject) {
384 g_critical("gtkmm: object `%s' not found in GtkBuilder file.", name.c_str());
385 return 0;
386 }
387 return cobject;
388 }
389
390 /*
391 ** GxBuilder::fixup_controlparameters + helper classes
392 */
393
check_get_parameter(gx_engine::GxMachineBase & machine,const std::string & id,Gtk::Widget * w)394 gx_engine::Parameter *check_get_parameter(
395 gx_engine::GxMachineBase& machine, const std::string& id, Gtk::Widget *w) {
396 if (!machine.parameter_hasId(id)) {
397 if (w) {
398 w->set_sensitive(0);
399 w->set_tooltip_text(id); // always shown
400 }
401 gx_print_warning(
402 "load dialog",
403 (boost::format("Parameter variable %1% not found") % id).str());
404 return nullptr;
405 }
406 return &machine.get_parameter(id);
407 }
408
check_get_float_parameter(gx_engine::GxMachineBase & machine,const std::string & id,Gtk::Widget * w)409 gx_engine::FloatParameter *check_get_float_parameter(
410 gx_engine::GxMachineBase& machine, const std::string& id, Gtk::Widget *w) {
411 gx_engine::Parameter *p = check_get_parameter(machine, id, w);
412 if (p) {
413 if (p->isFloat()) {
414 return &machine.get_parameter(id).getFloat();
415 }
416 gx_print_error(
417 "load dialog",
418 Glib::ustring::compose(
419 "Continuous Parameter variable %1: type not handled", id));
420 }
421 return nullptr;
422 }
423
424 /**
425 * uiElement: base class for adding signal connections to
426 * sigc::trackable derived objects
427 *
428 * uiElement derived class instances must be allocated with new() and
429 * will be deleted when the destructor of the sigc::trackable object
430 * is called. This removes all signal connections to member functions
431 * of the derived class instance (by uiElement itself being a
432 * sigc::trackable).
433 */
434 class uiElement: public sigc::trackable {
435 private:
436 static void *destroy(void *p);
437 protected:
uiElement(sigc::trackable * obj)438 uiElement(sigc::trackable *obj) { obj->add_destroy_notify_callback(this, destroy); }
~uiElement()439 virtual ~uiElement() {}
440 };
441
442 //static
destroy(void * p)443 void *uiElement::destroy(void *p) {
444 delete static_cast<uiElement*>(p);
445 return nullptr;
446 }
447
448 /**
449 * uiConnector: abstract base class
450 *
451 * does midi controller connection and output variable activation
452 *
453 * derived classes do signal connections to parameter variables
454 */
455 class uiConnector: public uiElement {
456 protected:
457 gx_engine::GxMachineBase& machine;
458 const std::string id;
459 void activate_output(bool state, Gtk::Widget *w);
460 void activate_enable_output(bool state, Gtk::Widget *w);
461 uiConnector(gx_engine::GxMachineBase& machine_, const gx_engine::Parameter& p,
462 Gtk::Widget *w, sigc::signal<void(bool)> *out_ctr, bool disable);
463 };
464
uiConnector(gx_engine::GxMachineBase & machine_,const gx_engine::Parameter & p,Gtk::Widget * w,sigc::signal<void (bool)> * out_ctr,bool disable)465 uiConnector::uiConnector(gx_engine::GxMachineBase& machine_, const gx_engine::Parameter& p,
466 Gtk::Widget *w, sigc::signal<void(bool)> *out_ctr, bool disable)
467 : uiElement(w), machine(machine_), id(p.id()) {
468 if (p.isControllable()) {
469 connect_midi_controller(w, id, machine);
470 }
471 if (p.isOutput()) {
472 if (out_ctr) {
473 if (disable) {
474 out_ctr->connect(
475 sigc::bind(
476 sigc::mem_fun(this, &uiConnector::activate_enable_output),
477 w));
478 } else {
479 out_ctr->connect(
480 sigc::bind(
481 sigc::mem_fun(this, &uiConnector::activate_output),
482 w));
483 }
484 }
485 }
486 }
487
activate_output(bool state,Gtk::Widget * w)488 void uiConnector::activate_output(bool state, Gtk::Widget *w) {
489 machine.set_update_parameter(w, id, state);
490 }
491
activate_enable_output(bool state,Gtk::Widget * w)492 void uiConnector::activate_enable_output(bool state, Gtk::Widget *w) {
493 machine.set_update_parameter(w, id, state);
494 w->set_sensitive(state);
495 }
496
497 /**
498 * uiSelector: signal connections for Gxw::Selector widgets
499 */
500 template <class T>
501 class uiSelector: public uiConnector {
502 protected:
503 Gtk::Range *rng;
504 void on_value_changed();
505 void set_value(T v);
506 uiSelector(gx_engine::GxMachineBase& machine, Gtk::Range *rng, const gx_engine::Parameter& p,
507 sigc::signal<void(bool)> *out_ctr, bool disable);
508 public:
create(gx_engine::GxMachineBase & machine,Gtk::Range * rng,const gx_engine::Parameter & p,sigc::signal<void (bool)> * out_ctr,bool disable)509 static uiSelector *create(
510 gx_engine::GxMachineBase& machine, Gtk::Range *rng, const gx_engine::Parameter& p,
511 sigc::signal<void(bool)> *out_ctr, bool disable) {
512 return new uiSelector(machine, rng, p, out_ctr, disable);
513 }
514 static uiSelector *create(
515 gx_engine::GxMachineBase& machine, Gtk::Range *rng, const std::string& id,
516 sigc::signal<void(bool)> *out_ctr, bool disable);
517 };
518
519 template <class T>
uiSelector(gx_engine::GxMachineBase & machine_,Gtk::Range * rng_,const gx_engine::Parameter & p,sigc::signal<void (bool)> * out_ctr,bool disable)520 uiSelector<T>::uiSelector(gx_engine::GxMachineBase& machine_, Gtk::Range *rng_,
521 const gx_engine::Parameter& p, sigc::signal<void(bool)> *out_ctr,
522 bool disable)
523 : uiConnector(machine_, p, rng_, out_ctr, disable), rng(rng_) {
524 set_value(machine.get_parameter_value<T>(id));
525 rng->signal_value_changed().connect(
526 sigc::mem_fun(*this, &uiSelector::on_value_changed));
527 machine.signal_parameter_value<T>(id).connect(
528 sigc::mem_fun(this, &uiSelector::set_value));
529 }
530
531 //static
532 template <class T>
create(gx_engine::GxMachineBase & machine_,Gtk::Range * rng_,const std::string & id_,sigc::signal<void (bool)> * out_ctr,bool disable)533 uiSelector<T> *uiSelector<T>::create(
534 gx_engine::GxMachineBase& machine_, Gtk::Range *rng_, const std::string& id_,
535 sigc::signal<void(bool)> *out_ctr, bool disable) {
536 auto p = check_get_parameter(machine_, id_, rng_);
537 if (!p) {
538 return nullptr;
539 }
540 return new uiSelector<T>(machine_, rng_, p, out_ctr, disable);
541 }
542
543 template <class T>
set_value(T v)544 void uiSelector<T>::set_value(T v) {
545 rng->set_value(v);
546 }
547
548 template<class T>
on_value_changed()549 void uiSelector<T>::on_value_changed() {
550 machine.set_parameter_value(id, static_cast<T>(rng->get_value()));
551 }
552
553 /**
554 * uiToggle: signal connections for Gxw::Switch and Gtk::ToggleButton widgets
555 */
556 template<class T>
557 class uiToggle: public uiConnector {
558 protected:
559 Gtk::ToggleButton* button;
560 void on_button_toggled();
561 void on_parameter_changed(T v);
562 uiToggle(gx_engine::GxMachineBase& machine, Gtk::ToggleButton *b,
563 const gx_engine::Parameter& p, sigc::signal<void(bool)> *out_ctr, bool disable);
564 public:
create(gx_engine::GxMachineBase & machine,Gtk::ToggleButton * b,const gx_engine::Parameter & p,sigc::signal<void (bool)> * out_ctr,bool disable)565 static uiToggle *create(gx_engine::GxMachineBase& machine, Gtk::ToggleButton *b,
566 const gx_engine::Parameter& p, sigc::signal<void(bool)> *out_ctr,
567 bool disable) {
568 return new uiToggle(machine, b, p, out_ctr, disable);
569 }
570 static uiToggle *create(gx_engine::GxMachineBase& machine, Gtk::ToggleButton *b,
571 const string& id, sigc::signal<void(bool)> *out_ctr, bool disable);
572 };
573
574 template<class T>
uiToggle(gx_engine::GxMachineBase & machine_,Gtk::ToggleButton * b,const gx_engine::Parameter & p,sigc::signal<void (bool)> * out_ctr,bool disable)575 uiToggle<T>::uiToggle(gx_engine::GxMachineBase& machine_, Gtk::ToggleButton *b,
576 const gx_engine::Parameter& p, sigc::signal<void(bool)> *out_ctr, bool disable)
577 : uiConnector(machine_, p, b, out_ctr, disable), button(b) {
578 button->set_active(machine.get_parameter_value<T>(id));
579 button->signal_toggled().connect(sigc::mem_fun(this, &uiToggle<T>::on_button_toggled));
580 machine.signal_parameter_value<T>(id).connect(
581 sigc::mem_fun(this, &uiToggle<T>::on_parameter_changed));
582 }
583
584 //static
585 template<class T>
create(gx_engine::GxMachineBase & machine_,Gtk::ToggleButton * b,const string & id_,sigc::signal<void (bool)> * out_ctr,bool disable)586 uiToggle<T> *create(gx_engine::GxMachineBase& machine_, Gtk::ToggleButton *b,
587 const string& id_, sigc::signal<void(bool)> *out_ctr, bool disable) {
588 auto p = check_get_parameter(machine_, id_, b);
589 if (!p) {
590 return nullptr;
591 }
592 return new uiToggle<T>(machine_, b, p, out_ctr, disable);
593 }
594
595 template<>
on_button_toggled()596 inline void uiToggle<float>::on_button_toggled() {
597 machine.set_parameter_value(id, static_cast<float>(button->get_active()));
598 }
599
600 template<>
on_button_toggled()601 inline void uiToggle<bool>::on_button_toggled() {
602 machine.set_parameter_value(id, button->get_active());
603 }
604
605 template<>
on_parameter_changed(float v)606 inline void uiToggle<float>::on_parameter_changed(float v) {
607 button->set_active(v != 0.0);
608 }
609
610 template<>
on_parameter_changed(bool v)611 inline void uiToggle<bool>::on_parameter_changed(bool v) {
612 button->set_active(v);
613 }
614
615 /**
616 * uiController: signal connections for output widgets (Gxw::Fastmeter, Gxw::PortDisplay)
617 */
618 class uiController: public uiConnector {
619 protected:
620 Gxw::ControlParameter *elem;
621 uiController(gx_engine::GxMachineBase& machine, const gx_engine::FloatParameter& p,
622 Gtk::Widget *w, sigc::signal<void(bool)> *out_ctr, bool disable);
623 public:
create(gx_engine::GxMachineBase & machine,const gx_engine::FloatParameter & p,Gtk::Widget * w,sigc::signal<void (bool)> * out_ctr,bool disable)624 static uiController *create(
625 gx_engine::GxMachineBase& machine, const gx_engine::FloatParameter& p,
626 Gtk::Widget *w, sigc::signal<void(bool)> *out_ctr, bool disable) {
627 return new uiController(machine, p, w, out_ctr, disable);
628 }
629 static uiController *create(
630 gx_engine::GxMachineBase& machine, const std::string& id,
631 Gtk::Widget *w, sigc::signal<void(bool)> *out_ctr, bool disable);
632 };
633
uiController(gx_engine::GxMachineBase & machine_,const gx_engine::FloatParameter & p,Gtk::Widget * w,sigc::signal<void (bool)> * out_ctr,bool disable)634 uiController::uiController(gx_engine::GxMachineBase& machine_, const gx_engine::FloatParameter& p,
635 Gtk::Widget *w, sigc::signal<void(bool)> *out_ctr, bool disable)
636 : uiConnector(machine_, p, w, out_ctr, disable),
637 elem(dynamic_cast<Gxw::ControlParameter*>(w)) {
638 elem->cp_configure(p.l_group(), p.l_name(), p.getLowerAsFloat(),
639 p.getUpperAsFloat(), p.is_log_display());
640 elem->cp_set_value(machine.get_parameter_value<float>(id));
641 machine.signal_parameter_value<float>(id).connect(
642 sigc::mem_fun(elem, &Gxw::ControlParameter::cp_set_value));
643 }
644
645 //static
create(gx_engine::GxMachineBase & machine_,const std::string & id_,Gtk::Widget * w,sigc::signal<void (bool)> * out_ctr,bool disable)646 uiController *uiController::create(
647 gx_engine::GxMachineBase& machine_, const std::string& id_,
648 Gtk::Widget *w, sigc::signal<void(bool)> *out_ctr, bool disable) {
649 gx_engine::FloatParameter *p = check_get_float_parameter(machine_, id_, w);
650 if (!p) {
651 return nullptr;
652 }
653 return new uiController(machine_, *p, w, out_ctr, disable);
654 }
655
656 /**
657 * uiAdjustment: signal connections to Gxw::Regler derived widgets
658 */
659 class uiAdjustment: public uiConnector {
660 private:
661 Gtk::Adjustment *adj;
662 bool log;
663 bool blocked;
664 private:
665 void changed();
666 void on_parameter_changed(float v);
667 protected:
668 uiAdjustment(gx_engine::GxMachineBase& machine_, const gx_engine::FloatParameter& p,
669 Gxw::Regler *regler, sigc::signal<void(bool)> *out_ctr, bool disable);
670 public:
create(gx_engine::GxMachineBase & machine_,const gx_engine::FloatParameter & p,Gxw::Regler * regler,sigc::signal<void (bool)> * out_ctr,bool disable)671 static uiAdjustment *create(
672 gx_engine::GxMachineBase& machine_, const gx_engine::FloatParameter& p,
673 Gxw::Regler *regler, sigc::signal<void(bool)> *out_ctr, bool disable) {
674 return new uiAdjustment(machine_, p, regler, out_ctr, disable);
675 }
676 static uiAdjustment *create(
677 gx_engine::GxMachineBase& machine_, const std::string& id,
678 Gxw::Regler *regler, sigc::signal<void(bool)> *out_ctr, bool disable);
679 };
680
changed()681 void uiAdjustment::changed() {
682 if (!blocked) {
683 float v = adj->get_value();
684 if (log) {
685 v = pow(10.0, v);
686 }
687 machine.set_parameter_value(id, v);
688 }
689 }
690
on_parameter_changed(float v)691 void uiAdjustment::on_parameter_changed(float v) {
692 blocked = true;
693 if (log) {
694 v = log10(v);
695 }
696 adj->set_value(v);
697 blocked = false;
698 }
699
uiAdjustment(gx_engine::GxMachineBase & machine_,const gx_engine::FloatParameter & p,Gxw::Regler * regler,sigc::signal<void (bool)> * out_ctr,bool disable)700 uiAdjustment::uiAdjustment(gx_engine::GxMachineBase& machine_, const gx_engine::FloatParameter& p,
701 Gxw::Regler *regler, sigc::signal<void(bool)> *out_ctr,
702 bool disable)
703 : uiConnector(machine_, p, regler, out_ctr, disable), adj(regler->get_adjustment().get()),
704 log(false), blocked(false) {
705 log = p.is_log_display();
706 float v = machine.get_parameter_value<float>(id);
707 if (log) {
708 v = log10(v);
709 double up = log10(p.getUpperAsFloat());
710 double step = log10(p.getStepAsFloat());
711 regler->cp_configure(p.l_group(), p.l_name(), log10(p.getLowerAsFloat()), up, step);
712 int prec = 0;
713 float d = log10((p.getStepAsFloat()-1)*p.getUpperAsFloat());
714 if (up > 0) {
715 prec = up;
716 if (d < 0) {
717 prec -= floor(d);
718 }
719 } else if (d < 0) {
720 prec = -floor(d);
721 }
722 regler->signal_format_value().connect(
723 sigc::bind(
724 sigc::ptr_fun(logarithmic_format_value),
725 prec));
726 regler->signal_input_value().connect(
727 sigc::ptr_fun(logarithmic_input_value));
728 regler->cp_set_value(log10(machine.get_parameter_value<float>(id)));
729 } else {
730 regler->cp_configure(p.l_group(), p.l_name(), p.getLowerAsFloat(),
731 p.getUpperAsFloat(), p.getStepAsFloat());
732 regler->cp_set_value(machine.get_parameter_value<float>(id));
733 }
734 adj->set_value(v);
735 machine.signal_parameter_value<float>(id).connect(
736 sigc::mem_fun(this, &uiAdjustment::on_parameter_changed));
737 adj->signal_value_changed().connect(
738 sigc::mem_fun(this, &uiAdjustment::changed));
739 }
740
741 //static
create(gx_engine::GxMachineBase & machine_,const std::string & id_,Gxw::Regler * regler,sigc::signal<void (bool)> * out_ctr,bool disable)742 uiAdjustment *uiAdjustment::create(
743 gx_engine::GxMachineBase& machine_, const std::string& id_, Gxw::Regler *regler,
744 sigc::signal<void(bool)> *out_ctr, bool disable) {
745 gx_engine::FloatParameter *p = check_get_float_parameter(machine_, id_, regler);
746 if (!p) {
747 return nullptr;
748 }
749 return new uiAdjustment(machine_, *p, regler, out_ctr, disable);
750 }
751
ui_connect_switch(gx_engine::GxMachineBase & machine,Gtk::ToggleButton * b,const gx_engine::Parameter & p,sigc::signal<void (bool)> * out_ctr,bool disable)752 void ui_connect_switch(
753 gx_engine::GxMachineBase& machine, Gtk::ToggleButton *b,
754 const gx_engine::Parameter& p, sigc::signal<void(bool)> *out_ctr, bool disable) {
755 auto cp = dynamic_cast<Gxw::ControlParameter*>(b);
756 if (cp) {
757 cp->cp_configure(p.l_group(), p.l_name(), 0, 0, 0);
758 }
759 if (p.isFloat()) {
760 b->set_active(machine.get_parameter_value<float>(p.id()));
761 uiToggle<float>::create(machine, b, p, out_ctr, false);
762 } else if (p.isBool()) {
763 b->set_active(machine.get_parameter_value<bool>(p.id()));
764 uiToggle<bool>::create(machine, b, p, out_ctr, false);
765 } else {
766 gx_print_error(
767 "load dialog",
768 Glib::ustring::compose("Switch Parameter variable %1: type not handled", p.id()));
769 }
770 }
771
772 /**
773 * parameter signal connections for Gxw::Switch and Gtk::ToggleButton
774 */
ui_connect_switch(gx_engine::GxMachineBase & machine,Gtk::ToggleButton * b,const std::string & id,sigc::signal<void (bool)> * out_ctr,bool disable)775 bool ui_connect_switch(
776 gx_engine::GxMachineBase& machine, Gtk::ToggleButton *b,
777 const std::string& id, sigc::signal<void(bool)> *out_ctr, bool disable) {
778 gx_engine::Parameter *p = check_get_parameter(machine, id, b);
779 if (!p) {
780 return false;
781 }
782 ui_connect_switch(machine, b, *p, out_ctr, disable);
783 return true;
784 }
785
make_switch_controller(gx_engine::GxMachineBase & machine,Gtk::Widget * w,const gx_engine::Parameter & p,sigc::signal<void (bool)> * out_ctr)786 static void make_switch_controller(gx_engine::GxMachineBase& machine,
787 Gtk::Widget *w,
788 const gx_engine::Parameter& p,
789 sigc::signal<void(bool)> *out_ctr) {
790 Gtk::ToggleButton *t = dynamic_cast<Gtk::ToggleButton*>(w);
791 if (!t) {
792 gx_print_error(
793 "load dialog",
794 Glib::ustring::compose(
795 "Switch Parameter variable %1: Widget must be ToggleButton based", p.id()));
796 return;
797 }
798 ui_connect_switch(machine, t, p, out_ctr, false);
799 }
800
make_continuous_controller(gx_engine::GxMachineBase & machine,Gtk::Widget * w,gx_engine::Parameter & p,sigc::signal<void (bool)> * out_ctr)801 static void make_continuous_controller(gx_engine::GxMachineBase& machine,
802 Gtk::Widget* w,
803 gx_engine::Parameter& p,
804 sigc::signal<void(bool)> *out_ctr) {
805 std::string var_id = p.id();
806 if (!p.isFloat()) {
807 // show error message
808 check_get_float_parameter(machine, var_id, w);
809 return;
810 }
811 gx_engine::FloatParameter &fp = p.getFloat();
812 if (dynamic_cast<Gxw::FastMeter*>(w) || dynamic_cast<Gxw::PortDisplay*>(w)) {
813 if (!p.isOutput()) {
814 gx_print_error(
815 "load dialog",
816 Glib::ustring::compose(
817 "Parameter variable %1: no output (register with o or O)", var_id));
818 return;
819 }
820 uiController::create(machine, fp, w, out_ctr, true);
821 return;
822 }
823 Gxw::Regler* r = dynamic_cast<Gxw::Regler*>(w);
824 if (!r) {
825 gx_print_error(
826 "load dialog",
827 Glib::ustring::compose(
828 "Continuous Parameter variable %1: Widget must be Regler based", var_id));
829 return;
830 }
831 uiAdjustment::create(machine, fp, r, out_ctr, false);
832 }
833
make_enum_controller(gx_engine::GxMachineBase & machine,Gtk::Widget * w,gx_engine::Parameter & p,sigc::signal<void (bool)> * out_ctr)834 static void make_enum_controller(gx_engine::GxMachineBase& machine,
835 Gtk::Widget *w,
836 gx_engine::Parameter& p,
837 sigc::signal<void(bool)> *out_ctr) {
838 Gxw::Selector *t = dynamic_cast<Gxw::Selector*>(w);
839 if (!t) {
840 gx_print_warning(
841 "load dialog",
842 Glib::ustring::compose("Enum Parameter variable %1: no Selector widget", p.id()));
843 make_continuous_controller(machine, w, p, out_ctr);
844 return;
845 }
846 Gtk::TreeModelColumn<Glib::ustring> label;
847 Gtk::TreeModelColumnRecord rec;
848 rec.add(label);
849 Glib::RefPtr<Gtk::ListStore> ls = Gtk::ListStore::create(rec);
850 for (const value_pair *vp = p.getValueNames(); vp->value_id; ++vp) {
851 ls->append()->set_value(0, Glib::ustring(p.value_label(*vp)));
852 }
853 t->set_model(ls);
854 t->cp_configure(p.l_group(), p.l_name(), p.getLowerAsFloat(), p.getUpperAsFloat(), 1.0);
855 if (p.isInt()) {
856 uiSelector<int>::create(machine, t, p, out_ctr, false);
857 } else if (p.isFloat()) {
858 uiSelector<float>::create(machine, t, p, out_ctr, false);
859 } else {
860 gx_print_error(
861 "load dialog",
862 Glib::ustring::compose("Enum Parameter variable %1: type not handled", p.id()));
863 }
864 }
865
gx_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer user_data)866 static gboolean gx_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode,
867 GtkTooltip *tooltip, gpointer user_data) {
868 if (!*static_cast<bool*>(user_data)) {
869 g_signal_stop_emission_by_name(widget, "query-tooltip");
870 return FALSE;
871 }
872 return FALSE;
873 }
874
connect_gx_tooltip_handler(GtkWidget * widget)875 void GxBuilder::connect_gx_tooltip_handler(GtkWidget *widget) {
876 g_signal_connect(widget, "query-tooltip", G_CALLBACK(gx_query_tooltip), &show_tooltips);
877 }
878
set_tooltip_text_connect_handler(GtkWidget * widget,const char * text)879 void GxBuilder::set_tooltip_text_connect_handler(GtkWidget *widget, const char *text) {
880 gtk_widget_set_tooltip_text(widget, text);
881 connect_gx_tooltip_handler(widget);
882 }
883
884 /**
885 * connect a widget w to the parameter id.
886 */
ui_connect(gx_engine::GxMachineBase & machine,Gtk::Widget * w,const std::string & id,sigc::signal<void (bool)> * out_ctr)887 bool ui_connect(gx_engine::GxMachineBase& machine, Gtk::Widget *w, const std::string& id,
888 sigc::signal<void(bool)> *out_ctr) {
889 auto wname = w->get_name();
890 if (wname.empty() || wname.substr(0,7) == "gtkmm__") {
891 string id_css = id;
892 boost::replace_all(id_css, ".", "-");
893 w->set_name(id_css);
894 }
895 gx_engine::Parameter *p = check_get_parameter(machine, id, w);
896 if (!p) {
897 return false;
898 }
899 if (!p->desc().empty()) {
900 GxBuilder::set_tooltip_text_connect_handler(*w, p->l_desc());
901 }
902 auto acc = w->get_accessible();
903 if (acc->get_description().empty()) {
904 acc->set_description(id);
905 }
906 if (acc->get_name().empty()) {
907 Glib::ustring nm = p->l_name();
908 if (nm.empty()) {
909 nm = id.substr(id.find_last_of(".")+1);
910 }
911 acc->set_name(nm);
912 }
913 switch (p->getControlType()) {
914 case gx_engine::Parameter::Continuous:
915 make_continuous_controller(machine, w, *p, out_ctr);
916 break;
917 case gx_engine::Parameter::Switch:
918 make_switch_controller(machine, w, *p, out_ctr);
919 break;
920 case gx_engine::Parameter::Enum:
921 make_enum_controller(machine, w, *p, out_ctr);
922 break;
923 default:
924 gx_print_error(
925 "load dialog",
926 Glib::ustring::compose("Parameter variable %1: type not handled", p->id()));
927 break;
928 }
929 return true;
930 }
931
fixup_controlparameters(gx_engine::GxMachineBase & machine,sigc::signal<void (bool)> * out_ctr)932 void GxBuilder::fixup_controlparameters(gx_engine::GxMachineBase& machine,
933 sigc::signal<void(bool)> *out_ctr) {
934 Glib::SListHandle<GObject*> objs = Glib::SListHandle<GObject*>(
935 gtk_builder_get_objects(gobj()), Glib::OWNERSHIP_DEEP);
936 for (Glib::SListHandle<GObject*>::iterator i = objs.begin(); i != objs.end(); ++i) {
937 const char *wname = nullptr;
938 GtkWidget *widget = nullptr;
939 if (g_type_is_a(G_OBJECT_TYPE(*i), GTK_TYPE_WIDGET)) {
940 const char *id = gtk_buildable_get_name(GTK_BUILDABLE(*i));
941 widget = GTK_WIDGET(*i);
942 wname = g_strstr_len(id, -1, ":");
943 if (wname) {
944 gtk_widget_set_name(widget, wname+1);
945 }
946 if (gtk_widget_get_has_tooltip(widget)) {
947 connect_gx_tooltip_handler(widget);
948 }
949 }
950 if (!g_type_is_a(G_OBJECT_TYPE(*i), GX_TYPE_CONTROL_PARAMETER)) {
951 continue;
952 }
953 assert(widget);
954 auto w = Glib::wrap(widget);
955 std::string v = dynamic_cast<Gxw::ControlParameter*>(w)->cp_get_var();
956 if (v.empty()) {
957 continue;
958 }
959 ui_connect(machine, w, v, out_ctr);
960 }
961 }
962
963 } /* end of gx_gui namespace */
964