diff --git a/src/assets/view/assetparameterview.cpp b/src/assets/view/assetparameterview.cpp index bd8b44de5..d2d507788 100644 --- a/src/assets/view/assetparameterview.cpp +++ b/src/assets/view/assetparameterview.cpp @@ -1,338 +1,337 @@ /*************************************************************************** * Copyright (C) 2017 by Nicolas Carion * * This file is part of Kdenlive. See www.kdenlive.org. * * * * 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) version 3 or any later version accepted by the * * membership of KDE e.V. (or its successor approved by the membership * * of KDE e.V.), which shall act as a proxy defined in Section 14 of * * version 3 of the license. * * * * 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 . * ***************************************************************************/ #include "assetparameterview.hpp" #include "assets/model/assetcommand.hpp" #include "assets/model/assetparametermodel.hpp" #include "assets/view/widgets/abstractparamwidget.hpp" #include "assets/view/widgets/keyframewidget.hpp" #include "core.h" #include #include #include #include #include #include #include #include #include #include AssetParameterView::AssetParameterView(QWidget *parent) : QWidget(parent) { m_lay = new QVBoxLayout(this); m_lay->setContentsMargins(0, 0, 0, 2); m_lay->setSpacing(0); setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); // Presets Combo m_presetMenu = new QMenu(this); - m_presetMenu->setToolTip(i18n("Presets")); } void AssetParameterView::setModel(const std::shared_ptr &model, QSize frameSize, bool addSpacer) { unsetModel(); QMutexLocker lock(&m_lock); m_model = model; const QString paramTag = model->getAssetId(); QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/effects/presets/")); const QString presetFile = dir.absoluteFilePath(QString("%1.json").arg(paramTag)); connect(this, &AssetParameterView::updatePresets, [this, presetFile](const QString &presetName) { m_presetMenu->clear(); m_presetGroup.reset(new QActionGroup(this)); m_presetGroup->setExclusive(true); m_presetMenu->addAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Reset Effect"), this, SLOT(resetValues())); // Save preset m_presetMenu->addAction(QIcon::fromTheme(QStringLiteral("document-save-as-template")), i18n("Save preset"), this, SLOT(slotSavePreset())); m_presetMenu->addAction(QIcon::fromTheme(QStringLiteral("document-save-as-template")), i18n("Update current preset"), this, SLOT(slotUpdatePreset())); m_presetMenu->addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete preset"), this, SLOT(slotDeletePreset())); m_presetMenu->addSeparator(); QStringList presets = m_model->getPresetList(presetFile); for (const QString &pName : presets) { QAction *ac = m_presetMenu->addAction(pName, this, SLOT(slotLoadPreset())); m_presetGroup->addAction(ac); ac->setData(pName); ac->setCheckable(true); if (pName == presetName) { ac->setChecked(true); } } }); emit updatePresets(); connect(m_model.get(), &AssetParameterModel::dataChanged, this, &AssetParameterView::refresh); if (paramTag.endsWith(QStringLiteral("lift_gamma_gain"))) { // Special case, the colorwheel widget manages several parameters QModelIndex index = model->index(0, 0); auto w = AbstractParamWidget::construct(model, index, frameSize, this); connect(w, &AbstractParamWidget::valueChanged, this, &AssetParameterView::commitChanges); m_lay->addWidget(w); m_widgets.push_back(w); } else { for (int i = 0; i < model->rowCount(); ++i) { QModelIndex index = model->index(i, 0); auto type = model->data(index, AssetParameterModel::TypeRole).value(); if (m_mainKeyframeWidget && (type == ParamType::Geometry || type == ParamType::Animated || type == ParamType::RestrictedAnim || type == ParamType::KeyframeParam)) { // Keyframe widget can have some extra params that shouldn't build a new widget qDebug() << "// FOUND ADDED PARAM"; m_mainKeyframeWidget->addParameter(index); } else { auto w = AbstractParamWidget::construct(model, index, frameSize, this); connect(this, &AssetParameterView::initKeyframeView, w, &AbstractParamWidget::slotInitMonitor); if (type == ParamType::KeyframeParam || type == ParamType::AnimatedRect || type == ParamType::Roto_spline) { m_mainKeyframeWidget = static_cast(w); } connect(w, &AbstractParamWidget::valueChanged, this, &AssetParameterView::commitChanges); connect(w, &AbstractParamWidget::seekToPos, this, &AssetParameterView::seekToPos); m_lay->addWidget(w); m_widgets.push_back(w); } } } if (addSpacer) { m_lay->addStretch(); } } QVector> AssetParameterView::getDefaultValues() const { QLocale locale; QVector> values; for (int i = 0; i < m_model->rowCount(); ++i) { QModelIndex index = m_model->index(i, 0); QString name = m_model->data(index, AssetParameterModel::NameRole).toString(); auto type = m_model->data(index, AssetParameterModel::TypeRole).value(); QVariant defaultValue = m_model->data(index, AssetParameterModel::DefaultRole); if (type == ParamType::KeyframeParam || type == ParamType::AnimatedRect) { QString val = type == ParamType::KeyframeParam ? locale.toString(defaultValue.toDouble()) : defaultValue.toString(); if (!val.contains(QLatin1Char('='))) { val.prepend(QStringLiteral("%1=").arg(m_model->data(index, AssetParameterModel::ParentInRole).toInt())); defaultValue = QVariant(val); } } values.append({name, defaultValue}); } return values; } void AssetParameterView::resetValues() { const QVector> values = getDefaultValues(); auto *command = new AssetUpdateCommand(m_model, values); pCore->pushUndo(command); /*if (m_mainKeyframeWidget) { m_mainKeyframeWidget->resetKeyframes(); }*/ } void AssetParameterView::commitChanges(const QModelIndex &index, const QString &value, bool storeUndo) { // Warning: please note that some widgets (for example keyframes) do NOT send the valueChanged signal and do modifications on their own auto *command = new AssetCommand(m_model, index, value); if (storeUndo) { pCore->pushUndo(command); } else { command->redo(); delete command; } } void AssetParameterView::unsetModel() { QMutexLocker lock(&m_lock); if (m_model) { // if a model is already there, we have to disconnect signals first disconnect(m_model.get(), &AssetParameterModel::dataChanged, this, &AssetParameterView::refresh); } m_mainKeyframeWidget = nullptr; // clear layout m_widgets.clear(); QLayoutItem *child; while ((child = m_lay->takeAt(0)) != nullptr) { if (child->layout()) { QLayoutItem *subchild; while ((subchild = child->layout()->takeAt(0)) != nullptr) { delete subchild->widget(); delete subchild->spacerItem(); } } delete child->widget(); delete child->spacerItem(); } // Release ownership of smart pointer m_model.reset(); } void AssetParameterView::refresh(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { QMutexLocker lock(&m_lock); if (m_widgets.size() == 0) { // no visible param for this asset, abort return; } Q_UNUSED(roles); // We are expecting indexes that are children of the root index, which is "invalid" Q_ASSERT(!topLeft.parent().isValid()); // We make sure the range is valid if (m_mainKeyframeWidget) { m_mainKeyframeWidget->slotRefresh(); } else { auto type = m_model->data(m_model->index(topLeft.row(), 0), AssetParameterModel::TypeRole).value(); if (type == ParamType::ColorWheel) { // Some special widgets, like colorwheel handle multiple params so we can have cases where param index row is greater than the number of widgets. // Should be better managed m_widgets[0]->slotRefresh(); return; } size_t max; if (!bottomRight.isValid()) { max = m_widgets.size() - 1; } else { max = (size_t)bottomRight.row(); } Q_ASSERT(max < (int)m_widgets.size()); for (auto i = (size_t)topLeft.row(); i <= max; ++i) { m_widgets[i]->slotRefresh(); } } } int AssetParameterView::contentHeight() const { return m_lay->minimumSize().height(); } MonitorSceneType AssetParameterView::needsMonitorEffectScene() const { if (m_mainKeyframeWidget) { return m_mainKeyframeWidget->requiredScene(); } for (int i = 0; i < m_model->rowCount(); ++i) { QModelIndex index = m_model->index(i, 0); auto type = m_model->data(index, AssetParameterModel::TypeRole).value(); if (type == ParamType::Geometry) { return MonitorSceneGeometry; } } return MonitorSceneDefault; } /*void AssetParameterView::initKeyframeView() { if (m_mainKeyframeWidget) { m_mainKeyframeWidget->initMonitor(); } else { for (int i = 0; i < m_model->rowCount(); ++i) { QModelIndex index = m_model->index(i, 0); auto type = m_model->data(index, AssetParameterModel::TypeRole).value(); if (type == ParamType::Geometry) { return MonitorSceneGeometry; } } } }*/ void AssetParameterView::slotRefresh() { refresh(m_model->index(0, 0), m_model->index(m_model->rowCount() - 1, 0), {}); } bool AssetParameterView::keyframesAllowed() const { return m_mainKeyframeWidget != nullptr; } bool AssetParameterView::modelHideKeyframes() const { return m_mainKeyframeWidget != nullptr && !m_mainKeyframeWidget->keyframesVisible(); } void AssetParameterView::toggleKeyframes(bool enable) { if (m_mainKeyframeWidget) { m_mainKeyframeWidget->showKeyframes(enable); } } void AssetParameterView::slotDeletePreset() { QAction *ac = m_presetGroup->checkedAction(); if (!ac) { return; } QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/effects/presets/")); if (!dir.exists()) { dir.mkpath(QStringLiteral(".")); } const QString presetFile = dir.absoluteFilePath(QString("%1.json").arg(m_model->getAssetId())); m_model->deletePreset(presetFile, ac->data().toString()); emit updatePresets(); } void AssetParameterView::slotUpdatePreset() { QAction *ac = m_presetGroup->checkedAction(); if (!ac) { return; } slotSavePreset(ac->data().toString()); } void AssetParameterView::slotSavePreset(QString presetName) { if (presetName.isEmpty()) { bool ok; presetName = QInputDialog::getText(this, i18n("Enter preset name"), i18n("Enter the name of this preset"), QLineEdit::Normal, QString(), &ok); if (!ok) return; } QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/effects/presets/")); if (!dir.exists()) { dir.mkpath(QStringLiteral(".")); } const QString presetFile = dir.absoluteFilePath(QString("%1.json").arg(m_model->getAssetId())); m_model->savePreset(presetFile, presetName); emit updatePresets(presetName); } void AssetParameterView::slotLoadPreset() { auto *action = qobject_cast(sender()); if (!action) { return; } const QString presetName = action->data().toString(); QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/effects/presets/")); const QString presetFile = dir.absoluteFilePath(QString("%1.json").arg(m_model->getAssetId())); const QVector> params = m_model->loadPreset(presetFile, presetName); auto *command = new AssetUpdateCommand(m_model, params); pCore->pushUndo(command); } QMenu *AssetParameterView::presetMenu() { return m_presetMenu; } diff --git a/src/effects/effectstack/view/collapsibleeffectview.cpp b/src/effects/effectstack/view/collapsibleeffectview.cpp index 3d296708a..60e0ec4de 100644 --- a/src/effects/effectstack/view/collapsibleeffectview.cpp +++ b/src/effects/effectstack/view/collapsibleeffectview.cpp @@ -1,806 +1,807 @@ /*************************************************************************** * Copyright (C) 2017 by Jean-Baptiste Mardelle (jb@kdenlive.org) * * This file is part of Kdenlive. See www.kdenlive.org. * * * * 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) version 3 or any later version accepted by the * * membership of KDE e.V. (or its successor approved by the membership * * of KDE e.V.), which shall act as a proxy defined in Section 14 of * * version 3 of the license. * * * * 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 . * ***************************************************************************/ #include "collapsibleeffectview.hpp" #include "assets/view/assetparameterview.hpp" #include "core.h" #include "dialogs/clipcreationdialog.h" #include "effects/effectsrepository.hpp" #include "effects/effectstack/model/effectitemmodel.hpp" #include "kdenlivesettings.h" #include "monitor/monitor.h" #include "kdenlive_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include CollapsibleEffectView::CollapsibleEffectView(const std::shared_ptr &effectModel, QSize frameSize, const QImage &icon, QWidget *parent) : AbstractCollapsibleWidget(parent) , m_view(nullptr) , m_model(effectModel) , m_regionEffect(false) { QString effectId = effectModel->getAssetId(); QString effectName = EffectsRepository::get()->getName(effectId); if (effectId == QLatin1String("region")) { m_regionEffect = true; decoframe->setObjectName(QStringLiteral("decoframegroup")); } filterWheelEvent = true; setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum); // decoframe->setProperty("active", true); // m_info.fromString(effect.attribute(QStringLiteral("kdenlive_info"))); // setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); buttonUp->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-up"))); buttonUp->setToolTip(i18n("Move effect up")); buttonDown->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-down"))); buttonDown->setToolTip(i18n("Move effect down")); buttonDel->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-deleffect"))); buttonDel->setToolTip(i18n("Delete effect")); // buttonUp->setEnabled(canMoveUp); // buttonDown->setEnabled(!lastEffect); if (effectId == QLatin1String("speed")) { // Speed effect is a "pseudo" effect, cannot be moved buttonUp->setVisible(false); buttonDown->setVisible(false); m_isMovable = false; setAcceptDrops(false); } else { setAcceptDrops(true); } // checkAll->setToolTip(i18n("Enable/Disable all effects")); // buttonShowComments->setIcon(QIcon::fromTheme("help-about")); // buttonShowComments->setToolTip(i18n("Show additional information for the parameters")); m_collapse = new KDualAction(i18n("Collapse Effect"), i18n("Expand Effect"), this); m_collapse->setActiveIcon(QIcon::fromTheme(QStringLiteral("arrow-right"))); collapseButton->setDefaultAction(m_collapse); connect(m_collapse, &KDualAction::activeChanged, this, &CollapsibleEffectView::slotSwitch); if (effectModel->rowCount() == 0) { // Effect has no paramerter m_collapse->setInactiveIcon(QIcon::fromTheme(QStringLiteral("tools-wizard"))); collapseButton->setEnabled(false); } else { m_collapse->setInactiveIcon(QIcon::fromTheme(QStringLiteral("arrow-down"))); } auto *l = static_cast(frame->layout()); m_colorIcon = new QLabel(this); l->insertWidget(0, m_colorIcon); m_colorIcon->setFixedSize(icon.size()); title = new QLabel(this); l->insertWidget(2, title); m_keyframesButton = new QToolButton(this); m_keyframesButton->setIcon(QIcon::fromTheme(QStringLiteral("adjustcurves"))); m_keyframesButton->setAutoRaise(true); m_keyframesButton->setCheckable(true); m_keyframesButton->setToolTip(i18n("Enable Keyframes")); l->insertWidget(3, m_keyframesButton); // Enable button m_enabledButton = new KDualAction(i18n("Disable Effect"), i18n("Enable Effect"), this); m_enabledButton->setActiveIcon(QIcon::fromTheme(QStringLiteral("hint"))); m_enabledButton->setInactiveIcon(QIcon::fromTheme(QStringLiteral("visibility"))); enabledButton->setDefaultAction(m_enabledButton); connect(m_model.get(), &AssetParameterModel::enabledChange, this, &CollapsibleEffectView::enableView); m_groupAction = new QAction(QIcon::fromTheme(QStringLiteral("folder-new")), i18n("Create Group"), this); connect(m_groupAction, &QAction::triggered, this, &CollapsibleEffectView::slotCreateGroup); if (m_regionEffect) { effectName.append(':' + QUrl(Xml::getXmlParameter(m_effect, QStringLiteral("resource"))).fileName()); } // Color thumb m_colorIcon->setPixmap(QPixmap::fromImage(icon)); title->setText(effectName); m_view = new AssetParameterView(this); const std::shared_ptr effectParamModel = std::static_pointer_cast(effectModel); m_view->setModel(effectParamModel, frameSize); connect(m_view, &AssetParameterView::seekToPos, this, &AbstractCollapsibleWidget::seekToPos); connect(this, &CollapsibleEffectView::refresh, m_view, &AssetParameterView::slotRefresh); m_keyframesButton->setVisible(m_view->keyframesAllowed()); auto *lay = new QVBoxLayout(widgetFrame); lay->setContentsMargins(0, 0, 0, 2); lay->setSpacing(0); connect(m_keyframesButton, &QToolButton::toggled, [this](bool toggle) { m_view->toggleKeyframes(toggle); // We need to switch twice to get a correct resize slotSwitch(!m_model->isCollapsed()); slotSwitch(!m_model->isCollapsed()); }); lay->addWidget(m_view); if (!effectParamModel->hasMoreThanOneKeyframe()) { // No keyframe or only one, allow hiding bool hideByDefault = effectParamModel->data(effectParamModel->index(0, 0), AssetParameterModel::HideKeyframesFirstRole).toBool(); if (hideByDefault) { m_view->toggleKeyframes(false); } else { m_keyframesButton->setChecked(true); } } else { m_keyframesButton->setChecked(true); } // Presets - presetButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new-from-template"))); + presetButton->setIcon(QIcon::fromTheme(QStringLiteral("adjustlevels"))); presetButton->setMenu(m_view->presetMenu()); + presetButton->setToolTip(i18n("Presets")); // Main menu m_menu = new QMenu(this); if (effectModel->rowCount() == 0) { collapseButton->setEnabled(false); m_view->setVisible(false); } m_menu->addAction(QIcon::fromTheme(QStringLiteral("document-save")), i18n("Save Effect"), this, SLOT(slotSaveEffect())); if (!m_regionEffect) { /*if (m_info.groupIndex == -1) { m_menu->addAction(m_groupAction); }*/ m_menu->addAction(QIcon::fromTheme(QStringLiteral("folder-new")), i18n("Create Region"), this, SLOT(slotCreateRegion())); } // setupWidget(info, metaInfo); menuButton->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-menu"))); menuButton->setMenu(m_menu); if (!effectModel->isEnabled()) { title->setEnabled(false); m_colorIcon->setEnabled(false); if (KdenliveSettings::disable_effect_parameters()) { widgetFrame->setEnabled(false); } m_enabledButton->setActive(true); } else { m_enabledButton->setActive(false); } connect(m_enabledButton, &KDualAction::activeChangedByUser, this, &CollapsibleEffectView::slotDisable); connect(buttonUp, &QAbstractButton::clicked, this, &CollapsibleEffectView::slotEffectUp); connect(buttonDown, &QAbstractButton::clicked, this, &CollapsibleEffectView::slotEffectDown); connect(buttonDel, &QAbstractButton::clicked, this, &CollapsibleEffectView::slotDeleteEffect); Q_FOREACH (QSpinBox *sp, findChildren()) { sp->installEventFilter(this); sp->setFocusPolicy(Qt::StrongFocus); } Q_FOREACH (KComboBox *cb, findChildren()) { cb->installEventFilter(this); cb->setFocusPolicy(Qt::StrongFocus); } Q_FOREACH (QProgressBar *cb, findChildren()) { cb->installEventFilter(this); cb->setFocusPolicy(Qt::StrongFocus); } m_collapse->setActive(m_model->isCollapsed()); slotSwitch(m_model->isCollapsed()); } CollapsibleEffectView::~CollapsibleEffectView() { qDebug() << "deleting collapsibleeffectview"; } void CollapsibleEffectView::setWidgetHeight(qreal value) { widgetFrame->setFixedHeight(m_view->contentHeight() * value); } void CollapsibleEffectView::slotCreateGroup() { emit createGroup(m_model); } void CollapsibleEffectView::slotCreateRegion() { QString allExtensions = ClipCreationDialog::getExtensions().join(QLatin1Char(' ')); const QString dialogFilter = allExtensions + QLatin1Char(' ') + QLatin1Char('|') + i18n("All Supported Files") + QStringLiteral("\n* ") + QLatin1Char('|') + i18n("All Files"); QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder")); if (clipFolder.isEmpty()) { clipFolder = QDir::homePath(); } QPointer d = new QFileDialog(QApplication::activeWindow(), QString(), clipFolder, dialogFilter); d->setFileMode(QFileDialog::ExistingFile); if (d->exec() == QDialog::Accepted && !d->selectedUrls().isEmpty()) { KRecentDirs::add(QStringLiteral(":KdenliveClipFolder"), d->selectedUrls().first().adjusted(QUrl::RemoveFilename).toLocalFile()); emit createRegion(effectIndex(), d->selectedUrls().first()); } delete d; } void CollapsibleEffectView::slotUnGroup() { emit unGroup(this); } bool CollapsibleEffectView::eventFilter(QObject *o, QEvent *e) { if (e->type() == QEvent::Enter) { frame->setProperty("mouseover", true); frame->setStyleSheet(frame->styleSheet()); return QWidget::eventFilter(o, e); } if (e->type() == QEvent::Wheel) { auto *we = static_cast(e); if (!filterWheelEvent || we->modifiers() != Qt::NoModifier) { e->accept(); return false; } if (qobject_cast(o)) { // if (qobject_cast(o)->focusPolicy() == Qt::WheelFocus) { e->accept(); return false; } if (qobject_cast(o)) { if (qobject_cast(o)->focusPolicy() == Qt::WheelFocus) { e->accept(); return false; } e->ignore(); return true; } if (qobject_cast(o)) { // if (qobject_cast(o)->focusPolicy() == Qt::WheelFocus)*/ { e->accept(); return false; } } return QWidget::eventFilter(o, e); } QDomElement CollapsibleEffectView::effect() const { return m_effect; } QDomElement CollapsibleEffectView::effectForSave() const { QDomElement effect = m_effect.cloneNode().toElement(); effect.removeAttribute(QStringLiteral("kdenlive_ix")); /* if (m_paramWidget) { int in = m_paramWidget->range().x(); EffectsController::offsetKeyframes(in, effect); } */ return effect; } bool CollapsibleEffectView::isActive() const { return decoframe->property("active").toBool(); } bool CollapsibleEffectView::isEnabled() const { return m_enabledButton->isActive(); } void CollapsibleEffectView::slotActivateEffect(QModelIndex ix) { // m_colorIcon->setEnabled(active); bool active = ix.row() == m_model->row(); decoframe->setProperty("active", active); decoframe->setStyleSheet(decoframe->styleSheet()); if (active) { pCore->getMonitor(m_model->monitorId)->slotShowEffectScene(needsMonitorEffectScene()); } m_view->initKeyframeView(active); } void CollapsibleEffectView::mousePressEvent(QMouseEvent *e) { m_dragStart = e->globalPos(); emit activateEffect(m_model); QWidget::mousePressEvent(e); } void CollapsibleEffectView::mouseMoveEvent(QMouseEvent *e) { if ((e->globalPos() - m_dragStart).manhattanLength() < QApplication::startDragDistance()) { QPixmap pix = frame->grab(); emit startDrag(pix, m_model); } QWidget::mouseMoveEvent(e); } void CollapsibleEffectView::mouseDoubleClickEvent(QMouseEvent *event) { if (frame->underMouse() && collapseButton->isEnabled()) { event->accept(); m_collapse->setActive(!m_collapse->isActive()); } else { event->ignore(); } } void CollapsibleEffectView::mouseReleaseEvent(QMouseEvent *event) { m_dragStart = QPoint(); if (!decoframe->property("active").toBool()) { // emit activateEffect(effectIndex()); } QWidget::mouseReleaseEvent(event); } void CollapsibleEffectView::slotDisable(bool disable) { QString effectId = m_model->getAssetId(); QString effectName = EffectsRepository::get()->getName(effectId); std::static_pointer_cast(m_model)->markEnabled(effectName, !disable); } void CollapsibleEffectView::slotDeleteEffect() { emit deleteEffect(m_model); } void CollapsibleEffectView::slotEffectUp() { emit moveEffect(qMax(0, m_model->row() - 1), m_model); } void CollapsibleEffectView::slotEffectDown() { emit moveEffect(m_model->row() + 2, m_model); } void CollapsibleEffectView::slotSaveEffect() { QString name = QInputDialog::getText(this, i18n("Save Effect"), i18n("Name for saved effect: ")); if (name.trimmed().isEmpty()) { return; } QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/effects/")); if (!dir.exists()) { dir.mkpath(QStringLiteral(".")); } if (dir.exists(name + QStringLiteral(".xml"))) if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", name + QStringLiteral(".xml"))) == KMessageBox::No) { return; } QDomDocument doc; // Get base effect xml QString effectId = m_model->getAssetId(); QDomElement effect = EffectsRepository::get()->getXml(effectId); // Adjust param values QVector> currentValues = m_model->getAllParameters(); QMap values; QLocale locale; for (const auto ¶m : currentValues) { if (param.second.type() == QVariant::Double) { values.insert(param.first, locale.toString(param.second.toDouble())); } else { values.insert(param.first, param.second.toString()); } } QDomNodeList params = effect.elementsByTagName("parameter"); for (int i = 0; i < params.count(); ++i) { const QString paramName = params.item(i).toElement().attribute("name"); const QString paramType = params.item(i).toElement().attribute("type"); if (paramType == QLatin1String("fixed") || !values.contains(paramName)) { continue; } params.item(i).toElement().setAttribute(QStringLiteral("value"), values.value(paramName)); } doc.appendChild(doc.importNode(effect, true)); effect = doc.firstChild().toElement(); effect.removeAttribute(QStringLiteral("kdenlive_ix")); effect.setAttribute(QStringLiteral("id"), name); effect.setAttribute(QStringLiteral("type"), QStringLiteral("custom")); /* if (m_paramWidget) { int in = m_paramWidget->range().x(); EffectsController::offsetKeyframes(in, effect); } */ QDomElement effectname = effect.firstChildElement(QStringLiteral("name")); effect.removeChild(effectname); effectname = doc.createElement(QStringLiteral("name")); QDomText nametext = doc.createTextNode(name); effectname.appendChild(nametext); effect.insertBefore(effectname, QDomNode()); QDomElement effectprops = effect.firstChildElement(QStringLiteral("properties")); effectprops.setAttribute(QStringLiteral("id"), name); effectprops.setAttribute(QStringLiteral("type"), QStringLiteral("custom")); QFile file(dir.absoluteFilePath(name + QStringLiteral(".xml"))); if (file.open(QFile::WriteOnly | QFile::Truncate)) { QTextStream out(&file); out << doc.toString(); } file.close(); emit reloadEffect(dir.absoluteFilePath(name + QStringLiteral(".xml"))); } void CollapsibleEffectView::slotResetEffect() { m_view->resetValues(); } void CollapsibleEffectView::slotSwitch(bool collapse) { widgetFrame->setFixedHeight(collapse ? 0 : m_view->sizeHint().height()); setFixedHeight(widgetFrame->height() + frame->height() + (2 * decoframe->lineWidth())); // m_view->setVisible(!collapse); emit switchHeight(m_model, height()); m_model->setCollapsed(collapse); } void CollapsibleEffectView::animationChanged(const QVariant &geom) { parentWidget()->setFixedHeight(geom.toRect().height()); } void CollapsibleEffectView::animationFinished() { if (m_collapse->isActive()) { widgetFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); } else { widgetFrame->setFixedHeight(m_view->contentHeight()); } } void CollapsibleEffectView::setGroupIndex(int ix) { Q_UNUSED(ix) /*if (m_info.groupIndex == -1 && ix != -1) { m_menu->removeAction(m_groupAction); } else if (m_info.groupIndex != -1 && ix == -1) { m_menu->addAction(m_groupAction); } m_info.groupIndex = ix; m_effect.setAttribute(QStringLiteral("kdenlive_info"), m_info.toString());*/ } void CollapsibleEffectView::setGroupName(const QString &groupName){ Q_UNUSED(groupName) /*m_info.groupName = groupName; m_effect.setAttribute(QStringLiteral("kdenlive_info"), m_info.toString());*/ } QString CollapsibleEffectView::infoString() const { return QString(); // m_info.toString(); } void CollapsibleEffectView::removeFromGroup() { /*if (m_info.groupIndex != -1) { m_menu->addAction(m_groupAction); } m_info.groupIndex = -1; m_info.groupName.clear(); m_effect.setAttribute(QStringLiteral("kdenlive_info"), m_info.toString()); emit parameterChanged(m_original_effect, m_effect, effectIndex());*/ } int CollapsibleEffectView::groupIndex() const { return -1; // m_info.groupIndex; } int CollapsibleEffectView::effectIndex() const { if (m_effect.isNull()) { return -1; } return m_effect.attribute(QStringLiteral("kdenlive_ix")).toInt(); } void CollapsibleEffectView::updateWidget(const ItemInfo &info, const QDomElement &effect) { // cleanup /* delete m_paramWidget; m_paramWidget = nullptr; */ m_effect = effect; setupWidget(info); } void CollapsibleEffectView::updateFrameInfo() { /* if (m_paramWidget) { m_paramWidget->refreshFrameInfo(); } */ } void CollapsibleEffectView::setActiveKeyframe(int kf) { Q_UNUSED(kf) /* if (m_paramWidget) { m_paramWidget->setActiveKeyframe(kf); } */ } void CollapsibleEffectView::setupWidget(const ItemInfo &info) { Q_UNUSED(info) /* if (m_effect.isNull()) { // //qCDebug(KDENLIVE_LOG) << "// EMPTY EFFECT STACK"; return; } delete m_paramWidget; m_paramWidget = nullptr; if (m_effect.attribute(QStringLiteral("tag")) == QLatin1String("region")) { m_regionEffect = true; QDomNodeList effects = m_effect.elementsByTagName(QStringLiteral("effect")); QDomNodeList origin_effects = m_original_effect.elementsByTagName(QStringLiteral("effect")); m_paramWidget = new ParameterContainer(m_effect, info, metaInfo, widgetFrame); QWidget *container = new QWidget(widgetFrame); QVBoxLayout *vbox = static_cast(widgetFrame->layout()); vbox->addWidget(container); // m_paramWidget = new ParameterContainer(m_effect.toElement(), info, metaInfo, container); for (int i = 0; i < effects.count(); ++i) { bool canMoveUp = true; if (i == 0 || effects.at(i - 1).toElement().attribute(QStringLiteral("id")) == QLatin1String("speed")) { canMoveUp = false; } CollapsibleEffectView *coll = new CollapsibleEffectView(effects.at(i).toElement(), origin_effects.at(i).toElement(), info, metaInfo, canMoveUp, i == effects.count() - 1, container); m_subParamWidgets.append(coll); connect(coll, &CollapsibleEffectView::parameterChanged, this, &CollapsibleEffectView::slotUpdateRegionEffectParams); // container = new QWidget(widgetFrame); vbox->addWidget(coll); // p = new ParameterContainer(effects.at(i).toElement(), info, isEffect, container); } } else { m_paramWidget = new ParameterContainer(m_effect, info, metaInfo, widgetFrame); connect(m_paramWidget, &ParameterContainer::disableCurrentFilter, this, &CollapsibleEffectView::slotDisable); connect(m_paramWidget, &ParameterContainer::importKeyframes, this, &CollapsibleEffectView::importKeyframes); if (m_effect.firstChildElement(QStringLiteral("parameter")).isNull()) { // Effect has no parameter, don't allow expand collapseButton->setEnabled(false); collapseButton->setVisible(false); widgetFrame->setVisible(false); } } if (collapseButton->isEnabled() && m_info.isCollapsed) { widgetFrame->setVisible(false); collapseButton->setArrowType(Qt::RightArrow); } connect(m_paramWidget, &ParameterContainer::parameterChanged, this, &CollapsibleEffectView::parameterChanged); connect(m_paramWidget, &ParameterContainer::startFilterJob, this, &CollapsibleEffectView::startFilterJob); connect(this, &CollapsibleEffectView::syncEffectsPos, m_paramWidget, &ParameterContainer::syncEffectsPos); connect(m_paramWidget, &ParameterContainer::checkMonitorPosition, this, &CollapsibleEffectView::checkMonitorPosition); connect(m_paramWidget, &ParameterContainer::seekTimeline, this, &CollapsibleEffectView::seekTimeline); connect(m_paramWidget, &ParameterContainer::importClipKeyframes, this, &CollapsibleEffectView::prepareImportClipKeyframes); */ } bool CollapsibleEffectView::isGroup() const { return false; } void CollapsibleEffectView::updateTimecodeFormat() { /* m_paramWidget->updateTimecodeFormat(); if (!m_subParamWidgets.isEmpty()) { // we have a group for (int i = 0; i < m_subParamWidgets.count(); ++i) { m_subParamWidgets.at(i)->updateTimecodeFormat(); } } */ } void CollapsibleEffectView::slotUpdateRegionEffectParams(const QDomElement & /*old*/, const QDomElement & /*e*/, int /*ix*/) { // qCDebug(KDENLIVE_LOG)<<"// EMIT CHANGE SUBEFFECT.....:"; emit parameterChanged(m_original_effect, m_effect, effectIndex()); } void CollapsibleEffectView::slotSyncEffectsPos(int pos) { emit syncEffectsPos(pos); } void CollapsibleEffectView::dragEnterEvent(QDragEnterEvent *event) { Q_UNUSED(event) /* if (event->mimeData()->hasFormat(QStringLiteral("kdenlive/effectslist"))) { frame->setProperty("target", true); frame->setStyleSheet(frame->styleSheet()); event->acceptProposedAction(); } else if (m_paramWidget->doesAcceptDrops() && event->mimeData()->hasFormat(QStringLiteral("kdenlive/geometry")) && event->source()->objectName() != QStringLiteral("ParameterContainer")) { event->setDropAction(Qt::CopyAction); event->setAccepted(true); } else { QWidget::dragEnterEvent(event); } */ } void CollapsibleEffectView::dragLeaveEvent(QDragLeaveEvent * /*event*/) { frame->setProperty("target", false); frame->setStyleSheet(frame->styleSheet()); } void CollapsibleEffectView::importKeyframes(const QString &kf) { QMap keyframes; if (kf.contains(QLatin1Char('\n'))) { const QStringList params = kf.split(QLatin1Char('\n'), QString::SkipEmptyParts); for (const QString ¶m : params) { keyframes.insert(param.section(QLatin1Char('='), 0, 0), param.section(QLatin1Char('='), 1)); } } else { keyframes.insert(kf.section(QLatin1Char('='), 0, 0), kf.section(QLatin1Char('='), 1)); } emit importClipKeyframes(AVWidget, m_itemInfo, m_effect.cloneNode().toElement(), keyframes); } void CollapsibleEffectView::dropEvent(QDropEvent *event) { if (event->mimeData()->hasFormat(QStringLiteral("kdenlive/geometry"))) { if (event->source()->objectName() == QStringLiteral("ParameterContainer")) { return; } // emit activateEffect(effectIndex()); QString itemData = event->mimeData()->data(QStringLiteral("kdenlive/geometry")); importKeyframes(itemData); return; } frame->setProperty("target", false); frame->setStyleSheet(frame->styleSheet()); const QString effects = QString::fromUtf8(event->mimeData()->data(QStringLiteral("kdenlive/effectslist"))); // event->acceptProposedAction(); QDomDocument doc; doc.setContent(effects, true); QDomElement e = doc.documentElement(); int ix = e.attribute(QStringLiteral("kdenlive_ix")).toInt(); int currentEffectIx = effectIndex(); if (ix == currentEffectIx || e.attribute(QStringLiteral("id")) == QLatin1String("speed")) { // effect dropped on itself, or unmovable speed dropped, reject event->ignore(); return; } if (ix == 0 || e.tagName() == QLatin1String("effectgroup")) { if (e.tagName() == QLatin1String("effectgroup")) { // moving a group QDomNodeList subeffects = e.elementsByTagName(QStringLiteral("effect")); if (subeffects.isEmpty()) { event->ignore(); return; } event->setDropAction(Qt::MoveAction); event->accept(); /* EffectInfo info; info.fromString(subeffects.at(0).toElement().attribute(QStringLiteral("kdenlive_info"))); if (info.groupIndex >= 0) { // Moving group QList effectsIds; // Collect moved effects ids for (int i = 0; i < subeffects.count(); ++i) { QDomElement effect = subeffects.at(i).toElement(); effectsIds << effect.attribute(QStringLiteral("kdenlive_ix")).toInt(); } // emit moveEffect(effectsIds, currentEffectIx, info.groupIndex, info.groupName); } else { // group effect dropped from effect list if (m_info.groupIndex > -1) { // TODO: Should we merge groups?? } emit addEffect(e); }*/ emit addEffect(e); return; } // effect dropped from effects list, add it e.setAttribute(QStringLiteral("kdenlive_ix"), ix); /*if (m_info.groupIndex > -1) { // Dropped on a group e.setAttribute(QStringLiteral("kdenlive_info"), m_info.toString()); }*/ event->setDropAction(Qt::CopyAction); event->accept(); emit addEffect(e); return; } // emit moveEffect(QList() << ix, currentEffectIx, m_info.groupIndex, m_info.groupName); event->setDropAction(Qt::MoveAction); event->accept(); } void CollapsibleEffectView::adjustButtons(int ix, int max) { buttonUp->setEnabled(ix > 0); buttonDown->setEnabled(ix < max - 1); } MonitorSceneType CollapsibleEffectView::needsMonitorEffectScene() const { if (!m_model->isEnabled() || !m_view) { return MonitorSceneDefault; } return m_view->needsMonitorEffectScene(); } void CollapsibleEffectView::setKeyframes(const QString &tag, const QString &keyframes) { Q_UNUSED(tag) Q_UNUSED(keyframes) /* m_paramWidget->setKeyframes(tag, keyframes); */ } bool CollapsibleEffectView::isMovable() const { return m_isMovable; } void CollapsibleEffectView::prepareImportClipKeyframes() { emit importClipKeyframes(AVWidget, m_itemInfo, m_effect.cloneNode().toElement(), QMap()); } void CollapsibleEffectView::enableView(bool enabled) { m_enabledButton->setActive(enabled); title->setEnabled(!enabled); m_colorIcon->setEnabled(!enabled); if (enabled) { if (KdenliveSettings::disable_effect_parameters()) { widgetFrame->setEnabled(false); } } else { widgetFrame->setEnabled(true); } }