File indexing completed on 2024-05-05 04:54:18

0001 /*
0002     SPDX-FileCopyrightText: 2017 Nicolas Carion
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "mixstackview.hpp"
0007 #include "assets/keyframes/model/keyframemodellist.hpp"
0008 #include "assets/model/assetparametermodel.hpp"
0009 #include "core.h"
0010 #include "monitor/monitor.h"
0011 #include "widgets/positionwidget.h"
0012 #include "widgets/timecodedisplay.h"
0013 
0014 #include <KLocalizedString>
0015 #include <QComboBox>
0016 #include <QDebug>
0017 #include <QHBoxLayout>
0018 #include <QLabel>
0019 #include <QSignalBlocker>
0020 #include <QToolButton>
0021 
0022 MixStackView::MixStackView(QWidget *parent)
0023     : AssetParameterView(parent)
0024 {
0025     // Position widget
0026     m_position = new PositionWidget(i18n("Position"), 0, 0, 0);
0027     // Duration widget
0028     m_durationLayout = new QHBoxLayout;
0029     m_duration = new TimecodeDisplay(this);
0030     m_duration->setRange(1, -1);
0031     m_durationLayout->addWidget(new QLabel(i18n("Duration:")));
0032     m_durationLayout->addWidget(m_duration);
0033     m_alignLeft = new QToolButton(this);
0034     m_alignLeft->setIcon(QIcon::fromTheme(QStringLiteral("align-horizontal-left")));
0035     m_alignLeft->setToolTip(i18n("Align left"));
0036     m_alignLeft->setAutoRaise(true);
0037     m_alignLeft->setCheckable(true);
0038     connect(m_alignLeft, &QToolButton::clicked, this, &MixStackView::slotAlignLeft);
0039     m_alignRight = new QToolButton(this);
0040     m_alignRight->setIcon(QIcon::fromTheme(QStringLiteral("align-horizontal-right")));
0041     m_alignRight->setToolTip(i18n("Align right"));
0042     m_alignRight->setAutoRaise(true);
0043     m_alignRight->setCheckable(true);
0044     connect(m_alignRight, &QToolButton::clicked, this, &MixStackView::slotAlignRight);
0045     m_alignCenter = new QToolButton(this);
0046     m_alignCenter->setIcon(QIcon::fromTheme(QStringLiteral("align-horizontal-center")));
0047     m_alignCenter->setToolTip(i18n("Center"));
0048     m_alignCenter->setAutoRaise(true);
0049     m_alignCenter->setCheckable(true);
0050     connect(m_alignCenter, &QToolButton::clicked, this, &MixStackView::slotAlignCenter);
0051     m_durationLayout->addStretch();
0052     m_durationLayout->addWidget(m_alignRight);
0053     m_durationLayout->addWidget(m_alignCenter);
0054     m_durationLayout->addWidget(m_alignLeft);
0055     connect(m_duration, &TimecodeDisplay::timeCodeUpdated, this, &MixStackView::updateDuration);
0056     connect(m_position, &PositionWidget::valueChanged, this, &MixStackView::updatePosition);
0057     connect(this, &AssetParameterView::seekToPos, [this](int pos) {
0058         // at this point, the effects returns a pos relative to the clip. We need to convert it to a global time
0059         int clipIn = pCore->getItemPosition(m_model->getOwnerId());
0060         Q_EMIT seekToTransPos(pos + clipIn);
0061     });
0062 }
0063 
0064 void MixStackView::setModel(const std::shared_ptr<AssetParameterModel> &model, QSize frameSize, bool addSpacer)
0065 {
0066     AssetParameterView::setModel(model, frameSize, addSpacer);
0067     m_model->setActive(true);
0068     auto kfr = model->getKeyframeModel();
0069     if (kfr) {
0070         connect(kfr.get(), &KeyframeModelList::modelChanged, this, &AssetParameterView::slotRefresh);
0071     }
0072     Q_EMIT initKeyframeView(true);
0073     pCore->getMonitor(m_model->monitorId)->slotShowEffectScene(needsMonitorEffectScene());
0074 
0075     const QSignalBlocker bk0(m_duration);
0076     const QSignalBlocker bk1(m_position);
0077     int duration = m_model->data(m_model->index(0, 0), AssetParameterModel::ParentDurationRole).toInt();
0078     m_duration->setValue(duration + 1);
0079     m_position->setRange(0, duration);
0080     m_position->setPosition(duration - pCore->getMixCutPos(stackOwner()));
0081     connect(m_model.get(), &AssetParameterModel::dataChanged, this, &MixStackView::durationChanged);
0082 
0083     // The layout is handled by AssetParameterView, so we can only add our custom stuff later here
0084     m_lay->addLayout(m_durationLayout);
0085     m_lay->addWidget(m_position);
0086     m_lay->addStretch(10);
0087     checkAlignment();
0088 }
0089 
0090 void MixStackView::checkAlignment()
0091 {
0092     MixAlignment align = pCore->getMixAlign(stackOwner());
0093     QSignalBlocker bk1(m_alignLeft);
0094     QSignalBlocker bk2(m_alignRight);
0095     QSignalBlocker bk3(m_alignCenter);
0096     m_alignLeft->setChecked(false);
0097     m_alignRight->setChecked(false);
0098     m_alignCenter->setChecked(false);
0099     switch (align) {
0100     case MixAlignment::AlignLeft:
0101         m_alignLeft->setChecked(true);
0102         break;
0103     case MixAlignment::AlignRight:
0104         m_alignRight->setChecked(true);
0105         break;
0106     case MixAlignment::AlignCenter:
0107         m_alignCenter->setChecked(true);
0108         break;
0109     default:
0110         // No alignment
0111         break;
0112     }
0113 }
0114 
0115 void MixStackView::durationChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &roles)
0116 {
0117     if (roles.contains(AssetParameterModel::ParentDurationRole)) {
0118         int duration = m_model->data(m_model->index(0, 0), AssetParameterModel::ParentDurationRole).toInt();
0119         int mixCutPos = pCore->getMixCutPos(stackOwner());
0120         if (duration + 1 == m_duration->getValue() && mixCutPos == m_position->getPosition()) {
0121             // No change
0122             return;
0123         }
0124         QSignalBlocker bk1(m_duration);
0125         QSignalBlocker bk2(m_position);
0126         m_duration->setValue(duration + 1);
0127         m_position->setRange(0, duration);
0128         m_position->setPosition(duration - mixCutPos);
0129         checkAlignment();
0130     }
0131 }
0132 
0133 MixAlignment MixStackView::alignment() const
0134 {
0135     if (m_alignRight->isChecked()) {
0136         return MixAlignment::AlignRight;
0137     }
0138     if (m_alignLeft->isChecked()) {
0139         return MixAlignment::AlignLeft;
0140     }
0141     if (m_alignCenter->isChecked()) {
0142         return MixAlignment::AlignCenter;
0143     }
0144     return MixAlignment::AlignNone;
0145 }
0146 
0147 void MixStackView::updateDuration()
0148 {
0149     if (m_model) {
0150         pCore->resizeMix(stackOwner().itemId, m_duration->getValue() - 1, alignment());
0151     }
0152 }
0153 
0154 void MixStackView::updatePosition()
0155 {
0156     if (m_model) {
0157         pCore->resizeMix(stackOwner().itemId, m_duration->getValue() - 1, MixAlignment::AlignNone, m_position->getPosition());
0158     }
0159 }
0160 
0161 void MixStackView::slotAlignLeft()
0162 {
0163     if (!m_alignLeft->isChecked()) {
0164         return;
0165     }
0166     m_alignRight->setChecked(false);
0167     m_alignCenter->setChecked(false);
0168     pCore->resizeMix(stackOwner().itemId, m_duration->getValue() - 1, MixAlignment::AlignLeft);
0169 }
0170 
0171 void MixStackView::slotAlignRight()
0172 {
0173     if (!m_alignRight->isChecked()) {
0174         return;
0175     }
0176     m_alignLeft->setChecked(false);
0177     m_alignCenter->setChecked(false);
0178     pCore->resizeMix(stackOwner().itemId, m_duration->getValue() - 1, MixAlignment::AlignRight);
0179 }
0180 
0181 void MixStackView::slotAlignCenter()
0182 {
0183     if (!m_alignCenter->isChecked()) {
0184         return;
0185     }
0186     m_alignLeft->setChecked(false);
0187     m_alignRight->setChecked(false);
0188     pCore->resizeMix(stackOwner().itemId, m_duration->getValue() - 1, MixAlignment::AlignCenter);
0189 }
0190 
0191 void MixStackView::unsetModel()
0192 {
0193     if (m_model) {
0194         m_model->setActive(false);
0195         m_lay->removeItem(m_durationLayout);
0196         m_lay->removeWidget(m_position);
0197         auto kfr = m_model->getKeyframeModel();
0198         if (kfr) {
0199             disconnect(kfr.get(), &KeyframeModelList::modelChanged, this, &AssetParameterView::slotRefresh);
0200         }
0201         disconnect(m_model.get(), &AssetParameterModel::dataChanged, this, &MixStackView::durationChanged);
0202         pCore->getMonitor(m_model->monitorId)->slotShowEffectScene(MonitorSceneDefault);
0203     }
0204     AssetParameterView::unsetModel();
0205 }
0206 
0207 ObjectId MixStackView::stackOwner() const
0208 {
0209     if (m_model) {
0210         return m_model->getOwnerId();
0211     }
0212     return ObjectId();
0213 }