/[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 3411 - (show annotations) (download)
Tue Jan 23 16:43:11 2018 UTC (3 years, 3 months ago) by schoenebeck
File size: 19998 byte(s)
- Fixed compilation error.

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

  ViewVC Help
Powered by ViewVC