8fd7c05973e52d76c16511efa1a0a3cc767b37da
[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 #include "ardour/session.h"
28 #include "ardour/user_bundle.h"
29 #include "ardour/audioengine.h"
30 #include "bundle_manager.h"
31 #include "i18n.h"
32
33 BundleEditorMatrix::BundleEditorMatrix (
34         ARDOUR::Session& session, boost::shared_ptr<ARDOUR::Bundle> bundle
35         )
36         : PortMatrix (session, bundle->type()),
37           _bundle (bundle)
38 {
39         _port_group = boost::shared_ptr<PortGroup> (new PortGroup (""));
40         _port_group->add_bundle (_bundle);
41 }
42
43 void
44 BundleEditorMatrix::setup_ports (int dim)
45 {
46         if (dim == OURS) {
47                 _ports[OURS].clear ();
48                 _ports[OURS].add_group (_port_group);
49         } else {
50                 _ports[OTHER].suspend_signals ();
51                 _ports[OTHER].gather (_session, _bundle->ports_are_inputs());
52                 _ports[OTHER].remove_bundle (_bundle);
53                 _ports[OTHER].resume_signals ();
54         }
55 }
56
57 void
58 BundleEditorMatrix::set_state (ARDOUR::BundleChannel c[2], bool s)
59 {
60         ARDOUR::Bundle::PortList const& pl = c[OTHER].bundle->channel_ports (c[OTHER].channel);
61         for (ARDOUR::Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
62                 if (s) {
63                         c[OURS].bundle->add_port_to_channel (c[OURS].channel, *i);
64                 } else {
65                         c[OURS].bundle->remove_port_from_channel (c[OURS].channel, *i);
66                 }
67         }
68 }
69
70 PortMatrixNode::State
71 BundleEditorMatrix::get_state (ARDOUR::BundleChannel c[2]) const
72 {
73         ARDOUR::Bundle::PortList const& pl = c[OTHER].bundle->channel_ports (c[OTHER].channel);
74         for (ARDOUR::Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
75                 if (!c[OURS].bundle->port_attached_to_channel (c[OURS].channel, *i)) {
76                         return PortMatrixNode::NOT_ASSOCIATED;
77                 }
78         }
79
80         return PortMatrixNode::ASSOCIATED;
81 }
82
83 std::string
84 BundleEditorMatrix::add_channel_name () const
85 {
86         return _bundle->name ();
87 }
88
89 void
90 BundleEditorMatrix::add_channel ()
91 {
92         NameChannelDialog d;
93         d.set_position (Gtk::WIN_POS_MOUSE);
94
95         if (d.run () != Gtk::RESPONSE_ACCEPT) {
96                 return;
97         }
98
99         _bundle->add_channel (d.get_name());
100         setup_ports (OURS);
101 }
102
103 void
104 BundleEditorMatrix::remove_channel (ARDOUR::BundleChannel bc)
105 {
106         bc.bundle->remove_channel (bc.channel);
107         setup_ports (OURS);
108 }
109
110 void
111 BundleEditorMatrix::rename_channel (ARDOUR::BundleChannel bc)
112 {
113         NameChannelDialog d (bc.bundle, bc.channel);
114         d.set_position (Gtk::WIN_POS_MOUSE);
115
116         if (d.run () != Gtk::RESPONSE_ACCEPT) {
117                 return;
118         }
119
120         bc.bundle->set_channel_name (bc.channel, d.get_name ());
121 }
122
123 bool
124 BundleEditorMatrix::list_is_global (int dim) const
125 {
126         return (dim == OTHER);
127 }
128
129 BundleEditor::BundleEditor (ARDOUR::Session& session, boost::shared_ptr<ARDOUR::UserBundle> bundle, bool add)
130         : ArdourDialog (_("Edit Bundle")), _matrix (session, bundle), _bundle (bundle)
131 {
132         Gtk::Table* t = new Gtk::Table (3, 2);
133         t->set_spacings (4);
134
135         /* Bundle name */
136         Gtk::Alignment* a = new Gtk::Alignment (1, 0.5, 0, 1);
137         a->add (*Gtk::manage (new Gtk::Label (_("Name:"))));
138         t->attach (*Gtk::manage (a), 0, 1, 0, 1, Gtk::FILL, Gtk::FILL);
139         t->attach (_name, 1, 2, 0, 1);
140         _name.set_text (_bundle->name ());
141         _name.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::name_changed));
142
143         /* Direction (input or output) */
144         a = new Gtk::Alignment (1, 0.5, 0, 1);
145         a->add (*Gtk::manage (new Gtk::Label (_("Direction:"))));
146         t->attach (*Gtk::manage (a), 0, 1, 1, 2, Gtk::FILL, Gtk::FILL);
147         a = new Gtk::Alignment (0, 0.5, 0, 1);
148         a->add (_input_or_output);
149         t->attach (*Gtk::manage (a), 1, 2, 1, 2);
150         _input_or_output.append_text (_("Input"));
151         _input_or_output.append_text (_("Output"));
152         
153         if (bundle->ports_are_inputs()) {
154                 _input_or_output.set_active_text (_("Input"));
155         } else {
156                 _input_or_output.set_active_text (_("Output"));
157         }
158
159         _input_or_output.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::input_or_output_changed));
160
161         /* Type (audio or MIDI) */
162         a = new Gtk::Alignment (1, 0.5, 0, 1);
163         a->add (*Gtk::manage (new Gtk::Label (_("Type:"))));
164         t->attach (*Gtk::manage (a), 0, 1, 2, 3, Gtk::FILL, Gtk::FILL);
165         a = new Gtk::Alignment (0, 0.5, 0, 1);
166         a->add (_type);
167         t->attach (*Gtk::manage (a), 1, 2, 2, 3);
168         
169         _type.append_text (_("Audio"));
170         _type.append_text (_("MIDI"));
171         
172         switch (bundle->type ()) {
173         case ARDOUR::DataType::AUDIO:
174                 _type.set_active_text (_("Audio"));
175                 break;
176         case ARDOUR::DataType::MIDI:
177                 _type.set_active_text (_("MIDI"));
178                 break;
179         }
180
181         _type.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::type_changed));
182                                         
183         get_vbox()->pack_start (*Gtk::manage (t), false, false);
184         get_vbox()->pack_start (_matrix);
185         get_vbox()->set_spacing (4);
186
187         show_all ();
188 }
189
190 void
191 BundleEditor::name_changed ()
192 {
193         _bundle->set_name (_name.get_text ());
194 }
195
196 void
197 BundleEditor::input_or_output_changed ()
198 {
199         _bundle->remove_ports_from_channels ();
200         
201         if (_input_or_output.get_active_text() == _("Output")) {
202                 _bundle->set_ports_are_outputs ();
203         } else {
204                 _bundle->set_ports_are_inputs ();
205         }
206
207         _matrix.setup_all_ports ();
208 }
209
210 void
211 BundleEditor::type_changed ()
212 {
213         _bundle->remove_ports_from_channels ();
214         
215         ARDOUR::DataType const t = _type.get_active_text() == _("Audio") ?
216                 ARDOUR::DataType::AUDIO : ARDOUR::DataType::MIDI;
217
218         _bundle->set_type (t);
219         _matrix.set_type (t);
220 }
221
222 void
223 BundleEditor::on_map ()
224 {
225         _matrix.setup_all_ports ();
226         Window::on_map ();
227 }
228
229
230 BundleManager::BundleManager (ARDOUR::Session& session)
231         : ArdourDialog (_("Bundle manager")), _session (session), edit_button (_("Edit")), delete_button (_("Delete"))
232 {
233         _list_model = Gtk::ListStore::create (_list_model_columns);
234         _tree_view.set_model (_list_model);
235         _tree_view.append_column (_("Name"), _list_model_columns.name);
236         _tree_view.set_headers_visible (false);
237
238         boost::shared_ptr<ARDOUR::BundleList> bundles = _session.bundles ();
239         for (ARDOUR::BundleList::iterator i = bundles->begin(); i != bundles->end(); ++i) {
240                 add_bundle (*i);
241         }
242         
243         /* New / Edit / Delete buttons */
244         Gtk::VBox* buttons = new Gtk::VBox;
245         buttons->set_spacing (8);
246         Gtk::Button* b = new Gtk::Button (_("New"));
247         b->set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::NEW, Gtk::ICON_SIZE_BUTTON)));
248         b->signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::new_clicked));
249         buttons->pack_start (*Gtk::manage (b), false, false);
250         edit_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::EDIT, Gtk::ICON_SIZE_BUTTON)));
251         edit_button.signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::edit_clicked));
252         buttons->pack_start (edit_button, false, false);
253         delete_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::DELETE, Gtk::ICON_SIZE_BUTTON)));
254         delete_button.signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::delete_clicked));
255         buttons->pack_start (delete_button, false, false);
256         
257         Gtk::HBox* h = new Gtk::HBox;
258         h->set_spacing (8);
259         h->set_border_width (8);
260         h->pack_start (_tree_view);
261         h->pack_start (*Gtk::manage (buttons), false, false);
262
263         get_vbox()->set_spacing (8);
264         get_vbox()->pack_start (*Gtk::manage (h));
265
266         set_default_size (480, 240);
267
268         _tree_view.get_selection()->signal_changed().connect (
269                 sigc::mem_fun (*this, &BundleManager::set_button_sensitivity)
270                 );
271
272         set_button_sensitivity ();
273
274         show_all ();
275 }
276
277 void
278 BundleManager::set_button_sensitivity ()
279 {
280         bool const sel = (_tree_view.get_selection()->get_selected() != 0);
281         edit_button.set_sensitive (sel);
282         delete_button.set_sensitive (sel);
283 }
284
285
286 void
287 BundleManager::new_clicked ()
288 {
289         boost::shared_ptr<ARDOUR::UserBundle> b (new ARDOUR::UserBundle (""));
290
291         /* Start off with a single channel */
292         b->add_channel ("");
293
294         BundleEditor e (_session, b, true);
295
296         if (e.run () == Gtk::RESPONSE_ACCEPT) {
297                 _session.add_bundle (b);
298                 add_bundle (b);
299         }
300 }
301
302 void
303 BundleManager::edit_clicked ()
304 {
305         Gtk::TreeModel::iterator i = _tree_view.get_selection()->get_selected();
306         if (i) {
307                 boost::shared_ptr<ARDOUR::UserBundle> b = (*i)[_list_model_columns.bundle];
308                 BundleEditor e (_session, b, false);
309                 if (e.run () == Gtk::RESPONSE_ACCEPT) {
310                         _session.set_dirty ();
311                 }
312         }
313 }
314
315 void
316 BundleManager::delete_clicked ()
317 {
318         Gtk::TreeModel::iterator i = _tree_view.get_selection()->get_selected();
319         if (i) {
320                 boost::shared_ptr<ARDOUR::UserBundle> b = (*i)[_list_model_columns.bundle];
321                 _session.remove_bundle (b);
322                 _list_model->erase (i);
323         }
324 }
325
326 void
327 BundleManager::add_bundle (boost::shared_ptr<ARDOUR::Bundle> b)
328 {
329         boost::shared_ptr<ARDOUR::UserBundle> u = boost::dynamic_pointer_cast<ARDOUR::UserBundle> (b);
330         if (u == 0) {
331                 return;
332         }
333
334         Gtk::TreeModel::iterator i = _list_model->append ();
335         (*i)[_list_model_columns.name] = u->name ();
336         (*i)[_list_model_columns.bundle] = u;
337
338         u->Changed.connect (sigc::bind (sigc::mem_fun (*this, &BundleManager::bundle_changed), u));
339 }
340
341 void
342 BundleManager::bundle_changed (ARDOUR::Bundle::Change c, boost::shared_ptr<ARDOUR::UserBundle> b)
343 {
344         if ((c & ARDOUR::Bundle::NameChanged) == 0) {
345                 return;
346         }
347         
348         Gtk::TreeModel::iterator i = _list_model->children().begin ();
349         while (i != _list_model->children().end()) {
350                 boost::shared_ptr<ARDOUR::UserBundle> t = (*i)[_list_model_columns.bundle];
351                 if (t == b) {
352                         break;
353                 }
354                 ++i;
355         }
356
357         if (i != _list_model->children().end()) {
358                 (*i)[_list_model_columns.name] = b->name ();
359         }
360 }
361
362
363 NameChannelDialog::NameChannelDialog ()
364         : ArdourDialog (_("Add channel")),
365           _adding (true)
366 {
367         setup ();
368 }
369
370 NameChannelDialog::NameChannelDialog (boost::shared_ptr<ARDOUR::Bundle> b, uint32_t c)
371         : ArdourDialog (_("Rename channel")),
372           _bundle (b),
373           _channel (c),
374           _adding (false)
375 {
376         _name.set_text (b->channel_name (c));
377
378         setup ();
379 }
380
381 void
382 NameChannelDialog::setup ()
383 {       
384         Gtk::HBox* box = Gtk::manage (new Gtk::HBox ());
385
386         box->pack_start (*Gtk::manage (new Gtk::Label (_("Name"))));
387         box->pack_start (_name);
388
389         get_vbox ()->pack_end (*box);
390         box->show_all ();
391
392         add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
393         if (_adding) {
394                 add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
395         } else {
396                 add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_ACCEPT);
397         }
398         set_default_response (Gtk::RESPONSE_ACCEPT);
399 }
400
401 std::string
402 NameChannelDialog::get_name () const
403 {
404         return _name.get_text ();
405 }