Attempt #46 to understand MIDI data locking.
[ardour2.git] / gtk2_ardour / bundle_manager.cc
blob9916f805810f65741e3e59a7befdff44608e27e7
1 /*
2 Copyright (C) 2007 Paul Davis
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.
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.
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.
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>
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"
36 using namespace std;
37 using namespace ARDOUR;
39 BundleEditorMatrix::BundleEditorMatrix (Gtk::Window* parent, Session* session, boost::shared_ptr<Bundle> bundle)
40 : PortMatrix (parent, session, DataType::NIL)
41 , _bundle (bundle)
43 _port_group = boost::shared_ptr<PortGroup> (new PortGroup (""));
44 _port_group->add_bundle (_bundle);
46 setup_all_ports ();
47 init ();
50 void
51 BundleEditorMatrix::setup_ports (int dim)
53 if (dim == OURS) {
54 _ports[OURS].clear ();
55 _ports[OURS].add_group (_port_group);
56 } else {
57 _ports[OTHER].suspend_signals ();
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 */
63 _ports[OTHER].gather (_session, DataType::NIL, _bundle->ports_are_inputs(), true);
64 _ports[OTHER].remove_bundle (_bundle);
65 _ports[OTHER].resume_signals ();
69 void
70 BundleEditorMatrix::set_state (BundleChannel c[2], bool s)
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);
82 PortMatrixNode::State
83 BundleEditorMatrix::get_state (BundleChannel c[2]) const
85 Bundle::PortList const& pl = c[OTHER].bundle->channel_ports (c[OTHER].channel);
86 if (pl.empty ()) {
87 return PortMatrixNode::NOT_ASSOCIATED;
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;
96 return PortMatrixNode::ASSOCIATED;
99 bool
100 BundleEditorMatrix::can_add_channel (boost::shared_ptr<Bundle> b) const
102 if (b == _bundle) {
103 return true;
106 return PortMatrix::can_add_channel (b);
109 void
110 BundleEditorMatrix::add_channel (boost::shared_ptr<Bundle> b, DataType t)
112 if (b == _bundle) {
114 NameChannelDialog d;
115 d.set_position (Gtk::WIN_POS_MOUSE);
117 if (d.run () != Gtk::RESPONSE_ACCEPT) {
118 return;
121 _bundle->add_channel (d.get_name(), t);
122 setup_ports (OURS);
124 } else {
126 PortMatrix::add_channel (b, t);
131 bool
132 BundleEditorMatrix::can_remove_channels (boost::shared_ptr<Bundle> b) const
134 if (b == _bundle) {
135 return true;
138 return PortMatrix::can_remove_channels (b);
141 void
142 BundleEditorMatrix::remove_channel (BundleChannel bc)
144 bc.bundle->remove_channel (bc.channel);
145 setup_ports (OURS);
148 bool
149 BundleEditorMatrix::can_rename_channels (boost::shared_ptr<Bundle> b) const
151 if (b == _bundle) {
152 return true;
155 return PortMatrix::can_rename_channels (b);
158 void
159 BundleEditorMatrix::rename_channel (BundleChannel bc)
161 NameChannelDialog d (bc.bundle, bc.channel);
162 d.set_position (Gtk::WIN_POS_MOUSE);
164 if (d.run () != Gtk::RESPONSE_ACCEPT) {
165 return;
168 bc.bundle->set_channel_name (bc.channel, d.get_name ());
171 bool
172 BundleEditorMatrix::list_is_global (int dim) const
174 return (dim == OTHER);
177 string
178 BundleEditorMatrix::disassociation_verb () const
180 return _("Disassociate");
183 BundleEditor::BundleEditor (Session* session, boost::shared_ptr<UserBundle> bundle)
184 : ArdourDialog (_("Edit Bundle")), _matrix (this, session, bundle), _bundle (bundle)
186 Gtk::Table* t = new Gtk::Table (3, 2);
187 t->set_spacings (4);
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));
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"));
207 if (bundle->ports_are_inputs()) {
208 _input_or_output.set_active_text (_("Input"));
209 } else {
210 _input_or_output.set_active_text (_("Output"));
213 _input_or_output.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::input_or_output_changed));
215 get_vbox()->pack_start (*Gtk::manage (t), false, false);
216 get_vbox()->pack_start (_matrix);
217 get_vbox()->set_spacing (4);
219 add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_ACCEPT);
220 show_all ();
223 void
224 BundleEditor::on_show ()
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);
231 void
232 BundleEditor::name_changed ()
234 _bundle->set_name (_name.get_text ());
237 void
238 BundleEditor::input_or_output_changed ()
240 _bundle->remove_ports_from_channels ();
242 if (_input_or_output.get_active_text() == _("Output")) {
243 _bundle->set_ports_are_outputs ();
244 } else {
245 _bundle->set_ports_are_inputs ();
248 _matrix.setup_all_ports ();
251 void
252 BundleEditor::on_map ()
254 _matrix.setup_all_ports ();
255 Window::on_map ();
259 BundleManager::BundleManager (Session* session)
260 : ArdourDialog (_("Bundle Manager"))
261 , edit_button (_("Edit"))
262 , delete_button (_("Delete"))
264 set_session (session);
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);
271 boost::shared_ptr<BundleList> bundles = _session->bundles ();
272 for (BundleList::iterator i = bundles->begin(); i != bundles->end(); ++i) {
273 add_bundle (*i);
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);
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);
296 get_vbox()->set_spacing (8);
297 get_vbox()->pack_start (*Gtk::manage (h));
299 set_default_size (480, 240);
301 _tree_view.get_selection()->signal_changed().connect (
302 sigc::mem_fun (*this, &BundleManager::set_button_sensitivity)
305 _tree_view.signal_row_activated().connect (
306 sigc::mem_fun (*this, &BundleManager::row_activated)
309 set_button_sensitivity ();
311 show_all ();
314 void
315 BundleManager::set_button_sensitivity ()
317 bool const sel = (_tree_view.get_selection()->get_selected() != 0);
318 edit_button.set_sensitive (sel);
319 delete_button.set_sensitive (sel);
323 void
324 BundleManager::new_clicked ()
326 boost::shared_ptr<UserBundle> b (new UserBundle (_("Bundle")));
328 /* Start off with a single channel */
329 /* XXX: allow user to specify type */
330 b->add_channel ("1", DataType::AUDIO);
332 _session->add_bundle (b);
333 add_bundle (b);
335 BundleEditor e (_session, b);
336 e.run ();
339 void
340 BundleManager::edit_clicked ()
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 ();
350 void
351 BundleManager::delete_clicked ()
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);
361 void
362 BundleManager::add_bundle (boost::shared_ptr<Bundle> b)
364 boost::shared_ptr<UserBundle> u = boost::dynamic_pointer_cast<UserBundle> (b);
365 if (u == 0) {
366 return;
369 Gtk::TreeModel::iterator i = _list_model->append ();
370 (*i)[_list_model_columns.name] = u->name ();
371 (*i)[_list_model_columns.bundle] = u;
373 u->Changed.connect (bundle_connections, invalidator (*this), ui_bind (&BundleManager::bundle_changed, this, _1, u), gui_context());
376 void
377 BundleManager::bundle_changed (Bundle::Change c, boost::shared_ptr<UserBundle> b)
379 if ((c & Bundle::NameChanged) == 0) {
380 return;
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;
389 ++i;
392 if (i != _list_model->children().end()) {
393 (*i)[_list_model_columns.name] = b->name ();
397 void
398 BundleManager::row_activated (Gtk::TreeModel::Path const & p, Gtk::TreeViewColumn*)
400 Gtk::TreeModel::iterator i = _list_model->get_iter (p);
401 if (!i) {
402 return;
405 boost::shared_ptr<UserBundle> b = (*i)[_list_model_columns.bundle];
406 BundleEditor e (_session, b);
407 e.run ();
410 NameChannelDialog::NameChannelDialog ()
411 : ArdourDialog (_("Add Channel")),
412 _adding (true)
414 setup ();
417 NameChannelDialog::NameChannelDialog (boost::shared_ptr<Bundle> b, uint32_t c)
418 : ArdourDialog (_("Rename Channel")),
419 _bundle (b),
420 _channel (c),
421 _adding (false)
423 _name.set_text (b->channel_name (c));
425 setup ();
428 void
429 NameChannelDialog::setup ()
431 Gtk::HBox* box = Gtk::manage (new Gtk::HBox ());
433 box->pack_start (*Gtk::manage (new Gtk::Label (_("Name"))));
434 box->pack_start (_name);
435 _name.set_activates_default (true);
437 get_vbox ()->pack_end (*box);
438 box->show_all ();
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);
446 set_default_response (Gtk::RESPONSE_ACCEPT);
449 string
450 NameChannelDialog::get_name () const
452 return _name.get_text ();