Fix ever expanding bank selector in add patch change dialog when changing channel...
[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 "ardour/midi_patch_manager.h"
29 #include "ardour/beats_frames_converter.h"
30 #include "ardour/instrument_info.h"
31
32 #include "patch_change_dialog.h"
33 #include "gui_thread.h"
34
35 #include "i18n.h"
36
37 using namespace std;
38 using namespace Gtk;
39 using namespace Gtkmm2ext;
40
41 /** @param tc If non-0, a time converter for this patch change.  If 0, time control will be desensitized */
42 PatchChangeDialog::PatchChangeDialog (
43         const ARDOUR::BeatsFramesConverter*              tc,
44         ARDOUR::Session*                                 session,
45         Evoral::PatchChange<Evoral::MusicalTime> const & patch,
46         ARDOUR::InstrumentInfo&                          info,
47         const Gtk::BuiltinStockID&                       ok,
48         bool                                             allow_delete)
49         : ArdourDialog (_("Patch Change"), true)
50         , _time_converter (tc)
51         , _info (info)
52         , _time (X_("patchchangetime"), true, "", true, false)
53         , _channel (*manage (new Adjustment (1, 1, 16, 1, 4)))
54         , _program (*manage (new Adjustment (1, 1, 128, 1, 16)))
55         , _bank (*manage (new Adjustment (1, 1, 16384, 1, 64)))
56         , _ignore_signals (false)
57 {
58         Table* t = manage (new Table (4, 2));
59         Label* l;
60         t->set_spacings (6);
61         int r = 0;
62
63         if (_time_converter) {
64                 
65                 l = manage (left_aligned_label (_("Time")));
66                 t->attach (*l, 0, 1, r, r + 1);
67                 t->attach (_time, 1, 2, r, r + 1);
68                 ++r;
69
70                 _time.set_session (session);
71                 _time.set_mode (AudioClock::BBT);
72                 _time.set (_time_converter->to (patch.time ()), true);
73         }
74
75         l = manage (left_aligned_label (_("Patch Bank")));
76         t->attach (*l, 0, 1, r, r + 1);
77         t->attach (_bank_combo, 1, 2, r, r + 1);
78         ++r;
79
80         _bank_combo.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::bank_combo_changed));
81
82         l = manage (left_aligned_label (_("Patch")));
83         t->attach (*l, 0, 1, r, r + 1);
84         t->attach (_patch_combo, 1, 2, r, r + 1);
85         ++r;
86         
87         _patch_combo.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::patch_combo_changed));
88
89         l = manage (left_aligned_label (_("Channel")));
90         t->attach (*l, 0, 1, r, r + 1);
91         t->attach (_channel, 1, 2, r, r + 1);
92         ++r;
93
94         _channel.set_value (patch.channel() + 1);
95         _channel.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::channel_changed));
96
97         l = manage (left_aligned_label (_("Program")));
98         t->attach (*l, 0, 1, r, r + 1);
99         t->attach (_program, 1, 2, r, r + 1);
100         ++r;
101
102         _program.set_value (patch.program () + 1);
103         _program.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::program_changed));
104
105         l = manage (left_aligned_label (_("Bank")));
106         t->attach (*l, 0, 1, r, r + 1);
107         t->attach (_bank, 1, 2, r, r + 1);
108         ++r;
109
110         _bank.set_value (patch.bank() + 1);
111         _bank.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::bank_changed));
112
113         get_vbox()->add (*t);
114
115         add_button (Stock::CANCEL, RESPONSE_CANCEL);
116         add_button (ok, RESPONSE_ACCEPT);
117         if (allow_delete) {
118                 add_button (Stock::DELETE, RESPONSE_REJECT);
119         }
120         set_default_response (RESPONSE_ACCEPT);
121
122         fill_bank_combo ();
123         set_active_bank_combo ();
124         bank_combo_changed ();
125
126         _info.Changed.connect (_info_changed_connection, invalidator (*this), 
127                                boost::bind (&PatchChangeDialog::instrument_info_changed, this), gui_context());
128
129         show_all ();
130 }
131
132 void
133 PatchChangeDialog::instrument_info_changed ()
134 {
135         _bank_combo.clear ();
136         _patch_combo.clear ();
137         fill_bank_combo ();
138         fill_patch_combo ();
139 }
140
141 Evoral::PatchChange<Evoral::MusicalTime>
142 PatchChangeDialog::patch () const
143 {
144         Evoral::MusicalTime t = 0;
145
146         if (_time_converter) {
147                 t = _time_converter->from (_time.current_time ());
148         }
149
150         return Evoral::PatchChange<Evoral::MusicalTime> (
151                 t,
152                 _channel.get_value_as_int() - 1,
153                 _program.get_value_as_int() - 1,
154                 _bank.get_value_as_int() - 1
155                 );
156 }
157
158 /** Fill the bank_combo according to the current _channel */
159 void
160 PatchChangeDialog::fill_bank_combo ()
161 {
162         _bank_combo.clear ();
163
164         boost::shared_ptr<MIDI::Name::ChannelNameSet> cns = _info.get_patches (_channel.get_value_as_int() - 1);
165
166         if (!cns) {
167                 return;
168         }
169
170         for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
171                 string n = (*i)->name ();
172                 boost::replace_all (n, "_", " ");
173                 _bank_combo.append_text (n);
174         }
175 }
176
177 /** Set the active value of the bank_combo, and _current_patch_bank, from the contents of _bank */
178 void
179 PatchChangeDialog::set_active_bank_combo ()
180 {
181         _current_patch_bank.reset ();
182         
183         boost::shared_ptr<MIDI::Name::ChannelNameSet> cns = _info.get_patches (_channel.get_value_as_int() - 1);
184
185         if (!cns) {
186                 return;
187         }
188
189         for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
190
191                 string n = (*i)->name ();
192                 boost::replace_all (n, "_", " ");
193
194                 if ((*i)->number() == _bank.get_value () - 1) {
195                         _current_patch_bank = *i;
196                         _ignore_signals = true;
197                         _bank_combo.set_active_text (n);
198                         _ignore_signals = false;
199                         return;
200                 }
201         }
202
203         _ignore_signals = true;
204         _bank_combo.set_active (-1);
205         _ignore_signals = false;
206 }
207
208 /** Update _current_patch_bank and reflect the current value of
209  *  bank_combo in the rest of the dialog.
210  */
211 void
212 PatchChangeDialog::bank_combo_changed ()
213 {
214         if (_ignore_signals) {
215                 return;
216         }
217         
218         _current_patch_bank.reset ();
219
220         boost::shared_ptr<MIDI::Name::ChannelNameSet> cns = _info.get_patches (_channel.get_value_as_int() - 1);
221
222         if (!cns) {
223                 return;
224         }
225
226         for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
227                 string n = (*i)->name ();
228                 boost::replace_all (n, "_", " ");
229                 if (n == _bank_combo.get_active_text()) {
230                         _current_patch_bank = *i;
231                 }
232         }
233
234         if (_current_patch_bank == 0) {
235                 return;
236         }
237
238         /* Reflect */
239
240         fill_patch_combo ();
241         set_active_patch_combo ();
242
243         _ignore_signals = true;
244         _bank.set_value (_current_patch_bank->number() + 1);
245         _ignore_signals = false;
246 }
247
248 /** Fill the contents of the patch combo */
249 void
250 PatchChangeDialog::fill_patch_combo ()
251 {
252         _patch_combo.clear ();
253
254         if (_current_patch_bank == 0) {
255                 return;
256         }
257
258         const MIDI::Name::PatchBank::PatchNameList& patches = _current_patch_bank->patch_name_list ();
259         for (MIDI::Name::PatchBank::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
260                 string n = (*j)->name ();
261                 boost::replace_all (n, "_", " ");
262                 _patch_combo.append_text (n);
263         }
264 }
265
266 /** Set the active value of the patch combo from the value of the _program entry */
267 void
268 PatchChangeDialog::set_active_patch_combo ()
269 {
270         if (_ignore_signals) {
271                 return;
272         }
273
274         if (_current_patch_bank == 0) {
275                 _ignore_signals = true;
276                 _patch_combo.set_active (-1);
277                 _ignore_signals = false;
278                 return;
279         }
280         
281         const MIDI::Name::PatchBank::PatchNameList& patches = _current_patch_bank->patch_name_list ();
282         for (MIDI::Name::PatchBank::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
283                 string n = (*j)->name ();
284                 boost::replace_all (n, "_", " ");
285
286                 MIDI::Name::PatchPrimaryKey const & key = (*j)->patch_primary_key ();
287                 if (key.program_number == _program.get_value() - 1) {
288                         _ignore_signals = true;
289                         _patch_combo.set_active_text (n);
290                         _ignore_signals = false;
291                         return;
292                 }
293         }
294
295         _ignore_signals = true;
296         _patch_combo.set_active (-1);
297         _ignore_signals = false;
298 }       
299
300 /** Set _program from the current state of _patch_combo */
301 void
302 PatchChangeDialog::patch_combo_changed ()
303 {
304         if (_ignore_signals || _current_patch_bank == 0) {
305                 return;
306         }
307
308         const MIDI::Name::PatchBank::PatchNameList& patches = _current_patch_bank->patch_name_list ();
309
310         for (MIDI::Name::PatchBank::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
311                 string n = (*j)->name ();
312                 boost::replace_all (n, "_", " ");
313
314                 if (n == _patch_combo.get_active_text ()) {
315                         _ignore_signals = true;
316                         _program.set_value ((*j)->program_number() + 1);
317                         _ignore_signals = false;
318                         break;
319                 }
320         }
321 }
322
323 void
324 PatchChangeDialog::channel_changed ()
325 {
326         fill_bank_combo ();
327         set_active_bank_combo ();
328         fill_patch_combo ();
329         set_active_patch_combo ();
330 }
331
332 void
333 PatchChangeDialog::program_changed ()
334 {
335         if (_ignore_signals) {
336                 return;
337         }
338
339         set_active_patch_combo ();
340 }
341
342 void
343 PatchChangeDialog::bank_changed ()
344 {
345         if (_ignore_signals) {
346                 return;
347         }
348
349         set_active_bank_combo ();
350         fill_patch_combo ();
351         set_active_patch_combo ();
352 }
353