Add some convenient public editor methods (for lua-bindings)
[ardour.git] / gtk2_ardour / patch_change_dialog.cc
1 /*
2     Copyright (C) 2010 Paul Davis
3     Author: Carl Hetherington <cth@carlh.net>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <gtkmm/stock.h>
22 #include <gtkmm/table.h>
23
24 #include <boost/algorithm/string.hpp>
25
26 #include "gtkmm2ext/utils.h"
27
28 #include "midi++/midnam_patch.h"
29
30 #include "ardour/beats_frames_converter.h"
31 #include "ardour/instrument_info.h"
32
33 #include "patch_change_dialog.h"
34 #include "gui_thread.h"
35
36 #include "pbd/i18n.h"
37
38 using namespace std;
39 using namespace Gtk;
40 using namespace Gtkmm2ext;
41
42 /** @param tc If non-0, a time converter for this patch change.  If 0, time control will be desensitized */
43 PatchChangeDialog::PatchChangeDialog (
44         const ARDOUR::BeatsFramesConverter*        tc,
45         ARDOUR::Session*                           session,
46         Evoral::PatchChange<Evoral::Beats> const & patch,
47         ARDOUR::InstrumentInfo&                    info,
48         const Gtk::BuiltinStockID&                 ok,
49         bool                                       allow_delete)
50         : ArdourDialog (_("Patch Change"), true)
51         , _time_converter (tc)
52         , _info (info)
53         , _time (X_("patchchangetime"), true, "", true, false)
54         , _channel (*manage (new Adjustment (1, 1, 16, 1, 4)))
55         , _program (*manage (new Adjustment (1, 1, 128, 1, 16)))
56         , _bank_msb (*manage (new Adjustment (0, 0, 127, 1, 16)))
57         , _bank_lsb (*manage (new Adjustment (0, 0, 127, 1, 16)))
58         , _ignore_signals (false)
59 {
60         Table* t = manage (new Table (4, 2));
61         Label* l;
62         t->set_spacings (6);
63         int r = 0;
64
65         if (_time_converter) {
66
67                 l = manage (left_aligned_label (_("Time")));
68                 t->attach (*l, 0, 1, r, r + 1);
69                 t->attach (_time, 1, 2, r, r + 1);
70                 ++r;
71
72                 _time.set_session (session);
73                 _time.set_mode (AudioClock::BBT);
74                 _time.set (_time_converter->to (patch.time ()), true);
75         }
76
77         l = manage (left_aligned_label (_("Patch Bank")));
78         t->attach (*l, 0, 1, r, r + 1);
79         t->attach (_bank_combo, 1, 2, r, r + 1);
80         ++r;
81
82         _bank_combo.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::bank_combo_changed));
83
84         l = manage (left_aligned_label (_("Patch")));
85         t->attach (*l, 0, 1, r, r + 1);
86         t->attach (_patch_combo, 1, 2, r, r + 1);
87         ++r;
88
89         _patch_combo.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::patch_combo_changed));
90
91         l = manage (left_aligned_label (_("Channel")));
92         t->attach (*l, 0, 1, r, r + 1);
93         t->attach (_channel, 1, 2, r, r + 1);
94         ++r;
95
96         _channel.set_value (patch.channel() + 1);
97         _channel.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::channel_changed));
98
99         l = manage (left_aligned_label (_("Program")));
100         t->attach (*l, 0, 1, r, r + 1);
101         t->attach (_program, 1, 2, r, r + 1);
102         ++r;
103
104         _program.set_value (patch.program () + 1);
105         _program.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::program_changed));
106
107         l = manage (left_aligned_label (_("Bank MSB")));
108         t->attach (*l, 0, 1, r, r + 1);
109         t->attach (_bank_msb, 1, 2, r, r + 1);
110         ++r;
111
112         l = manage (left_aligned_label (_("Bank LSB")));
113         t->attach (*l, 0, 1, r, r + 1);
114         t->attach (_bank_lsb, 1, 2, r, r + 1);
115         ++r;
116
117         _bank_msb.set_value ((patch.bank() >> 7));
118         _bank_msb.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::bank_changed));
119
120         _bank_lsb.set_value ((patch.bank() & 127));
121         _bank_lsb.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::bank_changed));
122
123         get_vbox()->add (*t);
124
125         add_button (Stock::CANCEL, RESPONSE_CANCEL);
126         add_button (ok, RESPONSE_ACCEPT);
127         if (allow_delete) {
128                 add_button (Gtk::StockID(GTK_STOCK_DELETE), RESPONSE_REJECT);
129         }
130         set_default_response (RESPONSE_ACCEPT);
131
132         fill_bank_combo ();
133         set_active_bank_combo ();
134         bank_combo_changed ();
135
136         _info.Changed.connect (_info_changed_connection, invalidator (*this),
137                                boost::bind (&PatchChangeDialog::instrument_info_changed, this), gui_context());
138
139         show_all ();
140 }
141
142 int
143 PatchChangeDialog::get_14bit_bank () const
144 {
145         return (_bank_msb.get_value_as_int() << 7) + _bank_lsb.get_value_as_int();
146 }
147
148 void
149 PatchChangeDialog::instrument_info_changed ()
150 {
151         _bank_combo.clear ();
152         _patch_combo.clear ();
153         fill_bank_combo ();
154         fill_patch_combo ();
155 }
156
157 Evoral::PatchChange<Evoral::Beats>
158 PatchChangeDialog::patch () const
159 {
160         Evoral::Beats t = Evoral::Beats();
161
162         if (_time_converter) {
163                 t = _time_converter->from (_time.current_time ());
164         }
165
166         return Evoral::PatchChange<Evoral::Beats> (
167                 t,
168                 _channel.get_value_as_int() - 1,
169                 _program.get_value_as_int() - 1,
170                 get_14bit_bank ()
171                 );
172 }
173
174 /** Fill the bank_combo according to the current _channel */
175 void
176 PatchChangeDialog::fill_bank_combo ()
177 {
178         _bank_combo.clear ();
179
180         boost::shared_ptr<MIDI::Name::ChannelNameSet> cns = _info.get_patches (_channel.get_value_as_int() - 1);
181
182         if (!cns) {
183                 return;
184         }
185
186         for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
187                 string n = (*i)->name ();
188                 boost::replace_all (n, "_", " ");
189                 _bank_combo.append_text (n);
190         }
191 }
192
193 /** Set the active value of the bank_combo, and _current_patch_bank, from the contents of _bank */
194 void
195 PatchChangeDialog::set_active_bank_combo ()
196 {
197         _current_patch_bank.reset ();
198
199         boost::shared_ptr<MIDI::Name::ChannelNameSet> cns = _info.get_patches (_channel.get_value_as_int() - 1);
200
201         if (!cns) {
202                 return;
203         }
204
205         for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
206
207                 string n = (*i)->name ();
208                 boost::replace_all (n, "_", " ");
209
210                 if ((*i)->number() == get_14bit_bank()) {
211                         _current_patch_bank = *i;
212                         _ignore_signals = true;
213                         _bank_combo.set_active_text (n);
214                         _ignore_signals = false;
215                         return;
216                 }
217         }
218
219         _ignore_signals = true;
220         _bank_combo.set_active (-1);
221         _ignore_signals = false;
222 }
223
224 /** Update _current_patch_bank and reflect the current value of
225  *  bank_combo in the rest of the dialog.
226  */
227 void
228 PatchChangeDialog::bank_combo_changed ()
229 {
230         if (_ignore_signals) {
231                 return;
232         }
233
234         _current_patch_bank.reset ();
235
236         boost::shared_ptr<MIDI::Name::ChannelNameSet> cns = _info.get_patches (_channel.get_value_as_int() - 1);
237
238         if (!cns) {
239                 return;
240         }
241
242         for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
243                 string n = (*i)->name ();
244                 boost::replace_all (n, "_", " ");
245                 if (n == _bank_combo.get_active_text()) {
246                         _current_patch_bank = *i;
247                 }
248         }
249
250         if (_current_patch_bank == 0) {
251                 return;
252         }
253
254         /* Reflect */
255
256         fill_patch_combo ();
257         set_active_patch_combo ();
258
259         _ignore_signals = true;
260         _bank_msb.set_value (_current_patch_bank->number() >> 7);
261         _bank_lsb.set_value (_current_patch_bank->number() & 127);
262         _ignore_signals = false;
263 }
264
265 /** Fill the contents of the patch combo */
266 void
267 PatchChangeDialog::fill_patch_combo ()
268 {
269         _patch_combo.clear ();
270
271         if (_current_patch_bank == 0) {
272                 return;
273         }
274
275         const MIDI::Name::PatchNameList& patches = _current_patch_bank->patch_name_list ();
276         for (MIDI::Name::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
277                 string n = (*j)->name ();
278                 boost::replace_all (n, "_", " ");
279                 _patch_combo.append_text (n);
280         }
281 }
282
283 /** Set the active value of the patch combo from the value of the _program entry */
284 void
285 PatchChangeDialog::set_active_patch_combo ()
286 {
287         if (_ignore_signals) {
288                 return;
289         }
290
291         if (_current_patch_bank == 0) {
292                 _ignore_signals = true;
293                 _patch_combo.set_active (-1);
294                 _ignore_signals = false;
295                 return;
296         }
297
298         const MIDI::Name::PatchNameList& patches = _current_patch_bank->patch_name_list ();
299         for (MIDI::Name::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
300                 string n = (*j)->name ();
301                 boost::replace_all (n, "_", " ");
302
303                 MIDI::Name::PatchPrimaryKey const & key = (*j)->patch_primary_key ();
304                 if (key.program() == _program.get_value() - 1) {
305                         _ignore_signals = true;
306                         _patch_combo.set_active_text (n);
307                         _ignore_signals = false;
308                         return;
309                 }
310         }
311
312         _ignore_signals = true;
313         _patch_combo.set_active (-1);
314         _ignore_signals = false;
315 }
316
317 /** Set _program from the current state of _patch_combo */
318 void
319 PatchChangeDialog::patch_combo_changed ()
320 {
321         if (_ignore_signals || _current_patch_bank == 0) {
322                 return;
323         }
324
325         const MIDI::Name::PatchNameList& patches = _current_patch_bank->patch_name_list ();
326
327         for (MIDI::Name::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
328                 string n = (*j)->name ();
329                 boost::replace_all (n, "_", " ");
330
331                 if (n == _patch_combo.get_active_text ()) {
332                         _ignore_signals = true;
333                         _program.set_value ((*j)->program_number() + 1);
334                         _ignore_signals = false;
335                         break;
336                 }
337         }
338 }
339
340 void
341 PatchChangeDialog::channel_changed ()
342 {
343         fill_bank_combo ();
344         set_active_bank_combo ();
345         fill_patch_combo ();
346         set_active_patch_combo ();
347 }
348
349 void
350 PatchChangeDialog::program_changed ()
351 {
352         if (_ignore_signals) {
353                 return;
354         }
355
356         set_active_patch_combo ();
357 }
358
359 void
360 PatchChangeDialog::bank_changed ()
361 {
362         if (_ignore_signals) {
363                 return;
364         }
365
366         set_active_bank_combo ();
367         fill_patch_combo ();
368         set_active_patch_combo ();
369 }
370