← index
acidcam-gpu/ACMX2/interface/gpufilter.cpp
Source: acidcam-gpu/ACMX2/interface/gpufilter.cpp
#include "gpufilter.hpp"
#include <QCompleter>
#include <QMessageBox>
#include <QRegularExpression>
#include <QSettings>
#include <QTextStream>
#include <algorithm>

GPUFilterDialog::GPUFilterDialog(const QString &executablePath, QWidget *parent)
    : QDialog(parent), execPath(executablePath) {
    setWindowTitle("GPU Filter Settings");
    setMinimumSize(500, 500);
    setupUI();
    loadFiltersFromExecutable();
}

void GPUFilterDialog::setupUI() {
    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    enableCheckBox = new QCheckBox("Enable GPU Filtering", this);
    mainLayout->addWidget(enableCheckBox);
    QGroupBox *bufferGroup = new QGroupBox("Frame Buffer Size", this);
    QHBoxLayout *bufferLayout = new QHBoxLayout(bufferGroup);
    QLabel *bufferLabel = new QLabel("Buffer Size (4-32):", this);
    bufferSizeSpinBox = new QSpinBox(this);
    bufferSizeSpinBox->setRange(4, 32);
    bufferSizeSpinBox->setValue(8);
    bufferLayout->addWidget(bufferLabel);
    bufferLayout->addWidget(bufferSizeSpinBox);
    bufferLayout->addStretch();
    mainLayout->addWidget(bufferGroup);
    QGroupBox *filterGroup = new QGroupBox("Filter Selection", this);
    QVBoxLayout *filterMainLayout = new QVBoxLayout(filterGroup);

    QHBoxLayout *searchLayout = new QHBoxLayout();
    QLabel *searchLabel = new QLabel("Search:", this);
    searchLineEdit = new QLineEdit(this);
    searchLineEdit->setPlaceholderText("Type to search filters...");
    searchLineEdit->setClearButtonEnabled(true);
    searchLayout->addWidget(searchLabel);
    searchLayout->addWidget(searchLineEdit, 1);
    filterMainLayout->addLayout(searchLayout);

    QHBoxLayout *comboLayout = new QHBoxLayout();
    QLabel *availableLabel = new QLabel("Available Filters:", this);
    filterComboBox = new QComboBox(this);
    filterComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    filterComboBox->setMaxVisibleItems(20);

    filterModel = new QStandardItemModel(this);
    proxyModel = new QSortFilterProxyModel(this);
    proxyModel->setSourceModel(filterModel);
    proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
    proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
    filterComboBox->setModel(proxyModel);

    comboLayout->addWidget(availableLabel);
    comboLayout->addWidget(filterComboBox, 1);
    filterMainLayout->addLayout(comboLayout);
    QHBoxLayout *buttonLayout = new QHBoxLayout();
    addButton = new QPushButton("Add →", this);
    removeButton = new QPushButton("← Remove", this);
    upButton = new QPushButton("↑ Up", this);
    downButton = new QPushButton("↓ Down", this);
    clearButton = new QPushButton("Clear All", this);
    buttonLayout->addWidget(addButton);
    buttonLayout->addWidget(removeButton);
    buttonLayout->addWidget(upButton);
    buttonLayout->addWidget(downButton);
    buttonLayout->addWidget(clearButton);
    filterMainLayout->addLayout(buttonLayout);
    QLabel *selectedLabel = new QLabel("Selected Filters (processing order):", this);
    filterMainLayout->addWidget(selectedLabel);
    selectedFiltersList = new QListWidget(this);
    selectedFiltersList->setMinimumHeight(150);
    filterMainLayout->addWidget(selectedFiltersList);
    mainLayout->addWidget(filterGroup);
    QHBoxLayout *dialogButtonLayout = new QHBoxLayout();
    okButton = new QPushButton("OK", this);
    cancelButton = new QPushButton("Cancel", this);
    dialogButtonLayout->addStretch();
    dialogButtonLayout->addWidget(okButton);
    dialogButtonLayout->addWidget(cancelButton);
    mainLayout->addLayout(dialogButtonLayout);
    connect(addButton, &QPushButton::clicked, this, &GPUFilterDialog::addFilter);
    connect(removeButton, &QPushButton::clicked, this, &GPUFilterDialog::removeFilter);
    connect(upButton, &QPushButton::clicked, this, &GPUFilterDialog::moveUp);
    connect(downButton, &QPushButton::clicked, this, &GPUFilterDialog::moveDown);
    connect(clearButton, &QPushButton::clicked, this, &GPUFilterDialog::clearAll);
    connect(okButton, &QPushButton::clicked, this, &QDialog::accept);
    connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
    connect(searchLineEdit, &QLineEdit::textChanged, this, &GPUFilterDialog::filterSearchChanged);
    connect(enableCheckBox, &QCheckBox::toggled, this, [this](bool checked) {
        filterComboBox->setEnabled(checked);
        selectedFiltersList->setEnabled(checked);
        bufferSizeSpinBox->setEnabled(checked);
        searchLineEdit->setEnabled(checked);
        addButton->setEnabled(checked);
        removeButton->setEnabled(checked);
        upButton->setEnabled(checked);
        downButton->setEnabled(checked);
        clearButton->setEnabled(checked);
    });

    enableCheckBox->setChecked(false);
    filterComboBox->setEnabled(false);
    selectedFiltersList->setEnabled(false);
    bufferSizeSpinBox->setEnabled(false);
    searchLineEdit->setEnabled(false);
    addButton->setEnabled(false);
    removeButton->setEnabled(false);
    upButton->setEnabled(false);
    downButton->setEnabled(false);
    clearButton->setEnabled(false);

    QString style = "QDialog { background-color: black; }"
                    "QGroupBox { color: red; border: 1px solid red; margin-top: 10px; padding-top: 10px; }"
                    "QGroupBox::title { subcontrol-origin: margin; left: 10px; }"
                    "QLabel { color: red; }"
                    "QCheckBox { color: red; }"
                    "QLineEdit { background-color: #110000; color: red; border: 1px solid red; padding: 3px; }"
                    "QComboBox { background-color: #110000; color: red; border: 1px solid red; }"
                    "QSpinBox { background-color: #110000; color: red; border: 1px solid red; }"
                    "QListWidget { background-color: #110000; color: lime; border: 1px solid red; }"
                    "QPushButton { border: 1px solid red; background-color: #110000; color: red; padding: 5px; }"
                    "QPushButton:hover { background-color: red; color: black; }";
    QSettings appSettings("LostSideDead");
    if (appSettings.value("useCustomStyle", true).toBool()) {
        setStyleSheet(style);
    }
}

