File indexing completed on 2024-11-10 04:24:49

0001 /*
0002     SPDX-FileCopyrightText: 2017 Nicolas Carion
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "assetpanel.hpp"
0007 #include "core.h"
0008 #include "definitions.h"
0009 #include "effects/effectsrepository.hpp"
0010 #include "effects/effectstack/model/effectitemmodel.hpp"
0011 #include "effects/effectstack/model/effectstackmodel.hpp"
0012 #include "effects/effectstack/view/effectstackview.hpp"
0013 #include "kdenlivesettings.h"
0014 #include "model/assetparametermodel.hpp"
0015 #include "monitor/monitor.h"
0016 #include "transitions/transitionsrepository.hpp"
0017 #include "transitions/view/mixstackview.hpp"
0018 #include "transitions/view/transitionstackview.hpp"
0019 
0020 #include "view/assetparameterview.hpp"
0021 
0022 #include <KColorScheme>
0023 #include <KColorUtils>
0024 #include <KDualAction>
0025 #include <KLocalizedString>
0026 #include <KMessageWidget>
0027 #include <KSqueezedTextLabel>
0028 #include <QApplication>
0029 #include <QComboBox>
0030 #include <QFontDatabase>
0031 #include <QScrollArea>
0032 #include <QScrollBar>
0033 #include <QToolBar>
0034 #include <QToolButton>
0035 #include <QVBoxLayout>
0036 
0037 AssetPanel::AssetPanel(QWidget *parent)
0038     : QWidget(parent)
0039     , m_lay(new QVBoxLayout(this))
0040     , m_assetTitle(new KSqueezedTextLabel(this))
0041     , m_container(new QWidget(this))
0042     , m_transitionWidget(new TransitionStackView(this))
0043     , m_mixWidget(new MixStackView(this))
0044     , m_effectStackWidget(new EffectStackView(this))
0045 {
0046     auto *buttonToolbar = new QToolBar(this);
0047     m_titleAction = buttonToolbar->addWidget(m_assetTitle);
0048     int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
0049     QSize iconSize(size, size);
0050     buttonToolbar->setIconSize(iconSize);
0051     // Edit composition button
0052     m_switchCompoButton = new QComboBox(this);
0053     m_switchCompoButton->setFrame(false);
0054     auto allTransitions = TransitionsRepository::get()->getNames();
0055     for (const auto &transition : qAsConst(allTransitions)) {
0056         if (transition.first != QLatin1String("mix")) {
0057             m_switchCompoButton->addItem(transition.second, transition.first);
0058         }
0059     }
0060     connect(m_switchCompoButton, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, [&]() {
0061         if (m_transitionWidget->stackOwner().type == KdenliveObjectType::TimelineComposition) {
0062             Q_EMIT switchCurrentComposition(m_transitionWidget->stackOwner().itemId, m_switchCompoButton->currentData().toString());
0063         } else if (m_mixWidget->isVisible()) {
0064             Q_EMIT switchCurrentComposition(m_mixWidget->stackOwner().itemId, m_switchCompoButton->currentData().toString());
0065         }
0066     });
0067     m_switchCompoButton->setToolTip(i18n("Change composition type"));
0068     m_switchAction = buttonToolbar->addWidget(m_switchCompoButton);
0069     m_switchAction->setVisible(false);
0070 
0071     // spacer
0072     QWidget *empty = new QWidget();
0073     empty->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
0074     buttonToolbar->addWidget(empty);
0075 
0076     m_switchBuiltStack = new QToolButton(this);
0077     m_switchBuiltStack->setIcon(QIcon::fromTheme(QStringLiteral("adjustlevels")));
0078     m_switchBuiltStack->setToolTip(i18n("Adjust clip"));
0079     m_switchBuiltStack->setCheckable(true);
0080     m_switchBuiltStack->setChecked(KdenliveSettings::showbuiltstack());
0081     m_switchBuiltStack->setVisible(false);
0082     // connect(m_switchBuiltStack, &QToolButton::toggled, m_effectStackWidget, &EffectStackView::switchBuiltStack);
0083     buttonToolbar->addWidget(m_switchBuiltStack);
0084 
0085     m_saveEffectStack = new QToolButton(this);
0086     m_saveEffectStack->setIcon(QIcon::fromTheme(QStringLiteral("document-save-all")));
0087     m_saveEffectStack->setToolTip(i18n("Save Effect Stackā€¦"));
0088     m_saveEffectStack->setWhatsThis(xi18nc("@info:whatsthis", "Saves the entire effect stack as an XML file for use in other projects."));
0089 
0090     // Would be better to have something like `setVisible(false)` here, but this apparently removes the button.
0091     // See https://stackoverflow.com/a/17645563/5172513
0092     m_saveEffectStack->setEnabled(false);
0093     connect(m_saveEffectStack, &QToolButton::released, this, &AssetPanel::slotSaveStack);
0094     buttonToolbar->addWidget(m_saveEffectStack);
0095 
0096     m_splitButton = new KDualAction(i18n("Normal view"), i18n("Compare effect"), this);
0097     m_splitButton->setActiveIcon(QIcon::fromTheme(QStringLiteral("view-right-close")));
0098     m_splitButton->setInactiveIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
0099     m_splitButton->setToolTip(i18n("Compare effect"));
0100     m_splitButton->setWhatsThis(xi18nc(
0101         "@info:whatsthis",
0102         "Turns on or off the split view in the project and/or clip monitor: on the left the clip with effect is shown, on the right the clip without effect."));
0103     m_splitButton->setVisible(false);
0104     connect(m_splitButton, &KDualAction::activeChangedByUser, this, &AssetPanel::processSplitEffect);
0105     buttonToolbar->addAction(m_splitButton);
0106 
0107     m_enableStackButton = new KDualAction(i18n("Effects disabled"), i18n("Effects enabled"), this);
0108     m_enableStackButton->setWhatsThis(
0109         xi18nc("@info:whatsthis",
0110                "Toggles the effect stack to be enabled or disabled. Useful to see the difference between original and edited or to speed up scrubbing."));
0111     m_enableStackButton->setInactiveIcon(QIcon::fromTheme(QStringLiteral("hint")));
0112     m_enableStackButton->setActiveIcon(QIcon::fromTheme(QStringLiteral("visibility")));
0113     connect(m_enableStackButton, &KDualAction::activeChangedByUser, this, &AssetPanel::enableStack);
0114     m_enableStackButton->setVisible(false);
0115     buttonToolbar->addAction(m_enableStackButton);
0116 
0117     m_timelineButton = new KDualAction(i18n("Display keyframes in timeline"), i18n("Hide keyframes in timeline"), this);
0118     m_timelineButton->setWhatsThis(xi18nc("@info:whatsthis", "Toggles the display of keyframes in the clip on the timeline"));
0119     m_timelineButton->setInactiveIcon(QIcon::fromTheme(QStringLiteral("keyframe-disable")));
0120     m_timelineButton->setActiveIcon(QIcon::fromTheme(QStringLiteral("keyframe")));
0121     m_timelineButton->setVisible(false);
0122     connect(m_timelineButton, &KDualAction::activeChangedByUser, this, &AssetPanel::showKeyframes);
0123     buttonToolbar->addAction(m_timelineButton);
0124 
0125     m_lay->addWidget(buttonToolbar);
0126     m_lay->setContentsMargins(0, 0, 0, 0);
0127     m_lay->setSpacing(0);
0128     auto *lay = new QVBoxLayout(m_container);
0129     lay->setContentsMargins(0, 0, 0, 0);
0130     lay->addWidget(m_transitionWidget);
0131     lay->addWidget(m_mixWidget);
0132     lay->addWidget(m_effectStackWidget);
0133     m_sc = new QScrollArea;
0134     m_sc->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0135     m_sc->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0136     m_sc->setFrameStyle(QFrame::NoFrame);
0137     m_sc->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding));
0138     m_container->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding));
0139     m_sc->setWidgetResizable(true);
0140 
0141     m_lay->addWidget(m_sc);
0142     m_infoMessage = new KMessageWidget(this);
0143     m_lay->addWidget(m_infoMessage);
0144     m_infoMessage->hide();
0145     m_sc->setWidget(m_container);
0146     m_transitionWidget->setVisible(false);
0147     m_mixWidget->setVisible(false);
0148     m_effectStackWidget->setVisible(false);
0149     updatePalette();
0150     connect(m_effectStackWidget, &EffectStackView::checkScrollBar, this, &AssetPanel::slotCheckWheelEventFilter);
0151     connect(m_effectStackWidget, &EffectStackView::scrollView, this, &AssetPanel::scrollTo);
0152     connect(m_effectStackWidget, &EffectStackView::seekToPos, this, &AssetPanel::seekToPos);
0153     connect(m_effectStackWidget, &EffectStackView::reloadEffect, this, &AssetPanel::reloadEffect);
0154     connect(m_transitionWidget, &TransitionStackView::seekToTransPos, this, &AssetPanel::seekToPos);
0155     connect(m_mixWidget, &MixStackView::seekToTransPos, this, &AssetPanel::seekToPos);
0156     connect(m_effectStackWidget, &EffectStackView::updateEnabledState, this,
0157             [this]() { m_enableStackButton->setActive(m_effectStackWidget->isStackEnabled()); });
0158 
0159     connect(this, &AssetPanel::slotSaveStack, m_effectStackWidget, &EffectStackView::slotSaveStack);
0160 }
0161 
0162 void AssetPanel::showTransition(int tid, const std::shared_ptr<AssetParameterModel> &transitionModel)
0163 {
0164     Q_UNUSED(tid)
0165     ObjectId id = transitionModel->getOwnerId();
0166     if (m_transitionWidget->stackOwner() == id) {
0167         // already on this effect stack, do nothing
0168         return;
0169     }
0170     clear();
0171     m_switchCompoButton->setCurrentIndex(m_switchCompoButton->findData(transitionModel->getAssetId()));
0172     m_switchAction->setVisible(true);
0173     m_titleAction->setVisible(false);
0174     m_assetTitle->clear();
0175     m_transitionWidget->setVisible(true);
0176     m_timelineButton->setVisible(true);
0177     m_enableStackButton->setVisible(false);
0178     QSize s = pCore->getCompositionSizeOnTrack(id);
0179     m_transitionWidget->setModel(transitionModel, s, true);
0180 }
0181 
0182 void AssetPanel::showMix(int cid, const std::shared_ptr<AssetParameterModel> &transitionModel, bool refreshOnly)
0183 {
0184     if (cid == -1) {
0185         clear();
0186         return;
0187     }
0188     ObjectId id(KdenliveObjectType::TimelineMix, cid, transitionModel->getOwnerId().uuid);
0189     if (refreshOnly) {
0190         if (m_mixWidget->stackOwner() != id) {
0191             // item not currently displayed, ignore
0192             return;
0193         }
0194     } else {
0195         if (m_mixWidget->stackOwner() == id) {
0196             // already on this effect stack, do nothing
0197             return;
0198         }
0199     }
0200     clear();
0201     // There is only 1 audio composition, so hide switch combobox
0202     m_switchAction->setVisible(transitionModel->getAssetId() != QLatin1String("mix"));
0203     m_titleAction->setVisible(false);
0204     m_assetTitle->clear();
0205     m_mixWidget->setVisible(true);
0206     m_timelineButton->setVisible(false);
0207     m_enableStackButton->setVisible(false);
0208     m_switchCompoButton->setCurrentIndex(m_switchCompoButton->findData(transitionModel->getAssetId()));
0209     m_mixWidget->setModel(transitionModel, QSize(), true);
0210 }
0211 
0212 void AssetPanel::showEffectStack(const QString &itemName, const std::shared_ptr<EffectStackModel> &effectsModel, QSize frameSize, bool showKeyframes)
0213 {
0214     if (effectsModel == nullptr) {
0215         // Item is not ready
0216         m_splitButton->setVisible(false);
0217         m_enableStackButton->setVisible(false);
0218         m_saveEffectStack->setEnabled(false);
0219         clear();
0220         return;
0221     }
0222     ObjectId id = effectsModel->getOwnerId();
0223     if (m_effectStackWidget->stackOwner() == id) {
0224         // already on this effect stack, do nothing
0225         // Disable split effect in case clip was moved
0226         if (id.type == KdenliveObjectType::TimelineClip && m_splitButton->isActive()) {
0227             m_splitButton->setActive(false);
0228             processSplitEffect(false);
0229         }
0230         return;
0231     }
0232     clear();
0233     QString title;
0234     bool showSplit = false;
0235     bool enableKeyframes = false;
0236     switch (id.type) {
0237     case KdenliveObjectType::TimelineClip:
0238         title = i18n("%1 effects", itemName);
0239         showSplit = true;
0240         enableKeyframes = true;
0241         break;
0242     case KdenliveObjectType::TimelineComposition:
0243         title = i18n("%1 parameters", itemName);
0244         enableKeyframes = true;
0245         break;
0246     case KdenliveObjectType::TimelineTrack:
0247         title = i18n("Track %1 effects", itemName);
0248         // TODO: track keyframes
0249         // enableKeyframes = true;
0250         break;
0251     case KdenliveObjectType::BinClip:
0252         title = i18n("Bin %1 effects", itemName);
0253         showSplit = true;
0254         break;
0255     default:
0256         title = itemName;
0257         break;
0258     }
0259     m_assetTitle->setText(title);
0260     m_titleAction->setVisible(true);
0261     m_splitButton->setVisible(showSplit);
0262     m_saveEffectStack->setEnabled(true);
0263     m_enableStackButton->setVisible(id.type != KdenliveObjectType::TimelineComposition);
0264     m_enableStackButton->setActive(effectsModel->isStackEnabled());
0265     if (showSplit) {
0266         m_splitButton->setEnabled(effectsModel->rowCount() > 0);
0267         QObject::connect(effectsModel.get(), &EffectStackModel::dataChanged, this, [&]() {
0268             if (m_effectStackWidget->isEmpty()) {
0269                 m_splitButton->setActive(false);
0270             }
0271             m_splitButton->setEnabled(!m_effectStackWidget->isEmpty());
0272         });
0273     }
0274     m_timelineButton->setVisible(enableKeyframes);
0275     m_timelineButton->setActive(showKeyframes);
0276     // Disable built stack until properly implemented
0277     // m_switchBuiltStack->setVisible(true);
0278     m_effectStackWidget->setVisible(true);
0279     m_effectStackWidget->setModel(effectsModel, frameSize);
0280 }
0281 
0282 void AssetPanel::clearAssetPanel(int itemId)
0283 {
0284     if (itemId == -1) {
0285         // closing project, reset panel
0286         clear();
0287         return;
0288     }
0289     ObjectId id = m_effectStackWidget->stackOwner();
0290     if (id.type == KdenliveObjectType::TimelineClip && id.itemId == itemId) {
0291         clear();
0292         return;
0293     }
0294     id = m_transitionWidget->stackOwner();
0295     if (id.type == KdenliveObjectType::TimelineComposition && id.itemId == itemId) {
0296         clear();
0297         return;
0298     }
0299     id = m_mixWidget->stackOwner();
0300     if (id.type == KdenliveObjectType::TimelineMix && id.itemId == itemId) {
0301         clear();
0302     }
0303 }
0304 
0305 void AssetPanel::clear()
0306 {
0307     if (m_splitButton->isActive()) {
0308         m_splitButton->setActive(false);
0309         processSplitEffect(false);
0310     }
0311     m_switchAction->setVisible(false);
0312     m_transitionWidget->setVisible(false);
0313     m_transitionWidget->unsetModel();
0314     m_mixWidget->setVisible(false);
0315     m_mixWidget->unsetModel();
0316     m_effectStackWidget->setVisible(false);
0317     m_splitButton->setVisible(false);
0318     m_saveEffectStack->setEnabled(false);
0319     m_timelineButton->setVisible(false);
0320     m_switchBuiltStack->setVisible(false);
0321     m_effectStackWidget->unsetModel();
0322     m_assetTitle->clear();
0323 }
0324 
0325 void AssetPanel::updatePalette()
0326 {
0327     QString styleSheet = getStyleSheet();
0328     setStyleSheet(styleSheet);
0329     m_transitionWidget->setStyleSheet(styleSheet);
0330     m_effectStackWidget->setStyleSheet(styleSheet);
0331     m_mixWidget->setStyleSheet(styleSheet);
0332 }
0333 
0334 // static
0335 const QString AssetPanel::getStyleSheet()
0336 {
0337     KColorScheme scheme(QApplication::palette().currentColorGroup(), KColorScheme::View);
0338     QColor selected_bg = scheme.decoration(KColorScheme::FocusColor).color();
0339     QColor hgh = KColorUtils::mix(QApplication::palette().window().color(), selected_bg, 0.2);
0340     QColor hover_bg = scheme.decoration(KColorScheme::HoverColor).color();
0341     QColor light_bg = scheme.shade(KColorScheme::LightShade);
0342     QColor alt_bg = scheme.background(KColorScheme::NormalBackground).color();
0343     // QColor normal_bg = QApplication::palette().window().color();
0344 
0345     QString stylesheet;
0346 
0347     // effect background
0348     stylesheet.append(QStringLiteral("QFrame#decoframe {border-bottom:2px solid "
0349                                      "palette(mid);background: transparent} QFrame#decoframe[active=\"true\"] {background: %1;}")
0350                           .arg(hgh.name()));
0351 
0352     // effect in group background
0353     stylesheet.append(
0354         QStringLiteral("QFrame#decoframesub {border-top:1px solid palette(light);}  QFrame#decoframesub[active=\"true\"] {background: %1;}").arg(hgh.name()));
0355 
0356     // group background
0357     stylesheet.append(QStringLiteral("QFrame#decoframegroup {border:2px solid palette(dark);margin:0px;margin-top:2px;} "));
0358 
0359     // effect title bar
0360     stylesheet.append(QStringLiteral("QFrame#frame {margin-bottom:2px;}  QFrame#frame[target=\"true\"] "
0361                                      "{background: palette(highlight);}"));
0362 
0363     // group effect title bar
0364     stylesheet.append(QStringLiteral("QFrame#framegroup {background: palette(dark);}  "
0365                                      "QFrame#framegroup[target=\"true\"] {background: palette(highlight);} "));
0366 
0367     // draggable effect bar content
0368     stylesheet.append(QStringLiteral("QProgressBar::chunk:horizontal {background: palette(button);border-top-left-radius: 4px;border-bottom-left-radius: 4px;} "
0369                                      "QProgressBar::chunk:horizontal#dragOnly {background: %1;border-top-left-radius: 4px;border-bottom-left-radius: 4px;} "
0370                                      "QProgressBar::chunk:horizontal:hover {background: %2;}")
0371                           .arg(alt_bg.name(), selected_bg.name()));
0372 
0373     // draggable effect bar
0374     stylesheet.append(QStringLiteral("QProgressBar:horizontal {border: 1px solid palette(dark);border-top-left-radius: 4px;border-bottom-left-radius: "
0375                                      "4px;border-right:0px;background:%3;padding: 0px;text-align:left center} QProgressBar:horizontal:disabled {border: 1px "
0376                                      "solid palette(button)} QProgressBar:horizontal#dragOnly {background: %3} QProgressBar:horizontal[inTimeline=\"true\"] { "
0377                                      "border: 1px solid %1;border-right: 0px;background: %2;padding: 0px;text-align:left center } "
0378                                      "QProgressBar::chunk:horizontal[inTimeline=\"true\"] {background: %1;}")
0379                           .arg(hover_bg.name(), light_bg.name(), alt_bg.name()));
0380 
0381     // spin box for draggable widget
0382     stylesheet.append(
0383         QStringLiteral("QAbstractSpinBox#dragBox {border: 1px solid palette(dark);border-top-right-radius: 4px;border-bottom-right-radius: "
0384                        "4px;padding-right:0px;} QAbstractSpinBox::down-button#dragBox {width:0px;padding:0px;} QAbstractSpinBox:disabled#dragBox {border: 1px "
0385                        "solid palette(button);} QAbstractSpinBox::up-button#dragBox {width:0px;padding:0px;} QAbstractSpinBox[inTimeline=\"true\"]#dragBox { "
0386                        "border: 1px solid %1;} QAbstractSpinBox:hover#dragBox {border: 1px solid %2;} ")
0387             .arg(hover_bg.name(), selected_bg.name()));
0388 
0389     // minimal double edit
0390     stylesheet.append(QStringLiteral("QAbstractSpinBox#dragMinimal {border: 0px "
0391                                      ";padding-right:0px; background-color:transparent} QAbstractSpinBox::down-button#dragMinimal {width:0px;padding:0px;} "
0392                                      "QAbstractSpinBox:disabled#dragMinimal {border: 0px;; background-color:transparent "
0393                                      ";} QAbstractSpinBox::up-button#dragMinimal {width:0px;padding:0px;}"));
0394     // group editable labels
0395     stylesheet.append(QStringLiteral("MyEditableLabel { background-color: transparent; color: palette(bright-text); border-radius: 2px;border: 1px solid "
0396                                      "transparent;} MyEditableLabel:hover {border: 1px solid palette(highlight);} "));
0397     return stylesheet;
0398 }
0399 
0400 void AssetPanel::processSplitEffect(bool enable)
0401 {
0402     KdenliveObjectType id = m_effectStackWidget->stackOwner().type;
0403     if (id == KdenliveObjectType::TimelineClip) {
0404         Q_EMIT doSplitEffect(enable);
0405     } else if (id == KdenliveObjectType::BinClip) {
0406         Q_EMIT doSplitBinEffect(enable);
0407     }
0408 }
0409 
0410 void AssetPanel::showKeyframes(bool enable)
0411 {
0412     if (m_effectStackWidget->isVisible()) {
0413         pCore->showClipKeyframes(m_effectStackWidget->stackOwner(), enable);
0414     } else if (m_transitionWidget->isVisible()) {
0415         pCore->showClipKeyframes(m_transitionWidget->stackOwner(), enable);
0416     }
0417 }
0418 
0419 ObjectId AssetPanel::effectStackOwner()
0420 {
0421     if (m_effectStackWidget->isVisible()) {
0422         return m_effectStackWidget->stackOwner();
0423     }
0424     if (m_transitionWidget->isVisible()) {
0425         return m_transitionWidget->stackOwner();
0426     }
0427     if (m_mixWidget->isVisible()) {
0428         return m_mixWidget->stackOwner();
0429     }
0430     return ObjectId();
0431 }
0432 
0433 bool AssetPanel::addEffect(const QString &effectId)
0434 {
0435     if (!m_effectStackWidget->isVisible()) {
0436         return false;
0437     }
0438     return m_effectStackWidget->addEffect(effectId);
0439 }
0440 
0441 void AssetPanel::enableStack(bool enable)
0442 {
0443     if (!m_effectStackWidget->isVisible()) {
0444         return;
0445     }
0446     m_effectStackWidget->enableStack(enable);
0447 }
0448 
0449 void AssetPanel::deleteCurrentEffect()
0450 {
0451     if (m_effectStackWidget->isVisible()) {
0452         Q_EMIT m_effectStackWidget->removeCurrentEffect();
0453     }
0454 }
0455 
0456 void AssetPanel::collapseCurrentEffect()
0457 {
0458     if (m_effectStackWidget->isVisible()) {
0459         m_effectStackWidget->switchCollapsed();
0460     }
0461 }
0462 
0463 void AssetPanel::scrollTo(QRect rect)
0464 {
0465     // Ensure the scrollview widget adapted its height to the effectstackview height change
0466     m_sc->widget()->adjustSize();
0467     if (rect.height() < m_sc->height()) {
0468         m_sc->ensureVisible(0, rect.y() + rect.height(), 0, 0);
0469     } else {
0470         m_sc->ensureVisible(0, rect.y() + m_sc->height(), 0, 0);
0471     }
0472 }
0473 
0474 void AssetPanel::slotCheckWheelEventFilter()
0475 {
0476     // If the effect stack widget has no scrollbar, we will not filter the
0477     // mouse wheel events, so that user can easily adjust effect params
0478     bool blockWheel = false;
0479     if (m_sc->verticalScrollBar() && m_sc->verticalScrollBar()->isVisible()) {
0480         // widget has scroll bar,
0481         blockWheel = true;
0482     }
0483     Q_EMIT m_effectStackWidget->blockWheelEvent(blockWheel);
0484 }
0485 
0486 void AssetPanel::assetPanelWarning(const QString &service, const QString & /*id*/, const QString &message)
0487 {
0488     QString finalMessage;
0489     if (!service.isEmpty() && EffectsRepository::get()->exists(service)) {
0490         QString effectName = EffectsRepository::get()->getName(service);
0491         if (!effectName.isEmpty()) {
0492             finalMessage = QStringLiteral("<b>") + effectName + QStringLiteral("</b><br />");
0493         }
0494     }
0495     finalMessage.append(message);
0496     m_infoMessage->setText(finalMessage);
0497     m_infoMessage->setWordWrap(message.length() > 35);
0498     m_infoMessage->setCloseButtonVisible(true);
0499     m_infoMessage->setMessageType(KMessageWidget::Warning);
0500     m_infoMessage->animatedShow();
0501 }
0502 
0503 void AssetPanel::slotAddRemoveKeyframe()
0504 {
0505     if (m_effectStackWidget->isVisible()) {
0506         m_effectStackWidget->addRemoveKeyframe();
0507     } else if (m_transitionWidget->isVisible()) {
0508         Q_EMIT m_transitionWidget->addRemoveKeyframe();
0509     } else if (m_mixWidget->isVisible()) {
0510         Q_EMIT m_mixWidget->addRemoveKeyframe();
0511     }
0512 }
0513 
0514 void AssetPanel::slotNextKeyframe()
0515 {
0516     if (m_effectStackWidget->isVisible()) {
0517         m_effectStackWidget->slotGoToKeyframe(true);
0518     } else if (m_transitionWidget->isVisible()) {
0519         Q_EMIT m_transitionWidget->nextKeyframe();
0520     } else if (m_mixWidget->isVisible()) {
0521         Q_EMIT m_mixWidget->nextKeyframe();
0522     }
0523 }
0524 
0525 void AssetPanel::slotPreviousKeyframe()
0526 {
0527     if (m_effectStackWidget->isVisible()) {
0528         m_effectStackWidget->slotGoToKeyframe(false);
0529     } else if (m_transitionWidget->isVisible()) {
0530         Q_EMIT m_transitionWidget->previousKeyframe();
0531     } else if (m_mixWidget->isVisible()) {
0532         Q_EMIT m_mixWidget->previousKeyframe();
0533     }
0534 }
0535 
0536 void AssetPanel::updateAssetPosition(int itemId, const QUuid uuid)
0537 {
0538     if (m_effectStackWidget->isVisible()) {
0539         ObjectId id(KdenliveObjectType::TimelineClip, itemId, uuid);
0540         if (m_effectStackWidget->stackOwner() == id) {
0541             Q_EMIT pCore->getMonitor(Kdenlive::ProjectMonitor)->seekPosition(pCore->getMonitorPosition());
0542         }
0543     } else if (m_transitionWidget->isVisible()) {
0544         ObjectId id(KdenliveObjectType::TimelineComposition, itemId, uuid);
0545         if (m_transitionWidget->stackOwner() == id) {
0546             Q_EMIT pCore->getMonitor(Kdenlive::ProjectMonitor)->seekPosition(pCore->getMonitorPosition());
0547         }
0548     }
0549 }
0550 
0551 void AssetPanel::sendStandardCommand(int command)
0552 {
0553     if (m_effectStackWidget->isVisible()) {
0554         m_effectStackWidget->sendStandardCommand(command);
0555     } else if (m_transitionWidget->isVisible()) {
0556         m_transitionWidget->sendStandardCommand(command);
0557     }
0558 }