/[svn]/gigedit/trunk/src/gigedit/paramedit.cpp
ViewVC logotype

Contents of /gigedit/trunk/src/gigedit/paramedit.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3364 - (show annotations) (download)
Tue Nov 14 18:07:25 2017 UTC (6 years, 5 months ago) by schoenebeck
File size: 17821 byte(s)
* Added experimental support for upcoming GTK(MM)4
  (for now up to GTKMM 3.91.2 while still preserving backward compatibility
  down to GTKMM 2).
* Re-merged r2845 to compile now with and without Gtk "Stock ID" API
  (see also r3158).

1 /*
2 * Copyright (C) 2006-2017 Andreas Persson
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with program; see the file COPYING. If not, write to the Free
16 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #include "global.h"
21 #include "paramedit.h"
22
23 #include "compat.h"
24 #include "Settings.h"
25
26 #include <gtkmm/messagedialog.h>
27
28 namespace {
29 struct CCText {
30 const char* const txt;
31 bool isExtension; ///< True if this is a controller only supported by LinuxSampler, but not supperted by Gigasampler/GigaStudio.
32 };
33 static const CCText controlChangeTexts[] = {
34 // 3 special ones (not being CCs)
35 { _("none") }, { _("channelaftertouch") }, { _("velocity") },
36 {0}, // bank select MSB (hard coded in sampler, so discouraged to be used here, even though considerable)
37 { _("modwheel") }, // "Modulation Wheel or Lever",
38 { _("breath") }, // "Breath Controller",
39 { _("undefined"), true },
40 { _("foot") }, // "Foot Controller",
41 { _("portamentotime") }, // "Portamento Time",
42 { _("data entry MSB"), true },
43 { _("volume"), true },
44 { _("balance"), true },
45 { _("undefined"), true },
46 { _("pan"), true },
47 { _("expression"), true },
48 { _("effect1") }, // "Effect Control 1",
49 { _("effect2") }, // "Effect Control 2",
50 { _("undefined"), true },
51 { _("undefined"), true },
52 { _("genpurpose1") }, // "General Purpose Controller 1",
53 { _("genpurpose2") }, // "General Purpose Controller 2",
54 { _("genpurpose3") }, // "General Purpose Controller 3",
55 { _("genpurpose4") }, // "General Purpose Controller 4",
56 { _("undefined"), true },
57 { _("undefined"), true },
58 { _("undefined"), true },
59 { _("undefined"), true },
60 { _("undefined"), true },
61 { _("undefined"), true },
62 { _("undefined"), true },
63 { _("undefined"), true },
64 { _("undefined"), true },
65 { _("undefined"), true },
66 { _("undefined"), true },
67 { _("undefined"), true },
68
69 // LSB variant of the various controllers above
70 // (so discouraged to be used here for now)
71 {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0},
72 {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0},
73 {0}, {0}, {0}, {0},
74
75 { _("sustainpedal") }, // "Damper Pedal on/off (Sustain)",
76 { _("portamento") }, // "Portamento On/Off",
77 { _("sostenuto") }, // "Sustenuto On/Off",
78 { _("softpedal") }, // "Soft Pedal On/Off",
79 { _("legato"), true },
80 { _("hold2"), true },
81 { _("soundvariation"), true },
82 { _("timbre"), true },
83 { _("releasetime"), true },
84 { _("attacktime"), true },
85 { _("brightness"), true },
86 { _("decaytime"), true },
87 { _("vibratorate"), true },
88 { _("vibratodepth"), true },
89 { _("vibratodelay"), true },
90 { _("undefined"), true },
91 { _("genpurpose5") }, // "General Purpose Controller 5",
92 { _("genpurpose6") }, // "General Purpose Controller 6",
93 { _("genpurpose7") }, // "General Purpose Controller 7",
94 { _("genpurpose8") }, // "General Purpose Controller 8",
95 { _("portamentoctrl"), true },
96 { _("undefined"), true },
97 { _("undefined"), true },
98 { _("undefined"), true },
99 {0}, // high resolution velocity prefix (so discouraged to be used here)
100 { _("undefined"), true },
101 { _("undefined"), true },
102 { _("effect1depth") }, // "Effects 1 Depth",
103 { _("effect2depth") }, // "Effects 2 Depth",
104 { _("effect3depth") }, // "Effects 3 Depth",
105 { _("effect4depth") }, // "Effects 4 Depth",
106 { _("effect5depth") }, // "Effects 5 Depth"
107 { _("dataincrement"), true },
108 { _("datadecrement"), true },
109 {0}, // NRPN LSB (so discouraged to be used here)
110 {0}, // NRPN MSB (so discouraged to be used here)
111 {0}, // RPN LSB (so discouraged to be used here)
112 {0}, // RPN MSB (so discouraged to be used here)
113 { _("undefined"), true },
114 { _("undefined"), true },
115 { _("undefined"), true },
116 { _("undefined"), true },
117 { _("undefined"), true },
118 { _("undefined"), true },
119 { _("undefined"), true },
120 { _("undefined"), true },
121 { _("undefined"), true },
122 { _("undefined"), true },
123 { _("undefined"), true },
124 { _("undefined"), true },
125 { _("undefined"), true },
126 { _("undefined"), true },
127 { _("undefined"), true },
128 { _("undefined"), true },
129 { _("undefined"), true },
130 { _("undefined"), true } // CC 119
131 // (all other ones that follow [CC 120- CC 127] are hard coded channel
132 // mode messages, so those are discouraged to be used here)
133 };
134 }
135
136 #define controlChangeTextsSize (sizeof(controlChangeTexts) / sizeof(CCText))
137
138 LabelWidget::LabelWidget(const char* labelText, Gtk::Widget& widget) :
139 label(Glib::ustring(labelText) + ":"),
140 widget(widget)
141 {
142 #if HAS_GTKMM_ALIGNMENT
143 label.set_alignment(Gtk::ALIGN_START);
144 #else
145 label.set_halign(Gtk::Align::START);
146 #endif
147 }
148
149 void LabelWidget::set_sensitive(bool sensitive)
150 {
151 label.set_sensitive(sensitive);
152 widget.set_sensitive(sensitive);
153 }
154
155 ReadOnlyLabelWidget::ReadOnlyLabelWidget(const char* leftHandText)
156 : LabelWidget(leftHandText, text)
157 {
158 #if HAS_GTKMM_ALIGNMENT
159 text.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START);
160 #else
161 label.set_halign(Gtk::Align::START);
162 label.set_valign(Gtk::Align::START);
163 #endif
164 }
165
166 ReadOnlyLabelWidget::ReadOnlyLabelWidget(const char* leftHandText, const char* rightHandText)
167 : LabelWidget(leftHandText, text)
168 {
169 #if HAS_GTKMM_ALIGNMENT
170 text.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START);
171 #else
172 text.set_halign(Gtk::Align::START);
173 text.set_valign(Gtk::Align::START);
174 #endif
175 text.set_text(rightHandText);
176 }
177
178 NumEntry::NumEntry(const char* labelText, double lower, double upper,
179 int decimals) :
180 LabelWidget(labelText, box),
181 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
182 adjust(lower, lower, upper, 1, 10),
183 #else
184 adjust(Gtk::Adjustment::create(lower, lower, upper, 1, 10)),
185 #endif
186 scale(adjust),
187 spinbutton(adjust)
188 {
189 scale.set_size_request(70);
190 spinbutton.set_digits(decimals);
191 spinbutton.set_value(0);
192 spinbutton.set_numeric();
193 scale.set_draw_value(false);
194 box.pack_start(spinbutton, Gtk::PACK_SHRINK);
195 box.add(scale);
196 }
197
198 NumEntryGain::NumEntryGain(const char* labelText,
199 double lower, double upper,
200 int decimals, double coeff) :
201 NumEntry(labelText, lower, upper, decimals),
202 value(0),
203 coeff(coeff),
204 connected(true)
205 {
206 spinbutton.signal_value_changed().connect(
207 sigc::mem_fun(*this, &NumEntryGain::value_changed));
208 }
209
210 void NumEntryGain::value_changed()
211 {
212 if (!connected) return;
213
214 const double f = pow(10, spinbutton.get_digits());
215 int new_value = round_to_int(spinbutton.get_value() * f);
216 if (new_value != round_to_int(value / coeff * f)) {
217 value = round_to_int(new_value / f * coeff);
218 sig_changed();
219 }
220 }
221
222 void NumEntryGain::set_value(int32_t value)
223 {
224 if (value != this->value) {
225 this->value = value;
226
227 connected = false;
228 bool plus6 = value < 0;
229 spinbutton.set_value(plus6 ? 0 : value / coeff);
230 set_sensitive(!plus6);
231 connected = true;
232
233 sig_changed();
234 }
235 }
236
237
238 BoolEntryPlus6::BoolEntryPlus6(const char* labelText, NumEntryGain& eGain, int32_t plus6value) :
239 LabelWidget(labelText, checkbutton),
240 checkbutton(labelText),
241 eGain(eGain),
242 plus6value(plus6value)
243 {
244 checkbutton.signal_toggled().connect(
245 sigc::mem_fun(*this, &BoolEntryPlus6::value_changed));
246 }
247
248 void BoolEntryPlus6::value_changed()
249 {
250 if (checkbutton.get_active()) eGain.set_value(plus6value);
251 else if (eGain.get_value() < 0) eGain.set_value(0);
252 }
253
254 int32_t BoolEntryPlus6::get_value() const
255 {
256 return eGain.get_value();
257 }
258
259 void BoolEntryPlus6::set_value(int32_t value)
260 {
261 checkbutton.set_active(value < 0);
262 }
263
264 NumEntryPermille::NumEntryPermille(const char* labelText,
265 double lower, double upper, int decimals) :
266 NumEntry(labelText, lower, upper, decimals),
267 value(0)
268 {
269 spinbutton.signal_value_changed().connect(
270 sigc::mem_fun(*this, &NumEntryPermille::value_changed));
271 }
272
273 void NumEntryPermille::value_changed()
274 {
275 uint16_t new_value = uint16_t(spinbutton.get_value() * 10 + 0.5);
276 if (new_value != value) {
277 value = uint16_t(spinbutton.get_value() * 10 + 0.5);
278 sig_changed();
279 }
280 }
281
282 void NumEntryPermille::set_value(uint16_t value)
283 {
284 if (value != this->value) {
285 spinbutton.set_value(value / 10.0);
286 }
287 }
288
289
290 NoteEntry::NoteEntry(const char* labelText) :
291 NumEntryTemp<uint8_t>(labelText)
292 {
293 spin_button_show_notes(spinbutton);
294 }
295
296 namespace {
297 const char* notes[] = {
298 _("C"), _("C#"), _("D"), _("D#"), _("E"), _("F"),_("F#"),
299 _("G"), _("G#"), _("A"), _("A#"), _("B")
300 };
301
302 int note_value(const Glib::ustring& note, double* value)
303 {
304 const char* str = note.c_str();
305
306 int i;
307 for (i = 11 ; i >= 0 ; i--) {
308 if (strncasecmp(str, notes[i], strlen(notes[i])) == 0) break;
309 }
310 if (i >= 0) {
311 char* endptr;
312 long x = strtol(str + strlen(notes[i]), &endptr, 10);
313 if (endptr != str + strlen(notes[i])) {
314 *value = std::max(0L, std::min(i + (x + 1) * 12, 127L));
315 return true;
316 }
317 } else {
318 char* endptr;
319 long x = strtol(str, &endptr, 10);
320 if (endptr != str) {
321 *value = std::max(0L, std::min(x, 127L));
322 return true;
323 }
324 }
325
326 #if HAS_GTKMM_CPP11_ENUMS
327 return Gtk::SpinButton::INPUT_ERROR;
328 #else
329 return Gtk::INPUT_ERROR;
330 #endif
331 }
332 }
333
334 int note_value(const Glib::ustring& note)
335 {
336 double value = 0;
337 note_value(note, &value);
338 return value;
339 }
340
341 Glib::ustring note_str(int note)
342 {
343 char buf[10];
344 sprintf(buf, "%s%d", notes[note % 12], note / 12 - 1);
345 return buf;
346 }
347
348 namespace {
349 // Convert the Entry text to a number
350 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
351 int on_input(double& new_value, Gtk::SpinButton* spinbutton) {
352 return note_value(spinbutton->get_text(), &new_value);
353 }
354 #else
355 int on_input(double* new_value, Gtk::SpinButton* spinbutton) {
356 return note_value(spinbutton->get_text(), new_value);
357 }
358 #endif
359
360 // Convert the Adjustment position to text
361 bool on_output(Gtk::SpinButton* spinbutton) {
362 spinbutton->set_text(
363 note_str(spinbutton->get_adjustment()->get_value() + 0.5));
364 return true;
365 }
366 }
367
368 // Make a SpinButton show notes instead of numbers
369 void spin_button_show_notes(Gtk::SpinButton& spin_button)
370 {
371 spin_button.set_numeric(false);
372 spin_button.set_width_chars(4);
373 spin_button.signal_input().connect(
374 sigc::bind(sigc::ptr_fun(&on_input), &spin_button));
375 spin_button.signal_output().connect(
376 sigc::bind(sigc::ptr_fun(&on_output), &spin_button));
377 }
378
379 ChoiceEntryLeverageCtrl::ChoiceEntryLeverageCtrl(const char* labelText) :
380 #if HAS_GTKMM_ALIGNMENT
381 LabelWidget(labelText, align),
382 align(0, 0, 0, 0)
383 #else
384 LabelWidget(labelText, combobox)
385 #endif
386 {
387 for (int i = 0 ; i < controlChangeTextsSize ; i++) {
388 if (controlChangeTexts[i].txt) {
389 const int cc = i - 3;
390 Glib::ustring s = (i < 3)
391 ? controlChangeTexts[i].txt
392 : Glib::ustring::compose("CC%1: %2%3", cc, controlChangeTexts[i].txt, controlChangeTexts[i].isExtension ? " [EXT]" : "");
393 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 24) || GTKMM_MAJOR_VERSION < 2
394 combobox.append_text(s);
395 #else
396 combobox.append(s);
397 #endif
398 }
399 }
400 combobox.signal_changed().connect(
401 sigc::mem_fun(*this, &ChoiceEntryLeverageCtrl::value_changed));
402 #if HAS_GTKMM_ALIGNMENT
403 align.add(combobox);
404 #else
405 combobox.set_halign(Gtk::Align::FILL);
406 combobox.set_valign(Gtk::Align::FILL);
407 #endif
408 value.type = gig::leverage_ctrl_t::type_none;
409 value.controller_number = 0;
410 }
411
412 void ChoiceEntryLeverageCtrl::value_changed()
413 {
414 int rowno = combobox.get_active_row_number();
415 switch (rowno)
416 {
417 case -1:
418 break;
419 case 0:
420 value.type = gig::leverage_ctrl_t::type_none;
421 break;
422 case 1:
423 value.type = gig::leverage_ctrl_t::type_channelaftertouch;
424 break;
425 case 2:
426 value.type = gig::leverage_ctrl_t::type_velocity;
427 break;
428 default:
429 value.type = gig::leverage_ctrl_t::type_controlchange;
430 int x = 3;
431 for (uint cc = 0 ; cc < controlChangeTextsSize - 3 ; cc++) {
432 if (controlChangeTexts[cc + 3].txt) {
433 if (rowno == x) {
434 value.controller_number = cc;
435 if (controlChangeTexts[cc + 3].isExtension &&
436 Settings::singleton()->warnUserOnExtensions)
437 {
438 Glib::ustring txt = _("<b>Format Extension</b>\n\nAll controllers marked with \"<b>[EXT]</b>\" are an extension to the original gig sound format. They will only work with LinuxSampler, but they will <b>not work</b> with Gigasampler/GigaStudio!\n\n(You may disable this warning in the <i>Settings</i> menu.)");
439 Gtk::MessageDialog msg(
440 txt, true, Gtk::MESSAGE_WARNING
441 );
442 msg.run();
443 }
444 break;
445 }
446 x++;
447 }
448 }
449 break;
450 }
451 if (rowno >= 0) sig_changed();
452 }
453
454 void ChoiceEntryLeverageCtrl::set_value(gig::leverage_ctrl_t value)
455 {
456 int comboIndex;
457 switch (value.type)
458 {
459 case gig::leverage_ctrl_t::type_none:
460 comboIndex = 0;
461 break;
462 case gig::leverage_ctrl_t::type_channelaftertouch:
463 comboIndex = 1;
464 break;
465 case gig::leverage_ctrl_t::type_velocity:
466 comboIndex = 2;
467 break;
468 case gig::leverage_ctrl_t::type_controlchange: {
469 comboIndex = -1;
470 int x = 3;
471 for (uint cc = 0 ; cc < controlChangeTextsSize - 3 ; cc++) {
472 if (controlChangeTexts[cc + 3].txt) {
473 if (value.controller_number == cc) {
474 comboIndex = x;
475 break;
476 }
477 x++;
478 }
479 }
480 break;
481 }
482 default:
483 comboIndex = -1;
484 break;
485 }
486 combobox.set_active(comboIndex);
487 }
488
489
490 BoolEntry::BoolEntry(const char* labelText) :
491 LabelWidget(labelText, checkbutton),
492 checkbutton(labelText)
493 {
494 checkbutton.signal_toggled().connect(sig_changed.make_slot());
495 }
496
497
498 StringEntry::StringEntry(const char* labelText) :
499 LabelWidget(labelText, entry)
500 {
501 entry.signal_changed().connect(sig_changed.make_slot());
502 }
503
504 gig::String StringEntry::get_value() const
505 {
506 return gig_from_utf8(entry.get_text());
507 }
508
509 void StringEntry::set_value(const gig::String& value) {
510 entry.set_text(gig_to_utf8(value));
511 }
512
513
514 StringEntryMultiLine::StringEntryMultiLine(const char* labelText) :
515 LabelWidget(labelText, frame)
516 {
517 text_buffer = text_view.get_buffer();
518 frame.set_shadow_type(Gtk::SHADOW_IN);
519 frame.add(text_view);
520 text_buffer->signal_changed().connect(sig_changed.make_slot());
521 }
522
523 gig::String StringEntryMultiLine::get_value() const
524 {
525 Glib::ustring value = text_buffer->get_text();
526 for (int i = 0 ; (i = value.find("\x0a", i)) >= 0 ; i += 2)
527 value.replace(i, 1, "\x0d\x0a");
528 return gig_from_utf8(value);
529 }
530
531 void StringEntryMultiLine::set_value(const gig::String& value)
532 {
533 Glib::ustring text = gig_to_utf8(value);
534 for (int i = 0 ; (i = text.find("\x0d\x0a", i, 2)) >= 0 ; i++)
535 text.replace(i, 2, "\x0a");
536 text_buffer->set_text(text);
537 }
538
539
540 Table::Table(int x, int y) :
541 #if USE_GTKMM_GRID
542 Gtk::Grid(),
543 cols(x),
544 #else
545 Gtk::Table(x, y),
546 #endif
547 rowno(0)
548 {
549 }
550
551 void Table::add(BoolEntry& boolentry)
552 {
553 #if USE_GTKMM_GRID
554 attach(boolentry.widget, 0, rowno, 2);
555 #else
556 attach(boolentry.widget, 0, 2, rowno, rowno + 1,
557 Gtk::FILL, Gtk::SHRINK);
558 #endif
559 rowno++;
560 }
561
562 void Table::add(BoolEntryPlus6& boolentry)
563 {
564 #if USE_GTKMM_GRID
565 attach(boolentry.widget, 0, rowno, 2);
566 #else
567 attach(boolentry.widget, 0, 2, rowno, rowno + 1,
568 Gtk::FILL, Gtk::SHRINK);
569 #endif
570 rowno++;
571 }
572
573 void Table::add(LabelWidget& prop)
574 {
575 #if USE_GTKMM_GRID
576 attach(prop.label, 1, rowno);
577 attach(prop.widget, 2, rowno);
578 #else
579 attach(prop.label, 1, 2, rowno, rowno + 1,
580 Gtk::FILL, Gtk::SHRINK);
581 attach(prop.widget, 2, 3, rowno, rowno + 1,
582 Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK);
583 #endif
584 rowno++;
585 }

  ViewVC Help
Powered by ViewVC