void GPUFilterDialog::loadFiltersFromExecutable() {
    QProcess process;
    QStringList args;
    args << "--list-filters";

    process.start(execPath, args);
    if (!process.waitForFinished(10000)) {
        QMessageBox::warning(this, "Error",
                             "Failed to get filter list from acmx2.\nMake sure the executable path is correct.");
        return;
    }

    QString output = process.readAllStandardOutput();
    QStringList lines = output.split('\n', Qt::SkipEmptyParts);
    QRegularExpression re("^\\s*(\\d+):\\s*(.+)$");

    filterNames.clear();
    filterNameToIndex.clear();
    filterModel->clear();

    QList<QPair<QString, int>> tempFilters;

    for (const QString &line : lines) {
        QRegularExpressionMatch match = re.match(line);
        if (match.hasMatch()) {
            int index = match.captured(1).toInt();
            QString name = match.captured(2).trimmed();
            tempFilters.append(qMakePair(name, index));
        }
    }

    std::sort(tempFilters.begin(), tempFilters.end(),
              [](const QPair<QString, int> &a, const QPair<QString, int> &b) {
                  return a.first.toLower() < b.first.toLower();
              });

    for (const auto &filter : tempFilters) {
        filterNames.append(filter.first);
        filterNameToIndex[filter.first] = filter.second;
        QStandardItem *item = new QStandardItem(filter.first);
        item->setData(filter.second, Qt::UserRole);
        filterModel->appendRow(item);
    }

    if (filterNames.isEmpty()) {
        QMessageBox::warning(this, "Warning",
                             "No GPU filters found. Make sure acmx2 is compiled with GPU filter support.");
    }
}

void GPUFilterDialog::filterSearchChanged(const QString &text) {
    proxyModel->setFilterFixedString(text);
    if (proxyModel->rowCount() > 0) {
        filterComboBox->setCurrentIndex(0);
    }
}

void GPUFilterDialog::addFilter() {
    if (filterComboBox->currentIndex() < 0) {
        return;
    }

    QString filterName = filterComboBox->currentText();
    selectedFiltersList->addItem(filterName);
}

void GPUFilterDialog::removeFilter() {
    QListWidgetItem *item = selectedFiltersList->currentItem();
    if (item) {
        delete selectedFiltersList->takeItem(selectedFiltersList->row(item));
    }
}

void GPUFilterDialog::moveUp() {
    int currentRow = selectedFiltersList->currentRow();
    if (currentRow > 0) {
        QListWidgetItem *item = selectedFiltersList->takeItem(currentRow);
        selectedFiltersList->insertItem(currentRow - 1, item);
        selectedFiltersList->setCurrentRow(currentRow - 1);
    }
}

void GPUFilterDialog::moveDown() {
    int currentRow = selectedFiltersList->currentRow();
    if (currentRow >= 0 && currentRow < selectedFiltersList->count() - 1) {
        QListWidgetItem *item = selectedFiltersList->takeItem(currentRow);
        selectedFiltersList->insertItem(currentRow + 1, item);
        selectedFiltersList->setCurrentRow(currentRow + 1);
    }
}

void GPUFilterDialog::clearAll() {
    selectedFiltersList->clear();
}

bool GPUFilterDialog::isGPUFilterEnabled() const {
    return enableCheckBox->isChecked() && selectedFiltersList->count() > 0;
}

QStringList GPUFilterDialog::getSelectedFilterIndices() const {
    QStringList indices;
    for (int i = 0; i < selectedFiltersList->count(); ++i) {
        QString itemText = selectedFiltersList->item(i)->text();
        if (filterNameToIndex.contains(itemText)) {
            indices.append(QString::number(filterNameToIndex[itemText]));
        }
    }
    return indices;
}

int GPUFilterDialog::getBufferSize() const {
    return bufferSizeSpinBox->value();
}

QString GPUFilterDialog::getFilterArgument() const {
    QStringList indices = getSelectedFilterIndices();
    return indices.join(",");
}