+
+void MIDIMappingDialog::fill_controls_from_mapping(const MIDIMappingProto &mapping_proto)
+{
+ for (const InstantiatedSpinner &is : controller_spinners) {
+ is.spinner->setValue(get_controller_mapping(mapping_proto, is.bus_idx, is.field_number, -1));
+ }
+ for (const InstantiatedSpinner &is : button_spinners) {
+ is.spinner->setValue(get_button_mapping(mapping_proto, is.bus_idx, is.field_number, -1));
+ }
+ for (const InstantiatedSpinner &is : light_spinners) {
+ is.spinner->setValue(get_light_mapping(mapping_proto, is.bus_idx, is.field_number, -1));
+ }
+ for (const InstantiatedComboBox &ic : bank_combo_boxes) {
+ ic.combo_box->setCurrentIndex(get_bank(mapping_proto, ic.field_number, -1) + 1);
+ }
+}
+
+void MIDIMappingDialog::controller_changed(unsigned controller)
+{
+ post_to_main_thread([=]{
+ for (const InstantiatedSpinner &is : controller_spinners) {
+ if (is.spinner->hasFocus()) {
+ is.spinner->setValue(controller);
+ is.spinner->selectAll();
+ }
+ }
+ });
+}
+
+void MIDIMappingDialog::note_on(unsigned note)
+{
+ post_to_main_thread([=]{
+ for (const InstantiatedSpinner &is : button_spinners) {
+ if (is.spinner->hasFocus()) {
+ is.spinner->setValue(note);
+ is.spinner->selectAll();
+ }
+ }
+ for (const InstantiatedSpinner &is : light_spinners) {
+ if (is.spinner->hasFocus()) {
+ is.spinner->setValue(note);
+ is.spinner->selectAll();
+ }
+ }
+ });
+}
+
+pair<int, int> MIDIMappingDialog::guess_offset(unsigned bus_idx, MIDIMappingDialog::SpinnerGroup spinner_group)
+{
+ constexpr pair<int, int> not_found(-1, 0);
+
+ if (bus_is_empty(bus_idx, spinner_group)) {
+ return not_found;
+ }
+
+ // See if we can find a non-empty bus to source from (prefer from the left).
+ unsigned source_bus_idx;
+ if (bus_idx > 0 && !bus_is_empty(bus_idx - 1, spinner_group)) {
+ source_bus_idx = bus_idx - 1;
+ } else if (bus_idx < num_buses - 1 && !bus_is_empty(bus_idx + 1, spinner_group)) {
+ source_bus_idx = bus_idx + 1;
+ } else {
+ return not_found;
+ }
+
+ // See if we can find a consistent offset.
+ bool found_offset = false;
+ int offset = 0;
+ int minimum_allowed_offset = numeric_limits<int>::min();
+ int maximum_allowed_offset = numeric_limits<int>::max();
+ for (const auto &field_number_and_spinner : spinners[bus_idx]) {
+ int field_number = field_number_and_spinner.first;
+ QSpinBox *spinner = field_number_and_spinner.second.spinner;
+ SpinnerGroup this_spinner_group = field_number_and_spinner.second.group;
+ assert(spinners[source_bus_idx].count(field_number));
+ QSpinBox *source_spinner = spinners[source_bus_idx][field_number].spinner;
+ assert(spinners[source_bus_idx][field_number].group == this_spinner_group);
+
+ if (spinner_group != SpinnerGroup::ALL_GROUPS &&
+ spinner_group != this_spinner_group) {
+ continue;
+ }
+ if (spinner->value() == -1) {
+ if (source_spinner->value() != -1) {
+ // If the source value is e.g. 3, offset can't be less than -2 or larger than 124.
+ // Otherwise, we'd extrapolate values outside [1..127].
+ minimum_allowed_offset = max(minimum_allowed_offset, 1 - source_spinner->value());
+ maximum_allowed_offset = min(maximum_allowed_offset, 127 - source_spinner->value());
+ }
+ continue;
+ }
+ if (source_spinner->value() == -1) {
+ // The bus has a controller set that the source bus doesn't set.
+ return not_found;
+ }
+
+ int candidate_offset = spinner->value() - source_spinner->value();
+ if (!found_offset) {
+ offset = candidate_offset;
+ found_offset = true;
+ } else if (candidate_offset != offset) {
+ return not_found;
+ }
+ }
+
+ if (!found_offset) {
+ // Given that the bus wasn't empty, this shouldn't happen.
+ assert(false);
+ return not_found;
+ }
+
+ if (offset < minimum_allowed_offset || offset > maximum_allowed_offset) {
+ return not_found;
+ }
+ return make_pair(source_bus_idx, offset);
+}
+
+bool MIDIMappingDialog::bus_is_empty(unsigned bus_idx, SpinnerGroup spinner_group)
+{
+ for (const auto &field_number_and_spinner : spinners[bus_idx]) {
+ QSpinBox *spinner = field_number_and_spinner.second.spinner;
+ SpinnerGroup this_spinner_group = field_number_and_spinner.second.group;
+ if (spinner_group != SpinnerGroup::ALL_GROUPS &&
+ spinner_group != this_spinner_group) {
+ continue;
+ }
+ if (spinner->value() != -1) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void MIDIMappingDialog::update_guess_button_state()
+{
+ FocusInfo focus = find_focus();
+ if (focus.bus_idx < 0) {
+ return;
+ }
+ {
+ pair<int, int> bus_and_offset = guess_offset(focus.bus_idx, SpinnerGroup::ALL_GROUPS);
+ ui->guess_bus_button->setEnabled(bus_and_offset.first != -1);
+ }
+ {
+ pair<int, int> bus_and_offset = guess_offset(focus.bus_idx, focus.spinner_group);
+ ui->guess_group_button->setEnabled(bus_and_offset.first != -1);
+ }
+ last_focus = focus;
+}
+
+MIDIMappingDialog::FocusInfo MIDIMappingDialog::find_focus() const
+{
+ for (const InstantiatedSpinner &is : controller_spinners) {
+ if (is.spinner->hasFocus()) {
+ return FocusInfo{ int(is.bus_idx), is.spinner_group, is.field_number };
+ }
+ }
+ for (const InstantiatedSpinner &is : button_spinners) {
+ if (is.spinner->hasFocus()) {
+ return FocusInfo{ int(is.bus_idx), is.spinner_group, is.field_number };
+ }
+ }
+ for (const InstantiatedSpinner &is : light_spinners) {
+ if (is.spinner->hasFocus()) {
+ return FocusInfo{ int(is.bus_idx), is.spinner_group, is.field_number };
+ }
+ }
+ return FocusInfo{ -1, SpinnerGroup::ALL_GROUPS, -1 };
+}