Remove use of i18n macros in headers. Prevents our gettext.h being included before...
[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, bundle->type())
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, _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                 _bundle->add_channel (d.get_name());
122                 setup_ports (OURS);
123
124         } else {
125
126                 PortMatrix::add_channel (b);
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         /* Type (audio or MIDI) */
216         a = new Gtk::Alignment (1, 0.5, 0, 1);
217         a->add (*Gtk::manage (new Gtk::Label (_("Type:"))));
218         t->attach (*Gtk::manage (a), 0, 1, 2, 3, Gtk::FILL, Gtk::FILL);
219         a = new Gtk::Alignment (0, 0.5, 0, 1);
220         a->add (_type);
221         t->attach (*Gtk::manage (a), 1, 2, 2, 3);
222
223         _type.append_text (_("Audio"));
224         _type.append_text (_("MIDI"));
225
226         switch (bundle->type ()) {
227         case DataType::AUDIO:
228                 _type.set_active_text (_("Audio"));
229                 break;
230         case DataType::MIDI:
231                 _type.set_active_text (_("MIDI"));
232                 break;
233         }
234
235         _type.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::type_changed));
236
237         get_vbox()->pack_start (*Gtk::manage (t), false, false);
238         get_vbox()->pack_start (_matrix);
239         get_vbox()->set_spacing (4);
240
241         add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_ACCEPT);
242         show_all ();
243 }
244
245 void
246 BundleEditor::on_show ()
247 {
248         Gtk::Window::on_show ();
249         pair<uint32_t, uint32_t> const pm_max = _matrix.max_size ();
250         resize_window_to_proportion_of_monitor (this, pm_max.first, pm_max.second);
251 }
252
253 void
254 BundleEditor::name_changed ()
255 {
256         _bundle->set_name (_name.get_text ());
257 }
258
259 void
260 BundleEditor::input_or_output_changed ()
261 {
262         _bundle->remove_ports_from_channels ();
263
264         if (_input_or_output.get_active_text() == _("Output")) {
265                 _bundle->set_ports_are_outputs ();
266         } else {
267                 _bundle->set_ports_are_inputs ();
268         }
269
270         _matrix.setup_all_ports ();
271 }
272
273 void
274 BundleEditor::type_changed ()
275 {
276         _bundle->remove_ports_from_channels ();
277
278         DataType const t = _type.get_active_text() == _("Audio") ?
279                 DataType::AUDIO : DataType::MIDI;
280
281         _bundle->set_type (t);
282         _matrix.set_type (t);
283 }
284
285 void
286 BundleEditor::on_map ()
287 {
288         _matrix.setup_all_ports ();
289         Window::on_map ();
290 }
291
292
293 BundleManager::BundleManager (Session* session)
294         : ArdourDialog (_("Bundle Manager"))
295         , edit_button (_("Edit"))
296         , delete_button (_("Delete"))
297 {
298         set_session (session);
299
300         _list_model = Gtk::ListStore::create (_list_model_columns);
301         _tree_view.set_model (_list_model);
302         _tree_view.append_column (_("Name"), _list_model_columns.name);
303         _tree_view.set_headers_visible (false);
304
305         boost::shared_ptr<BundleList> bundles = _session->bundles ();
306         for (BundleList::iterator i = bundles->begin(); i != bundles->end(); ++i) {
307                 add_bundle (*i);
308         }
309
310         /* New / Edit / Delete buttons */
311         Gtk::VBox* buttons = new Gtk::VBox;
312         buttons->set_spacing (8);
313         Gtk::Button* b = new Gtk::Button (_("New"));
314         b->set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::NEW, Gtk::ICON_SIZE_BUTTON)));
315         b->signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::new_clicked));
316         buttons->pack_start (*Gtk::manage (b), false, false);
317         edit_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::EDIT, Gtk::ICON_SIZE_BUTTON)));
318         edit_button.signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::edit_clicked));
319         buttons->pack_start (edit_button, false, false);
320         delete_button.set_image (*Gtk::manage (new Gtk::Image (Gtk::Stock::DELETE, Gtk::ICON_SIZE_BUTTON)));
321         delete_button.signal_clicked().connect (sigc::mem_fun (*this, &BundleManager::delete_clicked));
322         buttons->pack_start (delete_button, false, false);
323
324         Gtk::HBox* h = new Gtk::HBox;
325         h->set_spacing (8);
326         h->set_border_width (8);
327         h->pack_start (_tree_view);
328         h->pack_start (*Gtk::manage (buttons), false, false);
329
330         get_vbox()->set_spacing (8);
331         get_vbox()->pack_start (*Gtk::manage (h));
332
333         set_default_size (480, 240);
334
335         _tree_view.get_selection()->signal_changed().connect (
336                 sigc::mem_fun (*this, &BundleManager::set_button_sensitivity)
337                 );
338
339         _tree_view.signal_row_activated().connect (
340                 sigc::mem_fun (*this, &BundleManager::row_activated)
341                 );
342
343         set_button_sensitivity ();
344
345         show_all ();
346 }
347
348 void
349 BundleManager::set_button_sensitivity ()
350 {
351         bool const sel = (_tree_view.get_selection()->get_selected() != 0);
352         edit_button.set_sensitive (sel);
353         delete_button.set_sensitive (sel);
354 }
355
356
357 void
358 BundleManager::new_clicked ()
359 {
360         boost::shared_ptr<UserBundle> b (new UserBundle (_("Bundle")));
361
362         /* Start off with a single channel */
363         b->add_channel ("1");
364
365         _session->add_bundle (b);
366         add_bundle (b);
367
368         BundleEditor e (_session, b);
369         e.run ();
370 }
371
372 void
373 BundleManager::edit_clicked ()
374 {
375         Gtk::TreeModel::iterator i = _tree_view.get_selection()->get_selected();
376         if (i) {
377                 boost::shared_ptr<UserBundle> b = (*i)[_list_model_columns.bundle];
378                 BundleEditor e (_session, b);
379                 e.run ();
380         }
381 }
382
383 void
384 BundleManager::delete_clicked ()
385 {
386         Gtk::TreeModel::iterator i = _tree_view.get_selection()->get_selected();
387         if (i) {
388                 boost::shared_ptr<UserBundle> b = (*i)[_list_model_columns.bundle];
389                 _session->remove_bundle (b);
390                 _list_model->erase (i);
391         }
392 }
393
394 void
395 BundleManager::add_bundle (boost::shared_ptr<Bundle> b)
396 {
397         boost::shared_ptr<UserBundle> u = boost::dynamic_pointer_cast<UserBundle> (b);
398         if (u == 0) {
399                 return;
400         }
401
402         Gtk::TreeModel::iterator i = _list_model->append ();
403         (*i)[_list_model_columns.name] = u->name ();
404         (*i)[_list_model_columns.bundle] = u;
405
406         u->Changed.connect (bundle_connections, invalidator (*this), ui_bind (&BundleManager::bundle_changed, this, _1, u), gui_context());
407 }
408
409 void
410 BundleManager::bundle_changed (Bundle::Change c, boost::shared_ptr<UserBundle> b)
411 {
412         if ((c & Bundle::NameChanged) == 0) {
413                 return;
414         }
415
416         Gtk::TreeModel::iterator i = _list_model->children().begin ();
417         while (i != _list_model->children().end()) {
418                 boost::shared_ptr<UserBundle> t = (*i)[_list_model_columns.bundle];
419                 if (t == b) {
420                         break;
421                 }
422                 ++i;
423         }
424
425         if (i != _list_model->children().end()) {
426                 (*i)[_list_model_columns.name] = b->name ();
427         }
428 }
429
430 void
431 BundleManager::row_activated (Gtk::TreeModel::Path const & p, Gtk::TreeViewColumn*)
432 {
433         Gtk::TreeModel::iterator i = _list_model->get_iter (p);
434         if (!i) {
435                 return;
436         }
437         
438         boost::shared_ptr<UserBundle> b = (*i)[_list_model_columns.bundle];
439         BundleEditor e (_session, b);
440         e.run ();
441 }
442
443 NameChannelDialog::NameChannelDialog ()
444         : ArdourDialog (_("Add Channel")),
445           _adding (true)
446 {
447         setup ();
448 }
449
450 NameChannelDialog::NameChannelDialog (boost::shared_ptr<Bundle> b, uint32_t c)
451         : ArdourDialog (_("Rename Channel")),
452           _bundle (b),
453           _channel (c),
454           _adding (false)
455 {
456         _name.set_text (b->channel_name (c));
457
458         setup ();
459 }
460
461 void
462 NameChannelDialog::setup ()
463 {
464         Gtk::HBox* box = Gtk::manage (new Gtk::HBox ());
465
466         box->pack_start (*Gtk::manage (new Gtk::Label (_("Name"))));
467         box->pack_start (_name);
468         _name.set_activates_default (true);
469
470         get_vbox ()->pack_end (*box);
471         box->show_all ();
472
473         add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
474         if (_adding) {
475                 add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
476         } else {
477                 add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_ACCEPT);
478         }
479         set_default_response (Gtk::RESPONSE_ACCEPT);
480 }
481
482 string
483 NameChannelDialog::get_name () const
484 {
485         return _name.get_text ();
486 }
487