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