From cf729d2ddd503b90413987bf94a928ae31efe0d0 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 16 Oct 2016 14:32:37 +0200 Subject: [PATCH] Make the save and load buttons in the MIDI mapping edit dialog work. --- midi_mapper.cpp | 20 +++++++++++++++++ midi_mapper.h | 1 + midi_mapping_dialog.cpp | 49 ++++++++++++++++++++++++++++++++++++++--- midi_mapping_dialog.h | 6 ++++- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/midi_mapper.cpp b/midi_mapper.cpp index 27e843e..392fac4 100644 --- a/midi_mapper.cpp +++ b/midi_mapper.cpp @@ -64,6 +64,26 @@ bool load_midi_mapping_from_file(const string &filename, MIDIMappingProto *new_m return true; } +bool save_midi_mapping_to_file(const MIDIMappingProto &mapping_proto, const string &filename) +{ + // Save to disk. We use the text format because it's friendlier + // for a user to look at and edit. + int fd = open(filename.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666); + if (fd == -1) { + perror(filename.c_str()); + return false; + } + io::FileOutputStream output(fd); // Takes ownership of fd. + if (!TextFormat::Print(mapping_proto, &output)) { + // TODO: Don't overwrite the old file (if any) on error. + output.Close(); + return false; + } + + output.Close(); + return true; +} + void MIDIMapper::set_midi_mapping(const MIDIMappingProto &new_mapping) { lock_guard lock(mapping_mu); diff --git a/midi_mapper.h b/midi_mapper.h index 84baf1f..357666d 100644 --- a/midi_mapper.h +++ b/midi_mapper.h @@ -70,5 +70,6 @@ private: }; bool load_midi_mapping_from_file(const std::string &filename, MIDIMappingProto *new_mapping); +bool save_midi_mapping_to_file(const MIDIMappingProto &mapping_proto, const std::string &filename); #endif // !defined(_MIDI_MAPPER_H) diff --git a/midi_mapping_dialog.cpp b/midi_mapping_dialog.cpp index cd622d7..2f57797 100644 --- a/midi_mapping_dialog.cpp +++ b/midi_mapping_dialog.cpp @@ -123,6 +123,7 @@ MIDIMappingDialog::MIDIMappingDialog(MIDIMapper *mapper) 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); + fill_controls_from_mapping(mapping_proto); // Auto-resize every column but the last. for (unsigned column_idx = 0; column_idx < num_buses + 3; ++column_idx) { @@ -131,6 +132,8 @@ MIDIMappingDialog::MIDIMappingDialog(MIDIMapper *mapper) 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); + connect(ui->load_button, &QPushButton::clicked, this, &MIDIMappingDialog::load_clicked); } MIDIMappingDialog::~MIDIMappingDialog() @@ -149,6 +152,36 @@ void MIDIMappingDialog::cancel_clicked() reject(); } +void MIDIMappingDialog::save_clicked() +{ + unique_ptr new_mapping = construct_mapping_proto_from_ui(); + QString filename = QFileDialog::getSaveFileName(this, + "Save MIDI mapping", QString(), tr("Mapping files (*.midimapping)")); + if (!filename.endsWith(".midimapping")) { + filename += ".midimapping"; + } + if (!save_midi_mapping_to_file(*new_mapping, filename.toStdString())) { + QMessageBox box; + box.setText("Could not save mapping to '" + filename + "'. Check that you have the right permissions and try again."); + box.exec(); + } +} + +void MIDIMappingDialog::load_clicked() +{ + QString filename = QFileDialog::getOpenFileName(this, + "Load MIDI mapping", QString(), tr("Mapping files (*.midimapping)")); + MIDIMappingProto new_mapping; + if (!load_midi_mapping_from_file(filename.toStdString(), &new_mapping)) { + QMessageBox box; + box.setText("Could not load mapping from '" + filename + "'. Check that the file exists, has the right permissions and is valid."); + box.exec(); + return; + } + + fill_controls_from_mapping(new_mapping); +} + namespace { template @@ -213,7 +246,6 @@ void MIDIMappingDialog::add_bank_selector(QTreeWidgetItem *item, const MIDIMappi QComboBox *bank_selector = new QComboBox(this); bank_selector->addItems(QStringList() << "" << "Bank 1" << "Bank 2" << "Bank 3" << "Bank 4" << "Bank 5"); bank_selector->setAutoFillBackground(true); - bank_selector->setCurrentIndex(get_bank(mapping_proto, bank_field_number, -1) + 1); bank_combo_boxes.push_back(InstantiatedComboBox{ bank_selector, bank_field_number }); @@ -243,14 +275,25 @@ void MIDIMappingDialog::add_controls(const string &heading, ui->treeWidget->setItemWidget(item, bus_idx + 2, spinner); if (control_type == ControlType::CONTROLLER) { - spinner->setValue(get_controller_mapping(mapping_proto, bus_idx, control.field_number, 0)); controller_spinners.push_back(InstantiatedSpinner{ spinner, bus_idx, control.field_number }); } else { assert(control_type == ControlType::BUTTON); - spinner->setValue(get_button_mapping(mapping_proto, bus_idx, control.field_number, 0)); button_spinners.push_back(InstantiatedSpinner{ spinner, bus_idx, control.field_number }); } } } ui->treeWidget->addTopLevelItem(heading_item); } + +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, 0)); + } + for (const InstantiatedSpinner &is : button_spinners) { + is.spinner->setValue(get_button_mapping(mapping_proto, is.bus_idx, is.field_number, 0)); + } + for (const InstantiatedComboBox &ic : bank_combo_boxes) { + ic.combo_box->setCurrentIndex(get_bank(mapping_proto, ic.field_number, -1) + 1); + } +} diff --git a/midi_mapping_dialog.h b/midi_mapping_dialog.h index 17cdc18..f2cd0bf 100644 --- a/midi_mapping_dialog.h +++ b/midi_mapping_dialog.h @@ -37,6 +37,8 @@ public: public slots: void ok_clicked(); void cancel_clicked(); + void save_clicked(); + void load_clicked(); private: static constexpr unsigned num_buses = 8; @@ -46,6 +48,7 @@ private: enum class ControlType { CONTROLLER, BUTTON }; void add_controls(const std::string &heading, ControlType control_type, const MIDIMappingProto &mapping_proto, const std::vector &controls); + void fill_controls_from_mapping(const MIDIMappingProto &mapping_proto); std::unique_ptr construct_mapping_proto_from_ui(); @@ -54,7 +57,8 @@ private: MIDIMapper *mapper; // All controllers actually laid out on the grid (we need to store them - // so that we can read its values back into the new protobuf). + // so that we can move values back and forth between the controls and + // the protobuf on save/load). struct InstantiatedSpinner { QSpinBox *spinner; unsigned bus_idx; -- 2.39.2