From 09ebf7ddfc6c361c052e6019d797b65b3d525828 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Mon, 2 Dec 2024 20:38:37 +0100 Subject: [PATCH] frontend: Split OBSImporter into single files per C++ class --- .../ImporterEntryPathItemDelegate.cpp | 424 +----------------- .../ImporterEntryPathItemDelegate.hpp | 58 --- frontend/importer/ImporterModel.cpp | 372 +-------------- frontend/importer/ImporterModel.hpp | 59 +-- frontend/importer/OBSImporter.cpp | 360 +-------------- frontend/importer/OBSImporter.hpp | 62 +-- 6 files changed, 34 insertions(+), 1301 deletions(-) diff --git a/frontend/importer/ImporterEntryPathItemDelegate.cpp b/frontend/importer/ImporterEntryPathItemDelegate.cpp index 88d4ad2d9..d6b3b8f7c 100644 --- a/frontend/importer/ImporterEntryPathItemDelegate.cpp +++ b/frontend/importer/ImporterEntryPathItemDelegate.cpp @@ -15,37 +15,18 @@ along with this program. If not, see . ******************************************************************************/ -#include "moc_window-importer.cpp" +#include "ImporterEntryPathItemDelegate.hpp" +#include "ImporterModel.hpp" -#include "obs-app.hpp" +#include -#include -#include -#include -#include -#include -#include -#include #include -#include "importers/importers.hpp" +#include +#include +#include -extern bool SceneCollectionExists(const char *findName); - -enum ImporterColumn { - Selected, - Name, - Path, - Program, - - Count -}; - -enum ImporterEntryRole { EntryStateRole = Qt::UserRole, NewPath, AutoPath, CheckEmpty }; - -/********************************************************** - Delegate - Presents cells in the grid. -**********************************************************/ +#include "moc_ImporterEntryPathItemDelegate.cpp" ImporterEntryPathItemDelegate::ImporterEntryPathItemDelegate() : QStyledItemDelegate() {} @@ -177,394 +158,3 @@ void ImporterEntryPathItemDelegate::updateText() QWidget *editor = lineEdit->parentWidget(); emit commitData(editor); } - -/** - Model -**/ - -int ImporterModel::rowCount(const QModelIndex &) const -{ - return options.length() + 1; -} - -int ImporterModel::columnCount(const QModelIndex &) const -{ - return ImporterColumn::Count; -} - -QVariant ImporterModel::data(const QModelIndex &index, int role) const -{ - QVariant result = QVariant(); - - if (index.row() >= options.length()) { - if (role == ImporterEntryRole::CheckEmpty) - result = true; - else - return QVariant(); - } else if (role == Qt::DisplayRole) { - switch (index.column()) { - case ImporterColumn::Path: - result = options[index.row()].path; - break; - case ImporterColumn::Program: - result = options[index.row()].program; - break; - case ImporterColumn::Name: - result = options[index.row()].name; - } - } else if (role == Qt::EditRole) { - if (index.column() == ImporterColumn::Name) { - result = options[index.row()].name; - } - } else if (role == Qt::CheckStateRole) { - switch (index.column()) { - case ImporterColumn::Selected: - if (options[index.row()].program != "") - result = options[index.row()].selected ? Qt::Checked : Qt::Unchecked; - else - result = Qt::Unchecked; - } - } else if (role == ImporterEntryRole::CheckEmpty) { - result = options[index.row()].empty; - } - - return result; -} - -Qt::ItemFlags ImporterModel::flags(const QModelIndex &index) const -{ - Qt::ItemFlags flags = QAbstractTableModel::flags(index); - - if (index.column() == ImporterColumn::Selected && index.row() != options.length()) { - flags |= Qt::ItemIsUserCheckable; - } else if (index.column() == ImporterColumn::Path || - (index.column() == ImporterColumn::Name && index.row() != options.length())) { - flags |= Qt::ItemIsEditable; - } - - return flags; -} - -void ImporterModel::checkInputPath(int row) -{ - ImporterEntry &entry = options[row]; - - if (entry.path.isEmpty()) { - entry.program = ""; - entry.empty = true; - entry.selected = false; - entry.name = ""; - } else { - entry.empty = false; - - std::string program = DetectProgram(entry.path.toStdString()); - entry.program = QTStr(program.c_str()); - - if (program.empty()) { - entry.selected = false; - } else { - std::string name = GetSCName(entry.path.toStdString(), program); - entry.name = name.c_str(); - } - } - - emit dataChanged(index(row, 0), index(row, ImporterColumn::Count)); -} - -bool ImporterModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (role == ImporterEntryRole::NewPath) { - QStringList list = value.toStringList(); - - if (list.size() == 0) { - if (index.row() < options.size()) { - beginRemoveRows(QModelIndex(), index.row(), index.row()); - options.removeAt(index.row()); - endRemoveRows(); - } - } else { - if (list.size() > 0 && index.row() < options.length()) { - options[index.row()].path = list[0]; - checkInputPath(index.row()); - - list.removeAt(0); - } - - if (list.size() > 0) { - int row = index.row(); - int lastRow = row + list.size() - 1; - beginInsertRows(QModelIndex(), row, lastRow); - - for (QString path : list) { - ImporterEntry entry; - entry.path = path; - - options.insert(row, entry); - - row++; - } - - endInsertRows(); - - for (row = index.row(); row <= lastRow; row++) { - checkInputPath(row); - } - } - } - } else if (index.row() == options.length()) { - QString path = value.toString(); - - if (!path.isEmpty()) { - ImporterEntry entry; - entry.path = path; - entry.selected = role != ImporterEntryRole::AutoPath; - entry.empty = false; - - beginInsertRows(QModelIndex(), options.length() + 1, options.length() + 1); - options.append(entry); - endInsertRows(); - - checkInputPath(index.row()); - } - } else if (index.column() == ImporterColumn::Selected) { - bool select = value.toBool(); - - options[index.row()].selected = select; - } else if (index.column() == ImporterColumn::Path) { - QString path = value.toString(); - options[index.row()].path = path; - - checkInputPath(index.row()); - } else if (index.column() == ImporterColumn::Name) { - QString name = value.toString(); - options[index.row()].name = name; - } - - emit dataChanged(index, index); - - return true; -} - -QVariant ImporterModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - QVariant result = QVariant(); - - if (role == Qt::DisplayRole && orientation == Qt::Orientation::Horizontal) { - switch (section) { - case ImporterColumn::Path: - result = QTStr("Importer.Path"); - break; - case ImporterColumn::Program: - result = QTStr("Importer.Program"); - break; - case ImporterColumn::Name: - result = QTStr("Name"); - } - } - - return result; -} - -/** - Window -**/ - -OBSImporter::OBSImporter(QWidget *parent) : QDialog(parent), optionsModel(new ImporterModel), ui(new Ui::OBSImporter) -{ - setAcceptDrops(true); - - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - - ui->setupUi(this); - - ui->tableView->setModel(optionsModel); - ui->tableView->setItemDelegateForColumn(ImporterColumn::Path, new ImporterEntryPathItemDelegate()); - ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents); - ui->tableView->horizontalHeader()->setSectionResizeMode(ImporterColumn::Path, QHeaderView::ResizeMode::Stretch); - - connect(optionsModel, &ImporterModel::dataChanged, this, &OBSImporter::dataChanged); - - ui->tableView->setEditTriggers(QAbstractItemView::EditTrigger::CurrentChanged); - - ui->buttonBox->button(QDialogButtonBox::Ok)->setText(QTStr("Import")); - ui->buttonBox->button(QDialogButtonBox::Open)->setText(QTStr("Add")); - - connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, - &OBSImporter::importCollections); - connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, this, &OBSImporter::browseImport); - connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &OBSImporter::close); - - ImportersInit(); - - bool autoSearchPrompt = config_get_bool(App()->GetUserConfig(), "General", "AutoSearchPrompt"); - - if (!autoSearchPrompt) { - QMessageBox::StandardButton button = OBSMessageBox::question( - parent, QTStr("Importer.AutomaticCollectionPrompt"), QTStr("Importer.AutomaticCollectionText")); - - if (button == QMessageBox::Yes) { - config_set_bool(App()->GetUserConfig(), "General", "AutomaticCollectionSearch", true); - } else { - config_set_bool(App()->GetUserConfig(), "General", "AutomaticCollectionSearch", false); - } - - config_set_bool(App()->GetUserConfig(), "General", "AutoSearchPrompt", true); - } - - bool autoSearch = config_get_bool(App()->GetUserConfig(), "General", "AutomaticCollectionSearch"); - - OBSImporterFiles f; - if (autoSearch) - f = ImportersFindFiles(); - - for (size_t i = 0; i < f.size(); i++) { - QString path = f[i].c_str(); - path.replace("\\", "/"); - addImportOption(path, true); - } - - f.clear(); - - ui->tableView->resizeColumnsToContents(); - - QModelIndex index = optionsModel->createIndex(optionsModel->rowCount() - 1, 2); - QMetaObject::invokeMethod(ui->tableView, "setCurrentIndex", Qt::QueuedConnection, - Q_ARG(const QModelIndex &, index)); -} - -void OBSImporter::addImportOption(QString path, bool automatic) -{ - QStringList list; - - list.append(path); - - QModelIndex insertIndex = optionsModel->index(optionsModel->rowCount() - 1, ImporterColumn::Path); - - optionsModel->setData(insertIndex, list, automatic ? ImporterEntryRole::AutoPath : ImporterEntryRole::NewPath); -} - -void OBSImporter::dropEvent(QDropEvent *ev) -{ - for (QUrl url : ev->mimeData()->urls()) { - QFileInfo fileInfo(url.toLocalFile()); - if (fileInfo.isDir()) { - - QDirIterator dirIter(fileInfo.absoluteFilePath(), QDir::Files); - - while (dirIter.hasNext()) { - addImportOption(dirIter.next(), false); - } - } else { - addImportOption(fileInfo.canonicalFilePath(), false); - } - } -} - -void OBSImporter::dragEnterEvent(QDragEnterEvent *ev) -{ - if (ev->mimeData()->hasUrls()) - ev->accept(); -} - -void OBSImporter::browseImport() -{ - QString Pattern = "(*.json *.bpres *.xml *.xconfig)"; - - QStringList paths = OpenFiles(this, QTStr("Importer.SelectCollection"), "", - QTStr("Importer.Collection") + QString(" ") + Pattern); - - if (!paths.empty()) { - for (int i = 0; i < paths.count(); i++) { - addImportOption(paths[i], false); - } - } -} - -bool GetUnusedName(std::string &name) -{ - OBSBasic *basic = reinterpret_cast(App()->GetMainWindow()); - - if (!basic->GetSceneCollectionByName(name)) { - return false; - } - - std::string newName; - int inc = 2; - do { - newName = name; - newName += " "; - newName += std::to_string(inc++); - } while (basic->GetSceneCollectionByName(newName)); - - name = newName; - return true; -} - -constexpr std::string_view OBSSceneCollectionPath = "obs-studio/basic/scenes/"; - -void OBSImporter::importCollections() -{ - setEnabled(false); - - const std::filesystem::path sceneCollectionLocation = - App()->userScenesLocation / std::filesystem::u8path(OBSSceneCollectionPath); - - for (int i = 0; i < optionsModel->rowCount() - 1; i++) { - int selected = optionsModel->index(i, ImporterColumn::Selected).data(Qt::CheckStateRole).value(); - - if (selected == Qt::Unchecked) - continue; - - std::string pathStr = optionsModel->index(i, ImporterColumn::Path) - .data(Qt::DisplayRole) - .value() - .toStdString(); - std::string nameStr = optionsModel->index(i, ImporterColumn::Name) - .data(Qt::DisplayRole) - .value() - .toStdString(); - - json11::Json res; - ImportSC(pathStr, nameStr, res); - - if (res != json11::Json()) { - json11::Json::object out = res.object_items(); - std::string name = res["name"].string_value(); - std::string file; - - if (GetUnusedName(name)) { - json11::Json::object newOut = out; - newOut["name"] = name; - out = newOut; - } - - std::string fileName; - if (!GetFileSafeName(name.c_str(), fileName)) { - blog(LOG_WARNING, "Failed to create safe file name for '%s'", fileName.c_str()); - } - - std::string collectionFile; - collectionFile.reserve(sceneCollectionLocation.u8string().size() + fileName.size()); - collectionFile.append(sceneCollectionLocation.u8string()).append(fileName); - - if (!GetClosestUnusedFileName(collectionFile, "json")) { - blog(LOG_WARNING, "Failed to get closest file name for %s", fileName.c_str()); - } - - std::string out_str = json11::Json(out).dump(); - - bool success = os_quick_write_utf8_file(collectionFile.c_str(), out_str.c_str(), out_str.size(), - false); - - blog(LOG_INFO, "Import Scene Collection: %s (%s) - %s", name.c_str(), fileName.c_str(), - success ? "SUCCESS" : "FAILURE"); - } - } - - close(); -} - -void OBSImporter::dataChanged() -{ - ui->tableView->resizeColumnToContents(ImporterColumn::Name); -} diff --git a/frontend/importer/ImporterEntryPathItemDelegate.hpp b/frontend/importer/ImporterEntryPathItemDelegate.hpp index 8e77db645..61304c9cf 100644 --- a/frontend/importer/ImporterEntryPathItemDelegate.hpp +++ b/frontend/importer/ImporterEntryPathItemDelegate.hpp @@ -17,65 +17,7 @@ #pragma once -#include "obs-app.hpp" -#include "window-basic-main.hpp" -#include #include -#include -#include "ui_OBSImporter.h" - -class ImporterModel; - -class OBSImporter : public QDialog { - Q_OBJECT - - QPointer optionsModel; - std::unique_ptr ui; - -public: - explicit OBSImporter(QWidget *parent = nullptr); - - void addImportOption(QString path, bool automatic); - -protected: - virtual void dropEvent(QDropEvent *ev) override; - virtual void dragEnterEvent(QDragEnterEvent *ev) override; - -public slots: - void browseImport(); - void importCollections(); - void dataChanged(); -}; - -class ImporterModel : public QAbstractTableModel { - Q_OBJECT - - friend class OBSImporter; - -public: - ImporterModel(QObject *parent = 0) : QAbstractTableModel(parent) {} - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - bool setData(const QModelIndex &index, const QVariant &value, int role); - -private: - struct ImporterEntry { - QString path; - QString program; - QString name; - - bool selected; - bool empty; - }; - - QList options; - - void checkInputPath(int row); -}; class ImporterEntryPathItemDelegate : public QStyledItemDelegate { Q_OBJECT diff --git a/frontend/importer/ImporterModel.cpp b/frontend/importer/ImporterModel.cpp index 88d4ad2d9..0c6e22deb 100644 --- a/frontend/importer/ImporterModel.cpp +++ b/frontend/importer/ImporterModel.cpp @@ -15,172 +15,12 @@ along with this program. If not, see . ******************************************************************************/ -#include "moc_window-importer.cpp" +#include "ImporterModel.hpp" -#include "obs-app.hpp" +#include +#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "importers/importers.hpp" - -extern bool SceneCollectionExists(const char *findName); - -enum ImporterColumn { - Selected, - Name, - Path, - Program, - - Count -}; - -enum ImporterEntryRole { EntryStateRole = Qt::UserRole, NewPath, AutoPath, CheckEmpty }; - -/********************************************************** - Delegate - Presents cells in the grid. -**********************************************************/ - -ImporterEntryPathItemDelegate::ImporterEntryPathItemDelegate() : QStyledItemDelegate() {} - -QWidget *ImporterEntryPathItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */, - const QModelIndex &index) const -{ - bool empty = index.model() - ->index(index.row(), ImporterColumn::Path) - .data(ImporterEntryRole::CheckEmpty) - .value(); - - QSizePolicy buttonSizePolicy(QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Expanding, - QSizePolicy::ControlType::PushButton); - - QWidget *container = new QWidget(parent); - - auto browseCallback = [this, container]() { - const_cast(this)->handleBrowse(container); - }; - - auto clearCallback = [this, container]() { - const_cast(this)->handleClear(container); - }; - - QHBoxLayout *layout = new QHBoxLayout(); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - - QLineEdit *text = new QLineEdit(); - text->setObjectName(QStringLiteral("text")); - text->setSizePolicy(QSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding, - QSizePolicy::ControlType::LineEdit)); - layout->addWidget(text); - - QObject::connect(text, &QLineEdit::editingFinished, this, &ImporterEntryPathItemDelegate::updateText); - - QToolButton *browseButton = new QToolButton(); - browseButton->setText("..."); - browseButton->setSizePolicy(buttonSizePolicy); - layout->addWidget(browseButton); - - container->connect(browseButton, &QToolButton::clicked, browseCallback); - - // The "clear" button is not shown in output cells - // or the insertion point's input cell. - if (!empty) { - QToolButton *clearButton = new QToolButton(); - clearButton->setText("X"); - clearButton->setSizePolicy(buttonSizePolicy); - layout->addWidget(clearButton); - - container->connect(clearButton, &QToolButton::clicked, clearCallback); - } - - container->setLayout(layout); - container->setFocusProxy(text); - return container; -} - -void ImporterEntryPathItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const -{ - QLineEdit *text = editor->findChild(); - text->setText(index.data().toString()); - editor->setProperty(PATH_LIST_PROP, QVariant()); -} - -void ImporterEntryPathItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const -{ - // We use the PATH_LIST_PROP property to pass a list of - // path strings from the editor widget into the model's - // NewPathsToProcessRole. This is only used when paths - // are selected through the "browse" or "delete" buttons - // in the editor. If the user enters new text in the - // text box, we simply pass that text on to the model - // as normal text data in the default role. - QVariant pathListProp = editor->property(PATH_LIST_PROP); - if (pathListProp.isValid()) { - QStringList list = editor->property(PATH_LIST_PROP).toStringList(); - model->setData(index, list, ImporterEntryRole::NewPath); - } else { - QLineEdit *lineEdit = editor->findChild(); - model->setData(index, lineEdit->text()); - } -} - -void ImporterEntryPathItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItem localOption = option; - initStyleOption(&localOption, index); - - QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &localOption, painter); -} - -void ImporterEntryPathItemDelegate::handleBrowse(QWidget *container) -{ - QString Pattern = "(*.json *.bpres *.xml *.xconfig)"; - - QLineEdit *text = container->findChild(); - - QString currentPath = text->text(); - - bool isSet = false; - QStringList paths = OpenFiles(container, QTStr("Importer.SelectCollection"), currentPath, - QTStr("Importer.Collection") + QString(" ") + Pattern); - - if (!paths.empty()) { - container->setProperty(PATH_LIST_PROP, paths); - isSet = true; - } - - if (isSet) - emit commitData(container); -} - -void ImporterEntryPathItemDelegate::handleClear(QWidget *container) -{ - // An empty string list will indicate that the entry is being - // blanked and should be deleted. - container->setProperty(PATH_LIST_PROP, QStringList()); - - emit commitData(container); -} - -void ImporterEntryPathItemDelegate::updateText() -{ - QLineEdit *lineEdit = dynamic_cast(sender()); - QWidget *editor = lineEdit->parentWidget(); - emit commitData(editor); -} - -/** - Model -**/ +#include "moc_ImporterModel.cpp" int ImporterModel::rowCount(const QModelIndex &) const { @@ -364,207 +204,3 @@ QVariant ImporterModel::headerData(int section, Qt::Orientation orientation, int return result; } - -/** - Window -**/ - -OBSImporter::OBSImporter(QWidget *parent) : QDialog(parent), optionsModel(new ImporterModel), ui(new Ui::OBSImporter) -{ - setAcceptDrops(true); - - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - - ui->setupUi(this); - - ui->tableView->setModel(optionsModel); - ui->tableView->setItemDelegateForColumn(ImporterColumn::Path, new ImporterEntryPathItemDelegate()); - ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents); - ui->tableView->horizontalHeader()->setSectionResizeMode(ImporterColumn::Path, QHeaderView::ResizeMode::Stretch); - - connect(optionsModel, &ImporterModel::dataChanged, this, &OBSImporter::dataChanged); - - ui->tableView->setEditTriggers(QAbstractItemView::EditTrigger::CurrentChanged); - - ui->buttonBox->button(QDialogButtonBox::Ok)->setText(QTStr("Import")); - ui->buttonBox->button(QDialogButtonBox::Open)->setText(QTStr("Add")); - - connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, - &OBSImporter::importCollections); - connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, this, &OBSImporter::browseImport); - connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &OBSImporter::close); - - ImportersInit(); - - bool autoSearchPrompt = config_get_bool(App()->GetUserConfig(), "General", "AutoSearchPrompt"); - - if (!autoSearchPrompt) { - QMessageBox::StandardButton button = OBSMessageBox::question( - parent, QTStr("Importer.AutomaticCollectionPrompt"), QTStr("Importer.AutomaticCollectionText")); - - if (button == QMessageBox::Yes) { - config_set_bool(App()->GetUserConfig(), "General", "AutomaticCollectionSearch", true); - } else { - config_set_bool(App()->GetUserConfig(), "General", "AutomaticCollectionSearch", false); - } - - config_set_bool(App()->GetUserConfig(), "General", "AutoSearchPrompt", true); - } - - bool autoSearch = config_get_bool(App()->GetUserConfig(), "General", "AutomaticCollectionSearch"); - - OBSImporterFiles f; - if (autoSearch) - f = ImportersFindFiles(); - - for (size_t i = 0; i < f.size(); i++) { - QString path = f[i].c_str(); - path.replace("\\", "/"); - addImportOption(path, true); - } - - f.clear(); - - ui->tableView->resizeColumnsToContents(); - - QModelIndex index = optionsModel->createIndex(optionsModel->rowCount() - 1, 2); - QMetaObject::invokeMethod(ui->tableView, "setCurrentIndex", Qt::QueuedConnection, - Q_ARG(const QModelIndex &, index)); -} - -void OBSImporter::addImportOption(QString path, bool automatic) -{ - QStringList list; - - list.append(path); - - QModelIndex insertIndex = optionsModel->index(optionsModel->rowCount() - 1, ImporterColumn::Path); - - optionsModel->setData(insertIndex, list, automatic ? ImporterEntryRole::AutoPath : ImporterEntryRole::NewPath); -} - -void OBSImporter::dropEvent(QDropEvent *ev) -{ - for (QUrl url : ev->mimeData()->urls()) { - QFileInfo fileInfo(url.toLocalFile()); - if (fileInfo.isDir()) { - - QDirIterator dirIter(fileInfo.absoluteFilePath(), QDir::Files); - - while (dirIter.hasNext()) { - addImportOption(dirIter.next(), false); - } - } else { - addImportOption(fileInfo.canonicalFilePath(), false); - } - } -} - -void OBSImporter::dragEnterEvent(QDragEnterEvent *ev) -{ - if (ev->mimeData()->hasUrls()) - ev->accept(); -} - -void OBSImporter::browseImport() -{ - QString Pattern = "(*.json *.bpres *.xml *.xconfig)"; - - QStringList paths = OpenFiles(this, QTStr("Importer.SelectCollection"), "", - QTStr("Importer.Collection") + QString(" ") + Pattern); - - if (!paths.empty()) { - for (int i = 0; i < paths.count(); i++) { - addImportOption(paths[i], false); - } - } -} - -bool GetUnusedName(std::string &name) -{ - OBSBasic *basic = reinterpret_cast(App()->GetMainWindow()); - - if (!basic->GetSceneCollectionByName(name)) { - return false; - } - - std::string newName; - int inc = 2; - do { - newName = name; - newName += " "; - newName += std::to_string(inc++); - } while (basic->GetSceneCollectionByName(newName)); - - name = newName; - return true; -} - -constexpr std::string_view OBSSceneCollectionPath = "obs-studio/basic/scenes/"; - -void OBSImporter::importCollections() -{ - setEnabled(false); - - const std::filesystem::path sceneCollectionLocation = - App()->userScenesLocation / std::filesystem::u8path(OBSSceneCollectionPath); - - for (int i = 0; i < optionsModel->rowCount() - 1; i++) { - int selected = optionsModel->index(i, ImporterColumn::Selected).data(Qt::CheckStateRole).value(); - - if (selected == Qt::Unchecked) - continue; - - std::string pathStr = optionsModel->index(i, ImporterColumn::Path) - .data(Qt::DisplayRole) - .value() - .toStdString(); - std::string nameStr = optionsModel->index(i, ImporterColumn::Name) - .data(Qt::DisplayRole) - .value() - .toStdString(); - - json11::Json res; - ImportSC(pathStr, nameStr, res); - - if (res != json11::Json()) { - json11::Json::object out = res.object_items(); - std::string name = res["name"].string_value(); - std::string file; - - if (GetUnusedName(name)) { - json11::Json::object newOut = out; - newOut["name"] = name; - out = newOut; - } - - std::string fileName; - if (!GetFileSafeName(name.c_str(), fileName)) { - blog(LOG_WARNING, "Failed to create safe file name for '%s'", fileName.c_str()); - } - - std::string collectionFile; - collectionFile.reserve(sceneCollectionLocation.u8string().size() + fileName.size()); - collectionFile.append(sceneCollectionLocation.u8string()).append(fileName); - - if (!GetClosestUnusedFileName(collectionFile, "json")) { - blog(LOG_WARNING, "Failed to get closest file name for %s", fileName.c_str()); - } - - std::string out_str = json11::Json(out).dump(); - - bool success = os_quick_write_utf8_file(collectionFile.c_str(), out_str.c_str(), out_str.size(), - false); - - blog(LOG_INFO, "Import Scene Collection: %s (%s) - %s", name.c_str(), fileName.c_str(), - success ? "SUCCESS" : "FAILURE"); - } - } - - close(); -} - -void OBSImporter::dataChanged() -{ - ui->tableView->resizeColumnToContents(ImporterColumn::Name); -} diff --git a/frontend/importer/ImporterModel.hpp b/frontend/importer/ImporterModel.hpp index 8e77db645..011ac83eb 100644 --- a/frontend/importer/ImporterModel.hpp +++ b/frontend/importer/ImporterModel.hpp @@ -17,36 +17,19 @@ #pragma once -#include "obs-app.hpp" -#include "window-basic-main.hpp" -#include -#include -#include -#include "ui_OBSImporter.h" +#include -class ImporterModel; +enum ImporterColumn { + Selected, + Name, + Path, + Program, -class OBSImporter : public QDialog { - Q_OBJECT - - QPointer optionsModel; - std::unique_ptr ui; - -public: - explicit OBSImporter(QWidget *parent = nullptr); - - void addImportOption(QString path, bool automatic); - -protected: - virtual void dropEvent(QDropEvent *ev) override; - virtual void dragEnterEvent(QDragEnterEvent *ev) override; - -public slots: - void browseImport(); - void importCollections(); - void dataChanged(); + Count }; +enum ImporterEntryRole { EntryStateRole = Qt::UserRole, NewPath, AutoPath, CheckEmpty }; + class ImporterModel : public QAbstractTableModel { Q_OBJECT @@ -76,27 +59,3 @@ private: void checkInputPath(int row); }; - -class ImporterEntryPathItemDelegate : public QStyledItemDelegate { - Q_OBJECT - -public: - ImporterEntryPathItemDelegate(); - - virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */, - const QModelIndex &index) const override; - - virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override; - virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; - virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const override; - -private: - const char *PATH_LIST_PROP = "pathList"; - - void handleBrowse(QWidget *container); - void handleClear(QWidget *container); - -private slots: - void updateText(); -}; diff --git a/frontend/importer/OBSImporter.cpp b/frontend/importer/OBSImporter.cpp index 88d4ad2d9..c33a095c6 100644 --- a/frontend/importer/OBSImporter.cpp +++ b/frontend/importer/OBSImporter.cpp @@ -15,360 +15,22 @@ along with this program. If not, see . ******************************************************************************/ -#include "moc_window-importer.cpp" +#include "OBSImporter.hpp" +#include "ImporterEntryPathItemDelegate.hpp" +#include "ImporterModel.hpp" -#include "obs-app.hpp" +#include +#include -#include -#include -#include -#include -#include -#include -#include #include -#include "importers/importers.hpp" - -extern bool SceneCollectionExists(const char *findName); - -enum ImporterColumn { - Selected, - Name, - Path, - Program, - - Count -}; - -enum ImporterEntryRole { EntryStateRole = Qt::UserRole, NewPath, AutoPath, CheckEmpty }; - -/********************************************************** - Delegate - Presents cells in the grid. -**********************************************************/ - -ImporterEntryPathItemDelegate::ImporterEntryPathItemDelegate() : QStyledItemDelegate() {} - -QWidget *ImporterEntryPathItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */, - const QModelIndex &index) const -{ - bool empty = index.model() - ->index(index.row(), ImporterColumn::Path) - .data(ImporterEntryRole::CheckEmpty) - .value(); - - QSizePolicy buttonSizePolicy(QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Expanding, - QSizePolicy::ControlType::PushButton); - - QWidget *container = new QWidget(parent); - - auto browseCallback = [this, container]() { - const_cast(this)->handleBrowse(container); - }; - - auto clearCallback = [this, container]() { - const_cast(this)->handleClear(container); - }; - - QHBoxLayout *layout = new QHBoxLayout(); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - - QLineEdit *text = new QLineEdit(); - text->setObjectName(QStringLiteral("text")); - text->setSizePolicy(QSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding, - QSizePolicy::ControlType::LineEdit)); - layout->addWidget(text); - - QObject::connect(text, &QLineEdit::editingFinished, this, &ImporterEntryPathItemDelegate::updateText); - - QToolButton *browseButton = new QToolButton(); - browseButton->setText("..."); - browseButton->setSizePolicy(buttonSizePolicy); - layout->addWidget(browseButton); - - container->connect(browseButton, &QToolButton::clicked, browseCallback); - - // The "clear" button is not shown in output cells - // or the insertion point's input cell. - if (!empty) { - QToolButton *clearButton = new QToolButton(); - clearButton->setText("X"); - clearButton->setSizePolicy(buttonSizePolicy); - layout->addWidget(clearButton); - - container->connect(clearButton, &QToolButton::clicked, clearCallback); - } - - container->setLayout(layout); - container->setFocusProxy(text); - return container; -} - -void ImporterEntryPathItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const -{ - QLineEdit *text = editor->findChild(); - text->setText(index.data().toString()); - editor->setProperty(PATH_LIST_PROP, QVariant()); -} - -void ImporterEntryPathItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const -{ - // We use the PATH_LIST_PROP property to pass a list of - // path strings from the editor widget into the model's - // NewPathsToProcessRole. This is only used when paths - // are selected through the "browse" or "delete" buttons - // in the editor. If the user enters new text in the - // text box, we simply pass that text on to the model - // as normal text data in the default role. - QVariant pathListProp = editor->property(PATH_LIST_PROP); - if (pathListProp.isValid()) { - QStringList list = editor->property(PATH_LIST_PROP).toStringList(); - model->setData(index, list, ImporterEntryRole::NewPath); - } else { - QLineEdit *lineEdit = editor->findChild(); - model->setData(index, lineEdit->text()); - } -} - -void ImporterEntryPathItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItem localOption = option; - initStyleOption(&localOption, index); - - QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &localOption, painter); -} - -void ImporterEntryPathItemDelegate::handleBrowse(QWidget *container) -{ - QString Pattern = "(*.json *.bpres *.xml *.xconfig)"; - - QLineEdit *text = container->findChild(); - - QString currentPath = text->text(); - - bool isSet = false; - QStringList paths = OpenFiles(container, QTStr("Importer.SelectCollection"), currentPath, - QTStr("Importer.Collection") + QString(" ") + Pattern); - - if (!paths.empty()) { - container->setProperty(PATH_LIST_PROP, paths); - isSet = true; - } - - if (isSet) - emit commitData(container); -} - -void ImporterEntryPathItemDelegate::handleClear(QWidget *container) -{ - // An empty string list will indicate that the entry is being - // blanked and should be deleted. - container->setProperty(PATH_LIST_PROP, QStringList()); - - emit commitData(container); -} - -void ImporterEntryPathItemDelegate::updateText() -{ - QLineEdit *lineEdit = dynamic_cast(sender()); - QWidget *editor = lineEdit->parentWidget(); - emit commitData(editor); -} - -/** - Model -**/ - -int ImporterModel::rowCount(const QModelIndex &) const -{ - return options.length() + 1; -} - -int ImporterModel::columnCount(const QModelIndex &) const -{ - return ImporterColumn::Count; -} - -QVariant ImporterModel::data(const QModelIndex &index, int role) const -{ - QVariant result = QVariant(); - - if (index.row() >= options.length()) { - if (role == ImporterEntryRole::CheckEmpty) - result = true; - else - return QVariant(); - } else if (role == Qt::DisplayRole) { - switch (index.column()) { - case ImporterColumn::Path: - result = options[index.row()].path; - break; - case ImporterColumn::Program: - result = options[index.row()].program; - break; - case ImporterColumn::Name: - result = options[index.row()].name; - } - } else if (role == Qt::EditRole) { - if (index.column() == ImporterColumn::Name) { - result = options[index.row()].name; - } - } else if (role == Qt::CheckStateRole) { - switch (index.column()) { - case ImporterColumn::Selected: - if (options[index.row()].program != "") - result = options[index.row()].selected ? Qt::Checked : Qt::Unchecked; - else - result = Qt::Unchecked; - } - } else if (role == ImporterEntryRole::CheckEmpty) { - result = options[index.row()].empty; - } - - return result; -} - -Qt::ItemFlags ImporterModel::flags(const QModelIndex &index) const -{ - Qt::ItemFlags flags = QAbstractTableModel::flags(index); - - if (index.column() == ImporterColumn::Selected && index.row() != options.length()) { - flags |= Qt::ItemIsUserCheckable; - } else if (index.column() == ImporterColumn::Path || - (index.column() == ImporterColumn::Name && index.row() != options.length())) { - flags |= Qt::ItemIsEditable; - } - - return flags; -} - -void ImporterModel::checkInputPath(int row) -{ - ImporterEntry &entry = options[row]; - - if (entry.path.isEmpty()) { - entry.program = ""; - entry.empty = true; - entry.selected = false; - entry.name = ""; - } else { - entry.empty = false; - - std::string program = DetectProgram(entry.path.toStdString()); - entry.program = QTStr(program.c_str()); - - if (program.empty()) { - entry.selected = false; - } else { - std::string name = GetSCName(entry.path.toStdString(), program); - entry.name = name.c_str(); - } - } - - emit dataChanged(index(row, 0), index(row, ImporterColumn::Count)); -} - -bool ImporterModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (role == ImporterEntryRole::NewPath) { - QStringList list = value.toStringList(); - - if (list.size() == 0) { - if (index.row() < options.size()) { - beginRemoveRows(QModelIndex(), index.row(), index.row()); - options.removeAt(index.row()); - endRemoveRows(); - } - } else { - if (list.size() > 0 && index.row() < options.length()) { - options[index.row()].path = list[0]; - checkInputPath(index.row()); - - list.removeAt(0); - } - - if (list.size() > 0) { - int row = index.row(); - int lastRow = row + list.size() - 1; - beginInsertRows(QModelIndex(), row, lastRow); - - for (QString path : list) { - ImporterEntry entry; - entry.path = path; - - options.insert(row, entry); - - row++; - } - - endInsertRows(); - - for (row = index.row(); row <= lastRow; row++) { - checkInputPath(row); - } - } - } - } else if (index.row() == options.length()) { - QString path = value.toString(); - - if (!path.isEmpty()) { - ImporterEntry entry; - entry.path = path; - entry.selected = role != ImporterEntryRole::AutoPath; - entry.empty = false; - - beginInsertRows(QModelIndex(), options.length() + 1, options.length() + 1); - options.append(entry); - endInsertRows(); - - checkInputPath(index.row()); - } - } else if (index.column() == ImporterColumn::Selected) { - bool select = value.toBool(); - - options[index.row()].selected = select; - } else if (index.column() == ImporterColumn::Path) { - QString path = value.toString(); - options[index.row()].path = path; - - checkInputPath(index.row()); - } else if (index.column() == ImporterColumn::Name) { - QString name = value.toString(); - options[index.row()].name = name; - } - - emit dataChanged(index, index); - - return true; -} - -QVariant ImporterModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - QVariant result = QVariant(); - - if (role == Qt::DisplayRole && orientation == Qt::Orientation::Horizontal) { - switch (section) { - case ImporterColumn::Path: - result = QTStr("Importer.Path"); - break; - case ImporterColumn::Program: - result = QTStr("Importer.Program"); - break; - case ImporterColumn::Name: - result = QTStr("Name"); - } - } - - return result; -} - -/** - Window -**/ +#include +#include +#include +#include +#include +#include "moc_OBSImporter.cpp" OBSImporter::OBSImporter(QWidget *parent) : QDialog(parent), optionsModel(new ImporterModel), ui(new Ui::OBSImporter) { setAcceptDrops(true); diff --git a/frontend/importer/OBSImporter.hpp b/frontend/importer/OBSImporter.hpp index 8e77db645..d9ab2bb6f 100644 --- a/frontend/importer/OBSImporter.hpp +++ b/frontend/importer/OBSImporter.hpp @@ -17,13 +17,11 @@ #pragma once -#include "obs-app.hpp" -#include "window-basic-main.hpp" -#include -#include -#include #include "ui_OBSImporter.h" +#include +#include + class ImporterModel; class OBSImporter : public QDialog { @@ -46,57 +44,3 @@ public slots: void importCollections(); void dataChanged(); }; - -class ImporterModel : public QAbstractTableModel { - Q_OBJECT - - friend class OBSImporter; - -public: - ImporterModel(QObject *parent = 0) : QAbstractTableModel(parent) {} - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - bool setData(const QModelIndex &index, const QVariant &value, int role); - -private: - struct ImporterEntry { - QString path; - QString program; - QString name; - - bool selected; - bool empty; - }; - - QList options; - - void checkInputPath(int row); -}; - -class ImporterEntryPathItemDelegate : public QStyledItemDelegate { - Q_OBJECT - -public: - ImporterEntryPathItemDelegate(); - - virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */, - const QModelIndex &index) const override; - - virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override; - virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; - virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const override; - -private: - const char *PATH_LIST_PROP = "pathList"; - - void handleBrowse(QWidget *container); - void handleClear(QWidget *container); - -private slots: - void updateText(); -};