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