Display hardware IO individually in the port matrices unless we are in _show_only_bun...
[ardour.git] / gtk2_ardour / bundle_manager.cc
1 /*
2     Copyright (C) 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 <gtkmm/stock.h>
21 #include <gtkmm/button.h>
22 #include <gtkmm/label.h>
23 #include <gtkmm/entry.h>
24 #include <gtkmm/table.h>
25 #include <gtkmm/comboboxtext.h>
26 #include <gtkmm/alignment.h>
27
28 #include "ardour/session.h"
29 #include "ardour/user_bundle.h"
30 #include "ardour/audioengine.h"
31 #include "bundle_manager.h"
32 #include "gui_thread.h"
33 #include "i18n.h"
34 #include "utils.h"
35
36 using namespace std;
37 using namespace ARDOUR;
38
39 BundleEditorMatrix::BundleEditorMatrix (Gtk::Window* parent, Session* session, boost::shared_ptr<Bundle> bundle)
40         : PortMatrix (parent, session, DataType::NIL)
41         , _bundle (bundle)
42 {
43         _port_group = boost::shared_ptr<PortGroup> (new PortGroup (""));
44         _port_group->add_bundle (_bundle);
45
46         setup_all_ports ();
47         init ();
48 }
49
50 void
51 BundleEditorMatrix::setup_ports (int dim)
52 {
53         if (dim == OURS) {
54                 _ports[OURS].clear ();
55                 _ports[OURS].add_group (_port_group);
56         } else {
57                 _ports[OTHER].suspend_signals ();
58
59                 /* when we gather, allow the matrix to contain bundles with duplicate port sets,
60                    otherwise in some cases the basic system IO ports may be hidden, making
61                    the bundle editor useless */
62
63                 _ports[OTHER].gather (_session, DataType::NIL, _bundle->ports_are_inputs(), true, show_only_bundles ());
64                 _ports[OTHER].remove_bundle (_bundle);
65                 _ports[OTHER].resume_signals ();
66         }
67 }
68
69 void
70 BundleEditorMatrix::set_state (BundleChannel c[2], bool s)
71 {
72         Bundle::PortList const& pl = c[OTHER].bundle->channel_ports (c[OTHER].channel);
73         for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
74                 if (s) {
75                         c[OURS].bundle->add_port_to_channel (c[OURS].channel, *i);
76                 } else {
77                         c[OURS].bundle->remove_port_from_channel (c[OURS].channel, *i);
78                 }
79         }
80 }
81
82 PortMatrixNode::State
83 BundleEditorMatrix::get_state (BundleChannel c[2]) const
84 {
85         if (c[0].bundle->nchannels() == ChanCount::ZERO || c[1].bundle->nchannels() == ChanCount::ZERO) {
86                 return PortMatrixNode::NOT_ASSOCIATED;
87         }
88
89         Bundle::PortList const& pl = c[OTHER].bundle->channel_ports (c[OTHER].channel);
90         if (pl.empty ()) {
91                 return PortMatrixNode::NOT_ASSOCIATED;
92         }
93
94         for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
95                 if (!c[OURS].bundle->port_attached_to_channel (c[OURS].channel, *i)) {
96                         return PortMatrixNode::NOT_ASSOCIATED;
97                 }
98         }
99
100         return PortMatrixNode::ASSOCIATED;
101 }
102
103 bool
104 BundleEditorMatrix::can_add_channel (boost::shared_ptr<Bundle> b) const
105 {
106         if (b == _bundle) {
107                 return true;
108         }
109
110         return PortMatrix::can_add_channel (b);
111 }
112
113 void
114 BundleEditorMatrix::add_channel (boost::shared_ptr<Bundle> b, DataType t)
115 {
116         if (b == _bundle) {
117
118                 NameChannelDialog d;
119                 d.set_position (Gtk::WIN_POS_MOUSE);
120
121                 if (d.run () != Gtk::RESPONSE_ACCEPT) {
122                         return;
123                 }
124
125                 _bundle->add_channel (d.get_name(), t);
126                 setup_ports (OURS);
127
128         } else {
129
130                 PortMatrix::add_channel (b, t);
131
132         }
133 }
134
135 bool
136 BundleEditorMatrix::can_remove_channels (boost::shared_ptr<Bundle> b) const
137 {
138         if (b == _bundle) {
139                 return true;
140         }
141
142         return PortMatrix::can_remove_channels (b);
143 }
144
145 void
146 BundleEditorMatrix::remove_channel (BundleChannel bc)
147 {
148         bc.bundle->remove_channel (bc.channel);
149         setup_ports (OURS);
150 }
151
152 bool
153 BundleEditorMatrix::can_rename_channels (boost::shared_ptr<Bundle> b) const
154 {
155         if (b == _bundle) {
156                 return true;
157         }
158
159         return PortMatrix::can_rename_channels (b);
160 }
161
162 void
163 BundleEditorMatrix::rename_channel (BundleChannel bc)
164 {
165         NameChannelDialog d (bc.bundle, bc.channel);
166         d.set_position (Gtk::WIN_POS_MOUSE);
167
168         if (d.run () != Gtk::RESPONSE_ACCEPT) {
169                 return;
170         }
171
172         bc.bundle->set_channel_name (bc.channel, d.get_name ());
173 }
174
175 bool
176 BundleEditorMatrix::list_is_global (int dim) const
177 {
178         return (dim == OTHER);
179 }
180
181 string
182 BundleEditorMatrix::disassociation_verb () const
183 {
184         return _("Disassociate");
185 }
186
187 BundleEditor::BundleEditor (Session* session, boost::shared_ptr<UserBundle> bundle)
188         : ArdourDialog (_("Edit Bundle")), _matrix (this, session, bundle), _bundle (bundle)
189 {
190         Gtk::Table* t = new Gtk::Table (3, 2);
191         t->set_spacings (4);
192
193         /* Bundle name */
194         Gtk::Alignment* a = new Gtk::Alignment (1, 0.5, 0, 1);
195         a->add (*Gtk::manage (new Gtk::Label (_("Name:"))));
196         t->attach (*Gtk::manage (a), 0, 1, 0, 1, Gtk::FILL, Gtk::FILL);
197         t->attach (_name, 1, 2, 0, 1);
198         _name.set_text (_bundle->name ());
199         _name.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::name_changed));
200
201         /* Direction (input or output) */
202         a = new Gtk::Alignment (1, 0.5, 0, 1);
203         a->add (*Gtk::manage (new Gtk::Label (_("Direction:"))));
204         t->attach (*Gtk::manage (a), 0, 1, 1, 2, Gtk::FILL, Gtk::FILL);
205         a = new Gtk::Alignment (0, 0.5, 0, 1);
206         a->add (_input_or_output);
207         t->attach (*Gtk::manage (a), 1, 2, 1, 2);
208         _input_or_output.append_text (_("Input"));
209         _input_or_output.append_text (_("Output"));
210
211         if (bundle->ports_are_inputs()) {
212                 _input_or_output.set_active_text (_("Input"));
213         } else {
214                 _input_or_output.set_active_text (_("Output"));
215         }
216
217         _input_or_output.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::input_or_output_changed));
218
219         get_vbox()->pack_start (*Gtk::manage (t), false, false);
220         get_vbox()->pack_start (_matrix);
221         get_vbox()->set_spacing (4);
222
223         add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_ACCEPT);
224         show_all ();
225 }
226
227 void
228 BundleEditor::on_show ()
229 {
230         Gtk::Window::on_show ();
231         pair<uint32_t, uint32_t> const pm_max = _matrix.max_size ();
232         resize_window_to_proportion_of_monitor (this, pm_max.first, pm_max.second);
233 }
234
235 void
236 BundleEditor::name_changed ()
237 {
238         _bundle->set_name (_name.get_text ());
239 }
240
241 void
242 BundleEditor::input_or_output_changed ()
243 {
244         _bundle->remove_ports_from_channels ();
245
246         if (_input_or_output.get_active_text() == _("Output")) {
247                 _bundle->set_ports_are_outputs ();
248         } else {
249                 _bundle->set_ports_are_inputs ();
250         }
251
252         _matrix.setup_all_ports ();
253 }
254
255 void
256 BundleEditor::on_map ()
257 {
258         _matrix.setup_all_ports ();
259         Window::on_map ();
260 }
261
262
263 BundleManager::BundleManager (Session* session)
264         : ArdourDialog (_("Bundle Manager"))
265         , edit_button (_("Edit"))
266         , delete_button (_("Delete"))
267 {
268         set_session (session);
269
270         _list_model = Gtk::ListStore::create (_list_model_columns);
271         _tree_view.set_model (_list_model);
272         _tree_view.append_column (_("Name"), _list_model_columns.name);
273         _tree_view.set_headers_visible (false);
274
275         boost::shared_ptr<BundleList> bundles = _session->bundles ();
276         for (BundleList::iterator i = bundles->begin(); i != bundles->end(); ++i) {
277                 add_bundle (*i);
278         }
279
280         /* New / Edit / Delete buttons */
281         Gtk::VBox* buttons = new Gtk::VBox;
282         buttons->set_spacing (8);
283         Gtk::Button* b = new Gtk::Button (_("New"));
284         b->set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::NEW, Gtk::ICON_SIZE_BUTTON)));
285         b->signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::new_clicked));
286         buttons->pack_start (*Gtk::manage (b), false, false);
287         edit_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::EDIT, Gtk::ICON_SIZE_BUTTON)));
288         edit_button.signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::edit_clicked));
289         buttons->pack_start (edit_button, false, false);
290         delete_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::DELETE, Gtk::ICON_SIZE_BUTTON)));
291         delete_button.signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::delete_clicked));
292         buttons->pack_start (delete_button, false, false);
293
294         Gtk::HBox* h = new Gtk::HBox;
295         h->set_spacing (8);
296         h->set_border_width (8);
297         h->pack_start (_tree_view);
298         h->pack_start (*Gtk::manage (buttons), false, false);
299
300         get_vbox()->set_spacing (8);
301         get_vbox()->pack_start (*Gtk::manage (h));
302
303         set_default_size (480, 240);
304
305         _tree_view.get_selection()->signal_changed().connect (
306                 sigc::mem_fun (*this, &BundleManager::set_button_sensitivity)
307                 );
308
309         _tree_view.signal_row_activated().connect (
310                 sigc::mem_fun (*this, &BundleManager::row_activated)
311                 );
312
313         Gtk::Button* close_but = add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_ACCEPT);
314         close_but->signal_clicked ().connect (sigc::mem_fun (*this, &Gtk::Window::hide));
315
316         set_button_sensitivity ();
317
318         show_all ();
319 }
320
321 void
322 BundleManager::set_button_sensitivity ()
323 {
324         bool const sel = (_tree_view.get_selection()->get_selected() != 0);
325         edit_button.set_sensitive (sel);
326         delete_button.set_sensitive (sel);
327 }
328
329
330 void
331 BundleManager::new_clicked ()
332 {
333         boost::shared_ptr<UserBundle> b (new UserBundle (_("Bundle")));
334
335         /* Start off with a single channel */
336         /* XXX: allow user to specify type */
337         b->add_channel ("1", DataType::AUDIO);
338
339         _session->add_bundle (b);
340         add_bundle (b);
341
342         BundleEditor e (_session, b);
343         e.run ();
344 }
345
346 void
347 BundleManager::edit_clicked ()
348 {
349         Gtk::TreeModel::iterator i = _tree_view.get_selection()->get_selected();
350         if (i) {
351                 boost::shared_ptr<UserBundle> b = (*i)[_list_model_columns.bundle];
352                 BundleEditor e (_session, b);
353                 e.run ();
354         }
355 }
356
357 void
358 BundleManager::delete_clicked ()
359 {
360         Gtk::TreeModel::iterator i = _tree_view.get_selection()->get_selected();
361         if (i) {
362                 boost::shared_ptr<UserBundle> b = (*i)[_list_model_columns.bundle];
363                 _session->remove_bundle (b);
364                 _list_model->erase (i);
365         }
366 }
367
368 void
369 BundleManager::add_bundle (boost::shared_ptr<Bundle> b)
370 {
371         boost::shared_ptr<UserBundle> u = boost::dynamic_pointer_cast<UserBundle> (b);
372         if (u == 0) {
373                 return;
374         }
375
376         Gtk::TreeModel::iterator i = _list_model->append ();
377         (*i)[_list_model_columns.name] = u->name ();
378         (*i)[_list_model_columns.bundle] = u;
379
380         u->Changed.connect (bundle_connections, invalidator (*this), boost::bind (&BundleManager::bundle_changed, this, _1, u), gui_context());
381 }
382
383 void
384 BundleManager::bundle_changed (Bundle::Change c, boost::shared_ptr<UserBundle> b)
385 {
386         if ((c & Bundle::NameChanged) == 0) {
387                 return;
388         }
389
390         Gtk::TreeModel::iterator i = _list_model->children().begin ();
391         while (i != _list_model->children().end()) {
392                 boost::shared_ptr<UserBundle> t = (*i)[_list_model_columns.bundle];
393                 if (t == b) {
394                         break;
395                 }
396                 ++i;
397         }
398
399         if (i != _list_model->children().end()) {
400                 (*i)[_list_model_columns.name] = b->name ();
401         }
402 }
403
404 void
405 BundleManager::row_activated (Gtk::TreeModel::Path const & p, Gtk::TreeViewColumn*)
406 {
407         Gtk::TreeModel::iterator i = _list_model->get_iter (p);
408         if (!i) {
409                 return;
410         }
411
412         boost::shared_ptr<UserBundle> b = (*i)[_list_model_columns.bundle];
413         BundleEditor e (_session, b);
414         e.run ();
415 }
416
417 NameChannelDialog::NameChannelDialog ()
418         : ArdourDialog (_("Add Channel")),
419           _adding (true)
420 {
421         setup ();
422 }
423
424 NameChannelDialog::NameChannelDialog (boost::shared_ptr<Bundle> b, uint32_t c)
425         : ArdourDialog (_("Rename Channel")),
426           _bundle (b),
427           _channel (c),
428           _adding (false)
429 {
430         _name.set_text (b->channel_name (c));
431
432         setup ();
433 }
434
435 void
436 NameChannelDialog::setup ()
437 {
438         Gtk::HBox* box = Gtk::manage (new Gtk::HBox ());
439
440         box->pack_start (*Gtk::manage (new Gtk::Label (_("Name"))));
441         box->pack_start (_name);
442         _name.set_activates_default (true);
443
444         get_vbox ()->pack_end (*box);
445         box->show_all ();
446
447         add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
448         if (_adding) {
449                 add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
450         } else {
451                 add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_ACCEPT);
452         }
453         set_default_response (Gtk::RESPONSE_ACCEPT);
454 }
455
456 string
457 NameChannelDialog::get_name () const
458 {
459         return _name.get_text ();
460 }
461