/[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 3409 - (show annotations) (download)
Tue Jan 23 16:30:56 2018 UTC (6 years, 3 months ago) by schoenebeck
File size: 19925 byte(s)
* Added new main menu item "View" -> "Tooltips for Beginners" which allows
  to disable tooltips intended for newbies only (default: on).
* Bumped version (1.1.0.svn3).

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

  ViewVC Help
Powered by ViewVC