2 Copyright (C) 2015 Paul Davis
3 Copyright (C) 2016 W.P. van Paassen
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.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <gtkmm/alignment.h>
22 #include <gtkmm/label.h>
23 #include <gtkmm/liststore.h>
25 #include "pbd/unwind.h"
26 #include "pbd/strsplit.h"
27 #include "pbd/file_utils.h"
29 #include "gtkmm2ext/actions.h"
30 #include "gtkmm2ext/bindings.h"
31 #include "gtkmm2ext/gtk_ui.h"
32 #include "gtkmm2ext/gui_thread.h"
33 #include "gtkmm2ext/utils.h"
35 #include "ardour/audioengine.h"
36 #include "ardour/filesystem_paths.h"
44 using namespace ARDOUR;
45 using namespace ArdourSurface;
48 using namespace Gtkmm2ext;
51 CC121::get_gui () const
54 const_cast<CC121*>(this)->build_gui ();
56 static_cast<Gtk::VBox*>(gui)->show_all();
61 CC121::tear_down_gui ()
64 Gtk::Widget *w = static_cast<Gtk::VBox*>(gui)->get_parent();
70 delete static_cast<CC121GUI*> (gui);
77 gui = (void*) new CC121GUI (*this);
80 /*--------------------*/
82 CC121GUI::CC121GUI (CC121& p)
86 , ignore_active_change (false)
88 set_border_width (12);
90 table.set_row_spacings (4);
91 table.set_col_spacings (6);
92 table.set_border_width (12);
93 table.set_homogeneous (false);
95 std::string data_file_path;
96 string name = "cc121.png";
97 Searchpath spath(ARDOUR::ardour_data_search_path());
98 spath.add_subdirectory_to_paths ("icons");
99 find_file (spath, name, data_file_path);
100 if (!data_file_path.empty()) {
101 image.set (data_file_path);
102 hpacker.pack_start (image, false, false);
106 Gtk::Alignment* align;
110 input_combo.pack_start (midi_port_columns.short_name);
111 output_combo.pack_start (midi_port_columns.short_name);
113 input_combo.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &CC121GUI::active_port_changed), &input_combo, true));
114 output_combo.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &CC121GUI::active_port_changed), &output_combo, false));
116 l = manage (new Gtk::Label);
117 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Incoming MIDI on:")));
118 l->set_alignment (1.0, 0.5);
119 table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
120 table.attach (input_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
123 l = manage (new Gtk::Label);
124 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Outgoing MIDI on:")));
125 l->set_alignment (1.0, 0.5);
126 table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
127 table.attach (output_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
130 build_available_action_menu ();
132 build_user_action_combo (function1_combo, CC121::ButtonState(0), CC121::Function1);
133 build_user_action_combo (function2_combo, CC121::ButtonState(0), CC121::Function2);
134 build_user_action_combo (function3_combo, CC121::ButtonState(0), CC121::Function3);
135 build_user_action_combo (function4_combo, CC121::ButtonState(0), CC121::Function4);
136 build_user_action_combo (value_combo, CC121::ButtonState(0), CC121::Value);
137 build_user_action_combo (lock_combo, CC121::ButtonState(0), CC121::Lock);
138 build_user_action_combo (eq1_combo, CC121::ButtonState(0), CC121::EQ1Enable);
139 build_user_action_combo (eq2_combo, CC121::ButtonState(0), CC121::EQ2Enable);
140 build_user_action_combo (eq3_combo, CC121::ButtonState(0), CC121::EQ3Enable);
141 build_user_action_combo (eq4_combo, CC121::ButtonState(0), CC121::EQ4Enable);
142 build_user_action_combo (eqtype_combo, CC121::ButtonState(0), CC121::EQType);
143 build_user_action_combo (allbypass_combo, CC121::ButtonState(0), CC121::AllBypass);
144 build_foot_action_combo (foot_combo, CC121::ButtonState(0));
145 action_table.set_row_spacings (4);
146 action_table.set_col_spacings (6);
147 action_table.set_border_width (12);
148 action_table.set_homogeneous (false);
150 l = manage (new Gtk::Label);
151 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Function 1")));
152 l->set_alignment (1.0, 0.5);
153 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
154 align = manage (new Alignment);
155 align->set (0.0, 0.5);
156 align->add (function1_combo);
157 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
160 l = manage (new Gtk::Label);
161 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Function 2")));
162 l->set_alignment (1.0, 0.5);
163 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
164 align = manage (new Alignment);
165 align->set (0.0, 0.5);
166 align->add (function2_combo);
167 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
170 l = manage (new Gtk::Label);
171 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Function 3")));
172 l->set_alignment (1.0, 0.5);
173 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
174 align = manage (new Alignment);
175 align->set (0.0, 0.5);
176 align->add (function3_combo);
177 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
180 l = manage (new Gtk::Label);
181 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Function 4")));
182 l->set_alignment (1.0, 0.5);
183 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
184 align = manage (new Alignment);
185 align->set (0.0, 0.5);
186 align->add (function4_combo);
187 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
190 l = manage (new Gtk::Label);
191 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Value")));
192 l->set_alignment (1.0, 0.5);
193 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
194 align = manage (new Alignment);
195 align->set (0.0, 0.5);
196 align->add (value_combo);
197 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
200 l = manage (new Gtk::Label);
201 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Lock")));
202 l->set_alignment (1.0, 0.5);
203 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
204 align = manage (new Alignment);
205 align->set (0.0, 0.5);
206 align->add (lock_combo);
207 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
210 l = manage (new Gtk::Label);
211 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("EQ1")));
212 l->set_alignment (1.0, 0.5);
213 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
214 align = manage (new Alignment);
215 align->set (0.0, 0.5);
216 align->add (eq1_combo);
217 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
220 l = manage (new Gtk::Label);
221 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("EQ2")));
222 l->set_alignment (1.0, 0.5);
223 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
224 align = manage (new Alignment);
225 align->set (0.0, 0.5);
226 align->add (eq2_combo);
227 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
230 l = manage (new Gtk::Label);
231 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("EQ3")));
232 l->set_alignment (1.0, 0.5);
233 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
234 align = manage (new Alignment);
235 align->set (0.0, 0.5);
236 align->add (eq3_combo);
237 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
240 l = manage (new Gtk::Label);
241 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("EQ4")));
242 l->set_alignment (1.0, 0.5);
243 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
244 align = manage (new Alignment);
245 align->set (0.0, 0.5);
246 align->add (eq4_combo);
247 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
250 l = manage (new Gtk::Label);
251 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("EQType")));
252 l->set_alignment (1.0, 0.5);
253 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
254 align = manage (new Alignment);
255 align->set (0.0, 0.5);
256 align->add (eqtype_combo);
257 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
260 l = manage (new Gtk::Label);
261 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("AllBypass")));
262 l->set_alignment (1.0, 0.5);
263 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
264 align = manage (new Alignment);
265 align->set (0.0, 0.5);
266 align->add (allbypass_combo);
267 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
270 l = manage (new Gtk::Label);
271 l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Footswitch")));
272 l->set_alignment (1.0, 0.5);
273 action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
274 align = manage (new Alignment);
275 align->set (0.0, 0.5);
276 align->add (foot_combo);
277 action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
280 table.attach (action_table, 0, 5, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
283 hpacker.pack_start (table, true, true);
284 pack_start (hpacker, false, false);
286 /* update the port connection combos */
288 update_port_combos ();
290 /* catch future changes to connection state */
292 fp.ConnectionChange.connect (connection_change_connection, invalidator (*this), boost::bind (&CC121GUI::connection_handler, this), gui_context());
295 CC121GUI::~CC121GUI ()
300 CC121GUI::connection_handler ()
302 /* ignore all changes to combobox active strings here, because we're
303 updating them to match a new ("external") reality - we were called
304 because port connections have changed.
307 PBD::Unwinder<bool> ici (ignore_active_change, true);
309 update_port_combos ();
313 CC121GUI::update_port_combos ()
315 vector<string> midi_inputs;
316 vector<string> midi_outputs;
318 ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsTerminal), midi_inputs);
319 ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsTerminal), midi_outputs);
321 Glib::RefPtr<Gtk::ListStore> input = build_midi_port_list (midi_inputs, true);
322 Glib::RefPtr<Gtk::ListStore> output = build_midi_port_list (midi_outputs, false);
323 bool input_found = false;
324 bool output_found = false;
327 input_combo.set_model (input);
328 output_combo.set_model (output);
330 Gtk::TreeModel::Children children = input->children();
331 Gtk::TreeModel::Children::iterator i;
332 i = children.begin();
333 ++i; /* skip "Disconnected" */
336 for (n = 1; i != children.end(); ++i, ++n) {
337 string port_name = (*i)[midi_port_columns.full_name];
338 if (fp.input_port()->connected_to (port_name)) {
339 input_combo.set_active (n);
346 input_combo.set_active (0); /* disconnected */
349 children = output->children();
350 i = children.begin();
351 ++i; /* skip "Disconnected" */
353 for (n = 1; i != children.end(); ++i, ++n) {
354 string port_name = (*i)[midi_port_columns.full_name];
355 if (fp.output_port()->connected_to (port_name)) {
356 output_combo.set_active (n);
363 output_combo.set_active (0); /* disconnected */
368 CC121GUI::build_available_action_menu ()
370 /* build a model of all available actions (needs to be tree structured
374 available_action_model = TreeStore::create (action_columns);
376 vector<string> paths;
377 vector<string> labels;
378 vector<string> tooltips;
380 vector<Glib::RefPtr<Gtk::Action> > actions;
382 ActionManager::get_all_actions (paths, labels, tooltips, keys, actions);
384 typedef std::map<string,TreeIter> NodeMap;
389 vector<string>::iterator k;
390 vector<string>::iterator p;
391 vector<string>::iterator t;
392 vector<string>::iterator l;
394 available_action_model->clear ();
397 TreeModel::Row parent;
399 /* Disabled item (row 0) */
401 rowp = available_action_model->append();
403 parent[action_columns.name] = _("Disabled");
405 for (l = labels.begin(), k = keys.begin(), p = paths.begin(), t = tooltips.begin(); l != labels.end(); ++k, ++p, ++t, ++l) {
408 vector<string> parts;
412 split (*p, parts, '/');
418 //kinda kludgy way to avoid displaying menu items as mappable
419 if ( parts[1] == _("Main_menu") )
421 if ( parts[1] == _("JACK") )
423 if ( parts[1] == _("redirectmenu") )
425 if ( parts[1] == _("Editor_menus") )
427 if ( parts[1] == _("RegionList") )
429 if ( parts[1] == _("ProcessorMenu") )
432 if ((r = nodes.find (parts[1])) == nodes.end()) {
434 /* top level is missing */
437 TreeModel::Row parent;
438 rowp = available_action_model->append();
439 nodes[parts[1]] = rowp;
441 parent[action_columns.name] = parts[1];
443 row = *(available_action_model->append (parent.children()));
447 row = *(available_action_model->append ((*r->second)->children()));
451 /* add this action */
454 row[action_columns.name] = *t;
457 row[action_columns.name] = *l;
462 /* ControlProtocol::access_action() is not interested in the
463 legacy "<Actions>/" prefix part of a path.
465 path = path.substr (strlen ("<Actions>/"));
467 row[action_columns.path] = path;
472 CC121GUI::action_changed (Gtk::ComboBox* cb, CC121::ButtonID id, CC121::ButtonState bs)
474 TreeModel::const_iterator row = cb->get_active ();
475 string action_path = (*row)[action_columns.path];
477 /* release binding */
478 fp.set_action (id, action_path, false, bs);
482 CC121GUI::build_action_combo (Gtk::ComboBox& cb, vector<pair<string,string> > const & actions, CC121::ButtonID id, CC121::ButtonState bs)
484 Glib::RefPtr<Gtk::ListStore> model (Gtk::ListStore::create (action_columns));
487 string current_action = fp.get_action (id, false, bs); /* lookup release action */
490 vector<pair<string,string> >::const_iterator i;
492 rowp = model->append();
494 row[action_columns.name] = _("Disabled");
495 row[action_columns.path] = string();
497 if (current_action.empty()) {
501 for (i = actions.begin(), n = 0; i != actions.end(); ++i, ++n) {
502 rowp = model->append();
504 row[action_columns.name] = i->first;
505 row[action_columns.path] = i->second;
506 if (current_action == i->second) {
511 cb.set_model (model);
512 cb.pack_start (action_columns.name);
514 if (active_row >= 0) {
515 cb.set_active (active_row);
518 cb.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &CC121GUI::action_changed), &cb, id, bs));
522 CC121GUI::build_foot_action_combo (Gtk::ComboBox& cb, CC121::ButtonState bs)
524 vector<pair<string,string> > actions;
526 actions.push_back (make_pair (string("Toggle Roll"), string(X_("Transport/ToggleRoll"))));
527 actions.push_back (make_pair (string("Toggle Rec-Enable"), string(X_("Transport/Record"))));
528 actions.push_back (make_pair (string("Toggle Roll+Rec"), string(X_("Transport/record-roll"))));
529 actions.push_back (make_pair (string("Toggle Loop"), string(X_("Transport/Loop"))));
530 actions.push_back (make_pair (string("Toggle Click"), string(X_("Transport/ToggleClick"))));
532 build_action_combo (cb, actions, CC121::Footswitch, bs);
536 CC121GUI::find_action_in_model (const TreeModel::iterator& iter, std::string const & action_path, TreeModel::iterator* found)
538 TreeModel::Row row = *iter;
539 string path = row[action_columns.path];
541 if (path == action_path) {
550 CC121GUI::build_user_action_combo (Gtk::ComboBox& cb, CC121::ButtonState bs, CC121::ButtonID id)
552 cb.set_model (available_action_model);
553 cb.pack_start (action_columns.name);
554 cb.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &CC121GUI::action_changed), &cb, id, bs));
556 /* set the active "row" to the right value for the current button binding */
558 string current_action = fp.get_action (id, false, bs); /* lookup release action */
560 if (current_action.empty()) {
561 cb.set_active (0); /* "disabled" */
565 TreeModel::iterator iter = available_action_model->children().end();
567 available_action_model->foreach_iter (sigc::bind (sigc::mem_fun (*this, &CC121GUI::find_action_in_model), current_action, &iter));
569 if (iter != available_action_model->children().end()) {
570 cb.set_active (iter);
576 Glib::RefPtr<Gtk::ListStore>
577 CC121GUI::build_midi_port_list (vector<string> const & ports, bool for_input)
579 Glib::RefPtr<Gtk::ListStore> store = ListStore::create (midi_port_columns);
582 row = *store->append ();
583 row[midi_port_columns.full_name] = string();
584 row[midi_port_columns.short_name] = _("Disconnected");
586 for (vector<string>::const_iterator p = ports.begin(); p != ports.end(); ++p) {
587 row = *store->append ();
588 row[midi_port_columns.full_name] = *p;
589 std::string pn = ARDOUR::AudioEngine::instance()->get_pretty_name_by_name (*p);
591 pn = (*p).substr ((*p).find (':') + 1);
593 row[midi_port_columns.short_name] = pn;
600 CC121GUI::active_port_changed (Gtk::ComboBox* combo, bool for_input)
602 if (ignore_active_change) {
606 TreeModel::iterator active = combo->get_active ();
607 string new_port = (*active)[midi_port_columns.full_name];
609 if (new_port.empty()) {
611 fp.input_port()->disconnect_all ();
613 fp.output_port()->disconnect_all ();
620 if (!fp.input_port()->connected_to (new_port)) {
621 fp.input_port()->disconnect_all ();
622 fp.input_port()->connect (new_port);
625 if (!fp.output_port()->connected_to (new_port)) {
626 fp.output_port()->disconnect_all ();
627 fp.output_port()->connect (new_port);