--- gigedit/trunk/src/gigedit/paramedit.cpp 2009/02/03 19:38:19 1831 +++ gigedit/trunk/src/gigedit/paramedit.cpp 2017/05/05 18:44:59 3151 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andreas Persson + * Copyright (C) 2006-2015 Andreas Persson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -17,55 +17,129 @@ * 02110-1301 USA. */ -#include - #include "paramedit.h" #include "global.h" +#include "compat.h" +#include "Settings.h" + +#include namespace { - const char* const controlChangeTexts[] = { - _("none"), _("channelaftertouch"), _("velocity"), - 0, - _("modwheel"), // "Modulation Wheel or Lever", - _("breath"), // "Breath Controller", - 0, - _("foot"), // "Foot Controller", - _("portamentotime"), // "Portamento Time", - 0, 0, 0, 0, 0, 0, - _("effect1"), // "Effect Control 1", - _("effect2"), // "Effect Control 2", - 0, 0, - _("genpurpose1"), // "General Purpose Controller 1", - _("genpurpose2"), // "General Purpose Controller 2", - _("genpurpose3"), // "General Purpose Controller 3", - _("genpurpose4"), // "General Purpose Controller 4", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - _("sustainpedal"), // "Damper Pedal on/off (Sustain)", - _("portamento"), // "Portamento On/Off", - _("sostenuto"), // "Sustenuto On/Off", - _("softpedal"), // "Soft Pedal On/Off", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - _("genpurpose5"), // "General Purpose Controller 5", - _("genpurpose6"), // "General Purpose Controller 6", - _("genpurpose7"), // "General Purpose Controller 7", - _("genpurpose8"), // "General Purpose Controller 8", - 0, 0, 0, 0, 0, 0, 0, - _("effect1depth"), // "Effects 1 Depth", - _("effect2depth"), // "Effects 2 Depth", - _("effect3depth"), // "Effects 3 Depth", - _("effect4depth"), // "Effects 4 Depth", - _("effect5depth"), // "Effects 5 Depth" + struct CCText { + const char* const txt; + bool isExtension; ///< True if this is a controller only supported by LinuxSampler, but not supperted by Gigasampler/GigaStudio. + }; + static const CCText controlChangeTexts[] = { + // 3 special ones (not being CCs) + { _("none") }, { _("channelaftertouch") }, { _("velocity") }, + {0}, // bank select MSB (hard coded in sampler, so discouraged to be used here, even though considerable) + { _("modwheel") }, // "Modulation Wheel or Lever", + { _("breath") }, // "Breath Controller", + { _("undefined"), true }, + { _("foot") }, // "Foot Controller", + { _("portamentotime") }, // "Portamento Time", + { _("data entry MSB"), true }, + { _("volume"), true }, + { _("balance"), true }, + { _("undefined"), true }, + { _("pan"), true }, + { _("expression"), true }, + { _("effect1") }, // "Effect Control 1", + { _("effect2") }, // "Effect Control 2", + { _("undefined"), true }, + { _("undefined"), true }, + { _("genpurpose1") }, // "General Purpose Controller 1", + { _("genpurpose2") }, // "General Purpose Controller 2", + { _("genpurpose3") }, // "General Purpose Controller 3", + { _("genpurpose4") }, // "General Purpose Controller 4", + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + + // LSB variant of the various controllers above + // (so discouraged to be used here for now) + {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, + {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, + {0}, {0}, {0}, {0}, + + { _("sustainpedal") }, // "Damper Pedal on/off (Sustain)", + { _("portamento") }, // "Portamento On/Off", + { _("sostenuto") }, // "Sustenuto On/Off", + { _("softpedal") }, // "Soft Pedal On/Off", + { _("legato"), true }, + { _("hold2"), true }, + { _("soundvariation"), true }, + { _("timbre"), true }, + { _("releasetime"), true }, + { _("attacktime"), true }, + { _("brightness"), true }, + { _("decaytime"), true }, + { _("vibratorate"), true }, + { _("vibratodepth"), true }, + { _("vibratodelay"), true }, + { _("undefined"), true }, + { _("genpurpose5") }, // "General Purpose Controller 5", + { _("genpurpose6") }, // "General Purpose Controller 6", + { _("genpurpose7") }, // "General Purpose Controller 7", + { _("genpurpose8") }, // "General Purpose Controller 8", + { _("portamentoctrl"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + {0}, // high resolution velocity prefix (so discouraged to be used here) + { _("undefined"), true }, + { _("undefined"), true }, + { _("effect1depth") }, // "Effects 1 Depth", + { _("effect2depth") }, // "Effects 2 Depth", + { _("effect3depth") }, // "Effects 3 Depth", + { _("effect4depth") }, // "Effects 4 Depth", + { _("effect5depth") }, // "Effects 5 Depth" + { _("dataincrement"), true }, + { _("datadecrement"), true }, + {0}, // NRPN LSB (so discouraged to be used here) + {0}, // NRPN MSB (so discouraged to be used here) + {0}, // RPN LSB (so discouraged to be used here) + {0}, // RPN MSB (so discouraged to be used here) + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true }, + { _("undefined"), true } // CC 119 + // (all other ones that follow [CC 120- CC 127] are hard coded channel + // mode messages, so those are discouraged to be used here) }; } +#define controlChangeTextsSize (sizeof(controlChangeTexts) / sizeof(CCText)) + LabelWidget::LabelWidget(const char* labelText, Gtk::Widget& widget) : label(Glib::ustring(labelText) + ":"), widget(widget) { - label.set_alignment(Gtk::ALIGN_LEFT); + label.set_alignment(Gtk::ALIGN_START); } void LabelWidget::set_sensitive(bool sensitive) @@ -74,15 +148,34 @@ widget.set_sensitive(sensitive); } +ReadOnlyLabelWidget::ReadOnlyLabelWidget(const char* leftHandText) + : LabelWidget(leftHandText, text) +{ + text.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START); +} + +ReadOnlyLabelWidget::ReadOnlyLabelWidget(const char* leftHandText, const char* rightHandText) + : LabelWidget(leftHandText, text) +{ + text.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START); + text.set_text(rightHandText); +} + NumEntry::NumEntry(const char* labelText, double lower, double upper, int decimals) : + LabelWidget(labelText, box), +#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 adjust(lower, lower, upper, 1, 10), +#else + adjust(Gtk::Adjustment::create(lower, lower, upper, 1, 10)), +#endif scale(adjust), - spinbutton(adjust), - LabelWidget(labelText, box) + spinbutton(adjust) { + scale.set_size_request(70); spinbutton.set_digits(decimals); spinbutton.set_value(0); + spinbutton.set_numeric(); scale.set_draw_value(false); box.pack_start(spinbutton, Gtk::PACK_SHRINK); box.add(scale); @@ -92,8 +185,8 @@ double lower, double upper, int decimals, double coeff) : NumEntry(labelText, lower, upper, decimals), - coeff(coeff), value(0), + coeff(coeff), connected(true) { spinbutton.signal_value_changed().connect( @@ -183,55 +276,96 @@ NoteEntry::NoteEntry(const char* labelText) : NumEntryTemp(labelText) { - spinbutton.signal_input().connect( - sigc::mem_fun(*this, &NoteEntry::on_input)); - spinbutton.signal_output().connect( - sigc::mem_fun(*this, &NoteEntry::on_output)); + spin_button_show_notes(spinbutton); } -const char* notes[] = { - _("C"), _("C#"), _("D"), _("D#"), _("E"), _("F"),_("F#"), - _("G"), _("G#"), _("A"), _("A#"), _("B") -}; +namespace { + const char* notes[] = { + _("C"), _("C#"), _("D"), _("D#"), _("E"), _("F"),_("F#"), + _("G"), _("G#"), _("A"), _("A#"), _("B") + }; + int note_value(const Glib::ustring& note, double* value) + { + const char* str = note.c_str(); -// Convert the Entry text to a number -int NoteEntry::on_input(double* new_value) + int i; + for (i = 11 ; i >= 0 ; i--) { + if (strncasecmp(str, notes[i], strlen(notes[i])) == 0) break; + } + if (i >= 0) { + char* endptr; + long x = strtol(str + strlen(notes[i]), &endptr, 10); + if (endptr != str + strlen(notes[i])) { + *value = std::max(0L, std::min(i + (x + 1) * 12, 127L)); + return true; + } + } else { + char* endptr; + long x = strtol(str, &endptr, 10); + if (endptr != str) { + *value = std::max(0L, std::min(x, 127L)); + return true; + } + } + return Gtk::INPUT_ERROR; + } +} + +int note_value(const Glib::ustring& note) +{ + double value = 0; + note_value(note, &value); + return value; +} + +Glib::ustring note_str(int note) { - const char* str = spinbutton.get_text().c_str(); + char buf[10]; + sprintf(buf, "%s%d", notes[note % 12], note / 12 - 1); + return buf; +} - int i; - for (i = 11 ; i >= 0 ; i--) { - if (strncmp(str, notes[i], strlen(notes[i])) == 0) break; +namespace { + // Convert the Entry text to a number + int on_input(double* new_value, Gtk::SpinButton* spinbutton) { + return note_value(spinbutton->get_text(), new_value); } - if (i >= 0) { - char* endptr; - long x = strtol(str + strlen(notes[i]), &endptr, 10); - if (endptr != str + strlen(notes[i])) { - *new_value = i + (x + 1) * 12; - return true; - } + + // Convert the Adjustment position to text + bool on_output(Gtk::SpinButton* spinbutton) { + spinbutton->set_text( + note_str(spinbutton->get_adjustment()->get_value() + 0.5)); + return true; } - return Gtk::INPUT_ERROR; } -// Convert the Adjustment position to text -bool NoteEntry::on_output() +// Make a SpinButton show notes instead of numbers +void spin_button_show_notes(Gtk::SpinButton& spin_button) { - int x = int(spinbutton.get_adjustment()->get_value() + 0.5); - char buf[10]; - sprintf(buf, "%s%d", notes[x % 12], x / 12 - 1); - spinbutton.set_text(buf); - return true; + spin_button.set_numeric(false); + spin_button.set_width_chars(4); + spin_button.signal_input().connect( + sigc::bind(sigc::ptr_fun(&on_input), &spin_button)); + spin_button.signal_output().connect( + sigc::bind(sigc::ptr_fun(&on_output), &spin_button)); } ChoiceEntryLeverageCtrl::ChoiceEntryLeverageCtrl(const char* labelText) : - align(0, 0, 0, 0), - LabelWidget(labelText, align) + LabelWidget(labelText, align), + align(0, 0, 0, 0) { - for (int i = 0 ; i < 99 ; i++) { - if (controlChangeTexts[i]) { - combobox.append_text(controlChangeTexts[i]); + for (int i = 0 ; i < controlChangeTextsSize ; i++) { + if (controlChangeTexts[i].txt) { + const int cc = i - 3; + Glib::ustring s = (i < 3) + ? controlChangeTexts[i].txt + : Glib::ustring::compose("CC%1: %2%3", cc, controlChangeTexts[i].txt, controlChangeTexts[i].isExtension ? " [EXT]" : ""); +#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 24) || GTKMM_MAJOR_VERSION < 2 + combobox.append_text(s); +#else + combobox.append(s); +#endif } } combobox.signal_changed().connect( @@ -260,10 +394,19 @@ default: value.type = gig::leverage_ctrl_t::type_controlchange; int x = 3; - for (int cc = 0 ; cc < 96 ; cc++) { - if (controlChangeTexts[cc + 3]) { + for (uint cc = 0 ; cc < controlChangeTextsSize - 3 ; cc++) { + if (controlChangeTexts[cc + 3].txt) { if (rowno == x) { value.controller_number = cc; + if (controlChangeTexts[cc + 3].isExtension && + Settings::singleton()->warnUserOnExtensions) + { + Glib::ustring txt = _("Format Extension\n\nAll controllers marked with \"[EXT]\" are an extension to the original gig sound format. They will only work with LinuxSampler, but they will not work with Gigasampler/GigaStudio!\n\n(You may disable this warning in the Settings menu.)"); + Gtk::MessageDialog msg( + txt, true, Gtk::MESSAGE_WARNING + ); + msg.run(); + } break; } x++; @@ -276,35 +419,37 @@ void ChoiceEntryLeverageCtrl::set_value(gig::leverage_ctrl_t value) { - int x; + int comboIndex; switch (value.type) { case gig::leverage_ctrl_t::type_none: - x = 0; + comboIndex = 0; break; case gig::leverage_ctrl_t::type_channelaftertouch: - x = 1; + comboIndex = 1; break; case gig::leverage_ctrl_t::type_velocity: - x = 2; + comboIndex = 2; break; - case gig::leverage_ctrl_t::type_controlchange: - x = -1; - for (int cc = 0 ; cc < 96 ; cc++) { - if (controlChangeTexts[cc + 3]) { - x++; + case gig::leverage_ctrl_t::type_controlchange: { + comboIndex = -1; + int x = 3; + for (uint cc = 0 ; cc < controlChangeTextsSize - 3 ; cc++) { + if (controlChangeTexts[cc + 3].txt) { if (value.controller_number == cc) { - x += 3; + comboIndex = x; break; } + x++; } } break; + } default: - x = -1; + comboIndex = -1; break; } - combobox.set_active(x); + combobox.set_active(comboIndex); } @@ -322,6 +467,16 @@ entry.signal_changed().connect(sig_changed.make_slot()); } +gig::String StringEntry::get_value() const +{ + return gig_from_utf8(entry.get_text()); +} + +void StringEntry::set_value(const gig::String& value) { + entry.set_text(gig_to_utf8(value)); +} + + StringEntryMultiLine::StringEntryMultiLine(const char* labelText) : LabelWidget(labelText, frame) { @@ -336,12 +491,39 @@ Glib::ustring value = text_buffer->get_text(); for (int i = 0 ; (i = value.find("\x0a", i)) >= 0 ; i += 2) value.replace(i, 1, "\x0d\x0a"); - return value; + return gig_from_utf8(value); +} + +void StringEntryMultiLine::set_value(const gig::String& value) +{ + Glib::ustring text = gig_to_utf8(value); + for (int i = 0 ; (i = text.find("\x0d\x0a", i, 2)) >= 0 ; i++) + text.replace(i, 2, "\x0a"); + text_buffer->set_text(text); +} + + +Table::Table(int x, int y) : Gtk::Table(x, y), rowno(0) { } + +void Table::add(BoolEntry& boolentry) +{ + attach(boolentry.widget, 0, 2, rowno, rowno + 1, + Gtk::FILL, Gtk::SHRINK); + rowno++; +} + +void Table::add(BoolEntryPlus6& boolentry) +{ + attach(boolentry.widget, 0, 2, rowno, rowno + 1, + Gtk::FILL, Gtk::SHRINK); + rowno++; } -void StringEntryMultiLine::set_value(gig::String value) +void Table::add(LabelWidget& prop) { - for (int i = 0 ; (i = value.find("\x0d\x0a", i, 2)) >= 0 ; i++) - value.replace(i, 2, "\x0a"); - text_buffer->set_text(value); + attach(prop.label, 1, 2, rowno, rowno + 1, + Gtk::FILL, Gtk::SHRINK); + attach(prop.widget, 2, 3, rowno, rowno + 1, + Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK); + rowno++; }