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