1 /*
2 * Copyright (C) 2009, 2010 Hermann Meyer, James Warden
3 * Copyright (C) 2011 Pete Shorthose
4 * Copyright (C) 2012 Andreas Degert
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 * ---------------------------------------------------------------------------
20 *
21 * ----------------------------------------------------------------------------
22 */
23
24 #include <guitarix.h>
25 #include <boost/algorithm/string/replace.hpp>
26
27 /****************************************************************
28 ** class PluginUI
29 **
30 ** This class represents a rack unit. It refers to an engine
31 ** plugin. The user interface in the rack is loaded on demand.
32 **
33 ** It is responsible for reflecting any changes done to display
34 ** parameter variables (box_visible, flat/expanded format, ordering).
35 **
36 ** Registering with an GxUI is done in PluginDict.
37 **
38 ** When a preset load is in progress re-ordering is blocked.
39 ** MainWindow connects RackContainer::check_order() to
40 ** GxSettings::signal_selection_changed so that ordering will be done
41 ** when the load is finished.
42 **
43 */
44
PluginUI(PluginDict & plugin_dict_,const char * name,const Glib::ustring & tooltip_)45 PluginUI::PluginUI(PluginDict& plugin_dict_, const char *name,
46 const Glib::ustring& tooltip_)
47 : action(),
48 group(),
49 toolitem(),
50 rackbox(),
51 hidden(false),
52 active(false),
53 output_widget_state(),
54 output_widgets_active(false),
55 tooltip(tooltip_),
56 shortname(),
57 plugin(plugin_dict_.get_machine().pluginlist_lookup_plugin(name)),
58 plugin_dict(plugin_dict_) {
59 if (plugin->get_pdef()->description && tooltip.empty()) {
60 tooltip = plugin->get_pdef()->description;
61 }
62 plugin->get_pdef()->flags |= gx_engine::PGNI_UI_REG;
63 active = !has_gui();
64 }
65
~PluginUI()66 PluginUI::~PluginUI() {
67 output_widget_state.clear();
68 delete rackbox;
69 if (toolitem) {
70 if (group) {
71 group->remove(*toolitem);
72 }
73 delete toolitem;
74 }
75 plugin->get_pdef()->flags &= ~gx_engine::PGNI_UI_REG;
76 }
77
on_plugin_preset_popup()78 void PluginUI::on_plugin_preset_popup() {
79 plugin_dict.plugin_preset_popup(plugin->get_pdef());
80 }
81
is_registered(gx_engine::GxMachineBase & m,const char * name)82 bool PluginUI::is_registered(gx_engine::GxMachineBase& m, const char *name) {
83 return m.pluginlist_lookup_plugin(name)->get_pdef()->flags & gx_engine::PGNI_UI_REG;
84 }
85
compress(bool state)86 void PluginUI::compress(bool state) {
87 plugin->set_plug_visible(state);
88 if (rackbox) {
89 if (rackbox->can_compress()) {
90 rackbox->swtch(state);
91 }
92 }
93 }
94
set_action(Glib::RefPtr<ToggleAction> & act)95 void PluginUI::set_action(Glib::RefPtr<ToggleAction>& act)
96 {
97 action = act;
98 action->signal_toggled().connect(sigc::mem_fun(*this, &PluginUI::on_action_toggled));
99 }
100
on_action_toggled()101 void PluginUI::on_action_toggled() {
102 if (rackbox && action->get_active() == active) {
103 return;
104 }
105 if (action->get_active()) {
106 plugin_dict.activate(get_id(), "", true);
107 } else {
108 plugin_dict.deactivate(get_id(), true);
109 }
110 }
111
hide(bool animate)112 void PluginUI::hide(bool animate) {
113 rackbox->display(false, animate);
114 }
115
show(bool animate)116 void PluginUI::show(bool animate) {
117 hidden = plugin_dict.get_plugins_hidden();
118 if (hidden) {
119 rackbox->hide();
120 } else {
121 rackbox->display(true, animate);
122 }
123 }
124
activate(bool animate,const string & before)125 void PluginUI::activate(bool animate, const string& before) {
126 if (!has_gui() || active) {
127 return;
128 }
129 active = true;
130 hidden = plugin_dict.get_plugins_hidden();
131 toolitem->hide();
132 bool plug = plugin->get_plug_visible();
133 if (rackbox) {
134 rackbox->swtch(plug);
135 } else {
136 rackbox = plugin_dict.add_rackbox(*this, plug, -1, animate);
137 }
138 plugin->set_box_visible(true);
139 rackbox->set_config_mode(plugin_dict.get_config_mode());
140 set_update_state(get_update_cond());
141 if (hidden) {
142 rackbox->display(false, false);
143 } else {
144 rackbox->display(true, animate);
145 }
146 set_active(true);
147 }
148
deactivate(bool animate)149 void PluginUI::deactivate(bool animate) {
150 if (!has_gui() || !active) {
151 return;
152 }
153 active = false;
154 plugin->set_box_visible(false);
155 toolitem->show();
156 hide(animate);
157 set_active(false);
158 }
159
set_config_mode(bool state)160 void PluginUI::set_config_mode(bool state) {
161 if (active) {
162 on_state_change();
163 rackbox->set_config_mode(state);
164 }
165 }
166
update_rackbox()167 void PluginUI::update_rackbox() {
168 if (!rackbox) {
169 return;
170 }
171 bool reorder = true;
172 RackContainer *container = NULL;
173 string before;
174 if (!plugin->get_box_visible()) {
175 delete rackbox;
176 rackbox = 0;
177 reorder = false;
178 } else {
179 container = rackbox->get_parent();
180 RackContainer::rackbox_list l = container->get_children();
181 for (RackContainer::rackbox_list::iterator i = l.begin(); i != l.end(); ++i) {
182 if (*i == rackbox) {
183 if (++i != l.end()) {
184 before = (*i)->get_id();
185 }
186 break;
187 }
188 }
189 if (rackbox) {
190 hide(false);
191 delete rackbox;
192 rackbox = 0;
193 }
194 }
195 rackbox = plugin_dict.add_rackbox(*this, plugin->get_plug_visible(), -1, false);
196 show(false);
197 if (reorder) container->reorder(get_id(), before);
198 }
199
plugins_by_name_less(PluginUI * a,PluginUI * b)200 bool plugins_by_name_less(PluginUI *a, PluginUI *b) {
201 int res = a->get_type() - b->get_type();
202 if (res == 0) {
203 gchar *an = g_utf8_casefold(a->get_shortname(), 1);
204 gchar *bn = g_utf8_casefold(b->get_shortname(), 1);
205 res = g_utf8_collate(an, bn);
206 g_free(an);
207 g_free(bn);
208 }
209 return res < 0;
210 }
211
on_rack_handle_press(GdkEventButton * ev)212 bool PluginUI::on_rack_handle_press(GdkEventButton* ev) {
213 if (ev->type == GDK_2BUTTON_PRESS && ev->button == 1) {
214 plugin_dict.deactivate(get_id(), true);
215 group->set_collapsed(false);
216 return true;
217 }
218 return false;
219 }
220
on_ti_drag_begin(const Glib::RefPtr<Gdk::DragContext> & context)221 void PluginUI::on_ti_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context) {
222 plugin_dict.drag_icon = new DragIcon(*this, context, plugin_dict.get_options());
223 }
224
on_ti_drag_data_get(const Glib::RefPtr<Gdk::DragContext> & context,Gtk::SelectionData & selection,int info,int timestamp)225 void PluginUI::on_ti_drag_data_get(const Glib::RefPtr<Gdk::DragContext>& context, Gtk::SelectionData& selection, int info, int timestamp) {
226 selection.set(*context->list_targets().begin(), get_id());
227 }
228
on_ti_drag_data_delete(const Glib::RefPtr<Gdk::DragContext> & context)229 void PluginUI::on_ti_drag_data_delete(const Glib::RefPtr<Gdk::DragContext>& context) {
230 toolitem->hide();
231 }
232
on_ti_button_press(GdkEventButton * ev)233 bool PluginUI::on_ti_button_press(GdkEventButton *ev) {
234 if (ev->type == GDK_2BUTTON_PRESS) {
235 plugin_dict.activate(get_id(), "", true);
236 return true;
237 }
238 return false;
239 }
240
on_ti_drag_end(const Glib::RefPtr<Gdk::DragContext> & context)241 void PluginUI::on_ti_drag_end(const Glib::RefPtr<Gdk::DragContext>& context) {
242 if (plugin_dict.drag_icon) {
243 delete plugin_dict.drag_icon;
244 plugin_dict.drag_icon = nullptr;
245 }
246 }
247
on_my_leave_out(GdkEventCrossing * focus)248 bool PluginUI::on_my_leave_out(GdkEventCrossing *focus) {
249 Glib::RefPtr<Gdk::Window> wind = toolitem->get_window();
250 wind->set_cursor();
251 return true;
252 }
253
on_my_enter_in(GdkEventCrossing * focus)254 bool PluginUI::on_my_enter_in(GdkEventCrossing *focus) {
255 Glib::RefPtr<Gdk::Window> wind = toolitem->get_window();
256 Glib::RefPtr<Gdk::Display> disp = toolitem->get_display();
257 Glib::RefPtr<Gdk::Cursor> cursor(Gdk::Cursor::create(disp, Gdk::HAND1));
258 wind->set_cursor(cursor);
259 return true;
260 }
261
get_displayname(bool useshort) const262 Glib::ustring PluginUI::get_displayname(bool useshort) const {
263 Glib::ustring name = useshort ? get_shortname() : get_name();
264 if (get_type() == PLUGIN_TYPE_STEREO) {
265 name = "◗◖ " + name; //♾⚮⦅◗◖⦆⚭ ⧓ Ꝏꝏ ⦅◉⦆● ▷◁ ▶◀
266 }
267 if (plugin->get_pdef()->flags & gx_engine::PGNI_IS_LV2) {
268 name += " (LV2)";
269 } else if (plugin->get_pdef()->flags & gx_engine::PGNI_IS_LADSPA) {
270 name += " (LADSPA)";
271 }
272 return name;
273 }
274
add_toolitem(Gtk::ToolItemGroup * gw)275 void PluginUI::add_toolitem(Gtk::ToolItemGroup *gw) {
276 Gtk::ToolItem *tb = new Gtk::ToolItem();
277 tb->set_use_drag_window(true);
278 tb->signal_drag_begin().connect(sigc::mem_fun(*this, &PluginUI::on_ti_drag_begin));
279 tb->signal_drag_end().connect(sigc::mem_fun(*this, &PluginUI::on_ti_drag_end));
280 tb->signal_drag_data_delete().connect(sigc::mem_fun(*this, &PluginUI::on_ti_drag_data_delete));
281 tb->signal_button_press_event().connect(sigc::mem_fun(*this, &PluginUI::on_ti_button_press));
282 tb->add_events(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
283 tb->signal_leave_notify_event().connect(sigc::mem_fun(*this, &PluginUI::on_my_leave_out));
284 tb->signal_enter_notify_event().connect(sigc::mem_fun(*this, &PluginUI::on_my_enter_in));
285 std::vector<Gtk::TargetEntry> listTargets;
286 if (get_type() == PLUGIN_TYPE_MONO) {
287 listTargets.push_back(Gtk::TargetEntry("application/x-gtk-tool-palette-item-mono", Gtk::TARGET_SAME_APP, 0));
288 } else {
289 listTargets.push_back(Gtk::TargetEntry("application/x-gtk-tool-palette-item-stereo", Gtk::TARGET_SAME_APP, 0));
290 }
291 tb->drag_source_set(listTargets, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE);
292 tb->signal_drag_data_get().connect(sigc::mem_fun(*this, &PluginUI::on_ti_drag_data_get));
293 Gtk::Label *img = new Gtk::Label(get_displayname(true));
294 img->set_xalign(0);
295 img->set_halign(Gtk::ALIGN_END);
296 if (!tooltip.empty()) {
297 gx_gui::GxBuilder::set_tooltip_text_connect_handler(*img, tooltip);
298 }
299 tb->add(*manage(img));
300 tb->show_all();
301 toolitem = tb;
302 gw->add(*manage(tb));
303 group = gw;
304 }
305
get_update_cond()306 bool PluginUI::get_update_cond() {
307 return active && !plugin_dict.get_config_mode() && plugin->get_on_off() /*&& !plugin->get_plug_visible()*/;
308 }
309
set_update_state(bool state)310 void PluginUI::set_update_state(bool state) {
311 output_widgets_active = state;
312 output_widget_state(state);
313 }
314
on_state_change()315 void PluginUI::on_state_change() {
316 int i = get_update_cond();
317 if (i != output_widgets_active) {
318 set_update_state(i);
319 }
320 }
321
322 // define if memory leaks in atk, pango, etc. are closed (valgrind --leak-check=full guitarix)
323 //#define LEAKS_OK
324
dispose_rackbox()325 void PluginUI::dispose_rackbox() {
326 if (plugin->get_box_visible()) {
327 rackbox->hide(); // dnd operation, just hide
328 } else {
329 #ifdef LEAKS_OK
330 delete rackbox;
331 rackbox = nullptr;
332 #else
333 rackbox->hide();
334 #endif
335 }
336 }
337
animate_vanish()338 bool PluginUI::animate_vanish() {
339 rackbox->anim_height -= rackbox->anim_step;
340 if (rackbox->anim_height > 0) {
341 rackbox->set_size_request(-1, rackbox->anim_height);
342 return true;
343 }
344 rackbox->set_visibility(true);
345 rackbox->set_size_request(-1,-1);
346 dispose_rackbox();
347 return false;
348 }
349
350 #define ANIMATE_STEPS 5
351 #define ANIMATE_TIME 20
352 #define AUTOSCROLL_TIMEOUT 50
353
remove(bool animate)354 void PluginUI::remove(bool animate) {
355 if (!animate || !plugin_dict.use_animations()) {
356 dispose_rackbox();
357 } else {
358 if (rackbox->anim_tag.connected()) {
359 rackbox->anim_tag.disconnect();
360 rackbox->set_size_request(-1,-1);
361 rackbox->show();
362 }
363 gint min_height, natural_height;
364 rackbox->get_preferred_height(min_height, natural_height);
365 rackbox->anim_height = min_height;
366 rackbox->set_size_request(-1, rackbox->anim_height);
367 rackbox->set_visibility(false);
368 rackbox->anim_step = rackbox->anim_height / ANIMATE_STEPS;
369 rackbox->anim_tag = Glib::signal_timeout().connect(
370 sigc::mem_fun(*this, &PluginUI::animate_vanish),
371 ANIMATE_TIME);
372 }
373 }
374
375
376 /****************************************************************
377 ** class PluginDict
378 */
379
PluginDict(gx_engine::GxMachineBase & machine_,gx_system::CmdlineOptions & options_,Gtk::ToolPalette & toolpalette_,gx_gui::StackBoxBuilder & boxbuilder_,UIManager & uimanager_)380 PluginDict::PluginDict(gx_engine::GxMachineBase& machine_, gx_system::CmdlineOptions& options_,
381 Gtk::ToolPalette& toolpalette_, gx_gui::StackBoxBuilder& boxbuilder_,
382 UIManager& uimanager_)
383 : monorackcontainer(*this),
384 stereorackcontainer(*this),
385 groupmap(),
386 monotargets(),
387 stereotargets(),
388 boxbuilder(boxbuilder_),
389 machine(machine_),
390 options(options_),
391 toolpalette(toolpalette_),
392 uimanager(uimanager_),
393 config_mode(false),
394 plugins_hidden(false),
395 drag_icon(0) {
396 std::vector<Gtk::TargetEntry> listTargets;
397 // the tool palette accepts drags from the racks
398 listTargets.push_back(Gtk::TargetEntry("application/x-guitarix-mono", Gtk::TARGET_SAME_APP, 0));
399 listTargets.push_back(Gtk::TargetEntry("application/x-guitarix-stereo", Gtk::TARGET_SAME_APP, 1));
400 toolpalette.drag_dest_set(listTargets, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_MOVE);
401 toolpalette.signal_drag_data_received().connect(sigc::mem_fun(*this, &PluginDict::on_tp_drag_data_received));
402 // the racks additionally accept drags from the tool palette
403 listTargets.push_back(Gtk::TargetEntry("application/x-gtk-tool-palette-item-mono", Gtk::TARGET_SAME_APP, 2));
404 listTargets.push_back(Gtk::TargetEntry("application/x-gtk-tool-palette-item-stereo", Gtk::TARGET_SAME_APP, 3));
405 monotargets.push_back("application/x-guitarix-mono");
406 monotargets.push_back("application/x-gtk-tool-palette-item-mono");
407 stereotargets.push_back("application/x-guitarix-stereo");
408 stereotargets.push_back("application/x-gtk-tool-palette-item-stereo");
409 monorackcontainer.set_list_targets(listTargets, monotargets, stereotargets);
410 stereorackcontainer.set_list_targets(listTargets, stereotargets, monotargets);
411 machine.signal_selection_changed().connect(
412 sigc::mem_fun0(this, &PluginDict::check_order));
413 machine.signal_rack_unit_order_changed().connect(
414 sigc::mem_fun(this, &PluginDict::unit_order_changed));
415 machine.signal_plugin_changed().connect(
416 sigc::mem_fun(this, &PluginDict::on_plugin_changed));
417 fill_pluginlist();
418 }
419
activate(const string & id,const string & before,bool animate)420 PluginUI *PluginDict::activate(const string& id, const string& before, bool animate) {
421 PluginUI *p = at(id);
422 if (!p->has_gui() || p->active) {
423 return p;
424 }
425 p->plugin->set_plug_visible(false);
426 p->activate(animate, before);
427 machine.insert_rack_unit(id, before, p->get_type());
428 return p;
429 }
430
deactivate(const string & id,bool animate)431 PluginUI *PluginDict::deactivate(const string& id, bool animate) {
432 PluginUI *p = at(id);
433 if (!p->has_gui() || !p->active) {
434 return p;
435 }
436 p->plugin->set_on_off(false);
437 p->deactivate(animate);
438 machine.remove_rack_unit(id, p->get_type());
439 return p;
440 }
441
stop_at(RackContainer * container,double off,double step_size,double page_size)442 double PluginDict::stop_at(RackContainer *container, double off, double step_size, double page_size) {
443 if (container == &monorackcontainer) {
444 return stereorackcontainer.stop_at_bottom(off, step_size, page_size);
445 } else {
446 assert(container == &stereorackcontainer);
447 return monorackcontainer.stop_at_top(off, step_size);
448 }
449 }
450
check_order()451 void PluginDict::check_order() {
452 check_order(PLUGIN_TYPE_MONO, false);
453 check_order(PLUGIN_TYPE_STEREO, false);
454 }
455
check_order(PluginType tp,bool animate)456 void PluginDict::check_order(PluginType tp, bool animate) {
457 RackContainer& container = (tp == PLUGIN_TYPE_STEREO) ? stereorackcontainer : monorackcontainer;
458 const std::vector<std::string> ol = machine.get_rack_unit_order(tp);
459 bool in_order = true;
460 int pos = 0;
461 unsigned int post_pre = 1;
462 bool need_renumber = false;
463 std::set<std::string> unit_set(ol.begin(), ol.end());
464 RackContainer::rackbox_list l = container.get_children();
465 std::vector<std::string>::const_iterator oi = ol.begin();
466 for (RackContainer::rackbox_list::iterator c = l.begin(); c != l.end(); ++c) {
467 string id = (*c)->get_id();
468 PluginUI *p = at(id);
469 if (!p->active) {
470 continue;
471 }
472 if (unit_set.find(id) == unit_set.end()) {
473 deactivate(id, animate);
474 continue;
475 }
476 if (!in_order) {
477 continue;
478 }
479 if (oi == ol.end()) {
480 in_order = false;
481 continue;
482 }
483 if (*oi != id) {
484 in_order = false;
485 continue;
486 }
487 if (id == "ampstack") {
488 pos = 0;
489 post_pre = 0;
490 continue;
491 }
492 if (!need_renumber && !(*c)->compare_position(pos, post_pre)) {
493 need_renumber = true;
494 }
495 ++oi;
496 }
497 if (oi != ol.end()) {
498 in_order = false;
499 }
500 if (!in_order) {
501 int n = 0;
502 for (std::vector<std::string>::const_iterator oi = ol.begin(); oi != ol.end(); ++oi) {
503 PluginUI *p = at(*oi);
504 p->activate(animate, "");
505 if (p->rackbox) {
506 container.reorder_child(*p->rackbox, n++);
507 }
508 }
509 }
510 if (!in_order || need_renumber) {
511 container.renumber();
512 }
513 container.set_child_count(ol.size());
514 }
515
unit_order_changed(bool stereo)516 void PluginDict::unit_order_changed(bool stereo) {
517 check_order(stereo ? PLUGIN_TYPE_STEREO : PLUGIN_TYPE_MONO, true);
518 }
519
reorder(RackContainer * container,const std::string & name,const std::string & before)520 void PluginDict::reorder(RackContainer *container, const std::string& name, const std::string& before) {
521 PluginType tp = (container == &monorackcontainer ? PLUGIN_TYPE_MONO : PLUGIN_TYPE_STEREO);
522 machine.insert_rack_unit(name, before, tp);
523 }
524
add(PluginUI * p)525 void PluginDict::add(PluginUI *p) {
526 insert(pair<std::string, PluginUI*>(p->get_id(), p));
527 }
528
remove(PluginUI * p)529 void PluginDict::remove(PluginUI *p) {
530 std::map<std::string, PluginUI*>::iterator i = find(p->get_id());
531 assert(i != end());
532 erase(i);
533 }
534
cleanup()535 void PluginDict::cleanup() {
536 for (std::map<std::string, PluginUI*>::iterator i = begin(); i != end(); ++i) {
537 delete i->second;
538 }
539 for (std::map<Glib::ustring, Gtk::ToolItemGroup*>::iterator i = groupmap.begin(); i != groupmap.end(); ++i) {
540 delete i->second;
541 }
542 clear();
543 }
544
~PluginDict()545 PluginDict::~PluginDict() {
546 cleanup();
547 }
548
compress(bool state)549 void PluginDict::compress(bool state) {
550 for (std::map<std::string, PluginUI*>::iterator i = begin(); i != end(); ++i) {
551 i->second->compress(state);
552 }
553 }
554
set_config_mode(bool state)555 void PluginDict::set_config_mode(bool state) {
556 config_mode = state;
557 for (std::map<std::string, PluginUI*>::iterator i = begin(); i != end(); ++i) {
558 i->second->set_config_mode(state);
559 }
560 }
561
show_entries()562 void PluginDict::show_entries() {
563 plugins_hidden = false;
564 for (std::map<std::string, PluginUI*>::iterator i = begin(); i != end(); ++i) {
565 i->second->hidden = false;
566 if (i->second->active) {
567 RackBox *r = i->second->rackbox;
568 if (r) {
569 r->show();
570 }
571 }
572 }
573 }
574
hide_entries()575 void PluginDict::hide_entries() {
576 plugins_hidden = true;
577 for (std::map<std::string, PluginUI*>::iterator i = begin(); i != end(); ++i) {
578 i->second->hidden = true;
579 RackBox *r = i->second->rackbox;
580 if (r) {
581 if (r->can_compress()) {
582 r->hide();
583 }
584 }
585 }
586 }
587
on_tp_drag_data_received(const Glib::RefPtr<Gdk::DragContext> & context,int x,int y,const Gtk::SelectionData & data,int info,int timestamp)588 void PluginDict::on_tp_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, int info, int timestamp) {
589 Glib::ustring id = data.get_data_as_string();
590 PluginUI *p = deactivate(id, false);
591 p->group->set_collapsed(false);
592 }
593
add_plugin(std::vector<PluginUI * > & p,const char * id,const Glib::ustring & tooltip)594 void PluginDict::add_plugin(std::vector<PluginUI*>& p, const char *id, const Glib::ustring& tooltip) {
595 if (PluginUI::is_registered(machine, id)) {
596 return;
597 }
598 p.push_back(new PluginUI(*this, id, tooltip));
599 }
600
add_bare(const char * id,Gtk::Container * box)601 void PluginDict::add_bare(const char * id, Gtk::Container *box) {
602 PluginUI *plugin = new PluginUI(*this, id);
603 add(plugin);
604 plugin->rackbox = add_rackbox_internal(*plugin, 0, 0, false, -1, false, box);
605 }
606
add_rackbox_internal(PluginUI & plugin,Gtk::Widget * mainwidget,Gtk::Widget * miniwidget,bool mini,int pos,bool animate,Gtk::Widget * bare)607 RackBox *PluginDict::add_rackbox_internal(PluginUI& plugin, Gtk::Widget *mainwidget, Gtk::Widget *miniwidget,
608 bool mini, int pos, bool animate, Gtk::Widget *bare) {
609 RackBox *r = new RackBox(plugin, *this, bare);
610 r->swtch(mini);
611 r->pack(mainwidget, miniwidget);
612 if (plugin.get_type() == PLUGIN_TYPE_MONO) {
613 monorackcontainer.add(*manage(r), pos);
614 } else {
615 stereorackcontainer.add(*manage(r), pos);
616 }
617 if (animate) {
618 r->animate_insert();
619 }
620 return r;
621 }
622
add_rackbox(PluginUI & pl,bool mini,int pos,bool animate)623 RackBox *PluginDict::add_rackbox(PluginUI& pl, bool mini, int pos, bool animate) {
624 gx_gui::UiBuilderImpl builder(this, &boxbuilder, nullptr, &pl);
625 Gtk::Widget *mainwidget = 0;
626 Gtk::Widget *miniwidget = 0;
627 if (machine.load_unit(builder, pl.plugin->get_pdef())) {
628 boxbuilder.fetch(mainwidget, miniwidget);
629 }
630 return add_rackbox_internal(pl, mainwidget, miniwidget, mini, pos, animate);
631 }
632
plugin_preset_popup(const PluginDef * pdef)633 void PluginDict::plugin_preset_popup(const PluginDef *pdef) {
634 new PluginPresetPopup(pdef, machine);
635 }
636
plugin_preset_popup(const PluginDef * pdef,const Glib::ustring & name)637 void PluginDict::plugin_preset_popup(const PluginDef *pdef, const Glib::ustring& name) {
638 new PluginPresetPopup(pdef, machine, name);
639 }
640
pluginlist_append(std::vector<PluginUI * > & p)641 void PluginDict::pluginlist_append(std::vector<PluginUI*>& p) {
642 gx_gui::UiBuilderImpl builder(this, &boxbuilder, &p, nullptr);
643 machine.pluginlist_append_rack(builder);
644 }
645
on_plugin_changed(gx_engine::Plugin * pl,gx_engine::PluginChange::pc c)646 void PluginDict::on_plugin_changed(gx_engine::Plugin *pl, gx_engine::PluginChange::pc c) {
647 if (!pl) { // end of update sequence
648 return;
649 }
650 if (c == gx_engine::PluginChange::add) {
651 register_plugin(new PluginUI(*this, pl->get_pdef()->id, ""));
652 return;
653 }
654 PluginUI *pui = at(pl->get_pdef()->id);
655 if (c == gx_engine::PluginChange::remove) {
656 Glib::ustring actname = pui->get_action()->get_name();
657 remove_plugin_menu_entry(pui);
658 uimanager.get_action_group()->remove(actname);
659 machine.remove_rack_unit(pui->get_id(), pui->get_type());
660 std::string group_id = pui->get_category();
661 remove(pui);
662 delete pui;
663 Gtk::ToolItemGroup * group = groupmap[group_id];
664 if (group->get_n_items() == 0) {
665 // removal is optional since empty toolitem groups and
666 // menus are already hidden.
667 uimanager.remove_item(category_id(pui->get_category(), false));
668 uimanager.remove_item(category_id(pui->get_category(), true));
669 groupmap.erase(group_id);
670 delete group;
671 }
672 } else {
673 assert(c == gx_engine::PluginChange::update ||
674 c == gx_engine::PluginChange::update_category);
675 bool state = pui->plugin->get_on_off();
676 pui->update_rackbox();
677 pui->plugin->set_on_off(state);
678 if (c == gx_engine::PluginChange::update_category) {
679 pui->group = add_plugin_category(pui->get_category());
680 // if the toolitem group becomes empty, it will be hidden automatically
681 pui->toolitem->reparent(*pui->group);
682 remove_plugin_menu_entry(pui);
683 add_plugin_menu_entry(pui);
684 }
685 }
686 }
687
category_id(const std::string & group,bool stereo)688 Glib::ustring PluginDict::category_id(const std::string& group, bool stereo) {
689 const char *tp = stereo ? "Stereo" : "Mono";
690 std::string group_id = group;
691 boost::replace_all(group_id, " ", ""); // FIXME: replace all invalid chars
692 return Glib::ustring::compose("Plugin%1Category_%2", tp, group_id);
693 }
694
add_plugin_category(const char * group,bool collapse)695 Gtk::ToolItemGroup *PluginDict::add_plugin_category(const char *group, bool collapse) {
696 std::map<Glib::ustring, Gtk::ToolItemGroup*>::iterator it = groupmap.find(group);
697 if (it != groupmap.end()) {
698 return it->second;
699 }
700 // add category menus (mono and stereo)
701 Glib::ustring display = gettext(group);
702 Glib::RefPtr<Gio::MenuItem> item;
703 Glib::ustring action;
704 action = category_id(group, false);
705 item = Gio::MenuItem::create(display, action);
706 item->set_submenu(Gio::Menu::create());
707 uimanager.get_linked_menu("MonoPlugins")->append_item(item);
708 uimanager.find_item(action)->hide();
709 action = category_id(group, true);
710 item = Gio::MenuItem::create(display, action);
711 item->set_submenu(Gio::Menu::create());
712 uimanager.get_linked_menu("StereoPlugins")->append_item(item);
713 uimanager.find_item(action)->hide();
714 // toolitem group
715 Gtk::ToolItemGroup *gw = new Gtk::ToolItemGroup(gettext(group));
716 groupmap[group] = gw;
717 gw->set_collapsed(collapse);
718 toolpalette.add(*manage(gw));
719 toolpalette.set_exclusive(*gw, true);
720 toolpalette.set_expand(*gw, true);
721 // make groups label left aligned (not settable in CSS)
722 gw->get_label_widget()->set_halign(Gtk::ALIGN_START);
723 return gw;
724 }
725
add_plugin_menu_entry(PluginUI * pui)726 Glib::ustring PluginDict::add_plugin_menu_entry(PluginUI *pui) {
727 Glib::ustring cat_id = category_id(
728 pui->get_category(), pui->get_type() == PLUGIN_TYPE_STEREO);
729 Glib::RefPtr<Gio::Menu> menu = uimanager.get_linked_menu(cat_id);
730 uimanager.find_item(cat_id)->show();
731 Glib::ustring actname = Glib::ustring::compose("Plugin_%1", pui->get_id());
732 auto a = Gio::MenuItem::create(pui->get_name(), actname);
733 menu->append_item(a);
734 return actname;
735 }
736
remove_plugin_menu_entry(PluginUI * pui)737 void PluginDict::remove_plugin_menu_entry(PluginUI *pui) {
738 Glib::ustring action = pui->get_action()->get_name();
739 auto *menu = dynamic_cast<Gtk::Menu*>(uimanager.find_item(action)->get_parent());
740 uimanager.remove_item(action);
741 if (menu->get_children().size() == 0) {
742 menu->get_attach_widget()->hide();
743 }
744 }
745
register_plugin(PluginUI * pui)746 void PluginDict::register_plugin(PluginUI *pui) {
747 add(pui);
748 Gtk::ToolItemGroup *gw = add_plugin_category(pui->get_category());
749 Glib::ustring actionname = add_plugin_menu_entry(pui);
750 pui->add_toolitem(gw);
751 Glib::RefPtr<ToggleAction> act = uimanager.add_toggle_action(
752 actionname, pui->plugin->get_box_visible());
753 pui->set_action(act);
754 if (pui->plugin->get_box_visible()) {
755 act->set_active(true);
756 }
757 }
758
759 class JConvPluginUI: public PluginUI {
760 private:
761 virtual void on_plugin_preset_popup();
762 public:
JConvPluginUI(PluginDict & plugin_dict,const char * id,const Glib::ustring & tooltip="")763 JConvPluginUI(PluginDict& plugin_dict, const char* id,
764 const Glib::ustring& tooltip="")
765 : PluginUI(plugin_dict, id, tooltip) {
766 }
767 };
768
on_plugin_preset_popup()769 void JConvPluginUI::on_plugin_preset_popup() {
770 gx_engine::JConvParameter *jcp = dynamic_cast<gx_engine::JConvParameter*>(
771 &plugin_dict.get_machine().get_parameter(std::string(get_id())+".convolver"));
772 assert(jcp);
773 Glib::ustring name = jcp->get_value().getIRFile();
774 Glib::ustring::size_type n = name.find_last_of('.');
775 if (n != Glib::ustring::npos) {
776 name.erase(n);
777 }
778 plugin_dict.plugin_preset_popup(plugin->get_pdef(), name);
779 }
780
fill_pluginlist()781 void PluginDict::fill_pluginlist() {
782 // define order of categories by registering
783 // them first
784 add_plugin_category(N_("Tone Control"), false);
785 add_plugin_category(N_("Distortion"));
786 add_plugin_category(N_("Fuzz"));
787 add_plugin_category(N_("Reverb"));
788 add_plugin_category(N_("Echo / Delay"));
789 add_plugin_category(N_("Modulation"));
790 add_plugin_category(N_("Guitar Effects"));
791 add_plugin_category(N_("Misc"));
792
793 std::vector<PluginUI*> p;
794 p.push_back(new JConvPluginUI(*this, "jconv"));
795 p.push_back(new JConvPluginUI(*this, "jconv_mono"));
796 pluginlist_append(p);
797 std::sort(p.begin(), p.end(), plugins_by_name_less);
798 for (std::vector<PluginUI*>::iterator v = p.begin(); v != p.end(); ++v) {
799 register_plugin(*v);
800 }
801 }
802
803
804 /****************************************************************
805 ** class DragIcon
806 */
807
DragIcon(PluginUI & plugin,Glib::RefPtr<Gdk::DragContext> context,gx_system::CmdlineOptions & options,int xoff)808 DragIcon::DragIcon(PluginUI& plugin, Glib::RefPtr<Gdk::DragContext> context, gx_system::CmdlineOptions& options, int xoff)
809 : window(), drag_icon_pixbuf() {
810 Glib::RefPtr<Gdk::Screen> screen = context->get_source_window()->get_screen();
811 Glib::RefPtr<Gdk::Visual> rgba;
812 if (screen->is_composited()) {
813 rgba = screen->get_rgba_visual();
814 window = new Gtk::Window(Gtk::WINDOW_POPUP);
815 if (rgba) { // else will look ugly..
816 gtk_widget_set_visual(GTK_WIDGET(window->gobj()), rgba->gobj());
817 }
818 }
819 create_drag_icon_pixbuf(plugin, rgba, options);
820 int w = drag_icon_pixbuf->get_width();
821 int h = drag_icon_pixbuf->get_height();
822 int h2 = (h/2)-2;
823 int w2 = std::min(std::max(0, xoff), w-gradient_length/2) - 4;
824 if (window) {
825 window->set_size_request(w, h);
826 window->signal_draw().connect(sigc::mem_fun(*this, &DragIcon::icon_draw));
827 //context->set_icon_widget(window, w2, h2);
828 gtk_drag_set_icon_widget(context->gobj(), GTK_WIDGET(window->gobj()), w2, h2);
829 } else {
830 context->set_icon(drag_icon_pixbuf, w2, h2);
831 }
832 }
833
~DragIcon()834 DragIcon::~DragIcon() {
835 delete window;
836 }
837
icon_draw(const Cairo::RefPtr<Cairo::Context> & cr)838 bool DragIcon::icon_draw(const Cairo::RefPtr<Cairo::Context> &cr) {
839 Gdk::Cairo::set_source_pixbuf(cr, drag_icon_pixbuf, 0, 0);
840 cr->paint();
841 return true;
842 }
843
create_drag_icon_pixbuf(const PluginUI & plugin,Glib::RefPtr<Gdk::Visual> rgba,gx_system::CmdlineOptions & options)844 void DragIcon::create_drag_icon_pixbuf(const PluginUI& plugin, Glib::RefPtr<Gdk::Visual> rgba, gx_system::CmdlineOptions& options) {
845 Gtk::OffscreenWindow w;
846 w.signal_draw().connect(sigc::bind(sigc::mem_fun(*this, &DragIcon::window_draw), sigc::ref(w)));
847 if (rgba) {
848 gtk_widget_set_visual(GTK_WIDGET(w.gobj()), rgba->gobj());
849 }
850 Gtk::Widget *r = RackBox::create_drag_widget(plugin, options);
851 w.add(*r);
852 w.show_all();
853 int busy_wait = 0;
854 while (Gtk::Main::events_pending() && busy_wait < 25) { // needed for style updates
855 Gtk::Main::iteration(false);
856 ++busy_wait;
857 }
858 w.get_window()->process_updates(true);
859 drag_icon_pixbuf = w.get_pixbuf();
860 }
861
window_draw(const Cairo::RefPtr<Cairo::Context> & cr,Gtk::OffscreenWindow & widget)862 bool DragIcon::window_draw(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::OffscreenWindow& widget) {
863 Gtk::Widget *child = widget.get_child();
864 if (child) {
865 widget.propagate_draw(*child, cr);
866 }
867 if (!window) {
868 return true;
869 }
870 int w = widget.get_window()->get_width();
871 int h = widget.get_window()->get_height();
872 Cairo::RefPtr<Cairo::LinearGradient> grad = Cairo::LinearGradient::create(w, 0, w-gradient_length, 0);
873 grad->add_color_stop_rgba(0, 1, 1, 1, 1);
874 grad->add_color_stop_rgba(1, 1, 1, 1, 0);
875 cr->rectangle(w-gradient_length, 0, gradient_length, h);
876 cr->mask(grad);
877 return true;
878 }
879
880
881 /****************************************************************
882 ** class MiniRackBox
883 */
884
885 Glib::RefPtr<Gtk::SizeGroup> MiniRackBox::szg_label;
886
make_delete_button(RackBox & rb)887 Gtk::Widget *MiniRackBox::make_delete_button(RackBox& rb) {
888 Gtk::Widget *w;
889 if (rb.has_delete()) {
890 Gtk::Label *l = new Gtk::Label("\u2a2f");
891 l->show();
892 Gtk::Button *b = new Gtk::Button();
893 b->set_focus_on_click(false);
894 b->add(*manage(l));
895 b->signal_clicked().connect(
896 sigc::hide_return(
897 sigc::bind(
898 sigc::mem_fun(rb.plugin.plugin_dict, &PluginDict::deactivate),
899 rb.get_id(), true)));
900 w = b;
901 } else {
902 w = new Gtk::Alignment();
903 }
904 w->set_size_request(20, 15);
905 return w;
906 }
907
on_my_leave_out(GdkEventCrossing * focus)908 bool MiniRackBox::on_my_leave_out(GdkEventCrossing *focus) {
909 if (!mconbox.get_visible()) {
910 Glib::RefPtr<Gdk::Window> window = this->get_window();
911 window->set_cursor();
912 }
913 return true;
914 }
915
on_my_enter_in(GdkEventCrossing * focus)916 bool MiniRackBox::on_my_enter_in(GdkEventCrossing *focus) {
917 if (!mconbox.get_visible()) {
918 Glib::RefPtr<Gdk::Window> window = this->get_window();
919 Glib::RefPtr<Gdk::Display> disp = this->get_display();
920 Glib::RefPtr<Gdk::Cursor> cursor(Gdk::Cursor::create(disp, Gdk::HAND1));
921 window->set_cursor(cursor);
922 }
923 return true;
924 }
925
MiniRackBox(RackBox & rb,gx_system::CmdlineOptions & options)926 MiniRackBox::MiniRackBox(RackBox& rb, gx_system::CmdlineOptions& options)
927 : Gtk::HBox(),
928 evbox(),
929 mconbox(false, 4),
930 mb_expand_button(),
931 mb_delete_button(),
932 preset_button(),
933 on_off_switch("switchit") {
934 gx_gui::ui_connect_switch(rb.plugin_dict.get_machine(), &on_off_switch,
935 rb.plugin.plugin->id_on_off(), nullptr, false);
936 if (strcmp(rb.plugin.get_id(), "ampstack") != 0) { // FIXME
937 if (!szg_label) {
938 szg_label = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
939 }
940 evbox.set_visible_window(false);
941 evbox.signal_leave_notify_event().connect(sigc::mem_fun(*this, &MiniRackBox::on_my_leave_out));
942 evbox.signal_enter_notify_event().connect(sigc::mem_fun(*this, &MiniRackBox::on_my_enter_in));
943 add(evbox);
944
945 Gtk::Alignment *al = new Gtk::Alignment();
946 al->set_padding(0, 4, 0, 0);
947 al->set_border_width(0);
948 al->get_style_context()->add_class("minibox-title");
949
950 evbox.add(*manage(al));
951
952 Gtk::HBox *box = new Gtk::HBox();
953 Gtk::HBox *top = new Gtk::HBox();
954 al->add(*manage(box));
955
956 this->set_spacing(0);
957 this->set_border_width(0);
958
959 box->set_spacing(0);
960 box->set_border_width(0);
961
962 top->set_spacing(4);
963 top->set_border_width(0);
964 top->set_name("rack_unit_title_bar");
965
966 box->pack_start(*manage(rb.wrap_bar()), Gtk::PACK_SHRINK);
967 box->pack_start(*manage(top));
968 box->pack_start(*manage(rb.wrap_bar()), Gtk::PACK_SHRINK);
969
970 top->pack_start(on_off_switch, Gtk::PACK_SHRINK);
971 on_off_switch.set_name("effect_on_off");
972 Gtk::Widget *effect_label = RackBox::make_label(rb.plugin, options);
973 szg_label->add_widget(*manage(effect_label));
974 top->pack_start(*manage(effect_label), Gtk::PACK_SHRINK);
975
976 top->pack_start(mconbox, Gtk::PACK_EXPAND_WIDGET);
977
978 mb_expand_button = rb.make_expand_button(true);
979 top->pack_end(*manage(mb_expand_button), Gtk::PACK_SHRINK);
980 if (!(rb.plugin.plugin->get_pdef()->flags & PGN_NO_PRESETS)) {
981 preset_button = rb.make_preset_button();
982 top->pack_end(*manage(preset_button), Gtk::PACK_SHRINK);
983 }
984 mb_delete_button = make_delete_button(rb);
985 mb_delete_button->set_no_show_all(true);
986 top->pack_end(*manage(mb_delete_button), Gtk::PACK_SHRINK);
987 } else { // special minibox for main amp in config mode
988 if (!szg_label) {
989 szg_label = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
990 }
991 evbox.set_visible_window(false);
992 evbox.signal_leave_notify_event().connect(sigc::mem_fun(*this, &MiniRackBox::on_my_leave_out));
993 evbox.signal_enter_notify_event().connect(sigc::mem_fun(*this, &MiniRackBox::on_my_enter_in));
994 add(evbox);
995
996 Gtk::Alignment *al = new Gtk::Alignment();
997 al->set_padding(0, 4, 0, 0);
998 al->set_border_width(0);
999
1000 Gtk::HBox *box = new Gtk::HBox();
1001 Gtk::HBox *top = new Gtk::HBox();
1002 evbox.add(*manage(box));
1003
1004 top->set_name("rack_unit_title_bar");
1005 Gtk::Widget *effect_label = RackBox::make_label(rb.plugin, options);
1006 szg_label->add_widget(*manage(effect_label));
1007 top->pack_start(*manage(effect_label), Gtk::PACK_SHRINK);
1008 top->pack_start(mconbox, Gtk::PACK_EXPAND_WIDGET);
1009 box->pack_start(*manage(al), Gtk::PACK_SHRINK);
1010 box->pack_start(*manage(top));
1011 }
1012 show_all();
1013 }
1014
pack(Gtk::Widget * w)1015 void MiniRackBox::pack(Gtk::Widget *w) {
1016 if (w) {
1017 mconbox.pack_start(*manage(w), Gtk::PACK_SHRINK, 4);
1018 }
1019 }
1020
set_config_mode(bool mode,PluginUI & plugin)1021 void MiniRackBox::set_config_mode(bool mode, PluginUI& plugin) {
1022 evbox.set_above_child(mode);
1023 if (mode) {
1024 mconbox.hide();
1025 if (preset_button) {
1026 preset_button->hide();
1027 }
1028 if (mb_expand_button) {
1029 mb_expand_button->hide();
1030 }
1031 if (mb_delete_button) {
1032 // force event handling of the button to be above the evbox
1033 mb_delete_button->hide();
1034 Glib::signal_idle().connect_once(
1035 sigc::mem_fun(mb_delete_button,&Gtk::Widget::show),
1036 Glib::PRIORITY_HIGH_IDLE);
1037 evbox_button = evbox.signal_button_press_event().connect(
1038 sigc::mem_fun(plugin, &PluginUI::on_rack_handle_press));
1039 }
1040 } else {
1041 mconbox.show();
1042 if (preset_button) {
1043 preset_button->show();
1044 }
1045 if (mb_expand_button) {
1046 mb_expand_button->show();
1047 }
1048 if (mb_delete_button) {
1049 evbox_button.disconnect();
1050 mb_delete_button->hide();
1051 }
1052 }
1053 }
1054
1055
1056 /****************************************************************
1057 ** class PluginPresetPopup
1058 */
1059
1060 /*
1061 ** InputWindow
1062 */
1063
1064 class InputWindow: public Gtk::Window {
1065 private:
1066 Glib::ustring name;
1067 void on_cancel();
1068 void on_ok(Gtk::Entry *e);
1069 virtual bool on_key_press_event(GdkEventKey *event);
1070 static InputWindow* create_from_builder(
1071 BaseObjectType* cobject, Glib::RefPtr<gx_gui::GxBuilder> bld, const Glib::ustring& save_name_default);
1072 InputWindow(BaseObjectType* cobject, Glib::RefPtr<gx_gui::GxBuilder> bld, const Glib::ustring& save_name_default);
1073 public:
1074 ~InputWindow();
1075 static InputWindow *create(const gx_system::CmdlineOptions& options, const Glib::ustring& save_name_default);
1076 void run();
get_name()1077 Glib::ustring& get_name() {
1078 return name;
1079 }
1080 };
1081
create_from_builder(BaseObjectType * cobject,Glib::RefPtr<gx_gui::GxBuilder> bld,const Glib::ustring & save_name_default)1082 InputWindow *InputWindow::create_from_builder(BaseObjectType* cobject, Glib::RefPtr<gx_gui::GxBuilder> bld,
1083 const Glib::ustring& save_name_default) {
1084 return new InputWindow(cobject, bld, save_name_default);
1085 }
1086
~InputWindow()1087 InputWindow::~InputWindow() {
1088 }
1089
create(const gx_system::CmdlineOptions & options,const Glib::ustring & save_name_default)1090 InputWindow *InputWindow::create(const gx_system::CmdlineOptions& options, const Glib::ustring& save_name_default) {
1091 Glib::RefPtr<gx_gui::GxBuilder> bld = gx_gui::GxBuilder::create_from_file(options.get_builder_filepath("pluginpreset_inputwindow.glade"));
1092 InputWindow *w;
1093 bld->get_toplevel_derived(
1094 "PluginPresetInputWindow", w,
1095 sigc::bind(sigc::ptr_fun(InputWindow::create_from_builder), bld, save_name_default));
1096 return w;
1097 }
1098
on_key_press_event(GdkEventKey * event)1099 bool InputWindow::on_key_press_event(GdkEventKey *event) {
1100 if (event->keyval == GDK_KEY_Escape && (event->state & Gtk::AccelGroup::get_default_mod_mask()) == 0) {
1101 hide();
1102 return true;
1103 }
1104 return Gtk::Window::on_key_press_event(event);
1105 }
1106
on_ok(Gtk::Entry * e)1107 void InputWindow::on_ok(Gtk::Entry *e) {
1108 name = e->get_text();
1109 hide();
1110 }
1111
InputWindow(BaseObjectType * cobject,Glib::RefPtr<gx_gui::GxBuilder> bld,const Glib::ustring & save_name_default)1112 InputWindow::InputWindow(BaseObjectType* cobject, Glib::RefPtr<gx_gui::GxBuilder> bld,
1113 const Glib::ustring& save_name_default)
1114 : Gtk::Window(cobject), name() {
1115 Gtk::Button *b;
1116 bld->find_widget("cancelbutton", b);
1117 b->signal_clicked().connect(
1118 sigc::mem_fun(*this, &InputWindow::hide));
1119 bld->find_widget("okbutton", b);
1120 Gtk::Entry *e;
1121 bld->find_widget("entry", e);
1122 e->set_text(save_name_default);
1123 e->select_region(0, -1);
1124 b->signal_clicked().connect(
1125 sigc::bind(sigc::mem_fun(*this, &InputWindow::on_ok), e));
1126 }
1127
run()1128 void InputWindow::run() {
1129 Gtk::Main::run(*this);
1130 }
1131
1132 /*
1133 ** PluginPresetListWindow
1134 */
1135
1136 class TextListStore: public Gtk::ListStore {
1137 public:
1138 class TextListColumns : public Gtk::TreeModel::ColumnRecord {
1139 public:
1140 Gtk::TreeModelColumn<Glib::ustring> name;
TextListColumns()1141 TextListColumns() {
1142 add(name);
1143 }
1144 } col;
1145 private:
TextListStore()1146 TextListStore(): Gtk::ListStore(), col() {
1147 set_column_types(col);
1148 }
1149 public:
create()1150 static Glib::RefPtr<TextListStore> create() {
1151 return Glib::RefPtr<TextListStore>(new TextListStore);
1152 }
1153 };
1154
1155 class PluginPresetListWindow: public Gtk::Window {
1156 private:
1157 Glib::RefPtr<TextListStore> textliststore;
1158 PluginPresetPopup& presetlist;
1159 //
1160 Gtk::TreeView *treeview;
1161 Gtk::Button *removebutton;
1162 using Gtk::Window::on_remove;
1163 void on_remove();
1164 void on_selection_changed();
1165 virtual bool on_key_press_event(GdkEventKey *event);
1166 static PluginPresetListWindow* create_from_builder(
1167 BaseObjectType* cobject, Glib::RefPtr<gx_gui::GxBuilder> bld, PluginPresetPopup& p);
1168 PluginPresetListWindow(BaseObjectType* cobject, Glib::RefPtr<gx_gui::GxBuilder> bld, PluginPresetPopup& p);
1169 public:
1170 ~PluginPresetListWindow();
1171 static PluginPresetListWindow *create(const gx_system::CmdlineOptions& options, PluginPresetPopup& p);
1172 void run();
1173 };
1174
create_from_builder(BaseObjectType * cobject,Glib::RefPtr<gx_gui::GxBuilder> bld,PluginPresetPopup & p)1175 PluginPresetListWindow *PluginPresetListWindow::create_from_builder(
1176 BaseObjectType* cobject, Glib::RefPtr<gx_gui::GxBuilder> bld, PluginPresetPopup& p) {
1177 return new PluginPresetListWindow(cobject, bld, p);
1178 }
1179
~PluginPresetListWindow()1180 PluginPresetListWindow::~PluginPresetListWindow() {
1181 }
1182
create(const gx_system::CmdlineOptions & options,PluginPresetPopup & p)1183 PluginPresetListWindow *PluginPresetListWindow::create(
1184 const gx_system::CmdlineOptions& options, PluginPresetPopup& p) {
1185 Glib::RefPtr<gx_gui::GxBuilder> bld = gx_gui::GxBuilder::create_from_file(
1186 options.get_builder_filepath("pluginpreset_listwindow.glade"));
1187 PluginPresetListWindow *w;
1188 bld->get_toplevel_derived(
1189 "PluginPresetListWindow", w,
1190 sigc::bind(sigc::ptr_fun(PluginPresetListWindow::create_from_builder), bld, sigc::ref(p)));
1191 return w;
1192 }
1193
on_key_press_event(GdkEventKey * event)1194 bool PluginPresetListWindow::on_key_press_event(GdkEventKey *event) {
1195 if (event->keyval == GDK_KEY_Escape && (event->state & Gtk::AccelGroup::get_default_mod_mask()) == 0) {
1196 hide();
1197 return true;
1198 }
1199 return Gtk::Window::on_key_press_event(event);
1200 }
1201
on_remove()1202 void PluginPresetListWindow::on_remove() {
1203 Gtk::TreeIter it = treeview->get_selection()->get_selected();
1204 if (it) {
1205 presetlist.get_machine().plugin_preset_list_remove(
1206 presetlist.get_pdef(), it->get_value(textliststore->col.name));
1207 textliststore->erase(it);
1208 }
1209 }
1210
PluginPresetListWindow(BaseObjectType * cobject,Glib::RefPtr<gx_gui::GxBuilder> bld,PluginPresetPopup & p)1211 PluginPresetListWindow::PluginPresetListWindow(
1212 BaseObjectType* cobject, Glib::RefPtr<gx_gui::GxBuilder> bld, PluginPresetPopup& p)
1213 : Gtk::Window(cobject),
1214 textliststore(TextListStore::create()),
1215 presetlist(p) {
1216 Gtk::Button *b;
1217 bld->find_widget("closebutton", b);
1218 b->signal_clicked().connect(
1219 sigc::mem_fun(*this, &PluginPresetListWindow::hide));
1220 bld->find_widget("removebutton", removebutton);
1221 removebutton->signal_clicked().connect(
1222 sigc::mem_fun0(*this, &PluginPresetListWindow::on_remove));
1223 bld->find_widget("treeview", treeview);
1224 for (gx_preset::UnitPresetList::const_iterator i = presetlist.begin(); i != presetlist.end(); ++i) {
1225 if (i->name.empty()) {
1226 break;
1227 }
1228 textliststore->append()->set_value(textliststore->col.name, i->name);
1229 }
1230 treeview->set_model(textliststore);
1231 removebutton->set_sensitive(false);
1232 Glib::RefPtr<Gtk::TreeSelection> sel = treeview->get_selection();
1233 sel->signal_changed().connect(
1234 sigc::mem_fun(*this, &PluginPresetListWindow::on_selection_changed));
1235 }
1236
on_selection_changed()1237 void PluginPresetListWindow::on_selection_changed() {
1238 removebutton->set_sensitive(treeview->get_selection()->get_selected());
1239 }
1240
run()1241 void PluginPresetListWindow::run() {
1242 Gtk::Main::run(*this);
1243 }
1244
1245 /*
1246 ** PluginPresetPopup
1247 */
1248
set_plugin_preset(bool factory,const Glib::ustring & name)1249 void PluginPresetPopup::set_plugin_preset(bool factory, const Glib::ustring& name) {
1250 if(strcmp(pdef->id,"seq")==0) {
1251 machine.plugin_preset_list_sync_set(pdef, factory, name);
1252 } else {
1253 machine.plugin_preset_list_set(pdef, factory, name);
1254 }
1255 }
1256
set_plugin_std_preset()1257 void PluginPresetPopup::set_plugin_std_preset() {
1258 machine.reset_unit(pdef);
1259 }
1260
save_plugin_preset()1261 void PluginPresetPopup::save_plugin_preset() {
1262 InputWindow *w = InputWindow::create(machine.get_options(), save_name_default);
1263 w->run();
1264 if (!w->get_name().empty()) {
1265 // save loop file to plugin preset name
1266 if(strcmp(pdef->id,"dubber")==0) {
1267 Glib::ustring name = "";
1268 machine.set_parameter_value("dubber.filename", name);
1269 machine.set_parameter_value("dubber.savefile", true);
1270 machine.set_parameter_value("dubber.filename", w->get_name());
1271 }
1272 machine.plugin_preset_list_save(pdef, w->get_name());
1273 if(strcmp(pdef->id,"seq")==0) {
1274 Glib::ustring id = "seq." + w->get_name();
1275 if (!machine.parameter_hasId(id)) {
1276 machine.insert_param("seq", w->get_name());
1277 }
1278 }
1279 }
1280 delete w;
1281 }
1282
remove_plugin_preset()1283 void PluginPresetPopup::remove_plugin_preset() {
1284 PluginPresetListWindow *w = PluginPresetListWindow::create(machine.get_options(), *this);
1285 w->run();
1286 delete w;
1287 }
1288
add_plugin_preset_list(bool * found)1289 bool PluginPresetPopup::add_plugin_preset_list(bool *found) {
1290 *found = false;
1291 bool found_presets = false;
1292 bool factory = false;
1293 for (gx_preset::UnitPresetList::iterator i = presetnames.begin(); i != presetnames.end(); ++i) {
1294 if (i->name.empty()) {
1295 factory = true;
1296 if (found_presets) {
1297 append(*manage(new Gtk::SeparatorMenuItem()));
1298 *found = true;
1299 found_presets = false;
1300 }
1301 continue;
1302 } else {
1303 found_presets = true;
1304 }
1305 Gtk::CheckMenuItem *c = new Gtk::CheckMenuItem(i->name);
1306 if (i->is_set) {
1307 c->set_active(true);
1308 }
1309 c->signal_activate().connect(
1310 sigc::bind(sigc::mem_fun(this, &PluginPresetPopup::set_plugin_preset), factory, i->name));
1311 append(*manage(c));
1312 }
1313 return found_presets;
1314 }
1315
delete_plugin_preset_popup(PluginPresetPopup * p)1316 static bool delete_plugin_preset_popup(PluginPresetPopup *p) {
1317 delete p;
1318 return false;
1319 }
1320
on_selection_done()1321 void PluginPresetPopup::on_selection_done() {
1322 Glib::signal_idle().connect(
1323 sigc::bind(
1324 sigc::ptr_fun(delete_plugin_preset_popup),
1325 this));
1326 }
1327
PluginPresetPopup(const PluginDef * pdef_,gx_engine::GxMachineBase & machine_,const Glib::ustring & save_name_default_)1328 PluginPresetPopup::PluginPresetPopup(const PluginDef *pdef_, gx_engine::GxMachineBase& machine_,
1329 const Glib::ustring& save_name_default_)
1330 : Gtk::Menu(),
1331 pdef(pdef_),
1332 machine(machine_),
1333 save_name_default(save_name_default_),
1334 presetnames() {
1335 machine.plugin_preset_list_load(pdef, presetnames);
1336 bool found_presets;
1337 if (!add_plugin_preset_list(&found_presets)) {
1338 Gtk::CheckMenuItem *c = new Gtk::CheckMenuItem(_("standard"));
1339 if (machine.parameter_unit_has_std_values(pdef)) {
1340 c->set_active(true);
1341 }
1342 c->signal_activate().connect(
1343 sigc::mem_fun(this, &PluginPresetPopup::set_plugin_std_preset));
1344 append(*manage(c));
1345 }
1346 append(*manage(new Gtk::SeparatorMenuItem()));
1347 Gtk::MenuItem *mi = new Gtk::MenuItem(_("save..."));
1348 append(*manage(mi));
1349 mi->signal_activate().connect(
1350 sigc::mem_fun(this, &PluginPresetPopup::save_plugin_preset));
1351 if (found_presets) {
1352 mi = new Gtk::MenuItem(_("remove..."));
1353 append(*manage(mi));
1354 mi->signal_activate().connect(
1355 sigc::mem_fun(this, &PluginPresetPopup::remove_plugin_preset));
1356 }
1357 show_all();
1358 popup_at_pointer(nullptr);
1359 }
1360
1361
1362 /****************************************************************
1363 ** class RackBox
1364 */
1365
set_paintbox_unit_shrink(Gxw::PaintBox & pb,PluginType tp)1366 void RackBox::set_paintbox_unit_shrink(Gxw::PaintBox& pb, PluginType tp) {
1367 pb.get_style_context()->add_class("rackmini");
1368 pb.set_border_width(0);
1369 }
1370
set_paintbox_unit(Gxw::PaintBox & pb,const PluginUI & plugin)1371 void RackBox::set_paintbox_unit(Gxw::PaintBox& pb, const PluginUI& plugin) {
1372 pb.set_border_width(0);
1373 }
1374
set_paintbox(Gxw::PaintBox & pb,PluginType tp)1375 void RackBox::set_paintbox(Gxw::PaintBox& pb, PluginType tp) {
1376 pb.set_name("rackbox");
1377 // pb.property_paint_func().set_value("rectangle_skin_color_expose");
1378 pb.set_border_width(0);
1379 }
1380
make_label(const PluginUI & plugin,gx_system::CmdlineOptions & options,bool useshort)1381 Gtk::Widget *RackBox::make_label(const PluginUI& plugin, gx_system::CmdlineOptions& options, bool useshort) {
1382 Gtk::Label *effect_label = new Gtk::Label(plugin.get_displayname(useshort));
1383 effect_label->set_alignment(0.0, 0.5);
1384 effect_label->get_style_context()->add_class("effect_title");
1385 return effect_label;
1386 }
1387
make_bar(int left,int right,bool sens)1388 Gtk::Widget *RackBox::make_bar(int left, int right, bool sens) {
1389 Gtk::Alignment *al = new Gtk::Alignment(0, 0, 1.0, 1.0);
1390 //al->set_padding(4, 4, left, right);
1391 Gtk::Button *button = new Gtk::Button();
1392 button->set_size_request(32,-1);
1393 //button->set_name("effect_reset");
1394 gx_gui::GxBuilder::set_tooltip_text_connect_handler(*button, _("Drag'n' Drop Handle"));
1395 button->set_relief(Gtk::RELIEF_NONE);
1396 button->set_sensitive(sens);
1397 al->add(*manage(button));
1398 return al;
1399 }
1400
on_my_leave_out(GdkEventCrossing * focus)1401 bool RackBox::on_my_leave_out(GdkEventCrossing *focus) {
1402 Glib::RefPtr<Gdk::Window> window = this->get_window();
1403 window->set_cursor();
1404 return true;
1405 }
1406
on_my_enter_in(GdkEventCrossing * focus)1407 bool RackBox::on_my_enter_in(GdkEventCrossing *focus) {
1408 Glib::RefPtr<Gdk::Window> window = this->get_window();
1409 Glib::RefPtr<Gdk::Display> disp = this->get_display();
1410 Glib::RefPtr<Gdk::Cursor> cursor(Gdk::Cursor::create(disp, Gdk::HAND1));
1411 window->set_cursor(cursor);
1412 return true;
1413 }
1414
wrap_bar(int left,int right,bool sens)1415 Gtk::Widget *RackBox::wrap_bar(int left, int right, bool sens) {
1416 Gtk::EventBox *ev = new Gtk::EventBox;
1417 ev->set_visible_window(false);
1418 ev->set_above_child(true);
1419 ev->add(*manage(make_bar(left, right, sens)));
1420 ev->signal_leave_notify_event().connect(sigc::mem_fun(*this, &RackBox::on_my_leave_out));
1421 ev->signal_enter_notify_event().connect(sigc::mem_fun(*this, &RackBox::on_my_enter_in));
1422 ev->signal_button_press_event().connect(sigc::mem_fun(plugin, &PluginUI::on_rack_handle_press));
1423 ev->signal_drag_begin().connect(sigc::mem_fun(*this, &RackBox::on_my_drag_begin));
1424 ev->signal_drag_end().connect(sigc::mem_fun(*this, &RackBox::on_my_drag_end));
1425 ev->signal_drag_failed().connect(sigc::mem_fun(*this, &RackBox::on_my_drag_failed));
1426 ev->signal_drag_data_get().connect(sigc::mem_fun(*this, &RackBox::on_my_drag_data_get));
1427 std::vector<Gtk::TargetEntry> listTargets;
1428 listTargets.push_back(Gtk::TargetEntry(target, Gtk::TARGET_SAME_APP, 0));
1429 ev->drag_source_set(listTargets, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE);
1430 return ev;
1431 }
1432
create_icon_widget(const PluginUI & plugin,gx_system::CmdlineOptions & options)1433 Gtk::Widget *RackBox::create_icon_widget(const PluginUI& plugin, gx_system::CmdlineOptions& options) {
1434 Gxw::PaintBox *pb = new Gxw::PaintBox(Gtk::ORIENTATION_HORIZONTAL);
1435 RackBox::set_paintbox(*pb, plugin.get_type());
1436 Gtk::Widget *effect_label = RackBox::make_label(plugin, options);
1437 Gtk::Alignment *al = new Gtk::Alignment(0.0, 0.0, 1.0, 1.0);
1438 al->set_padding(0,2,2,0);
1439 al->add(*manage(effect_label));
1440 pb->pack_start(*manage(al), Gtk::PACK_SHRINK);
1441 pb->show_all();
1442 return pb;
1443 }
1444
create_drag_widget(const PluginUI & plugin,gx_system::CmdlineOptions & options)1445 Gtk::Widget *RackBox::create_drag_widget(const PluginUI& plugin, gx_system::CmdlineOptions& options) {
1446 Gxw::PaintBox *pb = new Gxw::PaintBox(Gtk::ORIENTATION_HORIZONTAL);
1447 RackBox::set_paintbox_unit_shrink(*pb, plugin.get_type());
1448 pb->set_name("drag_widget");
1449 Gtk::Widget *effect_label = RackBox::make_label(plugin, options);
1450 if (plugin.plugin->get_pdef()->flags & gx_engine::PGNI_IS_LV2) {
1451 pb->get_style_context()->add_class("PL-LV2");
1452 pb->set_name("rack_unit_title_bar");
1453 } else if (plugin.plugin->get_pdef()->flags & gx_engine::PGNI_IS_LADSPA) {
1454 pb->get_style_context()->add_class("PL-LADSPA");
1455 pb->set_name("rack_unit_title_bar");
1456 }
1457 Gtk::Alignment *al = new Gtk::Alignment(0.0, 0.0, 0.0, 0.0);
1458 al->set_padding(0,0,4,20);
1459 al->add(*manage(RackBox::make_bar(4, 4, true))); // FIXME: fix style and remove sens parameter
1460 pb->pack_start(*manage(al), Gtk::PACK_SHRINK);
1461 //pb->pack_start(*manage(swtch), Gtk::PACK_SHRINK);
1462 pb->pack_start(*manage(effect_label), Gtk::PACK_SHRINK);
1463 al = new Gtk::Alignment(0.0, 0.0, 0.0, 0.0);
1464 al->set_size_request(70,30);
1465 pb->pack_start(*manage(al), Gtk::PACK_SHRINK);
1466 pb->show_all();
1467 return pb;
1468 }
1469
display(bool v,bool animate)1470 void RackBox::display(bool v, bool animate) {
1471 if (v) {
1472 if (animate) {
1473 animate_insert();
1474 } else {
1475 show();
1476 }
1477 } else {
1478 plugin.remove(animate);
1479 }
1480 }
1481
RackBox(PluginUI & plugin_,PluginDict & tl,Gtk::Widget * bare)1482 RackBox::RackBox(PluginUI& plugin_, PluginDict& tl, Gtk::Widget* bare)
1483 : Gtk::VBox(),
1484 plugin(plugin_),
1485 plugin_dict(tl),
1486 anim_tag(),
1487 compress(true),
1488 delete_button(true),
1489 mbox(Gtk::ORIENTATION_HORIZONTAL),
1490 minibox(0),
1491 fbox(0),
1492 target(),
1493 anim_height(0),
1494 anim_step(),
1495 drag_icon(),
1496 target_height(0),
1497 box(Gtk::ORIENTATION_HORIZONTAL, 2),
1498 on_off_switch("switchit") {
1499 gx_gui::ui_connect_switch(tl.get_machine(), &on_off_switch,
1500 plugin.plugin->id_on_off(), nullptr, false);
1501 if (bare) {
1502 compress = false;
1503 delete_button = false;
1504 }
1505 set_paintbox_unit_shrink(mbox, plugin.get_type());
1506 init_dnd();
1507 minibox = new MiniRackBox(*this, tl.get_options());
1508 mbox.pack_start(*manage(minibox));
1509 pack_start(mbox, Gtk::PACK_EXPAND_WIDGET);
1510 if (bare) {
1511 add(*manage(bare));
1512 fbox = bare;
1513 } else {
1514 Gxw::PaintBox *pb = new Gxw::PaintBox(Gtk::ORIENTATION_HORIZONTAL);
1515 pb->show();
1516 set_paintbox_unit(*pb, plugin);
1517 pb->pack_start(*manage(make_full_box(tl.get_options())));
1518 pack_start(*manage(pb), Gtk::PACK_SHRINK);
1519 fbox = pb;
1520 }
1521 fbox->get_style_context()->add_class("rackmain");
1522
1523 string css_id = plugin.plugin->get_pdef()->id;
1524 boost::replace_all(css_id, ".", "-");
1525 set_name("PLUI-" + css_id);
1526 Glib::RefPtr<Gtk::StyleContext> context = get_style_context();
1527 context->add_class("rackbox");
1528 if (plugin.plugin->get_pdef()->flags & gx_engine::PGNI_IS_LV2) {
1529 context->add_class("PL-LV2");
1530 fbox->get_style_context()->add_class("PL-LV2");
1531 mbox.get_style_context()->add_class("PL-LV2");
1532 } else if (plugin.plugin->get_pdef()->flags & gx_engine::PGNI_IS_LADSPA) {
1533 context->add_class("PL-LADSPA");
1534 fbox->get_style_context()->add_class("PL-LADSPA");
1535 mbox.get_style_context()->add_class("PL-LADSPA");
1536 }
1537 on_off_switch.signal_toggled().connect(
1538 sigc::mem_fun(plugin, &PluginUI::on_state_change));
1539 show();
1540 }
1541
init_dnd()1542 void RackBox::init_dnd() {
1543 target = "application/x-guitarix-";
1544 if (plugin.get_type() == PLUGIN_TYPE_MONO) {
1545 target += "mono";
1546 } else {
1547 target += "stereo";
1548 }
1549 mbox.signal_drag_begin().connect(sigc::mem_fun(*this, &RackBox::on_my_drag_begin));
1550 mbox.signal_drag_end().connect(sigc::mem_fun(*this, &RackBox::on_my_drag_end));
1551 mbox.signal_drag_failed().connect(sigc::mem_fun(*this, &RackBox::on_my_drag_failed));
1552 mbox.signal_drag_data_get().connect(sigc::mem_fun(*this, &RackBox::on_my_drag_data_get));
1553 }
1554
enable_drag(bool v)1555 void RackBox::enable_drag(bool v) {
1556 if (v) {
1557 std::vector<Gtk::TargetEntry> listTargets;
1558 listTargets.push_back(Gtk::TargetEntry(target, Gtk::TARGET_SAME_APP, 0));
1559 mbox.drag_source_set(listTargets, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE);
1560 } else {
1561 mbox.drag_source_unset();
1562 }
1563 }
1564
get_parent()1565 RackContainer *RackBox::get_parent() {
1566 return dynamic_cast<RackContainer*>(Gtk::VBox::get_parent());
1567 }
1568
on_my_drag_begin(const Glib::RefPtr<Gdk::DragContext> & context)1569 void RackBox::on_my_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context) {
1570 int x, y;
1571 get_pointer(x, y);
1572 drag_icon = new DragIcon(plugin, context, plugin_dict.get_options(), x);
1573 plugin.remove(true);
1574 get_parent()->change_child_count(-1);
1575 }
1576
animate_create()1577 bool RackBox::animate_create() {
1578 bool ret = true;
1579 anim_height += anim_step;
1580 if (anim_height >= target_height) {
1581 set_visibility(true);
1582 set_size_request(-1,-1);
1583 ret = false;
1584 } else {
1585 set_size_request(-1, anim_height);
1586 }
1587 get_parent()->ensure_visible(*this);
1588 return ret;
1589 }
1590
animate_insert()1591 void RackBox::animate_insert() {
1592 if (!plugin_dict.use_animations()) {
1593 show();
1594 get_parent()->ensure_visible(*this);
1595 } else {
1596 if (anim_tag.connected()) {
1597 hide();
1598 anim_tag.disconnect();
1599 set_size_request(-1,-1);
1600 }
1601 show();
1602 gint min_height, natural_height;
1603 get_preferred_height(min_height, natural_height); // needs show and visibility true
1604 target_height = min_height;
1605 set_size_request(-1,0);
1606 set_visibility(false);
1607 anim_height = 0;
1608 anim_step = target_height / ANIMATE_STEPS;
1609 anim_tag = Glib::signal_timeout().connect(mem_fun(*this, &RackBox::animate_create), ANIMATE_TIME);
1610 }
1611 }
1612
on_my_drag_end(const Glib::RefPtr<Gdk::DragContext> & context)1613 void RackBox::on_my_drag_end(const Glib::RefPtr<Gdk::DragContext>& context) {
1614 if (drag_icon) {
1615 delete drag_icon;
1616 drag_icon = 0;
1617 }
1618 if (plugin.plugin->get_box_visible()) {
1619 animate_insert();
1620 }
1621 }
1622
on_my_drag_failed(const Glib::RefPtr<Gdk::DragContext> & context,Gtk::DragResult result)1623 bool RackBox::on_my_drag_failed(const Glib::RefPtr<Gdk::DragContext>& context, Gtk::DragResult result) {
1624 get_parent()->change_child_count(1);
1625 return false;
1626 }
1627
on_my_drag_data_get(const Glib::RefPtr<Gdk::DragContext> & context,Gtk::SelectionData & selection,int info,int timestamp)1628 void RackBox::on_my_drag_data_get(const Glib::RefPtr<Gdk::DragContext>& context, Gtk::SelectionData& selection, int info, int timestamp) {
1629 selection.set(target, plugin.get_id());
1630 }
1631
vis_switch(Gtk::Widget & a,Gtk::Widget & b)1632 void RackBox::vis_switch(Gtk::Widget& a, Gtk::Widget& b) {
1633 a.hide();
1634 b.show();
1635 }
1636
set_visibility(bool v)1637 void RackBox::set_visibility(bool v) {
1638 bool config_mode = plugin_dict.get_config_mode();
1639 if (config_mode || get_plug_visible()) {
1640 mbox.set_visible(v);
1641 set_config_mode(plugin_dict.get_config_mode());
1642 } else {
1643 fbox->set_visible(v);
1644 }
1645 }
1646
swtch(bool mini)1647 void RackBox::swtch(bool mini) {
1648 plugin.plugin->set_plug_visible(mini);
1649 plugin.on_state_change();
1650 if (mini || plugin_dict.get_config_mode()) {
1651 vis_switch(*fbox, mbox);
1652 } else {
1653 vis_switch(mbox, *fbox);
1654 }
1655 }
1656
set_config_mode(bool mode)1657 void RackBox::set_config_mode(bool mode) {
1658 if (!can_compress() || !get_plug_visible()) {
1659 if (mode) {
1660 vis_switch(*fbox, mbox);
1661 if (strcmp(plugin.get_id(), "ampstack") == 0) { // FIXME
1662 return;
1663 }
1664 } else {
1665 vis_switch(mbox, *fbox);
1666 }
1667 }
1668 minibox->set_config_mode(mode, plugin);
1669 enable_drag(mode);
1670 }
1671
set_position(int pos,int post_pre)1672 void RackBox::set_position(int pos, int post_pre) {
1673 plugin.plugin->set_position(pos);
1674 if (plugin.get_type() == PLUGIN_TYPE_MONO) {
1675 plugin.plugin->set_effect_post_pre(post_pre);
1676 }
1677 }
1678
compare_position(int pos,int post_pre)1679 bool RackBox::compare_position(int pos, int post_pre) {
1680 if (plugin.plugin->get_position() != pos) {
1681 return false;
1682 }
1683 if (plugin.get_type() == PLUGIN_TYPE_MONO && plugin.plugin->get_effect_post_pre() != post_pre) {
1684 return false;
1685 }
1686 return true;
1687 }
1688
do_expand()1689 void RackBox::do_expand() {
1690 swtch(false);
1691 Glib::signal_idle().connect_once(
1692 sigc::bind(
1693 sigc::mem_fun(get_parent(), &RackContainer::ensure_visible),
1694 sigc::ref(*this)));
1695 }
1696
make_expand_button(bool expand)1697 Gtk::Button *RackBox::make_expand_button(bool expand) {
1698 const gchar *t;
1699 Gtk::Button *b = new Gtk::Button();
1700 //b->set_relief(Gtk::RELIEF_NONE);
1701 if (expand) {
1702 t = "rack_expand";
1703 gx_gui::GxBuilder::set_tooltip_text_connect_handler(*b, _("expand effect unit"));
1704 } else {
1705 t = "rack_shrink";
1706 gx_gui::GxBuilder::set_tooltip_text_connect_handler(*b, _("shrink effect unit"));
1707 }
1708 Gtk::Image *l = new Gtk::Image();
1709 l->Gtk::Image::set_from_icon_name(t, Gtk::ICON_SIZE_BUTTON);
1710 b->set_focus_on_click(false);
1711 b->add(*manage(l));
1712 b->set_name("effect_on_off");
1713 if (expand) {
1714 b->signal_clicked().connect(
1715 sigc::mem_fun(*this, &RackBox::do_expand));
1716 } else {
1717 b->signal_clicked().connect(
1718 sigc::bind(sigc::mem_fun(*this, &RackBox::swtch), true));
1719 }
1720 return b;
1721 }
1722
make_preset_button()1723 Gtk::Button *RackBox::make_preset_button() {
1724 Gtk::Button *p = new Gtk::Button();
1725 //p->set_relief(Gtk::RELIEF_NONE);
1726 Gtk::Image *l = new Gtk::Image();
1727 l->Gtk::Image::set_from_icon_name("rack_preset", Gtk::ICON_SIZE_BUTTON);
1728 p->add(*manage(l));
1729 p->set_can_default(false);
1730 p->set_can_focus(false);
1731 gx_gui::GxBuilder::set_tooltip_text_connect_handler(*p, _("manage effect unit presets"));
1732 p->set_name("effect_on_off");
1733 p->signal_clicked().connect(
1734 sigc::mem_fun(plugin, &PluginUI::on_plugin_preset_popup));
1735 return p;
1736 }
1737
pack(Gtk::Widget * main,Gtk::Widget * mini)1738 void RackBox::pack(Gtk::Widget *main, Gtk::Widget *mini) {
1739 if (!main) {
1740 return;
1741 }
1742 box.pack_start(*manage(main));
1743 minibox->pack(mini);
1744 }
1745
make_full_box(gx_system::CmdlineOptions & options)1746 Gtk::HBox *RackBox::make_full_box(gx_system::CmdlineOptions& options) {
1747 Gtk::HBox *bx = new Gtk::HBox();
1748 Gtk::Widget *effect_label = make_label(plugin, options, false);
1749
1750 // overall hbox: drag-button - center vbox - drag button
1751 Gtk::HBox *main = new Gtk::HBox();
1752 // center vbox containing title bar and widgets
1753 Gtk::VBox *center = new Gtk::VBox();
1754 // title vbox on top
1755 Gtk::HBox *top = new Gtk::HBox();
1756
1757 // spacing for bottom shadow
1758 Gtk::Alignment *al = new Gtk::Alignment();
1759 al->set_padding(0, 4, 0, 0);
1760 al->add(*manage(main));
1761
1762 main->set_spacing(0);
1763
1764 center->set_name("rack_unit_center");
1765 center->set_border_width(0);
1766 center->set_spacing(4);
1767 center->pack_start(*manage(top), Gtk::PACK_SHRINK);
1768 center->pack_start(box, Gtk::PACK_EXPAND_WIDGET);
1769
1770 top->set_spacing(4);
1771 top->set_name("rack_unit_title_bar");
1772
1773 top->pack_start(on_off_switch, Gtk::PACK_SHRINK);
1774 on_off_switch.set_name("effect_on_off");
1775 top->pack_start(*manage(effect_label), Gtk::PACK_SHRINK);
1776 top->pack_end(*manage(make_expand_button(false)), Gtk::PACK_SHRINK);
1777 if (!(plugin.plugin->get_pdef()->flags & PGN_NO_PRESETS))
1778 top->pack_end(*manage(make_preset_button()), Gtk::PACK_SHRINK);
1779
1780 main->pack_start(*manage(wrap_bar()), Gtk::PACK_SHRINK);
1781 main->pack_start(*manage(center), Gtk::PACK_EXPAND_WIDGET);
1782 main->pack_end(*manage(wrap_bar()), Gtk::PACK_SHRINK);
1783
1784 main->set_name(plugin.get_id());
1785 bx->pack_start(*manage(al), Gtk::PACK_EXPAND_WIDGET);
1786 //al->show_all();
1787 bx->show_all();
1788 return bx;
1789 }
1790
1791
1792 /****************************************************************
1793 ** class RackContainer
1794 */
1795
1796 static const int min_containersize = 40;
1797
RackContainer(PluginDict & plugin_dict_)1798 RackContainer::RackContainer(PluginDict& plugin_dict_)
1799 : Gtk::VBox(),
1800 plugin_dict(plugin_dict_),
1801 in_drag(-2),
1802 count(0),
1803 targets(),
1804 othertargets(),
1805 highlight_connection(),
1806 autoscroll_connection() {
1807 set_size_request(-1, min_containersize);
1808 show_all();
1809 }
1810
set_list_targets(const std::vector<Gtk::TargetEntry> & listTargets,const std::vector<std::string> & targets_,const std::vector<std::string> & othertargets_)1811 void RackContainer::set_list_targets(const std::vector<Gtk::TargetEntry>& listTargets,
1812 const std::vector<std::string>& targets_,
1813 const std::vector<std::string>& othertargets_) {
1814 targets = targets_;
1815 othertargets = othertargets_;
1816 drag_dest_set(listTargets, Gtk::DEST_DEFAULT_DROP, Gdk::ACTION_MOVE);
1817 }
1818
drag_highlight_draw(const Cairo::RefPtr<Cairo::Context> & cr,int y0)1819 bool RackContainer::drag_highlight_draw(const Cairo::RefPtr<Cairo::Context> &cr, int y0) {
1820 if (!get_is_drawable()) {
1821 return false;
1822 }
1823 int x, y, width, height;
1824 if (!get_has_window()) {
1825 Gtk::Allocation a = get_allocation();
1826 x = 0;
1827 y = 0;
1828 width = a.get_width();
1829 height = a.get_height();
1830 } else {
1831 get_window()->get_geometry(x, y, width, height);
1832 x = 0;
1833 y = 0;
1834 }
1835 Glib::RefPtr<Gdk::Pixbuf> pb_ = Gtk::IconTheme::get_default()->load_icon("insert", -1, Gtk::ICON_LOOKUP_GENERIC_FALLBACK);
1836 if (pb_) {
1837 Gdk::Cairo::set_source_pixbuf(cr, pb_, x, y);
1838 cr->get_source()->set_extend(Cairo::EXTEND_REPEAT);
1839 if (y0 < 0) {
1840 cr->set_line_width(4.0);
1841 cr->rectangle(x, max(0, y), width, height);
1842 cr->stroke();
1843 } else {
1844 cr->rectangle(x, max(y, y0 - 3), width, 2);
1845 cr->fill();
1846 }
1847 }
1848 return false;
1849 }
1850
1851 struct childpos {
1852 int y0, y1, pos;
childposchildpos1853 childpos(int y0_, int y1_, int pos_): y0(y0_), y1(y1_), pos(pos_) {}
operator <childpos1854 bool operator<(const childpos& p) {
1855 return y0 < p.y0;
1856 }
1857 };
1858
find_index(int x,int y,string * before,int * ypos)1859 void RackContainer::find_index(int x, int y, string *before, int *ypos) {
1860 std::list<childpos> l;
1861 std::vector<RackBox*> children = get_children();
1862 int mpos = -1;
1863 for (std::vector<RackBox*>::iterator ch = children.begin(); ch != children.end(); ++ch) {
1864 ++mpos;
1865 if (!(*ch)->get_visible()) {
1866 continue;
1867 }
1868 Gtk::Allocation a = (*ch)->get_allocation();
1869 l.push_back(childpos(a.get_y(), a.get_y()+a.get_height(), mpos));
1870 }
1871 if (l.empty()) {
1872 if (before) {
1873 *before = "";
1874 }
1875 *ypos = -1;
1876 return;
1877 }
1878 Gtk::Allocation a0 = get_allocation();
1879 y += a0.get_y();
1880 int sy = l.begin()->y0;
1881 for (std::list<childpos>::iterator cp = l.begin(); cp != l.end(); ++cp) {
1882 if (y < (cp->y0 + cp->y1) / 2) {
1883 if (before) {
1884 *before = children[cp->pos]->get_id();
1885 }
1886 *ypos = (cp->y0+sy)/2 - a0.get_y();
1887 return;
1888 }
1889 sy = cp->y1;
1890 }
1891 if (before) {
1892 *before = "";
1893 }
1894 *ypos = sy - a0.get_y();
1895 }
1896
check_targets(const std::vector<std::string> & tgts1,const std::vector<std::string> & tgts2)1897 bool RackContainer::check_targets(const std::vector<std::string>& tgts1, const std::vector<std::string>& tgts2) {
1898 for (std::vector<std::string>::const_iterator t1 = tgts1.begin(); t1 != tgts1.end(); ++t1) {
1899 for (std::vector<std::string>::const_iterator t2 = tgts2.begin(); t2 != tgts2.end(); ++t2) {
1900 if (*t1 == *t2) {
1901 return true;
1902 }
1903 }
1904 }
1905 return false;
1906 }
1907
on_drag_motion(const Glib::RefPtr<Gdk::DragContext> & context,int x,int y,guint timestamp)1908 bool RackContainer::on_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint timestamp) {
1909 const std::vector<std::string>& tg = context->list_targets();
1910 if (!check_targets(tg, targets)) {
1911 if (check_targets(tg, othertargets)) {
1912 if (!autoscroll_connection.connected()) {
1913 autoscroll_connection = Glib::signal_timeout().connect(
1914 sigc::mem_fun(*this, &RackContainer::scrollother_timeout), AUTOSCROLL_TIMEOUT);
1915 }
1916 context->drag_status(Gdk::DragAction(0), timestamp);
1917 return true;
1918 }
1919 return false;
1920 }
1921 context->drag_status(Gdk::ACTION_MOVE, timestamp);
1922 int ind;
1923 find_index(x, y, nullptr, &ind);
1924 if (in_drag == ind) {
1925 return true;
1926 }
1927 if (in_drag > -2) {
1928 highlight_connection.disconnect();
1929 }
1930 highlight_connection = signal_draw().connect(sigc::bind(sigc::mem_fun(*this, &RackContainer::drag_highlight_draw), ind), true);
1931 queue_draw();
1932 in_drag = ind;
1933 if (!autoscroll_connection.connected()) {
1934 autoscroll_connection = Glib::signal_timeout().connect(
1935 sigc::mem_fun(*this, &RackContainer::scroll_timeout), AUTOSCROLL_TIMEOUT);
1936 }
1937 return true;
1938 }
1939
ensure_visible(RackBox & child)1940 void RackContainer::ensure_visible(RackBox& child) {
1941 Gtk::Allocation alloc = child.get_allocation();
1942 Gtk::Viewport *p = dynamic_cast<Gtk::Viewport*>(get_ancestor(GTK_TYPE_VIEWPORT));
1943 p->get_vadjustment()->clamp_page(alloc.get_y(), alloc.get_y()+alloc.get_height());
1944 }
1945
1946 static const double scroll_edge_size = 60.0;
1947 static const int step_size = 20;
1948
scrollother_timeout()1949 bool RackContainer::scrollother_timeout() {
1950 Gtk::Viewport *p = dynamic_cast<Gtk::Viewport*>(get_ancestor(GTK_TYPE_VIEWPORT));
1951 Glib::RefPtr<Gtk::Adjustment> a = p->get_vadjustment();
1952 double off = a->get_value();
1953 Gtk::Allocation alloc = get_allocation();
1954 int x, y;
1955 get_pointer(x, y);
1956 y -= alloc.get_height();
1957 double step;
1958 if (y < -scroll_edge_size) {
1959 step = step_size;
1960 } else {
1961 step = step_size * exp(-(y+scroll_edge_size)/(1.0*scroll_edge_size));
1962 if (step < 1.5) {
1963 return false;
1964 }
1965 }
1966 off = plugin_dict.stop_at(this, off, step_size, a->get_page_size());
1967 if (off < a->get_lower()) {
1968 off = a->get_lower();
1969 }
1970 if (off > a->get_upper() - a->get_page_size()) {
1971 off = a->get_upper() - a->get_page_size();
1972 }
1973 a->set_value(off);
1974 return true;
1975 }
1976
scroll_timeout()1977 bool RackContainer::scroll_timeout() {
1978 Gtk::Viewport *p = dynamic_cast<Gtk::Viewport*>(get_ancestor(GTK_TYPE_VIEWPORT));
1979 Glib::RefPtr<Gtk::Adjustment> a = p->get_vadjustment();
1980 double off = a->get_value();
1981 Gtk::Allocation alloc = get_allocation();
1982 int x, y;
1983 get_pointer(x, y);
1984 double sez = scroll_edge_size;
1985 if (sez > a->get_page_size() / 3) {
1986 sez = a->get_page_size() / 3;
1987 }
1988 double yw = y + alloc.get_y() - off;
1989 double step;
1990 if (yw <= sez) {
1991 step = step_size * (sez-yw) / sez;
1992 off = max(double(alloc.get_y()), off-step);
1993 } else {
1994 yw = a->get_page_size() - yw;
1995 if (yw <= sez) {
1996 step = step_size * (sez-yw) / sez;
1997 off = min(alloc.get_y()+alloc.get_height()-a->get_page_size(), off+step);
1998 } else {
1999 return true;
2000 }
2001 }
2002 if (off < a->get_lower()) {
2003 off = a->get_lower();
2004 }
2005 if (off > a->get_upper() - a->get_page_size()) {
2006 off = a->get_upper() - a->get_page_size();
2007 }
2008 a->set_value(off);
2009 return true;
2010 }
2011
stop_at_bottom(double off,double step_size,double pagesize)2012 double RackContainer::stop_at_bottom(double off, double step_size, double pagesize) {
2013 Gtk::Allocation alloc = get_allocation();
2014 double lim = alloc.get_y() + alloc.get_height() - pagesize;
2015 if (off >= lim) {
2016 return off;
2017 }
2018 return min(off+step_size, lim);
2019 }
2020
stop_at_top(double off,double step_size)2021 double RackContainer::stop_at_top(double off, double step_size) {
2022 Gtk::Allocation alloc = get_allocation();
2023 if (off < alloc.get_y()) {
2024 return off;
2025 }
2026 return max(off-step_size, double(alloc.get_y()));
2027 }
2028
on_drag_leave(const Glib::RefPtr<Gdk::DragContext> & context,guint timestamp)2029 void RackContainer::on_drag_leave(const Glib::RefPtr<Gdk::DragContext>& context, guint timestamp) {
2030 if (in_drag > -2) {
2031 highlight_connection.disconnect();
2032 queue_draw();
2033 in_drag = -2;
2034 }
2035 autoscroll_connection.disconnect();
2036 }
2037
on_drag_data_received(const Glib::RefPtr<Gdk::DragContext> & context,int x,int y,const Gtk::SelectionData & data,guint info,guint timestamp)2038 void RackContainer::on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint timestamp) {
2039 int ind;
2040 string before;
2041 find_index(x, y, &before, &ind);
2042 string id = data.get_data_as_string();
2043 std::string dtype = data.get_data_type();
2044 if (dtype == "application/x-gtk-tool-palette-item-mono" || dtype == "application/x-gtk-tool-palette-item-stereo") {
2045 plugin_dict.activate(id, before, true);
2046 } else {
2047 reorder(id, before);
2048 plugin_dict[id]->show(true);
2049 }
2050 }
2051
on_add(Widget * ch)2052 void RackContainer::on_add(Widget *ch) {
2053 assert(false);
2054 }
2055
add(RackBox & r,int pos)2056 void RackContainer::add(RackBox& r, int pos) {
2057 pack_start(r, false, true);
2058 if (pos != -1) {
2059 reorder_child(r, pos);
2060 }
2061 }
2062
set_child_count(int n)2063 void RackContainer::set_child_count(int n) {
2064 if (n == count) {
2065 return;
2066 }
2067 count = n;
2068 if (!count) {
2069 set_size_request(-1, min_containersize);
2070 } else {
2071 set_size_request(-1, -1);
2072 }
2073 }
2074
renumber()2075 void RackContainer::renumber() {
2076 rackbox_list l = get_children();
2077 int pos = 0;
2078 unsigned int post_pre = 1;
2079 for (rackbox_list::iterator c = l.begin(); c != l.end(); ++c, ++pos) {
2080 if (strcmp((*c)->get_id(), "ampstack") == 0) { // FIXME
2081 pos = 0;
2082 post_pre = 0;
2083 continue;
2084 }
2085 (*c)->set_position(pos, post_pre);
2086 }
2087 }
2088