Commit d9bc135e authored by Nicolas Carion's avatar Nicolas Carion
Browse files

Preliminary implementation of a marker model. Integration is WIP

parent 85369537
set(kdenlive_SRCS
${kdenlive_SRCS}
bin/bin.cpp
bin/projectitemmodel.cpp
bin/abstractprojectitem.cpp
bin/bin.cpp
bin/bincommands.cpp
bin/generators/generators.cpp
bin/model/markerlistmodel.cpp
bin/projectclip.cpp
bin/projectsubclip.cpp
bin/projectfolder.cpp
bin/projectfolderup.cpp
bin/projectitemmodel.cpp
bin/projectsortproxymodel.cpp
bin/bincommands.cpp
bin/generators/generators.cpp
bin/projectsubclip.cpp
PARENT_SCOPE
)
/***************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "markerlistmodel.hpp"
#include "bin/bin.h"
#include "bin/projectclip.h"
#include "core.h"
#include "doc/docundostack.hpp"
#include "doc/kdenlivedoc.h"
#include "macros.hpp"
#include "project/projectmanager.h"
#include <QDebug>
#include <klocalizedstring.h>
MarkerListModel::MarkerListModel(const QString &clipId, std::weak_ptr<DocUndoStack> undo_stack, QObject *parent)
: QAbstractListModel(parent)
, m_undoStack(std::move(undo_stack))
, m_guide(false)
, m_clipId(clipId)
{
}
MarkerListModel::MarkerListModel(std::weak_ptr<DocUndoStack> undo_stack, QObject *parent)
: QAbstractListModel(parent)
, m_undoStack(std::move(undo_stack))
, m_guide(true)
{
}
void MarkerListModel::addMarker(GenTime pos, const QString &comment)
{
QWriteLocker locker(&m_lock);
if (m_markerList.count(pos) > 0) {
// In this case we simply change the comment
QString oldComment = m_markerList[pos];
Fun undo = changeComment_lambda(pos, oldComment);
Fun redo = changeComment_lambda(pos, comment);
if (redo()) {
PUSH_UNDO(undo, redo, i18n("Rename marker"));
}
} else {
// In this case we create one
Fun redo = addMarker_lambda(pos, comment);
Fun undo = deleteMarker_lambda(pos);
if (redo()) {
PUSH_UNDO(undo, redo, i18n("Add marker"));
}
}
}
void MarkerListModel::removeMarker(GenTime pos)
{
QWriteLocker locker(&m_lock);
Q_ASSERT(m_markerList.count(pos) > 0);
QString oldComment = m_markerList[pos];
Fun undo = addMarker_lambda(pos, oldComment);
Fun redo = deleteMarker_lambda(pos);
if (redo()) {
PUSH_UNDO(undo, redo, i18n("Delete marker"));
}
}
Fun MarkerListModel::changeComment_lambda(GenTime pos, const QString &comment)
{
auto guide = m_guide;
auto clipId = m_clipId;
return [guide, clipId, pos, comment]() {
auto model = getModel(guide, clipId);
Q_ASSERT(model->m_markerList.count(pos) > 0);
int row = static_cast<int>(std::distance(model->m_markerList.begin(), model->m_markerList.find(pos)));
emit model->dataChanged(model->index(row), model->index(row));
model->m_markerList[pos] = comment;
return true;
};
}
Fun MarkerListModel::addMarker_lambda(GenTime pos, const QString &comment)
{
auto guide = m_guide;
auto clipId = m_clipId;
return [guide, clipId, pos, comment]() {
auto model = getModel(guide, clipId);
Q_ASSERT(model->m_markerList.count(pos) == 0);
// We determine the row of the newly added marker
auto insertionIt = model->m_markerList.lower_bound(pos);
int insertionRow = static_cast<int>(model->m_markerList.size());
if (insertionIt != model->m_markerList.end()) {
insertionRow = static_cast<int>(std::distance(model->m_markerList.begin(), insertionIt));
}
model->beginInsertRows(QModelIndex(), insertionRow, insertionRow);
model->m_markerList[pos] = comment;
model->endInsertRows();
return true;
};
}
Fun MarkerListModel::deleteMarker_lambda(GenTime pos)
{
auto guide = m_guide;
auto clipId = m_clipId;
return [guide, clipId, pos]() {
auto model = getModel(guide, clipId);
Q_ASSERT(model->m_markerList.count(pos) > 0);
int row = static_cast<int>(std::distance(model->m_markerList.begin(), model->m_markerList.find(pos)));
model->beginRemoveRows(QModelIndex(), row, row);
model->m_markerList.erase(pos);
model->endRemoveRows();
return true;
};
}
std::shared_ptr<MarkerListModel> MarkerListModel::getModel(bool guide, const QString &clipId)
{
if (guide) {
return pCore->projectManager()->current()->getGuideModel();
}
return pCore->bin()->getBinClip(clipId)->getMarkerModel();
}
QVariant MarkerListModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= static_cast<int>(m_markerList.size()) || !index.isValid()) {
return QVariant();
}
auto it = m_markerList.begin();
std::advance(it, index.row());
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
case CommentRole:
return it->second;
case PosRole:
return it->first.seconds();
}
return QVariant();
}
int MarkerListModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) return 0;
return static_cast<int>(m_markerList.size());
}
/***************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifndef MARKERLISTMODEL_H
#define MARKERLISTMODEL_H
#include "gentime.h"
#include "undohelper.hpp"
#include <QAbstractListModel>
#include <QReadWriteLock>
#include <map>
#include <memory>
class DocUndoStack;
/* @brief This class is the model for a list of markers.
A marker is defined by a time and a comment string.
We store them in a sorted fashion using a std::map
A marker is essentially bound to a clip. We can also define guides, that are timeline-wise markers. For that, use the constructors without clipId
*/
class MarkerListModel : public QAbstractListModel
{
Q_OBJECT
public:
/* @brief Construct a marker list bound to the bin clip with given id */
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);
enum { CommentRole = Qt::UserRole + 1, PosRole };
/* @brief Adds a marker at the given position. If there is already one, the comment will be overriden */
void addMarker(GenTime pos, const QString &comment);
/* @brief Removes the marker at the given position. */
void removeMarker(GenTime pos);
// Mandatory overloads
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
protected:
/** @brief Helper function that generate a lambda to change comment of given marker */
Fun changeComment_lambda(GenTime pos, const QString &comment);
/** @brief Helper function that generate a lambda to add given marker */
Fun addMarker_lambda(GenTime pos, const QString &comment);
/** @brief Helper function that generate a lambda to remove given marker */
Fun deleteMarker_lambda(GenTime pos);
/** @brief Helper function that retrieves a pointer to the markermodel, given whether it's a guide model and its clipId*/
static std::shared_ptr<MarkerListModel> getModel(bool guide, const QString &clipId);
private:
std::weak_ptr<DocUndoStack> m_undoStack;
bool m_guide; // whether this model represents timeline-wise guides
QString m_clipId; // the Id of the clip this model corresponds to, if any.
mutable QReadWriteLock m_lock; // This is a lock that ensures safety in case of concurrent access
std::map<GenTime, QString> m_markerList;
public:
// this is to enable for range loops
auto begin() -> decltype(m_markerList.begin()) { return m_markerList.begin(); }
auto end() -> decltype(m_markerList.end()) { return m_markerList.end(); }
};
#endif
......@@ -23,14 +23,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "projectclip.h"
#include "bin.h"
#include "core.h"
#include "doc/docundostack.hpp"
#include "doc/kdenlivedoc.h"
#include "doc/kthumb.h"
#include "kdenlivesettings.h"
#include "lib/audio/audioStreamInfo.h"
#include "mltcontroller/bincontroller.h"
#include "mltcontroller/clipcontroller.h"
#include "mltcontroller/clippropertiescontroller.h"
#include "model/markerlistmodel.hpp"
#include "profiles/profilemodel.hpp"
#include "project/projectcommands.h"
#include "project/projectmanager.h"
#include "projectfolder.h"
#include "projectitemmodel.h"
#include "projectsubclip.h"
......@@ -54,6 +58,7 @@ ProjectClip::ProjectClip(const QString &id, const QIcon &thumb, ProjectItemModel
, m_abortAudioThumb(false)
, m_thumbsProducer(nullptr)
{
m_markerModel = std::make_shared<MarkerListModel>(id, pCore->projectManager()->current()->commandStack());
m_clipStatus = StatusReady;
m_name = clipName();
m_duration = getStringDuration();
......@@ -1232,3 +1237,8 @@ void ProjectClip::deregisterTimelineClip(int clipId)
Q_ASSERT(m_registeredClips.count(clipId) > 0);
m_registeredClips.erase(clipId);
}
std::shared_ptr<MarkerListModel> ProjectClip::getMarkerModel() const
{
return m_markerModel;
}
......@@ -34,11 +34,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QUrl>
#include <memory>
class ProjectFolder;
class AudioStreamInfo;
class QDomElement;
class ClipPropertiesController;
class MarkerListModel;
class ProjectFolder;
class ProjectSubClip;
class QDomElement;
class QUndoCommand;
namespace Mlt {
......@@ -216,6 +217,9 @@ public slots:
* @param statusMessage The job info message */
void setJobStatus(int jobType, int status, int progress = 0, const QString &statusMessage = QString());
/* @brief Returns the marker model associated with this clip */
std::shared_ptr<MarkerListModel> getMarkerModel() const;
private:
bool m_abortAudioThumb;
/** @brief Generate and store file hash if not available. */
......@@ -237,6 +241,8 @@ private:
std::map<int, std::weak_ptr<TimelineModel>> m_registeredClips;
std::shared_ptr<MarkerListModel> m_markerModel;
private slots:
void updateFfmpegProgress();
......
......@@ -20,6 +20,7 @@
#include "kdenlivedoc.h"
#include "bin/bin.h"
#include "bin/bincommands.h"
#include "bin/model/markerlistmodel.hpp"
#include "bin/projectclip.h"
#include "core.h"
#include "dialogs/profilesdialog.h"
......@@ -81,8 +82,10 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QString &projectFolder, QUndoGro
, m_modified(false)
, m_projectFolder(projectFolder)
{
// init m_profile struct
m_commandStack = std::make_shared<DocUndoStack>(undoGroup);
m_guideModel.reset(new MarkerListModel(m_commandStack, this));
// init m_profile struct
m_profile.frame_rate_num = 0;
m_profile.frame_rate_den = 0;
m_profile.width = 0;
......@@ -1866,3 +1869,8 @@ int KdenliveDoc::compositingMode()
// Cairoblend or qtblend available
return 1;
}
std::shared_ptr<MarkerListModel> KdenliveDoc::getGuideModel() const
{
return m_guideModel;
}
......@@ -49,6 +49,7 @@ class MainWindow;
class TrackInfo;
class ProjectClip;
class ClipController;
class MarkerListModel;
class QTextEdit;
class QUndoGroup;
......@@ -168,6 +169,9 @@ public:
/** @brief Move project data files to new url */
void moveProjectData(const QString &src, const QString &dest);
/** @brief Returns a pointer to the guide model */
std::shared_ptr<MarkerListModel> getGuideModel() const;
private:
QUrl m_url;
QDomDocument m_document;
......@@ -193,6 +197,7 @@ private:
QList<int> m_undoChunks;
QMap<QString, QString> m_documentProperties;
QMap<QString, QString> m_documentMetadata;
std::shared_ptr<MarkerListModel> m_guideModel;
QString searchFileRecursively(const QDir &dir, const QString &matchSize, const QString &matchHash) const;
......
......@@ -3762,8 +3762,7 @@ void MainWindow::slotArchiveProject()
{
QList<std::shared_ptr<ClipController>> list = pCore->binController()->getControllerList();
KdenliveDoc *doc = pCore->projectManager()->current();
pCore->binController()->saveDocumentProperties(pCore->projectManager()->currentTimeline()->documentProperties(), doc->metadata(),
pCore->projectManager()->currentTimeline()->projectView()->guidesData());
pCore->binController()->saveDocumentProperties(pCore->projectManager()->currentTimeline()->documentProperties(), doc->metadata(), doc->getGuideModel());
QDomDocument xmlDoc = doc->xmlSceneList(m_projectMonitor->sceneList(doc->url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile()));
QScopedPointer<ArchiveWidget> d(
new ArchiveWidget(doc->url().fileName(), xmlDoc, list, pCore->projectManager()->currentTimeline()->projectView()->extractTransitionsLumas(), this));
......
......@@ -18,6 +18,7 @@
***************************************************************************/
#include "bincontroller.h"
#include "bin/model/markerlistmodel.hpp"
#include "clipcontroller.h"
#include "kdenlivesettings.h"
#include "timeline/clip.h"
......@@ -549,7 +550,8 @@ void BinController::checkAudioThumbs()
}
}
void BinController::saveDocumentProperties(const QMap<QString, QString> &props, const QMap<QString, QString> &metadata, const QMap<double, QString> &guidesData)
void BinController::saveDocumentProperties(const QMap<QString, QString> &props, const QMap<QString, QString> &metadata,
std::shared_ptr<MarkerListModel> guideModel)
{
// Clear previous properites
Mlt::Properties playlistProps(m_binPlaylist->get_properties());
......@@ -589,12 +591,10 @@ void BinController::saveDocumentProperties(const QMap<QString, QString> &props,
}
// Append guides
QMapIterator<double, QString> g(guidesData);
QLocale locale;
while (g.hasNext()) {
g.next();
QString propertyName = "kdenlive:guide." + locale.toString(g.key());
playlistProps.set(propertyName.toUtf8().constData(), g.value().toUtf8().constData());
for (const auto &guide : *guideModel) {
QString propertyName = "kdenlive:guide." + locale.toString(guide.first.seconds());
playlistProps.set(propertyName.toUtf8().constData(), guide.second.toUtf8().constData());
}
}
......
......@@ -29,6 +29,8 @@
#include <QStringList>
#include <memory>
class MarkerListModel;
namespace Mlt {
class Playlist;
class Profile;
......@@ -158,7 +160,7 @@ public:
void checkAudioThumbs();
/** @brief Save document properties in MLT's bin playlist */
void saveDocumentProperties(const QMap<QString, QString> &props, const QMap<QString, QString> &metadata, const QMap<double, QString> &guidesData);
void saveDocumentProperties(const QMap<QString, QString> &props, const QMap<QString, QString> &metadata, std::shared_ptr<MarkerListModel> guideModel);
/** @brief Save a property to main bin */
void saveProperty(const QString &name, const QString &value);
......
......@@ -746,8 +746,7 @@ QString ProjectManager::documentNotes() const
void ProjectManager::prepareSave()
{
// TODO REFAC: save target tracks, preview chunks and guides
pCore->binController()->saveDocumentProperties(m_project->documentProperties(), m_project->metadata(),
QMap<double, QString>() /*m_trackView->projectView()->guidesData()*/);
pCore->binController()->saveDocumentProperties(m_project->documentProperties(), m_project->metadata(), m_project->getGuideModel());
pCore->binController()->saveProperty(QStringLiteral("kdenlive:documentnotes"), documentNotes());
pCore->binController()->saveProperty(QStringLiteral("kdenlive:clipgroups"), m_project->groupsXml());
}
......
......@@ -501,7 +501,7 @@ protected:
Mlt::Profile *m_profile;
// The black track producer. It's length / out should always be adjusted to the projects's length
// The black track producer. Its length / out should always be adjusted to the projects's length
std::unique_ptr<Mlt::Producer> m_blackClip;
mutable QReadWriteLock m_lock; // This is a lock that ensures safety in case of concurrent access
......
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