incomplete merge of master into windows (requires upcoming changes to master to be...
[ardour.git] / gtk2_ardour / io_selector.cc
1 /*
2     Copyright (C) 2002-2007 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 */
19
20 #include <stdint.h>
21
22 #include <glibmm/objectbase.h>
23
24 #include <gtkmm2ext/doi.h>
25
26 #include "ardour/audioengine.h"
27 #include "ardour/bundle.h"
28 #include "ardour/data_type.h"
29 #include "ardour/io.h"
30 #include "ardour/port.h"
31 #include "ardour/session.h"
32
33 #include "io_selector.h"
34 #include "utils.h"
35 #include "gui_thread.h"
36 #include "i18n.h"
37
38 using namespace ARDOUR;
39 using namespace Gtk;
40
41 IOSelector::IOSelector (Gtk::Window* p, ARDOUR::Session* session, boost::shared_ptr<ARDOUR::IO> io)
42         : PortMatrix (p, session, DataType::NIL)
43         , _io (io)
44 {
45         setup_type ();
46
47         /* signal flow from 0 to 1 */
48
49         _find_inputs_for_io_outputs = (_io->direction() == IO::Output);
50
51         if (_find_inputs_for_io_outputs) {
52                 _other = 1;
53                 _ours = 0;
54         } else {
55                 _other = 0;
56                 _ours = 1;
57         }
58
59         _port_group.reset (new PortGroup (io->name()));
60         _ports[_ours].add_group (_port_group);
61
62         io->changed.connect (_io_connection, invalidator (*this), boost::bind (&IOSelector::io_changed_proxy, this), gui_context ());
63
64         setup_all_ports ();
65         init ();
66 }
67
68 void
69 IOSelector::setup_type ()
70 {
71         /* set type according to what's in the IO */
72
73         int N = 0;
74         DataType type_with_ports = DataType::NIL;
75         for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
76                 if (_io->ports().num_ports (*i)) {
77                         type_with_ports = *i;
78                         ++N;
79                 }
80         }
81
82         if (N <= 1) {
83                 set_type (type_with_ports);
84         } else {
85                 set_type (DataType::NIL);
86         }
87 }
88
89 void
90 IOSelector::io_changed_proxy ()
91 {
92         /* The IO's changed signal is emitted from code that holds its route's processor lock,
93            so we can't call setup_all_ports (which results in a call to Route::foreach_processor)
94            without a deadlock unless we break things up with this idle handler.
95         */
96
97         Glib::signal_idle().connect_once (sigc::mem_fun (*this, &IOSelector::io_changed));
98 }
99
100 void
101 IOSelector::io_changed ()
102 {
103         setup_type ();
104         setup_all_ports ();
105 }
106
107 void
108 IOSelector::setup_ports (int dim)
109 {
110         if (!_session) {
111                 return;
112         }
113
114         _ports[dim].suspend_signals ();
115
116         if (dim == _other) {
117
118                 _ports[_other].gather (_session, type(), _find_inputs_for_io_outputs, false, show_only_bundles ());
119
120         } else {
121
122                 _port_group->clear ();
123                 _port_group->add_bundle (_io->bundle (), _io);
124         }
125
126         _ports[dim].resume_signals ();
127 }
128
129 void
130 IOSelector::set_state (ARDOUR::BundleChannel c[2], bool s)
131 {
132         ARDOUR::Bundle::PortList const & our_ports = c[_ours].bundle->channel_ports (c[_ours].channel);
133         ARDOUR::Bundle::PortList const & other_ports = c[_other].bundle->channel_ports (c[_other].channel);
134
135         for (ARDOUR::Bundle::PortList::const_iterator i = our_ports.begin(); i != our_ports.end(); ++i) {
136                 for (ARDOUR::Bundle::PortList::const_iterator j = other_ports.begin(); j != other_ports.end(); ++j) {
137
138                         boost::shared_ptr<Port> f = _session->engine().get_port_by_name (*i);
139                         if (!f) {
140                                 return;
141                         }
142
143                         if (s) {
144                                 if (!f->connected_to (*j)) {
145                                         _io->connect (f, *j, 0);
146                                 }
147                         } else {
148                                 if (f->connected_to (*j)) {
149                                         _io->disconnect (f, *j, 0);
150                                 }
151                         }
152                 }
153         }
154 }
155
156 PortMatrixNode::State
157 IOSelector::get_state (ARDOUR::BundleChannel c[2]) const
158 {
159         if (c[0].bundle->nchannels() == ChanCount::ZERO || c[1].bundle->nchannels() == ChanCount::ZERO) {
160                 return PortMatrixNode::NOT_ASSOCIATED;
161         }
162
163         ARDOUR::Bundle::PortList const & our_ports = c[_ours].bundle->channel_ports (c[_ours].channel);
164         ARDOUR::Bundle::PortList const & other_ports = c[_other].bundle->channel_ports (c[_other].channel);
165
166         if (!_session || our_ports.empty() || other_ports.empty()) {
167                 /* we're looking at a bundle with no parts associated with this channel,
168                    so nothing to connect */
169                 return PortMatrixNode::NOT_ASSOCIATED;
170         }
171
172         for (ARDOUR::Bundle::PortList::const_iterator i = our_ports.begin(); i != our_ports.end(); ++i) {
173                 for (ARDOUR::Bundle::PortList::const_iterator j = other_ports.begin(); j != other_ports.end(); ++j) {
174
175                         boost::shared_ptr<Port> f = _session->engine().get_port_by_name (*i);
176
177                         /* since we are talking about an IO, our ports should all have an associated Port *,
178                            so the above call should never fail */
179                         assert (f);
180
181                         if (!f->connected_to (*j)) {
182                                 /* if any one thing is not connected, all bets are off */
183                                 return PortMatrixNode::NOT_ASSOCIATED;
184                         }
185                 }
186         }
187
188         return PortMatrixNode::ASSOCIATED;
189 }
190
191 uint32_t
192 IOSelector::n_io_ports () const
193 {
194         if (!_find_inputs_for_io_outputs) {
195                 return _io->n_ports().get (_io->default_type());
196         } else {
197                 return _io->n_ports().get (_io->default_type());
198         }
199 }
200
201 bool
202 IOSelector::list_is_global (int dim) const
203 {
204         return (dim == _other);
205 }
206
207 std::string
208 IOSelector::disassociation_verb () const
209 {
210         return _("Disconnect");
211 }
212
213 std::string
214 IOSelector::channel_noun () const
215 {
216         return _("port");
217 }
218
219 IOSelectorWindow::IOSelectorWindow (ARDOUR::Session* session, boost::shared_ptr<ARDOUR::IO> io, bool /*can_cancel*/)
220         : ArdourWindow (_("I/O selector"))
221         , _selector (this, session, io)
222 {
223         set_name ("IOSelectorWindow2");
224
225         add (_selector);
226
227         io_name_changed (this);
228
229         show_all ();
230
231         signal_delete_event().connect (sigc::mem_fun (*this, &IOSelectorWindow::wm_delete));
232 }
233
234 bool
235 IOSelectorWindow::wm_delete (GdkEventAny* /*event*/)
236 {
237         _selector.Finished (IOSelector::Accepted);
238         return false;
239 }
240
241
242 void
243 IOSelectorWindow::on_map ()
244 {
245         _selector.setup_all_ports ();
246         Window::on_map ();
247 }
248
249 void
250 IOSelectorWindow::on_show ()
251 {
252         Gtk::Window::on_show ();
253         std::pair<uint32_t, uint32_t> const pm_max = _selector.max_size ();
254         resize_window_to_proportion_of_monitor (this, pm_max.first, pm_max.second);
255 }
256
257 void
258 IOSelectorWindow::io_name_changed (void*)
259 {
260         ENSURE_GUI_THREAD (*this, &IOSelectorWindow::io_name_changed, src)
261                 
262         std::string title;
263
264         if (!_selector.find_inputs_for_io_outputs()) {
265                 title = string_compose(_("%1 input"), _selector.io()->name());
266         } else {
267                 title = string_compose(_("%1 output"), _selector.io()->name());
268         }
269
270         set_title (title);
271 }
272