2 Copyright (C) 2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <gtkmm2ext/gtk_ui.h>
26 #include <gtkmm2ext/utils.h>
27 #include <sigc++/bind.h>
29 #include "connection_editor.h"
31 #include <ardour/session.h>
32 #include <ardour/session_connection.h>
33 #include <ardour/audioengine.h>
34 #include <ardour/connection.h>
45 using namespace ARDOUR;
50 ConnectionEditor::ConnectionEditor ()
51 : ArdourDialog (_("ardour: connections")),
52 input_frame (_("Input Connections")),
53 output_frame (_("Output Connections")),
54 new_input_connection_button (_("New Input")),
55 new_output_connection_button (_("New Output")),
56 delete_connection_button (_("Delete")),
57 clear_button (_("Clear")),
58 add_port_button (_("Add Port")),
59 ok_button (_("Close")),
60 cancel_button (_("Cancel")),
61 rescan_button (_("Rescan"))
64 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
68 current_connection = 0;
69 push_at_front = false;
71 set_name ("ConnectionEditorWindow");
73 ok_button.set_name ("ConnectionEditorButton");
74 cancel_button.set_name ("ConnectionEditorButton");
75 rescan_button.set_name ("ConnectionEditorButton");
76 new_input_connection_button.set_name ("ConnectionEditorButton");
77 new_output_connection_button.set_name ("ConnectionEditorButton");
78 clear_button.set_name ("ConnectionEditorButton");
80 button_frame.set_name ("ConnectionEditorFrame");
81 input_frame.set_name ("ConnectionEditorFrame");
82 output_frame.set_name ("ConnectionEditorFrame");
84 button_box.set_spacing (15);
85 button_box.set_border_width (5);
86 Gtkmm2ext::set_size_request_to_display_given_text (ok_button, _("OK"), 40, 15);
87 button_box.pack_end (ok_button, false, false);
88 // button_box.pack_end (cancel_button, false, false);
90 button_frame.add (button_box);
92 ok_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::accept));
93 cancel_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::cancel));
94 cancel_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::rescan));
96 notebook.set_name ("ConnectionEditorNotebook");
97 notebook.set_size_request (-1, 125);
99 clear_button.set_name ("ConnectionEditorButton");
100 add_port_button.set_name ("ConnectionEditorButton");
101 Gtkmm2ext::set_size_request_to_display_given_text (add_port_button, _("Add Port"), 35, 15);
103 selector_frame.set_name ("ConnectionEditorFrame");
104 port_frame.set_name ("ConnectionEditorFrame");
106 selector_frame.set_label (_("Available Ports"));
108 selector_button_box.set_spacing (5);
109 selector_button_box.set_border_width (5);
110 Gtkmm2ext::set_size_request_to_display_given_text (rescan_button, _("Rescan"), 35, 15);
111 selector_button_box.pack_start (rescan_button, false, false);
113 selector_box.set_spacing (5);
114 selector_box.set_border_width (5);
115 selector_box.pack_start (notebook);
116 selector_box.pack_start (selector_button_box);
118 selector_frame.add (selector_box);
120 port_box.set_spacing (5);
121 port_box.set_border_width (3);
123 port_button_box.set_spacing (5);
124 port_button_box.set_border_width (2);
126 port_button_box.pack_start (add_port_button, false, false);
127 port_and_button_box.set_border_width (5);
128 port_and_button_box.pack_start (port_button_box, false, false);
129 port_and_button_box.pack_start (port_box);
131 port_frame.add (port_and_button_box);
133 port_and_selector_box.set_spacing (5);
134 port_and_selector_box.pack_start (port_frame);
135 port_and_selector_box.pack_start (selector_frame);
137 right_vbox.set_spacing (5);
138 right_vbox.set_border_width (5);
139 right_vbox.pack_start (port_and_selector_box);
141 input_connection_model = ListStore::create (connection_columns);
142 output_connection_model = ListStore::create (connection_columns);
144 input_connection_display.set_model (input_connection_model);
145 output_connection_display.set_model (output_connection_model);
147 input_connection_display.append_column (_("Connections"), connection_columns.name);
148 output_connection_display.append_column (_("Connections"), connection_columns.name);
150 input_connection_display.get_selection()->set_mode(Gtk::SELECTION_SINGLE);
151 input_connection_display.set_size_request (80, -1);
152 input_connection_display.set_name ("ConnectionEditorConnectionList");
154 output_connection_display.get_selection()->set_mode(Gtk::SELECTION_SINGLE);
155 output_connection_display.set_size_request (80, -1);
156 output_connection_display.set_name ("ConnectionEditorConnectionList");
158 input_connection_display.get_selection()->signal_changed().connect (bind (mem_fun(*this, &ConnectionEditor::selection_changed), &input_connection_display));
159 output_connection_display.get_selection()->signal_changed().connect (bind (mem_fun(*this, &ConnectionEditor::selection_changed), &output_connection_display));
162 input_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
163 output_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
165 input_scroller.add (input_connection_display);
166 output_scroller.add (output_connection_display);
168 input_box.set_border_width (5);
169 input_box.set_spacing (5);
170 input_box.pack_start (input_scroller);
171 input_box.pack_start (new_input_connection_button, false, false);
172 input_frame.add (input_box);
174 output_box.set_border_width (5);
175 output_box.set_spacing (5);
176 output_box.pack_start (output_scroller);
177 output_box.pack_start (new_output_connection_button, false, false);
178 output_frame.add (output_box);
180 connection_box.set_spacing (5);
181 connection_box.pack_start (input_frame);
182 connection_box.pack_start (output_frame);
184 left_vbox.set_spacing (5);
185 left_vbox.pack_start (connection_box);
187 main_hbox.set_border_width (10);
188 main_hbox.set_spacing (5);
189 main_hbox.pack_start (left_vbox);
190 main_hbox.pack_start (right_vbox);
192 main_vbox.set_border_width (10);
193 main_vbox.set_spacing (5);
194 main_vbox.pack_start (main_hbox);
195 main_vbox.pack_start (button_frame, false, false);
197 get_vbox()->pack_start (main_vbox);
199 clear_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::clear));
200 add_port_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::add_port));
201 new_input_connection_button.signal_clicked().connect (bind (mem_fun(*this, &ConnectionEditor::new_connection), true));
202 new_output_connection_button.signal_clicked().connect (bind (mem_fun(*this, &ConnectionEditor::new_connection), false));
203 delete_connection_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::delete_connection));
206 ConnectionEditor::~ConnectionEditor()
211 ConnectionEditor::set_session (Session *s)
215 ArdourDialog::set_session (s);
218 session->ConnectionAdded.connect (mem_fun(*this, &ConnectionEditor::proxy_add_connection_and_select));
219 session->ConnectionRemoved.connect (mem_fun(*this, &ConnectionEditor::proxy_remove_connection));
227 ConnectionEditor::rescan ()
229 refill_connection_display ();
234 ConnectionEditor::cancel ()
240 ConnectionEditor::accept ()
246 ConnectionEditor::clear ()
248 if (current_connection) {
249 current_connection->clear ();
254 ConnectionEditor::on_map ()
256 refill_connection_display ();
261 ConnectionEditor::add_connection (ARDOUR::Connection *connection)
265 if (dynamic_cast<InputConnection *> (connection)) {
268 row = *(input_connection_model->prepend());
270 row = *(input_connection_model->append());
276 row = *(output_connection_model->prepend());
278 row = *(output_connection_model->append());
282 row[connection_columns.connection] = connection;
283 row[connection_columns.name] = connection->name();
287 ConnectionEditor::remove_connection (ARDOUR::Connection *connection)
289 TreeModel::iterator i;
290 Glib::RefPtr<TreeModel> model = input_connection_model;
292 if (dynamic_cast<InputConnection *> (connection) == 0) {
293 model = output_connection_model;
296 TreeModel::Children rows = model->children();
298 for (i = rows.begin(); i != rows.end(); ++i) {
299 if ((*i)[connection_columns.connection] == connection) {
307 ConnectionEditor::proxy_add_connection_and_select (ARDOUR::Connection *connection)
309 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun(*this, &ConnectionEditor::add_connection_and_select), connection));
313 ConnectionEditor::proxy_remove_connection (ARDOUR::Connection *connection)
315 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun(*this, &ConnectionEditor::remove_connection), connection));
319 ConnectionEditor::add_connection_and_select (ARDOUR::Connection *connection)
321 add_connection (connection);
324 // if (dynamic_cast<InputConnection *> (connection)) {
325 // input_connection_display.rows().front().select ();
327 // output_connection_display.rows().front().select ();
332 ConnectionEditor::refill_connection_display ()
334 input_connection_display.set_model (Glib::RefPtr<TreeModel>(0));
335 output_connection_display.set_model (Glib::RefPtr<TreeModel>(0));
337 input_connection_model->clear();
338 output_connection_model->clear();
340 current_connection = 0;
343 session->foreach_connection (this, &ConnectionEditor::add_connection);
346 input_connection_display.set_model (input_connection_model);
347 output_connection_display.set_model (output_connection_model);
352 ConnectionEditor::selection_changed (TreeView* view)
354 ARDOUR::Connection *old_current = current_connection;
357 TreeModel::Path path;
358 Glib::RefPtr<TreeView::Selection> selection = view->get_selection();
359 Glib::RefPtr<TreeModel> model = view->get_model();
360 bool input = (view == &input_connection_display);
362 iter = model->get_iter (path);
364 current_connection = (*iter)[connection_columns.connection];
366 if (old_current != current_connection) {
367 config_connection.disconnect ();
368 connect_connection.disconnect ();
371 if (current_connection) {
372 config_connection = current_connection->ConfigurationChanged.connect
373 (bind (mem_fun(*this, &ConnectionEditor::configuration_changed), input));
374 connect_connection = current_connection->ConnectionsChanged.connect
375 (bind (mem_fun(*this, &ConnectionEditor::connections_changed), input));
378 display_connection_state (input);
383 ConnectionEditor::configuration_changed (bool for_input)
385 display_connection_state (for_input);
389 ConnectionEditor::connections_changed (int which_port, bool for_input)
391 display_connection_state (for_input);
395 ConnectionEditor::display_ports ()
397 if (session == 0 || current_connection == 0) {
401 using namespace Notebook_Helpers;
403 typedef std::map<std::string,std::vector<std::pair<std::string,std::string> > > PortMap;
406 PageList& pages = notebook.pages();
408 vector<string> rowdata;
411 current_page = notebook.get_current_page ();
414 /* get relevant current JACK ports */
416 for_input = (dynamic_cast<InputConnection *> (current_connection) != 0);
418 ports = session->engine().get_ports ("", JACK_DEFAULT_AUDIO_TYPE, for_input?JackPortIsOutput:JackPortIsInput);
424 /* find all the client names and group their ports into a list-by-client */
426 for (int n = 0; ports[n]; ++n) {
428 pair<string,vector<pair<string,string> > > newpair;
429 pair<string,string> strpair;
430 pair<PortMap::iterator,bool> result;
432 string str = ports[n];
433 string::size_type pos;
436 pos = str.find (':');
438 newpair.first = str.substr (0, pos);
439 portname = str.substr (pos+1);
441 result = portmap.insert (newpair);
443 strpair.first = portname;
444 strpair.second = str;
446 result.first->second.push_back (strpair);
451 for (i = portmap.begin(); i != portmap.end(); ++i) {
453 Box *client_box = manage (new VBox);
454 Gtk::CTreeView *display = manage (new Gtk::TreeView);
455 RefPtr<TreeModel> model = TreeModel::create (columns);
456 ScrolledWindow *scroller = manage (new ScrolledWindow);
458 display->set_selection_mode (GTK_SELECTION_SINGLE);
459 display->set_name ("ConnectionEditorList");
461 for (vector<pair<string,string> >::iterator s = i->second.begin(); s != i->second.end(); ++s) {
463 Row row = model->append ();
465 row[displayed_name] = s->first;
466 row[full_name] = s->second;
469 display->get_selection()->signal_changed().connect (bind (mem_fun(*this, &ConnectionEditor::port_selection_handler), display));
471 Label *tab_label = manage (new Label);
473 tab_label->set_name ("ConnectionEditorNotebookTab");
474 tab_label->set_text ((*i).first);
476 display->set_model (model);
478 scroller->add (*client_port_display);
479 scroller->set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
481 client_box->pack_start (*scroller);
483 pages.push_back (TabElem (*client_box, *tab_label));
486 notebook.set_page (current_page);
487 notebook.show.connect (bind (mem_fun (notebook, &Notebook::set_page), current_page));
488 selector_box.show_all ();
492 ConnectionEditor::display_connection_state (bool for_input)
494 Glib::Mutex::Lock lm (port_display_lock);
497 if (session == 0 || current_connection == 0) {
501 string frame_label = _("Connection \"");
502 frame_label += current_connection->name();
503 frame_label += _("\"");
504 port_frame.set_label (frame_label);
506 for (slist<ScrolledWindow *>::iterator i = port_displays.begin(); i != port_displays.end(); ) {
508 slist<ScrolledWindow *>::iterator tmp;
513 port_box.remove (**i);
515 port_displays.erase (i);
520 limit = current_connection->nports();
522 for (uint32_t n = 0; n < limit; ++n) {
525 ScrolledWindow *scroller;
527 const gchar *title[1];
529 string really_short_name;
532 snprintf(buf, sizeof(buf)-1, _("in %d"), n+1);
534 snprintf(buf, sizeof(buf)-1, _("out %d"), n+1);
537 tview = manage (new TreeView());
538 Glib::RefPtr<ListStore> port_model = ListStore::create (*port_display_columns);
540 tview->set_model (port_model);
541 tview->append_column (_(buf), port_display_columns->name);
542 tview->set_selection()->set_mode (Gtk::SELECTION_SINGLE);
543 tview->set_data ("port", (gpointer) ((intptr_t) n));
544 tview->set_headers_visible (true);
545 tview->set_name ("ConnectionEditorPortList");
546 tview->signal_button_press_event().connect (bind (mem_fun(*this, &ConnectionEditor::port_column_click), clist));
548 scroller = manage (new ScrolledWindow);
550 scroller->add (*tview);
551 scroller->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
553 port_displays.insert (port_displays.end(), scroller);
554 port_box.pack_start (*scroller);
556 scroller->set_size_request (-1, 75);
558 /* now fill the clist with the current connections */
560 const ARDOUR::Connection::PortList& connections = current_connection->port_connections (n);
562 for (ARDOUR::Connection::PortList::const_iterator i = connections.begin(); i != connections.end(); ++i) {
564 TreeModel::Row row = *(model->append());
566 row[port_connection_columns.name] = (*i)->name();
570 port_box.show_all ();
574 ConnectionEditor::port_selection_changed (TreeView* tview)
576 Glib::RefPtr<TreeView::Selection> sel = tview->get_selection();
577 TreeModel::iterator iter = sel->get_selected();
579 if (!current_connection) {
584 TreeModel::Row row = *iter;
585 string other_port_name = row[port_display_columns.full_name];
588 if (current_connection && selected_port >= 0) {
589 current_connection->add_connection (selected_port, other_port_name);
595 ConnectionEditor::add_port ()
597 if (current_connection) {
598 current_connection->add_port ();
603 ConnectionEditor::connection_port_button_press_event (GdkEventButton* ev, TreeView* tview)
605 Glib::Mutex::Lock lm (port_display_lock);
607 int which_port = reinterpret_cast<intptr_t> (treeview->get_data ("port"));
609 if (which_port != selected_port) {
611 selected_port = which_port;
614 tview->set_name ("ConnectionEditorPortListSelected");
616 for (slist<ScrolledWindow *>::iterator i = port_displays.begin(); i != port_displays.end(); ++i) {
618 Widget *child = (*i)->get_child();
620 if (static_cast<TreeView *> (child) != tview) {
621 child->set_name ("ConnectionEditorPortList");
622 child->queue_draw ();
629 clist->set_name ("ConnectionEditorPortList");
635 ConnectionEditor::connection_selection_changed (TreeView* tview);
637 Glib::RefPtr<TreeView::Selection> sel = tview->get_selection();
638 TreeModel::iterator iter = sel->get_selected();
641 TreeModel::Row row = *iter;
642 current_connection = row[XXXX_display_columns.connection];
644 current_connection = 0;
649 ConnectionEditor::new_connection (bool for_input)
657 ArdourPrompter prompter (true);
658 prompter.set_prompt (_("Name for new connection:"));
659 prompter.done.connect (Gtk::Main::quit.slot());
661 switch (prompter.run()) {
662 case Gtk::RESPONSE_ACCEPT:
663 prompter.get_result (name);
664 push_at_front = true;
667 session->add_connection (new ARDOUR::InputConnection (name));
669 session->add_connection (new ARDOUR::OutputConnection (name));
672 push_at_front = false;
681 ConnectionEditor::delete_connection ()
683 if (session && current_connection) {
684 session->remove_connection (current_connection);
685 current_connection = 0;
690 ConnectionEditor::port_button_event (GdkEventButton *ev, Treeview* treeview)
694 TreeModel::Path path;
695 TreeViewColumn* column;
699 if (current_connection == 0) {
703 if (!(Keyboard::is_delete_event (ev))) {
707 if (!treeview->get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
711 if ((iter = treeview->get_model()->get_iter (path))) {
714 string port_name = (*iter)[columns.full_name];
715 int which_port = (intptr_t) treeview->get_data ("port");
717 current_connection->remove_connection (which_port, port_name);