+
+pair<int, int> MIDIMappingDialog::guess_offset(unsigned bus_idx)
+{
+ constexpr pair<int, int> not_found(-1, 0);
+
+ if (bus_is_empty(bus_idx)) {
+ 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)) {
+ source_bus_idx = bus_idx - 1;
+ } else if (bus_idx < num_buses - 1 && !bus_is_empty(bus_idx + 1)) {
+ 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;
+ 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;
+
+ if (spinner->value() == 0) {
+ continue;
+ }
+
+ assert(spinners[source_bus_idx].count(field_number));
+ QSpinBox *source_spinner = spinners[source_bus_idx][field_number];
+ if (source_spinner->value() == 0) {
+ // 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;
+ }
+ return make_pair(source_bus_idx, offset);
+}
+
+bool MIDIMappingDialog::bus_is_empty(unsigned bus_idx)
+{
+ for (const auto &field_number_and_spinner : spinners[bus_idx]) {
+ QSpinBox *spinner = field_number_and_spinner.second;
+ if (spinner->value() != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void MIDIMappingDialog::update_guess_button_state()
+{
+ int focus_bus_idx = find_focus_bus();
+ if (focus_bus_idx < 0) {
+ return;
+ }
+ pair<int, int> bus_and_offset = guess_offset(focus_bus_idx);
+ ui->guess_button->setEnabled(bus_and_offset.first != -1);
+ last_focus_bus_idx = focus_bus_idx;
+}
+
+int MIDIMappingDialog::find_focus_bus()
+{
+ for (const InstantiatedSpinner &is : controller_spinners) {
+ if (is.spinner->hasFocus()) {
+ return is.bus_idx;
+ }
+ }
+ for (const InstantiatedSpinner &is : button_spinners) {
+ if (is.spinner->hasFocus()) {
+ return is.bus_idx;
+ }
+ }
+ return -1;
+}