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