| |
| |
| |
|
|
| #include <unordered_map> |
| #include <QCheckBox> |
| #include <QStringList> |
| #include "citra_qt/dumping/option_set_dialog.h" |
| #include "common/logging/log.h" |
| #include "common/string_util.h" |
| #include "ui_option_set_dialog.h" |
|
|
| static const std::unordered_map<AVOptionType, const char*> TypeNameMap{{ |
| {AV_OPT_TYPE_BOOL, QT_TR_NOOP("boolean")}, |
| {AV_OPT_TYPE_FLAGS, QT_TR_NOOP("flags")}, |
| {AV_OPT_TYPE_DURATION, QT_TR_NOOP("duration")}, |
| {AV_OPT_TYPE_INT, QT_TR_NOOP("int")}, |
| {AV_OPT_TYPE_UINT64, QT_TR_NOOP("uint64")}, |
| {AV_OPT_TYPE_INT64, QT_TR_NOOP("int64")}, |
| {AV_OPT_TYPE_DOUBLE, QT_TR_NOOP("double")}, |
| {AV_OPT_TYPE_FLOAT, QT_TR_NOOP("float")}, |
| {AV_OPT_TYPE_RATIONAL, QT_TR_NOOP("rational")}, |
| {AV_OPT_TYPE_PIXEL_FMT, QT_TR_NOOP("pixel format")}, |
| {AV_OPT_TYPE_SAMPLE_FMT, QT_TR_NOOP("sample format")}, |
| {AV_OPT_TYPE_COLOR, QT_TR_NOOP("color")}, |
| {AV_OPT_TYPE_IMAGE_SIZE, QT_TR_NOOP("image size")}, |
| {AV_OPT_TYPE_STRING, QT_TR_NOOP("string")}, |
| {AV_OPT_TYPE_DICT, QT_TR_NOOP("dictionary")}, |
| {AV_OPT_TYPE_VIDEO_RATE, QT_TR_NOOP("video rate")}, |
| {AV_OPT_TYPE_CHANNEL_LAYOUT, QT_TR_NOOP("channel layout")}, |
| }}; |
|
|
| static const std::unordered_map<AVOptionType, const char*> TypeDescriptionMap{{ |
| {AV_OPT_TYPE_DURATION, QT_TR_NOOP("[<hours (integer)>:][<minutes (integer):]<seconds " |
| "(decimal)> e.g. 03:00.5 (3min 500ms)")}, |
| {AV_OPT_TYPE_RATIONAL, QT_TR_NOOP("<num>/<den>")}, |
| {AV_OPT_TYPE_COLOR, QT_TR_NOOP("0xRRGGBBAA")}, |
| {AV_OPT_TYPE_IMAGE_SIZE, QT_TR_NOOP("<width>x<height>, or preset values like 'vga'.")}, |
| {AV_OPT_TYPE_DICT, |
| QT_TR_NOOP("Comma-splitted list of <key>=<value>. Do not put spaces.")}, |
| {AV_OPT_TYPE_VIDEO_RATE, QT_TR_NOOP("<num>/<den>, or preset values like 'pal'.")}, |
| {AV_OPT_TYPE_CHANNEL_LAYOUT, QT_TR_NOOP("Hexadecimal channel layout mask starting with '0x'.")}, |
| }}; |
|
|
| |
| std::vector<std::pair<QString, QString>> GetPresetValues(const VideoDumper::OptionInfo& option) { |
| switch (option.type) { |
| case AV_OPT_TYPE_BOOL: { |
| return {{QObject::tr("auto"), QStringLiteral("auto")}, |
| {QObject::tr("true"), QStringLiteral("true")}, |
| {QObject::tr("false"), QStringLiteral("false")}}; |
| } |
| case AV_OPT_TYPE_PIXEL_FMT: { |
| std::vector<std::pair<QString, QString>> out{{QObject::tr("none"), QStringLiteral("none")}}; |
| for (const auto& name : VideoDumper::GetPixelFormats()) { |
| out.emplace_back(QString::fromUtf8(name), QString::fromUtf8(name)); |
| } |
| return out; |
| } |
| case AV_OPT_TYPE_SAMPLE_FMT: { |
| std::vector<std::pair<QString, QString>> out{{QObject::tr("none"), QStringLiteral("none")}}; |
| for (const auto& name : VideoDumper::GetSampleFormats()) { |
| out.emplace_back(QString::fromUtf8(name), QString::fromUtf8(name)); |
| } |
| return out; |
| } |
| case AV_OPT_TYPE_INT: |
| case AV_OPT_TYPE_INT64: |
| case AV_OPT_TYPE_UINT64: { |
| std::vector<std::pair<QString, QString>> out; |
| |
| for (const auto& constant : option.named_constants) { |
| out.emplace_back(QObject::tr("%1 (0x%2)") |
| .arg(QString::fromStdString(constant.name)) |
| .arg(constant.value, 0, 16), |
| QString::fromStdString(constant.name)); |
| } |
| return out; |
| } |
| default: |
| return {}; |
| } |
| } |
|
|
| void OptionSetDialog::InitializeUI(const std::string& initial_value) { |
| const QString type_name = |
| TypeNameMap.count(option.type) ? tr(TypeNameMap.at(option.type)) : tr("unknown"); |
| ui->nameLabel->setText(tr("%1 <%2> %3") |
| .arg(QString::fromStdString(option.name), type_name, |
| QString::fromStdString(option.description))); |
| if (TypeDescriptionMap.count(option.type)) { |
| ui->formatLabel->setVisible(true); |
| ui->formatLabel->setText(tr(TypeDescriptionMap.at(option.type))); |
| } |
|
|
| if (option.type == AV_OPT_TYPE_INT || option.type == AV_OPT_TYPE_INT64 || |
| option.type == AV_OPT_TYPE_UINT64 || option.type == AV_OPT_TYPE_FLOAT || |
| option.type == AV_OPT_TYPE_DOUBLE || option.type == AV_OPT_TYPE_DURATION || |
| option.type == AV_OPT_TYPE_RATIONAL) { |
|
|
| ui->formatLabel->setVisible(true); |
| if (!ui->formatLabel->text().isEmpty()) { |
| ui->formatLabel->text().append(QStringLiteral("\n")); |
| } |
| ui->formatLabel->setText( |
| ui->formatLabel->text().append(tr("Range: %1 - %2").arg(option.min).arg(option.max))); |
| } |
|
|
| |
| if (option.type == AV_OPT_TYPE_BOOL || option.type == AV_OPT_TYPE_PIXEL_FMT || |
| option.type == AV_OPT_TYPE_SAMPLE_FMT || |
| ((option.type == AV_OPT_TYPE_INT || option.type == AV_OPT_TYPE_INT64 || |
| option.type == AV_OPT_TYPE_UINT64) && |
| !option.named_constants.empty())) { |
|
|
| layout_type = 1; |
| ui->comboBox->setVisible(true); |
| ui->comboBoxHelpLabel->setVisible(true); |
|
|
| QString real_initial_value = QString::fromStdString(initial_value); |
| if (option.type == AV_OPT_TYPE_INT || option.type == AV_OPT_TYPE_INT64 || |
| option.type == AV_OPT_TYPE_UINT64) { |
|
|
| |
| try { |
| s64 initial_value_integer = std::stoll(initial_value, nullptr, 0); |
| for (const auto& constant : option.named_constants) { |
| if (constant.value == initial_value_integer) { |
| real_initial_value = QString::fromStdString(constant.name); |
| break; |
| } |
| } |
| } catch (...) { |
| |
| } |
| } |
|
|
| bool found = false; |
| for (const auto& [display, value] : GetPresetValues(option)) { |
| ui->comboBox->addItem(display, value); |
| if (value == real_initial_value) { |
| found = true; |
| ui->comboBox->setCurrentIndex(ui->comboBox->count() - 1); |
| } |
| } |
| ui->comboBox->addItem(tr("custom")); |
|
|
| if (!found) { |
| ui->comboBox->setCurrentIndex(ui->comboBox->count() - 1); |
| ui->lineEdit->setText(QString::fromStdString(initial_value)); |
| } |
|
|
| UpdateUIDisplay(); |
|
|
| connect(ui->comboBox, &QComboBox::currentTextChanged, this, |
| &OptionSetDialog::UpdateUIDisplay); |
| } else if (option.type == AV_OPT_TYPE_FLAGS && |
| !option.named_constants.empty()) { |
|
|
| layout_type = 2; |
|
|
| for (const auto& constant : option.named_constants) { |
| auto* checkBox = new QCheckBox(tr("%1 (0x%2) %3") |
| .arg(QString::fromStdString(constant.name)) |
| .arg(constant.value, 0, 16) |
| .arg(QString::fromStdString(constant.description))); |
| checkBox->setProperty("value", static_cast<unsigned long long>(constant.value)); |
| checkBox->setProperty("name", QString::fromStdString(constant.name)); |
| ui->checkBoxLayout->addWidget(checkBox); |
| } |
| SetCheckBoxDefaults(initial_value); |
| } else { |
| layout_type = 0; |
| ui->lineEdit->setVisible(true); |
| ui->lineEdit->setText(QString::fromStdString(initial_value)); |
| } |
|
|
| adjustSize(); |
| } |
|
|
| void OptionSetDialog::SetCheckBoxDefaults(const std::string& initial_value) { |
| if (initial_value.size() >= 2 && |
| (initial_value.substr(0, 2) == "0x" || initial_value.substr(0, 2) == "0X")) { |
| |
| try { |
| u64 value = std::stoull(initial_value, nullptr, 16); |
| for (int i = 0; i < ui->checkBoxLayout->count(); ++i) { |
| auto* checkBox = qobject_cast<QCheckBox*>(ui->checkBoxLayout->itemAt(i)->widget()); |
| if (checkBox) { |
| checkBox->setChecked(value & checkBox->property("value").toULongLong()); |
| } |
| } |
| } catch (...) { |
| LOG_ERROR(Frontend, "Could not convert {} to number", initial_value); |
| } |
| } else { |
| |
| const auto tmp = Common::SplitString(initial_value, '+'); |
|
|
| std::vector<std::string> out; |
| for (const auto& str : tmp) { |
| const auto tmp2 = Common::SplitString(str, '|'); |
| out.insert(out.end(), tmp2.begin(), tmp2.end()); |
| } |
| for (int i = 0; i < ui->checkBoxLayout->count(); ++i) { |
| auto* checkBox = qobject_cast<QCheckBox*>(ui->checkBoxLayout->itemAt(i)->widget()); |
| if (checkBox) { |
| checkBox->setChecked( |
| std::find(out.begin(), out.end(), |
| checkBox->property("name").toString().toStdString()) != out.end()); |
| } |
| } |
| } |
| } |
|
|
| void OptionSetDialog::UpdateUIDisplay() { |
| if (layout_type != 1) |
| return; |
|
|
| if (ui->comboBox->currentIndex() == ui->comboBox->count() - 1) { |
| ui->comboBoxHelpLabel->setVisible(false); |
| ui->lineEdit->setVisible(true); |
| adjustSize(); |
| return; |
| } |
|
|
| ui->lineEdit->setVisible(false); |
| for (const auto& constant : option.named_constants) { |
| if (constant.name == ui->comboBox->currentData().toString().toStdString()) { |
| ui->comboBoxHelpLabel->setVisible(true); |
| ui->comboBoxHelpLabel->setText(QString::fromStdString(constant.description)); |
| return; |
| } |
| } |
| } |
|
|
| std::pair<bool, std::string> OptionSetDialog::GetCurrentValue() { |
| if (!is_set) { |
| return {}; |
| } |
|
|
| switch (layout_type) { |
| case 0: |
| return {true, ui->lineEdit->text().toStdString()}; |
| case 1: |
| if (ui->comboBox->currentIndex() == ui->comboBox->count() - 1) { |
| return {true, ui->lineEdit->text().toStdString()}; |
| } |
| return {true, ui->comboBox->currentData().toString().toStdString()}; |
| case 2: { |
| std::string out; |
| for (int i = 0; i < ui->checkBoxLayout->count(); ++i) { |
| auto* checkBox = qobject_cast<QCheckBox*>(ui->checkBoxLayout->itemAt(i)->widget()); |
| if (checkBox && checkBox->isChecked()) { |
| if (!out.empty()) { |
| out.append("+"); |
| } |
| out.append(checkBox->property("name").toString().toStdString()); |
| } |
| } |
| if (out.empty()) { |
| out = "0x0"; |
| } |
| return {true, out}; |
| } |
| default: |
| return {}; |
| } |
| } |
|
|
| OptionSetDialog::OptionSetDialog(QWidget* parent, VideoDumper::OptionInfo option_, |
| const std::string& initial_value) |
| : QDialog(parent), ui(std::make_unique<Ui::OptionSetDialog>()), option(std::move(option_)) { |
|
|
| ui->setupUi(this); |
| InitializeUI(initial_value); |
|
|
| connect(ui->unsetButton, &QPushButton::clicked, [this] { |
| is_set = false; |
| accept(); |
| }); |
| connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &OptionSetDialog::accept); |
| connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &OptionSetDialog::reject); |
| } |
|
|
| OptionSetDialog::~OptionSetDialog() = default; |
|
|