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