jp9000 f53df7da64 clang-format: Apply formatting
Code submissions have continually suffered from formatting
inconsistencies that constantly have to be addressed.  Using
clang-format simplifies this by making code formatting more consistent,
and allows automation of the code formatting so that maintainers can
focus more on the code itself instead of code formatting.
2019-06-23 23:49:10 -07:00

567 lines
13 KiB
C++

#include "scripts.hpp"
#include "frontend-tools-config.h"
#include "../../properties-view.hpp"
#include <QFileDialog>
#include <QPlainTextEdit>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QScrollBar>
#include <QPushButton>
#include <QFontDatabase>
#include <QFont>
#include <QDialogButtonBox>
#include <QResizeEvent>
#include <QAction>
#include <obs.hpp>
#include <obs-module.h>
#include <obs-frontend-api.h>
#include <obs-scripting.h>
#include <util/config-file.h>
#include <util/platform.h>
#include <util/util.hpp>
#include <string>
#include "ui_scripts.h"
#if COMPILE_PYTHON && (defined(_WIN32) || defined(__APPLE__))
#define PYTHON_UI 1
#else
#define PYTHON_UI 0
#endif
#if ARCH_BITS == 64
#define ARCH_NAME "64bit"
#else
#define ARCH_NAME "32bit"
#endif
#define PYTHONPATH_LABEL_TEXT "PythonSettings.PythonInstallPath" ARCH_NAME
/* ----------------------------------------------------------------- */
using OBSScript = OBSObj<obs_script_t *, obs_script_destroy>;
struct ScriptData {
std::vector<OBSScript> scripts;
inline obs_script_t *FindScript(const char *path)
{
for (OBSScript &script : scripts) {
const char *script_path = obs_script_get_path(script);
if (strcmp(script_path, path) == 0) {
return script;
}
}
return nullptr;
}
bool ScriptOpened(const char *path)
{
for (OBSScript &script : scripts) {
const char *script_path = obs_script_get_path(script);
if (strcmp(script_path, path) == 0) {
return true;
}
}
return false;
}
};
static ScriptData *scriptData = nullptr;
static ScriptsTool *scriptsWindow = nullptr;
static ScriptLogWindow *scriptLogWindow = nullptr;
static QPlainTextEdit *scriptLogWidget = nullptr;
/* ----------------------------------------------------------------- */
ScriptLogWindow::ScriptLogWindow() : QWidget(nullptr)
{
const QFont fixedFont =
QFontDatabase::systemFont(QFontDatabase::FixedFont);
QPlainTextEdit *edit = new QPlainTextEdit();
edit->setReadOnly(true);
edit->setFont(fixedFont);
edit->setWordWrapMode(QTextOption::NoWrap);
QHBoxLayout *buttonLayout = new QHBoxLayout();
QPushButton *clearButton = new QPushButton(tr("Clear"));
connect(clearButton, &QPushButton::clicked, this,
&ScriptLogWindow::ClearWindow);
QPushButton *closeButton = new QPushButton(tr("Close"));
connect(closeButton, &QPushButton::clicked, this, &QDialog::hide);
buttonLayout->addStretch();
buttonLayout->addWidget(clearButton);
buttonLayout->addWidget(closeButton);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(edit);
layout->addLayout(buttonLayout);
setLayout(layout);
scriptLogWidget = edit;
resize(600, 400);
config_t *global_config = obs_frontend_get_global_config();
const char *geom =
config_get_string(global_config, "ScriptLogWindow", "geometry");
if (geom != nullptr) {
QByteArray ba = QByteArray::fromBase64(QByteArray(geom));
restoreGeometry(ba);
}
setWindowTitle(obs_module_text("ScriptLogWindow"));
connect(edit->verticalScrollBar(), &QAbstractSlider::sliderMoved, this,
&ScriptLogWindow::ScrollChanged);
}
ScriptLogWindow::~ScriptLogWindow()
{
config_t *global_config = obs_frontend_get_global_config();
config_set_string(global_config, "ScriptLogWindow", "geometry",
saveGeometry().toBase64().constData());
}
void ScriptLogWindow::ScrollChanged(int val)
{
QScrollBar *scroll = scriptLogWidget->verticalScrollBar();
bottomScrolled = (val == scroll->maximum());
}
void ScriptLogWindow::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
if (bottomScrolled) {
QScrollBar *scroll = scriptLogWidget->verticalScrollBar();
scroll->setValue(scroll->maximum());
}
}
void ScriptLogWindow::AddLogMsg(int log_level, QString msg)
{
QScrollBar *scroll = scriptLogWidget->verticalScrollBar();
bottomScrolled = scroll->value() == scroll->maximum();
lines += QStringLiteral("\n");
lines += msg;
scriptLogWidget->setPlainText(lines);
if (bottomScrolled)
scroll->setValue(scroll->maximum());
if (log_level <= LOG_WARNING) {
show();
raise();
}
}
void ScriptLogWindow::ClearWindow()
{
Clear();
scriptLogWidget->setPlainText(QString());
}
void ScriptLogWindow::Clear()
{
lines.clear();
}
/* ----------------------------------------------------------------- */
ScriptsTool::ScriptsTool() : QWidget(nullptr), ui(new Ui_ScriptsTool)
{
ui->setupUi(this);
RefreshLists();
#if PYTHON_UI
config_t *config = obs_frontend_get_global_config();
const char *path =
config_get_string(config, "Python", "Path" ARCH_NAME);
ui->pythonPath->setText(path);
ui->pythonPathLabel->setText(obs_module_text(PYTHONPATH_LABEL_TEXT));
#else
delete ui->pythonSettingsTab;
ui->pythonSettingsTab = nullptr;
#endif
delete propertiesView;
propertiesView = new QWidget();
propertiesView->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
ui->propertiesLayout->addWidget(propertiesView);
}
ScriptsTool::~ScriptsTool()
{
delete ui;
}
void ScriptsTool::RemoveScript(const char *path)
{
for (size_t i = 0; i < scriptData->scripts.size(); i++) {
OBSScript &script = scriptData->scripts[i];
const char *script_path = obs_script_get_path(script);
if (strcmp(script_path, path) == 0) {
scriptData->scripts.erase(scriptData->scripts.begin() +
i);
break;
}
}
}
void ScriptsTool::ReloadScript(const char *path)
{
for (OBSScript &script : scriptData->scripts) {
const char *script_path = obs_script_get_path(script);
if (strcmp(script_path, path) == 0) {
obs_script_reload(script);
OBSData settings = obs_data_create();
obs_data_release(settings);
obs_properties_t *prop =
obs_script_get_properties(script);
obs_properties_apply_settings(prop, settings);
obs_properties_destroy(prop);
break;
}
}
}
void ScriptsTool::RefreshLists()
{
ui->scripts->clear();
for (OBSScript &script : scriptData->scripts) {
const char *script_file = obs_script_get_file(script);
const char *script_path = obs_script_get_path(script);
QListWidgetItem *item = new QListWidgetItem(script_file);
item->setData(Qt::UserRole, QString(script_path));
ui->scripts->addItem(item);
}
}
void ScriptsTool::on_close_clicked()
{
close();
}
void ScriptsTool::on_addScripts_clicked()
{
const char **formats = obs_scripting_supported_formats();
const char **cur_format = formats;
QString extensions;
QString filter;
while (*cur_format) {
if (!extensions.isEmpty())
extensions += QStringLiteral(" ");
extensions += QStringLiteral("*.");
extensions += *cur_format;
cur_format++;
}
if (!extensions.isEmpty()) {
filter += obs_module_text("FileFilter.ScriptFiles");
filter += QStringLiteral(" (");
filter += extensions;
filter += QStringLiteral(")");
}
if (filter.isEmpty())
return;
static std::string lastBrowsedDir;
if (lastBrowsedDir.empty()) {
BPtr<char> baseScriptPath = obs_module_file("scripts");
lastBrowsedDir = baseScriptPath;
}
QFileDialog dlg(this, obs_module_text("AddScripts"));
dlg.setFileMode(QFileDialog::ExistingFiles);
dlg.setDirectory(QDir(lastBrowsedDir.c_str()));
dlg.setNameFilter(filter);
dlg.exec();
QStringList files = dlg.selectedFiles();
if (!files.count())
return;
lastBrowsedDir = dlg.directory().path().toUtf8().constData();
for (const QString &file : files) {
QByteArray pathBytes = file.toUtf8();
const char *path = pathBytes.constData();
if (scriptData->ScriptOpened(path)) {
continue;
}
obs_script_t *script = obs_script_create(path, NULL);
if (script) {
const char *script_file = obs_script_get_file(script);
scriptData->scripts.emplace_back(script);
QListWidgetItem *item =
new QListWidgetItem(script_file);
item->setData(Qt::UserRole, QString(file));
ui->scripts->addItem(item);
OBSData settings = obs_data_create();
obs_data_release(settings);
obs_properties_t *prop =
obs_script_get_properties(script);
obs_properties_apply_settings(prop, settings);
obs_properties_destroy(prop);
}
}
}
void ScriptsTool::on_removeScripts_clicked()
{
QList<QListWidgetItem *> items = ui->scripts->selectedItems();
for (QListWidgetItem *item : items)
RemoveScript(item->data(Qt::UserRole)
.toString()
.toUtf8()
.constData());
RefreshLists();
}
void ScriptsTool::on_reloadScripts_clicked()
{
QList<QListWidgetItem *> items = ui->scripts->selectedItems();
for (QListWidgetItem *item : items)
ReloadScript(item->data(Qt::UserRole)
.toString()
.toUtf8()
.constData());
on_scripts_currentRowChanged(ui->scripts->currentRow());
}
void ScriptsTool::on_scriptLog_clicked()
{
scriptLogWindow->show();
scriptLogWindow->raise();
}
void ScriptsTool::on_pythonPathBrowse_clicked()
{
QString curPath = ui->pythonPath->text();
QString newPath = QFileDialog::getExistingDirectory(
this, ui->pythonPathLabel->text(), curPath);
if (newPath.isEmpty())
return;
QByteArray array = newPath.toUtf8();
const char *path = array.constData();
config_t *config = obs_frontend_get_global_config();
config_set_string(config, "Python", "Path" ARCH_NAME, path);
ui->pythonPath->setText(newPath);
if (obs_scripting_python_loaded())
return;
if (!obs_scripting_load_python(path))
return;
for (OBSScript &script : scriptData->scripts) {
enum obs_script_lang lang = obs_script_get_lang(script);
if (lang == OBS_SCRIPT_LANG_PYTHON) {
obs_script_reload(script);
}
}
on_scripts_currentRowChanged(ui->scripts->currentRow());
}
void ScriptsTool::on_scripts_currentRowChanged(int row)
{
ui->propertiesLayout->removeWidget(propertiesView);
delete propertiesView;
if (row == -1) {
propertiesView = new QWidget();
propertiesView->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
ui->propertiesLayout->addWidget(propertiesView);
ui->description->setText(QString());
return;
}
QByteArray array =
ui->scripts->item(row)->data(Qt::UserRole).toString().toUtf8();
const char *path = array.constData();
obs_script_t *script = scriptData->FindScript(path);
if (!script) {
propertiesView = nullptr;
return;
}
OBSData settings = obs_script_get_settings(script);
obs_data_release(settings);
propertiesView = new OBSPropertiesView(
settings, script,
(PropertiesReloadCallback)obs_script_get_properties,
(PropertiesUpdateCallback)obs_script_update);
ui->propertiesLayout->addWidget(propertiesView);
ui->description->setText(obs_script_get_description(script));
}
/* ----------------------------------------------------------------- */
extern "C" void FreeScripts()
{
obs_scripting_unload();
}
static void obs_event(enum obs_frontend_event event, void *)
{
if (event == OBS_FRONTEND_EVENT_EXIT) {
delete scriptData;
delete scriptsWindow;
delete scriptLogWindow;
} else if (event == OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP) {
scriptLogWindow->hide();
scriptLogWindow->Clear();
delete scriptData;
scriptData = new ScriptData;
}
}
static void load_script_data(obs_data_t *load_data, bool, void *)
{
obs_data_array_t *array = obs_data_get_array(load_data, "scripts-tool");
delete scriptData;
scriptData = new ScriptData;
size_t size = obs_data_array_count(array);
for (size_t i = 0; i < size; i++) {
obs_data_t *obj = obs_data_array_item(array, i);
const char *path = obs_data_get_string(obj, "path");
obs_data_t *settings = obs_data_get_obj(obj, "settings");
obs_script_t *script = obs_script_create(path, settings);
if (script) {
scriptData->scripts.emplace_back(script);
}
obs_data_release(settings);
obs_data_release(obj);
}
if (scriptsWindow)
scriptsWindow->RefreshLists();
obs_data_array_release(array);
}
static void save_script_data(obs_data_t *save_data, bool saving, void *)
{
if (!saving)
return;
obs_data_array_t *array = obs_data_array_create();
for (OBSScript &script : scriptData->scripts) {
const char *script_path = obs_script_get_path(script);
obs_data_t *settings = obs_script_save(script);
obs_data_t *obj = obs_data_create();
obs_data_set_string(obj, "path", script_path);
obs_data_set_obj(obj, "settings", settings);
obs_data_array_push_back(array, obj);
obs_data_release(obj);
obs_data_release(settings);
}
obs_data_set_array(save_data, "scripts-tool", array);
obs_data_array_release(array);
}
static void script_log(void *, obs_script_t *script, int log_level,
const char *message)
{
QString qmsg;
if (script) {
qmsg = QStringLiteral("[%1] %2").arg(
obs_script_get_file(script), message);
} else {
qmsg = QStringLiteral("[Unknown Script] %1").arg(message);
}
QMetaObject::invokeMethod(scriptLogWindow, "AddLogMsg",
Q_ARG(int, log_level), Q_ARG(QString, qmsg));
}
extern "C" void InitScripts()
{
scriptLogWindow = new ScriptLogWindow();
obs_scripting_load();
obs_scripting_set_log_callback(script_log, nullptr);
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
obs_module_text("Scripts"));
#if PYTHON_UI
config_t *config = obs_frontend_get_global_config();
const char *python_path =
config_get_string(config, "Python", "Path" ARCH_NAME);
if (!obs_scripting_python_loaded() && python_path && *python_path)
obs_scripting_load_python(python_path);
#endif
scriptData = new ScriptData;
auto cb = []() {
obs_frontend_push_ui_translation(obs_module_get_string);
if (!scriptsWindow) {
scriptsWindow = new ScriptsTool();
scriptsWindow->show();
} else {
scriptsWindow->show();
scriptsWindow->raise();
}
obs_frontend_pop_ui_translation();
};
obs_frontend_add_save_callback(save_script_data, nullptr);
obs_frontend_add_preload_callback(load_script_data, nullptr);
obs_frontend_add_event_callback(obs_event, nullptr);
action->connect(action, &QAction::triggered, cb);
}