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