remove close buttons from Bundle Manager (rely on WM provided button)
[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 "pbd/i18n.h"
33 #include "utils.h"
34
35 using namespace std;
36 using namespace ARDOUR;
37 using namespace ARDOUR_UI_UTILS;
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, show_only_bundles ());
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         if (c[0].bundle->nchannels() == ChanCount::ZERO || c[1].bundle->nchannels() == ChanCount::ZERO) {
86                 return PortMatrixNode::NOT_ASSOCIATED;
87         }
88
89         Bundle::PortList const& pl = c[OTHER].bundle->channel_ports (c[OTHER].channel);
90         if (pl.empty ()) {
91                 return PortMatrixNode::NOT_ASSOCIATED;
92         }
93
94         for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
95                 if (!c[OURS].bundle->port_attached_to_channel (c[OURS].channel, *i)) {
96                         return PortMatrixNode::NOT_ASSOCIATED;
97                 }
98         }
99
100         return PortMatrixNode::ASSOCIATED;
101 }
102
103 bool
104 BundleEditorMatrix::can_add_channels (boost::shared_ptr<Bundle> b) const
105 {
106         if (b == _bundle) {
107                 return true;
108         }
109
110         return PortMatrix::can_add_channels (b);
111 }
112
113 void
114 BundleEditorMatrix::add_channel (boost::shared_ptr<Bundle> b, DataType t)
115 {
116         if (b == _bundle) {
117
118                 NameChannelDialog d;
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
166         if (d.run () != Gtk::RESPONSE_ACCEPT) {
167                 return;
168         }
169
170         bc.bundle->set_channel_name (bc.channel, d.get_name ());
171 }
172
173 bool
174 BundleEditorMatrix::list_is_global (int dim) const
175 {
176         return (dim == OTHER);
177 }
178
179 string
180 BundleEditorMatrix::disassociation_verb () const
181 {
182         return _("Disassociate");
183 }
184
185 BundleEditor::BundleEditor (Session* session, boost::shared_ptr<UserBundle> bundle)
186         : ArdourDialog (_("Edit Bundle")), _matrix (this, session, bundle), _bundle (bundle)
187 {
188         Gtk::Table* t = new Gtk::Table (3, 2);
189         t->set_spacings (4);
190
191         /* Bundle name */
192         Gtk::Alignment* a = new Gtk::Alignment (1, 0.5, 0, 1);
193         a->add (*Gtk::manage (new Gtk::Label (_("Name:"))));
194         t->attach (*Gtk::manage (a), 0, 1, 0, 1, Gtk::FILL, Gtk::FILL);
195         t->attach (_name, 1, 2, 0, 1);
196         _name.set_text (_bundle->name ());
197         _name.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::name_changed));
198
199         /* Direction (input or output) */
200         a = new Gtk::Alignment (1, 0.5, 0, 1);
201         a->add (*Gtk::manage (new Gtk::Label (_("Direction:"))));
202         t->attach (*Gtk::manage (a), 0, 1, 1, 2, Gtk::FILL, Gtk::FILL);
203         a = new Gtk::Alignment (0, 0.5, 0, 1);
204         a->add (_input_or_output);
205         t->attach (*Gtk::manage (a), 1, 2, 1, 2);
206         _input_or_output.append_text (_("Destination"));
207         _input_or_output.append_text (_("Source"));
208
209         if (bundle->ports_are_inputs()) {
210                 _input_or_output.set_active_text (_("Source"));
211         } else {
212                 _input_or_output.set_active_text (_("Destination"));
213         }
214
215         _input_or_output.signal_changed().connect (sigc::mem_fun (*this, &BundleEditor::input_or_output_changed));
216
217         get_vbox()->pack_start (*Gtk::manage (t), false, false);
218         get_vbox()->pack_start (_matrix);
219         get_vbox()->set_spacing (4);
220
221         show_all ();
222
223         signal_key_press_event().connect (sigc::mem_fun (_matrix, &BundleEditorMatrix::key_press));
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() == _("Source")) {
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::StockID(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         set_button_sensitivity ();
313
314         show_all ();
315 }
316
317 void
318 BundleManager::set_button_sensitivity ()
319 {
320         bool const sel = (_tree_view.get_selection()->get_selected() != 0);
321         edit_button.set_sensitive (sel);
322         delete_button.set_sensitive (sel);
323 }
324
325
326 void
327 BundleManager::new_clicked ()
328 {
329         boost::shared_ptr<UserBundle> b (new UserBundle (_("Bundle")));
330
331         /* Start off with a single channel */
332         /* XXX: allow user to specify type */
333         b->add_channel ("1", DataType::AUDIO);
334
335         _session->add_bundle (b);
336         add_bundle (b);
337
338         BundleEditor e (_session, b);
339         e.run ();
340 }
341
342 void
343 BundleManager::edit_clicked ()
344 {
345         Gtk::TreeModel::iterator i = _tree_view.get_selection()->get_selected();
346         if (i) {
347                 boost::shared_ptr<UserBundle> b = (*i)[_list_model_columns.bundle];
348                 BundleEditor e (_session, b);
349                 e.run ();
350         }
351 }
352
353 void
354 BundleManager::delete_clicked ()
355 {
356         Gtk::TreeModel::iterator i = _tree_view.get_selection()->get_selected();
357         if (i) {
358                 boost::shared_ptr<UserBundle> b = (*i)[_list_model_columns.bundle];
359                 _session->remove_bundle (b);
360                 _list_model->erase (i);
361         }
362 }
363
364 void
365 BundleManager::add_bundle (boost::shared_ptr<Bundle> b)
366 {
367         boost::shared_ptr<UserBundle> u = boost::dynamic_pointer_cast<UserBundle> (b);
368         if (u == 0) {
369                 return;
370         }
371
372         Gtk::TreeModel::iterator i = _list_model->append ();
373         (*i)[_list_model_columns.name] = u->name ();
374         (*i)[_list_model_columns.bundle] = u;
375
376         u->Changed.connect (bundle_connections, invalidator (*this), boost::bind (&BundleManager::bundle_changed, this, _1, u), gui_context());
377 }
378
379 void
380 BundleManager::bundle_changed (Bundle::Change c, boost::shared_ptr<UserBundle> b)
381 {
382         if ((c & Bundle::NameChanged) == 0) {
383                 return;
384         }
385
386         Gtk::TreeModel::iterator i = _list_model->children().begin ();
387         while (i != _list_model->children().end()) {
388                 boost::shared_ptr<UserBundle> t = (*i)[_list_model_columns.bundle];
389                 if (t == b) {
390                         break;
391                 }
392                 ++i;
393         }
394
395         if (i != _list_model->children().end()) {
396                 (*i)[_list_model_columns.name] = b->name ();
397         }
398 }
399
400 void
401 BundleManager::row_activated (Gtk::TreeModel::Path const & p, Gtk::TreeViewColumn*)
402 {
403         Gtk::TreeModel::iterator i = _list_model->get_iter (p);
404         if (!i) {
405                 return;
406         }
407
408         boost::shared_ptr<UserBundle> b = (*i)[_list_model_columns.bundle];
409         BundleEditor e (_session, b);
410         e.run ();
411 }
412
413 NameChannelDialog::NameChannelDialog ()
414         : ArdourDialog (_("Add Channel")),
415           _adding (true)
416 {
417         setup ();
418 }
419
420 NameChannelDialog::NameChannelDialog (boost::shared_ptr<Bundle> b, uint32_t c)
421         : ArdourDialog (_("Rename Channel")),
422           _bundle (b),
423           _adding (false)
424 {
425         _name.set_text (b->channel_name (c));
426
427         setup ();
428 }
429
430 void
431 NameChannelDialog::setup ()
432 {
433         Gtk::HBox* box = Gtk::manage (new Gtk::HBox ());
434
435         box->pack_start (*Gtk::manage (new Gtk::Label (_("Name"))));
436         box->pack_start (_name);
437         _name.set_activates_default (true);
438
439         get_vbox ()->pack_end (*box);
440         box->show_all ();
441
442         add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
443         if (_adding) {
444                 add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
445         } else {
446                 add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_ACCEPT);
447         }
448         set_default_response (Gtk::RESPONSE_ACCEPT);
449 }
450
451 string
452 NameChannelDialog::get_name () const
453 {
454         return _name.get_text ();
455 }
456