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