Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Cleanup, move snap model into kdenlivedoc so it can be used by timeline guides

parent cb53f4d8
......@@ -39,13 +39,15 @@ MarkerListModel::MarkerListModel(const QString &clipId, std::weak_ptr<DocUndoSta
, m_undoStack(std::move(undo_stack))
, m_guide(false)
, m_clipId(clipId)
, m_snaps(nullptr)
{
}
MarkerListModel::MarkerListModel(std::weak_ptr<DocUndoStack> undo_stack, QObject *parent)
MarkerListModel::MarkerListModel(std::weak_ptr<DocUndoStack> undo_stack, std::unique_ptr<SnapModel> &snapModel, QObject *parent)
: QAbstractListModel(parent)
, m_undoStack(std::move(undo_stack))
, m_guide(true)
, m_snaps(snapModel.get())
{
}
......@@ -117,6 +119,7 @@ Fun MarkerListModel::addMarker_lambda(GenTime pos, const QString &comment, int t
model->beginInsertRows(QModelIndex(), insertionRow, insertionRow);
model->m_markerList[pos] = {comment, type};
model->endInsertRows();
model->addSnapPoint(pos);
return true;
};
}
......@@ -132,6 +135,7 @@ Fun MarkerListModel::deleteMarker_lambda(GenTime pos)
model->beginRemoveRows(QModelIndex(), row, row);
model->m_markerList.erase(pos);
model->endRemoveRows();
model->removeSnapPoint(pos);
return true;
};
}
......@@ -154,6 +158,20 @@ QHash<int, QByteArray> MarkerListModel::roleNames() const
return roles;
}
void MarkerListModel::addSnapPoint(GenTime pos)
{
if (m_snaps) {
m_snaps->addPoint(pos.frames(pCore->getCurrentFps()));
}
}
void MarkerListModel::removeSnapPoint(GenTime pos)
{
if (m_snaps) {
m_snaps->removePoint(pos.frames(pCore->getCurrentFps()));
}
}
QVariant MarkerListModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= static_cast<int>(m_markerList.size()) || !index.isValid()) {
......
......@@ -25,6 +25,7 @@
#include "gentime.h"
#include "definitions.h"
#include "undohelper.hpp"
#include "timeline2/model/snapmodel.hpp"
#include <QAbstractListModel>
#include <QReadWriteLock>
......@@ -34,6 +35,7 @@
class DocUndoStack;
/* @brief This class is the model for a list of markers.
A marker is defined by a time, a type (the color used to represent it) and a comment string.
We store them in a sorted fashion using a std::map
......@@ -51,7 +53,7 @@ public:
explicit MarkerListModel(const QString &clipId, std::weak_ptr<DocUndoStack> undo_stack, QObject *parent = nullptr);
/* @brief Construct a guide list (bound to the timeline) */
MarkerListModel(std::weak_ptr<DocUndoStack> undo_stack, QObject *parent = nullptr);
MarkerListModel(std::weak_ptr<DocUndoStack> undo_stack, std::unique_ptr<SnapModel> &snapModel, QObject *parent = nullptr);
enum { CommentRole = Qt::UserRole + 1, PosRole, FrameRole, ColorRole };
......@@ -76,6 +78,10 @@ public:
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
/** Adds a snap point at marker position */
void addSnapPoint(GenTime pos);
void removeSnapPoint(GenTime pos);
protected:
/** @brief Helper function that generate a lambda to change comment / type of given marker */
Fun changeComment_lambda(GenTime pos, const QString &comment, int type);
......@@ -98,6 +104,7 @@ private:
mutable QReadWriteLock m_lock; // This is a lock that ensures safety in case of concurrent access
std::map<GenTime, std::pair<QString, int>> m_markerList;
std::unique_ptr<SnapModel> m_snaps;
public:
// this is to enable for range loops
......
......@@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mltcontroller/clipcontroller.h"
#include "mltcontroller/clippropertiescontroller.h"
#include "model/markerlistmodel.hpp"
#include "timeline2/model/snapmodel.hpp"
#include "profiles/profilemodel.hpp"
#include "project/projectcommands.h"
#include "project/projectmanager.h"
......
......@@ -20,7 +20,6 @@
#include "wizard.h"
#include "kdenlivesettings.h"
#include "profilesdialog.h"
#include "renderer.h"
#include "utils/KoIconUtils.h"
#include "utils/thememanager.h"
#ifdef USE_V4L
......
......@@ -28,6 +28,7 @@
#include "documentvalidator.h"
#include "docundostack.hpp"
#include "effectslist/initeffects.h"
#include "timeline2/model/snapmodel.hpp"
#include "kdenlivesettings.h"
#include "mainwindow.h"
#include "renderer.h"
......@@ -80,9 +81,10 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QString &projectFolder, QUndoGro
, m_height(0)
, m_modified(false)
, m_projectFolder(projectFolder)
, m_snaps(new SnapModel())
{
m_commandStack = std::make_shared<DocUndoStack>(undoGroup);
m_guideModel.reset(new MarkerListModel(m_commandStack, this));
m_guideModel.reset(new MarkerListModel(m_commandStack, m_snaps, this));
// init m_profile struct
m_profile.frame_rate_num = 0;
......@@ -1870,3 +1872,8 @@ void KdenliveDoc::addGuides(QList<CommentedTime> &markers)
}
}
}
std::unique_ptr<SnapModel> &KdenliveDoc::snapModel()
{
return m_snaps;
}
......@@ -50,6 +50,7 @@ class ProjectClip;
class ClipController;
class MarkerListModel;
class Render;
class SnapModel;
class QTextEdit;
class QUndoGroup;
......@@ -172,6 +173,9 @@ public:
/** @brief Edit timeline guide */
void addGuides(QList<CommentedTime> &markers);
/** @brief Returns a pointer to snap model */
std::unique_ptr<SnapModel> &snapModel();
// TODO REFAC: delete */
Render *renderer();
......@@ -199,6 +203,7 @@ private:
QList<int> m_undoChunks;
QMap<QString, QString> m_documentProperties;
QMap<QString, QString> m_documentMetadata;
std::unique_ptr<SnapModel> m_snaps;
std::shared_ptr<MarkerListModel> m_guideModel;
QString searchFileRecursively(const QDir &dir, const QString &matchSize, const QString &matchHash) const;
......
/***************************************************************************
* Copyright (C) 2010 by Till Theato (root@ttill.de) *
* *
* 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 "cornerswidget.h"
#include "kdenlivesettings.h"
#include "monitor/monitor.h"
#include "onmonitoritems/onmonitorcornersitem.h"
#include "renderer.h"
#include "utils/KoIconUtils.h"
#include "klocalizedstring.h"
#include <QGraphicsView>
inline int lerp(const int a, const int b, double t)
{
return a + (b - a) * t;
}
CornersWidget::CornersWidget(Monitor *monitor, const QDomElement &e, int minFrame, int maxFrame, int pos, const Timecode &tc, int activeKeyframe,
QWidget *parent)
: KeyframeEdit(e, minFrame, maxFrame, tc, activeKeyframe, parent)
, m_monitor(monitor)
, m_pos(pos)
{
m_monitor->slotShowEffectScene(MonitorSceneCorners);
connect(m_monitor, &Monitor::effectPointsChanged, this, &CornersWidget::slotUpdateGeometry);
connect(this, &KeyframeEdit::valueChanged, this, &CornersWidget::slotUpdateItem, Qt::UniqueConnection);
connect(m_monitor, &Monitor::addKeyframe, this, &CornersWidget::slotPrepareKeyframe);
}
CornersWidget::~CornersWidget()
{
}
void CornersWidget::setFrameSize(const QPoint &size, double stretch)
{
QSize profileSize = m_monitor->profileSize();
if (size.y() == 0 || (size.x() == profileSize.width() && size.y() == profileSize.height())) {
// no need for dar bars
m_monitor->setQmlProperty(QStringLiteral("sourcedar"), 0);
} else {
// show bars indicating source clip aspect ratio
m_monitor->setQmlProperty(QStringLiteral("sourcedar"), (double)size.x() * stretch / size.y());
}
}
void CornersWidget::addParameter(const QDomElement &e, int activeKeyframe)
{
KeyframeEdit::addParameter(e, activeKeyframe);
slotUpdateItem();
}
void CornersWidget::slotUpdateItem()
{
if (keyframe_list->columnCount() < 8) {
return;
}
QTableWidgetItem *keyframe, *keyframeOld;
keyframe = keyframeOld = keyframe_list->item(0, 0);
for (int row = 0; row < keyframe_list->rowCount(); ++row) {
keyframeOld = keyframe;
keyframe = keyframe_list->item(row, 0);
int pos = getPos(row);
if (pos >= m_pos) {
if (pos == m_pos) {
keyframe_list->setCurrentCell(row, 0);
}
break;
}
}
QVariantList points, pointsPrev, pointsNext;
pointsPrev = getPoints(keyframeOld);
pointsNext = getPoints(keyframe);
if (pointsPrev.count() != 4 || pointsNext.count() != 4) {
return;
}
qreal position = (m_pos - getPos(keyframeOld->row())) / (qreal)(getPos(keyframe->row()) - getPos(keyframeOld->row()) + 1);
if (keyframeOld == keyframe) {
points = pointsNext;
} else {
points.reserve(4);
for (int i = 0; i < 4; ++i) {
points.append(QVariant(QLineF(pointsPrev.at(i).toPointF(), pointsNext.at(i).toPointF()).pointAt(position).toPoint()));
}
}
// m_item->setPolygon(QPolygonF() << points.at(0) << points.at(1) << points.at(2) << points.at(3));
// m_monitor->setUpEffectGeometry(QPolygonF() << points.at(0) << points.at(1) << points.at(2) << points.at(3));
m_monitor->setUpEffectGeometry(QRect(), points);
bool enable = getPos(keyframe->row()) - m_min == m_pos || keyframe_list->rowCount() == 1;
m_monitor->setEffectKeyframe(enable);
}
void CornersWidget::slotUpdateGeometry(const QVariantList &points)
{
if (keyframe_list->columnCount() < 8) {
return;
}
QTableWidgetItem *item = keyframe_list->currentItem();
blockSignals(true);
for (int col = 0; col < 4; ++col) {
QPoint value = points.at(col).toPoint();
int valX = 2000.0 * value.x() / m_monitor->render->frameRenderWidth() + 2000;
int valY = 2000.0 * value.y() / m_monitor->render->renderHeight() + 2000;
QTableWidgetItem *nitem = keyframe_list->item(item->row(), col * 2);
if (nitem->text().toInt() != valX) {
nitem->setText(QString::number(valX));
}
nitem = keyframe_list->item(item->row(), col * 2 + 1);
if (nitem->text().toInt() != valY) {
nitem->setText(QString::number(valY));
}
}
slotAdjustKeyframeInfo(false);
blockSignals(false);
disconnect(this, &KeyframeEdit::valueChanged, this, &CornersWidget::slotUpdateItem);
generateAllParams();
connect(this, &KeyframeEdit::valueChanged, this, &CornersWidget::slotUpdateItem, Qt::UniqueConnection);
}
QVariantList CornersWidget::getPoints(QTableWidgetItem *keyframe)
{
QVariantList points;
if (!keyframe) {
return points;
}
for (int col = 0; col < 4; ++col) {
if (!keyframe_list->item(keyframe->row(), col)) {
return QVariantList();
}
double xVal = (keyframe_list->item(keyframe->row(), col * 2)->text().toInt() - 2000) / 2000.;
double yVal = (keyframe_list->item(keyframe->row(), col * 2 + 1)->text().toInt() - 2000) / 2000.;
points << QPoint(xVal * m_monitor->render->frameRenderWidth(), yVal * m_monitor->render->renderHeight());
}
return points;
}
void CornersWidget::slotShowLines(bool show)
{
KdenliveSettings::setOnmonitoreffects_cornersshowlines(show);
// m_item->update();
}
void CornersWidget::slotShowControls(bool show)
{
KdenliveSettings::setOnmonitoreffects_cornersshowcontrols(show);
// m_item->update();
}
void CornersWidget::slotSyncPosition(int relTimelinePos)
{
if (keyframe_list->rowCount() != 0) {
m_pos = qBound(0, relTimelinePos, m_max);
slotUpdateItem();
}
}
void CornersWidget::slotInsertKeyframe()
{
keyframe_list->blockSignals(true);
int row;
QTableWidgetItem *keyframe, *keyframeOld;
keyframe = keyframeOld = keyframe_list->item(0, 0);
for (row = 0; row < keyframe_list->rowCount(); ++row) {
keyframeOld = keyframe;
keyframe = keyframe_list->item(row, 0);
if (getPos(row) >= m_pos) {
break;
}
}
int pos2;
if (row == keyframe_list->rowCount()) {
pos2 = m_max;
} else {
pos2 = getPos(row);
if (pos2 == m_pos) {
return;
}
}
int pos1 = 0;
if (row > 0) {
pos1 = getPos(row - 1);
}
int col = keyframe_list->currentColumn();
double pos = (m_pos - pos1) / (double)(pos2 - pos1 + 1);
keyframe_list->insertRow(row);
keyframe_list->setVerticalHeaderItem(row, new QTableWidgetItem(getPosString(m_pos)));
QVariantList points = m_monitor->effectPolygon();
double val;
for (int i = 0; i < keyframe_list->columnCount(); ++i) {
if (i < 8) {
if (i % 2 == 0) {
val = points.at(i / 2).toPoint().x() / (double)m_monitor->render->frameRenderWidth();
} else {
val = points.at(i / 2).toPoint().y() / (double)m_monitor->render->renderHeight();
}
val *= 2000;
val += 2000;
keyframe_list->setItem(row, i, new QTableWidgetItem(QString::number((int)val)));
} else {
keyframe_list->setItem(row, i, new QTableWidgetItem(QString::number(lerp(keyframe_list->item(keyframeOld->row(), i)->text().toInt(),
keyframe_list->item(keyframe->row(), i)->text().toInt(), pos))));
}
}
keyframe_list->resizeRowsToContents();
slotAdjustKeyframeInfo();
keyframe_list->blockSignals(false);
generateAllParams();
button_delete->setEnabled(true);
keyframe_list->setCurrentCell(row, col);
keyframe_list->selectRow(row);
}
void CornersWidget::slotPrepareKeyframe()
{
slotAddKeyframe(m_pos);
}
/***************************************************************************
* Copyright (C) 2010 by Till Theato (root@ttill.de) *
* *
* 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 CORNERSWIDGET_H
#define CORNERSWIDGET_H
#include "keyframeedit.h"
class QDomElement;
class Monitor;
class CornersWidget : public KeyframeEdit
{
Q_OBJECT
public:
/** @brief Sets up the UI and connects it.
* @param monitor Project monitor
* @param clipPos Position of the clip in timeline
* @param isEffect true if used in an effect, false if used in a transition
* @param factor Factor by which the parameters differ from the range 0-1
* @param parent (optional) Parent widget */
explicit CornersWidget(Monitor *monitor, const QDomElement &e, int minFrame, int maxFrame, int pos, const Timecode &tc, int activeKeyframe,
QWidget *parent = nullptr);
virtual ~CornersWidget();
void addParameter(const QDomElement &e, int activeKeyframe = -1) override;
public slots:
/** @brief Updates the on-monitor item. */
void slotSyncPosition(int relTimelinePos);
void slotPrepareKeyframe();
/** @brief Get info about frame size, source clip dar. */
void setFrameSize(const QPoint &size, double stretch);
private:
Monitor *m_monitor;
int m_pos;
/** @brief Returns the corner positions set in the row of @param keyframe. */
QVariantList getPoints(QTableWidgetItem *keyframe);
private slots:
/** @brief Updates the on-monitor item according to the current timeline position. */
void slotUpdateItem();
/** @brief Updates the keyframe editor according to the on-monitor item. */
void slotUpdateGeometry(const QVariantList &points);
/** @brief Inserts a keyframe at the current (playback) position (m_pos). */
void slotInsertKeyframe();
/** @brief Shows/Hides the lines connecting the corners in the on-monitor item according to @param show. */
void slotShowLines(bool show = true);
/** @brief Shows/Hides additional controls on the monitor according to @param show. */
void slotShowControls(bool show = true);
};
#endif
......@@ -881,7 +881,7 @@ void ProjectManager::updateTimeline(Mlt::Tractor tractor)
pCore->bin()->slotProducerReady(info, pCore->binController()->getController(id).get());
}
pCore->binController()->setBinPlaylist(&tractor);
m_mainTimelineModel = TimelineItemModel::construct(&pCore->getCurrentProfile()->profile(), m_project->commandStack());
m_mainTimelineModel = TimelineItemModel::construct(&pCore->getCurrentProfile()->profile(), m_project->snapModel(), m_project->commandStack());
constructTimelineFromMelt(m_mainTimelineModel, tractor);
// TODO this is for testing purposes, remove.
......
......@@ -35,14 +35,14 @@
#include <mlt++/MltTransition.h>
#include <utility>
TimelineItemModel::TimelineItemModel(Mlt::Profile *profile, std::weak_ptr<DocUndoStack> undo_stack)
: TimelineModel(profile, undo_stack)
TimelineItemModel::TimelineItemModel(Mlt::Profile *profile, std::unique_ptr<SnapModel> &snapModel, std::weak_ptr<DocUndoStack> undo_stack)
: TimelineModel(profile, snapModel, undo_stack)
{
}
std::shared_ptr<TimelineItemModel> TimelineItemModel::construct(Mlt::Profile *profile, std::weak_ptr<DocUndoStack> undo_stack)
std::shared_ptr<TimelineItemModel> TimelineItemModel::construct(Mlt::Profile *profile, std::unique_ptr<SnapModel> &snapModel, std::weak_ptr<DocUndoStack> undo_stack)
{
std::shared_ptr<TimelineItemModel> ptr(new TimelineItemModel(profile, std::move(undo_stack)));
std::shared_ptr<TimelineItemModel> ptr(new TimelineItemModel(profile, snapModel, std::move(undo_stack)));
ptr->m_groups = std::unique_ptr<GroupsModel>(new GroupsModel(ptr));
return ptr;
}
......
......@@ -51,14 +51,14 @@ public:
/* @brief construct a timeline object and returns a pointer to the created object
@param undo_stack is a weak pointer to the undo stack of the project
*/
static std::shared_ptr<TimelineItemModel> construct(Mlt::Profile *profile, std::weak_ptr<DocUndoStack> undo_stack);
static std::shared_ptr<TimelineItemModel> construct(Mlt::Profile *profile, std::unique_ptr<SnapModel> &snapModel, std::weak_ptr<DocUndoStack> undo_stack);
friend bool constructTimelineFromMelt(const std::shared_ptr<TimelineItemModel> &timeline, Mlt::Tractor mlt_timeline);
protected:
/* @brief this constructor should not be called. Call the static construct instead
*/
TimelineItemModel(Mlt::Profile *profile, std::weak_ptr<DocUndoStack> undo_stack);
TimelineItemModel(Mlt::Profile *profile, std::unique_ptr<SnapModel> &snapModel, std::weak_ptr<DocUndoStack> undo_stack);
public:
~TimelineItemModel();
......
......@@ -45,10 +45,10 @@
int TimelineModel::next_id = 0;
TimelineModel::TimelineModel(Mlt::Profile *profile, std::weak_ptr<DocUndoStack> undo_stack)
TimelineModel::TimelineModel(Mlt::Profile *profile, std::unique_ptr<SnapModel> &snapModel, std::weak_ptr<DocUndoStack> undo_stack)
: QAbstractItemModel()
, m_tractor(new Mlt::Tractor(*profile))
, m_snaps(new SnapModel())
, m_snaps(snapModel.get())
, m_undoStack(undo_stack)
, m_profile(profile)
, m_blackClip(new Mlt::Producer(*profile, "color:black"))
......@@ -1397,3 +1397,4 @@ Mlt::Producer *TimelineModel::producer()
auto *prod = new Mlt::Producer(tractor());
return prod;
}
......@@ -86,7 +86,7 @@ class TimelineModel : public QAbstractItemModel, public std::enable_shared_from_
protected:
/* @brief this constructor should not be called. Call the static construct instead
*/
TimelineModel(Mlt::Profile *profile, std::weak_ptr<DocUndoStack> undo_stack);
TimelineModel(Mlt::Profile *profile, std::unique_ptr<SnapModel> &snapModel, std::weak_ptr<DocUndoStack> undo_stack);
public:
friend class TrackModel;
......@@ -477,6 +477,9 @@ protected:
/* @brief Debugging function that checks consistency with Mlt objects */
bool checkConsistency();
/* @brief Returns snap model */
std::unique_ptr<SnapModel> &getSnapModel();
protected:
std::unique_ptr<Mlt::Tractor> m_tractor;
......
......@@ -14,6 +14,7 @@
#include "timeline2/model/compositionmodel.hpp"
#include "timeline2/model/timelineitemmodel.hpp"
#include "timeline2/model/timelinemodel.hpp"
#include "timeline2/model/snapmodel.hpp"
#include "timeline2/model/trackmodel.hpp"
#include "transitions/transitionsrepository.hpp"
......@@ -40,7 +41,8 @@ TEST_CASE("Basic creation/deletion of a composition", "[CompositionModel]")
REQUIRE(mlt_transition->is_valid());
std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
std::shared_ptr<TimelineItemModel> timeline = TimelineItemModel::construct(new Mlt::Profile(), undoStack);
std::unique_ptr<SnapModel> snap(new SnapModel());
std::shared_ptr<TimelineItemModel> timeline = TimelineItemModel::construct(new Mlt::Profile(), snap, undoStack);
REQUIRE(timeline->getCompositionsCount() == 0);
int id1 = CompositionModel::construct(timeline, aCompo);
......@@ -64,7 +66,8 @@ TEST_CASE("Basic creation/deletion of a composition", "[CompositionModel]")
TEST_CASE("Composition manipulation", "[CompositionModel]")
{
std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
std::shared_ptr<TimelineItemModel> timeline = TimelineItemModel::construct(new Mlt::Profile(), undoStack);
std::unique_ptr<SnapModel> snap(new SnapModel());
std::shared_ptr<TimelineItemModel> timeline = TimelineItemModel::construct(new Mlt::Profile(), snap, undoStack);
int tid0 = TrackModel::construct(timeline);
int tid1 = TrackModel::construct(timeline);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment