diff --git a/src/bin/clipcreator.cpp b/src/bin/clipcreator.cpp
index 7a3289174..4679b2cf3 100644
--- a/src/bin/clipcreator.cpp
+++ b/src/bin/clipcreator.cpp
@@ -1,326 +1,327 @@
/***************************************************************************
* 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 "clipcreator.hpp"
#include "bin/bin.h"
#include "core.h"
#include "doc/kdenlivedoc.h"
#include "kdenlivesettings.h"
#include "klocalizedstring.h"
#include "macros.hpp"
#include "mainwindow.h"
#include "projectitemmodel.h"
#include "titler/titledocument.h"
#include "utils/devices.hpp"
#include "xml/xml.hpp"
#include
#include
#include
#include
#include
#include
#include
namespace {
QDomElement createProducer(QDomDocument &xml, ClipType::ProducerType type, const QString &resource, const QString &name, int duration, const QString &service)
{
QDomElement prod = xml.createElement(QStringLiteral("producer"));
xml.appendChild(prod);
prod.setAttribute(QStringLiteral("type"), (int)type);
prod.setAttribute(QStringLiteral("in"), QStringLiteral("0"));
prod.setAttribute(QStringLiteral("length"), duration);
std::unordered_map properties;
properties[QStringLiteral("resource")] = resource;
if (!name.isEmpty()) {
properties[QStringLiteral("kdenlive:clipname")] = name;
}
if (!service.isEmpty()) {
properties[QStringLiteral("mlt_service")] = service;
}
Xml::addXmlProperties(prod, properties);
return prod;
}
} // namespace
QString ClipCreator::createTitleClip(const std::unordered_map &properties, int duration, const QString &name, const QString &parentFolder,
const std::shared_ptr &model)
{
QDomDocument xml;
auto prod = createProducer(xml, ClipType::Text, QString(), name, duration, QStringLiteral("kdenlivetitle"));
Xml::addXmlProperties(prod, properties);
QString id;
bool res = model->requestAddBinClip(id, xml.documentElement(), parentFolder, i18n("Create title clip"));
return res ? id : QStringLiteral("-1");
}
QString ClipCreator::createColorClip(const QString &color, int duration, const QString &name, const QString &parentFolder,
const std::shared_ptr &model)
{
QDomDocument xml;
auto prod = createProducer(xml, ClipType::Color, color, name, duration, QStringLiteral("color"));
QString id;
bool res = model->requestAddBinClip(id, xml.documentElement(), parentFolder, i18n("Create color clip"));
return res ? id : QStringLiteral("-1");
}
-QString ClipCreator::createClipFromFile(const QString &path, const QString &parentFolder, const std::shared_ptr &model, Fun &undo, Fun &redo)
+QString ClipCreator::createClipFromFile(const QString &path, const QString &parentFolder, const std::shared_ptr &model, Fun &undo, Fun &redo,
+ const std::function &readyCallBack)
{
QDomDocument xml;
QMimeDatabase db;
QMimeType type = db.mimeTypeForUrl(QUrl::fromLocalFile(path));
qDebug() << "/////////// createClipFromFile" << path << parentFolder << path << type.name();
QDomElement prod;
if (type.name().startsWith(QLatin1String("image/"))) {
int duration = pCore->currentDoc()->getFramePos(KdenliveSettings::image_duration());
prod = createProducer(xml, ClipType::Image, path, QString(), duration, QString());
} else if (type.inherits(QStringLiteral("application/x-kdenlivetitle"))) {
// opening a title file
QDomDocument txtdoc(QStringLiteral("titledocument"));
QFile txtfile(path);
if (txtfile.open(QIODevice::ReadOnly) && txtdoc.setContent(&txtfile)) {
txtfile.close();
// extract embedded images
QDomNodeList items = txtdoc.elementsByTagName(QStringLiteral("content"));
for (int j = 0; j < items.count(); ++j) {
QDomElement content = items.item(j).toElement();
if (content.hasAttribute(QStringLiteral("base64"))) {
QString titlesFolder = pCore->currentDoc()->projectDataFolder() + QStringLiteral("/titles/");
QString imgPath = TitleDocument::extractBase64Image(titlesFolder, content.attribute(QStringLiteral("base64")));
if (!imgPath.isEmpty()) {
content.setAttribute(QStringLiteral("url"), imgPath);
content.removeAttribute(QStringLiteral("base64"));
}
}
}
prod.setAttribute(QStringLiteral("in"), 0);
int duration = 0;
if (txtdoc.documentElement().hasAttribute(QStringLiteral("duration"))) {
duration = txtdoc.documentElement().attribute(QStringLiteral("duration")).toInt();
} else if (txtdoc.documentElement().hasAttribute(QStringLiteral("out"))) {
duration = txtdoc.documentElement().attribute(QStringLiteral("out")).toInt();
}
if (duration <= 0) {
duration = pCore->currentDoc()->getFramePos(KdenliveSettings::title_duration()) - 1;
}
prod = createProducer(xml, ClipType::Text, path, QString(), duration, QString());
txtdoc.documentElement().setAttribute(QStringLiteral("kdenlive:duration"), duration);
QString titleData = txtdoc.toString();
prod.setAttribute(QStringLiteral("xmldata"), titleData);
} else {
txtfile.close();
return QStringLiteral("-1");
}
} else {
// it is a "normal" file, just use a producer
prod = xml.createElement(QStringLiteral("producer"));
xml.appendChild(prod);
QMap properties;
properties.insert(QStringLiteral("resource"), path);
Xml::addXmlProperties(prod, properties);
}
if (pCore->bin()->isEmpty() && (KdenliveSettings::default_profile().isEmpty() || KdenliveSettings::checkfirstprojectclip())) {
prod.setAttribute(QStringLiteral("_checkProfile"), 1);
}
qDebug() << "/////////// final xml" << xml.toString();
QString id;
- bool res = model->requestAddBinClip(id, xml.documentElement(), parentFolder, undo, redo);
+ bool res = model->requestAddBinClip(id, xml.documentElement(), parentFolder, undo, redo, readyCallBack);
return res ? id : QStringLiteral("-1");
}
bool ClipCreator::createClipFromFile(const QString &path, const QString &parentFolder, std::shared_ptr model)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
auto id = ClipCreator::createClipFromFile(path, parentFolder, std::move(model), undo, redo);
bool ok = (id != QStringLiteral("-1"));
if (ok) {
pCore->pushUndo(undo, redo, i18n("Add clip"));
}
return ok;
}
QString ClipCreator::createSlideshowClip(const QString &path, int duration, const QString &name, const QString &parentFolder,
const std::unordered_map &properties, const std::shared_ptr &model)
{
QDomDocument xml;
auto prod = createProducer(xml, ClipType::SlideShow, path, name, duration, QString());
Xml::addXmlProperties(prod, properties);
QString id;
bool res = model->requestAddBinClip(id, xml.documentElement(), parentFolder, i18n("Create slideshow clip"));
return res ? id : QStringLiteral("-1");
}
QString ClipCreator::createTitleTemplate(const QString &path, const QString &text, const QString &name, const QString &parentFolder,
const std::shared_ptr &model)
{
QDomDocument xml;
// We try to retrieve duration for template
int duration = 0;
QDomDocument titledoc;
QFile txtfile(path);
if (txtfile.open(QIODevice::ReadOnly) && titledoc.setContent(&txtfile)) {
if (titledoc.documentElement().hasAttribute(QStringLiteral("duration"))) {
duration = titledoc.documentElement().attribute(QStringLiteral("duration")).toInt();
} else {
// keep some time for backwards compatibility - 26/12/12
duration = titledoc.documentElement().attribute(QStringLiteral("out")).toInt();
}
}
txtfile.close();
// Duration not found, we fall-back to defaults
if (duration == 0) {
duration = pCore->currentDoc()->getFramePos(KdenliveSettings::title_duration());
}
auto prod = createProducer(xml, ClipType::TextTemplate, path, name, duration, QString());
if (!text.isEmpty()) {
prod.setAttribute(QStringLiteral("templatetext"), text);
}
QString id;
bool res = model->requestAddBinClip(id, xml.documentElement(), parentFolder, i18n("Create title template"));
return res ? id : QStringLiteral("-1");
}
bool ClipCreator::createClipsFromList(const QList &list, bool checkRemovable, const QString &parentFolder, const std::shared_ptr &model,
Fun &undo, Fun &redo, bool topLevel)
{
QScopedPointer progressDialog;
if (topLevel) {
progressDialog.reset(new QProgressDialog(pCore->window()));
progressDialog->setWindowTitle(i18n("Loading clips"));
progressDialog->setCancelButton(nullptr);
progressDialog->setLabelText(i18n("Importing bin clips..."));
progressDialog->setMaximum(0);
progressDialog->show();
progressDialog->repaint();
qApp->processEvents();
}
qDebug() << "/////////// creatclipsfromlist" << list << checkRemovable << parentFolder;
bool created = false;
QMimeDatabase db;
for (const QUrl &file : list) {
if (!QFile::exists(file.toLocalFile())) {
continue;
}
QMimeType mType = db.mimeTypeForUrl(file);
if (mType.inherits(QLatin1String("inode/directory"))) {
// user dropped a folder, import its files
QDir dir(file.path());
QStringList result = dir.entryList(QDir::Files);
QStringList subfolders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QList folderFiles;
for (const QString &path : result) {
QUrl url = QUrl::fromLocalFile(dir.absoluteFilePath(path));
// Check file is of a supported type
mType = db.mimeTypeForUrl(url);
QString mimeAliases = mType.name();
bool isValid = mimeAliases.contains(QLatin1String("video/"));
if (!isValid) {
isValid = mimeAliases.contains(QLatin1String("audio/"));
}
if (!isValid) {
isValid = mimeAliases.contains(QLatin1String("image/"));
}
if (!isValid && (mType.inherits(QLatin1String("video/mlt-playlist")) || mType.inherits(QLatin1String("application/x-kdenlivetitle")))) {
isValid = true;
}
if (isValid) {
folderFiles.append(url);
}
}
QString folderId;
Fun local_undo = []() { return true; };
Fun local_redo = []() { return true; };
if (folderFiles.isEmpty()) {
QList sublist;
for (const QString &sub : subfolders) {
QUrl url = QUrl::fromLocalFile(dir.absoluteFilePath(sub));
if (!list.contains(url)) {
sublist << url;
}
}
if (!sublist.isEmpty()) {
// load subfolders
created = created || createClipsFromList(sublist, checkRemovable, parentFolder, model, undo, redo, false);
}
} else {
bool ok = pCore->projectItemModel()->requestAddFolder(folderId, dir.dirName(), parentFolder, local_undo, local_redo);
if (ok) {
ok = createClipsFromList(folderFiles, checkRemovable, folderId, model, local_undo, local_redo, false);
created = true;
if (!ok) {
local_undo();
} else {
UPDATE_UNDO_REDO_NOLOCK(local_redo, local_undo, undo, redo);
}
// Check subfolders
QList sublist;
for (const QString &sub : subfolders) {
QUrl url = QUrl::fromLocalFile(dir.absoluteFilePath(sub));
if (!list.contains(url)) {
sublist << url;
}
}
if (!sublist.isEmpty()) {
// load subfolders
createClipsFromList(sublist, checkRemovable, folderId, model, undo, redo, false);
}
}
}
continue;
}
if (checkRemovable && isOnRemovableDevice(file) && !isOnRemovableDevice(pCore->currentDoc()->projectDataFolder())) {
int answer = KMessageBox::warningContinueCancel(
QApplication::activeWindow(),
i18n("Clip %1
is on a removable device, will not be available when device is unplugged or mounted at a different position. You "
"may want to copy it first to your hard-drive. Would you like to add it anyways?",
file.path()),
i18n("Removable device"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("confirm_removable_device"));
if (answer == KMessageBox::Cancel) continue;
}
QString id = ClipCreator::createClipFromFile(file.toLocalFile(), parentFolder, model, undo, redo);
created = created || (id != QStringLiteral("-1"));
}
qDebug() << "/////////// creatclipsfromlist return" << created;
return created;
}
bool ClipCreator::createClipsFromList(const QList &list, bool checkRemovable, const QString &parentFolder, std::shared_ptr model)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool ok = ClipCreator::createClipsFromList(list, checkRemovable, parentFolder, std::move(model), undo, redo);
if (ok) {
pCore->pushUndo(undo, redo, i18np("Add clip", "Add clips", list.size()));
}
return ok;
}
diff --git a/src/bin/clipcreator.hpp b/src/bin/clipcreator.hpp
index 753b68378..dec4d2b0c 100644
--- a/src/bin/clipcreator.hpp
+++ b/src/bin/clipcreator.hpp
@@ -1,99 +1,100 @@
/***************************************************************************
* 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 CLIPCREATOR_H
#define CLIPCREATOR_H
#include "definitions.h"
#include "undohelper.hpp"
#include
#include
#include
/** @brief This namespace provides convenience functions to create clips based on various parameters
*/
class ProjectItemModel;
namespace ClipCreator {
/* @brief Create and inserts a color clip
@param color : a string of the form "0xff0000ff" (solid red in RGBA)
@param duration : duration expressed in number of frames
@param name: name of the clip
@param parentFolder: the binId of the containing folder
@param model: a shared pointer to the bin item model
@return the binId of the created clip
*/
QString createColorClip(const QString &color, int duration, const QString &name, const QString &parentFolder, const std::shared_ptr &model);
/* @brief Create a title clip
@param properties : title properties (xmldata, etc)
@param duration : duration of the clip
@param name: name of the clip
@param parentFolder: the binId of the containing folder
@param model: a shared pointer to the bin item model
*/
QString createTitleClip(const std::unordered_map &properties, int duration, const QString &name, const QString &parentFolder,
const std::shared_ptr &model);
/* @brief Create a title template
@param path : path to the template
@param text : text of the template (optional)
@param name: name of the clip
@param parentFolder: the binId of the containing folder
@param model: a shared pointer to the bin item model
@return the binId of the created clip
*/
QString createTitleTemplate(const QString &path, const QString &text, const QString &name, const QString &parentFolder,
const std::shared_ptr &model);
/* @brief Create a slideshow clip
@param path : path to the selected folder
@param duration: this should be nbr of images * duration of one image
@param name: name of the clip
@param parentFolder: the binId of the containing folder
@param properties: description of the slideshow
@param model: a shared pointer to the bin item model
@return the binId of the created clip
*/
QString createSlideshowClip(const QString &path, int duration, const QString &name, const QString &parentFolder,
const std::unordered_map &properties, const std::shared_ptr &model);
/* @brief Reads a file from disk and create the corresponding clip
@param path : path to the file
@param parentFolder: the binId of the containing folder
@param model: a shared pointer to the bin item model
@return the binId of the created clip
*/
-QString createClipFromFile(const QString &path, const QString &parentFolder, const std::shared_ptr &model, Fun &undo, Fun &redo);
+QString createClipFromFile(const QString &path, const QString &parentFolder, const std::shared_ptr &model, Fun &undo, Fun &redo,
+ const std::function &readyCallBack = [](const QString &) {});
bool createClipFromFile(const QString &path, const QString &parentFolder, std::shared_ptr model);
/* @brief Iterates recursively through the given url list and add the files it finds, recreating a folder structure
@param list: the list of items (can be folders)
@param checkRemovable: if true, it will check if files are on removable devices, and warn the user if so
@param parentFolder: the binId of the containing folder
@param model: a shared pointer to the bin item model
*/
bool createClipsFromList(const QList &list, bool checkRemovable, const QString &parentFolder, const std::shared_ptr &model, Fun &undo,
Fun &redo, bool topLevel = true);
bool createClipsFromList(const QList &list, bool checkRemovable, const QString &parentFolder, std::shared_ptr model);
} // namespace ClipCreator
#endif
diff --git a/src/bin/projectitemmodel.cpp b/src/bin/projectitemmodel.cpp
index 41c9b7730..f3aa2d6f2 100644
--- a/src/bin/projectitemmodel.cpp
+++ b/src/bin/projectitemmodel.cpp
@@ -1,966 +1,967 @@
/*
Copyright (C) 2012 Till Theato
Copyright (C) 2014 Jean-Baptiste Mardelle
Copyright (C) 2017 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 "projectitemmodel.h"
#include "abstractprojectitem.h"
#include "binplaylist.hpp"
#include "core.h"
#include "doc/kdenlivedoc.h"
#include "filewatcher.hpp"
#include "jobs/audiothumbjob.hpp"
#include "jobs/jobmanager.h"
#include "jobs/loadjob.hpp"
#include "jobs/thumbjob.hpp"
#include "kdenlivesettings.h"
#include "macros.hpp"
#include "profiles/profilemodel.hpp"
#include "project/projectmanager.h"
#include "projectclip.h"
#include "projectfolder.h"
#include "projectsubclip.h"
#include "xml/xml.hpp"
#include
#include
#include
#include
#include
#include
#include
ProjectItemModel::ProjectItemModel(QObject *parent)
: AbstractTreeModel(parent)
, m_lock(QReadWriteLock::Recursive)
, m_binPlaylist(new BinPlaylist())
, m_fileWatcher(new FileWatcher())
, m_nextId(1)
, m_blankThumb()
, m_dragType(PlaylistState::Disabled)
{
QPixmap pix(QSize(160, 90));
pix.fill(Qt::lightGray);
m_blankThumb.addPixmap(pix);
connect(m_fileWatcher.get(), &FileWatcher::binClipModified, this, &ProjectItemModel::reloadClip);
connect(m_fileWatcher.get(), &FileWatcher::binClipWaiting, this, &ProjectItemModel::setClipWaiting);
connect(m_fileWatcher.get(), &FileWatcher::binClipMissing, this, &ProjectItemModel::setClipInvalid);
}
std::shared_ptr ProjectItemModel::construct(QObject *parent)
{
std::shared_ptr self(new ProjectItemModel(parent));
self->rootItem = ProjectFolder::construct(self);
return self;
}
ProjectItemModel::~ProjectItemModel() = default;
int ProjectItemModel::mapToColumn(int column) const
{
switch (column) {
case 0:
return AbstractProjectItem::DataName;
break;
case 1:
return AbstractProjectItem::DataDate;
break;
case 2:
return AbstractProjectItem::DataDescription;
break;
default:
return AbstractProjectItem::DataName;
}
}
QVariant ProjectItemModel::data(const QModelIndex &index, int role) const
{
READ_LOCK();
if (!index.isValid()) {
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole) {
std::shared_ptr item = getBinItemByIndex(index);
auto type = static_cast(mapToColumn(index.column()));
QVariant ret = item->getData(type);
return ret;
}
if (role == Qt::DecorationRole) {
if (index.column() != 0) {
return QVariant();
}
// Data has to be returned as icon to allow the view to scale it
std::shared_ptr item = getBinItemByIndex(index);
QVariant thumb = item->getData(AbstractProjectItem::DataThumbnail);
QIcon icon;
if (thumb.canConvert()) {
icon = thumb.value();
} else {
qDebug() << "ERROR: invalid icon found";
}
return icon;
}
std::shared_ptr item = getBinItemByIndex(index);
return item->getData(static_cast(role));
}
bool ProjectItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
QWriteLocker locker(&m_lock);
std::shared_ptr item = getBinItemByIndex(index);
if (item->rename(value.toString(), index.column())) {
emit dataChanged(index, index, {role});
return true;
}
// Item name was not changed
return false;
}
Qt::ItemFlags ProjectItemModel::flags(const QModelIndex &index) const
{
/*return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;*/
if (!index.isValid()) {
return Qt::ItemIsDropEnabled;
}
std::shared_ptr item = getBinItemByIndex(index);
AbstractProjectItem::PROJECTITEMTYPE type = item->itemType();
switch (type) {
case AbstractProjectItem::FolderItem:
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
break;
case AbstractProjectItem::ClipItem:
if (!item->statusReady()) {
return Qt::ItemIsSelectable;
}
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
break;
case AbstractProjectItem::SubClipItem:
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
break;
case AbstractProjectItem::FolderUpItem:
return Qt::ItemIsEnabled;
break;
default:
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
}
// cppcheck-suppress unusedFunction
bool ProjectItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
Q_UNUSED(row)
Q_UNUSED(column)
if (action == Qt::IgnoreAction) {
return true;
}
if (data->hasUrls()) {
emit itemDropped(data->urls(), parent);
return true;
}
if (data->hasFormat(QStringLiteral("kdenlive/producerslist"))) {
// Dropping an Bin item
const QStringList ids = QString(data->data(QStringLiteral("kdenlive/producerslist"))).split(QLatin1Char(';'));
if (ids.constFirst().contains(QLatin1Char('/'))) {
// subclip zone
QStringList clipData = ids.constFirst().split(QLatin1Char('/'));
if (clipData.length() >= 3) {
QString id;
return requestAddBinSubClip(id, clipData.at(1).toInt(), clipData.at(2).toInt(), QString(), clipData.at(0));
} else {
// error, malformed clip zone, abort
return false;
}
} else {
emit itemDropped(ids, parent);
}
return true;
}
if (data->hasFormat(QStringLiteral("kdenlive/effect"))) {
// Dropping effect on a Bin item
QStringList effectData;
effectData << QString::fromUtf8(data->data(QStringLiteral("kdenlive/effect")));
QStringList source = QString::fromUtf8(data->data(QStringLiteral("kdenlive/effectsource"))).split(QLatin1Char('-'));
effectData << source;
emit effectDropped(effectData, parent);
return true;
}
if (data->hasFormat(QStringLiteral("kdenlive/clip"))) {
const QStringList list = QString(data->data(QStringLiteral("kdenlive/clip"))).split(QLatin1Char(';'));
QString id;
return requestAddBinSubClip(id, list.at(1).toInt(), list.at(2).toInt(), QString(), list.at(0));
}
return false;
}
QVariant ProjectItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
QVariant columnName;
switch (section) {
case 0:
columnName = i18n("Name");
break;
case 1:
columnName = i18n("Date");
break;
case 2:
columnName = i18n("Description");
break;
default:
columnName = i18n("Unknown");
break;
}
return columnName;
}
return QAbstractItemModel::headerData(section, orientation, role);
}
int ProjectItemModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return getBinItemByIndex(parent)->supportedDataCount();
}
return std::static_pointer_cast(rootItem)->supportedDataCount();
}
// cppcheck-suppress unusedFunction
Qt::DropActions ProjectItemModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
QStringList ProjectItemModel::mimeTypes() const
{
QStringList types;
types << QStringLiteral("kdenlive/producerslist") << QStringLiteral("text/uri-list") << QStringLiteral("kdenlive/clip")
<< QStringLiteral("kdenlive/effect");
return types;
}
QMimeData *ProjectItemModel::mimeData(const QModelIndexList &indices) const
{
// Mime data is a list of id's separated by ';'.
// Clip ids are represented like: 2 (where 2 is the clip's id)
// Clip zone ids are represented like: 2/10/200 (where 2 is the clip's id, 10 and 200 are in and out points)
// Folder ids are represented like: #2 (where 2 is the folder's id)
auto *mimeData = new QMimeData();
QStringList list;
size_t duration = 0;
for (int i = 0; i < indices.count(); i++) {
QModelIndex ix = indices.at(i);
if (!ix.isValid() || ix.column() != 0) {
continue;
}
std::shared_ptr item = getBinItemByIndex(ix);
AbstractProjectItem::PROJECTITEMTYPE type = item->itemType();
if (type == AbstractProjectItem::ClipItem) {
ClipType::ProducerType cType = item->clipType();
QString dragId = item->clipId();
if ((cType == ClipType::AV || cType == ClipType::Playlist)) {
switch (m_dragType) {
case PlaylistState::AudioOnly:
dragId.prepend(QLatin1Char('A'));
break;
case PlaylistState::VideoOnly:
dragId.prepend(QLatin1Char('V'));
break;
default:
break;
}
}
list << dragId;
duration += (std::static_pointer_cast(item))->frameDuration();
} else if (type == AbstractProjectItem::SubClipItem) {
QPoint p = item->zone();
list << std::static_pointer_cast(item)->getMasterClip()->clipId() + QLatin1Char('/') + QString::number(p.x()) + QLatin1Char('/') +
QString::number(p.y());
} else if (type == AbstractProjectItem::FolderItem) {
list << "#" + item->clipId();
}
}
if (!list.isEmpty()) {
QByteArray data;
data.append(list.join(QLatin1Char(';')).toUtf8());
mimeData->setData(QStringLiteral("kdenlive/producerslist"), data);
mimeData->setText(QString::number(duration));
}
return mimeData;
}
void ProjectItemModel::onItemUpdated(const std::shared_ptr &item, int role)
{
auto tItem = std::static_pointer_cast(item);
auto ptr = tItem->parentItem().lock();
if (ptr) {
auto index = getIndexFromItem(tItem);
emit dataChanged(index, index, {role});
}
}
void ProjectItemModel::onItemUpdated(const QString &binId, int role)
{
std::shared_ptr item = getItemByBinId(binId);
if (item) {
onItemUpdated(item, role);
}
}
std::shared_ptr ProjectItemModel::getClipByBinID(const QString &binId)
{
if (binId.contains(QLatin1Char('_'))) {
return getClipByBinID(binId.section(QLatin1Char('_'), 0, 0));
}
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast(clip.second.lock());
if (c->itemType() == AbstractProjectItem::ClipItem && c->clipId() == binId) {
return std::static_pointer_cast(c);
}
}
return nullptr;
}
-const QList ProjectItemModel::getAudioLevelsByBinID(const QString &binId)
+const QList ProjectItemModel::getAudioLevelsByBinID(const QString &binId)
{
if (binId.contains(QLatin1Char('_'))) {
return getAudioLevelsByBinID(binId.section(QLatin1Char('_'), 0, 0));
}
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast(clip.second.lock());
if (c->itemType() == AbstractProjectItem::ClipItem && c->clipId() == binId) {
return std::static_pointer_cast(c)->audioFrameCache;
}
}
- return QList ();
+ return QList();
}
bool ProjectItemModel::hasClip(const QString &binId)
{
return getClipByBinID(binId) != nullptr;
}
std::shared_ptr ProjectItemModel::getFolderByBinId(const QString &binId)
{
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast(clip.second.lock());
if (c->itemType() == AbstractProjectItem::FolderItem && c->clipId() == binId) {
return std::static_pointer_cast(c);
}
}
return nullptr;
}
const QString ProjectItemModel::getFolderIdByName(const QString &folderName)
{
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast(clip.second.lock());
if (c->itemType() == AbstractProjectItem::FolderItem && c->name() == folderName) {
return c->clipId();
}
}
return QString();
}
std::shared_ptr ProjectItemModel::getItemByBinId(const QString &binId)
{
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast(clip.second.lock());
if (c->clipId() == binId) {
return c;
}
}
return nullptr;
}
void ProjectItemModel::setBinEffectsEnabled(bool enabled)
{
return std::static_pointer_cast(rootItem)->setBinEffectsEnabled(enabled);
}
QStringList ProjectItemModel::getEnclosingFolderInfo(const QModelIndex &index) const
{
QStringList noInfo;
noInfo << QString::number(-1);
noInfo << QString();
if (!index.isValid()) {
return noInfo;
}
std::shared_ptr currentItem = getBinItemByIndex(index);
auto folder = currentItem->getEnclosingFolder(true);
if ((folder == nullptr) || folder == rootItem) {
return noInfo;
}
QStringList folderInfo;
folderInfo << currentItem->clipId();
folderInfo << currentItem->name();
return folderInfo;
}
void ProjectItemModel::clean()
{
std::vector> toDelete;
toDelete.reserve((size_t)rootItem->childCount());
for (int i = 0; i < rootItem->childCount(); ++i) {
toDelete.push_back(std::static_pointer_cast(rootItem->child(i)));
}
Fun undo = []() { return true; };
Fun redo = []() { return true; };
for (const auto &child : toDelete) {
requestBinClipDeletion(child, undo, redo);
}
Q_ASSERT(rootItem->childCount() == 0);
m_nextId = 1;
m_fileWatcher->clear();
}
std::shared_ptr ProjectItemModel::getRootFolder() const
{
return std::static_pointer_cast(rootItem);
}
void ProjectItemModel::loadSubClips(const QString &id, const QMap &dataMap)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
loadSubClips(id, dataMap, undo, redo);
}
void ProjectItemModel::loadSubClips(const QString &id, const QMap &dataMap, Fun &undo, Fun &redo)
{
std::shared_ptr clip = getClipByBinID(id);
if (!clip) {
return;
}
QMapIterator i(dataMap);
QList missingThumbs;
int maxFrame = clip->duration().frames(pCore->getCurrentFps()) - 1;
while (i.hasNext()) {
i.next();
if (!i.value().contains(QLatin1Char(';'))) {
// Problem, the zone has no in/out points
continue;
}
int in = i.value().section(QLatin1Char(';'), 0, 0).toInt();
int out = i.value().section(QLatin1Char(';'), 1, 1).toInt();
if (maxFrame > 0) {
out = qMin(out, maxFrame);
}
QString subId;
requestAddBinSubClip(subId, in, out, i.key(), id, undo, redo);
}
}
std::shared_ptr ProjectItemModel::getBinItemByIndex(const QModelIndex &index) const
{
return std::static_pointer_cast(getItemById((int)index.internalId()));
}
bool ProjectItemModel::requestBinClipDeletion(const std::shared_ptr &clip, Fun &undo, Fun &redo)
{
QWriteLocker locker(&m_lock);
Q_ASSERT(clip);
if (!clip) return false;
int parentId = -1;
if (auto ptr = clip->parent()) parentId = ptr->getId();
clip->selfSoftDelete(undo, redo);
int id = clip->getId();
Fun operation = removeItem_lambda(id);
Fun reverse = addItem_lambda(clip, parentId);
bool res = operation();
if (res) {
UPDATE_UNDO_REDO(operation, reverse, undo, redo);
}
return res;
}
void ProjectItemModel::registerItem(const std::shared_ptr &item)
{
auto clip = std::static_pointer_cast(item);
m_binPlaylist->manageBinItemInsertion(clip);
AbstractTreeModel::registerItem(item);
if (clip->itemType() == AbstractProjectItem::ClipItem) {
auto clipItem = std::static_pointer_cast(clip);
updateWatcher(clipItem);
}
}
void ProjectItemModel::deregisterItem(int id, TreeItem *item)
{
auto clip = static_cast(item);
m_binPlaylist->manageBinItemDeletion(clip);
// TODO : here, we should suspend jobs belonging to the item we delete. They can be restarted if the item is reinserted by undo
AbstractTreeModel::deregisterItem(id, item);
if (clip->itemType() == AbstractProjectItem::ClipItem) {
auto clipItem = static_cast(clip);
m_fileWatcher->removeFile(clipItem->clipId());
}
}
int ProjectItemModel::getFreeFolderId()
{
while (!isIdFree(QString::number(++m_nextId))) {
};
return m_nextId;
}
int ProjectItemModel::getFreeClipId()
{
while (!isIdFree(QString::number(++m_nextId))) {
};
return m_nextId;
}
bool ProjectItemModel::addItem(const std::shared_ptr &item, const QString &parentId, Fun &undo, Fun &redo)
{
QWriteLocker locker(&m_lock);
std::shared_ptr parentItem = getItemByBinId(parentId);
if (!parentItem) {
qCDebug(KDENLIVE_LOG) << " / / ERROR IN PARENT FOLDER";
return false;
}
if (item->itemType() == AbstractProjectItem::ClipItem && parentItem->itemType() != AbstractProjectItem::FolderItem) {
qCDebug(KDENLIVE_LOG) << " / / ERROR when inserting clip: a clip should be inserted in a folder";
return false;
}
if (item->itemType() == AbstractProjectItem::SubClipItem && parentItem->itemType() != AbstractProjectItem::ClipItem) {
qCDebug(KDENLIVE_LOG) << " / / ERROR when inserting subclip: a subclip should be inserted in a clip";
return false;
}
Fun operation = addItem_lambda(item, parentItem->getId());
int itemId = item->getId();
Fun reverse = removeItem_lambda(itemId);
bool res = operation();
Q_ASSERT(item->isInModel());
if (res) {
UPDATE_UNDO_REDO(operation, reverse, undo, redo);
}
return res;
}
bool ProjectItemModel::requestAddFolder(QString &id, const QString &name, const QString &parentId, Fun &undo, Fun &redo)
{
QWriteLocker locker(&m_lock);
if (!id.isEmpty() && !isIdFree(id)) {
id = QString();
}
if (id.isEmpty()) {
id = QString::number(getFreeFolderId());
}
std::shared_ptr new_folder = ProjectFolder::construct(id, name, std::static_pointer_cast(shared_from_this()));
return addItem(new_folder, parentId, undo, redo);
}
-bool ProjectItemModel::requestAddBinClip(QString &id, const QDomElement &description, const QString &parentId, Fun &undo, Fun &redo)
+bool ProjectItemModel::requestAddBinClip(QString &id, const QDomElement &description, const QString &parentId, Fun &undo, Fun &redo,
+ const std::function &readyCallBack)
{
qDebug() << "/////////// requestAddBinClip" << parentId;
QWriteLocker locker(&m_lock);
if (id.isEmpty()) {
id =
Xml::getTagContentByAttribute(description, QStringLiteral("property"), QStringLiteral("name"), QStringLiteral("kdenlive:id"), QStringLiteral("-1"));
if (id == QStringLiteral("-1") || !isIdFree(id)) {
id = QString::number(getFreeClipId());
}
}
Q_ASSERT(!id.isEmpty() && isIdFree(id));
qDebug() << "/////////// found id" << id;
std::shared_ptr new_clip =
ProjectClip::construct(id, description, m_blankThumb, std::static_pointer_cast(shared_from_this()));
qDebug() << "/////////// constructed ";
bool res = addItem(new_clip, parentId, undo, redo);
qDebug() << "/////////// added " << res;
if (res) {
- int loadJob = pCore->jobManager()->startJob({id}, -1, QString(), description);
+ int loadJob = pCore->jobManager()->startJob({id}, -1, QString(), description, std::bind(readyCallBack, id));
pCore->jobManager()->startJob({id}, loadJob, QString(), 150, 0, true);
pCore->jobManager()->startJob({id}, loadJob, QString());
}
return res;
}
bool ProjectItemModel::requestAddBinClip(QString &id, const QDomElement &description, const QString &parentId, const QString &undoText)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool res = requestAddBinClip(id, description, parentId, undo, redo);
if (res) {
pCore->pushUndo(undo, redo, undoText.isEmpty() ? i18n("Add bin clip") : undoText);
}
return res;
}
bool ProjectItemModel::requestAddBinClip(QString &id, const std::shared_ptr &producer, const QString &parentId, Fun &undo, Fun &redo)
{
QWriteLocker locker(&m_lock);
if (id.isEmpty()) {
id = QString::number(producer->get_int("kdenlive:id"));
if (!isIdFree(id)) {
id = QString::number(getFreeClipId());
}
}
Q_ASSERT(!id.isEmpty() && isIdFree(id));
std::shared_ptr new_clip = ProjectClip::construct(id, m_blankThumb, std::static_pointer_cast(shared_from_this()), producer);
bool res = addItem(new_clip, parentId, undo, redo);
if (res) {
int blocking = pCore->jobManager()->getBlockingJobId(id, AbstractClipJob::LOADJOB);
pCore->jobManager()->startJob({id}, blocking, QString(), 150, -1, true);
pCore->jobManager()->startJob({id}, blocking, QString());
}
return res;
}
bool ProjectItemModel::requestAddBinSubClip(QString &id, int in, int out, const QString &zoneName, const QString &parentId, Fun &undo, Fun &redo)
{
QWriteLocker locker(&m_lock);
if (id.isEmpty()) {
id = QString::number(getFreeClipId());
}
Q_ASSERT(!id.isEmpty() && isIdFree(id));
QString subId = parentId;
if (subId.startsWith(QLatin1Char('A')) || subId.startsWith(QLatin1Char('V'))) {
subId.remove(0, 1);
}
auto clip = getClipByBinID(subId);
Q_ASSERT(clip->itemType() == AbstractProjectItem::ClipItem);
auto tc = pCore->currentDoc()->timecode().getDisplayTimecodeFromFrames(in, KdenliveSettings::frametimecode());
std::shared_ptr new_clip =
ProjectSubClip::construct(id, clip, std::static_pointer_cast(shared_from_this()), in, out, tc, zoneName);
bool res = addItem(new_clip, subId, undo, redo);
if (res) {
int parentJob = pCore->jobManager()->getBlockingJobId(subId, AbstractClipJob::LOADJOB);
pCore->jobManager()->startJob({id}, parentJob, QString(), 150, -1, true);
}
return res;
}
bool ProjectItemModel::requestAddBinSubClip(QString &id, int in, int out, const QString &zoneName, const QString &parentId)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool res = requestAddBinSubClip(id, in, out, zoneName, parentId, undo, redo);
if (res) {
pCore->pushUndo(undo, redo, i18n("Add a sub clip"));
}
return res;
}
Fun ProjectItemModel::requestRenameFolder_lambda(const std::shared_ptr &folder, const QString &newName)
{
int id = folder->getId();
return [this, id, newName]() {
auto currentFolder = std::static_pointer_cast(m_allItems[id].lock());
if (!currentFolder) {
return false;
}
currentFolder->setName(newName);
m_binPlaylist->manageBinFolderRename(currentFolder);
auto index = getIndexFromItem(currentFolder);
emit dataChanged(index, index, {AbstractProjectItem::DataName});
return true;
};
}
bool ProjectItemModel::requestRenameFolder(const std::shared_ptr &folder, const QString &name, Fun &undo, Fun &redo)
{
QWriteLocker locker(&m_lock);
QString oldName = folder->name();
auto operation = requestRenameFolder_lambda(folder, name);
if (operation()) {
auto reverse = requestRenameFolder_lambda(folder, oldName);
UPDATE_UNDO_REDO(operation, reverse, undo, redo);
return true;
}
return false;
}
bool ProjectItemModel::requestRenameFolder(std::shared_ptr folder, const QString &name)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool res = requestRenameFolder(std::move(folder), name, undo, redo);
if (res) {
pCore->pushUndo(undo, redo, i18n("Rename Folder"));
}
return res;
}
bool ProjectItemModel::requestCleanup()
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool res = true;
std::vector> to_delete;
// Iterate to find clips that are not in timeline
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast(clip.second.lock());
if (c->itemType() == AbstractProjectItem::ClipItem && !c->isIncludedInTimeline()) {
to_delete.push_back(c);
}
}
// it is important to execute deletion in a separate loop, because otherwise
// the iterators of m_allItems get messed up
for (const auto &c : to_delete) {
res = requestBinClipDeletion(c, undo, redo);
if (!res) {
bool undone = undo();
Q_ASSERT(undone);
return false;
}
}
pCore->pushUndo(undo, redo, i18n("Clean Project"));
return true;
}
std::vector ProjectItemModel::getAllClipIds() const
{
std::vector result;
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast(clip.second.lock());
if (c->itemType() == AbstractProjectItem::ClipItem) {
result.push_back(c->clipId());
}
}
return result;
}
QStringList ProjectItemModel::getClipByUrl(const QFileInfo &url) const
{
QStringList result;
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast(clip.second.lock());
if (c->itemType() == AbstractProjectItem::ClipItem) {
if (QFileInfo(std::static_pointer_cast(c)->clipUrl()) == url) {
result << c->clipId();
}
}
}
return result;
}
bool ProjectItemModel::loadFolders(Mlt::Properties &folders)
{
// At this point, we expect the folders properties to have a name of the form "x.y" where x is the id of the parent folder and y the id of the child.
// Note that for root folder, x = -1
// The value of the property is the name of the child folder
std::unordered_map> downLinks; // key are parents, value are children
std::unordered_map upLinks; // key are children, value are parent
std::unordered_map newIds; // we store the correspondence to the new ids
std::unordered_map folderNames;
newIds[-1] = getRootFolder()->clipId();
if (folders.count() == 0) return true;
for (int i = 0; i < folders.count(); i++) {
QString folderName = folders.get(i);
QString id = folders.get_name(i);
int parentId = id.section(QLatin1Char('.'), 0, 0).toInt();
int folderId = id.section(QLatin1Char('.'), 1, 1).toInt();
downLinks[parentId].push_back(folderId);
upLinks[folderId] = parentId;
folderNames[folderId] = folderName;
qDebug() << "Found folder " << folderId << "name = " << folderName << "parent=" << parentId;
}
// In case there are some non-existant parent, we fall back to root
for (const auto &f : downLinks) {
if (upLinks.count(f.first) == 0) {
upLinks[f.first] = -1;
}
if (f.first != -1 && downLinks.count(upLinks[f.first]) == 0) {
qDebug() << "Warning: parent folder " << upLinks[f.first] << "for folder" << f.first << "is invalid. Folder will be placed in topmost directory.";
upLinks[f.first] = -1;
}
}
// We now do a BFS to construct the folders in order
Q_ASSERT(downLinks.count(-1) > 0);
Fun undo = []() { return true; };
Fun redo = []() { return true; };
std::queue queue;
std::unordered_set seen;
queue.push(-1);
while (!queue.empty()) {
int current = queue.front();
seen.insert(current);
queue.pop();
if (current != -1) {
QString id = QString::number(current);
bool res = requestAddFolder(id, folderNames[current], newIds[upLinks[current]], undo, redo);
if (!res) {
bool undone = undo();
Q_ASSERT(undone);
return false;
}
newIds[current] = id;
}
for (int c : downLinks[current]) {
queue.push(c);
}
}
return true;
}
bool ProjectItemModel::isIdFree(const QString &id) const
{
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast(clip.second.lock());
if (c->clipId() == id) {
return false;
}
}
return true;
}
void ProjectItemModel::loadBinPlaylist(Mlt::Tractor *documentTractor, Mlt::Tractor *modelTractor, std::unordered_map &binIdCorresp)
{
clean();
Mlt::Properties retainList((mlt_properties)documentTractor->get_data("xml_retain"));
qDebug() << "Loading bin playlist...";
if (retainList.is_valid()) {
qDebug() << "retain is valid";
Mlt::Playlist playlist((mlt_playlist)retainList.get_data(BinPlaylist::binPlaylistId.toUtf8().constData()));
if (playlist.is_valid() && playlist.type() == playlist_type) {
qDebug() << "playlist is valid";
// Load bin clips
qDebug() << "init bin";
// Load folders
Mlt::Properties folderProperties;
Mlt::Properties playlistProps(playlist.get_properties());
folderProperties.pass_values(playlistProps, "kdenlive:folder.");
loadFolders(folderProperties);
// Read notes
QString notes = playlistProps.get("kdenlive:documentnotes");
pCore->projectManager()->setDocumentNotes(notes);
Fun undo = []() { return true; };
Fun redo = []() { return true; };
qDebug() << "Found " << playlist.count() << "clips";
int max = playlist.count();
for (int i = 0; i < max; i++) {
QScopedPointer prod(playlist.get_clip(i));
std::shared_ptr producer(new Mlt::Producer(prod->parent()));
qDebug() << "dealing with bin clip" << i;
if (producer->is_blank() || !producer->is_valid()) {
qDebug() << "producer is not valid or blank";
continue;
}
QString id = qstrdup(producer->get("kdenlive:id"));
QString parentId = qstrdup(producer->get("kdenlive:folderid"));
if (parentId.isEmpty()) {
parentId = QStringLiteral("-1");
}
qDebug() << "clip id" << id;
if (id.contains(QLatin1Char('_'))) {
// TODO refac ?
/*
// This is a track producer
QString mainId = id.section(QLatin1Char('_'), 0, 0);
// QString track = id.section(QStringLiteral("_"), 1, 1);
if (m_clipList.contains(mainId)) {
// The controller for this track producer already exists
} else {
// Create empty controller for this clip
requestClipInfo info;
info.imageHeight = 0;
info.clipId = id;
info.replaceProducer = true;
emit slotProducerReady(info, ClipController::mediaUnavailable);
}
*/
} else {
QString newId = isIdFree(id) ? id : QString::number(getFreeClipId());
producer->set("_kdenlive_processed", 1);
requestAddBinClip(newId, producer, parentId, undo, redo);
binIdCorresp[id] = newId;
qDebug() << "Loaded clip " << id << "under id" << newId;
}
}
}
}
m_binPlaylist->setRetainIn(modelTractor);
}
/** @brief Save document properties in MLT's bin playlist */
void ProjectItemModel::saveDocumentProperties(const QMap &props, const QMap &metadata,
std::shared_ptr guideModel)
{
m_binPlaylist->saveDocumentProperties(props, metadata, std::move(guideModel));
}
void ProjectItemModel::saveProperty(const QString &name, const QString &value)
{
m_binPlaylist->saveProperty(name, value);
}
QMap ProjectItemModel::getProxies(const QString &root)
{
return m_binPlaylist->getProxies(root);
}
void ProjectItemModel::reloadClip(const QString &binId)
{
std::shared_ptr clip = getClipByBinID(binId);
if (clip) {
clip->reloadProducer();
}
}
void ProjectItemModel::setClipWaiting(const QString &binId)
{
std::shared_ptr clip = getClipByBinID(binId);
if (clip) {
clip->setClipStatus(AbstractProjectItem::StatusWaiting);
}
}
void ProjectItemModel::setClipInvalid(const QString &binId)
{
std::shared_ptr clip = getClipByBinID(binId);
if (clip) {
clip->setClipStatus(AbstractProjectItem::StatusMissing);
// TODO: set producer as blank invalid
}
}
void ProjectItemModel::updateWatcher(const std::shared_ptr &clipItem)
{
if (clipItem->clipType() == ClipType::AV || clipItem->clipType() == ClipType::Audio || clipItem->clipType() == ClipType::Image ||
clipItem->clipType() == ClipType::Video || clipItem->clipType() == ClipType::Playlist || clipItem->clipType() == ClipType::TextTemplate) {
m_fileWatcher->removeFile(clipItem->clipId());
m_fileWatcher->addFile(clipItem->clipId(), clipItem->clipUrl());
}
}
void ProjectItemModel::setDragType(PlaylistState::ClipState type)
{
m_dragType = type;
}
int ProjectItemModel::clipsCount() const
{
return m_binPlaylist->count();
}
diff --git a/src/bin/projectitemmodel.h b/src/bin/projectitemmodel.h
index fc1a6fad9..1ee39e5cf 100644
--- a/src/bin/projectitemmodel.h
+++ b/src/bin/projectitemmodel.h
@@ -1,262 +1,264 @@
/*
Copyright (C) 2012 Till Theato
Copyright (C) 2014 Jean-Baptiste Mardelle
Copyright (C) 2017 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 PROJECTITEMMODEL_H
#define PROJECTITEMMODEL_H
#include "abstractmodel/abstracttreemodel.hpp"
#include "definitions.h"
#include "undohelper.hpp"
#include
#include
#include
#include
#include
class AbstractProjectItem;
class BinPlaylist;
class FileWatcher;
class MarkerListModel;
class ProjectClip;
class ProjectFolder;
namespace Mlt {
class Producer;
class Properties;
class Tractor;
} // namespace Mlt
/**
* @class ProjectItemModel
* @brief Acts as an adaptor to be able to use BinModel with item views.
*/
class ProjectItemModel : public AbstractTreeModel
{
Q_OBJECT
protected:
explicit ProjectItemModel(QObject *parent);
public:
static std::shared_ptr construct(QObject *parent = nullptr);
~ProjectItemModel() override;
friend class ProjectClip;
/** @brief Returns a clip from the hierarchy, given its id */
std::shared_ptr getClipByBinID(const QString &binId);
/** @brief Returns audio levels for a clip from its id */
const QList getAudioLevelsByBinID(const QString &binId);
/** @brief Returns a list of clips using the given url */
QStringList getClipByUrl(const QFileInfo &url) const;
/** @brief Helper to check whether a clip with a given id exists */
bool hasClip(const QString &binId);
/** @brief Gets a folder by its id. If none is found, nullptr is returned */
std::shared_ptr getFolderByBinId(const QString &binId);
/** @brief Gets a id folder by its name. If none is found, empty string returned */
const QString getFolderIdByName(const QString &folderName);
/** @brief Gets any item by its id. */
std::shared_ptr getItemByBinId(const QString &binId);
/** @brief This function change the global enabled state of the bin effects */
void setBinEffectsEnabled(bool enabled);
/** @brief Returns some info about the folder containing the given index */
QStringList getEnclosingFolderInfo(const QModelIndex &index) const;
/** @brief Deletes all element and start a fresh model */
void clean();
/** @brief Returns the id of all the clips (excluding folders) */
std::vector getAllClipIds() const;
/** @brief Convenience method to access root folder */
std::shared_ptr getRootFolder() const;
/** @brief Create the subclips defined in the parent clip.
@param id is the id of the parent clip
@param data is a definition of the subclips (keys are subclips' names, value are "in:out")*/
void loadSubClips(const QString &id, const QMap &data);
void loadSubClips(const QString &id, const QMap &dataMap, Fun &undo, Fun &redo);
/* @brief Convenience method to retrieve a pointer to an element given its index */
std::shared_ptr getBinItemByIndex(const QModelIndex &index) const;
/* @brief Load the folders given the property containing them */
bool loadFolders(Mlt::Properties &folders);
/* @brief Parse a bin playlist from the document tractor and reconstruct the tree */
void loadBinPlaylist(Mlt::Tractor *documentTractor, Mlt::Tractor *modelTractor, std::unordered_map &binIdCorresp);
/** @brief Save document properties in MLT's bin playlist */
void saveDocumentProperties(const QMap &props, const QMap &metadata, std::shared_ptr guideModel);
/** @brief Save a property to main bin */
void saveProperty(const QString &name, const QString &value);
/** @brief Returns item data depending on role requested */
QVariant data(const QModelIndex &index, int role) const override;
/** @brief Called when user edits an item */
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
/** @brief Allow selection and drag & drop */
Qt::ItemFlags flags(const QModelIndex &index) const override;
/** @brief Returns column names in case we want to use columns in QTreeView */
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
/** @brief Mandatory reimplementation from QAbstractItemModel */
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
/** @brief Returns the MIME type used for Drag actions */
QStringList mimeTypes() const override;
/** @brief Create data that will be used for Drag events */
QMimeData *mimeData(const QModelIndexList &indices) const override;
/** @brief Set size for thumbnails */
void setIconSize(QSize s);
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
Qt::DropActions supportedDropActions() const override;
/* @brief Request deletion of a bin clip from the project bin
@param clip : pointer to the clip to delete
@param undo,redo: lambdas that are updated to accumulate operation.
*/
bool requestBinClipDeletion(const std::shared_ptr &clip, Fun &undo, Fun &redo);
/* @brief Request creation of a bin folder
@param id Id of the requested bin. If this is empty or invalid (already used, for example), it will be used as a return parameter to give the automatic
bin id used.
@param name Name of the folder
@param parentId Bin id of the parent folder
@param undo,redo: lambdas that are updated to accumulate operation.
*/
bool requestAddFolder(QString &id, const QString &name, const QString &parentId, Fun &undo, Fun &redo);
/* @brief Request creation of a bin clip
@param id Id of the requested bin. If this is empty, it will be used as a return parameter to give the automatic bin id used.
@param description Xml description of the clip
@param parentId Bin id of the parent folder
@param undo,redo: lambdas that are updated to accumulate operation.
+ @parame readyCallBack: lambda that will be executed when the clip becomes ready. It is given the binId as parameter
*/
- bool requestAddBinClip(QString &id, const QDomElement &description, const QString &parentId, Fun &undo, Fun &redo);
+ bool requestAddBinClip(QString &id, const QDomElement &description, const QString &parentId, Fun &undo, Fun &redo,
+ const std::function &readyCallBack = [](const QString &) {});
bool requestAddBinClip(QString &id, const QDomElement &description, const QString &parentId, const QString &undoText = QString());
/* @brief This is the addition function when we already have a producer for the clip*/
bool requestAddBinClip(QString &id, const std::shared_ptr &producer, const QString &parentId, Fun &undo, Fun &redo);
/* @brief Create a subClip
@param id Id of the requested bin. If this is empty, it will be used as a return parameter to give the automatic bin id used.
@param parentId Bin id of the parent clip
@param in,out : zone that corresponds to the subclip
@param undo,redo: lambdas that are updated to accumulate operation.
*/
bool requestAddBinSubClip(QString &id, int in, int out, const QString &zoneName, const QString &parentId, Fun &undo, Fun &redo);
bool requestAddBinSubClip(QString &id, int in, int out, const QString &zoneName, const QString &parentId);
/* @brief Request that a folder's name is changed
@param clip : pointer to the folder to rename
@param name: new name of the folder
@param undo,redo: lambdas that are updated to accumulate operation.
*/
bool requestRenameFolder(const std::shared_ptr &folder, const QString &name, Fun &undo, Fun &redo);
/* Same functions but pushes the undo object directly */
bool requestRenameFolder(std::shared_ptr folder, const QString &name);
/* @brief Request that the unused clips are deleted */
bool requestCleanup();
/* @brief Retrieves the next id available for attribution to a folder */
int getFreeFolderId();
/* @brief Retrieves the next id available for attribution to a clip */
int getFreeClipId();
/** @brief Retrieve a list of proxy/original urls */
QMap getProxies(const QString &root);
/** @brief Request that the producer of a given clip is reloaded */
void reloadClip(const QString &binId);
/** @brief Set the status of the clip to "waiting". This happens when the corresponding file has changed*/
void setClipWaiting(const QString &binId);
void setClipInvalid(const QString &binId);
/** @brief Number of clips in the bin playlist */
int clipsCount() const;
protected:
/* @brief Register the existence of a new element
*/
void registerItem(const std::shared_ptr &item) override;
/* @brief Deregister the existence of a new element*/
void deregisterItem(int id, TreeItem *item) override;
/* @brief Helper function to generate a lambda that rename a folder */
Fun requestRenameFolder_lambda(const std::shared_ptr &folder, const QString &newName);
/* @brief Helper function to add a given item to the tree */
bool addItem(const std::shared_ptr &item, const QString &parentId, Fun &undo, Fun &redo);
/* @brief Function to be called when the url of a clip changes */
void updateWatcher(const std::shared_ptr &item);
public slots:
/** @brief An item in the list was modified, notify */
void onItemUpdated(const std::shared_ptr &item, int role);
void onItemUpdated(const QString &binId, int role);
/** @brief Check whether a given id is currently used or not*/
bool isIdFree(const QString &id) const;
void setDragType(PlaylistState::ClipState type);
private:
/** @brief Return reference to column specific data */
int mapToColumn(int column) const;
mutable QReadWriteLock m_lock; // This is a lock that ensures safety in case of concurrent access
std::unique_ptr m_binPlaylist;
std::unique_ptr m_fileWatcher;
int m_nextId;
QIcon m_blankThumb;
PlaylistState::ClipState m_dragType;
signals:
// thumbs of the given clip were modified, request update of the monitor if need be
void refreshAudioThumbs(const QString &id);
void refreshClip(const QString &id);
void emitMessage(const QString &, int, MessageType);
void updateTimelineProducers(const QString &id, const QMap &passProperties);
void refreshPanel(const QString &id);
void requestAudioThumbs(const QString &id, long duration);
// TODO
void markersNeedUpdate(const QString &id, const QList &);
void itemDropped(const QStringList &, const QModelIndex &);
void itemDropped(const QList &, const QModelIndex &);
void effectDropped(const QStringList &, const QModelIndex &);
void addClipCut(const QString &, int, int);
};
#endif
diff --git a/src/capture/mediacapture.cpp b/src/capture/mediacapture.cpp
index 407b046a7..f2d3d0370 100644
--- a/src/capture/mediacapture.cpp
+++ b/src/capture/mediacapture.cpp
@@ -1,268 +1,279 @@
/*
Copyright (C) 2019 Akhil K Gangadharan
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 "mediacapture.h"
#include
#include
#include
MediaCapture::MediaCapture(QObject *parent)
: QObject(parent)
, m_volume(1.)
{
m_probe = std::make_unique(this);
connect(m_probe.get(), &QAudioProbe::audioBufferProbed, this, &MediaCapture::processBuffer);
}
MediaCapture::~MediaCapture() = default;
void MediaCapture::displayErrorMessage()
{
qDebug() << " !!!!!!!!!!!!!!!! ERROR : QMediarecorder - Capture failed";
}
void MediaCapture::recordAudio(bool record)
{
if (!m_audioRecorder) {
m_audioRecorder = std::make_unique(this);
}
m_probe->setSource(m_audioRecorder.get());
if (record && m_audioRecorder->state() == QMediaRecorder::StoppedState) {
m_audioRecorder->setAudioInput(m_audioDevice);
m_audioRecorder->setVolume(m_volume);
m_audioRecorder->setOutputLocation(m_path);
connect(m_audioRecorder.get(), SIGNAL(error(QMediaRecorder::Error)), this, SLOT(displayErrorMessage()));
QAudioEncoderSettings audioSettings;
audioSettings.setBitRate(48000);
QString container = "audio/x-wav";
m_audioRecorder->setEncodingSettings(audioSettings, QVideoEncoderSettings(), container);
m_audioRecorder->record();
} else if (m_audioRecorder->state() != QMediaRecorder::PausedState) {
m_audioRecorder->stop();
m_audioRecorder.reset();
} else {
m_audioRecorder->record();
}
}
void MediaCapture::recordVideo(bool record)
{
// TO DO - fix video capture
if (!m_videoRecorder) {
QList availableCameras = QCameraInfo::availableCameras();
foreach (const QCameraInfo &cameraInfo, availableCameras) {
if (cameraInfo == QCameraInfo::defaultCamera()) {
m_camera = std::make_unique(cameraInfo, this);
break;
}
}
m_videoRecorder = std::make_unique(m_camera.get(), this);
}
if (record && m_videoRecorder->state() == QMediaRecorder::StoppedState) {
connect(m_videoRecorder.get(), SIGNAL(error(QMediaRecorder::Error)), this, SLOT(displayErrorMessage()));
m_videoRecorder->setOutputLocation(m_path);
m_camera->setCaptureMode(QCamera::CaptureVideo);
m_camera->start();
// QString container = "video/mpeg";
// By default, Qt chooses appropriate parameters
m_videoRecorder->record();
} else {
m_videoRecorder->stop();
m_camera->stop();
m_videoRecorder.reset();
m_camera.reset();
}
}
void MediaCapture::setCaptureOutputLocation(QUrl path)
{
m_path = std::move(path);
}
QStringList MediaCapture::getAudioCaptureDevices()
{
m_audioRecorder = std::make_unique(this);
QStringList audioDevices = m_audioRecorder->audioInputs();
m_audioRecorder.reset();
return audioDevices;
}
void MediaCapture::setAudioCaptureDevice(QString audioDevice)
{
m_audioDevice = std::move(audioDevice);
}
void MediaCapture::setAudioVolume(qreal volume)
{
m_volume = volume;
}
int MediaCapture::getState()
{
if (m_audioRecorder != nullptr) {
return m_audioRecorder->state();
}
if (m_videoRecorder != nullptr) {
return m_videoRecorder->state();
}
return -1;
}
template QVector getBufferLevels(const T *buffer, int frames, int channels)
{
QVector max_values;
max_values.fill(0, channels);
for (int i = 0; i < frames; ++i) {
for (int j = 0; j < channels; ++j) {
qreal value = qAbs(qreal(buffer[i * channels + j]));
if (value > max_values.at(j)) {
max_values.replace(j, value);
}
}
}
return max_values;
}
// This function returns the maximum possible sample value for a given audio format
qreal getPeakValue(const QAudioFormat &format)
{
// Note: Only the most common sample formats are supported
if (!format.isValid()) {
return qreal(0);
}
if (format.codec() != "audio/pcm") {
return qreal(0);
}
switch (format.sampleType()) {
case QAudioFormat::Unknown:
break;
case QAudioFormat::Float:
if (format.sampleSize() != 32) { // other sample formats are not supported
return qreal(0);
}
return qreal(1.00003);
case QAudioFormat::SignedInt:
if (format.sampleSize() == 32) {
return qreal(INT_MAX);
}
if (format.sampleSize() == 16) {
return qreal(SHRT_MAX);
}
if (format.sampleSize() == 8) {
return qreal(CHAR_MAX);
}
break;
case QAudioFormat::UnSignedInt:
if (format.sampleSize() == 32) {
return qreal(UINT_MAX);
}
if (format.sampleSize() == 16) {
return qreal(USHRT_MAX);
}
if (format.sampleSize() == 8) {
return qreal(UCHAR_MAX);
}
break;
}
return qreal(0);
}
QVector getBufferLevels(const QAudioBuffer &buffer)
{
QVector values;
if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian) {
return values;
}
if (buffer.format().codec() != "audio/pcm") {
return values;
}
int channelCount = buffer.format().channelCount();
values.fill(0, channelCount);
qreal peak_value = getPeakValue(buffer.format());
if (qFuzzyCompare(peak_value, qreal(0))) {
return values;
}
switch (buffer.format().sampleType()) {
case QAudioFormat::Unknown:
case QAudioFormat::UnSignedInt:
if (buffer.format().sampleSize() == 32) {
values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount);
}
if (buffer.format().sampleSize() == 16) {
values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount);
}
if (buffer.format().sampleSize() == 8) {
values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount);
}
for (double &value : values) {
value = qAbs(value - peak_value / 2) / (peak_value / 2);
}
break;
case QAudioFormat::Float:
if (buffer.format().sampleSize() == 32) {
values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount);
for (double &value : values) {
value /= peak_value;
}
}
break;
case QAudioFormat::SignedInt:
if (buffer.format().sampleSize() == 32) {
values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount);
}
if (buffer.format().sampleSize() == 16) {
values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount);
}
if (buffer.format().sampleSize() == 8) {
values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount);
}
for (double &value : values) {
value /= peak_value;
}
break;
}
return values;
}
void MediaCapture::processBuffer(const QAudioBuffer &buffer)
{
m_levels = getBufferLevels(buffer);
emit levelsChanged();
}
QVector MediaCapture::levels() const
{
return m_levels;
}
+
+bool MediaCapture::isRecording() const
+{
+ if (m_audioRecorder && m_audioRecorder->state() == QMediaRecorder::RecordingState) {
+ return true;
+ }
+ if (m_videoRecorder && m_videoRecorder->state() == QMediaRecorder::RecordingState) {
+ return true;
+ }
+ return false;
+}
diff --git a/src/capture/mediacapture.h b/src/capture/mediacapture.h
index f7e9b6aa7..7f961a43e 100644
--- a/src/capture/mediacapture.h
+++ b/src/capture/mediacapture.h
@@ -1,81 +1,83 @@
/*
Copyright (C) 2019 Akhil K Gangadharan
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 MEDIACAPTURE_H
#define MEDIACAPTURE_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class QAudioRecorder;
class QAudioProbe;
class MediaCapture : public QObject
{
Q_OBJECT
Q_PROPERTY(QVector levels READ levels NOTIFY levelsChanged)
public:
MediaCapture(QObject *parent);
~MediaCapture() override;
void recordAudio(bool /*record*/);
void recordVideo(bool /*record*/);
+ /** @brief Returns true if a recording is in progress **/
+ bool isRecording() const;
/** @brief Sets m_path to selected output location **/
void setCaptureOutputLocation(QUrl /*path*/);
/** @brief Sets m_device to selected audio capture device **/
void setAudioCaptureDevice(QString /*audioDevice*/);
/** @brief Sets m_volume to selected audio capture volume **/
void setAudioVolume(qreal /*volume*/);
/** @brief Returns list of audio devices available for capture **/
QStringList getAudioCaptureDevices();
/** @brief Returns QMediaRecorder::State value **/
int getState();
Q_INVOKABLE QVector levels() const;
public slots:
void displayErrorMessage();
private:
std::unique_ptr m_audioRecorder;
std::unique_ptr m_videoRecorder;
std::unique_ptr m_camera;
std::unique_ptr m_probe;
QString m_audioDevice;
qreal m_volume;
QUrl m_path;
QVector m_levels;
private slots:
void processBuffer(const QAudioBuffer &buffer);
signals:
void levelsChanged();
};
#endif
diff --git a/src/core.cpp b/src/core.cpp
index 2afe289ff..e5be4ef8b 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -1,721 +1,726 @@
/*
Copyright (C) 2014 Till Theato
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 3 of the License, or
(at your option) any later version.
*/
#include "core.h"
#include "bin/bin.h"
#include "bin/projectitemmodel.h"
#include "capture/mediacapture.h"
#include "doc/docundostack.hpp"
#include "doc/kdenlivedoc.h"
#include "jobs/jobmanager.h"
#include "kdenlive_debug.h"
#include "kdenlivesettings.h"
#include "library/librarywidget.h"
#include "mainwindow.h"
#include "mltconnection.h"
#include "mltcontroller/clipcontroller.h"
#include "monitor/monitormanager.h"
#include "profiles/profilemodel.hpp"
#include "profiles/profilerepository.hpp"
#include "project/projectmanager.h"
#include "timeline2/model/timelineitemmodel.hpp"
#include "timeline2/view/timelinecontroller.h"
#include "timeline2/view/timelinewidget.h"
#include
#include
#include
#include
#include
#include
#ifdef Q_OS_MAC
#include
#endif
std::unique_ptr Core::m_self;
Core::Core()
: m_thumbProfile(nullptr)
, m_capture(new MediaCapture(this))
{
}
void Core::prepareShutdown()
{
m_guiConstructed = false;
}
Core::~Core()
{
if (m_monitorManager) {
delete m_monitorManager;
}
// delete m_binWidget;
if (m_projectManager) {
delete m_projectManager;
}
ClipController::mediaUnavailable.reset();
}
void Core::build(const QString &MltPath)
{
if (m_self) {
return;
}
m_self.reset(new Core());
m_self->initLocale();
qRegisterMetaType("audioShortVector");
qRegisterMetaType>("QVector");
qRegisterMetaType("MessageType");
qRegisterMetaType("stringMap");
qRegisterMetaType("audioByteArray");
qRegisterMetaType>("QList");
qRegisterMetaType>("std::shared_ptr");
qRegisterMetaType>();
qRegisterMetaType("QDomElement");
qRegisterMetaType("requestClipInfo");
// Open connection with Mlt
MltConnection::construct(MltPath);
// load the profile from disk
ProfileRepository::get()->refresh();
// load default profile
m_self->m_profile = KdenliveSettings::default_profile();
if (m_self->m_profile.isEmpty()) {
m_self->m_profile = ProjectManager::getDefaultProjectFormat();
KdenliveSettings::setDefault_profile(m_self->m_profile);
}
// Init producer shown for unavailable media
// TODO make it a more proper image, it currently causes a crash on exit
ClipController::mediaUnavailable = std::make_shared(ProfileRepository::get()->getProfile(m_self->m_profile)->profile(), "color:blue");
ClipController::mediaUnavailable->set("length", 99999999);
m_self->m_projectItemModel = ProjectItemModel::construct();
// Job manager must be created before bin to correctly connect
m_self->m_jobManager.reset(new JobManager(m_self.get()));
}
void Core::initGUI(const QUrl &Url)
{
m_guiConstructed = true;
m_profile = KdenliveSettings::default_profile();
m_currentProfile = m_profile;
profileChanged();
m_mainWindow = new MainWindow();
// load default profile and ask user to select one if not found.
if (m_profile.isEmpty()) {
m_profile = ProjectManager::getDefaultProjectFormat();
profileChanged();
KdenliveSettings::setDefault_profile(m_profile);
}
if (!ProfileRepository::get()->profileExists(m_profile)) {
KMessageBox::sorry(m_mainWindow, i18n("The default profile of Kdenlive is not set or invalid, press OK to set it to a correct value."));
// TODO this simple widget should be improved and probably use profileWidget
// we get the list of profiles
QVector