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