use ActionManager namespace, rather than ActionMap objects
[ardour.git] / libs / surfaces / cc121 / gui.cc
1 /*
2     Copyright (C) 2015 Paul Davis
3     Copyright (C) 2016 W.P. van Paassen
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <gtkmm/alignment.h>
22 #include <gtkmm/label.h>
23 #include <gtkmm/liststore.h>
24
25 #include "pbd/unwind.h"
26 #include "pbd/strsplit.h"
27 #include "pbd/file_utils.h"
28
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"
34
35 #include "ardour/audioengine.h"
36 #include "ardour/filesystem_paths.h"
37
38 #include "cc121.h"
39 #include "gui.h"
40
41 #include "pbd/i18n.h"
42
43 using namespace PBD;
44 using namespace ARDOUR;
45 using namespace ArdourSurface;
46 using namespace std;
47 using namespace Gtk;
48 using namespace Gtkmm2ext;
49
50 void*
51 CC121::get_gui () const
52 {
53         if (!gui) {
54                 const_cast<CC121*>(this)->build_gui ();
55         }
56         static_cast<Gtk::VBox*>(gui)->show_all();
57         return gui;
58 }
59
60 void
61 CC121::tear_down_gui ()
62 {
63         if (gui) {
64                 Gtk::Widget *w = static_cast<Gtk::VBox*>(gui)->get_parent();
65                 if (w) {
66                         w->hide();
67                         delete w;
68                 }
69         }
70         delete static_cast<CC121GUI*> (gui);
71         gui = 0;
72 }
73
74 void
75 CC121::build_gui ()
76 {
77         gui = (void*) new CC121GUI (*this);
78 }
79
80 /*--------------------*/
81
82 CC121GUI::CC121GUI (CC121& p)
83         : fp (p)
84         , table (2, 5)
85         , action_table (5, 4)
86         , ignore_active_change (false)
87 {
88         set_border_width (12);
89
90         table.set_row_spacings (4);
91         table.set_col_spacings (6);
92         table.set_border_width (12);
93         table.set_homogeneous (false);
94
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);
103         }
104
105         Gtk::Label* l;
106         Gtk::Alignment* align;
107         int row = 0;
108         int action_row = 1;
109
110         input_combo.pack_start (midi_port_columns.short_name);
111         output_combo.pack_start (midi_port_columns.short_name);
112
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));
115
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);
121         row++;
122
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);
128         row++;
129
130         build_available_action_menu ();
131
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);
149
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));
158         action_row++;
159
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));
168         action_row++;
169
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));
178         action_row++;
179
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));
188         action_row++;
189
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));
198         action_row++;
199
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));
208         action_row++;
209
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));
218         action_row++;
219
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));
228         action_row++;
229
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));
238         action_row++;
239
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));
248         action_row++;
249
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));
258         action_row++;
259
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));
268         action_row++;
269
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));
278         action_row++;
279
280         table.attach (action_table, 0, 5, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
281         row++;
282
283         hpacker.pack_start (table, true, true);
284         pack_start (hpacker, false, false);
285
286         /* update the port connection combos */
287
288         update_port_combos ();
289
290         /* catch future changes to connection state */
291
292         fp.ConnectionChange.connect (connection_change_connection, invalidator (*this), boost::bind (&CC121GUI::connection_handler, this), gui_context());
293 }
294
295 CC121GUI::~CC121GUI ()
296 {
297 }
298
299 void
300 CC121GUI::connection_handler ()
301 {
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.
305         */
306
307         PBD::Unwinder<bool> ici (ignore_active_change, true);
308
309         update_port_combos ();
310 }
311
312 void
313 CC121GUI::update_port_combos ()
314 {
315         vector<string> midi_inputs;
316         vector<string> midi_outputs;
317
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);
320
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;
325         int n;
326
327         input_combo.set_model (input);
328         output_combo.set_model (output);
329
330         Gtk::TreeModel::Children children = input->children();
331         Gtk::TreeModel::Children::iterator i;
332         i = children.begin();
333         ++i; /* skip "Disconnected" */
334
335
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);
340                         input_found = true;
341                         break;
342                 }
343         }
344
345         if (!input_found) {
346                 input_combo.set_active (0); /* disconnected */
347         }
348
349         children = output->children();
350         i = children.begin();
351         ++i; /* skip "Disconnected" */
352
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);
357                         output_found = true;
358                         break;
359                 }
360         }
361
362         if (!output_found) {
363                 output_combo.set_active (0); /* disconnected */
364         }
365 }
366
367 void
368 CC121GUI::build_available_action_menu ()
369 {
370         /* build a model of all available actions (needs to be tree structured
371          * more)
372          */
373
374         available_action_model = TreeStore::create (action_columns);
375
376         vector<string> paths;
377         vector<string> labels;
378         vector<string> tooltips;
379         vector<string> keys;
380         vector<Glib::RefPtr<Gtk::Action> > actions;
381
382         ActionManager::get_all_actions (paths, labels, tooltips, keys, actions);
383
384         typedef std::map<string,TreeIter> NodeMap;
385         NodeMap nodes;
386         NodeMap::iterator r;
387
388
389         vector<string>::iterator k;
390         vector<string>::iterator p;
391         vector<string>::iterator t;
392         vector<string>::iterator l;
393
394         available_action_model->clear ();
395
396         TreeIter rowp;
397         TreeModel::Row parent;
398
399         /* Disabled item (row 0) */
400
401         rowp = available_action_model->append();
402         parent = *(rowp);
403         parent[action_columns.name] = _("Disabled");
404
405         for (l = labels.begin(), k = keys.begin(), p = paths.begin(), t = tooltips.begin(); l != labels.end(); ++k, ++p, ++t, ++l) {
406
407                 TreeModel::Row row;
408                 vector<string> parts;
409
410                 parts.clear ();
411
412                 split (*p, parts, '/');
413
414                 if (parts.empty()) {
415                         continue;
416                 }
417
418                 //kinda kludgy way to avoid displaying menu items as mappable
419                 if ( parts[1] == _("Main_menu") )
420                         continue;
421                 if ( parts[1] == _("JACK") )
422                         continue;
423                 if ( parts[1] == _("redirectmenu") )
424                         continue;
425                 if ( parts[1] == _("Editor_menus") )
426                         continue;
427                 if ( parts[1] == _("RegionList") )
428                         continue;
429                 if ( parts[1] == _("ProcessorMenu") )
430                         continue;
431
432                 if ((r = nodes.find (parts[1])) == nodes.end()) {
433
434                         /* top level is missing */
435
436                         TreeIter rowp;
437                         TreeModel::Row parent;
438                         rowp = available_action_model->append();
439                         nodes[parts[1]] = rowp;
440                         parent = *(rowp);
441                         parent[action_columns.name] = parts[1];
442
443                         row = *(available_action_model->append (parent.children()));
444
445                 } else {
446
447                         row = *(available_action_model->append ((*r->second)->children()));
448
449                 }
450
451                 /* add this action */
452
453                 if (l->empty ()) {
454                         row[action_columns.name] = *t;
455                         action_map[*t] = *p;
456                 } else {
457                         row[action_columns.name] = *l;
458                         action_map[*l] = *p;
459                 }
460
461                 string path = (*p);
462                 /* ControlProtocol::access_action() is not interested in the
463                    legacy "<Actions>/" prefix part of a path.
464                 */
465                 path = path.substr (strlen ("<Actions>/"));
466
467                 row[action_columns.path] = path;
468         }
469 }
470
471 void
472 CC121GUI::action_changed (Gtk::ComboBox* cb, CC121::ButtonID id, CC121::ButtonState bs)
473 {
474         TreeModel::const_iterator row = cb->get_active ();
475         string action_path = (*row)[action_columns.path];
476
477         /* release binding */
478         fp.set_action (id, action_path, false, bs);
479 }
480
481 void
482 CC121GUI::build_action_combo (Gtk::ComboBox& cb, vector<pair<string,string> > const & actions, CC121::ButtonID id, CC121::ButtonState bs)
483 {
484         Glib::RefPtr<Gtk::ListStore> model (Gtk::ListStore::create (action_columns));
485         TreeIter rowp;
486         TreeModel::Row row;
487         string current_action = fp.get_action (id, false, bs); /* lookup release action */
488         int active_row = -1;
489         int n;
490         vector<pair<string,string> >::const_iterator i;
491
492         rowp = model->append();
493         row = *(rowp);
494         row[action_columns.name] = _("Disabled");
495         row[action_columns.path] = string();
496
497         if (current_action.empty()) {
498                 active_row = 0;
499         }
500
501         for (i = actions.begin(), n = 0; i != actions.end(); ++i, ++n) {
502                 rowp = model->append();
503                 row = *(rowp);
504                 row[action_columns.name] = i->first;
505                 row[action_columns.path] = i->second;
506                 if (current_action == i->second) {
507                         active_row = n+1;
508                 }
509         }
510
511         cb.set_model (model);
512         cb.pack_start (action_columns.name);
513
514         if (active_row >= 0) {
515                 cb.set_active (active_row);
516         }
517
518         cb.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &CC121GUI::action_changed), &cb, id, bs));
519 }
520
521 void
522 CC121GUI::build_foot_action_combo (Gtk::ComboBox& cb, CC121::ButtonState bs)
523 {
524         vector<pair<string,string> > actions;
525
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"))));
531
532         build_action_combo (cb, actions, CC121::Footswitch, bs);
533 }
534
535 bool
536 CC121GUI::find_action_in_model (const TreeModel::iterator& iter, std::string const & action_path, TreeModel::iterator* found)
537 {
538         TreeModel::Row row = *iter;
539         string path = row[action_columns.path];
540
541         if (path == action_path) {
542                 *found = iter;
543                 return true;
544         }
545
546         return false;
547 }
548
549 void
550 CC121GUI::build_user_action_combo (Gtk::ComboBox& cb, CC121::ButtonState bs, CC121::ButtonID id)
551 {
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));
555
556         /* set the active "row" to the right value for the current button binding */
557
558         string current_action = fp.get_action (id, false, bs); /* lookup release action */
559
560         if (current_action.empty()) {
561                 cb.set_active (0); /* "disabled" */
562                 return;
563         }
564
565         TreeModel::iterator iter = available_action_model->children().end();
566
567         available_action_model->foreach_iter (sigc::bind (sigc::mem_fun (*this, &CC121GUI::find_action_in_model), current_action, &iter));
568
569         if (iter != available_action_model->children().end()) {
570                 cb.set_active (iter);
571         } else {
572                 cb.set_active (0);
573         }
574 }
575
576 Glib::RefPtr<Gtk::ListStore>
577 CC121GUI::build_midi_port_list (vector<string> const & ports, bool for_input)
578 {
579         Glib::RefPtr<Gtk::ListStore> store = ListStore::create (midi_port_columns);
580         TreeModel::Row row;
581
582         row = *store->append ();
583         row[midi_port_columns.full_name] = string();
584         row[midi_port_columns.short_name] = _("Disconnected");
585
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);
590                 if (pn.empty ()) {
591                         pn = (*p).substr ((*p).find (':') + 1);
592                 }
593                 row[midi_port_columns.short_name] = pn;
594         }
595
596         return store;
597 }
598
599 void
600 CC121GUI::active_port_changed (Gtk::ComboBox* combo, bool for_input)
601 {
602         if (ignore_active_change) {
603                 return;
604         }
605
606         TreeModel::iterator active = combo->get_active ();
607         string new_port = (*active)[midi_port_columns.full_name];
608
609         if (new_port.empty()) {
610                 if (for_input) {
611                         fp.input_port()->disconnect_all ();
612                 } else {
613                         fp.output_port()->disconnect_all ();
614                 }
615
616                 return;
617         }
618
619         if (for_input) {
620                 if (!fp.input_port()->connected_to (new_port)) {
621                         fp.input_port()->disconnect_all ();
622                         fp.input_port()->connect (new_port);
623                 }
624         } else {
625                 if (!fp.output_port()->connected_to (new_port)) {
626                         fp.output_port()->disconnect_all ();
627                         fp.output_port()->connect (new_port);
628                 }
629         }
630 }