yolobs-studio/UI/window-basic-filters.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

980 lines
26 KiB
C++
Raw Normal View History

2016-02-23 23:16:51 +00:00
/******************************************************************************
Copyright (C) 2015 by Hugh Bailey <obs.jim@gmail.com>
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
(at your option) 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "window-namedialog.hpp"
#include "window-basic-main.hpp"
#include "window-basic-filters.hpp"
#include "display-helpers.hpp"
#include "qt-wrappers.hpp"
#include "visibility-item-widget.hpp"
#include "item-widget-helpers.hpp"
#include "obs-app.hpp"
#include <QMessageBox>
#include <QCloseEvent>
2017-06-29 19:01:10 +00:00
#include <vector>
2016-02-23 23:16:51 +00:00
#include <string>
#include <QMenu>
#include <QVariant>
using namespace std;
Q_DECLARE_METATYPE(OBSSource);
OBSBasicFilters::OBSBasicFilters(QWidget *parent, OBSSource source_)
2019-09-22 21:19:10 +00:00
: QDialog(parent),
ui(new Ui::OBSBasicFilters),
source(source_),
addSignal(obs_source_get_signal_handler(source), "filter_add",
OBSBasicFilters::OBSSourceFilterAdded, this),
removeSignal(obs_source_get_signal_handler(source), "filter_remove",
OBSBasicFilters::OBSSourceFilterRemoved, this),
reorderSignal(obs_source_get_signal_handler(source),
"reorder_filters", OBSBasicFilters::OBSSourceReordered,
this),
removeSourceSignal(obs_source_get_signal_handler(source), "remove",
OBSBasicFilters::SourceRemoved, this),
renameSourceSignal(obs_source_get_signal_handler(source), "rename",
OBSBasicFilters::SourceRenamed, this),
noPreviewMargin(13)
{
main = reinterpret_cast<OBSBasic *>(parent);
2016-02-23 23:16:51 +00:00
2020-03-25 08:07:22 +00:00
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
2016-02-23 23:16:51 +00:00
ui->setupUi(this);
UpdateFilters();
ui->asyncFilters->setItemDelegate(
2019-09-22 21:19:10 +00:00
new VisibilityItemDelegate(ui->asyncFilters));
2016-02-23 23:16:51 +00:00
ui->effectFilters->setItemDelegate(
2019-09-22 21:19:10 +00:00
new VisibilityItemDelegate(ui->effectFilters));
2016-02-23 23:16:51 +00:00
const char *name = obs_source_get_name(source);
setWindowTitle(QTStr("Basic.Filters.Title").arg(QT_UTF8(name)));
2019-07-27 12:47:10 +00:00
#ifndef QT_NO_SHORTCUT
2019-09-22 21:19:10 +00:00
ui->actionRemoveFilter->setShortcut(
QApplication::translate("OBSBasicFilters", "Del", nullptr));
2019-07-27 12:47:10 +00:00
#endif // QT_NO_SHORTCUT
addAction(ui->actionRemoveFilter);
addAction(ui->actionMoveUp);
addAction(ui->actionMoveDown);
2016-02-23 23:16:51 +00:00
installEventFilter(CreateShortcutFilter());
connect(ui->asyncFilters->itemDelegate(),
2019-09-22 21:19:10 +00:00
SIGNAL(closeEditor(QWidget *,
QAbstractItemDelegate::EndEditHint)),
this,
SLOT(AsyncFilterNameEdited(
QWidget *, QAbstractItemDelegate::EndEditHint)));
2016-02-23 23:16:51 +00:00
connect(ui->effectFilters->itemDelegate(),
2019-09-22 21:19:10 +00:00
SIGNAL(closeEditor(QWidget *,
QAbstractItemDelegate::EndEditHint)),
this,
SLOT(EffectFilterNameEdited(
QWidget *, QAbstractItemDelegate::EndEditHint)));
2016-02-23 23:16:51 +00:00
2018-02-19 19:54:37 +00:00
QPushButton *close = ui->buttonBox->button(QDialogButtonBox::Close);
connect(close, SIGNAL(clicked()), this, SLOT(close()));
close->setDefault(true);
2019-09-22 21:19:10 +00:00
ui->buttonBox->button(QDialogButtonBox::Reset)
->setText(QTStr("Defaults"));
2018-02-19 19:54:37 +00:00
connect(ui->buttonBox->button(QDialogButtonBox::Reset),
SIGNAL(clicked()), this, SLOT(ResetFilters()));
2018-12-16 16:14:58 +00:00
uint32_t caps = obs_source_get_output_flags(source);
2019-09-22 21:19:10 +00:00
bool audio = (caps & OBS_SOURCE_AUDIO) != 0;
2018-12-16 16:14:58 +00:00
bool audioOnly = (caps & OBS_SOURCE_VIDEO) == 0;
2019-09-22 21:19:10 +00:00
bool async = (caps & OBS_SOURCE_ASYNC) != 0;
2016-02-23 23:16:51 +00:00
if (!async && !audio) {
ui->asyncWidget->setVisible(false);
ui->separatorLine->setVisible(false);
}
if (audioOnly) {
ui->effectWidget->setVisible(false);
ui->separatorLine->setVisible(false);
}
if (audioOnly || (audio && !async))
ui->asyncLabel->setText(QTStr("Basic.Filters.AudioFilters"));
2019-09-22 21:19:10 +00:00
auto addDrawCallback = [this]() {
2016-02-23 23:16:51 +00:00
obs_display_add_draw_callback(ui->preview->GetDisplay(),
2019-09-22 21:19:10 +00:00
OBSBasicFilters::DrawPreview,
this);
2016-02-23 23:16:51 +00:00
};
2016-05-24 19:53:01 +00:00
enum obs_source_type type = obs_source_get_type(source);
bool drawable_type = type == OBS_SOURCE_TYPE_INPUT ||
2019-09-22 21:19:10 +00:00
type == OBS_SOURCE_TYPE_SCENE;
2016-05-24 19:53:01 +00:00
2018-12-16 16:14:58 +00:00
if ((caps & OBS_SOURCE_VIDEO) != 0) {
ui->rightLayout->setContentsMargins(0, 0, 0, 0);
ui->preview->show();
if (drawable_type)
connect(ui->preview, &OBSQTDisplay::DisplayCreated,
2019-09-22 21:19:10 +00:00
addDrawCallback);
2018-12-16 16:14:58 +00:00
} else {
ui->rightLayout->setContentsMargins(0, noPreviewMargin, 0, 0);
ui->rightContainerLayout->insertStretch(1);
ui->preview->hide();
}
2020-03-25 08:07:22 +00:00
QAction *renameAsync = new QAction(ui->asyncWidget);
renameAsync->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(renameAsync, SIGNAL(triggered()), this,
SLOT(RenameAsyncFilter()));
ui->asyncWidget->addAction(renameAsync);
QAction *renameEffect = new QAction(ui->effectWidget);
renameEffect->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(renameEffect, SIGNAL(triggered()), this,
SLOT(RenameEffectFilter()));
ui->effectWidget->addAction(renameEffect);
#ifdef __APPLE__
renameAsync->setShortcut({Qt::Key_Return});
renameEffect->setShortcut({Qt::Key_Return});
#else
renameAsync->setShortcut({Qt::Key_F2});
renameEffect->setShortcut({Qt::Key_F2});
#endif
2016-02-23 23:16:51 +00:00
}
OBSBasicFilters::~OBSBasicFilters()
{
ClearListItems(ui->asyncFilters);
ClearListItems(ui->effectFilters);
}
void OBSBasicFilters::Init()
{
show();
}
inline OBSSource OBSBasicFilters::GetFilter(int row, bool async)
{
if (row == -1)
return OBSSource();
QListWidget *list = async ? ui->asyncFilters : ui->effectFilters;
QListWidgetItem *item = list->item(row);
if (!item)
return OBSSource();
QVariant v = item->data(Qt::UserRole);
return v.value<OBSSource>();
}
void OBSBasicFilters::UpdatePropertiesView(int row, bool async)
{
2016-10-10 19:01:40 +00:00
if (view) {
2018-02-19 19:54:37 +00:00
updatePropertiesSignal.Disconnect();
2017-04-19 19:54:15 +00:00
ui->rightLayout->removeWidget(view);
2016-10-10 19:01:40 +00:00
view->deleteLater();
view = nullptr;
}
2016-02-23 23:16:51 +00:00
OBSSource filter = GetFilter(row, async);
if (!filter)
return;
obs_data_t *settings = obs_source_get_settings(filter);
2019-09-22 21:19:10 +00:00
view = new OBSPropertiesView(
settings, filter,
(PropertiesReloadCallback)obs_source_properties,
(PropertiesUpdateCallback)obs_source_update);
2016-02-23 23:16:51 +00:00
2018-02-19 19:54:37 +00:00
updatePropertiesSignal.Connect(obs_source_get_signal_handler(filter),
2019-09-22 21:19:10 +00:00
"update_properties",
OBSBasicFilters::UpdateProperties, this);
2018-02-19 19:54:37 +00:00
2016-02-23 23:16:51 +00:00
obs_data_release(settings);
view->setMaximumHeight(250);
view->setMinimumHeight(150);
ui->rightLayout->addWidget(view);
view->show();
}
2018-02-19 19:54:37 +00:00
void OBSBasicFilters::UpdateProperties(void *data, calldata_t *)
{
2019-09-22 21:19:10 +00:00
QMetaObject::invokeMethod(static_cast<OBSBasicFilters *>(data)->view,
"ReloadProperties");
2018-02-19 19:54:37 +00:00
}
2020-12-22 17:32:50 +00:00
void OBSBasicFilters::AddFilter(OBSSource filter, bool focus)
2016-02-23 23:16:51 +00:00
{
uint32_t flags = obs_source_get_output_flags(filter);
bool async = (flags & OBS_SOURCE_ASYNC) != 0;
QListWidget *list = async ? ui->asyncFilters : ui->effectFilters;
QListWidgetItem *item = new QListWidgetItem();
Qt::ItemFlags itemFlags = item->flags();
item->setFlags(itemFlags | Qt::ItemIsEditable);
item->setData(Qt::UserRole, QVariant::fromValue(filter));
list->addItem(item);
2020-12-22 17:32:50 +00:00
if (focus)
list->setCurrentItem(item);
2016-02-23 23:16:51 +00:00
SetupVisibilityItem(list, item, filter);
}
void OBSBasicFilters::RemoveFilter(OBSSource filter)
{
uint32_t flags = obs_source_get_output_flags(filter);
bool async = (flags & OBS_SOURCE_ASYNC) != 0;
QListWidget *list = async ? ui->asyncFilters : ui->effectFilters;
for (int i = 0; i < list->count(); i++) {
QListWidgetItem *item = list->item(i);
QVariant v = item->data(Qt::UserRole);
OBSSource curFilter = v.value<OBSSource>();
if (filter == curFilter) {
DeleteListItem(list, item);
break;
}
}
2016-08-28 12:07:43 +00:00
const char *filterName = obs_source_get_name(filter);
const char *sourceName = obs_source_get_name(source);
if (!sourceName || !filterName)
return;
const char *filterId = obs_source_get_id(filter);
blog(LOG_INFO, "User removed filter '%s' (%s) from source '%s'",
2019-09-22 21:19:10 +00:00
filterName, filterId, sourceName);
2016-08-28 12:07:43 +00:00
2016-02-23 23:16:51 +00:00
main->SaveProject();
}
struct FilterOrderInfo {
int asyncIdx = 0;
int effectIdx = 0;
OBSBasicFilters *window;
inline FilterOrderInfo(OBSBasicFilters *window_) : window(window_) {}
};
2019-09-22 21:19:10 +00:00
void OBSBasicFilters::ReorderFilter(QListWidget *list, obs_source_t *filter,
size_t idx)
2016-02-23 23:16:51 +00:00
{
int count = list->count();
for (int i = 0; i < count; i++) {
QListWidgetItem *listItem = list->item(i);
QVariant v = listItem->data(Qt::UserRole);
OBSSource filterItem = v.value<OBSSource>();
if (filterItem == filter) {
if ((int)idx != i) {
bool sel = (list->currentRow() == i);
listItem = TakeListItem(list, i);
2019-09-22 21:19:10 +00:00
if (listItem) {
2016-02-23 23:16:51 +00:00
list->insertItem((int)idx, listItem);
2019-09-22 21:19:10 +00:00
SetupVisibilityItem(list, listItem,
filterItem);
2016-02-23 23:16:51 +00:00
if (sel)
list->setCurrentRow((int)idx);
}
}
break;
}
}
}
void OBSBasicFilters::ReorderFilters()
{
FilterOrderInfo info(this);
2019-09-22 21:19:10 +00:00
obs_source_enum_filters(
source,
[](obs_source_t *, obs_source_t *filter, void *p) {
FilterOrderInfo *info =
reinterpret_cast<FilterOrderInfo *>(p);
uint32_t flags;
bool async;
flags = obs_source_get_output_flags(filter);
async = (flags & OBS_SOURCE_ASYNC) != 0;
if (async) {
info->window->ReorderFilter(
info->window->ui->asyncFilters, filter,
info->asyncIdx++);
} else {
info->window->ReorderFilter(
info->window->ui->effectFilters, filter,
info->effectIdx++);
}
},
&info);
2016-02-23 23:16:51 +00:00
}
void OBSBasicFilters::UpdateFilters()
{
if (!source)
return;
ClearListItems(ui->effectFilters);
ClearListItems(ui->asyncFilters);
2019-09-22 21:19:10 +00:00
obs_source_enum_filters(
source,
[](obs_source_t *, obs_source_t *filter, void *p) {
OBSBasicFilters *window =
reinterpret_cast<OBSBasicFilters *>(p);
2016-02-23 23:16:51 +00:00
2020-12-22 17:32:50 +00:00
window->AddFilter(filter, false);
2019-09-22 21:19:10 +00:00
},
this);
2016-02-23 23:16:51 +00:00
2020-12-22 17:32:50 +00:00
if (ui->asyncFilters->count() > 0) {
ui->asyncFilters->setCurrentItem(ui->asyncFilters->item(0));
} else if (ui->effectFilters->count() > 0) {
ui->effectFilters->setCurrentItem(ui->effectFilters->item(0));
}
2016-02-23 23:16:51 +00:00
main->SaveProject();
}
static bool filter_compatible(bool async, uint32_t sourceFlags,
2019-09-22 21:19:10 +00:00
uint32_t filterFlags)
2016-02-23 23:16:51 +00:00
{
bool filterVideo = (filterFlags & OBS_SOURCE_VIDEO) != 0;
bool filterAsync = (filterFlags & OBS_SOURCE_ASYNC) != 0;
bool filterAudio = (filterFlags & OBS_SOURCE_AUDIO) != 0;
2019-09-22 21:19:10 +00:00
bool audio = (sourceFlags & OBS_SOURCE_AUDIO) != 0;
bool audioOnly = (sourceFlags & OBS_SOURCE_VIDEO) == 0;
2016-02-23 23:16:51 +00:00
bool asyncSource = (sourceFlags & OBS_SOURCE_ASYNC) != 0;
if (async && ((audioOnly && filterVideo) || (!audio && !asyncSource)))
return false;
return (async && (filterAudio || filterAsync)) ||
2019-09-22 21:19:10 +00:00
(!async && !filterAudio && !filterAsync);
2016-02-23 23:16:51 +00:00
}
QMenu *OBSBasicFilters::CreateAddFilterPopupMenu(bool async)
{
uint32_t sourceFlags = obs_source_get_output_flags(source);
2017-06-29 19:01:10 +00:00
const char *type_str;
2016-02-23 23:16:51 +00:00
bool foundValues = false;
size_t idx = 0;
2017-06-29 19:01:10 +00:00
struct FilterInfo {
string type;
string name;
inline FilterInfo(const char *type_, const char *name_)
: type(type_), name(name_)
2019-09-22 21:19:10 +00:00
{
}
2017-06-29 19:01:10 +00:00
};
vector<FilterInfo> types;
while (obs_enum_filter_types(idx++, &type_str)) {
const char *name = obs_source_get_display_name(type_str);
2018-02-19 19:54:37 +00:00
uint32_t caps = obs_get_source_output_flags(type_str);
if ((caps & OBS_SOURCE_DEPRECATED) != 0)
continue;
if ((caps & OBS_SOURCE_CAP_DISABLED) != 0)
continue;
2020-10-01 20:15:25 +00:00
if ((caps & OBS_SOURCE_CAP_OBSOLETE) != 0)
continue;
2017-06-29 19:01:10 +00:00
auto it = types.begin();
for (; it != types.end(); ++it) {
if (it->name >= name)
break;
}
types.emplace(it, type_str, name);
}
2016-02-23 23:16:51 +00:00
QMenu *popup = new QMenu(QTStr("Add"), this);
2017-06-29 19:01:10 +00:00
for (FilterInfo &type : types) {
2019-09-22 21:19:10 +00:00
uint32_t filterFlags =
obs_get_source_output_flags(type.type.c_str());
2016-02-23 23:16:51 +00:00
if (!filter_compatible(async, sourceFlags, filterFlags))
continue;
2019-09-22 21:19:10 +00:00
QAction *popupItem =
new QAction(QT_UTF8(type.name.c_str()), this);
2017-06-29 19:01:10 +00:00
popupItem->setData(QT_UTF8(type.type.c_str()));
2019-09-22 21:19:10 +00:00
connect(popupItem, SIGNAL(triggered(bool)), this,
SLOT(AddFilterFromAction()));
2016-02-23 23:16:51 +00:00
popup->addAction(popupItem);
foundValues = true;
}
if (!foundValues) {
delete popup;
popup = nullptr;
}
return popup;
}
void OBSBasicFilters::AddNewFilter(const char *id)
{
if (id && *id) {
obs_source_t *existing_filter;
string name = obs_source_get_display_name(id);
2020-03-25 08:07:22 +00:00
QString placeholder = QString::fromStdString(name);
QString text{placeholder};
int i = 2;
while ((existing_filter = obs_source_get_filter_by_name(
source, QT_TO_UTF8(text)))) {
obs_source_release(existing_filter);
text = QString("%1 %2").arg(placeholder).arg(i++);
}
2019-09-22 21:19:10 +00:00
bool success = NameDialog::AskForName(
this, QTStr("Basic.Filters.AddFilter.Title"),
2020-03-25 08:07:22 +00:00
QTStr("Basic.Filters.AddFilter.Text"), name, text);
2016-02-23 23:16:51 +00:00
if (!success)
return;
if (name.empty()) {
2019-07-27 12:47:10 +00:00
OBSMessageBox::warning(this,
2019-09-22 21:19:10 +00:00
QTStr("NoNameEntered.Title"),
QTStr("NoNameEntered.Text"));
2016-02-23 23:16:51 +00:00
AddNewFilter(id);
return;
}
2019-09-22 21:19:10 +00:00
existing_filter =
obs_source_get_filter_by_name(source, name.c_str());
2016-02-23 23:16:51 +00:00
if (existing_filter) {
2019-09-22 21:19:10 +00:00
OBSMessageBox::warning(this, QTStr("NameExists.Title"),
QTStr("NameExists.Text"));
2016-02-23 23:16:51 +00:00
obs_source_release(existing_filter);
AddNewFilter(id);
return;
}
2019-09-22 21:19:10 +00:00
obs_source_t *filter =
obs_source_create(id, name.c_str(), nullptr, nullptr);
2016-02-23 23:16:51 +00:00
if (filter) {
2016-08-28 12:07:43 +00:00
const char *sourceName = obs_source_get_name(source);
2019-09-22 21:19:10 +00:00
blog(LOG_INFO,
"User added filter '%s' (%s) "
"to source '%s'",
name.c_str(), id, sourceName);
2016-08-28 12:07:43 +00:00
2016-02-23 23:16:51 +00:00
obs_source_filter_add(source, filter);
obs_source_release(filter);
}
}
}
void OBSBasicFilters::AddFilterFromAction()
{
2019-09-22 21:19:10 +00:00
QAction *action = qobject_cast<QAction *>(sender());
2016-02-23 23:16:51 +00:00
if (!action)
return;
AddNewFilter(QT_TO_UTF8(action->data().toString()));
}
void OBSBasicFilters::closeEvent(QCloseEvent *event)
{
QDialog::closeEvent(event);
if (!event->isAccepted())
return;
2019-09-22 21:19:10 +00:00
obs_display_remove_draw_callback(ui->preview->GetDisplay(),
OBSBasicFilters::DrawPreview, this);
2016-05-24 19:53:01 +00:00
2016-02-23 23:16:51 +00:00
main->SaveProject();
}
/* OBS Signals */
void OBSBasicFilters::OBSSourceFilterAdded(void *param, calldata_t *data)
{
2019-09-22 21:19:10 +00:00
OBSBasicFilters *window = reinterpret_cast<OBSBasicFilters *>(param);
obs_source_t *filter = (obs_source_t *)calldata_ptr(data, "filter");
2016-02-23 23:16:51 +00:00
QMetaObject::invokeMethod(window, "AddFilter",
2019-09-22 21:19:10 +00:00
Q_ARG(OBSSource, OBSSource(filter)));
2016-02-23 23:16:51 +00:00
}
void OBSBasicFilters::OBSSourceFilterRemoved(void *param, calldata_t *data)
{
2019-09-22 21:19:10 +00:00
OBSBasicFilters *window = reinterpret_cast<OBSBasicFilters *>(param);
obs_source_t *filter = (obs_source_t *)calldata_ptr(data, "filter");
2016-02-23 23:16:51 +00:00
QMetaObject::invokeMethod(window, "RemoveFilter",
2019-09-22 21:19:10 +00:00
Q_ARG(OBSSource, OBSSource(filter)));
2016-02-23 23:16:51 +00:00
}
void OBSBasicFilters::OBSSourceReordered(void *param, calldata_t *data)
{
2019-09-22 21:19:10 +00:00
QMetaObject::invokeMethod(reinterpret_cast<OBSBasicFilters *>(param),
"ReorderFilters");
2016-02-23 23:16:51 +00:00
UNUSED_PARAMETER(data);
}
2019-07-27 12:47:10 +00:00
void OBSBasicFilters::SourceRemoved(void *param, calldata_t *data)
2016-02-23 23:16:51 +00:00
{
2019-07-27 12:47:10 +00:00
UNUSED_PARAMETER(data);
2016-02-23 23:16:51 +00:00
2019-09-22 21:19:10 +00:00
QMetaObject::invokeMethod(static_cast<OBSBasicFilters *>(param),
"close");
2016-02-23 23:16:51 +00:00
}
2019-07-27 12:47:10 +00:00
void OBSBasicFilters::SourceRenamed(void *param, calldata_t *data)
2016-02-23 23:16:51 +00:00
{
2019-07-27 12:47:10 +00:00
const char *name = calldata_string(data, "new_name");
2016-02-23 23:16:51 +00:00
QString title = QTStr("Basic.Filters.Title").arg(QT_UTF8(name));
2019-09-22 21:19:10 +00:00
QMetaObject::invokeMethod(static_cast<OBSBasicFilters *>(param),
"setWindowTitle", Q_ARG(QString, title));
2016-02-23 23:16:51 +00:00
}
void OBSBasicFilters::DrawPreview(void *data, uint32_t cx, uint32_t cy)
{
2019-09-22 21:19:10 +00:00
OBSBasicFilters *window = static_cast<OBSBasicFilters *>(data);
2016-02-23 23:16:51 +00:00
if (!window->source)
return;
uint32_t sourceCX = max(obs_source_get_width(window->source), 1u);
uint32_t sourceCY = max(obs_source_get_height(window->source), 1u);
2019-09-22 21:19:10 +00:00
int x, y;
int newCX, newCY;
2016-02-23 23:16:51 +00:00
float scale;
GetScaleAndCenterPos(sourceCX, sourceCY, cx, cy, x, y, scale);
newCX = int(scale * float(sourceCX));
newCY = int(scale * float(sourceCY));
gs_viewport_push();
gs_projection_push();
gs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), -100.0f, 100.0f);
gs_set_viewport(x, y, newCX, newCY);
obs_source_video_render(window->source);
gs_projection_pop();
gs_viewport_pop();
}
/* Qt Slots */
static bool QueryRemove(QWidget *parent, obs_source_t *source)
{
2019-09-22 21:19:10 +00:00
const char *name = obs_source_get_name(source);
2016-02-23 23:16:51 +00:00
QString text = QTStr("ConfirmRemove.Text");
text.replace("$1", QT_UTF8(name));
QMessageBox remove_source(parent);
remove_source.setText(text);
2019-09-22 21:19:10 +00:00
QAbstractButton *Yes =
remove_source.addButton(QTStr("Yes"), QMessageBox::YesRole);
2016-02-23 23:16:51 +00:00
remove_source.addButton(QTStr("No"), QMessageBox::NoRole);
remove_source.setIcon(QMessageBox::Question);
remove_source.setWindowTitle(QTStr("ConfirmRemove.Title"));
remove_source.exec();
return Yes == remove_source.clickedButton();
}
void OBSBasicFilters::on_addAsyncFilter_clicked()
{
2018-12-16 16:14:58 +00:00
QScopedPointer<QMenu> popup(CreateAddFilterPopupMenu(true));
2016-02-23 23:16:51 +00:00
if (popup)
popup->exec(QCursor::pos());
}
void OBSBasicFilters::on_removeAsyncFilter_clicked()
{
OBSSource filter = GetFilter(ui->asyncFilters->currentRow(), true);
if (filter) {
if (QueryRemove(this, filter))
obs_source_filter_remove(source, filter);
}
}
void OBSBasicFilters::on_moveAsyncFilterUp_clicked()
{
OBSSource filter = GetFilter(ui->asyncFilters->currentRow(), true);
if (filter)
obs_source_filter_set_order(source, filter, OBS_ORDER_MOVE_UP);
}
void OBSBasicFilters::on_moveAsyncFilterDown_clicked()
{
OBSSource filter = GetFilter(ui->asyncFilters->currentRow(), true);
if (filter)
obs_source_filter_set_order(source, filter,
2019-09-22 21:19:10 +00:00
OBS_ORDER_MOVE_DOWN);
2016-02-23 23:16:51 +00:00
}
void OBSBasicFilters::on_asyncFilters_GotFocus()
{
UpdatePropertiesView(ui->asyncFilters->currentRow(), true);
2018-02-19 19:54:37 +00:00
isAsync = true;
2016-02-23 23:16:51 +00:00
}
void OBSBasicFilters::on_asyncFilters_currentRowChanged(int row)
{
UpdatePropertiesView(row, true);
}
void OBSBasicFilters::on_addEffectFilter_clicked()
{
2018-12-16 16:14:58 +00:00
QScopedPointer<QMenu> popup(CreateAddFilterPopupMenu(false));
2016-02-23 23:16:51 +00:00
if (popup)
popup->exec(QCursor::pos());
}
void OBSBasicFilters::on_removeEffectFilter_clicked()
{
OBSSource filter = GetFilter(ui->effectFilters->currentRow(), false);
if (filter) {
if (QueryRemove(this, filter))
obs_source_filter_remove(source, filter);
}
}
void OBSBasicFilters::on_moveEffectFilterUp_clicked()
{
OBSSource filter = GetFilter(ui->effectFilters->currentRow(), false);
if (filter)
obs_source_filter_set_order(source, filter, OBS_ORDER_MOVE_UP);
}
void OBSBasicFilters::on_moveEffectFilterDown_clicked()
{
OBSSource filter = GetFilter(ui->effectFilters->currentRow(), false);
if (filter)
obs_source_filter_set_order(source, filter,
2019-09-22 21:19:10 +00:00
OBS_ORDER_MOVE_DOWN);
2016-02-23 23:16:51 +00:00
}
void OBSBasicFilters::on_effectFilters_GotFocus()
{
UpdatePropertiesView(ui->effectFilters->currentRow(), false);
2018-02-19 19:54:37 +00:00
isAsync = false;
2016-02-23 23:16:51 +00:00
}
void OBSBasicFilters::on_effectFilters_currentRowChanged(int row)
{
UpdatePropertiesView(row, false);
}
2019-07-27 12:47:10 +00:00
void OBSBasicFilters::on_actionRemoveFilter_triggered()
{
if (ui->asyncFilters->hasFocus())
on_removeAsyncFilter_clicked();
else if (ui->effectFilters->hasFocus())
on_removeEffectFilter_clicked();
}
void OBSBasicFilters::on_actionMoveUp_triggered()
{
if (ui->asyncFilters->hasFocus())
on_moveAsyncFilterUp_clicked();
else if (ui->effectFilters->hasFocus())
on_moveEffectFilterUp_clicked();
}
void OBSBasicFilters::on_actionMoveDown_triggered()
{
if (ui->asyncFilters->hasFocus())
on_moveAsyncFilterDown_clicked();
else if (ui->effectFilters->hasFocus())
on_moveEffectFilterDown_clicked();
}
2016-02-23 23:16:51 +00:00
void OBSBasicFilters::CustomContextMenu(const QPoint &pos, bool async)
{
QListWidget *list = async ? ui->asyncFilters : ui->effectFilters;
QListWidgetItem *item = list->itemAt(pos);
QMenu popup(window());
QPointer<QMenu> addMenu = CreateAddFilterPopupMenu(async);
if (addMenu)
popup.addMenu(addMenu);
if (item) {
2020-12-22 17:32:50 +00:00
const char *dulpicateSlot =
async ? SLOT(DuplicateAsyncFilter())
: SLOT(DuplicateEffectFilter());
2019-09-22 21:19:10 +00:00
const char *renameSlot = async ? SLOT(RenameAsyncFilter())
: SLOT(RenameEffectFilter());
const char *removeSlot =
async ? SLOT(on_removeAsyncFilter_clicked())
: SLOT(on_removeEffectFilter_clicked());
2016-02-23 23:16:51 +00:00
2020-12-22 17:32:50 +00:00
popup.addSeparator();
popup.addAction(QTStr("Duplicate"), this, dulpicateSlot);
2016-02-23 23:16:51 +00:00
popup.addSeparator();
popup.addAction(QTStr("Rename"), this, renameSlot);
popup.addAction(QTStr("Remove"), this, removeSlot);
2020-12-22 17:32:50 +00:00
popup.addSeparator();
QAction *copyAction = new QAction(QTStr("Copy"));
connect(copyAction, SIGNAL(triggered()), this,
SLOT(CopyFilter()));
copyAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C));
ui->effectWidget->addAction(copyAction);
ui->asyncWidget->addAction(copyAction);
popup.addAction(copyAction);
2016-02-23 23:16:51 +00:00
}
2020-12-22 17:32:50 +00:00
QAction *pasteAction = new QAction(QTStr("Paste"));
pasteAction->setEnabled(main->copyFilter);
connect(pasteAction, SIGNAL(triggered()), this, SLOT(PasteFilter()));
pasteAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_V));
ui->effectWidget->addAction(pasteAction);
ui->asyncWidget->addAction(pasteAction);
popup.addAction(pasteAction);
2016-02-23 23:16:51 +00:00
popup.exec(QCursor::pos());
}
void OBSBasicFilters::EditItem(QListWidgetItem *item, bool async)
{
2020-03-25 08:07:22 +00:00
if (editActive)
return;
2016-02-23 23:16:51 +00:00
Qt::ItemFlags flags = item->flags();
2019-09-22 21:19:10 +00:00
OBSSource filter = item->data(Qt::UserRole).value<OBSSource>();
const char *name = obs_source_get_name(filter);
QListWidget *list = async ? ui->asyncFilters : ui->effectFilters;
2016-02-23 23:16:51 +00:00
item->setText(QT_UTF8(name));
item->setFlags(flags | Qt::ItemIsEditable);
list->removeItemWidget(item);
list->editItem(item);
item->setFlags(flags);
2020-03-25 08:07:22 +00:00
editActive = true;
2016-02-23 23:16:51 +00:00
}
2020-12-22 17:32:50 +00:00
void OBSBasicFilters::DuplicateItem(QListWidgetItem *item)
{
OBSSource filter = item->data(Qt::UserRole).value<OBSSource>();
string name = obs_source_get_name(filter);
obs_source_t *existing_filter;
QString placeholder = QString::fromStdString(name);
QString text{placeholder};
int i = 2;
while ((existing_filter = obs_source_get_filter_by_name(
source, QT_TO_UTF8(text)))) {
obs_source_release(existing_filter);
text = QString("%1 %2").arg(placeholder).arg(i++);
}
bool success = NameDialog::AskForName(
this, QTStr("Basic.Filters.AddFilter.Title"),
QTStr("Basic.Filters.AddFilter.Text"), name, text);
if (!success)
return;
if (name.empty()) {
OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"),
QTStr("NoNameEntered.Text"));
DuplicateItem(item);
return;
}
existing_filter = obs_source_get_filter_by_name(source, name.c_str());
if (existing_filter) {
OBSMessageBox::warning(this, QTStr("NameExists.Title"),
QTStr("NameExists.Text"));
obs_source_release(existing_filter);
DuplicateItem(item);
return;
}
bool enabled = obs_source_enabled(filter);
obs_source_t *new_filter =
obs_source_duplicate(filter, name.c_str(), false);
if (new_filter) {
const char *sourceName = obs_source_get_name(source);
const char *id = obs_source_get_id(new_filter);
blog(LOG_INFO,
"User duplicated filter '%s' (%s) from '%s' "
"to source '%s'",
name.c_str(), id, name.c_str(), sourceName);
obs_source_set_enabled(new_filter, enabled);
obs_source_filter_add(source, new_filter);
obs_source_release(new_filter);
}
}
2016-02-23 23:16:51 +00:00
void OBSBasicFilters::on_asyncFilters_customContextMenuRequested(
2019-09-22 21:19:10 +00:00
const QPoint &pos)
2016-02-23 23:16:51 +00:00
{
CustomContextMenu(pos, true);
}
void OBSBasicFilters::on_effectFilters_customContextMenuRequested(
2019-09-22 21:19:10 +00:00
const QPoint &pos)
2016-02-23 23:16:51 +00:00
{
CustomContextMenu(pos, false);
}
void OBSBasicFilters::RenameAsyncFilter()
{
EditItem(ui->asyncFilters->currentItem(), true);
}
void OBSBasicFilters::RenameEffectFilter()
{
EditItem(ui->effectFilters->currentItem(), false);
}
2020-12-22 17:32:50 +00:00
void OBSBasicFilters::DuplicateAsyncFilter()
{
DuplicateItem(ui->asyncFilters->currentItem());
}
void OBSBasicFilters::DuplicateEffectFilter()
{
DuplicateItem(ui->effectFilters->currentItem());
}
2016-02-23 23:16:51 +00:00
void OBSBasicFilters::FilterNameEdited(QWidget *editor, QListWidget *list)
{
QListWidgetItem *listItem = list->currentItem();
OBSSource filter = listItem->data(Qt::UserRole).value<OBSSource>();
2019-09-22 21:19:10 +00:00
QLineEdit *edit = qobject_cast<QLineEdit *>(editor);
2016-02-23 23:16:51 +00:00
string name = QT_TO_UTF8(edit->text().trimmed());
const char *prevName = obs_source_get_name(filter);
bool sameName = (name == prevName);
obs_source_t *foundFilter = nullptr;
if (!sameName)
2019-09-22 21:19:10 +00:00
foundFilter =
obs_source_get_filter_by_name(source, name.c_str());
2016-02-23 23:16:51 +00:00
if (foundFilter || name.empty() || sameName) {
listItem->setText(QT_UTF8(prevName));
if (foundFilter) {
2017-06-29 19:01:10 +00:00
OBSMessageBox::information(window(),
2019-09-22 21:19:10 +00:00
QTStr("NameExists.Title"),
QTStr("NameExists.Text"));
2016-02-23 23:16:51 +00:00
obs_source_release(foundFilter);
} else if (name.empty()) {
2017-06-29 19:01:10 +00:00
OBSMessageBox::information(window(),
2019-09-22 21:19:10 +00:00
QTStr("NoNameEntered.Title"),
QTStr("NoNameEntered.Text"));
2016-02-23 23:16:51 +00:00
}
} else {
2016-08-28 12:07:43 +00:00
const char *sourceName = obs_source_get_name(source);
2019-09-22 21:19:10 +00:00
blog(LOG_INFO,
"User renamed filter '%s' on source '%s' to '%s'",
prevName, sourceName, name.c_str());
2016-08-28 12:07:43 +00:00
2016-02-23 23:16:51 +00:00
listItem->setText(QT_UTF8(name.c_str()));
obs_source_set_name(filter, name.c_str());
}
listItem->setText(QString());
SetupVisibilityItem(list, listItem, filter);
2020-03-25 08:07:22 +00:00
editActive = false;
2016-02-23 23:16:51 +00:00
}
2019-09-22 21:19:10 +00:00
void OBSBasicFilters::AsyncFilterNameEdited(
QWidget *editor, QAbstractItemDelegate::EndEditHint endHint)
2016-02-23 23:16:51 +00:00
{
FilterNameEdited(editor, ui->asyncFilters);
UNUSED_PARAMETER(endHint);
}
2019-09-22 21:19:10 +00:00
void OBSBasicFilters::EffectFilterNameEdited(
QWidget *editor, QAbstractItemDelegate::EndEditHint endHint)
2016-02-23 23:16:51 +00:00
{
FilterNameEdited(editor, ui->effectFilters);
UNUSED_PARAMETER(endHint);
}
2018-02-19 19:54:37 +00:00
void OBSBasicFilters::ResetFilters()
{
QListWidget *list = isAsync ? ui->asyncFilters : ui->effectFilters;
int row = list->currentRow();
OBSSource filter = GetFilter(row, isAsync);
if (!filter)
return;
obs_data_t *settings = obs_source_get_settings(filter);
obs_data_clear(settings);
obs_data_release(settings);
if (!view->DeferUpdate())
obs_source_update(filter, nullptr);
view->RefreshProperties();
}
2020-12-22 17:32:50 +00:00
void OBSBasicFilters::CopyFilter()
{
OBSSource filter = nullptr;
if (isAsync)
filter = GetFilter(ui->asyncFilters->currentRow(), true);
else
filter = GetFilter(ui->effectFilters->currentRow(), false);
main->copyFilter = OBSGetWeakRef(filter);
}
void OBSBasicFilters::PasteFilter()
{
OBSSource filter = OBSGetStrongRef(main->copyFilter);
if (!filter)
return;
obs_source_copy_single_filter(source, filter);
}