diff --git a/src/assets/assetpanel.cpp b/src/assets/assetpanel.cpp
index 7965c39ee..1c903859b 100644
--- a/src/assets/assetpanel.cpp
+++ b/src/assets/assetpanel.cpp
@@ -1,198 +1,199 @@
/***************************************************************************
* 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 "assetpanel.hpp"
#include "effects/effectstack/model/effectitemmodel.hpp"
#include "effects/effectstack/model/effectstackmodel.hpp"
#include "effects/effectstack/view/effectstackview.hpp"
#include "transitions/view/transitionstackview.hpp"
#include "kdenlivesettings.h"
#include "model/assetparametermodel.hpp"
#include "transitions/transitionsrepository.hpp"
#include "view/assetparameterview.hpp"
#include "utils/KoIconUtils.h"
#include "definitions.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
AssetPanel::AssetPanel(QWidget *parent)
: QScrollArea(parent)
, m_lay(new QVBoxLayout(this))
, m_assetTitle(new KSqueezedTextLabel(this))
, m_transitionWidget(new TransitionStackView(this))
, m_effectStackWidget(new EffectStackView(this))
{
QHBoxLayout *tLayout = new QHBoxLayout;
tLayout->addWidget(m_assetTitle);
m_splitButton = new QToolButton(this);
m_splitButton->setIcon(KoIconUtils::themedIcon(QStringLiteral("view-split-left-right")));
m_splitButton->setToolTip(i18n("Compare effect"));
m_splitButton->setCheckable(true);
m_splitButton->setVisible(false);
connect(m_splitButton, &QToolButton::toggled, this, &AssetPanel::processSplitEffect);
tLayout->addWidget(m_splitButton);
m_lay->addLayout(tLayout);
m_lay->addWidget(m_transitionWidget);
m_lay->addWidget(m_effectStackWidget);
m_transitionWidget->setVisible(false);
updatePalette();
+ connect(m_effectStackWidget, &EffectStackView::seekToPos, this, &AssetPanel::seekToPos);
}
void AssetPanel::showTransition(int tid, std::shared_ptr transitionModel)
{
clear();
QString transitionId = transitionModel->getAssetId();
m_transitionWidget->setProperty("compositionId", tid);
QString transitionName = TransitionsRepository::get()->getName(transitionId);
m_assetTitle->setText(i18n("Properties of transition %1", transitionName));
m_transitionWidget->setVisible(true);
m_transitionWidget->setModel(transitionModel, QPair(-1, -1), true);
}
void AssetPanel::showEffectStack(const QString &clipName, std::shared_ptr effectsModel, QPair range)
{
clear();
if (effectsModel == nullptr) {
// Item is not ready
return;
}
m_assetTitle->setText(i18n("%1 effects", clipName));
m_splitButton->setVisible(true);
m_effectStackWidget->setVisible(true);
m_effectStackWidget->setModel(effectsModel, range);
}
void AssetPanel::clearAssetPanel(int itemId)
{
if (itemId == -1 || m_transitionWidget->property("compositionId").toInt() == itemId || m_effectStackWidget->property("clipId").toInt() == itemId) {
clear();
}
}
void AssetPanel::adjustAssetPanelRange(int itemId, int in, int out)
{
if (m_effectStackWidget->property("clipId").toInt() == itemId) {
m_effectStackWidget->setRange(in, out);
}
}
void AssetPanel::clear()
{
m_transitionWidget->setVisible(false);
m_transitionWidget->setProperty("compositionId", QVariant());
m_transitionWidget->unsetModel();
m_effectStackWidget->setVisible(false);
m_splitButton->setVisible(false);
m_effectStackWidget->setProperty("clipId", QVariant());
m_effectStackWidget->unsetModel();
m_assetTitle->setText(QString());
}
void AssetPanel::updatePalette()
{
QString styleSheet = getStyleSheet();
setStyleSheet(styleSheet);
m_transitionWidget->setStyleSheet(styleSheet);
m_effectStackWidget->setStyleSheet(styleSheet);
}
// static
const QString AssetPanel::getStyleSheet()
{
KColorScheme scheme(QApplication::palette().currentColorGroup(), KColorScheme::View, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
QColor selected_bg = scheme.decoration(KColorScheme::FocusColor).color();
QColor hgh = KColorUtils::mix(QApplication::palette().window().color(), selected_bg, 0.2);
QColor hover_bg = scheme.decoration(KColorScheme::HoverColor).color();
QColor light_bg = scheme.shade(KColorScheme::LightShade);
QColor alt_bg = scheme.background(KColorScheme::NormalBackground).color();
QString stylesheet;
// effect background
stylesheet.append(QStringLiteral("QFrame#decoframe {border-bottom:2px solid "
"palette(mid);background: transparent} QFrame#decoframe[active=\"true\"] {background: %1;}")
.arg(hgh.name()));
// effect in group background
stylesheet.append(
QStringLiteral("QFrame#decoframesub {border-top:1px solid palette(light);} QFrame#decoframesub[active=\"true\"] {background: %1;}").arg(hgh.name()));
// group background
stylesheet.append(QStringLiteral("QFrame#decoframegroup {border:2px solid palette(dark);margin:0px;margin-top:2px;} "));
// effect title bar
stylesheet.append(QStringLiteral("QFrame#frame {margin-bottom:2px;} QFrame#frame[target=\"true\"] "
"{background: palette(highlight);}"));
// group effect title bar
stylesheet.append(QStringLiteral("QFrame#framegroup {background: palette(dark);} "
"QFrame#framegroup[target=\"true\"] {background: palette(highlight);} "));
// draggable effect bar content
stylesheet.append(QStringLiteral("QProgressBar::chunk:horizontal {background: palette(button);border-top-left-radius: 4px;border-bottom-left-radius: 4px;} "
"QProgressBar::chunk:horizontal#dragOnly {background: %1;border-top-left-radius: 4px;border-bottom-left-radius: 4px;} "
"QProgressBar::chunk:horizontal:hover {background: %2;}")
.arg(alt_bg.name(), selected_bg.name()));
// draggable effect bar
stylesheet.append(QStringLiteral("QProgressBar:horizontal {border: 1px solid palette(dark);border-top-left-radius: 4px;border-bottom-left-radius: "
"4px;border-right:0px;background:%3;padding: 0px;text-align:left center} QProgressBar:horizontal:disabled {border: 1px "
"solid palette(button)} QProgressBar:horizontal#dragOnly {background: %3} QProgressBar:horizontal[inTimeline=\"true\"] { "
"border: 1px solid %1;border-right: 0px;background: %2;padding: 0px;text-align:left center } "
"QProgressBar::chunk:horizontal[inTimeline=\"true\"] {background: %1;}")
.arg(hover_bg.name(), light_bg.name(), alt_bg.name()));
// spin box for draggable widget
stylesheet.append(
QStringLiteral("QAbstractSpinBox#dragBox {border: 1px solid palette(dark);border-top-right-radius: 4px;border-bottom-right-radius: "
"4px;padding-right:0px;} QAbstractSpinBox::down-button#dragBox {width:0px;padding:0px;} QAbstractSpinBox:disabled#dragBox {border: 1px "
"solid palette(button);} QAbstractSpinBox::up-button#dragBox {width:0px;padding:0px;} QAbstractSpinBox[inTimeline=\"true\"]#dragBox { "
"border: 1px solid %1;} QAbstractSpinBox:hover#dragBox {border: 1px solid %2;} ")
.arg(hover_bg.name(), selected_bg.name()));
// group editable labels
stylesheet.append(QStringLiteral("MyEditableLabel { background-color: transparent; color: palette(bright-text); border-radius: 2px;border: 1px solid "
"transparent;} MyEditableLabel:hover {border: 1px solid palette(highlight);} "));
// transparent qcombobox
stylesheet.append(QStringLiteral("QComboBox { background-color: transparent;} "));
return stylesheet;
}
void AssetPanel::processSplitEffect(bool enable)
{
ObjectType id = m_effectStackWidget->stackOwner();
if (id == ObjectType::TimelineClip) {
emit doSplitEffect(enable);
} else if (id == ObjectType::BinClip) {
emit doSplitBinEffect(enable);
}
}
diff --git a/src/assets/assetpanel.hpp b/src/assets/assetpanel.hpp
index 2b15231d9..4b89f0816 100644
--- a/src/assets/assetpanel.hpp
+++ b/src/assets/assetpanel.hpp
@@ -1,86 +1,87 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef ASSETPANEL_H
#define ASSETPANEL_H
#include
#include
#include
class KSqueezedTextLabel;
class QToolButton;
/** @brief This class is the widget that provides interaction with the asset currently selected.
That is, it either displays an effectStack or the parameters of a transition
*/
class AssetParameterModel;
class AssetParameterView;
class EffectStackModel;
class EffectStackView;
class TransitionStackView;
class QLabel;
class AssetPanel : public QScrollArea
{
Q_OBJECT
public:
AssetPanel(QWidget *parent);
/* @brief Shows the parameters of the given transition model */
void showTransition(int tid, std::shared_ptr transition_model);
/* @brief Shows the parameters of the given effect stack model */
void showEffectStack(const QString &clipName, std::shared_ptr effectsModel, QPair range);
/* @brief Clear the panel so that it doesn't display anything */
void clear();
/* @brief This method should be called when the style changes */
void updatePalette();
public slots:
/** @brief Clear panel if displaying itemId */
void clearAssetPanel(int itemId);
void adjustAssetPanelRange(int itemId, int in, int out);
protected:
/** @brief Return the stylesheet used to display the panel (based on current palette). */
static const QString getStyleSheet();
QVBoxLayout *m_lay;
KSqueezedTextLabel *m_assetTitle;
TransitionStackView *m_transitionWidget;
EffectStackView *m_effectStackWidget;
private:
QToolButton *m_splitButton;
private slots:
void processSplitEffect(bool enable);
signals:
void doSplitEffect(bool);
void doSplitBinEffect(bool);
+ void seekToPos(int);
};
#endif
diff --git a/src/assets/keyframes/view/keyframeview.cpp b/src/assets/keyframes/view/keyframeview.cpp
index f4acdb092..371a862c3 100644
--- a/src/assets/keyframes/view/keyframeview.cpp
+++ b/src/assets/keyframes/view/keyframeview.cpp
@@ -1,280 +1,280 @@
/***************************************************************************
* Copyright (C) 2011 by Till Theato (root@ttill.de) *
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive (www.kdenlive.org). *
* *
* Kdenlive 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. *
* *
* Kdenlive 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 Kdenlive. If not, see . *
***************************************************************************/
#include "keyframeview.hpp"
#include "assets/keyframes/model/keyframemodellist.hpp"
#include "core.h"
#include "kdenlivesettings.h"
#include
#include
#include
#include
KeyframeView::KeyframeView(std::shared_ptr model, QWidget *parent)
: QWidget(parent)
, m_model(model)
, m_duration(1)
, m_position(0)
, m_currentKeyframe(-1)
, m_currentKeyframeOriginal(-1)
, m_hoverKeyframe(-1)
, m_scale(1)
, m_currentType(KeyframeType::Linear)
{
setMouseTracking(true);
setMinimumSize(QSize(150, 20));
setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum));
setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
QPalette p = palette();
KColorScheme scheme(p.currentColorGroup(), KColorScheme::Window, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
m_colSelected = palette().highlight().color();
m_colKeyframe = scheme.foreground(KColorScheme::NormalText).color();
m_size = QFontInfo(font()).pixelSize() * 1.8;
m_lineHeight = m_size / 2;
setMinimumHeight(m_size);
setMaximumHeight(m_size);
connect(m_model.get(), &KeyframeModelList::modelChanged, [&](){
emit atKeyframe(m_model->hasKeyframe(m_position));
update();
});
}
void KeyframeView::slotSetPosition(int pos)
{
if (pos != m_position) {
m_position = pos;
emit atKeyframe(m_model->hasKeyframe(pos));
- emit positionChanged(pos);
+ emit seekToPos(pos);
update();
}
}
void KeyframeView::slotAddKeyframe(int pos)
{
if (pos < 0) {
pos = m_position;
}
m_model->addKeyframe(GenTime(pos, pCore->getCurrentFps()), m_currentType);
}
void KeyframeView::slotAddRemove()
{
if (m_model->hasKeyframe(m_position)) {
slotRemoveKeyframe(m_position);
} else {
slotAddKeyframe(m_position);
}
}
void KeyframeView::slotRemoveKeyframe(int pos)
{
if (pos < 0) {
pos = m_position;
}
m_model->removeKeyframe(GenTime(pos, pCore->getCurrentFps()));
}
void KeyframeView::setDuration(int dur)
{
m_duration = dur;
}
void KeyframeView::slotGoToNext()
{
if (m_position == m_duration) {
return;
}
bool ok;
auto next = m_model->getNextKeyframe(GenTime(m_position, pCore->getCurrentFps()), &ok);
if (ok) {
slotSetPosition(next.first.frames(pCore->getCurrentFps()));
} else {
// no keyframe after current position
slotSetPosition(m_duration);
}
}
void KeyframeView::slotGoToPrev()
{
if (m_position == 0) {
return;
}
bool ok;
auto prev = m_model->getPrevKeyframe(GenTime(m_position, pCore->getCurrentFps()), &ok);
if (ok) {
slotSetPosition(prev.first.frames(pCore->getCurrentFps()));
} else {
// no keyframe after current position
slotSetPosition(m_duration);
}
}
void KeyframeView::mousePressEvent(QMouseEvent *event)
{
int pos = event->x() / m_scale;
if (event->y() < m_lineHeight && event->button() == Qt::LeftButton) {
bool ok;
GenTime position(pos, pCore->getCurrentFps());
auto keyframe = m_model->getClosestKeyframe(position, &ok);
if (ok && qAbs(keyframe.first.frames(pCore->getCurrentFps()) - pos) < 5) {
m_currentKeyframeOriginal = keyframe.first.frames(pCore->getCurrentFps());
if (m_model->moveKeyframe(keyframe.first, position, false)) {
m_currentKeyframe = pos;
slotSetPosition(pos);
return;
}
}
}
// no keyframe next to mouse
m_currentKeyframe = m_currentKeyframeOriginal = -1;
slotSetPosition(pos);
update();
}
void KeyframeView::mouseMoveEvent(QMouseEvent *event)
{
int pos = qBound(0, (int)(event->x() / m_scale), m_duration);
GenTime position(pos, pCore->getCurrentFps());
if ((event->buttons() & Qt::LeftButton) != 0u) {
if (m_currentKeyframe >= 0) {
if (!m_model->hasKeyframe(pos)) {
// snap to position cursor
if (KdenliveSettings::snaptopoints() && qAbs(pos - m_position) < 5 && !m_model->hasKeyframe(m_position)) {
pos = m_position;
}
GenTime currentPos(m_currentKeyframe, pCore->getCurrentFps());
if (m_model->moveKeyframe(currentPos, position, false)) {
m_currentKeyframe = pos;
}
}
}
slotSetPosition(pos);
return;
}
if (event->y() < m_lineHeight) {
bool ok;
auto keyframe = m_model->getClosestKeyframe(position, &ok);
if (ok && qAbs(keyframe.first.frames(pCore->getCurrentFps()) - pos) < 5) {
m_hoverKeyframe = keyframe.first.frames(pCore->getCurrentFps());
setCursor(Qt::PointingHandCursor);
update();
return;
}
}
if (m_hoverKeyframe != -1) {
m_hoverKeyframe = -1;
setCursor(Qt::ArrowCursor);
update();
}
}
void KeyframeView::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event)
if (m_currentKeyframe >= 0) {
GenTime initPos(m_currentKeyframeOriginal, pCore->getCurrentFps());
GenTime targetPos(m_currentKeyframe, pCore->getCurrentFps());
m_model->moveKeyframe(targetPos, initPos, false);
m_model->moveKeyframe(initPos, targetPos, true);
}
}
void KeyframeView::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && event->y() < m_lineHeight) {
int pos = qBound(0, (int)(event->x() / m_scale), m_duration);
GenTime position(pos, pCore->getCurrentFps());
bool ok;
auto keyframe = m_model->getClosestKeyframe(position, &ok);
if (ok && qAbs(keyframe.first.frames(pCore->getCurrentFps()) - pos) < 5) {
m_model->removeKeyframe(keyframe.first);
if (keyframe.first.frames(pCore->getCurrentFps()) == m_currentKeyframe) {
m_currentKeyframe = m_currentKeyframeOriginal = -1;
}
if (keyframe.first.frames(pCore->getCurrentFps()) == m_position) {
emit atKeyframe(false);
}
return;
}
// add new keyframe
m_model->addKeyframe(position, m_currentType);
} else {
QWidget::mouseDoubleClickEvent(event);
}
}
void KeyframeView::wheelEvent(QWheelEvent *event)
{
int change = event->delta() < 0 ? -1 : 1;
int pos = qBound(0, m_position + change, m_duration);
slotSetPosition(pos);
}
void KeyframeView::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QStylePainter p(this);
m_scale = width() / (double)(m_duration);
// p.translate(0, m_lineHeight);
int headOffset = m_lineHeight / 1.5;
/*
* keyframes
*/
for (const auto &keyframe : *m_model.get()) {
int pos = keyframe.first.frames(pCore->getCurrentFps());
if (pos == m_currentKeyframe || pos == m_hoverKeyframe) {
p.setBrush(m_colSelected);
} else {
p.setBrush(m_colKeyframe);
}
int scaledPos = pos * m_scale;
p.drawLine(scaledPos, headOffset, scaledPos, m_lineHeight + (headOffset / 2));
p.drawEllipse(scaledPos - headOffset / 2, 0, headOffset, headOffset);
}
p.setPen(palette().dark().color());
/*
* Time-"line"
*/
p.setPen(m_colKeyframe);
p.drawLine(0, m_lineHeight + (headOffset / 2), width(), m_lineHeight + (headOffset / 2));
/*
* current position
*/
QPolygon pa(3);
int cursorwidth = (m_size - (m_lineHeight + headOffset / 2)) / 2 + 1;
QPolygonF position = QPolygonF() << QPointF(-cursorwidth, m_size) << QPointF(cursorwidth, m_size) << QPointF(0, m_lineHeight + (headOffset / 2) + 1);
position.translate(m_position * m_scale, 0);
p.setBrush(m_colKeyframe);
p.drawPolygon(position);
}
diff --git a/src/assets/keyframes/view/keyframeview.hpp b/src/assets/keyframes/view/keyframeview.hpp
index ce0e4cae0..9180149ca 100644
--- a/src/assets/keyframes/view/keyframeview.hpp
+++ b/src/assets/keyframes/view/keyframeview.hpp
@@ -1,85 +1,85 @@
/***************************************************************************
* Copyright (C) 2011 by Till Theato (root@ttill.de) *
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive (www.kdenlive.org). *
* *
* Kdenlive 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. *
* *
* Kdenlive 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 Kdenlive. If not, see . *
***************************************************************************/
#ifndef KEYFRAMEVIEW_H
#define KEYFRAMEVIEW_H
#include "assets/keyframes/model/keyframemodel.hpp"
#include "assets/keyframes/model/keyframemodellist.hpp"
#include
#include
class KeyframeModelList;
class KeyframeView : public QWidget
{
Q_OBJECT
public:
explicit KeyframeView(std::shared_ptr model, QWidget *parent = nullptr);
void setDuration(int dur);
public slots:
/* @brief moves the current position*/
void slotSetPosition(int pos);
/* @brief remove the keyframe at given position
If pos is negative, we remove keyframe at current position
*/
void slotRemoveKeyframe(int pos);
/* @brief Add a keyframe with given parameter value at given pos.
If pos is negative, then keyframe is added at current position
*/
void slotAddKeyframe(int pos = -1);
/* @brief If there is a keyframe at current position, it is removed.
Otherwise, we add a new one with given value.
*/
void slotAddRemove();
void slotGoToNext();
void slotGoToPrev();
protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
private:
std::shared_ptr m_model;
int m_duration;
int m_position;
int m_currentKeyframe;
int m_currentKeyframeOriginal;
int m_hoverKeyframe;
int m_lineHeight;
double m_scale;
int m_size;
KeyframeType m_currentType;
QColor m_colSelected;
QColor m_colKeyframe;
QColor m_colKeyframeBg;
signals:
- void positionChanged(int pos);
+ void seekToPos(int pos);
void atKeyframe(bool);
};
#endif
diff --git a/src/assets/view/assetparameterview.cpp b/src/assets/view/assetparameterview.cpp
index 754530d1c..efb316a3d 100644
--- a/src/assets/view/assetparameterview.cpp
+++ b/src/assets/view/assetparameterview.cpp
@@ -1,156 +1,158 @@
/***************************************************************************
* 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 "core.h"
#include "widgets/animationwidget.h"
#include
#include
#include
#include
#include
AssetParameterView::AssetParameterView(QWidget *parent)
: QWidget(parent)
{
m_lay = new QVBoxLayout(this);
m_lay->setContentsMargins(2, 2, 2, 2);
m_lay->setSpacing(2);
setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
}
void AssetParameterView::setModel(const std::shared_ptr &model, QPair range, bool addSpacer)
{
qDebug() << "set model " << model.get();
unsetModel();
QMutexLocker lock(&m_lock);
m_model = model;
connect(m_model.get(), &AssetParameterModel::dataChanged, this, &AssetParameterView::refresh);
AnimationWidget *animWidget = nullptr;
for (int i = 0; i < model->rowCount(); ++i) {
QModelIndex index = model->index(i, 0);
auto type = model->data(index, AssetParameterModel::TypeRole).value();
if (animWidget && (type == ParamType::Geometry || type == ParamType::Animated || type == ParamType::RestrictedAnim)) {
// Animation widget can have some extra params that should'nt build a new widget
// TODO refac
// animWidget->addParameter(index);
} else {
auto w = AbstractParamWidget::construct(model, index, range, this);
if (type == ParamType::Geometry || type == ParamType::Animated || type == ParamType::RestrictedAnim || type == ParamType::AnimatedRect) {
animWidget = 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();
}
}
void AssetParameterView::resetValues()
{
QMutexLocker lock(&m_lock);
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();
QString defaultValue = m_model->data(index, AssetParameterModel::DefaultRole).toString();
m_model->setParameter(name, defaultValue);
refresh(index, index, QVector());
}
}
void AssetParameterView::setRange(QPair range)
{
+ qDebug() << "SETTING RANGE"<slotSetRange(range);
}
}
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
AssetCommand *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);
}
// 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
Q_ASSERT(bottomRight.row() < (int)m_widgets.size());
for (auto i = (size_t)topLeft.row(); i <= (size_t)bottomRight.row(); ++i) {
m_widgets[i]->slotRefresh();
}
}
int AssetParameterView::contentHeight() const
{
return m_lay->sizeHint().height();
}
diff --git a/src/assets/view/assetparameterview.hpp b/src/assets/view/assetparameterview.hpp
index 21fd54476..44e8f945d 100644
--- a/src/assets/view/assetparameterview.hpp
+++ b/src/assets/view/assetparameterview.hpp
@@ -1,82 +1,85 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef ASSETPARAMETERVIEW_H
#define ASSETPARAMETERVIEW_H
#include
#include
#include
#include
#include
/* @brief This class is the view for a list of parameters.
*/
class QVBoxLayout;
class AbstractParamWidget;
class AssetParameterModel;
class AssetParameterView : public QWidget
{
Q_OBJECT
public:
AssetParameterView(QWidget *parent = nullptr);
/** Sets the model to be displayed by current view */
virtual void setModel(const std::shared_ptr &model, QPair range, bool addSpacer = false);
/** Set the widget to display no model (this yield ownership on the smart-ptr)*/
void unsetModel();
/** Returns the preferred widget height */
int contentHeight() const;
/** Reset all parameter values to default */
void resetValues();
/** The parent clip in/out points changed, update effects */
void setRange(QPair range);
protected:
/** @brief This is a handler for the dataChanged slot of the model.
It basically instructs the widgets in the given range to be refreshed */
void refresh(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles);
QVBoxLayout *m_lay;
/** @brief Protect from concurrent operations
**/
QMutex m_lock;
std::shared_ptr m_model;
std::vector m_widgets;
private slots:
/** @brief Apply a change of parameter sent by the view
@param index is the index corresponding to the modified param
@param value is the new value of the parameter
@param storeUndo: if true, an undo object is created
*/
void commitChanges(const QModelIndex &index, const QString &value, bool storeUndo);
+
+signals:
+ void seekToPos(int);
};
#endif
diff --git a/src/assets/view/widgets/abstractparamwidget.hpp b/src/assets/view/widgets/abstractparamwidget.hpp
index 254c86173..07511ce6d 100644
--- a/src/assets/view/widgets/abstractparamwidget.hpp
+++ b/src/assets/view/widgets/abstractparamwidget.hpp
@@ -1,81 +1,83 @@
/***************************************************************************
* Copyright (C) 2016 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 . *
***************************************************************************/
#ifndef ABSTRACTPARAMWIDGET_H
#define ABSTRACTPARAMWIDGET_H
#include
#include
#include
#include
/** @brief Base class of all the widgets representing a parameter of an asset (effect or transition)
*/
class AssetParameterModel;
class AbstractParamWidget : public QWidget
{
Q_OBJECT
public:
AbstractParamWidget() = delete;
AbstractParamWidget(std::shared_ptr model, QModelIndex index, QWidget *parent);
virtual ~AbstractParamWidget(){};
/** @brief Factory method to construct a parameter widget.
@param model Parameter model this parameter belongs to
@param index Index of the parameter in the given model
@param range For timeline clips, the in/out point in playlist
@param parent parent widget
*/
static AbstractParamWidget *construct(const std::shared_ptr &model, QModelIndex index, QPair range, QWidget *parent);
signals:
/** @brief Signal sent when the parameters hold by the widgets are modified
The index corresponds which parameter is changed
The string is the new value
The bool allows to decide whether an undo object should be created
*/
void valueChanged(QModelIndex, QString, bool);
/* @brief Signal sent when the filter needs to be deactivated or reactivated.
This happens for example when the user has to pick a color.
*/
void disableCurrentFilter(bool);
+
+ void seekToPos(int);
public slots:
/** @brief Toggle the comments on or off
*/
virtual void slotShowComment(bool) { qDebug() << "DEBUG: show_comment not correctly overriden"; }
/** @brief refresh the properties to reflect changes in the model
*/
virtual void slotRefresh() = 0;
/** @brief update the clip's in/out point
*/
virtual void slotSetRange(QPair range) = 0;
protected:
std::shared_ptr m_model;
QPersistentModelIndex m_index;
};
#endif
diff --git a/src/assets/view/widgets/keyframewidget.cpp b/src/assets/view/widgets/keyframewidget.cpp
index 9aa3db2a6..023f509b1 100644
--- a/src/assets/view/widgets/keyframewidget.cpp
+++ b/src/assets/view/widgets/keyframewidget.cpp
@@ -1,155 +1,155 @@
/***************************************************************************
* Copyright (C) 2011 by Till Theato (root@ttill.de) *
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive (www.kdenlive.org). *
* *
* Kdenlive 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. *
* *
* Kdenlive 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 Kdenlive. If not, see . *
***************************************************************************/
#include "keyframewidget.hpp"
#include "assets/keyframes/view/keyframeview.hpp"
#include "assets/keyframes/model/keyframemodellist.hpp"
#include "assets/model/assetparametermodel.hpp"
#include "core.h"
#include "monitor/monitor.h"
#include "timecode.h"
#include "timecodedisplay.h"
#include "utils/KoIconUtils.h"
#include
#include
#include
KeyframeWidget::KeyframeWidget(std::shared_ptr model, QModelIndex index, QWidget *parent)
: AbstractParamWidget(std::move(model), index, parent)
{
m_keyframes = std::shared_ptr(new KeyframeModelList(model, index, pCore->undoStack()));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
auto *l = new QGridLayout(this);
bool ok = false;
int duration = m_model->data(m_index, AssetParameterModel::ParentDurationRole).toInt(&ok);
Q_ASSERT(ok);
m_keyframeview = new KeyframeView(m_keyframes, this);
m_keyframeview->setDuration(duration);
m_buttonAddDelete = new QToolButton(this);
m_buttonAddDelete->setAutoRaise(true);
m_buttonAddDelete->setIcon(KoIconUtils::themedIcon(QStringLiteral("list-add")));
m_buttonAddDelete->setToolTip(i18n("Add keyframe"));
m_buttonPrevious = new QToolButton(this);
m_buttonPrevious->setAutoRaise(true);
m_buttonPrevious->setIcon(KoIconUtils::themedIcon(QStringLiteral("media-skip-backward")));
m_buttonPrevious->setToolTip(i18n("Go to previous keyframe"));
m_buttonNext = new QToolButton(this);
m_buttonNext->setAutoRaise(true);
m_buttonNext->setIcon(KoIconUtils::themedIcon(QStringLiteral("media-skip-forward")));
m_buttonNext->setToolTip(i18n("Go to next keyframe"));
m_time = new TimecodeDisplay(pCore->getMonitor(m_model->monitorId)->timecode(), this);
m_time->setRange(0, duration);
l->addWidget(m_keyframeview, 0, 0, 1, -1);
l->addWidget(m_buttonPrevious, 1, 0);
l->addWidget(m_buttonAddDelete, 1, 1);
l->addWidget(m_buttonNext, 1, 2);
l->addWidget(m_time, 1, 3, Qt::AlignRight);
connect(m_time, &TimecodeDisplay::timeCodeEditingFinished, [&](){slotSetPosition(-1, true);});
- connect(m_keyframeview, &KeyframeView::positionChanged, [&](int p){slotSetPosition(p, true);});
+ connect(m_keyframeview, &KeyframeView::seekToPos, [&](int p){slotSetPosition(p, true);});
connect(m_keyframeview, &KeyframeView::atKeyframe, this, &KeyframeWidget::slotAtKeyframe);
connect(m_buttonAddDelete, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotAddRemove);
connect(m_buttonPrevious, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotGoToPrev);
connect(m_buttonNext, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotGoToNext);
}
KeyframeWidget::~KeyframeWidget()
{
delete m_keyframeview;
delete m_buttonAddDelete;
delete m_buttonPrevious;
delete m_buttonNext;
delete m_time;
}
void KeyframeWidget::slotSetPosition(int pos, bool update)
{
if (pos < 0) {
pos = m_time->getValue();
m_keyframeview->slotSetPosition(pos);
} else {
m_time->setValue(pos);
m_keyframeview->slotSetPosition(pos);
}
if (update) {
- emit positionChanged(pos);
+ emit seekToPos(pos);
}
}
int KeyframeWidget::getPosition() const
{
return m_time->getValue();
}
void KeyframeWidget::addKeyframe(int pos)
{
blockSignals(true);
m_keyframeview->slotAddKeyframe(pos);
blockSignals(false);
setEnabled(true);
}
void KeyframeWidget::updateTimecodeFormat()
{
m_time->slotUpdateTimeCodeFormat();
}
void KeyframeWidget::slotAtKeyframe(bool atKeyframe)
{
if (atKeyframe) {
m_buttonAddDelete->setIcon(KoIconUtils::themedIcon(QStringLiteral("list-remove")));
m_buttonAddDelete->setToolTip(i18n("Delete keyframe"));
} else {
m_buttonAddDelete->setIcon(KoIconUtils::themedIcon(QStringLiteral("list-add")));
m_buttonAddDelete->setToolTip(i18n("Add keyframe"));
}
}
void KeyframeWidget::slotSetRange(QPair /*range*/)
{
bool ok = false;
int duration = m_model->data(m_index, AssetParameterModel::ParentDurationRole).toInt(&ok);
Q_ASSERT(ok);
m_keyframeview->setDuration(duration);
m_time->setRange(0, duration);
}
void KeyframeWidget::slotRefresh()
{
// update duration
slotSetRange(QPair(-1, -1));
// refresh keyframes
m_keyframes->refresh();
}
diff --git a/src/assets/view/widgets/keyframewidget.hpp b/src/assets/view/widgets/keyframewidget.hpp
index 95226e648..6a06557ba 100644
--- a/src/assets/view/widgets/keyframewidget.hpp
+++ b/src/assets/view/widgets/keyframewidget.hpp
@@ -1,68 +1,65 @@
/***************************************************************************
* Copyright (C) 2011 by Till Theato (root@ttill.de) *
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive (www.kdenlive.org). *
* *
* Kdenlive 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. *
* *
* Kdenlive 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 Kdenlive. If not, see . *
***************************************************************************/
#ifndef KEYFRAMEWIDGET_H
#define KEYFRAMEWIDGET_H
#include "abstractparamwidget.hpp"
#include
#include
class AssetParameterModel;
class KeyframeModelList;
class KeyframeView;
class QToolButton;
class TimecodeDisplay;
class KeyframeWidget : public AbstractParamWidget
{
Q_OBJECT
public:
explicit KeyframeWidget(std::shared_ptr model, QModelIndex index, QWidget *parent = nullptr);
~KeyframeWidget();
int getPosition() const;
void addKeyframe(int pos = -1);
void updateTimecodeFormat();
void slotSetRange(QPair range) override;
void slotRefresh() override;
public slots:
void slotSetPosition(int pos = -1, bool update = true);
private slots:
void slotAtKeyframe(bool atKeyframe);
-signals:
- void positionChanged(int pos);
-
private:
std::shared_ptr m_keyframes;
KeyframeView *m_keyframeview;
QToolButton *m_buttonAddDelete;
QToolButton *m_buttonPrevious;
QToolButton *m_buttonNext;
TimecodeDisplay *m_time;
};
#endif
diff --git a/src/effects/effectstack/view/abstractcollapsiblewidget.h b/src/effects/effectstack/view/abstractcollapsiblewidget.h
index 20790edd7..d9901b1c3 100644
--- a/src/effects/effectstack/view/abstractcollapsiblewidget.h
+++ b/src/effects/effectstack/view/abstractcollapsiblewidget.h
@@ -1,47 +1,49 @@
/***************************************************************************
* Copyright (C) 2012 by Jean-Baptiste Mardelle (jb@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) 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, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#ifndef ABSTRACTCOLLAPSIBLEWIDGET_H
#define ABSTRACTCOLLAPSIBLEWIDGET_H
#include "ui_collapsiblewidget_ui.h"
#include
#include
class AbstractCollapsibleWidget : public QWidget, public Ui::CollapsibleWidget_UI
{
Q_OBJECT
public:
explicit AbstractCollapsibleWidget(QWidget *parent = nullptr);
virtual void setActive(bool activate) = 0;
virtual bool isGroup() const = 0;
signals:
void addEffect(const QDomElement &e);
/** @brief Move effects in the stack one step up or down. */
void changeEffectPosition(const QList &, bool upwards);
/** @brief Move effects in the stack. */
void moveEffect(const QList ¤t_pos, int new_pos, int groupIndex, const QString &groupName);
/** @brief An effect was saved, trigger effect list reload. */
void reloadEffects();
+
+ void seekToPos(int);
};
#endif
diff --git a/src/effects/effectstack/view/collapsibleeffectview.cpp b/src/effects/effectstack/view/collapsibleeffectview.cpp
index d2bc53f9b..02f37907a 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 "effectslist/effectslist.h"
#include "kdenlivesettings.h"
#include "mltcontroller/effectscontroller.h"
#include "utils/KoIconUtils.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(std::shared_ptr effectModel, QPair range, QImage icon, QWidget *parent)
: AbstractCollapsibleWidget(parent)
/* , m_effect(effect)
, m_itemInfo(info)
, m_original_effect(original_effect)
, m_isMovable(true)*/
, m_model(effectModel)
, m_view(nullptr)
, 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;
// decoframe->setProperty("active", true);
// m_info.fromString(effect.attribute(QStringLiteral("kdenlive_info")));
// setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
buttonUp->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-up")));
QSize iconSize = buttonUp->iconSize();
buttonUp->setMaximumSize(iconSize);
buttonDown->setMaximumSize(iconSize);
menuButton->setMaximumSize(iconSize);
enabledButton->setMaximumSize(iconSize);
buttonDel->setMaximumSize(iconSize);
buttonUp->setToolTip(i18n("Move effect up"));
buttonDown->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-down")));
buttonDown->setToolTip(i18n("Move effect down"));
buttonDel->setIcon(KoIconUtils::themedIcon(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(KoIconUtils::themedIcon("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(KoIconUtils::themedIcon(QStringLiteral("arrow-right")));
m_collapse->setInactiveIcon(KoIconUtils::themedIcon(QStringLiteral("arrow-down")));
collapseButton->setDefaultAction(m_collapse);
connect(m_collapse, &KDualAction::activeChanged, this, &CollapsibleEffectView::slotSwitch);
QHBoxLayout *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_enabledButton = new KDualAction(i18n("Disable Effect"), i18n("Enable Effect"), this);
m_enabledButton->setActiveIcon(KoIconUtils::themedIcon(QStringLiteral("hint")));
m_enabledButton->setInactiveIcon(KoIconUtils::themedIcon(QStringLiteral("visibility")));
enabledButton->setDefaultAction(m_enabledButton);
m_groupAction = new QAction(KoIconUtils::themedIcon(QStringLiteral("folder-new")), i18n("Create Group"), this);
connect(m_groupAction, &QAction::triggered, this, &CollapsibleEffectView::slotCreateGroup);
if (m_regionEffect) {
effectName.append(':' + QUrl(EffectsList::parameter(m_effect, QStringLiteral("resource"))).fileName());
}
// Color thumb
m_colorIcon->setPixmap(QPixmap::fromImage(icon));
title->setText(effectName);
m_view = new AssetParameterView(this);
m_view->setModel(std::static_pointer_cast(effectModel), range);
+ connect(m_view, &AssetParameterView::seekToPos, this, &AbstractCollapsibleWidget::seekToPos);
QVBoxLayout *lay = new QVBoxLayout(widgetFrame);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(0);
lay->addWidget(m_view);
m_menu = new QMenu(this);
if (effectModel->rowCount() > 0) {
m_menu->addAction(KoIconUtils::themedIcon(QStringLiteral("view-refresh")), i18n("Reset Effect"), this, SLOT(slotResetEffect()));
} else {
collapseButton->setEnabled(false);
m_view->setVisible(false);
}
m_menu->addAction(KoIconUtils::themedIcon(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(KoIconUtils::themedIcon(QStringLiteral("folder-new")), i18n("Create Region"), this, SLOT(slotCreateRegion()));
}
// setupWidget(info, metaInfo);
menuButton->setIcon(KoIconUtils::themedIcon(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, SIGNAL(activeChangedByUser(bool)), this, SLOT(slotDisable(bool)));
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);
}
}
CollapsibleEffectView::~CollapsibleEffectView()
{
// delete m_paramWidget;
delete m_menu;
}
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) {
QWheelEvent *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;
}
e->ignore();
return true;
}
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;
}
e->ignore();
return true;
}
}
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)
{
decoframe->setProperty("active", ix.row() == m_model->row());
decoframe->setStyleSheet(decoframe->styleSheet());
}
void CollapsibleEffectView::setActive(bool activate)
{
/*
decoframe->setProperty("active", activate);
decoframe->setStyleSheet(decoframe->styleSheet());
if (m_paramWidget) {
m_paramWidget->connectMonitor(activate);
}
if (activate) {
m_colorIcon->setPixmap(m_iconPix);
} else {
// desaturate icon
QPixmap alpha = m_iconPix;
QPainter p(&alpha);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.fillRect(alpha.rect(), QColor(80, 80, 80, 80));
p.end();
m_colorIcon->setPixmap(alpha);
}
*/
}
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() + 1, 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;
QDomElement effect = m_effect.cloneNode().toElement();
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 reloadEffects();
}
void CollapsibleEffectView::slotResetEffect()
{
m_view->resetValues();
}
void CollapsibleEffectView::slotSwitch(bool expand)
{
slotShow(expand);
emit switchHeight(m_model, expand ? frame->height() + 4 : frame->height() + m_view->contentHeight() + 4);
setFixedHeight(expand ? frame->height() + 4 : frame->height() + m_view->contentHeight() + 4);
widgetFrame->setVisible(!expand);
/*if (!expand) {
widgetFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
widgetFrame->setFixedHeight(m_view->contentHeight());
} else {
widgetFrame->setFixedHeight(QWIDGETSIZE_MAX);
}*/
/*const QRect final_geometry = expand ? QRect(0, 0, width(), title->height()) : QRect(rect().topLeft(), size());
QPropertyAnimation *anim = new QPropertyAnimation(this, "geometry", this);
anim->setDuration(200);
anim->setEasingCurve(QEasingCurve::InOutQuad);
anim->setEndValue(final_geometry);
//connect(anim, SIGNAL(valueChanged(const QVariant &)), SLOT(animationChanged(const QVariant &)));
connect(anim, SIGNAL(finished()), SLOT(animationFinished()));
anim->start(QPropertyAnimation::DeleteWhenStopped);*/
}
void CollapsibleEffectView::animationChanged(const QVariant &geom)
{
parentWidget()->setFixedHeight(geom.toRect().height());
}
void CollapsibleEffectView::animationFinished()
{
if (m_collapse->isActive()) {
widgetFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
} else {
widgetFrame->setFixedHeight(m_view->contentHeight());
}
}
void CollapsibleEffectView::slotShow(bool show)
{
if (show) {
// collapseButton->setArrowType(Qt::DownArrow);
m_info.isCollapsed = false;
} else {
// collapseButton->setArrowType(Qt::RightArrow);
m_info.isCollapsed = true;
}
updateCollapsedState();
}
void CollapsibleEffectView::groupStateChanged(bool collapsed)
{
m_info.groupIsCollapsed = collapsed;
updateCollapsedState();
}
void CollapsibleEffectView::updateCollapsedState()
{
QString info = m_info.toString();
if (info != m_effect.attribute(QStringLiteral("kdenlive_info"))) {
m_effect.setAttribute(QStringLiteral("kdenlive_info"), info);
emit parameterChanged(m_original_effect, m_effect, effectIndex());
}
}
void CollapsibleEffectView::setGroupIndex(int 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)
{
m_info.groupName = groupName;
m_effect.setAttribute(QStringLiteral("kdenlive_info"), m_info.toString());
}
QString CollapsibleEffectView::infoString() const
{
return 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 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)
{
/*
if (m_paramWidget) {
m_paramWidget->setActiveKeyframe(kf);
}
*/
}
void CollapsibleEffectView::setupWidget(const ItemInfo &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)
{
/*
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;
}
EffectInfo info;
info.fromString(subeffects.at(0).toElement().attribute(QStringLiteral("kdenlive_info")));
event->setDropAction(Qt::MoveAction);
event->accept();
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);
}
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_paramWidget != nullptr) && !m_enabledButton->isActive()) {
return m_paramWidget->needsMonitorEffectScene();
}
*/
return MonitorSceneDefault;
}
void CollapsibleEffectView::setRange(QPair range)
{
if (m_view) {
m_view->setRange(range);
}
}
void CollapsibleEffectView::setKeyframes(const QString &tag, const QString &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());
}
diff --git a/src/effects/effectstack/view/collapsibleeffectview.hpp b/src/effects/effectstack/view/collapsibleeffectview.hpp
index fc411b67a..5314877a1 100644
--- a/src/effects/effectstack/view/collapsibleeffectview.hpp
+++ b/src/effects/effectstack/view/collapsibleeffectview.hpp
@@ -1,173 +1,173 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef COLLAPSIBLEEFFECTVIEW_H
#define COLLAPSIBLEEFFECTVIEW_H
#include "mltcontroller/effectscontroller.h"
#include "timecode.h"
#include "abstractcollapsiblewidget.h"
#include
#include
class QLabel;
class KDualAction;
class EffectItemModel;
class AssetParameterView;
/**)
* @class CollapsibleEffectView
- * @brief A dialog for editing markers and guides.
+ * @brief A container for the parameters of an effect
* @author Jean-Baptiste Mardelle
*/
class CollapsibleEffectView : public AbstractCollapsibleWidget
{
Q_OBJECT
public:
explicit CollapsibleEffectView(std::shared_ptr effectModel, QPair range, QImage icon, QWidget *parent = nullptr);
~CollapsibleEffectView();
QLabel *title;
void setupWidget(const ItemInfo &info);
void updateTimecodeFormat();
void setActive(bool activate) override;
/** @brief Install event filter so that scrolling with mouse wheel does not change parameter value. */
bool eventFilter(QObject *o, QEvent *e) override;
/** @brief Update effect GUI to reflect parameted changes. */
void updateWidget(const ItemInfo &info, const QDomElement &effect);
/** @brief Returns effect xml. */
QDomElement effect() const;
/** @brief Returns effect xml with keyframe offset for saving. */
QDomElement effectForSave() const;
int groupIndex() const;
bool isGroup() const override;
int effectIndex() const;
void setGroupIndex(int ix);
void setGroupName(const QString &groupName);
/** @brief Remove this effect from its group. */
void removeFromGroup();
QString infoString() const;
bool isActive() const;
bool isEnabled() const;
/** @brief Should the wheel event be sent to parent widget for scrolling. */
bool filterWheelEvent;
/** @brief Parent group was collapsed, update. */
void groupStateChanged(bool collapsed);
/** @brief Show / hide up / down buttons. */
void adjustButtons(int ix, int max);
/** @brief Returns this effect's monitor scene type if any is needed. */
MonitorSceneType needsMonitorEffectScene() const;
/** @brief Set clip in / out points. */
void setRange(QPair range);
/** @brief Import keyframes from a clip's data. */
void setKeyframes(const QString &tag, const QString &keyframes);
/** @brief Pass frame size info (dar, etc). */
void updateFrameInfo();
/** @brief Select active keyframe. */
void setActiveKeyframe(int kf);
/** @brief Returns true if effect can be moved (false for speed effect). */
bool isMovable() const;
public slots:
void slotSyncEffectsPos(int pos);
void slotDisable(bool disable);
void slotResetEffect();
void importKeyframes(const QString &keyframes);
void slotActivateEffect(QModelIndex ix);
private slots:
void setWidgetHeight(qreal value);
void animationFinished();
private slots:
void slotSwitch(bool expand);
void slotShow(bool show);
void slotDeleteEffect();
void slotEffectUp();
void slotEffectDown();
void slotSaveEffect();
void slotCreateGroup();
void slotCreateRegion();
void slotUnGroup();
/** @brief A sub effect parameter was changed */
void slotUpdateRegionEffectParams(const QDomElement & /*old*/, const QDomElement & /*e*/, int /*ix*/);
void prepareImportClipKeyframes();
void animationChanged(const QVariant &geom);
private:
AssetParameterView *m_view;
std::shared_ptr m_model;
KDualAction *m_collapse;
QList m_subParamWidgets;
QDomElement m_effect;
ItemInfo m_itemInfo;
QDomElement m_original_effect;
QList m_subEffects;
QMenu *m_menu;
EffectInfo m_info;
bool m_isMovable;
/** @brief True if this is a region effect, which behaves in a special way, like a group. */
bool m_regionEffect;
/** @brief The add group action. */
QAction *m_groupAction;
KDualAction *m_enabledButton;
QLabel *m_colorIcon;
QPixmap m_iconPix;
QPoint m_dragStart;
/** @brief Check if collapsed state changed and inform MLT. */
void updateCollapsedState();
protected:
void mouseDoubleClickEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dropEvent(QDropEvent *event) override;
signals:
void parameterChanged(const QDomElement &, const QDomElement &, int);
void syncEffectsPos(int);
void effectStateChanged(bool, int ix, MonitorSceneType effectNeedsMonitorScene);
void deleteEffect(std::shared_ptr effect);
void moveEffect(int destRow, std::shared_ptr effect);
void checkMonitorPosition(int);
void seekTimeline(int);
/** @brief Start an MLT filter job on this clip. */
void startFilterJob(QMap &, QMap &, QMap &);
/** @brief An effect was reset, trigger param reload. */
void resetEffect(int ix);
/** @brief Ask for creation of a group. */
void createGroup(std::shared_ptr effectModel);
void unGroup(CollapsibleEffectView *);
void createRegion(int, const QUrl &);
void deleteGroup(const QDomDocument &);
void importClipKeyframes(GraphicsRectItem, ItemInfo, QDomElement, const QMap &keyframes = QMap());
void switchHeight(std::shared_ptr model, int height);
void startDrag(QPixmap, std::shared_ptr effectModel);
void activateEffect(std::shared_ptr effectModel);
};
#endif
diff --git a/src/effects/effectstack/view/effectstackview.cpp b/src/effects/effectstack/view/effectstackview.cpp
index f4ce42b4d..17ee74827 100644
--- a/src/effects/effectstack/view/effectstackview.cpp
+++ b/src/effects/effectstack/view/effectstackview.cpp
@@ -1,269 +1,275 @@
/***************************************************************************
* 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 "effectstackview.hpp"
#include "assets/assetlist/view/qmltypes/asseticonprovider.hpp"
#include "assets/view/assetparameterview.hpp"
#include "collapsibleeffectview.hpp"
+#include "core.h"
#include "effects/effectstack/model/effectitemmodel.hpp"
#include "effects/effectstack/model/effectstackmodel.hpp"
#include
#include
#include
#include
#include
#include
#include
WidgetDelegate::WidgetDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
QSize WidgetDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QSize s = QStyledItemDelegate::sizeHint(option, index);
if (m_height.contains(index)) {
s.setHeight(m_height.value(index));
}
return s;
}
void WidgetDelegate::setHeight(const QModelIndex &index, int height)
{
m_height[index] = height;
emit sizeHintChanged(index);
}
void WidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem opt(option);
initStyleOption(&opt, index);
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
// QRect r = QStyle::alignedRect(opt.direction, Qt::AlignVCenter | Qt::AlignLeft, opt.decorationSize, r1);
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
}
EffectStackView::EffectStackView(QWidget *parent)
: QWidget(parent)
, m_thumbnailer(new AssetIconProvider(true))
, m_range(-1, -1)
, m_model(nullptr)
{
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_lay = new QVBoxLayout(this);
m_lay->setContentsMargins(0, 0, 0, 0);
m_lay->setSpacing(0);
setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
setAcceptDrops(true);
m_effectsTree = new QTreeView(this);
m_effectsTree->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_effectsTree->setHeaderHidden(true);
m_effectsTree->setRootIsDecorated(false);
QString style = QStringLiteral("QTreeView {border: none;}");
// m_effectsTree->viewport()->setAutoFillBackground(false);
m_effectsTree->setStyleSheet(style);
m_lay->addWidget(m_effectsTree);
}
EffectStackView::~EffectStackView()
{
delete m_thumbnailer;
}
void EffectStackView::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat(QStringLiteral("kdenlive/effect"))) {
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
} else {
event->setDropAction(Qt::CopyAction);
}
event->setAccepted(true);
} else {
event->setAccepted(false);
}
}
void EffectStackView::dropEvent(QDropEvent *event)
{
event->accept();
QString effectId = event->mimeData()->data(QStringLiteral("kdenlive/effect"));
int row = m_model->rowCount();
for (int i = 0; i < m_model->rowCount(); i++) {
auto item = m_model->getEffectStackRow(i);
if (item->childCount() > 0) {
// TODO: group
continue;
}
std::shared_ptr eff = std::static_pointer_cast(item);
QModelIndex ix = m_model->getIndexFromItem(eff);
QWidget *w = m_effectsTree->indexWidget(ix);
if (w && w->geometry().contains(event->pos())) {
qDebug() << "// DROPPED ON EFF: " << eff->getAssetId();
row = i;
break;
}
}
if (event->source() == this) {
int oldRow = event->mimeData()->data(QStringLiteral("kdenlive/effectrow")).toInt();
qDebug() << "// MOVING EFFECT FROM : " << oldRow << " TO " << row;
m_model->moveEffect(row, m_model->getEffectStackRow(oldRow));
} else {
if (row < m_model->rowCount()) {
m_model->appendEffect(effectId);
m_model->moveEffect(row, m_model->getEffectStackRow(m_model->rowCount() - 1));
} else {
m_model->appendEffect(effectId);
}
}
}
void EffectStackView::setModel(std::shared_ptr model, QPair range)
{
m_mutex.lock();
unsetModel();
m_model = model;
m_effectsTree->setModel(m_model.get());
m_effectsTree->setItemDelegateForColumn(0, new WidgetDelegate(this));
m_effectsTree->setColumnHidden(1, true);
m_effectsTree->setAcceptDrops(true);
m_effectsTree->setDragDropMode(QAbstractItemView::DragDrop);
m_effectsTree->setDragEnabled(true);
m_effectsTree->setUniformRowHeights(false);
m_mutex.unlock();
loadEffects(range);
connect(m_model.get(), &EffectStackModel::dataChanged, this, &EffectStackView::refresh);
}
void EffectStackView::loadEffects(QPair range, int start, int end)
{
QMutexLocker lock (&m_mutex);
m_range = range;
int max = m_model->rowCount();
if (end == -1) {
end = max;
}
int active = m_model->getActiveEffect();
for (int i = 0; i < end; i++) {
std::shared_ptr item = m_model->getEffectStackRow(i);
QSize size;
if (item->childCount() > 0) {
// group, create sub stack
continue;
}
std::shared_ptr effectModel = std::static_pointer_cast(item);
QImage effectIcon = m_thumbnailer->requestImage(effectModel->getAssetId(), &size, QSize(QStyle::PM_SmallIconSize, QStyle::PM_SmallIconSize));
CollapsibleEffectView *view = new CollapsibleEffectView(effectModel, range, effectIcon, this);
qDebug() << "__ADDING EFFECT: " << effectModel->filter().get("id") << ", ACT: " << active;
if (i == active) {
view->slotActivateEffect(m_model->getIndexFromItem(effectModel));
}
view->buttonUp->setEnabled(i > 0);
view->buttonDown->setEnabled(i < max - 1);
connect(view, &CollapsibleEffectView::deleteEffect, m_model.get(), &EffectStackModel::removeEffect);
connect(view, &CollapsibleEffectView::moveEffect, m_model.get(), &EffectStackModel::moveEffect);
connect(view, &CollapsibleEffectView::switchHeight, this, &EffectStackView::slotAdjustDelegate, Qt::DirectConnection);
connect(view, &CollapsibleEffectView::startDrag, this, &EffectStackView::slotStartDrag);
connect(view, &CollapsibleEffectView::createGroup, m_model.get(), &EffectStackModel::slotCreateGroup);
connect(view, &CollapsibleEffectView::activateEffect, this, &EffectStackView::slotActivateEffect);
+ connect(view, &CollapsibleEffectView::seekToPos, [this](int pos){
+ // at this point, the effects returns a pos relative to the clip. We need to convert it to a global time
+ int clipIn = pCore->getItemIn(m_model->getOwnerId());
+ emit seekToPos(pos + clipIn);
+ });
connect(this, &EffectStackView::doActivateEffect, view, &CollapsibleEffectView::slotActivateEffect);
QModelIndex ix = m_model->getIndexFromItem(effectModel);
m_effectsTree->setIndexWidget(ix, view);
}
}
void EffectStackView::slotActivateEffect(std::shared_ptr effectModel)
{
QMutexLocker lock (&m_mutex);
m_model->setActiveEffect(effectModel->row());
QModelIndex activeIx = m_model->getIndexFromItem(effectModel);
emit doActivateEffect(activeIx);
}
void EffectStackView::slotStartDrag(QPixmap pix, std::shared_ptr effectModel)
{
auto *drag = new QDrag(this);
drag->setPixmap(pix);
auto *mime = new QMimeData;
mime->setData(QStringLiteral("kdenlive/effect"), effectModel->getAssetId().toUtf8());
// TODO this will break if source effect is not on the stack of a timeline clip
QByteArray effectSource;
effectSource += QString::number((int) effectModel->getOwnerId().first).toUtf8();
effectSource += '-';
effectSource += QString::number((int) effectModel->getOwnerId().second).toUtf8();
effectSource += '-';
effectSource += QString::number(effectModel->row()).toUtf8();
mime->setData(QStringLiteral("kdenlive/effectsource"), effectSource);
//mime->setData(QStringLiteral("kdenlive/effectrow"), QString::number(effectModel->row()).toUtf8());
// Assign ownership of the QMimeData object to the QDrag object.
drag->setMimeData(mime);
// Start the drag and drop operation
drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
}
void EffectStackView::slotAdjustDelegate(std::shared_ptr effectModel, int height)
{
QMutexLocker lock (&m_mutex);
QModelIndex ix = m_model->getIndexFromItem(effectModel);
WidgetDelegate *del = static_cast(m_effectsTree->itemDelegate(ix));
del->setHeight(ix, height);
}
void EffectStackView::refresh(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles)
{
loadEffects(m_range, topLeft.row(), bottomRight.row() + 1);
}
void EffectStackView::unsetModel(bool reset)
{
// Release ownership of smart pointer
if (reset) {
m_model.reset();
}
}
void EffectStackView::setRange(int in, int out)
{
QMutexLocker lock (&m_mutex);
m_range.first = in;
m_range.second = out;
int max = m_model->rowCount();
for (int i = 0; i < max; i++) {
std::shared_ptr item = m_model->getEffectStackRow(i);
std::shared_ptr eff = std::static_pointer_cast(item);
QModelIndex ix = m_model->getIndexFromItem(eff);
auto w = m_effectsTree->indexWidget(ix);
static_cast(w)->setRange(m_range);
}
}
ObjectType EffectStackView::stackOwner() const
{
if (m_model) {
return m_model->getOwnerId().first;
}
return ObjectType::NoItem;
}
diff --git a/src/effects/effectstack/view/effectstackview.hpp b/src/effects/effectstack/view/effectstackview.hpp
index 3460208eb..67a307772 100644
--- a/src/effects/effectstack/view/effectstackview.hpp
+++ b/src/effects/effectstack/view/effectstackview.hpp
@@ -1,89 +1,90 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef EFFECTSTACKVIEW_H
#define EFFECTSTACKVIEW_H
#include
#include
#include
#include
#include "definitions.h"
class QVBoxLayout;
class QTreeView;
class CollapsibleEffectView;
class AssetParameterModel;
class EffectStackModel;
class EffectItemModel;
class AssetIconProvider;
class WidgetDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit WidgetDelegate(QObject *parent = nullptr);
void setHeight(const QModelIndex &index, int height);
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
QMap m_height;
};
class EffectStackView : public QWidget
{
Q_OBJECT
public:
EffectStackView(QWidget *parent = nullptr);
virtual ~EffectStackView();
void setModel(std::shared_ptr model, QPair range);
void unsetModel(bool reset = true);
void setRange(int in, int out);
ObjectType stackOwner() const;
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
private:
QMutex m_mutex;
QVBoxLayout *m_lay;
QTreeView *m_effectsTree;
std::shared_ptr m_model;
std::vector m_widgets;
AssetIconProvider *m_thumbnailer;
QPair m_range;
const QString getStyleSheet();
void loadEffects(QPair range, int start = 0, int end = -1);
private slots:
void refresh(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles);
void slotAdjustDelegate(std::shared_ptr effectModel, int height);
void slotStartDrag(QPixmap pix, std::shared_ptr effectModel);
void slotActivateEffect(std::shared_ptr effectModel);
signals:
void doActivateEffect(QModelIndex);
+ void seekToPos(int);
};
#endif
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 4df480a08..ed19086e5 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1,4140 +1,4142 @@
/***************************************************************************
* Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@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) 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, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "mainwindow.h"
#include "assets/assetpanel.hpp"
#include "bin/generators/generators.h"
#include "bin/projectclip.h"
#include "core.h"
#include "dialogs/clipcreationdialog.h"
#include "dialogs/kdenlivesettingsdialog.h"
#include "dialogs/renderwidget.h"
#include "dialogs/wizard.h"
#include "doc/docundostack.hpp"
#include "doc/kdenlivedoc.h"
#include "effects/effectlist/view/effectlistwidget.hpp"
#include "effectslist/effectbasket.h"
#include "effectslist/effectslistview.h"
#include "effectslist/effectslistwidget.h"
#include "effectslist/initeffects.h"
#include "hidetitlebars.h"
#include "kdenlivesettings.h"
#include "layoutmanagement.h"
#include "library/librarywidget.h"
#include "mainwindowadaptor.h"
#include "mltconnection.h"
#include "mltcontroller/bincontroller.h"
#include "mltcontroller/clipcontroller.h"
#include "mltcontroller/producerqueue.h"
#include "monitor/monitor.h"
#include "monitor/monitormanager.h"
#include "monitor/scopes/audiographspectrum.h"
#include "profiles/profilemodel.hpp"
#include "project/clipmanager.h"
#include "project/cliptranscode.h"
#include "project/dialogs/archivewidget.h"
#include "project/dialogs/projectsettings.h"
#include "project/projectcommands.h"
#include "project/projectmanager.h"
#include "renderer.h"
#include "scopes/scopemanager.h"
#include "timeline2/view/timelinecontroller.h"
#include "timeline2/view/timelinetabs.hpp"
#include "timeline2/view/timelinewidget.h"
#include "titler/titlewidget.h"
#include "transitions/transitionlist/view/transitionlistwidget.hpp"
#include "transitions/transitionsrepository.hpp"
#include "utils/resourcewidget.h"
#include "utils/thememanager.h"
#include "widgets/progressbutton.h"
#include
#include "effectslist/effectslistwidget.h"
#include "profiles/profilerepository.hpp"
#include "project/dialogs/temporarydata.h"
#include "utils/KoIconUtils.h"
#ifdef USE_JOGSHUTTLE
#include "jogshuttle/jogmanager.h"
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kdenlive_debug.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static const char version[] = KDENLIVE_VERSION;
namespace Mlt {
class Producer;
}
EffectsList MainWindow::videoEffects;
EffectsList MainWindow::audioEffects;
EffectsList MainWindow::customEffects;
EffectsList MainWindow::transitions;
QMap MainWindow::m_lumacache;
QMap MainWindow::m_lumaFiles;
/*static bool sortByNames(const QPair &a, const QPair &b)
{
return a.first < b.first;
}*/
// determine the default KDE style as defined BY THE USER
// (as opposed to whatever style KDE considers default)
static QString defaultStyle(const char *fallback = nullptr)
{
KSharedConfigPtr kdeGlobals = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::NoGlobals);
KConfigGroup cg(kdeGlobals, "KDE");
return cg.readEntry("widgetStyle", fallback);
}
MainWindow::MainWindow(QWidget *parent)
: KXmlGuiWindow(parent)
, m_assetPanel(nullptr)
, m_exitCode(EXIT_SUCCESS)
, m_effectList(nullptr)
, m_transitionList(nullptr)
, m_clipMonitor(nullptr)
, m_projectMonitor(nullptr)
, m_renderWidget(nullptr)
, m_messageLabel(nullptr)
, m_themeInitialized(false)
, m_isDarkTheme(false)
{
}
void MainWindow::init()
{
// Widget themes for non KDE users
KActionMenu *stylesAction = new KActionMenu(i18n("Style"), this);
auto *stylesGroup = new QActionGroup(stylesAction);
// GTK theme does not work well with Kdenlive, and does not support color theming, so avoid it
QStringList availableStyles = QStyleFactory::keys();
QString desktopStyle = QApplication::style()->objectName();
if (KdenliveSettings::widgetstyle().isEmpty()) {
// First run
QStringList incompatibleStyles;
incompatibleStyles << QStringLiteral("GTK+") << QStringLiteral("windowsvista") << QStringLiteral("windowsxp");
if (incompatibleStyles.contains(desktopStyle, Qt::CaseInsensitive)) {
if (availableStyles.contains(QStringLiteral("breeze"), Qt::CaseInsensitive)) {
// Auto switch to Breeze theme
KdenliveSettings::setWidgetstyle(QStringLiteral("Breeze"));
} else if (availableStyles.contains(QStringLiteral("fusion"), Qt::CaseInsensitive)) {
KdenliveSettings::setWidgetstyle(QStringLiteral("Fusion"));
}
}
}
// Add default style action
QAction *defaultStyle = new QAction(i18n("Default"), stylesGroup);
defaultStyle->setCheckable(true);
stylesAction->addAction(defaultStyle);
if (KdenliveSettings::widgetstyle().isEmpty()) {
defaultStyle->setChecked(true);
}
for (const QString &style : availableStyles) {
auto *a = new QAction(style, stylesGroup);
a->setCheckable(true);
a->setData(style);
if (KdenliveSettings::widgetstyle() == style) {
a->setChecked(true);
}
stylesAction->addAction(a);
}
connect(stylesGroup, &QActionGroup::triggered, this, &MainWindow::slotChangeStyle);
// QIcon::setThemeSearchPaths(QStringList() <setCurrentProfile(defaultProfile.isEmpty() ? ProjectManager::getDefaultProjectFormat() : defaultProfile);
m_commandStack = new QUndoGroup();
// If using a custom profile, make sure the file exists or fallback to default
QString currentProfilePath = pCore->getCurrentProfile()->path();
if (currentProfilePath.startsWith(QLatin1Char('/')) && !QFile::exists(currentProfilePath)) {
KMessageBox::sorry(this, i18n("Cannot find your default profile, switching to ATSC 1080p 25"));
pCore->setCurrentProfile(QStringLiteral("atsc_1080p_25"));
KdenliveSettings::setDefault_profile(QStringLiteral("atsc_1080p_25"));
}
m_gpuAllowed = initEffects::parseEffectFiles(pCore->getMltRepository());
// initEffects::parseCustomEffectsFile();
m_shortcutRemoveFocus = new QShortcut(QKeySequence(QStringLiteral("Esc")), this);
connect(m_shortcutRemoveFocus, &QShortcut::activated, this, &MainWindow::slotRemoveFocus);
/// Add Widgets
setDockOptions(dockOptions() | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
setDockOptions(dockOptions() | QMainWindow::GroupedDragging);
#endif
setTabPosition(Qt::AllDockWidgetAreas, (QTabWidget::TabPosition)KdenliveSettings::tabposition());
m_timelineToolBar = toolBar(QStringLiteral("timelineToolBar"));
m_timelineToolBarContainer = new QWidget(this);
auto *ctnLay = new QVBoxLayout;
ctnLay->setSpacing(0);
ctnLay->setContentsMargins(0, 0, 0, 0);
m_timelineToolBarContainer->setLayout(ctnLay);
ctnLay->addWidget(m_timelineToolBar);
KSharedConfigPtr config = KSharedConfig::openConfig();
KConfigGroup mainConfig(config, QStringLiteral("MainWindow"));
KConfigGroup tbGroup(&mainConfig, QStringLiteral("Toolbar timelineToolBar"));
m_timelineToolBar->applySettings(tbGroup);
QFrame *fr = new QFrame(this);
fr->setFrameShape(QFrame::HLine);
fr->setMaximumHeight(1);
fr->setLineWidth(1);
ctnLay->addWidget(fr);
setCentralWidget(m_timelineToolBarContainer);
setupActions();
QDockWidget *libraryDock = addDock(i18n("Library"), QStringLiteral("library"), pCore->library());
m_clipMonitor = new Monitor(Kdenlive::ClipMonitor, pCore->monitorManager(), this);
pCore->bin()->setMonitor(m_clipMonitor);
connect(m_clipMonitor, &Monitor::showConfigDialog, this, &MainWindow::slotPreferences);
connect(m_clipMonitor, &Monitor::addMarker, this, &MainWindow::slotAddMarkerGuideQuickly);
connect(m_clipMonitor, &Monitor::deleteMarker, this, &MainWindow::slotDeleteClipMarker);
connect(m_clipMonitor, &Monitor::seekToPreviousSnap, this, &MainWindow::slotSnapRewind);
connect(m_clipMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
connect(pCore->bin(), &Bin::findInTimeline, this, &MainWindow::slotClipInTimeline);
// TODO deprecated, replace with Bin methods if necessary
/*connect(m_projectList, SIGNAL(loadingIsOver()), this, SLOT(slotElapsedTime()));
connect(m_projectList, SIGNAL(updateRenderStatus()), this, SLOT(slotCheckRenderStatus()));
connect(m_projectList, SIGNAL(updateProfile(QString)), this, SLOT(slotUpdateProjectProfile(QString)));
connect(m_projectList, SIGNAL(refreshClip(QString,bool)), pCore->monitorManager(), SLOT(slotRefreshCurrentMonitor(QString)));
connect(m_clipMonitor, SIGNAL(zoneUpdated(QPoint)), m_projectList, SLOT(slotUpdateClipCut(QPoint)));*/
connect(m_clipMonitor, &Monitor::extractZone, pCore->bin(), &Bin::slotStartCutJob);
connect(m_clipMonitor, &Monitor::passKeyPress, this, &MainWindow::triggerKey);
m_projectMonitor = new Monitor(Kdenlive::ProjectMonitor, pCore->monitorManager(), this);
connect(m_projectMonitor, &Monitor::passKeyPress, this, &MainWindow::triggerKey);
connect(m_projectMonitor, &Monitor::addMarker, this, &MainWindow::slotAddMarkerGuideQuickly);
connect(m_projectMonitor, &Monitor::deleteMarker, this, &MainWindow::slotDeleteClipMarker);
connect(m_projectMonitor, &Monitor::seekToPreviousSnap, this, &MainWindow::slotSnapRewind);
connect(m_projectMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
connect(m_loopClip, &QAction::triggered, m_projectMonitor, &Monitor::slotLoopClip);
pCore->monitorManager()->initMonitors(m_clipMonitor, m_projectMonitor);
connect(m_clipMonitor, &Monitor::addMasterEffect, pCore->bin(), &Bin::slotAddEffect);
m_timelineTabs = new TimelineTabs(this);
ctnLay->addWidget(m_timelineTabs);
// Color schemes
KActionMenu *themeAction = new KActionMenu(i18n("Theme"), this);
ThemeManager::instance()->setThemeMenuAction(themeAction);
ThemeManager::instance()->setCurrentTheme(KdenliveSettings::colortheme());
connect(ThemeManager::instance(), &ThemeManager::signalThemeChanged, this, &MainWindow::slotThemeChanged, Qt::DirectConnection);
if (!KdenliveSettings::widgetstyle().isEmpty() && QString::compare(desktopStyle, KdenliveSettings::widgetstyle(), Qt::CaseInsensitive) != 0) {
// User wants a custom widget style, init
doChangeStyle();
} else {
ThemeManager::instance()->slotChangePalette();
}
// Audio spectrum scope
m_audioSpectrum = new AudioGraphSpectrum(pCore->monitorManager());
QDockWidget *spectrumDock = addDock(i18n("Audio Spectrum"), QStringLiteral("audiospectrum"), m_audioSpectrum);
connect(this, &MainWindow::reloadTheme, m_audioSpectrum, &AudioGraphSpectrum::refreshPixmap);
// Close library and audiospectrum on first run
libraryDock->close();
spectrumDock->close();
m_projectBinDock = addDock(i18n("Project Bin"), QStringLiteral("project_bin"), pCore->bin());
m_assetPanel = new AssetPanel(this);
connect(m_assetPanel, &AssetPanel::doSplitEffect, m_projectMonitor, &Monitor::slotSwitchCompare);
connect(m_assetPanel, &AssetPanel::doSplitBinEffect, m_clipMonitor, &Monitor::slotSwitchCompare);
connect(m_timelineTabs, &TimelineTabs::showTransitionModel, m_assetPanel, &AssetPanel::showTransition);
connect(m_timelineTabs, &TimelineTabs::showClipEffectStack, m_assetPanel, &AssetPanel::showEffectStack);
connect(pCore->bin(), &Bin::requestShowEffectStack, m_assetPanel, &AssetPanel::showEffectStack);
connect(this, &MainWindow::clearAssetPanel, m_assetPanel, &AssetPanel::clearAssetPanel);
connect(this, &MainWindow::adjustAssetPanelRange, m_assetPanel, &AssetPanel::adjustAssetPanelRange);
+ connect(m_assetPanel, &AssetPanel::seekToPos, [this](int pos){getCurrentTimeline()->controller()->setPosition(pos);} );
+
m_effectStackDock = addDock(i18n("Properties"), QStringLiteral("effect_stack"), m_assetPanel);
m_effectList = new EffectsListView();
// m_effectListDock = addDock(i18n("Effects"), QStringLiteral("effect_list"), m_effectList);
m_effectList2 = new EffectListWidget(this);
connect(m_effectList2, &EffectListWidget::activateAsset, pCore->projectManager(), &ProjectManager::activateAsset);
m_effectListDock = addDock(i18n("Effects"), QStringLiteral("effect_list"), m_effectList2);
m_transitionList = new EffectsListView(EffectsListView::TransitionMode);
m_transitionList2 = new TransitionListWidget(this);
// m_transitionListDock = addDock(i18n("Transitions"), QStringLiteral("transition_list"), m_transitionList);
m_transitionListDock = addDock(i18n("Transitions"), QStringLiteral("transition_list"), m_transitionList2);
// Add monitors here to keep them at the right of the window
m_clipMonitorDock = addDock(i18n("Clip Monitor"), QStringLiteral("clip_monitor"), m_clipMonitor);
m_projectMonitorDock = addDock(i18n("Project Monitor"), QStringLiteral("project_monitor"), m_projectMonitor);
m_undoView = new QUndoView();
m_undoView->setCleanIcon(KoIconUtils::themedIcon(QStringLiteral("edit-clear")));
m_undoView->setEmptyLabel(i18n("Clean"));
m_undoView->setGroup(m_commandStack);
m_undoViewDock = addDock(i18n("Undo History"), QStringLiteral("undo_history"), m_undoView);
// Color and icon theme stuff
addAction(QStringLiteral("themes_menu"), themeAction);
connect(m_commandStack, &QUndoGroup::cleanChanged, m_saveAction, &QAction::setDisabled);
addAction(QStringLiteral("styles_menu"), stylesAction);
QAction *iconAction = new QAction(i18n("Force Breeze Icon Theme"), this);
iconAction->setCheckable(true);
iconAction->setChecked(KdenliveSettings::force_breeze());
addAction(QStringLiteral("force_icon_theme"), iconAction);
connect(iconAction, &QAction::triggered, this, &MainWindow::forceIconSet);
// Close non-general docks for the initial layout
// only show important ones
m_undoViewDock->close();
/// Tabify Widgets
tabifyDockWidget(m_transitionListDock, m_effectListDock);
tabifyDockWidget(m_effectStackDock, pCore->bin()->clipPropertiesDock());
// tabifyDockWidget(m_effectListDock, m_effectStackDock);
tabifyDockWidget(m_clipMonitorDock, m_projectMonitorDock);
bool firstRun = readOptions();
// Monitor Record action
addAction(QStringLiteral("switch_monitor_rec"), m_clipMonitor->recAction());
// Build effects menu
m_effectsMenu = new QMenu(i18n("Add Effect"), this);
m_effectActions = new KActionCategory(i18n("Effects"), actionCollection());
m_effectList->reloadEffectList(m_effectsMenu, m_effectActions);
m_transitionsMenu = new QMenu(i18n("Add Transition"), this);
m_transitionActions = new KActionCategory(i18n("Transitions"), actionCollection());
m_transitionList->reloadEffectList(m_transitionsMenu, m_transitionActions);
auto *scmanager = new ScopeManager(this);
new LayoutManagement(this);
new HideTitleBars(this);
m_extraFactory = new KXMLGUIClient(this);
buildDynamicActions();
// Add shortcut to action tooltips
QList collections = KActionCollection::allCollections();
for (int i = 0; i < collections.count(); ++i) {
KActionCollection *coll = collections.at(i);
for (QAction *tempAction : coll->actions()) {
// find the shortcut pattern and delete (note the preceding space in the RegEx)
QString strippedTooltip = tempAction->toolTip().remove(QRegExp(QStringLiteral("\\s\\(.*\\)")));
// append shortcut if it exists for action
if (tempAction->shortcut() == QKeySequence(0)) {
tempAction->setToolTip(strippedTooltip);
} else {
tempAction->setToolTip(strippedTooltip + QStringLiteral(" (") + tempAction->shortcut().toString() + QLatin1Char(')'));
}
}
}
// Create Effect Basket (dropdown list of favorites)
m_effectBasket = new EffectBasket(m_effectList);
connect(m_effectBasket, SIGNAL(addEffect(QDomElement)), this, SLOT(slotAddEffect(QDomElement)));
auto *widgetlist = new QWidgetAction(this);
widgetlist->setDefaultWidget(m_effectBasket);
// widgetlist->setText(i18n("Favorite Effects"));
widgetlist->setToolTip(i18n("Favorite Effects"));
widgetlist->setIcon(KoIconUtils::themedIcon(QStringLiteral("favorite")));
auto *menu = new QMenu(this);
menu->addAction(widgetlist);
auto *basketButton = new QToolButton(this);
basketButton->setMenu(menu);
basketButton->setToolButtonStyle(toolBar()->toolButtonStyle());
basketButton->setDefaultAction(widgetlist);
basketButton->setPopupMode(QToolButton::InstantPopup);
// basketButton->setText(i18n("Favorite Effects"));
basketButton->setToolTip(i18n("Favorite Effects"));
basketButton->setIcon(KoIconUtils::themedIcon(QStringLiteral("favorite")));
auto *toolButtonAction = new QWidgetAction(this);
toolButtonAction->setText(i18n("Favorite Effects"));
toolButtonAction->setIcon(KoIconUtils::themedIcon(QStringLiteral("favorite")));
toolButtonAction->setDefaultWidget(basketButton);
addAction(QStringLiteral("favorite_effects"), toolButtonAction);
connect(toolButtonAction, &QAction::triggered, basketButton, &QToolButton::showMenu);
// Render button
ProgressButton *timelineRender = new ProgressButton(i18n("Render"), 100, this);
auto *tlrMenu = new QMenu(this);
timelineRender->setMenu(tlrMenu);
connect(this, &MainWindow::setRenderProgress, timelineRender, &ProgressButton::setProgress);
auto *renderButtonAction = new QWidgetAction(this);
renderButtonAction->setText(i18n("Render Button"));
renderButtonAction->setIcon(KoIconUtils::themedIcon(QStringLiteral("media-record")));
renderButtonAction->setDefaultWidget(timelineRender);
addAction(QStringLiteral("project_render_button"), renderButtonAction);
// Timeline preview button
ProgressButton *timelinePreview = new ProgressButton(i18n("Rendering preview"), 1000, this);
auto *tlMenu = new QMenu(this);
timelinePreview->setMenu(tlMenu);
connect(this, &MainWindow::setPreviewProgress, timelinePreview, &ProgressButton::setProgress);
auto *previewButtonAction = new QWidgetAction(this);
previewButtonAction->setText(i18n("Timeline Preview"));
previewButtonAction->setIcon(KoIconUtils::themedIcon(QStringLiteral("preview-render-on")));
previewButtonAction->setDefaultWidget(timelinePreview);
addAction(QStringLiteral("timeline_preview_button"), previewButtonAction);
setupGUI();
if (firstRun) {
QScreen *current = QApplication::primaryScreen();
if (current) {
if (current->availableSize().height() < 1000) {
resize(current->availableSize());
} else {
resize(current->availableSize() / 1.5);
}
}
}
timelinePreview->setToolButtonStyle(m_timelineToolBar->toolButtonStyle());
connect(m_timelineToolBar, &QToolBar::toolButtonStyleChanged, timelinePreview, &ProgressButton::setToolButtonStyle);
timelineRender->setToolButtonStyle(toolBar()->toolButtonStyle());
/*connect(m_timelineToolBar, &QToolBar::toolButtonStyleChanged, timelinePreview, &ProgressButton::setToolButtonStyle);*/
/*ScriptingPart* sp = new ScriptingPart(this, QStringList());
guiFactory()->addClient(sp);*/
loadGenerators();
loadDockActions();
loadClipActions();
// Connect monitor overlay info menu.
QMenu *monitorOverlay = static_cast(factory()->container(QStringLiteral("monitor_config_overlay"), this));
connect(monitorOverlay, &QMenu::triggered, this, &MainWindow::slotSwitchMonitorOverlay);
m_projectMonitor->setupMenu(static_cast(factory()->container(QStringLiteral("monitor_go"), this)), monitorOverlay, m_playZone, m_loopZone, nullptr,
m_loopClip);
m_clipMonitor->setupMenu(static_cast(factory()->container(QStringLiteral("monitor_go"), this)), monitorOverlay, m_playZone, m_loopZone,
static_cast(factory()->container(QStringLiteral("marker_menu"), this)));
QMenu *clipInTimeline = static_cast(factory()->container(QStringLiteral("clip_in_timeline"), this));
clipInTimeline->setIcon(KoIconUtils::themedIcon(QStringLiteral("go-jump")));
pCore->bin()->setupGeneratorMenu();
connect(pCore->monitorManager(), &MonitorManager::updateOverlayInfos, this, &MainWindow::slotUpdateMonitorOverlays);
// Setup and fill effects and transitions menus.
QMenu *m = static_cast(factory()->container(QStringLiteral("video_effects_menu"), this));
connect(m, &QMenu::triggered, this, &MainWindow::slotAddVideoEffect);
connect(m_effectsMenu, &QMenu::triggered, this, &MainWindow::slotAddVideoEffect);
connect(m_transitionsMenu, &QMenu::triggered, this, &MainWindow::slotAddTransition);
m_timelineContextMenu = new QMenu(this);
m_timelineContextClipMenu = new QMenu(this);
m_timelineContextTransitionMenu = new QMenu(this);
m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("insert_space")));
m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("delete_space")));
m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("delete_space_all_tracks")));
m_timelineContextMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Paste)));
m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("clip_in_project_tree")));
// m_timelineContextClipMenu->addAction(actionCollection()->action("clip_to_project_tree"));
m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));
m_timelineContextClipMenu->addSeparator();
m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("group_clip")));
m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("ungroup_clip")));
m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("split_audio")));
m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("set_audio_align_ref")));
m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("align_audio")));
m_timelineContextClipMenu->addSeparator();
m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("cut_timeline_clip")));
m_timelineContextClipMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("paste_effects")));
m_timelineContextClipMenu->addSeparator();
QMenu *markersMenu = static_cast(factory()->container(QStringLiteral("marker_menu"), this));
m_timelineContextClipMenu->addMenu(markersMenu);
m_timelineContextClipMenu->addSeparator();
m_timelineContextClipMenu->addMenu(m_transitionsMenu);
m_timelineContextClipMenu->addMenu(m_effectsMenu);
m_timelineContextTransitionMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));
m_timelineContextTransitionMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
m_timelineContextTransitionMenu->addAction(actionCollection()->action(QStringLiteral("auto_transition")));
connect(m_effectList, &EffectsListView::addEffect, this, &MainWindow::slotAddEffect);
connect(m_effectList, &EffectsListView::reloadEffects, this, &MainWindow::slotReloadEffects);
slotConnectMonitors();
m_timelineToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
// TODO: let user select timeline toolbar toolbutton style
// connect(toolBar(), &QToolBar::iconSizeChanged, m_timelineToolBar, &QToolBar::setToolButtonStyle);
m_timelineToolBar->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_timelineToolBar, &QWidget::customContextMenuRequested, this, &MainWindow::showTimelineToolbarMenu);
QAction *prevRender = actionCollection()->action(QStringLiteral("prerender_timeline_zone"));
QAction *stopPrevRender = actionCollection()->action(QStringLiteral("stop_prerender_timeline"));
tlMenu->addAction(stopPrevRender);
tlMenu->addAction(actionCollection()->action(QStringLiteral("set_render_timeline_zone")));
tlMenu->addAction(actionCollection()->action(QStringLiteral("unset_render_timeline_zone")));
tlMenu->addAction(actionCollection()->action(QStringLiteral("clear_render_timeline_zone")));
// Automatic timeline preview action
QAction *autoRender = new QAction(KoIconUtils::themedIcon(QStringLiteral("view-refresh")), i18n("Automatic Preview"), this);
autoRender->setCheckable(true);
autoRender->setChecked(KdenliveSettings::autopreview());
connect(autoRender, &QAction::triggered, this, &MainWindow::slotToggleAutoPreview);
tlMenu->addAction(autoRender);
tlMenu->addSeparator();
tlMenu->addAction(actionCollection()->action(QStringLiteral("disable_preview")));
tlMenu->addAction(actionCollection()->action(QStringLiteral("manage_cache")));
timelinePreview->defineDefaultAction(prevRender, stopPrevRender);
timelinePreview->setAutoRaise(true);
QAction *showRender = actionCollection()->action(QStringLiteral("project_render"));
tlrMenu->addAction(showRender);
tlrMenu->addAction(actionCollection()->action(QStringLiteral("stop_project_render")));
timelineRender->defineDefaultAction(showRender, showRender);
timelineRender->setAutoRaise(true);
// Populate encoding profiles
KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
if (KdenliveSettings::proxyparams().isEmpty() || KdenliveSettings::proxyextension().isEmpty()) {
KConfigGroup group(&conf, "proxy");
QMap values = group.entryMap();
QMapIterator i(values);
if (i.hasNext()) {
i.next();
QString proxystring = i.value();
KdenliveSettings::setProxyparams(proxystring.section(QLatin1Char(';'), 0, 0));
KdenliveSettings::setProxyextension(proxystring.section(QLatin1Char(';'), 1, 1));
}
}
if (KdenliveSettings::v4l_parameters().isEmpty() || KdenliveSettings::v4l_extension().isEmpty()) {
KConfigGroup group(&conf, "video4linux");
QMap values = group.entryMap();
QMapIterator i(values);
if (i.hasNext()) {
i.next();
QString v4lstring = i.value();
KdenliveSettings::setV4l_parameters(v4lstring.section(QLatin1Char(';'), 0, 0));
KdenliveSettings::setV4l_extension(v4lstring.section(QLatin1Char(';'), 1, 1));
}
}
if (KdenliveSettings::grab_parameters().isEmpty() || KdenliveSettings::grab_extension().isEmpty()) {
KConfigGroup group(&conf, "screengrab");
QMap values = group.entryMap();
QMapIterator i(values);
if (i.hasNext()) {
i.next();
QString grabstring = i.value();
KdenliveSettings::setGrab_parameters(grabstring.section(QLatin1Char(';'), 0, 0));
KdenliveSettings::setGrab_extension(grabstring.section(QLatin1Char(';'), 1, 1));
}
}
if (KdenliveSettings::decklink_parameters().isEmpty() || KdenliveSettings::decklink_extension().isEmpty()) {
KConfigGroup group(&conf, "decklink");
QMap values = group.entryMap();
QMapIterator i(values);
if (i.hasNext()) {
i.next();
QString decklinkstring = i.value();
KdenliveSettings::setDecklink_parameters(decklinkstring.section(QLatin1Char(';'), 0, 0));
KdenliveSettings::setDecklink_extension(decklinkstring.section(QLatin1Char(';'), 1, 1));
}
}
if (!QDir(KdenliveSettings::currenttmpfolder()).isReadable())
KdenliveSettings::setCurrenttmpfolder(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
QTimer::singleShot(0, this, &MainWindow::GUISetupDone);
connect(this, &MainWindow::reloadTheme, this, &MainWindow::slotReloadTheme, Qt::UniqueConnection);
#ifdef USE_JOGSHUTTLE
new JogManager(this);
#endif
scmanager->slotCheckActiveScopes();
// m_messageLabel->setMessage(QStringLiteral("This is a beta version. Always backup your data"), MltError);
}
void MainWindow::slotThemeChanged(const QString &theme)
{
disconnect(this, &MainWindow::reloadTheme, this, &MainWindow::slotReloadTheme);
KSharedConfigPtr config = KSharedConfig::openConfig(theme);
QPalette plt = KColorScheme::createApplicationPalette(config);
setPalette(plt);
qApp->setPalette(palette());
// Required for qml palette change
QGuiApplication::setPalette(plt);
KdenliveSettings::setColortheme(theme);
QColor background = plt.window().color();
bool useDarkIcons = background.value() < 100;
if (KdenliveSettings::force_breeze() && useDarkIcons != KdenliveSettings::use_dark_breeze()) {
// We need to reload icon theme
KdenliveSettings::setUse_dark_breeze(useDarkIcons);
if (KMessageBox::warningContinueCancel(this, i18n("Kdenlive needs to be restarted to apply color theme change. Restart now ?")) ==
KMessageBox::Continue) {
slotRestart();
}
}
if (m_assetPanel) {
m_assetPanel->updatePalette();
}
if (m_effectList) {
m_effectList->updatePalette();
}
if (m_transitionList) {
m_transitionList->updatePalette();
}
if (m_clipMonitor) {
m_clipMonitor->setPalette(plt);
}
if (m_projectMonitor) {
m_projectMonitor->setPalette(plt);
}
m_timelineTabs->setPalette(plt);
#if KXMLGUI_VERSION_MINOR < 23 && KXMLGUI_VERSION_MAJOR == 5
// Not required anymore with auto colored icons since KF5 5.23
QColor background = plt.window().color();
bool useDarkIcons = background.value() < 100;
if (m_themeInitialized && useDarkIcons != m_isDarkTheme) {
if (pCore->bin()) {
pCore->bin()->refreshIcons();
}
if (m_clipMonitor) {
m_clipMonitor->refreshIcons();
}
if (m_projectMonitor) {
m_projectMonitor->refreshIcons();
}
if (pCore->monitorManager()) {
pCore->monitorManager()->refreshIcons();
}
if (m_effectList) {
m_effectList->refreshIcons();
}
for (QAction *action : actionCollection()->actions()) {
QIcon icon = action->icon();
if (icon.isNull()) {
continue;
}
QString iconName = icon.name();
QIcon newIcon = KoIconUtils::themedIcon(iconName);
if (newIcon.isNull()) {
continue;
}
action->setIcon(newIcon);
}
}
m_themeInitialized = true;
m_isDarkTheme = useDarkIcons;
#endif
connect(this, &MainWindow::reloadTheme, this, &MainWindow::slotReloadTheme, Qt::UniqueConnection);
}
bool MainWindow::event(QEvent *e)
{
switch (e->type()) {
case QEvent::ApplicationPaletteChange:
emit reloadTheme();
e->accept();
break;
default:
break;
}
return KXmlGuiWindow::event(e);
}
void MainWindow::slotReloadTheme()
{
ThemeManager::instance()->slotSettingsChanged();
}
MainWindow::~MainWindow()
{
delete m_audioSpectrum;
if (m_projectMonitor) {
m_projectMonitor->stop();
}
if (m_clipMonitor) {
m_clipMonitor->stop();
}
ClipController::mediaUnavailable.reset();
delete m_projectMonitor;
delete m_clipMonitor;
delete m_shortcutRemoveFocus;
delete m_effectList2;
delete m_transitionList2;
qDeleteAll(m_transitions);
// Mlt::Factory::close();
}
// virtual
bool MainWindow::queryClose()
{
if (m_renderWidget) {
int waitingJobs = m_renderWidget->waitingJobsCount();
if (waitingJobs > 0) {
switch (KMessageBox::warningYesNoCancel(this, i18np("You have 1 rendering job waiting in the queue.\nWhat do you want to do with this job?",
"You have %1 rendering jobs waiting in the queue.\nWhat do you want to do with these jobs?",
waitingJobs),
QString(), KGuiItem(i18n("Start them now")), KGuiItem(i18n("Delete them")))) {
case KMessageBox::Yes:
// create script with waiting jobs and start it
if (!m_renderWidget->startWaitingRenderJobs()) {
return false;
}
break;
case KMessageBox::No:
// Don't do anything, jobs will be deleted
break;
default:
return false;
}
}
}
saveOptions();
// WARNING: According to KMainWindow::queryClose documentation we are not supposed to close the document here?
return pCore->projectManager()->closeCurrentDocument(true, true);
}
void MainWindow::loadGenerators()
{
QMenu *addMenu = static_cast(factory()->container(QStringLiteral("generators"), this));
Generators::getGenerators(KdenliveSettings::producerslist(), addMenu);
connect(addMenu, &QMenu::triggered, this, &MainWindow::buildGenerator);
}
void MainWindow::buildGenerator(QAction *action)
{
Generators gen(m_clipMonitor, action->data().toString(), this);
if (gen.exec() == QDialog::Accepted) {
pCore->bin()->slotAddClipToProject(gen.getSavedClip());
}
}
void MainWindow::saveProperties(KConfigGroup &config)
{
// save properties here
KXmlGuiWindow::saveProperties(config);
// TODO: fix session management
if (qApp->isSavingSession() && pCore->projectManager()) {
if (pCore->currentDoc() && !pCore->currentDoc()->url().isEmpty()) {
config.writeEntry("kdenlive_lastUrl", pCore->currentDoc()->url().toLocalFile());
}
}
}
void MainWindow::readProperties(const KConfigGroup &config)
{
// read properties here
KXmlGuiWindow::readProperties(config);
// TODO: fix session management
/*if (qApp->isSessionRestored()) {
pCore->projectManager()->openFile(QUrl::fromLocalFile(config.readEntry("kdenlive_lastUrl", QString())));
}*/
}
void MainWindow::saveNewToolbarConfig()
{
KXmlGuiWindow::saveNewToolbarConfig();
// TODO for some reason all dynamically inserted actions are removed by the save toolbar
// So we currently re-add them manually....
loadDockActions();
loadClipActions();
pCore->bin()->rebuildMenu();
QMenu *monitorOverlay = static_cast(factory()->container(QStringLiteral("monitor_config_overlay"), this));
if (monitorOverlay) {
m_projectMonitor->setupMenu(static_cast(factory()->container(QStringLiteral("monitor_go"), this)), monitorOverlay, m_playZone, m_loopZone,
nullptr, m_loopClip);
m_clipMonitor->setupMenu(static_cast(factory()->container(QStringLiteral("monitor_go"), this)), monitorOverlay, m_playZone, m_loopZone,
static_cast(factory()->container(QStringLiteral("marker_menu"), this)));
}
}
void MainWindow::slotReloadEffects()
{
initEffects::parseCustomEffectsFile();
m_effectList->reloadEffectList(m_effectsMenu, m_effectActions);
}
void MainWindow::configureNotifications()
{
KNotifyConfigWidget::configure(this);
}
void MainWindow::slotFullScreen()
{
KToggleFullScreenAction::setFullScreen(this, actionCollection()->action(QStringLiteral("fullscreen"))->isChecked());
}
void MainWindow::slotAddEffect(const QDomElement &effect)
{
// TODO refac : reimplement
/*
if (effect.isNull()) {
qCDebug(KDENLIVE_LOG) << "--- ERROR, TRYING TO APPEND nullptr EFFECT";
return;
}
QDomElement effectToAdd = effect.cloneNode().toElement();
EFFECTMODE status = m_effectStack->effectStatus();
switch (status) {
case TIMELINE_TRACK:
pCore->projectManager()->currentTimeline()->projectView()->slotAddTrackEffect(effectToAdd, m_effectStack->trackIndex());
break;
case TIMELINE_CLIP:
pCore->projectManager()->currentTimeline()->projectView()->slotAddEffectToCurrentItem(effectToAdd);
break;
case MASTER_CLIP:
// TODO refac reimplement this.
// pCore->bin()->slotEffectDropped(QString(), effectToAdd);
break;
default:
// No clip selected
m_messageLabel->setMessage(i18n("Select a clip if you want to apply an effect"), ErrorMessage);
}
*/
}
void MainWindow::slotConnectMonitors()
{
// connect(m_projectList, SIGNAL(deleteProjectClips(QStringList,QMap)), this,
// SLOT(slotDeleteProjectClips(QStringList,QMap)));
connect(m_clipMonitor, &Monitor::refreshClipThumbnail, pCore->bin(), &Bin::slotRefreshClipThumbnail);
connect(m_projectMonitor, &Monitor::requestFrameForAnalysis, this, &MainWindow::slotMonitorRequestRenderFrame);
connect(m_projectMonitor, &Monitor::createSplitOverlay, this, &MainWindow::createSplitOverlay);
connect(m_projectMonitor, &Monitor::removeSplitOverlay, this, &MainWindow::removeSplitOverlay);
}
void MainWindow::createSplitOverlay(Mlt::Filter *filter)
{
getMainTimeline()->controller()->createSplitOverlay(filter);
m_projectMonitor->activateSplit();
}
void MainWindow::removeSplitOverlay()
{
getMainTimeline()->controller()->removeSplitOverlay();
}
void MainWindow::addAction(const QString &name, QAction *action)
{
m_actionNames.append(name);
actionCollection()->addAction(name, action);
actionCollection()->setDefaultShortcut(action, action->shortcut()); // Fix warning about setDefaultShortcut
}
QAction *MainWindow::addAction(const QString &name, const QString &text, const QObject *receiver, const char *member, const QIcon &icon,
const QKeySequence &shortcut)
{
auto *action = new QAction(text, this);
if (!icon.isNull()) {
action->setIcon(icon);
}
if (!shortcut.isEmpty()) {
action->setShortcut(shortcut);
}
addAction(name, action);
connect(action, SIGNAL(triggered(bool)), receiver, member);
return action;
}
void MainWindow::setupActions()
{
// create edit mode buttons
m_normalEditTool = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-normal-edit")), i18n("Normal mode"), this);
m_normalEditTool->setShortcut(i18nc("Normal editing", "n"));
m_normalEditTool->setCheckable(true);
m_normalEditTool->setChecked(true);
m_overwriteEditTool = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-overwrite-edit")), i18n("Overwrite mode"), this);
m_overwriteEditTool->setCheckable(true);
m_overwriteEditTool->setChecked(false);
m_insertEditTool = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-insert-edit")), i18n("Insert mode"), this);
m_insertEditTool->setCheckable(true);
m_insertEditTool->setChecked(false);
KSelectAction *sceneMode = new KSelectAction(i18n("Timeline Edit Mode"), this);
sceneMode->addAction(m_normalEditTool);
sceneMode->addAction(m_overwriteEditTool);
sceneMode->addAction(m_insertEditTool);
sceneMode->setCurrentItem(0);
connect(sceneMode, SIGNAL(triggered(QAction *)), this, SLOT(slotChangeEdit(QAction *)));
addAction(QStringLiteral("timeline_mode"), sceneMode);
KDualAction *ac = new KDualAction(i18n("Don't Use Timeline Zone for Insert"), i18n("Use Timeline Zone for Insert"), this);
ac->setActiveIcon(KoIconUtils::themedIcon(QStringLiteral("timeline-use-zone-on")));
ac->setInactiveIcon(KoIconUtils::themedIcon(QStringLiteral("timeline-use-zone-off")));
ac->setShortcut(Qt::Key_G);
addAction(QStringLiteral("use_timeline_zone_in_edit"), ac);
m_compositeAction = new KSelectAction(KoIconUtils::themedIcon(QStringLiteral("composite-track-off")), i18n("Track compositing"), this);
m_compositeAction->setToolTip(i18n("Track compositing"));
QAction *noComposite = new QAction(KoIconUtils::themedIcon(QStringLiteral("composite-track-off")), i18n("None"), this);
noComposite->setCheckable(true);
noComposite->setData(0);
m_compositeAction->addAction(noComposite);
QString compose = TransitionsRepository::get()->getCompositingTransition();
if (compose == QStringLiteral("movit.overlay")) {
// Movit, do not show "preview" option since movit is faster
QAction *hqComposite = new QAction(KoIconUtils::themedIcon(QStringLiteral("composite-track-on")), i18n("High Quality"), this);
hqComposite->setCheckable(true);
hqComposite->setData(2);
m_compositeAction->addAction(hqComposite);
m_compositeAction->setCurrentAction(hqComposite);
} else {
QAction *previewComposite = new QAction(KoIconUtils::themedIcon(QStringLiteral("composite-track-preview")), i18n("Preview"), this);
previewComposite->setCheckable(true);
previewComposite->setData(1);
m_compositeAction->addAction(previewComposite);
if (compose != QStringLiteral("composite")) {
QAction *hqComposite = new QAction(KoIconUtils::themedIcon(QStringLiteral("composite-track-on")), i18n("High Quality"), this);
hqComposite->setData(2);
hqComposite->setCheckable(true);
m_compositeAction->addAction(hqComposite);
m_compositeAction->setCurrentAction(hqComposite);
} else {
m_compositeAction->setCurrentAction(previewComposite);
}
}
connect(m_compositeAction, SIGNAL(triggered(QAction *)), this, SLOT(slotUpdateCompositing(QAction *)));
addAction(QStringLiteral("timeline_compositing"), m_compositeAction);
m_timeFormatButton = new KSelectAction(QStringLiteral("00:00:00:00 / 00:00:00:00"), this);
m_timeFormatButton->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
m_timeFormatButton->addAction(i18n("hh:mm:ss:ff"));
m_timeFormatButton->addAction(i18n("Frames"));
if (KdenliveSettings::frametimecode()) {
m_timeFormatButton->setCurrentItem(1);
} else {
m_timeFormatButton->setCurrentItem(0);
}
connect(m_timeFormatButton, SIGNAL(triggered(int)), this, SLOT(slotUpdateTimecodeFormat(int)));
m_timeFormatButton->setToolBarMode(KSelectAction::MenuMode);
m_timeFormatButton->setToolButtonPopupMode(QToolButton::InstantPopup);
addAction(QStringLiteral("timeline_timecode"), m_timeFormatButton);
// create tools buttons
m_buttonSelectTool = new QAction(KoIconUtils::themedIcon(QStringLiteral("cursor-arrow")), i18n("Selection tool"), this);
m_buttonSelectTool->setShortcut(i18nc("Selection tool shortcut", "s"));
// toolbar->addAction(m_buttonSelectTool);
m_buttonSelectTool->setCheckable(true);
m_buttonSelectTool->setChecked(true);
m_buttonRazorTool = new QAction(KoIconUtils::themedIcon(QStringLiteral("edit-cut")), i18n("Razor tool"), this);
m_buttonRazorTool->setShortcut(i18nc("Razor tool shortcut", "x"));
// toolbar->addAction(m_buttonRazorTool);
m_buttonRazorTool->setCheckable(true);
m_buttonRazorTool->setChecked(false);
m_buttonSpacerTool = new QAction(KoIconUtils::themedIcon(QStringLiteral("distribute-horizontal-x")), i18n("Spacer tool"), this);
m_buttonSpacerTool->setShortcut(i18nc("Spacer tool shortcut", "m"));
// toolbar->addAction(m_buttonSpacerTool);
m_buttonSpacerTool->setCheckable(true);
m_buttonSpacerTool->setChecked(false);
auto *toolGroup = new QActionGroup(this);
toolGroup->addAction(m_buttonSelectTool);
toolGroup->addAction(m_buttonRazorTool);
toolGroup->addAction(m_buttonSpacerTool);
toolGroup->setExclusive(true);
// toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
/*QWidget * actionWidget;
int max = toolbar->iconSizeDefault() + 2;
actionWidget = toolbar->widgetForAction(m_normalEditTool);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget = toolbar->widgetForAction(m_insertEditTool);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget = toolbar->widgetForAction(m_overwriteEditTool);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget = toolbar->widgetForAction(m_buttonSelectTool);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget = toolbar->widgetForAction(m_buttonRazorTool);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget = toolbar->widgetForAction(m_buttonSpacerTool);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);*/
connect(toolGroup, &QActionGroup::triggered, this, &MainWindow::slotChangeTool);
// create automatic audio split button
m_buttonAutomaticSplitAudio =
new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-split-audio")), i18n("Split audio and video automatically"), this);
m_buttonAutomaticSplitAudio->setCheckable(true);
m_buttonAutomaticSplitAudio->setChecked(KdenliveSettings::splitaudio());
connect(m_buttonAutomaticSplitAudio, &QAction::toggled, this, &MainWindow::slotSwitchSplitAudio);
m_buttonVideoThumbs = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-show-videothumb")), i18n("Show video thumbnails"), this);
m_buttonVideoThumbs->setCheckable(true);
m_buttonVideoThumbs->setChecked(KdenliveSettings::videothumbnails());
connect(m_buttonVideoThumbs, &QAction::triggered, this, &MainWindow::slotSwitchVideoThumbs);
m_buttonAudioThumbs = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-show-audiothumb")), i18n("Show audio thumbnails"), this);
m_buttonAudioThumbs->setCheckable(true);
m_buttonAudioThumbs->setChecked(KdenliveSettings::audiothumbnails());
connect(m_buttonAudioThumbs, &QAction::triggered, this, &MainWindow::slotSwitchAudioThumbs);
m_buttonShowMarkers = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-show-markers")), i18n("Show markers comments"), this);
m_buttonShowMarkers->setCheckable(true);
m_buttonShowMarkers->setChecked(KdenliveSettings::showmarkers());
connect(m_buttonShowMarkers, &QAction::triggered, this, &MainWindow::slotSwitchMarkersComments);
m_buttonSnap = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-snap")), i18n("Snap"), this);
m_buttonSnap->setCheckable(true);
m_buttonSnap->setChecked(KdenliveSettings::snaptopoints());
connect(m_buttonSnap, &QAction::triggered, this, &MainWindow::slotSwitchSnap);
m_buttonAutomaticTransition = new QAction(KoIconUtils::themedIcon(QStringLiteral("auto-transition")), i18n("Automatic transitions"), this);
m_buttonAutomaticTransition->setCheckable(true);
m_buttonAutomaticTransition->setChecked(KdenliveSettings::automatictransitions());
connect(m_buttonAutomaticTransition, &QAction::triggered, this, &MainWindow::slotSwitchAutomaticTransition);
m_buttonFitZoom = new QAction(KoIconUtils::themedIcon(QStringLiteral("zoom-fit-best")), i18n("Fit zoom to project"), this);
m_buttonFitZoom->setCheckable(false);
m_zoomOut = new QAction(KoIconUtils::themedIcon(QStringLiteral("zoom-out")), i18n("Zoom Out"), this);
m_zoomOut->setShortcut(Qt::CTRL + Qt::Key_Minus);
m_zoomSlider = new QSlider(Qt::Horizontal, this);
m_zoomSlider->setMaximum(13);
m_zoomSlider->setPageStep(1);
m_zoomSlider->setInvertedAppearance(true);
m_zoomSlider->setInvertedControls(true);
m_zoomSlider->setMaximumWidth(150);
m_zoomSlider->setMinimumWidth(100);
m_zoomIn = new QAction(KoIconUtils::themedIcon(QStringLiteral("zoom-in")), i18n("Zoom In"), this);
m_zoomIn->setShortcut(Qt::CTRL + Qt::Key_Plus);
/*actionWidget = toolbar->widgetForAction(m_buttonFitZoom);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget->setStyleSheet(styleBorderless);
actionWidget = toolbar->widgetForAction(m_zoomIn);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget->setStyleSheet(styleBorderless);
actionWidget = toolbar->widgetForAction(m_zoomOut);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget->setStyleSheet(styleBorderless);*/
connect(m_zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(slotSetZoom(int)));
connect(m_zoomSlider, &QAbstractSlider::sliderMoved, this, &MainWindow::slotShowZoomSliderToolTip);
connect(m_buttonFitZoom, &QAction::triggered, this, &MainWindow::slotFitZoom);
connect(m_zoomIn, &QAction::triggered, this, &MainWindow::slotZoomIn);
connect(m_zoomOut, &QAction::triggered, this, &MainWindow::slotZoomOut);
m_trimLabel = new QLabel(QStringLiteral(" "), this);
m_trimLabel->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
// m_trimLabel->setAutoFillBackground(true);
m_trimLabel->setAlignment(Qt::AlignHCenter);
m_trimLabel->setStyleSheet(QStringLiteral("QLabel { background-color :red; }"));
KToolBar *toolbar = new KToolBar(QStringLiteral("statusToolBar"), this, Qt::BottomToolBarArea);
toolbar->setMovable(false);
toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
/*QString styleBorderless = QStringLiteral("QToolButton { border-width: 0px;margin: 1px 3px 0px;padding: 0px;}");*/
toolbar->addWidget(m_trimLabel);
toolbar->addAction(m_buttonAutomaticSplitAudio);
toolbar->addAction(m_buttonAutomaticTransition);
toolbar->addAction(m_buttonVideoThumbs);
toolbar->addAction(m_buttonAudioThumbs);
toolbar->addAction(m_buttonShowMarkers);
toolbar->addAction(m_buttonSnap);
toolbar->addSeparator();
toolbar->addAction(m_buttonFitZoom);
toolbar->addAction(m_zoomOut);
toolbar->addWidget(m_zoomSlider);
toolbar->addAction(m_zoomIn);
/*actionWidget = toolbar->widgetForAction(m_buttonAutomaticSplitAudio);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget = toolbar->widgetForAction(m_buttonVideoThumbs);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget = toolbar->widgetForAction(m_buttonAudioThumbs);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget = toolbar->widgetForAction(m_buttonShowMarkers);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);
actionWidget = toolbar->widgetForAction(m_buttonSnap);
actionWidget->setMaximumWidth(max);
actionWidget->setMaximumHeight(max - 4);*/
int small = style()->pixelMetric(QStyle::PM_SmallIconSize);
statusBar()->setMaximumHeight(2 * small);
m_messageLabel = new StatusBarMessageLabel(this);
m_messageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
connect(this, &MainWindow::displayMessage, m_messageLabel, &StatusBarMessageLabel::setMessage);
statusBar()->addWidget(m_messageLabel, 0);
QWidget *spacer = new QWidget(this);
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
statusBar()->addWidget(spacer, 1);
statusBar()->addPermanentWidget(toolbar);
toolbar->setIconSize(QSize(small, small));
toolbar->layout()->setContentsMargins(0, 0, 0, 0);
statusBar()->setContentsMargins(0, 0, 0, 0);
addAction(QStringLiteral("normal_mode"), m_normalEditTool);
addAction(QStringLiteral("overwrite_mode"), m_overwriteEditTool);
addAction(QStringLiteral("insert_mode"), m_insertEditTool);
addAction(QStringLiteral("select_tool"), m_buttonSelectTool);
addAction(QStringLiteral("razor_tool"), m_buttonRazorTool);
addAction(QStringLiteral("spacer_tool"), m_buttonSpacerTool);
addAction(QStringLiteral("automatic_split_audio"), m_buttonAutomaticSplitAudio);
addAction(QStringLiteral("automatic_transition"), m_buttonAutomaticTransition);
addAction(QStringLiteral("show_video_thumbs"), m_buttonVideoThumbs);
addAction(QStringLiteral("show_audio_thumbs"), m_buttonAudioThumbs);
addAction(QStringLiteral("show_markers"), m_buttonShowMarkers);
addAction(QStringLiteral("snap"), m_buttonSnap);
addAction(QStringLiteral("zoom_fit"), m_buttonFitZoom);
addAction(QStringLiteral("zoom_in"), m_zoomIn);
addAction(QStringLiteral("zoom_out"), m_zoomOut);
KNS3::standardAction(i18n("Download New Wipes..."), this, SLOT(slotGetNewLumaStuff()), actionCollection(), "get_new_lumas");
KNS3::standardAction(i18n("Download New Render Profiles..."), this, SLOT(slotGetNewRenderStuff()), actionCollection(), "get_new_profiles");
KNS3::standardAction(i18n("Download New Title Templates..."), this, SLOT(slotGetNewTitleStuff()), actionCollection(), "get_new_titles");
addAction(QStringLiteral("run_wizard"), i18n("Run Config Wizard"), this, SLOT(slotRunWizard()), KoIconUtils::themedIcon(QStringLiteral("tools-wizard")));
addAction(QStringLiteral("project_settings"), i18n("Project Settings"), this, SLOT(slotEditProjectSettings()),
KoIconUtils::themedIcon(QStringLiteral("configure")));
addAction(QStringLiteral("project_render"), i18n("Render"), this, SLOT(slotRenderProject()), KoIconUtils::themedIcon(QStringLiteral("media-record")),
Qt::CTRL + Qt::Key_Return);
addAction(QStringLiteral("stop_project_render"), i18n("Stop Render"), this, SLOT(slotStopRenderProject()),
KoIconUtils::themedIcon(QStringLiteral("media-record")));
addAction(QStringLiteral("project_clean"), i18n("Clean Project"), this, SLOT(slotCleanProject()), KoIconUtils::themedIcon(QStringLiteral("edit-clear")));
// TODO
// addAction("project_adjust_profile", i18n("Adjust Profile to Current Clip"), pCore->bin(), SLOT(adjustProjectProfileToItem()));
m_playZone = addAction(QStringLiteral("monitor_play_zone"), i18n("Play Zone"), pCore->monitorManager(), SLOT(slotPlayZone()),
KoIconUtils::themedIcon(QStringLiteral("media-playback-start")), Qt::CTRL + Qt::Key_Space);
m_loopZone = addAction(QStringLiteral("monitor_loop_zone"), i18n("Loop Zone"), pCore->monitorManager(), SLOT(slotLoopZone()),
KoIconUtils::themedIcon(QStringLiteral("media-playback-start")), Qt::ALT + Qt::Key_Space);
m_loopClip = new QAction(KoIconUtils::themedIcon(QStringLiteral("media-playback-start")), i18n("Loop selected clip"), this);
addAction(QStringLiteral("monitor_loop_clip"), m_loopClip);
m_loopClip->setEnabled(false);
addAction(QStringLiteral("dvd_wizard"), i18n("DVD Wizard"), this, SLOT(slotDvdWizard()), KoIconUtils::themedIcon(QStringLiteral("media-optical")));
addAction(QStringLiteral("transcode_clip"), i18n("Transcode Clips"), this, SLOT(slotTranscodeClip()), KoIconUtils::themedIcon(QStringLiteral("edit-copy")));
addAction(QStringLiteral("archive_project"), i18n("Archive Project"), this, SLOT(slotArchiveProject()),
KoIconUtils::themedIcon(QStringLiteral("document-save-all")));
addAction(QStringLiteral("switch_monitor"), i18n("Switch monitor"), this, SLOT(slotSwitchMonitors()), QIcon(), Qt::Key_T);
addAction(QStringLiteral("expand_timeline_clip"), i18n("Expand Clip"), pCore->projectManager(), SLOT(slotExpandClip()),
KoIconUtils::themedIcon(QStringLiteral("document-open")));
QAction *overlayInfo = new QAction(KoIconUtils::themedIcon(QStringLiteral("help-hint")), i18n("Monitor Info Overlay"), this);
addAction(QStringLiteral("monitor_overlay"), overlayInfo);
overlayInfo->setCheckable(true);
overlayInfo->setData(0x01);
QAction *overlayTCInfo = new QAction(KoIconUtils::themedIcon(QStringLiteral("help-hint")), i18n("Monitor Overlay Timecode"), this);
addAction(QStringLiteral("monitor_overlay_tc"), overlayTCInfo);
overlayTCInfo->setCheckable(true);
overlayTCInfo->setData(0x02);
QAction *overlayFpsInfo = new QAction(KoIconUtils::themedIcon(QStringLiteral("help-hint")), i18n("Monitor Overlay Playback Fps"), this);
addAction(QStringLiteral("monitor_overlay_fps"), overlayFpsInfo);
overlayFpsInfo->setCheckable(true);
overlayFpsInfo->setData(0x20);
QAction *overlayMarkerInfo = new QAction(KoIconUtils::themedIcon(QStringLiteral("help-hint")), i18n("Monitor Overlay Markers"), this);
addAction(QStringLiteral("monitor_overlay_markers"), overlayMarkerInfo);
overlayMarkerInfo->setCheckable(true);
overlayMarkerInfo->setData(0x04);
QAction *overlaySafeInfo = new QAction(KoIconUtils::themedIcon(QStringLiteral("help-hint")), i18n("Monitor Overlay Safe Zones"), this);
addAction(QStringLiteral("monitor_overlay_safezone"), overlaySafeInfo);
overlaySafeInfo->setCheckable(true);
overlaySafeInfo->setData(0x08);
QAction *overlayAudioInfo = new QAction(KoIconUtils::themedIcon(QStringLiteral("help-hint")), i18n("Monitor Overlay Audio Waveform"), this);
addAction(QStringLiteral("monitor_overlay_audiothumb"), overlayAudioInfo);
overlayAudioInfo->setCheckable(true);
overlayAudioInfo->setData(0x10);
QAction *dropFrames = new QAction(QIcon(), i18n("Real Time (drop frames)"), this);
dropFrames->setCheckable(true);
dropFrames->setChecked(KdenliveSettings::monitor_dropframes());
connect(dropFrames, &QAction::toggled, this, &MainWindow::slotSwitchDropFrames);
KSelectAction *monitorGamma = new KSelectAction(i18n("Monitor Gamma"), this);
monitorGamma->addAction(i18n("sRGB (computer)"));
monitorGamma->addAction(i18n("Rec. 709 (TV)"));
addAction(QStringLiteral("mlt_gamma"), monitorGamma);
monitorGamma->setCurrentItem(KdenliveSettings::monitor_gamma());
connect(monitorGamma, SIGNAL(triggered(int)), this, SLOT(slotSetMonitorGamma(int)));
addAction(QStringLiteral("switch_trim"), i18n("Trim Mode"), this, SLOT(slotSwitchTrimMode()), KoIconUtils::themedIcon(QStringLiteral("cursor-arrow")));
// disable shortcut until fully working, Qt::CTRL + Qt::Key_T);
addAction(QStringLiteral("insert_project_tree"), i18n("Insert Zone in Project Bin"), this, SLOT(slotInsertZoneToTree()), QIcon(), Qt::CTRL + Qt::Key_I);
addAction(QStringLiteral("insert_timeline"), i18n("Insert Zone in Timeline"), this, SLOT(slotInsertZoneToTimeline()), QIcon(),
Qt::SHIFT + Qt::CTRL + Qt::Key_I);
QAction *resizeStart = new QAction(QIcon(), i18n("Resize Item Start"), this);
addAction(QStringLiteral("resize_timeline_clip_start"), resizeStart);
resizeStart->setShortcut(Qt::Key_1);
connect(resizeStart, &QAction::triggered, this, &MainWindow::slotResizeItemStart);
QAction *resizeEnd = new QAction(QIcon(), i18n("Resize Item End"), this);
addAction(QStringLiteral("resize_timeline_clip_end"), resizeEnd);
resizeEnd->setShortcut(Qt::Key_2);
connect(resizeEnd, &QAction::triggered, this, &MainWindow::slotResizeItemEnd);
addAction(QStringLiteral("monitor_seek_snap_backward"), i18n("Go to Previous Snap Point"), this, SLOT(slotSnapRewind()),
KoIconUtils::themedIcon(QStringLiteral("media-seek-backward")), Qt::ALT + Qt::Key_Left);
addAction(QStringLiteral("seek_clip_start"), i18n("Go to Clip Start"), this, SLOT(slotClipStart()),
KoIconUtils::themedIcon(QStringLiteral("media-seek-backward")), Qt::Key_Home);
addAction(QStringLiteral("seek_clip_end"), i18n("Go to Clip End"), this, SLOT(slotClipEnd()), KoIconUtils::themedIcon(QStringLiteral("media-seek-forward")),
Qt::Key_End);
addAction(QStringLiteral("monitor_seek_snap_forward"), i18n("Go to Next Snap Point"), this, SLOT(slotSnapForward()),
KoIconUtils::themedIcon(QStringLiteral("media-seek-forward")), Qt::ALT + Qt::Key_Right);
addAction(QStringLiteral("delete_timeline_clip"), i18n("Delete Selected Item"), this, SLOT(slotDeleteItem()),
KoIconUtils::themedIcon(QStringLiteral("edit-delete")), Qt::Key_Delete);
addAction(QStringLiteral("align_playhead"), i18n("Align Playhead to Mouse Position"), this, SLOT(slotAlignPlayheadToMousePos()), QIcon(), Qt::Key_P);
QAction *stickTransition = new QAction(i18n("Automatic Transition"), this);
stickTransition->setData(QStringLiteral("auto"));
stickTransition->setCheckable(true);
stickTransition->setEnabled(false);
addAction(QStringLiteral("auto_transition"), stickTransition);
connect(stickTransition, &QAction::triggered, this, &MainWindow::slotAutoTransition);
addAction(QStringLiteral("group_clip"), i18n("Group Clips"), this, SLOT(slotGroupClips()), KoIconUtils::themedIcon(QStringLiteral("object-group")),
Qt::CTRL + Qt::Key_G);
QAction *ungroupClip = addAction(QStringLiteral("ungroup_clip"), i18n("Ungroup Clips"), this, SLOT(slotUnGroupClips()),
KoIconUtils::themedIcon(QStringLiteral("object-ungroup")), Qt::CTRL + Qt::SHIFT + Qt::Key_G);
ungroupClip->setData("ungroup_clip");
addAction(QStringLiteral("edit_item_duration"), i18n("Edit Duration"), this, SLOT(slotEditItemDuration()),
KoIconUtils::themedIcon(QStringLiteral("measure")));
addAction(QStringLiteral("clip_in_project_tree"), i18n("Clip in Project Bin"), this, SLOT(slotClipInProjectTree()),
KoIconUtils::themedIcon(QStringLiteral("go-jump-definition")));
addAction(QStringLiteral("overwrite_to_in_point"), i18n("Overwrite Clip Zone in Timeline"), this, SLOT(slotInsertClipOverwrite()),
KoIconUtils::themedIcon(QStringLiteral("timeline-overwrite")), Qt::Key_B);
addAction(QStringLiteral("insert_to_in_point"), i18n("Insert Clip Zone in Timeline"), this, SLOT(slotInsertClipInsert()),
KoIconUtils::themedIcon(QStringLiteral("timeline-insert")), Qt::Key_V);
addAction(QStringLiteral("remove_extract"), i18n("Extract Timeline Zone"), this, SLOT(slotExtractZone()),
KoIconUtils::themedIcon(QStringLiteral("timeline-extract")), Qt::SHIFT + Qt::Key_X);
addAction(QStringLiteral("remove_lift"), i18n("Lift Timeline Zone"), this, SLOT(slotLiftZone()), KoIconUtils::themedIcon(QStringLiteral("timeline-lift")),
Qt::Key_Z);
addAction(QStringLiteral("set_render_timeline_zone"), i18n("Add Preview Zone"), this, SLOT(slotDefinePreviewRender()),
KoIconUtils::themedIcon(QStringLiteral("preview-add-zone")));
addAction(QStringLiteral("unset_render_timeline_zone"), i18n("Remove Preview Zone"), this, SLOT(slotRemovePreviewRender()),
KoIconUtils::themedIcon(QStringLiteral("preview-remove-zone")));
addAction(QStringLiteral("clear_render_timeline_zone"), i18n("Remove All Preview Zones"), this, SLOT(slotClearPreviewRender()),
KoIconUtils::themedIcon(QStringLiteral("preview-remove-all")));
addAction(QStringLiteral("prerender_timeline_zone"), i18n("Start Preview Render"), this, SLOT(slotPreviewRender()),
KoIconUtils::themedIcon(QStringLiteral("preview-render-on")), QKeySequence(Qt::SHIFT + Qt::Key_Return));
addAction(QStringLiteral("stop_prerender_timeline"), i18n("Stop Preview Render"), this, SLOT(slotStopPreviewRender()),
KoIconUtils::themedIcon(QStringLiteral("preview-render-off")));
addAction(QStringLiteral("select_timeline_clip"), i18n("Select Clip"), this, SLOT(slotSelectTimelineClip()),
KoIconUtils::themedIcon(QStringLiteral("edit-select")), Qt::Key_Plus);
addAction(QStringLiteral("deselect_timeline_clip"), i18n("Deselect Clip"), this, SLOT(slotDeselectTimelineClip()),
KoIconUtils::themedIcon(QStringLiteral("edit-select")), Qt::Key_Minus);
addAction(QStringLiteral("select_add_timeline_clip"), i18n("Add Clip To Selection"), this, SLOT(slotSelectAddTimelineClip()),
KoIconUtils::themedIcon(QStringLiteral("edit-select")), Qt::ALT + Qt::Key_Plus);
addAction(QStringLiteral("select_timeline_transition"), i18n("Select Transition"), this, SLOT(slotSelectTimelineTransition()),
KoIconUtils::themedIcon(QStringLiteral("edit-select")), Qt::SHIFT + Qt::Key_Plus);
addAction(QStringLiteral("deselect_timeline_transition"), i18n("Deselect Transition"), this, SLOT(slotDeselectTimelineTransition()),
KoIconUtils::themedIcon(QStringLiteral("edit-select")), Qt::SHIFT + Qt::Key_Minus);
addAction(QStringLiteral("select_add_timeline_transition"), i18n("Add Transition To Selection"), this, SLOT(slotSelectAddTimelineTransition()),
KoIconUtils::themedIcon(QStringLiteral("edit-select")), Qt::ALT + Qt::SHIFT + Qt::Key_Plus);
addAction(QStringLiteral("cut_timeline_clip"), i18n("Cut Clip"), this, SLOT(slotCutTimelineClip()), KoIconUtils::themedIcon(QStringLiteral("edit-cut")),
Qt::SHIFT + Qt::Key_R);
addAction(QStringLiteral("add_clip_marker"), i18n("Add Marker"), this, SLOT(slotAddClipMarker()), KoIconUtils::themedIcon(QStringLiteral("bookmark-new")));
addAction(QStringLiteral("delete_clip_marker"), i18n("Delete Marker"), this, SLOT(slotDeleteClipMarker()),
KoIconUtils::themedIcon(QStringLiteral("edit-delete")));
addAction(QStringLiteral("delete_all_clip_markers"), i18n("Delete All Markers"), this, SLOT(slotDeleteAllClipMarkers()),
KoIconUtils::themedIcon(QStringLiteral("edit-delete")));
QAction *editClipMarker = addAction(QStringLiteral("edit_clip_marker"), i18n("Edit Marker"), this, SLOT(slotEditClipMarker()),
KoIconUtils::themedIcon(QStringLiteral("document-properties")));
editClipMarker->setData(QStringLiteral("edit_marker"));
addAction(QStringLiteral("add_marker_guide_quickly"), i18n("Add Marker/Guide quickly"), this, SLOT(slotAddMarkerGuideQuickly()),
KoIconUtils::themedIcon(QStringLiteral("bookmark-new")), Qt::Key_Asterisk);
QAction *splitAudio =
addAction(QStringLiteral("split_audio"), i18n("Split Audio"), this, SLOT(slotSplitAudio()), KoIconUtils::themedIcon(QStringLiteral("document-new")));
// "A+V" as data means this action should only be available for clips with audio AND video
splitAudio->setData("A+V");
QAction *setAudioAlignReference = addAction(QStringLiteral("set_audio_align_ref"), i18n("Set Audio Reference"), this, SLOT(slotSetAudioAlignReference()));
// "A" as data means this action should only be available for clips with audio
setAudioAlignReference->setData("A");
QAction *alignAudio = addAction(QStringLiteral("align_audio"), i18n("Align Audio to Reference"), this, SLOT(slotAlignAudio()), QIcon());
// "A" as data means this action should only be available for clips with audio
alignAudio->setData("A");
QAction *audioOnly = new QAction(KoIconUtils::themedIcon(QStringLiteral("document-new")), i18n("Audio Only"), this);
addAction(QStringLiteral("clip_audio_only"), audioOnly);
audioOnly->setData(PlaylistState::AudioOnly);
audioOnly->setCheckable(true);
QAction *videoOnly = new QAction(KoIconUtils::themedIcon(QStringLiteral("document-new")), i18n("Video Only"), this);
addAction(QStringLiteral("clip_video_only"), videoOnly);
videoOnly->setData(PlaylistState::VideoOnly);
videoOnly->setCheckable(true);
QAction *audioAndVideo = new QAction(KoIconUtils::themedIcon(QStringLiteral("document-new")), i18n("Audio and Video"), this);
addAction(QStringLiteral("clip_audio_and_video"), audioAndVideo);
audioAndVideo->setData(PlaylistState::Original);
audioAndVideo->setCheckable(true);
m_clipTypeGroup = new QActionGroup(this);
m_clipTypeGroup->addAction(audioOnly);
m_clipTypeGroup->addAction(videoOnly);
m_clipTypeGroup->addAction(audioAndVideo);
connect(m_clipTypeGroup, &QActionGroup::triggered, this, &MainWindow::slotUpdateClipType);
m_clipTypeGroup->setEnabled(false);
addAction(QStringLiteral("insert_space"), i18n("Insert Space"), this, SLOT(slotInsertSpace()));
addAction(QStringLiteral("delete_space"), i18n("Remove Space"), this, SLOT(slotRemoveSpace()));
addAction(QStringLiteral("delete_space_all_tracks"), i18n("Remove Space In All Tracks"), this, SLOT(slotRemoveAllSpace()));
KActionCategory *timelineActions = new KActionCategory(i18n("Tracks"), actionCollection());
QAction *insertTrack = new QAction(QIcon(), i18n("Insert Track"), this);
connect(insertTrack, &QAction::triggered, this, &MainWindow::slotInsertTrack);
timelineActions->addAction(QStringLiteral("insert_track"), insertTrack);
QAction *deleteTrack = new QAction(QIcon(), i18n("Delete Track"), this);
connect(deleteTrack, &QAction::triggered, this, &MainWindow::slotDeleteTrack);
timelineActions->addAction(QStringLiteral("delete_track"), deleteTrack);
deleteTrack->setData("delete_track");
QAction *configTracks = new QAction(KoIconUtils::themedIcon(QStringLiteral("configure")), i18n("Configure Tracks"), this);
connect(configTracks, &QAction::triggered, this, &MainWindow::slotConfigTrack);
timelineActions->addAction(QStringLiteral("config_tracks"), configTracks);
QAction *selectTrack = new QAction(QIcon(), i18n("Select All in Current Track"), this);
connect(selectTrack, &QAction::triggered, this, &MainWindow::slotSelectTrack);
timelineActions->addAction(QStringLiteral("select_track"), selectTrack);
QAction *selectAll = KStandardAction::selectAll(this, SLOT(slotSelectAllTracks()), this);
selectAll->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-select-all")));
selectAll->setShortcutContext(Qt::WidgetWithChildrenShortcut);
timelineActions->addAction(QStringLiteral("select_all_tracks"), selectAll);
QAction *unselectAll = KStandardAction::deselect(this, SLOT(slotUnselectAllTracks()), this);
unselectAll->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-unselect-all")));
unselectAll->setShortcutContext(Qt::WidgetWithChildrenShortcut);
timelineActions->addAction(QStringLiteral("unselect_all_tracks"), unselectAll);
kdenliveCategoryMap.insert(QStringLiteral("timeline"), timelineActions);
// Cached data management
addAction(QStringLiteral("manage_cache"), i18n("Manage Cached Data"), this, SLOT(slotManageCache()),
KoIconUtils::themedIcon(QStringLiteral("network-server-database")));
QAction *disablePreview = new QAction(i18n("Disable Timeline Preview"), this);
disablePreview->setCheckable(true);
addAction(QStringLiteral("disable_preview"), disablePreview);
addAction(QStringLiteral("add_guide"), i18n("Add Guide"), this, SLOT(slotAddGuide()), KoIconUtils::themedIcon(QStringLiteral("list-add")));
addAction(QStringLiteral("delete_guide"), i18n("Delete Guide"), this, SLOT(slotDeleteGuide()), KoIconUtils::themedIcon(QStringLiteral("edit-delete")));
addAction(QStringLiteral("edit_guide"), i18n("Edit Guide"), this, SLOT(slotEditGuide()), KoIconUtils::themedIcon(QStringLiteral("document-properties")));
addAction(QStringLiteral("delete_all_guides"), i18n("Delete All Guides"), this, SLOT(slotDeleteAllGuides()),
KoIconUtils::themedIcon(QStringLiteral("edit-delete")));
QAction *pasteEffects = addAction(QStringLiteral("paste_effects"), i18n("Paste Effects"), this, SLOT(slotPasteEffects()),
KoIconUtils::themedIcon(QStringLiteral("edit-paste")));
pasteEffects->setData("paste_effects");
m_saveAction = KStandardAction::save(pCore->projectManager(), SLOT(saveFile()), actionCollection());
m_saveAction->setIcon(KoIconUtils::themedIcon(QStringLiteral("document-save")));
addAction(QStringLiteral("save_selection"), i18n("Save Selection"), pCore->projectManager(), SLOT(slotSaveSelection()),
KoIconUtils::themedIcon(QStringLiteral("document-save")));
QAction *sentToLibrary = addAction(QStringLiteral("send_library"), i18n("Add Timeline Selection to Library"), pCore->library(), SLOT(slotAddToLibrary()),
KoIconUtils::themedIcon(QStringLiteral("bookmark-new")));
pCore->library()->setupActions(QList() << sentToLibrary);
KStandardAction::showMenubar(this, SLOT(showMenuBar(bool)), actionCollection());
QAction *a = KStandardAction::quit(this, SLOT(close()), actionCollection());
a->setIcon(KoIconUtils::themedIcon(QStringLiteral("application-exit")));
// TODO: make the following connection to slotEditKeys work
// KStandardAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());
a = KStandardAction::preferences(this, SLOT(slotPreferences()), actionCollection());
a->setIcon(KoIconUtils::themedIcon(QStringLiteral("configure")));
a = KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection());
a->setIcon(KoIconUtils::themedIcon(QStringLiteral("configure")));
a = KStandardAction::copy(this, SLOT(slotCopy()), actionCollection());
a->setIcon(KoIconUtils::themedIcon(QStringLiteral("edit-copy")));
a = KStandardAction::paste(this, SLOT(slotPaste()), actionCollection());
a->setIcon(KoIconUtils::themedIcon(QStringLiteral("edit-paste")));
a = KStandardAction::fullScreen(this, SLOT(slotFullScreen()), this, actionCollection());
a->setIcon(KoIconUtils::themedIcon(QStringLiteral("view-fullscreen")));
QAction *undo = KStandardAction::undo(m_commandStack, SLOT(undo()), actionCollection());
undo->setIcon(KoIconUtils::themedIcon(QStringLiteral("edit-undo")));
undo->setEnabled(false);
connect(m_commandStack, &QUndoGroup::canUndoChanged, undo, &QAction::setEnabled);
QAction *redo = KStandardAction::redo(m_commandStack, SLOT(redo()), actionCollection());
redo->setIcon(KoIconUtils::themedIcon(QStringLiteral("edit-redo")));
redo->setEnabled(false);
connect(m_commandStack, &QUndoGroup::canRedoChanged, redo, &QAction::setEnabled);
auto *addClips = new QMenu(this);
QAction *addClip = addAction(QStringLiteral("add_clip"), i18n("Add Clip"), pCore->bin(), SLOT(slotAddClip()),
KoIconUtils::themedIcon(QStringLiteral("kdenlive-add-clip")));
addClips->addAction(addClip);
QAction *action = addAction(QStringLiteral("add_color_clip"), i18n("Add Color Clip"), pCore->bin(), SLOT(slotCreateProjectClip()),
KoIconUtils::themedIcon(QStringLiteral("kdenlive-add-color-clip")));
action->setData((int)Color);
addClips->addAction(action);
action = addAction(QStringLiteral("add_slide_clip"), i18n("Add Slideshow Clip"), pCore->bin(), SLOT(slotCreateProjectClip()),
KoIconUtils::themedIcon(QStringLiteral("kdenlive-add-slide-clip")));
action->setData((int)SlideShow);
addClips->addAction(action);
action = addAction(QStringLiteral("add_text_clip"), i18n("Add Title Clip"), pCore->bin(), SLOT(slotCreateProjectClip()),
KoIconUtils::themedIcon(QStringLiteral("kdenlive-add-text-clip")));
action->setData((int)Text);
addClips->addAction(action);
action = addAction(QStringLiteral("add_text_template_clip"), i18n("Add Template Title"), pCore->bin(), SLOT(slotCreateProjectClip()),
KoIconUtils::themedIcon(QStringLiteral("kdenlive-add-text-clip")));
action->setData((int)TextTemplate);
addClips->addAction(action);
/*action = addAction(QStringLiteral("add_qtext_clip"), i18n("Add Simple Text Clip"), pCore->bin(), SLOT(slotCreateProjectClip()),
KoIconUtils::themedIcon(QStringLiteral("kdenlive-add-text-clip")));
action->setData((int) QText);
addClips->addAction(action);*/
QAction *addFolder = addAction(QStringLiteral("add_folder"), i18n("Create Folder"), pCore->bin(), SLOT(slotAddFolder()),
KoIconUtils::themedIcon(QStringLiteral("folder-new")));
addClips->addAction(addAction(QStringLiteral("download_resource"), i18n("Online Resources"), this, SLOT(slotDownloadResources()),
KoIconUtils::themedIcon(QStringLiteral("edit-download"))));
QAction *clipProperties = addAction(QStringLiteral("clip_properties"), i18n("Clip Properties"), pCore->bin(), SLOT(slotSwitchClipProperties()),
KoIconUtils::themedIcon(QStringLiteral("document-edit")));
clipProperties->setData("clip_properties");
QAction *openClip =
addAction(QStringLiteral("edit_clip"), i18n("Edit Clip"), pCore->bin(), SLOT(slotOpenClip()), KoIconUtils::themedIcon(QStringLiteral("document-open")));
openClip->setData("edit_clip");
openClip->setEnabled(false);
QAction *deleteClip = addAction(QStringLiteral("delete_clip"), i18n("Delete Clip"), pCore->bin(), SLOT(slotDeleteClip()),
KoIconUtils::themedIcon(QStringLiteral("edit-delete")));
deleteClip->setData("delete_clip");
deleteClip->setEnabled(false);
QAction *reloadClip = addAction(QStringLiteral("reload_clip"), i18n("Reload Clip"), pCore->bin(), SLOT(slotReloadClip()),
KoIconUtils::themedIcon(QStringLiteral("view-refresh")));
reloadClip->setData("reload_clip");
reloadClip->setEnabled(false);
QAction *disableEffects = addAction(QStringLiteral("disable_timeline_effects"), i18n("Disable Timeline Effects"), pCore->projectManager(),
SLOT(slotDisableTimelineEffects(bool)), KoIconUtils::themedIcon(QStringLiteral("favorite")));
disableEffects->setData("disable_timeline_effects");
disableEffects->setCheckable(true);
disableEffects->setChecked(false);
QAction *locateClip = addAction(QStringLiteral("locate_clip"), i18n("Locate Clip..."), pCore->bin(), SLOT(slotLocateClip()),
KoIconUtils::themedIcon(QStringLiteral("edit-file")));
locateClip->setData("locate_clip");
locateClip->setEnabled(false);
QAction *duplicateClip = addAction(QStringLiteral("duplicate_clip"), i18n("Duplicate Clip"), pCore->bin(), SLOT(slotDuplicateClip()),
KoIconUtils::themedIcon(QStringLiteral("edit-copy")));
duplicateClip->setData("duplicate_clip");
duplicateClip->setEnabled(false);
QAction *proxyClip = new QAction(i18n("Proxy Clip"), this);
addAction(QStringLiteral("proxy_clip"), proxyClip);
proxyClip->setData(QStringList() << QString::number((int)AbstractClipJob::PROXYJOB));
proxyClip->setCheckable(true);
proxyClip->setChecked(false);
addAction(QStringLiteral("switch_track_lock"), i18n("Toggle Track Lock"), pCore->projectManager(), SLOT(slotSwitchTrackLock()), QIcon(),
Qt::SHIFT + Qt::Key_L);
addAction(QStringLiteral("switch_all_track_lock"), i18n("Toggle All Track Lock"), pCore->projectManager(), SLOT(slotSwitchAllTrackLock()), QIcon(),
Qt::CTRL + Qt::SHIFT + Qt::Key_L);
addAction(QStringLiteral("switch_track_target"), i18n("Toggle Track Target"), pCore->projectManager(), SLOT(slotSwitchTrackTarget()), QIcon(),
Qt::SHIFT + Qt::Key_T);
QHash actions;
actions.insert(QStringLiteral("locate"), locateClip);
actions.insert(QStringLiteral("reload"), reloadClip);
actions.insert(QStringLiteral("duplicate"), duplicateClip);
actions.insert(QStringLiteral("proxy"), proxyClip);
actions.insert(QStringLiteral("properties"), clipProperties);
actions.insert(QStringLiteral("open"), openClip);
actions.insert(QStringLiteral("delete"), deleteClip);
actions.insert(QStringLiteral("folder"), addFolder);
pCore->bin()->setupMenu(addClips, addClip, actions);
// Setup effects and transitions actions.
KActionCategory *transitionActions = new KActionCategory(i18n("Transitions"), actionCollection());
// m_transitions = new QAction*[transitions.count()];
for (int i = 0; i < transitions.count(); ++i) {
QStringList effectInfo = transitions.effectIdInfo(i);
if (effectInfo.isEmpty()) {
continue;
}
auto *transAction = new QAction(effectInfo.at(0), this);
transAction->setData(effectInfo);
transAction->setIconVisibleInMenu(false);
m_transitions << transAction;
QString id = effectInfo.at(2);
if (id.isEmpty()) {
id = effectInfo.at(1);
}
transitionActions->addAction("transition_" + id, transAction);
}
// monitor actions
addAction(QStringLiteral("extract_frame"), i18n("Extract frame..."), pCore->monitorManager(), SLOT(slotExtractCurrentFrame()), KoIconUtils::themedIcon(QStringLiteral("insert-image")));
addAction(QStringLiteral("extract_frame_to_project"), i18n("Extract frame to project..."), pCore->monitorManager(), SLOT(slotExtractCurrentFrameToProject()), KoIconUtils::themedIcon(QStringLiteral("insert-image")));
}
void MainWindow::saveOptions()
{
KdenliveSettings::self()->save();
}
bool MainWindow::readOptions()
{
KSharedConfigPtr config = KSharedConfig::openConfig();
pCore->projectManager()->recentFilesAction()->loadEntries(KConfigGroup(config, "Recent Files"));
if (KdenliveSettings::defaultprojectfolder().isEmpty()) {
QDir dir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
dir.mkpath(QStringLiteral("."));
KdenliveSettings::setDefaultprojectfolder(dir.absolutePath());
}
if (KdenliveSettings::trackheight() == 0) {
QFontMetrics metrics(font());
int trackHeight = 2 * metrics.height();
QStyle *style = qApp->style();
trackHeight += style->pixelMetric(QStyle::PM_ToolBarIconSize) + 2 * style->pixelMetric(QStyle::PM_ToolBarItemMargin) +
style->pixelMetric(QStyle::PM_ToolBarItemSpacing) + 2;
KdenliveSettings::setTrackheight(trackHeight);
}
if (KdenliveSettings::trackheight() == 0) {
KdenliveSettings::setTrackheight(50);
}
bool firstRun = false;
KConfigGroup initialGroup(config, "version");
if (!initialGroup.exists()) {
// First run, check if user is on a KDE Desktop
firstRun = true;
// this is our first run, show Wizard
QPointer w = new Wizard(true);
if (w->exec() == QDialog::Accepted && w->isOk()) {
w->adjustSettings();
delete w;
} else {
delete w;
::exit(1);
}
} else if (!KdenliveSettings::ffmpegpath().isEmpty() && !QFile::exists(KdenliveSettings::ffmpegpath())) {
// Invalid entry for FFmpeg, check system
QPointer w = new Wizard(true);
if (w->exec() == QDialog::Accepted && w->isOk()) {
w->adjustSettings();
}
delete w;
}
initialGroup.writeEntry("version", version);
return firstRun;
}
void MainWindow::slotRunWizard()
{
QPointer w = new Wizard(false, this);
if (w->exec() == QDialog::Accepted && w->isOk()) {
w->adjustSettings();
}
delete w;
}
void MainWindow::slotRefreshProfiles()
{
KdenliveSettingsDialog *d = static_cast(KConfigDialog::exists(QStringLiteral("settings")));
if (d) {
d->checkProfile();
}
}
void MainWindow::slotEditProjectSettings()
{
KdenliveDoc *project = pCore->currentDoc();
QPoint p = getMainTimeline()->getTracksCount();
ProjectSettings *w = new ProjectSettings(project, project->metadata(), getMainTimeline()->controller()->extractCompositionLumas(), p.x(),
p.y(), project->projectTempFolder(), true, !project->isModified(), this);
connect(w, &ProjectSettings::disableProxies, this, &MainWindow::slotDisableProxies);
//connect(w, SIGNAL(disablePreview()), pCore->projectManager()->currentTimeline(), SLOT(invalidateRange()));
connect(w, &ProjectSettings::refreshProfiles, this, &MainWindow::slotRefreshProfiles);
if (w->exec() == QDialog::Accepted) {
QString profile = w->selectedProfile();
// project->setProjectFolder(w->selectedFolder());
// TODO: timeline preview
// pCore->projectManager()->currentTimeline()->updatePreviewSettings(w->selectedPreview());
bool modified = false;
if (m_renderWidget) {
m_renderWidget->setDocumentPath(project->projectDataFolder() + QDir::separator());
}
if (KdenliveSettings::videothumbnails() != w->enableVideoThumbs()) {
slotSwitchVideoThumbs();
}
if (KdenliveSettings::audiothumbnails() != w->enableAudioThumbs()) {
slotSwitchAudioThumbs();
}
if (pCore->getCurrentProfile()->path() != profile || project->profileChanged(profile)) {
pCore->setCurrentProfile(profile);
pCore->projectManager()->slotResetProfiles();
slotUpdateDocumentState(true);
}
if (project->getDocumentProperty(QStringLiteral("proxyparams")) != w->proxyParams() ||
project->getDocumentProperty(QStringLiteral("proxyextension")) != w->proxyExtension()) {
modified = true;
project->setDocumentProperty(QStringLiteral("proxyparams"), w->proxyParams());
project->setDocumentProperty(QStringLiteral("proxyextension"), w->proxyExtension());
if (pCore->binController()->clipCount() > 0 &&
KMessageBox::questionYesNo(this, i18n("You have changed the proxy parameters. Do you want to recreate all proxy clips for this project?")) ==
KMessageBox::Yes) {
// TODO: rebuild all proxies
pCore->bin()->rebuildProxies();
}
}
if (project->getDocumentProperty(QStringLiteral("generateproxy")) != QString::number((int)w->generateProxy())) {
modified = true;
project->setDocumentProperty(QStringLiteral("generateproxy"), QString::number((int)w->generateProxy()));
}
if (project->getDocumentProperty(QStringLiteral("proxyminsize")) != QString::number(w->proxyMinSize())) {
modified = true;
project->setDocumentProperty(QStringLiteral("proxyminsize"), QString::number(w->proxyMinSize()));
}
if (project->getDocumentProperty(QStringLiteral("generateimageproxy")) != QString::number((int)w->generateImageProxy())) {
modified = true;
project->setDocumentProperty(QStringLiteral("generateimageproxy"), QString::number((int)w->generateImageProxy()));
}
if (project->getDocumentProperty(QStringLiteral("proxyimageminsize")) != QString::number(w->proxyImageMinSize())) {
modified = true;
project->setDocumentProperty(QStringLiteral("proxyimageminsize"), QString::number(w->proxyImageMinSize()));
}
if (QString::number((int)w->useProxy()) != project->getDocumentProperty(QStringLiteral("enableproxy"))) {
project->setDocumentProperty(QStringLiteral("enableproxy"), QString::number((int)w->useProxy()));
modified = true;
slotUpdateProxySettings();
}
if (w->metadata() != project->metadata()) {
project->setMetadata(w->metadata());
}
QString newProjectFolder = w->storageFolder();
if (newProjectFolder.isEmpty()) {
newProjectFolder = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
}
if (newProjectFolder != project->projectTempFolder()) {
KMessageBox::ButtonCode answer;
// Project folder changed:
if (project->isModified()) {
answer = KMessageBox::warningContinueCancel(this, i18n("The current project has not been saved. This will first save the project, then move "
"all temporary files from %1 to %2, and the project file will be reloaded",
project->projectTempFolder(), newProjectFolder));
if (answer == KMessageBox::Continue) {
pCore->projectManager()->saveFile();
}
} else {
answer = KMessageBox::warningContinueCancel(
this, i18n("This will move all temporary files from %1 to %2, the project file will then be reloaded",
project->projectTempFolder(), newProjectFolder));
}
if (answer == KMessageBox::Continue) {
// Proceeed with move
QString documentId = QDir::cleanPath(project->getDocumentProperty(QStringLiteral("documentid")));
bool ok;
documentId.toLongLong(&ok, 10);
if (!ok || documentId.isEmpty()) {
KMessageBox::sorry(this, i18n("Cannot perform operation, invalid document id: %1", documentId));
} else {
QDir newDir(newProjectFolder);
QDir oldDir(project->projectTempFolder());
if (newDir.exists(documentId)) {
KMessageBox::sorry(this, i18n("Cannot perform operation, target directory already exists: %1", newDir.absoluteFilePath(documentId)));
} else {
// Proceed with the move
pCore->projectManager()->moveProjectData(oldDir.absoluteFilePath(documentId), newDir.absolutePath());
}
}
}
}
if (modified) {
project->setModified();
}
}
delete w;
}
void MainWindow::slotDisableProxies()
{
pCore->currentDoc()->setDocumentProperty(QStringLiteral("enableproxy"), QString::number((int)false));
pCore->currentDoc()->setModified();
slotUpdateProxySettings();
}
void MainWindow::slotStopRenderProject()
{
if (m_renderWidget) {
m_renderWidget->slotAbortCurrentJob();
}
}
void MainWindow::slotRenderProject()
{
KdenliveDoc *project = pCore->currentDoc();
if (!m_renderWidget) {
QString projectfolder = project ? project->projectDataFolder() + QDir::separator() : KdenliveSettings::defaultprojectfolder();
if (project) {
m_renderWidget = new RenderWidget(projectfolder, project->useProxy(), this);
connect(m_renderWidget, &RenderWidget::shutdown, this, &MainWindow::slotShutdown);
connect(m_renderWidget, &RenderWidget::selectedRenderProfile, this, &MainWindow::slotSetDocumentRenderProfile);
connect(m_renderWidget, &RenderWidget::prepareRenderingData, this, &MainWindow::slotPrepareRendering);
connect(m_renderWidget, &RenderWidget::abortProcess, this, &MainWindow::abortRenderJob);
connect(m_renderWidget, &RenderWidget::openDvdWizard, this, &MainWindow::slotDvdWizard);
connect(this, &MainWindow::updateRenderWidgetProfile, m_renderWidget, &RenderWidget::adjustViewToProfile);
m_renderWidget->setGuides(project->getGuideModel()->getAllMarkers(), project->projectDuration());
m_renderWidget->setDocumentPath(project->projectDataFolder() + QDir::separator());
m_renderWidget->setRenderProfile(project->getRenderProperties());
}
if (m_compositeAction->currentAction()) {
m_renderWidget->errorMessage(RenderWidget::CompositeError,
m_compositeAction->currentAction()->data().toInt() == 1 ? i18n("Rendering using low quality track compositing")
: QString());
}
}
slotCheckRenderStatus();
m_renderWidget->show();
// m_renderWidget->showNormal();
// What are the following lines supposed to do?
// m_renderWidget->enableAudio(false);
// m_renderWidget->export_audio;
}
void MainWindow::slotCheckRenderStatus()
{
// Make sure there are no missing clips
// TODO
/*if (m_renderWidget)
m_renderWidget->missingClips(pCore->bin()->hasMissingClips());*/
}
void MainWindow::setRenderingProgress(const QString &url, int progress)
{
emit setRenderProgress(progress);
if (m_renderWidget) {
m_renderWidget->setRenderJob(url, progress);
}
}
void MainWindow::setRenderingFinished(const QString &url, int status, const QString &error)
{
emit setRenderProgress(100);
if (m_renderWidget) {
m_renderWidget->setRenderStatus(url, status, error);
}
}
void MainWindow::addProjectClip(const QString &url)
{
if (pCore->currentDoc()) {
QStringList ids = pCore->binController()->getBinIdsByResource(QFileInfo(url));
if (!ids.isEmpty()) {
// Clip is already in project bin, abort
return;
}
ClipCreationDialog::createClipsCommand(pCore->currentDoc(), QList() << QUrl::fromLocalFile(url), QStringList(), pCore->bin());
}
}
void MainWindow::addTimelineClip(const QString &url)
{
if (pCore->currentDoc()) {
QStringList ids = pCore->binController()->getBinIdsByResource(QFileInfo(url));
if (!ids.isEmpty()) {
pCore->bin()->selectClipById(ids.constFirst());
slotInsertClipInsert();
}
}
}
void MainWindow::addEffect(const QString &effectName)
{
QStringList effectInfo;
effectInfo << effectName << effectName;
const QDomElement effect = EffectsListWidget::itemEffect(5, effectInfo);
if (!effect.isNull()) {
slotAddEffect(effect);
} else {
qCDebug(KDENLIVE_LOG) << " * * *EFFECT: " << effectName << " NOT AVAILABLE";
exitApp();
}
}
void MainWindow::scriptRender(const QString &url)
{
slotRenderProject();
m_renderWidget->slotPrepareExport(true, url);
}
void MainWindow::exitApp()
{
QApplication::exit(0);
}
void MainWindow::slotCleanProject()
{
if (KMessageBox::warningContinueCancel(this, i18n("This will remove all unused clips from your project."), i18n("Clean up project")) ==
KMessageBox::Cancel) {
return;
}
pCore->bin()->cleanup();
}
void MainWindow::slotUpdateMousePosition(int pos)
{
if (pCore->currentDoc()) {
switch (m_timeFormatButton->currentItem()) {
case 0:
m_timeFormatButton->setText(
pCore->currentDoc()->timecode().getTimecodeFromFrames(pos) + QStringLiteral(" / ") +
pCore->currentDoc()->timecode().getTimecodeFromFrames(getMainTimeline()->controller()->duration()));
break;
default:
m_timeFormatButton->setText(QStringLiteral("%1 / %2")
.arg(pos, 6, 10, QLatin1Char('0'))
.arg(getMainTimeline()->controller()->duration(), 6, 10, QLatin1Char('0')));
}
}
}
void MainWindow::slotUpdateProjectDuration(int pos)
{
if (pCore->currentDoc()) {
slotUpdateMousePosition(getMainTimeline()->controller()->getMousePos());
}
}
void MainWindow::slotUpdateDocumentState(bool modified)
{
setWindowTitle(pCore->currentDoc()->description());
setWindowModified(modified);
m_saveAction->setEnabled(modified);
}
void MainWindow::connectDocument()
{
KdenliveDoc *project = pCore->currentDoc();
connect(project, &KdenliveDoc::startAutoSave, pCore->projectManager(), &ProjectManager::slotStartAutoSave);
connect(project, &KdenliveDoc::reloadEffects, this, &MainWindow::slotReloadEffects);
KdenliveSettings::setProject_fps(pCore->getCurrentFps());
// TODO REFAC: reconnect to new timeline
/*
Timeline *trackView = pCore->projectManager()->currentTimeline();
connect(trackView, &Timeline::configTrack, this, &MainWindow::slotConfigTrack);
connect(trackView, &Timeline::updateTracksInfo, this, &MainWindow::slotUpdateTrackInfo);
connect(trackView, &Timeline::mousePosition, this, &MainWindow::slotUpdateMousePosition);
connect(pCore->producerQueue(), &ProducerQueue::infoProcessingFinished, trackView->projectView(), &CustomTrackView::slotInfoProcessingFinished,
Qt::DirectConnection);
connect(trackView->projectView(), &CustomTrackView::importKeyframes, this, &MainWindow::slotProcessImportKeyframes);
connect(trackView->projectView(), &CustomTrackView::updateTrimMode, this, &MainWindow::setTrimMode);
connect(m_projectMonitor, &Monitor::multitrackView, trackView, &Timeline::slotMultitrackView);
connect(m_projectMonitor, SIGNAL(renderPosition(int)), trackView, SLOT(moveCursorPos(int)));
connect(m_projectMonitor, SIGNAL(zoneUpdated(QPoint)), trackView, SLOT(slotSetZone(QPoint)));
connect(trackView->projectView(), &CustomTrackView::guidesUpdated, this, &MainWindow::slotGuidesUpdated);
connect(trackView->projectView(), &CustomTrackView::loadMonitorScene, m_projectMonitor, &Monitor::slotShowEffectScene);
connect(trackView->projectView(), &CustomTrackView::setQmlProperty, m_projectMonitor, &Monitor::setQmlProperty);
connect(m_projectMonitor, SIGNAL(acceptRipple(bool)), trackView->projectView(), SLOT(slotAcceptRipple(bool)));
connect(m_projectMonitor, SIGNAL(switchTrimMode(int)), trackView->projectView(), SLOT(switchTrimMode(int)));
connect(project, &KdenliveDoc::saveTimelinePreview, trackView, &Timeline::slotSaveTimelinePreview);
connect(trackView, SIGNAL(showTrackEffects(int, TrackInfo)), this, SLOT(slotTrackSelected(int, TrackInfo)));
connect(trackView->projectView(), &CustomTrackView::clipItemSelected, this, &MainWindow::slotTimelineClipSelected, Qt::DirectConnection);
connect(trackView->projectView(), &CustomTrackView::setActiveKeyframe, m_effectStack, &EffectStackView2::setActiveKeyframe);
connect(trackView->projectView(), SIGNAL(transitionItemSelected(Transition *, int, QPoint, bool)), m_effectStack, SLOT(slotTransitionItemSelected(Transition
*, int, QPoint, bool)), Qt::DirectConnection);
connect(trackView->projectView(), SIGNAL(transitionItemSelected(Transition *, int, QPoint, bool)), this, SLOT(slotActivateTransitionView(Transition *)));
connect(trackView->projectView(), &CustomTrackView::zoomIn, this, &MainWindow::slotZoomIn);
connect(trackView->projectView(), &CustomTrackView::zoomOut, this, &MainWindow::slotZoomOut);
connect(trackView, SIGNAL(setZoom(int)), this, SLOT(slotSetZoom(int)));
connect(trackView, SIGNAL(displayMessage(QString, MessageType)), m_messageLabel, SLOT(setMessage(QString, MessageType)));
connect(trackView->projectView(), SIGNAL(displayMessage(QString, MessageType)), m_messageLabel, SLOT(setMessage(QString, MessageType)));
connect(pCore->bin(), &Bin::clipNameChanged, trackView->projectView(), &CustomTrackView::clipNameChanged);
connect(trackView->projectView(), SIGNAL(showClipFrame(QString, int)), pCore->bin(), SLOT(selectClipById(QString, int)));
connect(trackView->projectView(), SIGNAL(playMonitor()), m_projectMonitor, SLOT(slotPlay()));
connect(trackView->projectView(), &CustomTrackView::pauseMonitor, m_projectMonitor, &Monitor::pause, Qt::DirectConnection);
connect(m_projectMonitor, &Monitor::addEffect, trackView->projectView(), &CustomTrackView::slotAddEffectToCurrentItem);
connect(trackView->projectView(), SIGNAL(transitionItemSelected(Transition *, int, QPoint, bool)), m_projectMonitor, SLOT(slotSetSelectedClip(Transition
*)));
connect(pCore->bin(), SIGNAL(gotFilterJobResults(QString, int, int, stringMap, stringMap)), trackView->projectView(), SLOT(slotGotFilterJobResults(QString,
int, int, stringMap, stringMap)));
//TODO
//connect(m_projectList, SIGNAL(addMarkers(QString,QList)), trackView->projectView(), SLOT(slotAddClipMarker(QString,QList)));
// Effect stack signals
connect(m_effectStack, &EffectStackView2::updateEffect, trackView->projectView(), &CustomTrackView::slotUpdateClipEffect);
connect(m_effectStack, &EffectStackView2::updateClipRegion, trackView->projectView(), &CustomTrackView::slotUpdateClipRegion);
connect(m_effectStack, SIGNAL(removeEffect(ClipItem *, int, QDomElement)), trackView->projectView(), SLOT(slotDeleteEffect(ClipItem *, int, QDomElement)));
connect(m_effectStack, SIGNAL(removeEffectGroup(ClipItem *, int, QDomDocument)), trackView->projectView(), SLOT(slotDeleteEffectGroup(ClipItem *, int,
QDomDocument)));
connect(m_effectStack, SIGNAL(addEffect(ClipItem *, QDomElement, int)), trackView->projectView(), SLOT(slotAddEffect(ClipItem *, QDomElement, int)));
connect(m_effectStack, SIGNAL(changeEffectState(ClipItem *, int, QList, bool)), trackView->projectView(), SLOT(slotChangeEffectState(ClipItem *, int,
QList, bool)));
connect(m_effectStack, SIGNAL(changeEffectPosition(ClipItem *, int, QList, int)), trackView->projectView(), SLOT(slotChangeEffectPosition(ClipItem *,
int, QList