ui->treeWidget->setColumnCount(num_buses + 3);
ui->treeWidget->setHeaderLabels(labels);
- add_controls("Per-bus controllers", ControlType::CONTROLLER, mapping_proto, per_bus_controllers);
- add_controls("Per-bus buttons", ControlType::BUTTON, mapping_proto, per_bus_buttons);
- add_controls("Global controllers", ControlType::CONTROLLER, mapping_proto, global_controllers);
- add_controls("Global buttons", ControlType::BUTTON, mapping_proto, global_buttons);
+ add_controls("Per-bus controllers", ControlType::CONTROLLER, SpinnerGroup::PER_BUS_CONTROLLERS, mapping_proto, per_bus_controllers);
+ add_controls("Per-bus buttons", ControlType::BUTTON, SpinnerGroup::PER_BUS_BUTTONS, mapping_proto, per_bus_buttons);
+ add_controls("Global controllers", ControlType::CONTROLLER, SpinnerGroup::GLOBAL_CONTROLLERS, mapping_proto, global_controllers);
+ add_controls("Global buttons", ControlType::BUTTON, SpinnerGroup::GLOBAL_BUTTONS, mapping_proto, global_buttons);
fill_controls_from_mapping(mapping_proto);
// Auto-resize every column but the last.
ui->treeWidget->resizeColumnToContents(column_idx);
}
- connect(ui->guess_button, &QPushButton::clicked, this, &MIDIMappingDialog::guess_clicked);
+ connect(ui->guess_bus_button, &QPushButton::clicked,
+ bind(&MIDIMappingDialog::guess_clicked, this, false));
+ connect(ui->guess_group_button, &QPushButton::clicked,
+ bind(&MIDIMappingDialog::guess_clicked, this, true));
connect(ui->ok_cancel_buttons, &QDialogButtonBox::accepted, this, &MIDIMappingDialog::ok_clicked);
connect(ui->ok_cancel_buttons, &QDialogButtonBox::rejected, this, &MIDIMappingDialog::cancel_clicked);
connect(ui->save_button, &QPushButton::clicked, this, &MIDIMappingDialog::save_clicked);
{
if (event->type() == QEvent::FocusIn ||
event->type() == QEvent::FocusOut) {
- // We ignore the guess button itself; it should be allowed
- // to navigate from a spinner to focus on the button (to click it).
- if (obj != ui->guess_button) {
+ // We ignore the guess buttons themselves; it should be allowed
+ // to navigate from a spinner to focus on a button (to click it).
+ if (obj != ui->guess_bus_button && obj != ui->guess_group_button) {
update_guess_button_state();
}
}
return false;
}
-void MIDIMappingDialog::guess_clicked()
+void MIDIMappingDialog::guess_clicked(bool limit_to_group)
{
- int focus_bus_idx = find_focus_bus();
- if (focus_bus_idx == -1) {
+ FocusInfo focus = find_focus();
+ if (focus.bus_idx == -1) {
// The guess button probably took the focus away from us.
- focus_bus_idx = last_focus_bus_idx;
+ focus = last_focus;
}
- assert(focus_bus_idx != -1); // The button should have been disabled.
- pair<int, int> bus_and_offset = guess_offset(focus_bus_idx);
+ assert(focus.bus_idx != -1); // The button should have been disabled.
+ pair<int, int> bus_and_offset = guess_offset(focus.bus_idx, limit_to_group ? focus.spinner_group : SpinnerGroup::ALL_GROUPS);
const int source_bus_idx = bus_and_offset.first;
const int offset = bus_and_offset.second;
assert(source_bus_idx != -1); // The button should have been disabled.
- for (const auto &field_number_and_spinner : spinners[focus_bus_idx]) {
+ for (const auto &field_number_and_spinner : spinners[focus.bus_idx]) {
int field_number = field_number_and_spinner.first;
- QSpinBox *spinner = field_number_and_spinner.second;
+ QSpinBox *spinner = field_number_and_spinner.second.spinner;
+ SpinnerGroup this_spinner_group = field_number_and_spinner.second.group;
+
+ if (limit_to_group && this_spinner_group != focus.spinner_group) {
+ continue;
+ }
assert(spinners[source_bus_idx].count(field_number));
- QSpinBox *source_spinner = spinners[source_bus_idx][field_number];
+ QSpinBox *source_spinner = spinners[source_bus_idx][field_number].spinner;
+ assert(spinners[source_bus_idx][field_number].group == this_spinner_group);
if (source_spinner->value() != 0) {
spinner->setValue(source_spinner->value() + offset);
void MIDIMappingDialog::add_controls(const string &heading,
MIDIMappingDialog::ControlType control_type,
+ MIDIMappingDialog::SpinnerGroup spinner_group,
const MIDIMappingProto &mapping_proto,
const vector<MIDIMappingDialog::Control> &controls)
{
ui->treeWidget->setItemWidget(item, bus_idx + 2, spinner);
if (control_type == ControlType::CONTROLLER) {
- controller_spinners.push_back(InstantiatedSpinner{ spinner, bus_idx, control.field_number });
+ controller_spinners.push_back(InstantiatedSpinner{ spinner, bus_idx, spinner_group, control.field_number });
} else {
assert(control_type == ControlType::BUTTON);
- button_spinners.push_back(InstantiatedSpinner{ spinner, bus_idx, control.field_number });
+ button_spinners.push_back(InstantiatedSpinner{ spinner, bus_idx, spinner_group, control.field_number });
}
- spinners[bus_idx][control.field_number] = spinner;
+ spinners[bus_idx][control.field_number] = SpinnerAndGroup{ spinner, spinner_group };
connect(spinner, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
bind(&MIDIMappingDialog::update_guess_button_state, this));
}
}
}
-pair<int, int> MIDIMappingDialog::guess_offset(unsigned bus_idx)
+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)) {
+ 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)) {
+ 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)) {
+ } 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;
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;
+ QSpinBox *spinner = field_number_and_spinner.second.spinner;
+ SpinnerGroup this_spinner_group = field_number_and_spinner.second.group;
if (spinner->value() == 0) {
continue;
}
+ if (spinner_group != SpinnerGroup::ALL_GROUPS &&
+ spinner_group != this_spinner_group) {
+ continue;
+ }
assert(spinners[source_bus_idx].count(field_number));
- QSpinBox *source_spinner = spinners[source_bus_idx][field_number];
+ QSpinBox *source_spinner = spinners[source_bus_idx][field_number].spinner;
+ assert(spinners[source_bus_idx][field_number].group == this_spinner_group);
if (source_spinner->value() == 0) {
// The bus has a controller set that the source bus doesn't set.
return not_found;
return make_pair(source_bus_idx, offset);
}
-bool MIDIMappingDialog::bus_is_empty(unsigned bus_idx)
+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;
+ 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() != 0) {
return false;
}
void MIDIMappingDialog::update_guess_button_state()
{
- int focus_bus_idx = find_focus_bus();
- if (focus_bus_idx < 0) {
+ FocusInfo focus = find_focus();
+ 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;
+ {
+ 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;
}
-int MIDIMappingDialog::find_focus_bus()
+MIDIMappingDialog::FocusInfo MIDIMappingDialog::find_focus() const
{
for (const InstantiatedSpinner &is : controller_spinners) {
if (is.spinner->hasFocus()) {
- return is.bus_idx;
+ return FocusInfo{ int(is.bus_idx), is.spinner_group };
}
}
for (const InstantiatedSpinner &is : button_spinners) {
if (is.spinner->hasFocus()) {
- return is.bus_idx;
+ return FocusInfo{ int(is.bus_idx), is.spinner_group };
}
}
- return -1;
+ return FocusInfo{ -1, SpinnerGroup::ALL_GROUPS };
}
void note_on(unsigned note) override;
public slots:
- void guess_clicked();
+ void guess_clicked(bool limit_to_group);
void ok_clicked();
void cancel_clicked();
void save_clicked();
private:
static constexpr unsigned num_buses = 8;
+ // Each spinner belongs to exactly one group, corresponding to the
+ // subheadings in the UI. This is so that we can extrapolate data
+ // across only single groups if need be.
+ enum class SpinnerGroup {
+ ALL_GROUPS = -1,
+ PER_BUS_CONTROLLERS,
+ PER_BUS_BUTTONS,
+ GLOBAL_CONTROLLERS,
+ GLOBAL_BUTTONS
+ };
+
void add_bank_selector(QTreeWidgetItem *item, const MIDIMappingProto &mapping_proto, int bank_field_number);
enum class ControlType { CONTROLLER, BUTTON };
void add_controls(const std::string &heading, ControlType control_type,
+ SpinnerGroup spinner_group,
const MIDIMappingProto &mapping_proto, const std::vector<Control> &controls);
void fill_controls_from_mapping(const MIDIMappingProto &mapping_proto);
// Tries to find a source bus and an offset to it that would give
// a consistent offset for the rest of the mappings in this bus.
- // Returns -1 for the bus (the first element) if no consistent offset
- // can be found.
- std::pair<int, int> guess_offset(unsigned bus_idx);
- bool bus_is_empty(unsigned bus_idx);
+ // Returns -1 for the bus if no consistent offset can be found.
+ std::pair<int, int> guess_offset(unsigned bus_idx, SpinnerGroup spinner_group);
+ bool bus_is_empty(unsigned bus_idx, SpinnerGroup spinner_group);
void update_guess_button_state();
- int find_focus_bus();
+ struct FocusInfo {
+ int bus_idx; // -1 for none.
+ SpinnerGroup spinner_group;
+ };
+ FocusInfo find_focus() const;
std::unique_ptr<MIDIMappingProto> construct_mapping_proto_from_ui();
Ui::MIDIMappingDialog *ui;
MIDIMapper *mapper;
ControllerReceiver *old_receiver;
- int last_focus_bus_idx{-1};
+ FocusInfo last_focus{-1, SpinnerGroup::ALL_GROUPS};
// All controllers actually laid out on the grid (we need to store them
// so that we can move values back and forth between the controls and
struct InstantiatedSpinner {
QSpinBox *spinner;
unsigned bus_idx;
+ SpinnerGroup spinner_group;
int field_number; // In MIDIMappingBusProto.
};
struct InstantiatedComboBox {
std::vector<InstantiatedComboBox> bank_combo_boxes;
// Keyed on bus index, then field number.
- std::map<unsigned, std::map<unsigned, QSpinBox *>> spinners;
+ struct SpinnerAndGroup {
+ QSpinBox *spinner;
+ SpinnerGroup group;
+ };
+ std::map<unsigned, std::map<unsigned, SpinnerAndGroup>> spinners;
};
#endif // !defined(_MIDI_MAPPING_DIALOG_H)