replaced slot() with mem_fun() and ptr_fun().
[ardour.git] / gtk2_ardour / connection_editor.cc
1 /*
2     Copyright (C) 2002 Paul Davis
3
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.
8
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.
13
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.
17
18     $Id$
19 */
20 #include <stdint.h>
21
22 #include <gtkmm2ext/gtk_ui.h>
23 #include <gtkmm2ext/utils.h>
24 #include <sigc++/bind.h>
25
26 #include "connection_editor.h"
27
28 #include <ardour/session.h>
29 #include <ardour/session_connection.h>
30 #include <ardour/audioengine.h>
31 #include <ardour/connection.h>
32
33 #include "utils.h"
34 #include "keyboard.h"
35 #include "prompter.h"
36
37 #include "i18n.h"
38
39 #include <inttypes.h>
40
41 using namespace std;
42 using namespace ARDOUR;
43 using namespace Gtk;
44 using namespace sigc;
45
46 ConnectionEditor::ConnectionEditor ()
47         : ArdourDialog ("connection editor"),
48           input_connection_display (1),
49           output_connection_display (1),
50           input_frame (_("Input Connections")),
51           output_frame (_("Output Connections")),
52           new_input_connection_button (_("New Input")),
53           new_output_connection_button (_("New Output")),
54           delete_connection_button (_("Delete")),
55           clear_button (_("Clear")),
56           add_port_button (_("Add Port")),
57           ok_button (_("Close")),
58           cancel_button (_("Cancel")),
59           rescan_button (_("Rescan"))
60           
61 {
62         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
63
64         session = 0;
65         selected_port = -1;
66         current_connection = 0;
67         push_at_front = false;
68
69         set_name ("ConnectionEditorWindow");
70
71         ok_button.set_name ("ConnectionEditorButton");
72         cancel_button.set_name ("ConnectionEditorButton");
73         rescan_button.set_name ("ConnectionEditorButton");
74         new_input_connection_button.set_name ("ConnectionEditorButton");
75         new_output_connection_button.set_name ("ConnectionEditorButton");
76         clear_button.set_name ("ConnectionEditorButton");
77         
78         button_frame.set_name ("ConnectionEditorFrame");
79         input_frame.set_name ("ConnectionEditorFrame");
80         output_frame.set_name ("ConnectionEditorFrame");
81
82         button_box.set_spacing (15);
83         button_box.set_border_width (5);
84         Gtkmm2ext::set_size_request_to_display_given_text (ok_button, _("OK"), 40, 15);
85         button_box.pack_end (ok_button, false, false);
86         // button_box.pack_end (cancel_button, false, false);
87         cancel_button.hide();
88         button_frame.add (button_box);
89
90         ok_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::accept));
91         cancel_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::cancel));
92         cancel_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::rescan));
93
94         notebook.set_name ("ConnectionEditorNotebook");
95         notebook.set_size_request (-1, 125);
96
97         clear_button.set_name ("ConnectionEditorButton");
98         add_port_button.set_name ("ConnectionEditorButton");
99         Gtkmm2ext::set_size_request_to_display_given_text (add_port_button, _("Add Port"), 35, 15);
100
101         selector_frame.set_name ("ConnectionEditorFrame");
102         port_frame.set_name ("ConnectionEditorFrame");
103
104         selector_frame.set_label (_("Available Ports"));
105         
106         selector_button_box.set_spacing (5);
107         selector_button_box.set_border_width (5);
108         Gtkmm2ext::set_size_request_to_display_given_text (rescan_button, _("Rescan"), 35, 15);
109         selector_button_box.pack_start (rescan_button, false, false);
110
111         selector_box.set_spacing (5);
112         selector_box.set_border_width (5);
113         selector_box.pack_start (notebook);
114         selector_box.pack_start (selector_button_box);
115
116         selector_frame.add (selector_box);
117
118         port_box.set_spacing (5);
119         port_box.set_border_width (3);
120
121         port_button_box.set_spacing (5);
122         port_button_box.set_border_width (2);
123
124         port_button_box.pack_start (add_port_button, false, false);
125         port_and_button_box.set_border_width (5);
126         port_and_button_box.pack_start (port_button_box, false, false);
127         port_and_button_box.pack_start (port_box);
128
129         port_frame.add (port_and_button_box);
130
131         port_and_selector_box.set_spacing (5);
132         port_and_selector_box.pack_start (port_frame);
133         port_and_selector_box.pack_start (selector_frame);
134
135         right_vbox.set_spacing (5);
136         right_vbox.set_border_width (5);
137         right_vbox.pack_start (port_and_selector_box);
138
139         input_connection_display.set_shadow_type (Gtk::SHADOW_IN);
140         input_connection_display.set_selection_mode (GTK_SELECTION_SINGLE);
141         input_connection_display.set_size_request (80, -1);
142         input_connection_display.set_name ("ConnectionEditorConnectionList");
143         input_connection_display.select_row.connect (bind (mem_fun(*this, &ConnectionEditor::connection_selected), true));
144
145         output_connection_display.set_shadow_type (Gtk::SHADOW_IN);
146         output_connection_display.set_selection_mode (GTK_SELECTION_SINGLE);
147         output_connection_display.set_size_request (80, -1);
148         output_connection_display.set_name ("ConnectionEditorConnectionList");
149         output_connection_display.select_row.connect (bind (mem_fun(*this, &ConnectionEditor::connection_selected), false));
150
151         input_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
152         output_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
153
154         input_scroller.add_with_viewport (input_connection_display);
155         output_scroller.add_with_viewport (output_connection_display);
156
157         input_box.set_border_width (5);
158         input_box.set_spacing (5);
159         input_box.pack_start (input_scroller);
160         input_box.pack_start (new_input_connection_button, false, false);
161         input_frame.add (input_box);
162
163         output_box.set_border_width (5);
164         output_box.set_spacing (5);
165         output_box.pack_start (output_scroller);
166         output_box.pack_start (new_output_connection_button, false, false);
167         output_frame.add (output_box);
168
169         connection_box.set_spacing (5);
170         connection_box.pack_start (input_frame);
171         connection_box.pack_start (output_frame);
172
173         left_vbox.set_spacing (5);
174         left_vbox.pack_start (connection_box);
175
176         main_hbox.set_border_width (10);
177         main_hbox.set_spacing (5);
178         main_hbox.pack_start (left_vbox);
179         main_hbox.pack_start (right_vbox);
180
181         main_vbox.set_border_width (10);
182         main_vbox.set_spacing (5);
183         main_vbox.pack_start (main_hbox);
184         main_vbox.pack_start (button_frame, false, false);
185
186         set_title (_("ardour: connections"));
187         add (main_vbox);
188
189         delete_event.connect (bind (ptr_fun (just_hide_it), reinterpret_cast<Window *> (this)));
190
191         clear_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::clear));
192         add_port_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::add_port));
193         new_input_connection_button.signal_clicked().connect (bind (mem_fun(*this, &ConnectionEditor::new_connection), true));
194         new_output_connection_button.signal_clicked().connect (bind (mem_fun(*this, &ConnectionEditor::new_connection), false));
195         delete_connection_button.signal_clicked().connect (mem_fun(*this, &ConnectionEditor::delete_connection));
196 }
197
198 ConnectionEditor::~ConnectionEditor()
199 {
200 }
201
202 void
203 ConnectionEditor::set_session (Session *s)
204 {
205         if (s != session) {
206
207                 ArdourDialog::set_session (s);
208                 
209                 if (session) {
210                         session->ConnectionAdded.connect (mem_fun(*this, &ConnectionEditor::proxy_add_connection_and_select));
211                         session->ConnectionRemoved.connect (mem_fun(*this, &ConnectionEditor::proxy_remove_connection));
212                 } else {
213                         hide ();
214                 }
215         }
216 }
217
218 void
219 ConnectionEditor::rescan ()
220 {
221         refill_connection_display ();
222         display_ports ();
223 }
224
225 void
226 ConnectionEditor::cancel ()
227 {
228         hide ();
229 }
230
231 void
232 ConnectionEditor::accept ()
233 {
234         hide ();
235 }
236
237 void
238 ConnectionEditor::clear ()
239 {
240         if (current_connection) {
241                 current_connection->clear ();
242         }
243 }
244
245 gint
246 ConnectionEditor::map_event_impl (GdkEventAny *ev)
247 {
248         refill_connection_display ();
249         return Window::map_event_impl (ev);
250 }
251
252 void
253 ConnectionEditor::add_connection (ARDOUR::Connection *connection)
254 {
255         using namespace CList_Helpers;
256         const char *rowtext[1];
257
258         rowtext[0] = connection->name().c_str();
259
260         if (dynamic_cast<InputConnection *> (connection)) {
261                 if (push_at_front) {
262                         input_connection_display.rows().push_front (rowtext);
263                         input_connection_display.rows().front().set_data (connection);
264                 } else {
265                         input_connection_display.rows().push_back (rowtext);
266                         input_connection_display.rows().back().set_data (connection);
267                 }
268         } else {
269                 if (push_at_front) {
270                         output_connection_display.rows().push_front (rowtext);
271                         output_connection_display.rows().front().set_data (connection);
272                 } else {
273                         output_connection_display.rows().push_back (rowtext);
274                         output_connection_display.rows().back().set_data (connection);
275                 }
276         }
277 }
278
279 void
280 ConnectionEditor::remove_connection (ARDOUR::Connection *connection)
281 {
282         using namespace Gtk::CList_Helpers;
283         RowList::iterator i;
284         RowList* rlist;
285
286         if (dynamic_cast<InputConnection *> (connection)) {
287                 rlist = &input_connection_display.rows();
288         } else {
289                 rlist = &output_connection_display.rows();
290         }
291
292         if ((i = rlist->find_data (connection)) != rlist->end()) {
293                 rlist->erase (i);
294         }
295 }
296
297 void
298 ConnectionEditor::proxy_add_connection_and_select (ARDOUR::Connection *connection)
299 {
300         Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun(*this, &ConnectionEditor::add_connection_and_select), connection));
301 }
302
303 void
304 ConnectionEditor::proxy_remove_connection (ARDOUR::Connection *connection)
305 {
306         Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun(*this, &ConnectionEditor::remove_connection), connection));
307 }
308
309 void
310 ConnectionEditor::add_connection_and_select (ARDOUR::Connection *connection)
311 {
312         add_connection (connection);
313
314         if (dynamic_cast<InputConnection *> (connection)) {
315                 input_connection_display.rows().front().select ();
316         } else {
317                 output_connection_display.rows().front().select ();
318         }
319 }
320
321 void
322 ConnectionEditor::refill_connection_display ()
323 {
324         input_connection_display.clear();
325         output_connection_display.clear();
326
327         current_connection = 0;
328         
329         if (session) {
330                 session->foreach_connection (this, &ConnectionEditor::add_connection);
331         }
332 }
333         
334 void
335 ConnectionEditor::connection_selected (gint row, gint col, GdkEvent *ev, bool input)
336 {
337         ARDOUR::Connection *old_current = current_connection;
338
339
340         if (input) {
341                 output_connection_display.unselect_all ();
342                 current_connection = reinterpret_cast<ARDOUR::Connection*> (input_connection_display.row(row).get_data());
343         } else {
344                 input_connection_display.unselect_all ();
345                 current_connection = reinterpret_cast<ARDOUR::Connection*> (output_connection_display.row(row).get_data());
346         }
347
348         if (old_current != current_connection) {
349                 config_connection.disconnect ();
350                 connect_connection.disconnect ();
351         }
352
353         if (current_connection) {
354                 config_connection = current_connection->ConfigurationChanged.connect 
355                         (bind (mem_fun(*this, &ConnectionEditor::configuration_changed), input));
356                 connect_connection = current_connection->ConnectionsChanged.connect 
357                         (bind (mem_fun(*this, &ConnectionEditor::connections_changed), input));
358         }
359
360         display_connection_state (input);
361         display_ports ();
362 }
363
364 void
365 ConnectionEditor::configuration_changed (bool for_input)
366 {
367         display_connection_state (for_input);
368 }
369
370 void
371 ConnectionEditor::connections_changed (int which_port, bool for_input)
372 {
373         display_connection_state (for_input);
374 }
375
376 void
377 ConnectionEditor::display_ports ()
378 {
379         if (session == 0 || current_connection == 0) {
380                 return;
381         }
382         
383         using namespace Notebook_Helpers;
384         using namespace CList_Helpers;
385
386         typedef map<string,vector<pair<string,string> > > PortMap;
387         PortMap portmap;
388         const char **ports;
389         PageList& pages = notebook.pages();
390         gint current_page;
391         vector<string> rowdata;
392         bool for_input;
393
394         current_page = notebook.get_current_page_num ();
395         pages.clear ();
396
397         /* get relevant current JACK ports */
398
399         for_input = (dynamic_cast<InputConnection *> (current_connection) != 0);
400
401         ports = session->engine().get_ports ("", JACK_DEFAULT_AUDIO_TYPE, for_input?JackPortIsOutput:JackPortIsInput);
402
403         if (ports == 0) {
404                 return;
405         }
406
407         /* find all the client names and group their ports into a list-by-client */
408         
409         for (int n = 0; ports[n]; ++n) {
410
411                 pair<string,vector<pair<string,string> > > newpair;
412                 pair<string,string> strpair;
413                 pair<PortMap::iterator,bool> result;
414
415                 string str = ports[n];
416                 string::size_type pos;
417                 string portname;
418
419                 pos = str.find (':');
420
421                 newpair.first = str.substr (0, pos); 
422                 portname = str.substr (pos+1);
423
424                 result = portmap.insert (newpair);
425
426                 strpair.first = portname;
427                 strpair.second = str;
428
429                 result.first->second.push_back (strpair);
430         }
431
432         PortMap::iterator i;
433
434         for (i = portmap.begin(); i != portmap.end(); ++i) {
435                 
436                 Box *client_box = manage (new VBox);
437                 Gtk::CList *client_port_display = manage (new Gtk::CList (1));
438                 ScrolledWindow *scroller = manage (new ScrolledWindow);
439
440                 scroller->add_with_viewport (*client_port_display);
441                 scroller->set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
442
443                 client_box->pack_start (*scroller);
444
445                 client_port_display->set_selection_mode (GTK_SELECTION_BROWSE);
446                 client_port_display->set_name ("ConnectionEditorList");
447
448                 for (vector<pair<string,string> >::iterator s = i->second.begin(); s != i->second.end(); ++s) {
449                         
450                         rowdata.clear ();
451                         rowdata.push_back (s->first);
452                         client_port_display->rows().push_back (rowdata);
453                         client_port_display->rows().back().set_data (g_strdup (s->second.c_str()), free);
454                 }
455
456                 client_port_display->columns_autosize ();
457                 client_port_display->select_row.connect (bind (mem_fun(*this, &ConnectionEditor::port_selection_handler), client_port_display));
458                 
459                 Label *tab_label = manage (new Label);
460
461                 tab_label->set_name ("ConnectionEditorNotebookTab");
462                 tab_label->set_text ((*i).first);
463
464                 pages.push_back (TabElem (*client_box, *tab_label));
465         }
466
467         notebook.set_page (current_page);
468         notebook.show.connect (bind (mem_fun (notebook, &Notebook::set_page), current_page));
469         selector_box.show_all ();
470 }       
471
472 void
473 ConnectionEditor::display_connection_state (bool for_input)
474 {
475         LockMonitor lm (port_display_lock, __LINE__, __FILE__);
476         uint32_t limit;
477
478         if (session == 0 || current_connection == 0) {
479                 return;
480         }
481
482         string frame_label = _("Connection \"");
483         frame_label += current_connection->name();
484         frame_label += _("\"");
485         port_frame.set_label (frame_label);
486
487         for (slist<ScrolledWindow *>::iterator i = port_displays.begin(); i != port_displays.end(); ) {
488                 
489                 slist<ScrolledWindow *>::iterator tmp;
490
491                 tmp = i;
492                 tmp++;
493
494                 port_box.remove (**i);
495                 delete *i;
496                 port_displays.erase (i);
497
498                 i = tmp;
499         } 
500         
501         limit = current_connection->nports();
502
503         for (uint32_t n = 0; n < limit; ++n) {
504
505                 CList *clist;
506                 ScrolledWindow *scroller;
507
508                 const gchar *title[1];
509                 char buf[32];
510                 string really_short_name;
511
512                 if (for_input) {
513                         snprintf(buf, sizeof(buf)-1, _("in %d"), n+1);
514                 } else {
515                         snprintf(buf, sizeof(buf)-1, _("out %d"), n+1);
516                 }
517                         
518                 title[0] = buf;
519                 clist = manage (new CList (1, title));
520                 scroller = new ScrolledWindow;
521                 
522                 scroller->add_with_viewport (*clist);
523                 scroller->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
524
525                 port_displays.insert (port_displays.end(), scroller);
526                 port_box.pack_start (*scroller);
527
528                 clist->set_data ("port", (gpointer) ((intptr_t) n));
529
530                 clist->set_name ("ConnectionEditorPortList");
531                 clist->click_column.connect (bind (mem_fun(*this, &ConnectionEditor::port_column_click), clist));
532                 clist->set_selection_mode (GTK_SELECTION_SINGLE);
533                 clist->set_shadow_type (Gtk::SHADOW_IN);
534
535                 scroller->set_size_request (-1, 75);
536
537                 /* now fill the clist with the current connections */
538
539                 const ARDOUR::Connection::PortList& connections = current_connection->port_connections (n);
540         
541                 for (ARDOUR::Connection::PortList::const_iterator i = connections.begin(); i != connections.end(); ++i) {
542                         const gchar *data[1];
543                         
544                         data[0] = (*i).c_str();
545                         clist->rows().push_back (data);
546                 }
547
548                 clist->columns_autosize ();
549                 clist->button_release_event.connect (bind (mem_fun(*this, &ConnectionEditor::port_button_event), clist));
550         }
551
552         port_box.show_all ();
553 }
554
555 void
556 ConnectionEditor::port_selection_handler (gint row, gint col, GdkEvent *ev, Gtk::CList *clist)
557 {
558         using namespace CList_Helpers;
559
560         string other_port_name = (char *) clist->rows()[row].get_data();
561         
562         if (current_connection && selected_port >= 0) {
563                 current_connection->add_connection (selected_port, other_port_name);
564         }
565
566 }
567
568 void
569 ConnectionEditor::add_port ()
570 {
571         if (current_connection) {
572                 current_connection->add_port ();
573         }
574 }
575
576 void
577 ConnectionEditor::port_column_click (gint col, CList *clist)
578 {
579         /* Gack. CList's don't respond visually to a change
580            in their state, so rename them to force a style
581            switch.
582         */
583
584         LockMonitor lm (port_display_lock, __LINE__, __FILE__);
585
586         int which_port = reinterpret_cast<intptr_t> (clist->get_data ("port"));
587
588         if (which_port != selected_port) {
589                 
590                 selected_port = which_port;
591                 display_ports ();
592
593                 clist->set_name ("ConnectionEditorPortListSelected");
594
595                 for (slist<ScrolledWindow *>::iterator i = port_displays.begin(); i != port_displays.end(); ++i) {
596
597                         Widget *child = (*i)->get_child();
598
599                         if (static_cast<CList *> (child) != clist) {
600                                 child->set_name ("ConnectionEditorPortList");
601                                 child->queue_draw ();
602                         }
603                 }
604                 
605         } else {
606                 
607                 selected_port = -1;
608                 clist->set_name ("ConnectionEditorPortList");
609                 clist->queue_draw();
610         }
611 }
612
613 gint
614 ConnectionEditor::connection_click (GdkEventButton *ev, CList *clist)
615 {
616         gint row, col;
617
618         if (clist->get_selection_info ((int)ev->x, (int)ev->y, &row, &col) == 0) {
619                 return FALSE;
620         }
621
622         current_connection = reinterpret_cast<ARDOUR::Connection *> (clist->row(row).get_data ());
623
624         return TRUE;
625 }
626
627 void
628 ConnectionEditor::new_connection (bool for_input)
629 {
630         if (session == 0) {
631                 return;
632         }
633
634         ArdourPrompter prompter (true);
635         prompter.set_prompt (_("Name for new connection:"));
636         prompter.done.connect (Gtk::Main::quit.slot());
637         prompter.show_all();
638
639         Gtk::Main::run();
640
641         if (prompter.status == Gtkmm2ext::Prompter::entered) {
642                 string name;
643                 prompter.get_result (name);
644
645                 push_at_front = true;
646
647                 if (name.length()) {
648                         if (for_input) {
649                                 session->add_connection (new ARDOUR::InputConnection (name));
650                         } else {
651                                 session->add_connection (new ARDOUR::OutputConnection (name));
652                         }
653                 }
654                 push_at_front = false;
655         }
656 }
657
658 void
659 ConnectionEditor::delete_connection ()
660 {
661         if (session && current_connection) {
662                 session->remove_connection (current_connection);
663                 current_connection = 0;
664         }
665 }
666
667 gint
668 ConnectionEditor::port_button_event (GdkEventButton *ev, CList *clist)
669 {
670         int row, col;
671
672         if (current_connection == 0) {
673                 return FALSE;
674         }
675
676         if (clist->get_selection_info ((int) ev->x, (int) ev->y, &row, &col) == 0) {
677                 return FALSE;
678         }
679
680         if (!(Keyboard::is_delete_event (ev))) {
681                 return FALSE;
682         }
683
684         string port_name = clist->cell (row, col).get_text ();
685         int which_port = (intptr_t) clist->get_data ("port");
686         
687         current_connection->remove_connection (which_port, port_name);
688
689         return TRUE;
690 }
691         
692