/* * Carla plugin host * Copyright (C) 2011-2023 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For a full copy of the GNU General Public License see the doc/GPL.txt file. */ #include "jackappdialog.hpp" #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy" # pragma clang diagnostic ignored "-Wdeprecated-register" #elif defined(__GNUC__) && __GNUC__ >= 8 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wclass-memaccess" # pragma GCC diagnostic ignored "-Wdeprecated-copy" #endif #include "ui_jackappdialog.h" #include #include #include #ifdef __clang__ # pragma clang diagnostic pop #elif defined(__GNUC__) && __GNUC__ >= 8 # pragma GCC diagnostic pop #endif #include "qsafesettings.hpp" #include "CarlaFrontend.h" #include "CarlaLibJackHints.h" #include "CarlaString.hpp" // -------------------------------------------------------------------------------------------------------------------- // Jack Application Dialog enum { UI_SESSION_NONE = 0, UI_SESSION_LADISH = 1, UI_SESSION_NSM = 2, }; struct JackAppDialog::Self { Ui_JackAppDialog ui; const QString fProjectFilename; Self(const char* const projectFilename) : fProjectFilename(projectFilename) {} static Self& create(const char* const projectFilename) { Self* const self = new Self(projectFilename); return *self; } }; JackAppDialog::JackAppDialog(QWidget* const parent, const char* const projectFilename) : QDialog(parent), self(Self::create(projectFilename)) { self.ui.setupUi(this); // ------------------------------------------------------------------------------------------------------------- // UI setup self.ui.group_error->setVisible(false); adjustSize(); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); #ifdef CARLA_OS_MAC if (parent != nullptr) setWindowModality(Qt::WindowModal); #endif // ------------------------------------------------------------------------------------------------------------- // Load settings loadSettings(); // ------------------------------------------------------------------------------------------------------------- // Set-up connections connect(this, &QDialog::finished, this, &JackAppDialog::slot_saveSettings); connect(self.ui.cb_session_mgr, static_cast(&QComboBox::currentIndexChanged), this, &JackAppDialog::slot_sessionManagerChanged); connect(self.ui.le_command, &QLineEdit::textChanged, this, &JackAppDialog::slot_commandChanged); } JackAppDialog::~JackAppDialog() { delete &self; } // ----------------------------------------------------------------------------------------------------------------- // public methods JackAppDialog::CommandAndFlags JackAppDialog::getCommandAndFlags() const { const QString command = self.ui.le_command->text(); QString name = self.ui.le_name->text(); if (name.isEmpty()) { name = QFileInfo(command.split(' ').first()).baseName(); name[0] = name[0].toTitleCase(); } SessionManager smgr; switch (self.ui.cb_session_mgr->currentIndex()) { case UI_SESSION_LADISH: smgr = LIBJACK_SESSION_MANAGER_LADISH; break; case UI_SESSION_NSM: smgr = LIBJACK_SESSION_MANAGER_NSM; break; default: smgr = LIBJACK_SESSION_MANAGER_NONE; break; } uint flags = 0x0; if (self.ui.cb_manage_window->isChecked()) flags |= LIBJACK_FLAG_CONTROL_WINDOW; if (self.ui.cb_capture_first_window->isChecked()) flags |= LIBJACK_FLAG_CAPTURE_FIRST_WINDOW; if (self.ui.cb_buffers_addition_mode->isChecked()) flags |= LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION; if (self.ui.cb_out_midi_mixdown->isChecked()) flags |= LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN; if (self.ui.cb_external_start->isChecked()) flags |= LIBJACK_FLAG_EXTERNAL_START; const QString labelSetup(QString("%1%2%3%4%5%6").arg(QChar('0' + self.ui.sb_audio_ins->value())) .arg(QChar('0' + self.ui.sb_audio_outs->value())) .arg(QChar('0' + self.ui.sb_midi_ins->value())) .arg(QChar('0' + self.ui.sb_midi_outs->value())) .arg(QChar('0' + smgr)) .arg(QChar('0' + flags))); return {command, name, labelSetup}; } // ----------------------------------------------------------------------------------------------------------------- // private methods void JackAppDialog::checkIfButtonBoxShouldBeEnabled(const int index, const QCarlaString& command) { bool enabled = command.isNotEmpty(); QCarlaString showErr; // NSM applications must not be abstract or absolute paths, and must not contain arguments if (enabled and index == UI_SESSION_NSM) { if (QVector{'.', '/'}.contains(command[0])) showErr = tr("NSM applications cannot use abstract or absolute paths"); else if (command.contains(' ') or command.contains(';') or command.contains('&')) showErr = tr("NSM applications cannot use CLI arguments"); else if (self.fProjectFilename.isEmpty()) showErr = tr("You need to save the current Carla project before NSM can be used"); } if (showErr.isNotEmpty()) { enabled = false; self.ui.l_error->setText(showErr); self.ui.group_error->setVisible(true); } else { self.ui.group_error->setVisible(false); } if (QPushButton* const button = self.ui.buttonBox->button(QDialogButtonBox::Ok)) button->setEnabled(enabled); } void JackAppDialog::loadSettings() { const QSafeSettings settings("falkTX", "CarlaAddJackApp"); const QString smName = settings.valueString("SessionManager", ""); if (smName == "LADISH (SIGUSR1)") self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH); else if (smName == "NSM") self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM); else self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE); self.ui.le_command->setText(settings.valueString("Command", "")); self.ui.le_name->setText(settings.valueString("Name", "")); self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2)); self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2)); self.ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2)); self.ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0)); self.ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0)); self.ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true)); self.ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false)); self.ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false)); checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(), self.ui.le_command->text()); } // ----------------------------------------------------------------------------------------------------------------- // private slots void JackAppDialog::slot_commandChanged(const QString& command) { checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(), command); } void JackAppDialog::slot_sessionManagerChanged(const int index) { checkIfButtonBoxShouldBeEnabled(index, self.ui.le_command->text()); } void JackAppDialog::slot_saveSettings() { QSafeSettings settings("falkTX", "CarlaAddJackApp"); settings.setValue("Command", self.ui.le_command->text()); settings.setValue("Name", self.ui.le_name->text()); settings.setValue("SessionManager", self.ui.cb_session_mgr->currentText()); settings.setValue("NumAudioIns", self.ui.sb_audio_ins->value()); settings.setValue("NumAudioOuts", self.ui.sb_audio_outs->value()); settings.setValue("NumMidiIns", self.ui.sb_midi_ins->value()); settings.setValue("NumMidiOuts", self.ui.sb_midi_outs->value()); settings.setValue("ManageWindow", self.ui.cb_manage_window->isChecked()); settings.setValue("CaptureFirstWindow", self.ui.cb_capture_first_window->isChecked()); settings.setValue("MidiOutMixdown", self.ui.cb_out_midi_mixdown->isChecked()); } // -------------------------------------------------------------------------------------------------------------------- const JackAppDialogResults* carla_frontend_createAndExecJackAppDialog(void* const parent, const char* const projectFilename) { JackAppDialog gui(reinterpret_cast(parent), projectFilename); if (gui.exec()) { static JackAppDialogResults ret = {}; static CarlaString retCommand; static CarlaString retName; static CarlaString retLabelSetup; const JackAppDialog::CommandAndFlags cafs = gui.getCommandAndFlags(); retCommand = cafs.command.toUtf8().constData(); retName = cafs.name.toUtf8().constData(); retLabelSetup = cafs.labelSetup.toUtf8().constData(); ret.command = retCommand; ret.name = retName; ret.labelSetup = retLabelSetup; return &ret; } return nullptr; } #if 0 // -------------------------------------------------------------------------------------------------------------------- // Testing #include "../utils/qsafesettings.cpp" int main(int argc, char* argv[]) { QApplication app(argc, argv); if (JackAppDialogResults* const res = carla_frontend_createAndExecJackAppDialog(nullptr, "")) { printf("Results:\n"); printf("\tCommand: %s\n", res->command); printf("\tName: %s\n", res->name); printf("\tLabelSetup: %s\n", res->labelSetup); } return 0; } #endif // --------------------------------------------------------------------------------------------------------------------