diff --git a/fuzzer/fuzzing.cpp b/fuzzer/fuzzing.cpp
index 6da6b9c1b..089039001 100644
--- a/fuzzer/fuzzing.cpp
+++ b/fuzzer/fuzzing.cpp
@@ -1,423 +1,423 @@
/***************************************************************************
* Copyright (C) 2019 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 "fuzzing.hpp"
#include "bin/model/markerlistmodel.hpp"
#include "doc/docundostack.hpp"
#include "fakeit_standalone.hpp"
#include "logger.hpp"
#include
#include
#include
#include
#include
#define private public
#define protected public
#include "assets/keyframes/model/keyframemodel.hpp"
#include "assets/model/assetparametermodel.hpp"
#include "bin/clipcreator.hpp"
#include "bin/projectclip.h"
#include "bin/projectfolder.h"
#include "bin/projectitemmodel.h"
#include "core.h"
#include "effects/effectsrepository.hpp"
#include "effects/effectstack/model/effectitemmodel.hpp"
#include "effects/effectstack/model/effectstackmodel.hpp"
#include "mltconnection.h"
#include "project/projectmanager.h"
#include "timeline2/model/clipmodel.hpp"
#include "timeline2/model/compositionmodel.hpp"
#include "timeline2/model/groupsmodel.hpp"
#include "timeline2/model/timelinefunctions.hpp"
#include "timeline2/model/timelineitemmodel.hpp"
#include "timeline2/model/timelinemodel.hpp"
#include "timeline2/model/trackmodel.hpp"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic ignored "-Wfloat-equal"
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wpedantic"
#include
#pragma GCC diagnostic pop
using namespace fakeit;
namespace {
QString createProducer(Mlt::Profile &prof, std::string color, std::shared_ptr binModel, int length, bool limited)
{
Logger::log_create_producer("test_producer", {color, binModel, length, limited});
std::shared_ptr producer = std::make_shared(prof, "color", color.c_str());
producer->set("length", length);
producer->set("out", length - 1);
Q_ASSERT(producer->is_valid());
QString binId = QString::number(binModel->getFreeClipId());
auto binClip = ProjectClip::construct(binId, QIcon(), binModel, producer);
if (limited) {
binClip->forceLimitedDuration();
}
Fun undo = []() { return true; };
Fun redo = []() { return true; };
Q_ASSERT(binModel->addItem(binClip, binModel->getRootFolder()->clipId(), undo, redo));
return binId;
}
QString createProducerWithSound(Mlt::Profile &prof, std::shared_ptr binModel)
{
Logger::log_create_producer("test_producer_sound", {binModel});
// std::shared_ptr producer = std::make_shared(prof,
// QFileInfo("../tests/small.mkv").absoluteFilePath().toStdString().c_str());
// In case the test system does not have avformat support, we can switch to the integrated blipflash producer
std::shared_ptr producer = std::make_shared(prof, "blipflash");
producer->set_in_and_out(0, 1);
producer->set("kdenlive:duration", 2);
Q_ASSERT(producer->is_valid());
QString binId = QString::number(binModel->getFreeClipId());
auto binClip = ProjectClip::construct(binId, QIcon(), binModel, producer);
Fun undo = []() { return true; };
Fun redo = []() { return true; };
Q_ASSERT(binModel->addItem(binClip, binModel->getRootFolder()->clipId(), undo, redo));
return binId;
}
inline int modulo(int a, int b)
{
const int result = a % b;
return result >= 0 ? result : result + b;
}
namespace {
bool isIthParamARef(const rttr::method &method, size_t i)
{
QString sig = QString::fromStdString(method.get_signature().to_string());
int deb = sig.indexOf("(");
int end = sig.lastIndexOf(")");
sig = sig.mid(deb + 1, deb - end - 1);
QStringList args = sig.split(QStringLiteral(","));
return args[(int)i].contains("&") && !args[(int)i].contains("const &");
}
} // namespace
} // namespace
void fuzz(const std::string &input)
{
Logger::init();
std::stringstream ss;
ss << input;
Mlt::Profile profile;
auto binModel = pCore->projectItemModel();
binModel->clean();
std::shared_ptr undoStack = std::make_shared(nullptr);
std::shared_ptr guideModel = std::make_shared(undoStack);
TimelineModel::next_id = 0;
Mock pmMock;
When(Method(pmMock, undoStack)).AlwaysReturn(undoStack);
ProjectManager &mocked = pmMock.get();
pCore->m_projectManager = &mocked;
std::vector> all_timelines;
std::unordered_map, std::vector> all_clips, all_tracks, all_compositions;
auto update_elems = [&]() {
all_clips.clear();
all_tracks.clear();
all_compositions.clear();
for (const auto &timeline : all_timelines) {
all_clips[timeline] = {};
all_tracks[timeline] = {};
all_compositions[timeline] = {};
auto &clips = all_clips[timeline];
clips.clear();
for (const auto &c : timeline->m_allClips) {
clips.push_back(c.first);
}
std::sort(clips.begin(), clips.end());
auto &compositions = all_compositions[timeline];
compositions.clear();
for (const auto &c : timeline->m_allCompositions) {
compositions.push_back(c.first);
}
std::sort(compositions.begin(), compositions.end());
auto &tracks = all_tracks[timeline];
tracks.clear();
for (const auto &c : timeline->m_iteratorTable) {
tracks.push_back(c.first);
}
std::sort(tracks.begin(), tracks.end());
}
};
auto get_timeline = [&]() -> std::shared_ptr {
int id = 0;
ss >> id;
if (all_timelines.size() == 0) return nullptr;
id = modulo(id, (int)all_timelines.size());
return all_timelines[size_t(id)];
};
auto get_clip = [&](std::shared_ptr timeline) {
int id = 0;
ss >> id;
if (!timeline) return -1;
if (timeline->isClip(id)) return id;
if (all_timelines.size() == 0) return -1;
if (all_clips.count(timeline) == 0) return -1;
if (all_clips[timeline].size() == 0) return -1;
id = modulo(id, (int)all_clips[timeline].size());
return all_clips[timeline][id];
};
auto get_compo = [&](std::shared_ptr timeline) {
int id = 0;
ss >> id;
if (!timeline) return -1;
if (timeline->isComposition(id)) return id;
if (all_timelines.size() == 0) return -1;
if (all_compositions.count(timeline) == 0) return -1;
if (all_compositions[timeline].size() == 0) return -1;
id = modulo(id, (int)all_compositions[timeline].size());
return all_compositions[timeline][id];
};
auto get_item = [&](std::shared_ptr timeline) {
int id = 0;
ss >> id;
if (!timeline) return -1;
if (timeline->isClip(id)) return id;
if (timeline->isComposition(id)) return id;
if (all_timelines.size() == 0) return -1;
int clip_count = 0;
if (all_clips.count(timeline) > 0) {
clip_count = all_clips[timeline].size();
}
int compo_count = 0;
if (all_compositions.count(timeline) > 0) {
compo_count = all_compositions[timeline].size();
}
if (clip_count + compo_count == 0) return -1;
id = modulo(id, clip_count + compo_count);
if (id < clip_count) {
return all_clips[timeline][id];
}
return all_compositions[timeline][id - clip_count];
};
auto get_track = [&](std::shared_ptr timeline) {
int id = 0;
ss >> id;
if (!timeline) return -1;
if (timeline->isTrack(id)) return id;
if (all_timelines.size() == 0) return -1;
if (all_tracks.count(timeline) == 0) return -1;
if (all_tracks[timeline].size() == 0) return -1;
id = modulo(id, (int)all_tracks[timeline].size());
return all_tracks[timeline][id];
};
std::string c;
while (ss >> c) {
if (Logger::back_translation_table.count(c) > 0) {
// std::cout << "found=" << c;
c = Logger::back_translation_table[c];
// std::cout << " tranlated=" << c << std::endl;
if (c == "constr_TimelineModel") {
all_timelines.emplace_back(TimelineItemModel::construct(&profile, guideModel, undoStack));
} else if (c == "constr_TrackModel") {
auto timeline = get_timeline();
int id, pos = 0;
std::string name;
bool audio = false;
ss >> id >> pos >> name >> audio;
if (name == "$$") {
name = "";
}
if (pos < -1) pos = 0;
pos = std::min((int)all_tracks[timeline].size(), pos);
if (timeline) {
TrackModel::construct(timeline, -1, pos, QString::fromStdString(name), audio);
}
} else if (c == "constr_test_producer") {
std::string color;
int length = 0;
bool limited = false;
ss >> color >> length >> limited;
createProducer(profile, color, binModel, length, limited);
} else if (c == "constr_test_producer_sound") {
createProducerWithSound(profile, binModel);
} else {
// std::cout << "executing " << c << std::endl;
rttr::type target_type = rttr::type::get();
bool found = false;
for (const std::string &t : {"TimelineModel"}) {
rttr::type current_type = rttr::type::get_by_name(t);
// std::cout << "type " << t << " has methods count=" << current_type.get_methods().size() << std::endl;
if (current_type.get_method(c).is_valid()) {
found = true;
target_type = current_type;
break;
}
}
if (found) {
bool valid = true;
rttr::method target_method = target_type.get_method(c);
std::vector arguments;
rttr::variant ptr;
if (target_type == rttr::type::get()) {
if (all_timelines.size() == 0) {
valid = false;
}
ptr = get_timeline();
}
int i = -1;
for (const auto &p : target_method.get_parameter_infos()) {
++i;
std::string arg_name = p.get_name().to_string();
// std::cout << arg_name << std::endl;
if (arg_name == "compoId") {
std::shared_ptr tim =
(ptr.can_convert>() ? ptr.convert>() : nullptr);
int compoId = get_compo(tim);
valid = valid && (compoId >= 0);
// std::cout << "got compo" << compoId << std::endl;
- arguments.push_back(compoId);
+ arguments.emplace_back(compoId);
} else if (arg_name == "clipId") {
std::shared_ptr tim =
(ptr.can_convert>() ? ptr.convert>() : nullptr);
int clipId = get_clip(tim);
valid = valid && (clipId >= 0);
- arguments.push_back(clipId);
+ arguments.emplace_back(clipId);
// std::cout << "got clipId" << clipId << std::endl;
} else if (arg_name == "trackId") {
std::shared_ptr tim =
(ptr.can_convert>() ? ptr.convert>() : nullptr);
int trackId = get_track(tim);
valid = valid && (trackId >= 0);
- arguments.push_back(rttr::variant(trackId));
+ arguments.emplace_back(trackId);
// std::cout << "got trackId" << trackId << std::endl;
} else if (arg_name == "itemId") {
std::shared_ptr tim =
(ptr.can_convert>() ? ptr.convert>() : nullptr);
int itemId = get_item(tim);
valid = valid && (itemId >= 0);
- arguments.push_back(itemId);
+ arguments.emplace_back(itemId);
// std::cout << "got itemId" << itemId << std::endl;
} else if (arg_name == "ids") {
int count = 0;
ss >> count;
// std::cout << "got ids. going to read count=" << count << std::endl;
if (count > 0) {
std::shared_ptr tim =
(ptr.can_convert>() ? ptr.convert>() : nullptr);
std::unordered_set ids;
for (int i = 0; i < count; ++i) {
int itemId = get_item(tim);
// std::cout << "\t read" << itemId << std::endl;
valid = valid && (itemId >= 0);
ids.insert(itemId);
}
- arguments.push_back(ids);
+ arguments.emplace_back(ids);
} else {
valid = false;
}
} else if (!isIthParamARef(target_method, i)) {
rttr::type arg_type = p.get_type();
if (arg_type == rttr::type::get()) {
int a = 0;
ss >> a;
// std::cout << "read int " << a << std::endl;
- arguments.push_back(a);
+ arguments.emplace_back(a);
} else if (arg_type == rttr::type::get()) {
bool a = false;
ss >> a;
// std::cout << "read bool " << a << std::endl;
- arguments.push_back(a);
+ arguments.emplace_back(a);
} else if (arg_type == rttr::type::get()) {
std::string str = "";
ss >> str;
// std::cout << "read str " << str << std::endl;
if (str == "$$") {
str = "";
}
- arguments.push_back(QString::fromStdString(str));
+ arguments.emplace_back(QString::fromStdString(str));
} else if (arg_type.is_enumeration()) {
int a = 0;
ss >> a;
rttr::variant var_a = a;
var_a.convert((const rttr::type &)arg_type);
// std::cout << "read enum " << arg_type.get_enumeration().value_to_name(var_a).to_string() << std::endl;
arguments.push_back(var_a);
} else {
assert(false);
}
} else {
if (p.get_type() == rttr::type::get()) {
- arguments.push_back(-1);
+ arguments.emplace_back(-1);
} else {
assert(false);
}
}
}
if (valid) {
// std::cout << "VALID!!!" << std::endl;
std::vector args;
args.reserve(arguments.size());
for (const auto &a : arguments) {
args.emplace_back(a);
// std::cout<<"argument="<checkConsistency());
}
}
}
all_clips.clear();
all_tracks.clear();
all_compositions.clear();
- for (size_t i = 0; i < all_timelines.size(); ++i) {
- all_timelines[i].reset();
+ for (auto &all_timeline : all_timelines) {
+ all_timeline.reset();
}
pCore->m_projectManager = nullptr;
Core::m_self.reset();
MltConnection::m_self.reset();
std::cout << "---------------------------------------------------------------------------------------------------------------------------------------------"
"---------------"
<< std::endl;
}
diff --git a/renderer/kdenlive_render.cpp b/renderer/kdenlive_render.cpp
index d9b568a28..576d238b4 100644
--- a/renderer/kdenlive_render.cpp
+++ b/renderer/kdenlive_render.cpp
@@ -1,143 +1,143 @@
/***************************************************************************
* Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "framework/mlt_version.h"
#include "mlt++/Mlt.h"
#include "renderjob.h"
#include
#include
#include
#include
#include
#include
#include
#include
-#include
+#include
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QStringList args = app.arguments();
QStringList preargs;
QString locale;
if (args.count() >= 4) {
// Remove program name
args.removeFirst();
// renderer path (melt)
QString render = args.at(0);
args.removeFirst();
// Source playlist path
QString playlist = args.at(0);
args.removeFirst();
// target - where to save result
QString target = args.at(0);
args.removeFirst();
int pid = 0;
// pid to send back progress
if (args.count() > 0 && args.at(0).startsWith(QLatin1String("-pid:"))) {
pid = args.at(0).section(QLatin1Char(':'), 1).toInt();
args.removeFirst();
}
// Do we want a split render
if (args.count() > 0 && args.at(0) == QLatin1String("-split")) {
args.removeFirst();
// chunks to render
QStringList chunks = args.at(0).split(QLatin1Char(','), QString::SkipEmptyParts);
args.removeFirst();
// chunk size in frames
int chunkSize = args.at(0).toInt();
args.removeFirst();
// rendered file extension
QString extension = args.at(0);
args.removeFirst();
// avformat consumer params
QStringList consumerParams = args.at(0).split(QLatin1Char(' '), QString::SkipEmptyParts);
args.removeFirst();
QDir baseFolder(target);
Mlt::Factory::init();
Mlt::Profile profile;
Mlt::Producer prod(profile, nullptr, playlist.toUtf8().constData());
if (!prod.is_valid()) {
fprintf(stderr, "INVALID playlist: %s \n", playlist.toUtf8().constData());
}
for (const QString &frame : chunks) {
fprintf(stderr, "START:%d \n", frame.toInt());
QString fileName = QStringLiteral("%1.%2").arg(frame).arg(extension);
if (baseFolder.exists(fileName)) {
// Don't overwrite an existing file
fprintf(stderr, "DONE:%d \n", frame.toInt());
continue;
}
QScopedPointer playlst(prod.cut(frame.toInt(), frame.toInt() + chunkSize));
QScopedPointer cons(
new Mlt::Consumer(profile, QString("avformat:%1").arg(baseFolder.absoluteFilePath(fileName)).toUtf8().constData()));
for (const QString ¶m : consumerParams) {
if (param.contains(QLatin1Char('='))) {
cons->set(param.section(QLatin1Char('='), 0, 0).toUtf8().constData(), param.section(QLatin1Char('='), 1).toUtf8().constData());
}
}
cons->set("terminate_on_pause", 1);
cons->connect(*playlst);
playlst.reset();
cons->run();
cons->stop();
cons->purge();
fprintf(stderr, "DONE:%d \n", frame.toInt());
}
// Mlt::Factory::close();
fprintf(stderr, "+ + + RENDERING FINSHED + + + \n");
return 0;
}
int in = -1;
int out = -1;
if (LIBMLT_VERSION_INT < 396544) {
// older MLT version, does not support consumer in/out, so read it manually
QFile f(playlist);
QDomDocument doc;
doc.setContent(&f, false);
f.close();
QDomElement consumer = doc.documentElement().firstChildElement(QStringLiteral("consumer"));
if (!consumer.isNull()) {
in = consumer.attribute("in").toInt();
out = consumer.attribute("out").toInt();
}
}
- RenderJob *rJob = new RenderJob(render, playlist, target, pid, in, out);
+ auto *rJob = new RenderJob(render, playlist, target, pid, in, out);
rJob->start();
return app.exec();
} else {
fprintf(stderr,
"Kdenlive video renderer for MLT.\nUsage: "
"kdenlive_render [-erase] [-kuiserver] [-locale:LOCALE] [in=pos] [out=pos] [render] [profile] [rendermodule] [player] [src] [dest] [[arg1] "
"[arg2] ...]\n"
" -erase: if that parameter is present, src file will be erased at the end\n"
" -kuiserver: if that parameter is present, use KDE job tracker\n"
" -locale:LOCALE : set a locale for rendering. For example, -locale:fr_FR.UTF-8 will use a french locale (comma as numeric separator)\n"
" in=pos: start rendering at frame pos\n"
" out=pos: end rendering at frame pos\n"
" render: path to MLT melt renderer\n"
" profile: the MLT video profile\n"
" rendermodule: the MLT consumer used for rendering, usually it is avformat\n"
" player: path to video player to play when rendering is over, use '-' to disable playing\n"
" src: source file (usually MLT XML)\n"
" dest: destination file\n"
" args: space separated libavformat arguments\n");
return 1;
}
}
diff --git a/renderer/renderjob.cpp b/renderer/renderjob.cpp
index e23985242..45b71c123 100644
--- a/renderer/renderjob.cpp
+++ b/renderer/renderjob.cpp
@@ -1,312 +1,312 @@
/***************************************************************************
* Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "renderjob.h"
#include
#include
#include
#include
-
+#include
// Can't believe I need to do this to sleep.
class SleepThread : QThread
{
public:
void run() override {}
static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
};
RenderJob::RenderJob(const QString &render, const QString &scenelist, const QString &target, int pid, int in, int out)
: QObject()
, m_scenelist(scenelist)
- , m_dest(target)
+ , m_dest(std::move(target))
, m_progress(0)
- , m_prog(render)
+ , m_prog(std::move(render))
, m_player()
, m_jobUiserver(nullptr)
, m_kdenliveinterface(nullptr)
, m_usekuiserver(true)
, m_logfile(scenelist + QStringLiteral(".txt"))
, m_erase(scenelist.startsWith(QDir::tempPath()))
, m_seconds(0)
, m_frame(0)
, m_pid(pid)
, m_dualpass(false)
{
m_renderProcess = new QProcess;
m_renderProcess->setReadChannel(QProcess::StandardError);
connect(m_renderProcess, &QProcess::stateChanged, this, &RenderJob::slotCheckProcess);
// Disable VDPAU so that rendering will work even if there is a Kdenlive instance using VDPAU
qputenv("MLT_NO_VDPAU", "1");
m_args << "-progress" << scenelist;
if (in != -1) {
m_args << QStringLiteral("in=") + QString::number(in);
}
if (out != -1) {
m_args << QStringLiteral("out=") + QString::number(out);
}
// Create a log of every render process.
if (!m_logfile.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "Unable to log to " << m_logfile.fileName();
} else {
m_logstream.setDevice(&m_logfile);
}
}
RenderJob::~RenderJob()
{
delete m_renderProcess;
m_logfile.close();
}
void RenderJob::setLocale(const QString &locale)
{
qputenv("LC_NUMERIC", locale.toUtf8().constData());
}
void RenderJob::slotAbort(const QString &url)
{
if (m_dest == url) {
slotAbort();
}
}
void RenderJob::slotAbort()
{
qWarning() << "Job aborted by user...";
m_renderProcess->kill();
if (m_kdenliveinterface) {
m_dbusargs[1] = -3;
m_dbusargs.append(QString());
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingFinished"), m_dbusargs);
}
if (m_jobUiserver) {
m_jobUiserver->call(QStringLiteral("terminate"), QString());
}
if (m_erase) {
QFile(m_scenelist).remove();
}
QFile(m_dest).remove();
m_logstream << "Job aborted by user" << endl;
m_logstream.flush();
m_logfile.close();
qApp->quit();
}
void RenderJob::receivedStderr()
{
QString result = QString::fromLocal8Bit(m_renderProcess->readAllStandardError()).simplified();
if (!result.startsWith(QLatin1String("Current Frame"))) {
m_errorMessage.append(result + QStringLiteral("
"));
} else {
m_logstream << "melt: " << result << endl;
int pro = result.section(QLatin1Char(' '), -1).toInt();
if (pro <= m_progress || pro <= 0 || pro > 100) {
return;
}
m_progress = pro;
if (m_args.contains(QStringLiteral("pass=1"))) {
m_progress /= 2.0;
} else if (m_args.contains(QStringLiteral("pass=2"))) {
m_progress = 50 + m_progress / 2.0;
}
int frame = result.section(QLatin1Char(','), 1).section(QLatin1Char(' '), -1).toInt();
if ((m_kdenliveinterface != nullptr) && m_kdenliveinterface->isValid()) {
m_dbusargs[1] = m_progress;
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingProgress"), m_dbusargs);
}
if (m_jobUiserver) {
m_jobUiserver->call(QStringLiteral("setPercent"), (uint)m_progress);
int seconds = m_startTime.secsTo(QTime::currentTime());
if (seconds < 0) {
// 1 day offset, add seconds in a day
seconds += 86400;
}
seconds = (int)(seconds * (100 - m_progress) / m_progress);
if (seconds == m_seconds) {
return;
}
m_jobUiserver->call(QStringLiteral("setDescriptionField"), (uint)0, QString(),
tr("Remaining time: ") + QTime(0, 0, 0).addSecs(seconds).toString(QStringLiteral("hh:mm:ss")));
// m_jobUiserver->call(QStringLiteral("setSpeed"), (frame - m_frame) / (seconds - m_seconds));
// m_jobUiserver->call("setSpeed", (frame - m_frame) / (seconds - m_seconds));
m_frame = frame;
m_seconds = seconds;
}
}
}
void RenderJob::start()
{
QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface();
if ((interface != nullptr) && m_usekuiserver) {
if (!interface->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) {
qWarning() << "No org.kde.JobViewServer registered, trying to start kuiserver";
if (QProcess::startDetached(QStringLiteral("kuiserver"))) {
// Give it a couple of seconds to start
QTime t;
t.start();
while (!interface->isServiceRegistered(QStringLiteral("org.kde.JobViewServer")) && t.elapsed() < 3000) {
SleepThread::msleep(100); // Sleep 100 ms
}
} else {
qWarning() << "Failed to start kuiserver";
}
}
if (interface->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) {
QDBusInterface kuiserver(QStringLiteral("org.kde.JobViewServer"), QStringLiteral("/JobViewServer"), QStringLiteral("org.kde.JobViewServer"));
QDBusReply objectPath =
kuiserver.asyncCall(QStringLiteral("requestView"), QLatin1String("kdenlive"), QLatin1String("kdenlive"), 0x0001);
QString reply = ((QDBusObjectPath)objectPath).path();
// Use of the KDE JobViewServer is an ugly hack, it is not reliable
QString dbusView = QStringLiteral("org.kde.JobViewV2");
m_jobUiserver = new QDBusInterface(QStringLiteral("org.kde.JobViewServer"), reply, dbusView);
if ((m_jobUiserver != nullptr) && m_jobUiserver->isValid()) {
m_startTime = QTime::currentTime();
if (!m_args.contains(QStringLiteral("pass=2"))) {
m_jobUiserver->call(QStringLiteral("setPercent"), (uint)0);
}
m_jobUiserver->call(QStringLiteral("setInfoMessage"), tr("Rendering %1").arg(QFileInfo(m_dest).fileName()));
QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.JobViewServer"), reply, dbusView, QStringLiteral("cancelRequested"), this,
SLOT(slotAbort()));
}
}
}
if (m_pid > -1) {
initKdenliveDbusInterface();
}
// Make sure the destination directory is writable
/*QFileInfo checkDestination(QFileInfo(m_dest).absolutePath());
if (!checkDestination.isWritable()) {
slotIsOver(QProcess::NormalExit, false);
}*/
// Because of the logging, we connect to stderr in all cases.
connect(m_renderProcess, &QProcess::readyReadStandardError, this, &RenderJob::receivedStderr);
m_renderProcess->start(m_prog, m_args);
qDebug() << "Started render process: " << m_prog << ' ' << m_args.join(QLatin1Char(' '));
m_logstream << "Started render process: " << m_prog << ' ' << m_args.join(QLatin1Char(' ')) << endl;
}
void RenderJob::initKdenliveDbusInterface()
{
QString kdenliveId;
QDBusConnection connection = QDBusConnection::sessionBus();
QDBusConnectionInterface *ibus = connection.interface();
kdenliveId = QStringLiteral("org.kde.kdenlive-%1").arg(m_pid);
if (!ibus->isServiceRegistered(kdenliveId)) {
kdenliveId.clear();
const QStringList services = ibus->registeredServiceNames();
for (const QString &service : services) {
if (!service.startsWith(QLatin1String("org.kde.kdenlive"))) {
continue;
}
kdenliveId = service;
break;
}
}
m_dbusargs.clear();
if (kdenliveId.isEmpty()) {
return;
}
m_kdenliveinterface =
new QDBusInterface(kdenliveId, QStringLiteral("/kdenlive/MainWindow_1"), QStringLiteral("org.kde.kdenlive.rendering"), connection, this);
if (m_kdenliveinterface) {
m_dbusargs.append(m_dest);
m_dbusargs.append((int)0);
if (!m_args.contains(QStringLiteral("pass=2"))) {
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingProgress"), m_dbusargs);
}
connect(m_kdenliveinterface, SIGNAL(abortRenderJob(QString)), this, SLOT(slotAbort(QString)));
}
}
void RenderJob::slotCheckProcess(QProcess::ProcessState state)
{
if (state == QProcess::NotRunning) {
slotIsOver(m_renderProcess->exitStatus());
}
}
void RenderJob::slotIsOver(QProcess::ExitStatus status, bool isWritable)
{
if (m_jobUiserver) {
m_jobUiserver->call(QStringLiteral("setDescriptionField"), (uint)1, tr("Rendered file"), m_dest);
// m_jobUiserver->call(QStringLiteral("terminate"), QString());
}
if (!isWritable) {
QString error = tr("Cannot write to %1, check permissions.").arg(m_dest);
if (m_kdenliveinterface) {
m_dbusargs[1] = (int)-2;
m_dbusargs.append(error);
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingFinished"), m_dbusargs);
}
QProcess::startDetached(QStringLiteral("kdialog"), QStringList() << QStringLiteral("--error") << error);
m_logstream << error << endl;
qApp->quit();
}
if (m_erase) {
QFile(m_scenelist).remove();
}
if (status == QProcess::CrashExit || m_renderProcess->error() != QProcess::UnknownError || m_renderProcess->exitCode() != 0) {
// rendering crashed
if (m_kdenliveinterface) {
m_dbusargs[1] = (int)-2;
m_dbusargs.append(m_errorMessage);
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingFinished"), m_dbusargs);
}
QStringList args;
QString error = tr("Rendering of %1 aborted, resulting video will probably be corrupted.").arg(m_dest);
args << QStringLiteral("--error") << error;
m_logstream << error << endl;
QProcess::startDetached(QStringLiteral("kdialog"), args);
qApp->quit();
} else {
if (!m_dualpass && (m_kdenliveinterface != nullptr)) {
m_dbusargs[1] = (int)-1;
m_dbusargs.append(QString());
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingFinished"), m_dbusargs);
}
m_logstream << "Rendering of " << m_dest << " finished" << endl;
if (!m_dualpass && m_player.length() > 3 && m_player.contains(QLatin1Char(' '))) {
QStringList args = m_player.split(QLatin1Char(' '));
QString exec = args.takeFirst();
// Decode url
QString url = QUrl::fromEncoded(args.takeLast().toUtf8()).toLocalFile();
args << url;
QProcess::startDetached(exec, args);
}
m_logstream.flush();
if (m_dualpass) {
emit renderingFinished();
deleteLater();
} else {
m_logfile.remove();
qApp->quit();
}
}
}
diff --git a/src/abstractmodel/abstracttreemodel.cpp b/src/abstractmodel/abstracttreemodel.cpp
index 299c00b95..df060e1a1 100644
--- a/src/abstractmodel/abstracttreemodel.cpp
+++ b/src/abstractmodel/abstracttreemodel.cpp
@@ -1,349 +1,349 @@
/***************************************************************************
* 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 "abstracttreemodel.hpp"
#include "treeitem.hpp"
#include
#include
#include
#include
int AbstractTreeModel::currentTreeId = 0;
AbstractTreeModel::AbstractTreeModel(QObject *parent)
: QAbstractItemModel(parent)
{
}
std::shared_ptr AbstractTreeModel::construct(QObject *parent)
{
std::shared_ptr self(new AbstractTreeModel(parent));
self->rootItem = TreeItem::construct(QList(), self, true);
return self;
}
AbstractTreeModel::~AbstractTreeModel()
{
m_allItems.clear();
rootItem.reset();
}
int AbstractTreeModel::columnCount(const QModelIndex &parent) const
{
if (!parent.isValid()) return rootItem->columnCount();
const auto id = (int)parent.internalId();
auto item = getItemById(id);
return item->columnCount();
}
QVariant AbstractTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (role != Qt::DisplayRole) {
return QVariant();
}
auto item = getItemById((int)index.internalId());
return item->dataColumn(index.column());
}
Qt::ItemFlags AbstractTreeModel::flags(const QModelIndex &index) const
{
const auto flags = QAbstractItemModel::flags(index);
if (index.isValid()) {
auto item = getItemById((int)index.internalId());
if (item->depth() == 1) {
return flags & ~Qt::ItemIsSelectable;
}
}
return flags;
}
QVariant AbstractTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return rootItem->dataColumn(section);
return QVariant();
}
QModelIndex AbstractTreeModel::index(int row, int column, const QModelIndex &parent) const
{
std::shared_ptr parentItem;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = getItemById((int)parent.internalId());
if (row >= parentItem->childCount()) return QModelIndex();
std::shared_ptr childItem = parentItem->child(row);
if (childItem) return createIndex(row, column, quintptr(childItem->getId()));
- return QModelIndex();
+ return {};
}
QModelIndex AbstractTreeModel::parent(const QModelIndex &index) const
{
- if (!index.isValid()) return QModelIndex();
+ if (!index.isValid()) return {};
std::shared_ptr childItem = getItemById((int)index.internalId());
std::shared_ptr parentItem = childItem->parentItem().lock();
Q_ASSERT(parentItem);
if (parentItem == rootItem) return QModelIndex();
return createIndex(parentItem->row(), 0, quintptr(parentItem->getId()));
}
int AbstractTreeModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0) return 0;
std::shared_ptr parentItem;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = getItemById((int)parent.internalId());
return parentItem->childCount();
}
QModelIndex AbstractTreeModel::getIndexFromItem(const std::shared_ptr &item) const
{
if (item == rootItem) {
- return QModelIndex();
+ return {};
}
auto parentIndex = getIndexFromItem(item->parentItem().lock());
return index(item->row(), 0, parentIndex);
}
QModelIndex AbstractTreeModel::getIndexFromId(int id) const
{
if (id == rootItem->getId()) {
return QModelIndex();
}
Q_ASSERT(m_allItems.count(id) > 0);
if (auto ptr = m_allItems.at(id).lock()) return getIndexFromItem(ptr);
Q_ASSERT(false);
- return QModelIndex();
+ return {};
}
void AbstractTreeModel::notifyRowAboutToAppend(const std::shared_ptr &item)
{
auto index = getIndexFromItem(item);
beginInsertRows(index, item->childCount(), item->childCount());
}
void AbstractTreeModel::notifyRowAppended(const std::shared_ptr &row)
{
Q_UNUSED(row);
endInsertRows();
}
void AbstractTreeModel::notifyRowAboutToDelete(std::shared_ptr item, int row)
{
auto index = getIndexFromItem(item);
beginRemoveRows(index, row, row);
}
void AbstractTreeModel::notifyRowDeleted()
{
endRemoveRows();
}
// static
int AbstractTreeModel::getNextId()
{
return currentTreeId++;
}
void AbstractTreeModel::registerItem(const std::shared_ptr &item)
{
int id = item->getId();
Q_ASSERT(m_allItems.count(id) == 0);
m_allItems[id] = item;
}
void AbstractTreeModel::deregisterItem(int id, TreeItem *item)
{
Q_UNUSED(item);
Q_ASSERT(m_allItems.count(id) > 0);
m_allItems.erase(id);
}
std::shared_ptr AbstractTreeModel::getItemById(int id) const
{
if (id == rootItem->getId()) {
return rootItem;
}
Q_ASSERT(m_allItems.count(id) > 0);
return m_allItems.at(id).lock();
}
std::shared_ptr AbstractTreeModel::getRoot() const
{
return rootItem;
}
bool AbstractTreeModel::checkConsistency()
{
// first check that the root is all good
if (!rootItem || !rootItem->m_isRoot || !rootItem->isInModel() || m_allItems.count(rootItem->getId()) == 0) {
qDebug() << !rootItem->m_isRoot << !rootItem->isInModel() << (m_allItems.count(rootItem->getId()) == 0);
qDebug() << "ERROR: Model is not valid because root is not properly constructed";
return false;
}
// Then we traverse the tree from the root, checking the infos on the way
std::unordered_set seenIDs;
std::queue>> queue; // store (id, (depth, parentId))
queue.push({rootItem->getId(), {0, rootItem->getId()}});
while (!queue.empty()) {
auto current = queue.front();
int currentId = current.first, currentDepth = current.second.first;
int parentId = current.second.second;
queue.pop();
if (seenIDs.count(currentId) != 0) {
qDebug() << "ERROR: Invalid tree: Id found twice."
<< "It either a cycle or a clash in id attribution";
return false;
}
if (m_allItems.count(currentId) == 0) {
qDebug() << "ERROR: Invalid tree: Id not found. Item is not registered";
return false;
}
auto currentItem = m_allItems[currentId].lock();
if (currentItem->depth() != currentDepth) {
qDebug() << "ERROR: Invalid tree: invalid depth info found";
return false;
}
if (!currentItem->isInModel()) {
qDebug() << "ERROR: Invalid tree: item thinks it is not in a model";
return false;
}
if (currentId != rootItem->getId()) {
if ((currentDepth == 0 || currentItem->m_isRoot)) {
qDebug() << "ERROR: Invalid tree: duplicate root";
return false;
}
if (auto ptr = currentItem->parentItem().lock()) {
if (ptr->getId() != parentId || ptr->child(currentItem->row())->getId() != currentItem->getId()) {
qDebug() << "ERROR: Invalid tree: invalid parent link";
return false;
}
} else {
qDebug() << "ERROR: Invalid tree: invalid parent";
return false;
}
}
// propagate to children
int i = 0;
for (const auto &child : currentItem->m_childItems) {
if (currentItem->child(i) != child) {
qDebug() << "ERROR: Invalid tree: invalid child ordering";
return false;
}
queue.push({child->getId(), {currentDepth + 1, currentId}});
i++;
}
}
return true;
}
Fun AbstractTreeModel::addItem_lambda(const std::shared_ptr &new_item, int parentId)
{
return [this, new_item, parentId]() {
/* Insertion is simply setting the parent of the item.*/
std::shared_ptr parent;
if (parentId != -1) {
parent = getItemById(parentId);
if (!parent) {
Q_ASSERT(parent);
return false;
}
}
return new_item->changeParent(parent);
};
}
Fun AbstractTreeModel::removeItem_lambda(int id)
{
return [this, id]() {
/* Deletion simply deregister clip and remove it from parent.
The actual object is not actually deleted, because a shared_pointer to it
is captured by the reverse operation.
Actual deletions occurs when the undo object is destroyed.
*/
auto item = m_allItems[id].lock();
Q_ASSERT(item);
if (!item) {
return false;
}
auto parent = item->parentItem().lock();
parent->removeChild(item);
return true;
};
}
Fun AbstractTreeModel::moveItem_lambda(int id, int destRow, bool force)
{
Fun lambda = []() { return true; };
std::vector> oldStack;
auto item = getItemById(id);
if (!force && item->row() == destRow) {
// nothing to do
return lambda;
}
if (auto parent = item->parentItem().lock()) {
if (destRow > parent->childCount() || destRow < 0) {
return []() { return false; };
}
int parentId = parent->getId();
// remove the element to move
oldStack.push_back(item);
Fun oper = removeItem_lambda(id);
PUSH_LAMBDA(oper, lambda);
// remove the tail of the stack
for (int i = destRow; i < parent->childCount(); ++i) {
auto current = parent->child(i);
if (current->getId() != id) {
oldStack.push_back(current);
oper = removeItem_lambda(current->getId());
PUSH_LAMBDA(oper, lambda);
}
}
// insert back in order
for (const auto &elem : oldStack) {
oper = addItem_lambda(elem, parentId);
PUSH_LAMBDA(oper, lambda);
}
return lambda;
}
return []() { return false; };
}
diff --git a/src/abstractmodel/abstracttreemodel.hpp b/src/abstractmodel/abstracttreemodel.hpp
index 24fec46ed..7f66e4706 100644
--- a/src/abstractmodel/abstracttreemodel.hpp
+++ b/src/abstractmodel/abstracttreemodel.hpp
@@ -1,126 +1,126 @@
/***************************************************************************
* 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 ABSTRACTTREEMODEL_H
#define ABSTRACTTREEMODEL_H
#include "undohelper.hpp"
#include
#include
#include
/* @brief This class represents a generic tree hierarchy
*/
class TreeItem;
class AbstractTreeModel : public QAbstractItemModel, public std::enable_shared_from_this
{
Q_OBJECT
public:
/* @brief Construct a TreeModel
@param parent is the parent object of the model
@return a ptr to the created object
*/
static std::shared_ptr construct(QObject *parent = nullptr);
protected:
// This is protected. Call construct instead.
explicit AbstractTreeModel(QObject *parent = nullptr);
public:
- virtual ~AbstractTreeModel();
+ ~AbstractTreeModel() override;
/* @brief Given an item from the hierarchy, construct the corresponding ModelIndex */
QModelIndex getIndexFromItem(const std::shared_ptr &item) const;
/* @brief Given an item id, construct the corresponding ModelIndex */
QModelIndex getIndexFromId(int id) const;
/* @brief Return a ptr to an item given its id */
std::shared_ptr getItemById(int id) const;
/* @brief Return a ptr to the root of the tree */
std::shared_ptr getRoot() const;
QVariant data(const QModelIndex &index, int role) const override;
// This is reimplemented to prevent selection of the categories
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
/* @brief Helper function to generate a lambda that adds an item to the tree */
Fun addItem_lambda(const std::shared_ptr &new_item, int parentId);
/* @brief Helper function to generate a lambda that removes an item from the tree */
Fun removeItem_lambda(int id);
/* @brief Helper function to generate a lambda that changes the row of an item */
Fun moveItem_lambda(int id, int destRow, bool force = false);
friend class TreeItem;
friend class AbstractProjectItem;
protected:
/* @brief Register a new item. This is a call-back meant to be called from TreeItem */
virtual void registerItem(const std::shared_ptr &item);
/* @brief Deregister an item. This is a call-back meant to be called from TreeItem */
virtual void deregisterItem(int id, TreeItem *item);
/* @brief Returns the next valid id to give to a new element */
static int getNextId();
/* @brief Send the appropriate notification related to a row that we are appending
@param item is the parent item to which row is appended
*/
void notifyRowAboutToAppend(const std::shared_ptr &item);
/* @brief Send the appropriate notification related to a row that we have appended
@param row is the new element
*/
void notifyRowAppended(const std::shared_ptr &row);
/* @brief Send the appropriate notification related to a row that we are deleting
@param item is the parent of the row being deleted
@param row is the index of the row being deleted
*/
void notifyRowAboutToDelete(std::shared_ptr item, int row);
/* @brief Send the appropriate notification related to a row that we have appended
@param row is the old element
*/
void notifyRowDeleted();
/* @brief This is a convenience function that helps check if the tree is in a valid state */
virtual bool checkConsistency();
protected:
std::shared_ptr rootItem;
std::unordered_map> m_allItems;
static int currentTreeId;
};
#endif
diff --git a/src/abstractmodel/treeitem.cpp b/src/abstractmodel/treeitem.cpp
index b0d97f9f2..51d6b63fe 100644
--- a/src/abstractmodel/treeitem.cpp
+++ b/src/abstractmodel/treeitem.cpp
@@ -1,291 +1,291 @@
/***************************************************************************
* 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 "treeitem.hpp"
#include "abstracttreemodel.hpp"
#include
#include
#include
-TreeItem::TreeItem(const QList &data, const std::shared_ptr &model, bool isRoot, int id)
- : m_itemData(data)
+TreeItem::TreeItem(QList data, const std::shared_ptr &model, bool isRoot, int id)
+ : m_itemData(std::move(data))
, m_model(model)
, m_depth(0)
, m_id(id == -1 ? AbstractTreeModel::getNextId() : id)
, m_isInModel(false)
, m_isRoot(isRoot)
{
}
std::shared_ptr TreeItem::construct(const QList &data, std::shared_ptr model, bool isRoot, int id)
{
std::shared_ptr self(new TreeItem(data, model, isRoot, id));
baseFinishConstruct(self);
return self;
}
// static
void TreeItem::baseFinishConstruct(const std::shared_ptr &self)
{
if (self->m_isRoot) {
registerSelf(self);
}
}
TreeItem::~TreeItem()
{
deregisterSelf();
}
std::shared_ptr TreeItem::appendChild(const QList &data)
{
if (auto ptr = m_model.lock()) {
auto child = construct(data, ptr, false);
appendChild(child);
return child;
}
qDebug() << "ERROR: Something went wrong when appending child in TreeItem. Model is not available anymore";
Q_ASSERT(false);
return std::shared_ptr();
}
bool TreeItem::appendChild(const std::shared_ptr &child)
{
if (hasAncestor(child->getId())) {
// in that case, we are trying to create a cycle, abort
return false;
}
if (auto oldParent = child->parentItem().lock()) {
if (oldParent->getId() == m_id) {
// no change needed
return true;
} else {
// in that case a call to removeChild should have been carried out
qDebug() << "ERROR: trying to append a child that alrealdy has a parent";
return false;
}
}
if (auto ptr = m_model.lock()) {
ptr->notifyRowAboutToAppend(shared_from_this());
child->updateParent(shared_from_this());
int id = child->getId();
auto it = m_childItems.insert(m_childItems.end(), child);
m_iteratorTable[id] = it;
registerSelf(child);
ptr->notifyRowAppended(child);
return true;
}
qDebug() << "ERROR: Something went wrong when appending child in TreeItem. Model is not available anymore";
Q_ASSERT(false);
return false;
}
void TreeItem::moveChild(int ix, const std::shared_ptr &child)
{
if (auto ptr = m_model.lock()) {
auto parentPtr = child->m_parentItem.lock();
if (parentPtr && parentPtr->getId() != m_id) {
parentPtr->removeChild(child);
} else {
// deletion of child
auto it = m_iteratorTable[child->getId()];
m_childItems.erase(it);
}
ptr->notifyRowAboutToAppend(shared_from_this());
child->updateParent(shared_from_this());
int id = child->getId();
auto pos = m_childItems.begin();
std::advance(pos, ix);
auto it = m_childItems.insert(pos, child);
m_iteratorTable[id] = it;
ptr->notifyRowAppended(child);
m_isInModel = true;
} else {
qDebug() << "ERROR: Something went wrong when moving child in TreeItem. Model is not available anymore";
Q_ASSERT(false);
}
}
void TreeItem::removeChild(const std::shared_ptr &child)
{
if (auto ptr = m_model.lock()) {
ptr->notifyRowAboutToDelete(shared_from_this(), child->row());
// get iterator corresponding to child
Q_ASSERT(m_iteratorTable.count(child->getId()) > 0);
auto it = m_iteratorTable[child->getId()];
// deletion of child
m_childItems.erase(it);
// clean iterator table
m_iteratorTable.erase(child->getId());
child->m_depth = 0;
child->m_parentItem.reset();
child->deregisterSelf();
ptr->notifyRowDeleted();
} else {
qDebug() << "ERROR: Something went wrong when removing child in TreeItem. Model is not available anymore";
Q_ASSERT(false);
}
}
bool TreeItem::changeParent(std::shared_ptr newParent)
{
Q_ASSERT(!m_isRoot);
if (m_isRoot) return false;
std::shared_ptr oldParent;
if ((oldParent = m_parentItem.lock())) {
oldParent->removeChild(shared_from_this());
}
bool res = true;
if (newParent) {
res = newParent->appendChild(shared_from_this());
if (res) {
m_parentItem = newParent;
} else if (oldParent) {
// something went wrong, we have to reset the parent.
bool reverse = oldParent->appendChild(shared_from_this());
Q_ASSERT(reverse);
}
}
return res;
}
std::shared_ptr TreeItem::child(int row) const
{
Q_ASSERT(row >= 0 && row < (int)m_childItems.size());
auto it = m_childItems.cbegin();
std::advance(it, row);
return (*it);
}
int TreeItem::childCount() const
{
return (int)m_childItems.size();
}
int TreeItem::columnCount() const
{
return m_itemData.count();
}
QVariant TreeItem::dataColumn(int column) const
{
return m_itemData.value(column);
}
void TreeItem::setData(int column, const QVariant &dataColumn)
{
m_itemData[column] = dataColumn;
}
std::weak_ptr TreeItem::parentItem() const
{
return m_parentItem;
}
int TreeItem::row() const
{
if (auto ptr = m_parentItem.lock()) {
// we compute the distance in the parent's children list
auto it = ptr->m_childItems.begin();
return (int)std::distance(it, (decltype(it))ptr->m_iteratorTable.at(m_id));
}
return -1;
}
int TreeItem::depth() const
{
return m_depth;
}
int TreeItem::getId() const
{
return m_id;
}
bool TreeItem::isInModel() const
{
return m_isInModel;
}
void TreeItem::registerSelf(const std::shared_ptr &self)
{
for (const auto &child : self->m_childItems) {
registerSelf(child);
}
if (auto ptr = self->m_model.lock()) {
ptr->registerItem(self);
self->m_isInModel = true;
} else {
qDebug() << "Error : construction of treeItem failed because parent model is not available anymore";
Q_ASSERT(false);
}
}
void TreeItem::deregisterSelf()
{
for (const auto &child : m_childItems) {
child->deregisterSelf();
}
if (m_isInModel) {
if (auto ptr = m_model.lock()) {
ptr->deregisterItem(m_id, this);
m_isInModel = false;
}
}
}
bool TreeItem::hasAncestor(int id)
{
if (m_id == id) {
return true;
}
if (auto ptr = m_parentItem.lock()) {
return ptr->hasAncestor(id);
}
return false;
}
bool TreeItem::isRoot() const
{
return m_isRoot;
}
void TreeItem::updateParent(std::shared_ptr parent)
{
m_parentItem = parent;
if (parent) {
m_depth = parent->m_depth + 1;
}
}
std::vector> TreeItem::getLeaves()
{
if (childCount() == 0) {
return {shared_from_this()};
}
std::vector> leaves;
for (const auto &c : m_childItems) {
for (const auto &l : c->getLeaves()) {
leaves.push_back(l);
}
}
return leaves;
}
diff --git a/src/abstractmodel/treeitem.hpp b/src/abstractmodel/treeitem.hpp
index 889c3fe13..1f86eaa3e 100644
--- a/src/abstractmodel/treeitem.hpp
+++ b/src/abstractmodel/treeitem.hpp
@@ -1,189 +1,189 @@
/***************************************************************************
* 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 TREEITEM_H
#define TREEITEM_H
#include "definitions.h"
#include
#include
#include
#include
/* @brief This class is a generic class to represent items of a tree-like model
It works in tandem with AbstractTreeModel or one of its derived classes.
There is a registration mechanism that takes place: each TreeItem holds a unique Id
that can allow to retrieve it directly from the model.
A TreeItem registers itself to the model as soon as it gets a proper parent (the node
above it in the hierarchy). This means that upon creation, the TreeItem is NOT
registered, because at this point it doesn't belong to any parent.
The only exception is for the rootItem, which is always registered.
Note that the root is a special object. In particular, it must stay at the root and
must not be declared as the child of any other item.
*/
class AbstractTreeModel;
class TreeItem : public enable_shared_from_this_virtual
{
public:
/* @brief Construct a TreeItem
@param data List of data elements (columns) of the created item
@param model Pointer to the model to which this elem belongs to
@param parentItem address of the parent if the child is not orphan
@param isRoot is true if the object is the topmost item of the tree
@param id of the newly created item. If left to -1, the id is assigned automatically
@return a ptr to the constructed item
*/
static std::shared_ptr construct(const QList &data, std::shared_ptr model, bool isRoot, int id = -1);
friend class AbstractTreeModel;
protected:
// This is protected. Call construct instead
- explicit TreeItem(const QList &data, const std::shared_ptr &model, bool isRoot, int id = -1);
+ explicit TreeItem(QList data, const std::shared_ptr &model, bool isRoot, int id = -1);
public:
virtual ~TreeItem();
/* @brief Creates a child of the current item
@param data: List of data elements (columns) to init the child with.
*/
std::shared_ptr appendChild(const QList &data);
/* @brief Appends an already created child
Useful for example if the child should be a subclass of TreeItem
@return true on success. Otherwise, nothing is modified.
*/
bool appendChild(const std::shared_ptr &child);
void moveChild(int ix, const std::shared_ptr &child);
/* @brief Remove given child from children list. The parent of the child is updated
accordingly
*/
void removeChild(const std::shared_ptr &child);
/* @brief Change the parent of the current item. Structures are modified accordingly
*/
virtual bool changeParent(std::shared_ptr newParent);
/* @brief Retrieves a child of the current item
@param row is the index of the child to retrieve
*/
std::shared_ptr child(int row) const;
/* @brief Returns a vector containing a pointer to all the leaves in the subtree rooted in this element */
std::vector> getLeaves();
/* @brief Return the number of children */
int childCount() const;
/* @brief Return the number of data fields (columns) */
int columnCount() const;
/* @brief Return the content of a column
@param column Index of the column to look-up
*/
QVariant dataColumn(int column) const;
void setData(int column, const QVariant &dataColumn);
/* @brief Return the index of current item amongst father's children
Returns -1 on error (eg: no parent set)
*/
int row() const;
/* @brief Return a ptr to the parent item
*/
std::weak_ptr parentItem() const;
/* @brief Return the depth of the current item*/
int depth() const;
/* @brief Return the id of the current item*/
int getId() const;
/* @brief Return true if the current item has been registered */
bool isInModel() const;
/* @brief This is similar to the std::accumulate function, except that it
operates on the whole subtree
@param init is the initial value of the operation
@param is the binary op to apply (signature should be (T, shared_ptr)->T)
*/
template T accumulate(T init, BinaryOperation op);
template T accumulate_const(T init, BinaryOperation op) const;
/* @brief Return true if the current item has the item with given id as an ancestor */
bool hasAncestor(int id);
/* @brief Return true if the item thinks it is a root.
Note that it should be consistent with what the model thinks, but it may have been
messed up at some point if someone wrongly constructed the object with isRoot = true */
bool isRoot() const;
protected:
/* @brief Finish construction of object given its pointer
This is a separated function so that it can be called from derived classes */
static void baseFinishConstruct(const std::shared_ptr &self);
/* @brief Helper functions to handle registration / deregistration to the model */
static void registerSelf(const std::shared_ptr &self);
void deregisterSelf();
/* @brief Reflect update of the parent ptr (for example set the correct depth)
This is meant to be overridden in derived classes
@param ptr is the pointer to the new parent
*/
virtual void updateParent(std::shared_ptr parent);
std::list> m_childItems;
std::unordered_map>::iterator>
m_iteratorTable; // this logs the iterator associated which each child id. This allows easy access of a child based on its id.
QList m_itemData;
std::weak_ptr m_parentItem;
std::weak_ptr m_model;
int m_depth;
int m_id;
bool m_isInModel;
bool m_isRoot;
};
template T TreeItem::accumulate(T init, BinaryOperation op)
{
T res = op(init, shared_from_this());
for (const auto &c : m_childItems) {
res = c->accumulate(res, op);
}
return res;
}
template T TreeItem::accumulate_const(T init, BinaryOperation op) const
{
T res = op(init, shared_from_this());
for (const auto &c : m_childItems) {
res = c->accumulate_const(res, op);
}
return res;
}
#endif
diff --git a/src/assets/abstractassetsrepository.hpp b/src/assets/abstractassetsrepository.hpp
index 397701586..87d3c53a0 100644
--- a/src/assets/abstractassetsrepository.hpp
+++ b/src/assets/abstractassetsrepository.hpp
@@ -1,121 +1,122 @@
/***************************************************************************
* 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 ASSETSREPOSITORY_H
#define ASSETSREPOSITORY_H
#include "definitions.h"
#include
#include
#include
#include
#include
#include
/** @brief This class is the base class for assets (transitions or effets) repositories
*/
template class AbstractAssetsRepository
{
public:
AbstractAssetsRepository();
- virtual ~AbstractAssetsRepository(){};
+ virtual ~AbstractAssetsRepository() = default;
+ ;
/* @brief Returns true if a given asset exists
*/
bool exists(const QString &assetId) const;
/* @brief Returns a vector of pair (asset id, asset name)
*/
QVector> getNames() const;
/* @brief Return type of asset */
AssetType getType(const QString &assetId) const;
/* @brief Return name of asset */
Q_INVOKABLE QString getName(const QString &assetId) const;
/* @brief Return description of asset */
QString getDescription(const QString &assetId) const;
/* @brief Set an asset as favorite (or not)*/
virtual void setFavorite(const QString &assetId, bool favorite) = 0;
/* @brief Returns a DomElement representing the asset's properties */
QDomElement getXml(const QString &assetId) const;
protected:
struct Info
{
QString id; // identifier of the asset
QString mltId; //"tag" of the asset, that is the name of the mlt service
QString name, description, author, version_str;
int version;
QDomElement xml;
AssetType type;
};
// Reads the blacklist file and populate appropriate structure
void parseBlackList(const QString &path);
void init();
virtual Mlt::Properties *retrieveListFromMlt() const = 0;
virtual void parseFavorites() = 0;
/* @brief Parse some info from a mlt structure
@param res Datastructure to fill
@return true on success
*/
bool parseInfoFromMlt(const QString &effectId, Info &res);
/* @brief Returns the metadata associated with the given asset*/
virtual Mlt::Properties *getMetadata(const QString &assetId) = 0;
/* @brief Parse one asset from its XML content
@param res data structure to fill
@return true of success
*/
bool parseInfoFromXml(const QDomElement ¤tAsset, Info &res) const;
/* @brief Figure what is the type of the asset based on its metadata and store it in res*/
virtual void parseType(QScopedPointer &metadata, Info &res) = 0;
/* @brief Retrieves additional info about asset from a custom XML file
The resulting assets are stored in customAssets
*/
virtual void parseCustomAssetFile(const QString &file_name, std::unordered_map &customAssets) const = 0;
/* @brief Returns the path to custom XML description of the assets*/
virtual QStringList assetDirs() const = 0;
/* @brief Returns the path to the assets' blacklist*/
virtual QString assetBlackListPath() const = 0;
std::unordered_map m_assets;
QSet m_blacklist;
QSet m_favorites;
};
#include "abstractassetsrepository.ipp"
#endif
diff --git a/src/assets/abstractassetsrepository.ipp b/src/assets/abstractassetsrepository.ipp
index 50f16d7fb..bcde738a6 100644
--- a/src/assets/abstractassetsrepository.ipp
+++ b/src/assets/abstractassetsrepository.ipp
@@ -1,315 +1,315 @@
/***************************************************************************
* 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 "xml/xml.hpp"
#include
#include
#include
#include
#include
#include
#ifdef Q_OS_MAC
#include
#endif
-template AbstractAssetsRepository::AbstractAssetsRepository() {}
+template AbstractAssetsRepository::AbstractAssetsRepository() = default;
template void AbstractAssetsRepository::init()
{
// Warning: Mlt::Factory::init() resets the locale to the default system value, make sure we keep correct locale
#ifndef Q_OS_MAC
setlocale(LC_NUMERIC, nullptr);
#else
setlocale(LC_NUMERIC_MASK, nullptr);
#endif
// Parse effects blacklist
parseBlackList(assetBlackListPath());
parseFavorites();
// Retrieve the list of MLT's available assets.
QScopedPointer assets(retrieveListFromMlt());
int max = assets->count();
QString sox = QStringLiteral("sox.");
for (int i = 0; i < max; ++i) {
Info info;
QString name = assets->get_name(i);
info.id = name;
if (name.startsWith(sox)) {
// sox effects are not usage directly (parameters not available)
continue;
}
// qDebug() << "trying to parse " < customAssets;
for (const auto &dir : asset_dirs) {
QDir current_dir(dir);
QStringList filter;
filter << QStringLiteral("*.xml");
QStringList fileList = current_dir.entryList(filter, QDir::Files);
for (const auto &file : fileList) {
QString path = current_dir.absoluteFilePath(file);
parseCustomAssetFile(path, customAssets);
}
}
// We add the custom assets
for (const auto &custom : customAssets) {
// Custom assets should override default ones
m_assets[custom.first] = custom.second;
/*if (m_assets.count(custom.second.mltId) > 0) {
m_assets.erase(custom.second.mltId);
}
if (m_assets.count(custom.first) == 0) {
m_assets[custom.first] = custom.second;
} else {
qDebug() << "Error: conflicting asset name " << custom.first;
}*/
}
}
template void AbstractAssetsRepository::parseBlackList(const QString &path)
{
QFile blacklist_file(path);
if (blacklist_file.open(QIODevice::ReadOnly)) {
QTextStream stream(&blacklist_file);
QString line;
while (stream.readLineInto(&line)) {
line = line.simplified();
if (!line.isEmpty() && !line.startsWith('#')) {
m_blacklist.insert(line);
}
}
blacklist_file.close();
}
}
template bool AbstractAssetsRepository::parseInfoFromMlt(const QString &assetId, Info &res)
{
QScopedPointer metadata(getMetadata(assetId));
if (metadata && metadata->is_valid()) {
if (metadata->get("title") && metadata->get("identifier") && strlen(metadata->get("title")) > 0) {
res.name = metadata->get("title");
res.name[0] = res.name[0].toUpper();
res.description = metadata->get("description");
res.author = metadata->get("creator");
res.version_str = metadata->get("version");
res.version = ceil(100 * metadata->get_double("version"));
res.id = res.mltId = assetId;
parseType(metadata, res);
// Create params
QDomDocument doc;
QDomElement eff = doc.createElement(QStringLiteral("effect"));
QString id = metadata->get("identifier");
eff.setAttribute(QStringLiteral("tag"), id);
eff.setAttribute(QStringLiteral("id"), id);
////qCDebug(KDENLIVE_LOG)<<"Effect: "<get_data("parameters"));
for (int j = 0; param_props.is_valid() && j < param_props.count(); ++j) {
QDomElement params = doc.createElement(QStringLiteral("parameter"));
Mlt::Properties paramdesc((mlt_properties) param_props.get_data(param_props.get_name(j)));
params.setAttribute(QStringLiteral("name"), paramdesc.get("identifier"));
if (params.attribute(QStringLiteral("name")) == QLatin1String("argument")) {
// This parameter has to be given as attribute when using command line, do not show it in Kdenlive
continue;
}
if (paramdesc.get("readonly") && !strcmp(paramdesc.get("readonly"), "yes")) {
// Do not expose readonly parameters
continue;
}
if (paramdesc.get("maximum")) {
params.setAttribute(QStringLiteral("max"), paramdesc.get("maximum"));
}
if (paramdesc.get("minimum")) {
params.setAttribute(QStringLiteral("min"), paramdesc.get("minimum"));
}
QString paramType = paramdesc.get("type");
if (paramType == QLatin1String("integer")) {
if (params.attribute(QStringLiteral("min")) == QLatin1String("0") && params.attribute(QStringLiteral("max")) == QLatin1String("1")) {
params.setAttribute(QStringLiteral("type"), QStringLiteral("bool"));
} else {
params.setAttribute(QStringLiteral("type"), QStringLiteral("constant"));
}
} else if (paramType == QLatin1String("float")) {
params.setAttribute(QStringLiteral("type"), QStringLiteral("constant"));
// param type is float, set default decimals to 3
params.setAttribute(QStringLiteral("decimals"), QStringLiteral("3"));
} else if (paramType == QLatin1String("boolean")) {
params.setAttribute(QStringLiteral("type"), QStringLiteral("bool"));
} else if (paramType == QLatin1String("geometry")) {
params.setAttribute(QStringLiteral("type"), QStringLiteral("geometry"));
} else if (paramType == QLatin1String("string")) {
// string parameter are not really supported, so if we have a default value, enforce it
params.setAttribute(QStringLiteral("type"), QStringLiteral("fixed"));
if (paramdesc.get("default")) {
QString stringDefault = paramdesc.get("default");
stringDefault.remove(QLatin1Char('\''));
params.setAttribute(QStringLiteral("value"), stringDefault);
} else {
// String parameter without default, skip it completely
continue;
}
} else {
params.setAttribute(QStringLiteral("type"), paramType);
if (!QString(paramdesc.get("format")).isEmpty()) {
params.setAttribute(QStringLiteral("format"), paramdesc.get("format"));
}
}
if (!params.hasAttribute(QStringLiteral("value"))) {
if (paramdesc.get("default")) {
params.setAttribute(QStringLiteral("default"), paramdesc.get("default"));
}
if (paramdesc.get("value")) {
params.setAttribute(QStringLiteral("value"), paramdesc.get("value"));
} else {
params.setAttribute(QStringLiteral("value"), paramdesc.get("default"));
}
}
QString paramName = paramdesc.get("title");
if (!paramName.isEmpty()) {
QDomElement pname = doc.createElement(QStringLiteral("name"));
pname.appendChild(doc.createTextNode(paramName));
params.appendChild(pname);
}
if (paramdesc.get("description")) {
QDomElement comment = doc.createElement(QStringLiteral("comment"));
comment.appendChild(doc.createTextNode(paramdesc.get("description")));
params.appendChild(comment);
}
eff.appendChild(params);
}
doc.appendChild(eff);
res.xml = eff;
return true;
}
}
return false;
}
template bool AbstractAssetsRepository::exists(const QString &assetId) const
{
return m_assets.count(assetId) > 0;
}
template QVector> AbstractAssetsRepository::getNames() const
{
QVector> res;
res.reserve((int)m_assets.size());
for (const auto &asset : m_assets) {
res.push_back({asset.first, asset.second.name});
}
std::sort(res.begin(), res.end(), [](const QPair &a, const QPair &b) { return a.second < b.second; });
return res;
}
template AssetType AbstractAssetsRepository::getType(const QString &assetId) const
{
Q_ASSERT(m_assets.count(assetId) > 0);
return m_assets.at(assetId).type;
}
template QString AbstractAssetsRepository::getName(const QString &assetId) const
{
Q_ASSERT(m_assets.count(assetId) > 0);
return m_assets.at(assetId).name;
}
template QString AbstractAssetsRepository::getDescription(const QString &assetId) const
{
Q_ASSERT(m_assets.count(assetId) > 0);
return m_assets.at(assetId).description;
}
template bool AbstractAssetsRepository::parseInfoFromXml(const QDomElement ¤tAsset, Info &res) const
{
QString tag = currentAsset.attribute(QStringLiteral("tag"), QString());
QString id = currentAsset.attribute(QStringLiteral("id"), QString());
if (id.isEmpty()) {
id = tag;
}
if (!exists(tag)) {
qDebug() << "++++++ Unknown asset : " << tag;
return false;
}
// Check if there is a maximal version set
if (currentAsset.hasAttribute(QStringLiteral("version"))) {
// a specific version of the filter is required
if (m_assets.at(tag).version < (int)(100 * currentAsset.attribute(QStringLiteral("version")).toDouble())) {
return false;
}
}
res = m_assets.at(tag);
res.id = id;
res.mltId = tag;
// Update description if the xml provide one
QString description = Xml::getSubTagContent(currentAsset, QStringLiteral("description"));
if (!description.isEmpty()) {
res.description = description;
}
// Update name if the xml provide one
QString name = Xml::getSubTagContent(currentAsset, QStringLiteral("name"));
if (!name.isEmpty()) {
res.name = name;
}
return true;
}
template QDomElement AbstractAssetsRepository::getXml(const QString &assetId) const
{
if (m_assets.count(assetId) == 0) {
qDebug() << "Error : Requesting info on unknown transition " << assetId;
return QDomElement();
}
return m_assets.at(assetId).xml.cloneNode().toElement();
}
diff --git a/src/assets/assetlist/model/assetfilter.cpp b/src/assets/assetlist/model/assetfilter.cpp
index 86a4341fa..52bb300a7 100644
--- a/src/assets/assetlist/model/assetfilter.cpp
+++ b/src/assets/assetlist/model/assetfilter.cpp
@@ -1,176 +1,176 @@
/***************************************************************************
* 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 "assetfilter.hpp"
#include "abstractmodel/abstracttreemodel.hpp"
#include "abstractmodel/treeitem.hpp"
#include "assettreemodel.hpp"
#include
AssetFilter::AssetFilter(QObject *parent)
: QSortFilterProxyModel(parent)
- , m_name_enabled(false)
+
{
setFilterRole(Qt::DisplayRole);
setSortRole(Qt::DisplayRole);
setDynamicSortFilter(false);
}
void AssetFilter::setFilterName(bool enabled, const QString &pattern)
{
m_name_enabled = enabled;
m_name_value = pattern;
invalidateFilter();
if (rowCount() > 1) {
sort(0);
}
}
bool AssetFilter::filterName(const std::shared_ptr &item) const
{
if (!m_name_enabled) {
return true;
}
QString itemText = item->dataColumn(AssetTreeModel::nameCol).toString();
itemText = itemText.normalized(QString::NormalizationForm_D).remove(QRegExp(QStringLiteral("[^a-zA-Z0-9\\s]")));
QString patt = m_name_value.normalized(QString::NormalizationForm_D).remove(QRegExp(QStringLiteral("[^a-zA-Z0-9\\s]")));
return itemText.contains(patt, Qt::CaseInsensitive);
}
bool AssetFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
QModelIndex row = sourceModel()->index(sourceRow, 0, sourceParent);
auto *model = static_cast(sourceModel());
std::shared_ptr item = model->getItemById((int)row.internalId());
if (item->dataColumn(AssetTreeModel::idCol) == QStringLiteral("root")) {
// In that case, we have a category. We hide it if it does not have children.
QModelIndex category = sourceModel()->index(sourceRow, 0, sourceParent);
if (!category.isValid()) {
return false;
}
bool accepted = false;
for (int i = 0; i < sourceModel()->rowCount(category) && !accepted; ++i) {
accepted = filterAcceptsRow(i, category);
}
return accepted;
}
return applyAll(item);
}
bool AssetFilter::isVisible(const QModelIndex &sourceIndex)
{
auto parent = sourceModel()->parent(sourceIndex);
return filterAcceptsRow(sourceIndex.row(), parent);
}
bool AssetFilter::applyAll(std::shared_ptr item) const
{
return filterName(item);
}
QModelIndex AssetFilter::getNextChild(const QModelIndex ¤t)
{
QModelIndex nextItem = current.sibling(current.row() + 1, current.column());
if (!nextItem.isValid()) {
QModelIndex folder = index(current.parent().row() + 1, 0, QModelIndex());
if (!folder.isValid()) {
return current;
}
while (folder.isValid() && rowCount(folder) == 0) {
folder = folder.sibling(folder.row() + 1, folder.column());
}
if (folder.isValid() && rowCount(folder) > 0) {
return index(0, current.column(), folder);
}
nextItem = current;
}
return nextItem;
}
QModelIndex AssetFilter::getPreviousChild(const QModelIndex ¤t)
{
QModelIndex nextItem = current.sibling(current.row() - 1, current.column());
if (!nextItem.isValid()) {
QModelIndex folder = index(current.parent().row() - 1, 0, QModelIndex());
if (!folder.isValid()) {
return current;
}
while (folder.isValid() && rowCount(folder) == 0) {
folder = folder.sibling(folder.row() - 1, folder.column());
}
if (folder.isValid() && rowCount(folder) > 0) {
return index(rowCount(folder) - 1, current.column(), folder);
}
nextItem = current;
}
return nextItem;
}
QModelIndex AssetFilter::firstVisibleItem(const QModelIndex ¤t)
{
if (current.isValid() && isVisible(mapToSource(current))) {
return current;
}
QModelIndex folder = index(0, 0, QModelIndex());
if (!folder.isValid()) {
return current;
}
while (folder.isValid() && rowCount(folder) == 0) {
folder = index(folder.row() + 1, 0, QModelIndex());
}
if (rowCount(folder) > 0) {
return index(0, 0, folder);
}
return current;
}
QModelIndex AssetFilter::getCategory(int catRow)
{
QModelIndex cat = index(catRow, 0, QModelIndex());
return cat;
}
QVariantList AssetFilter::getCategories()
{
QVariantList list;
for (int i = 0; i < sourceModel()->rowCount(); i++) {
QModelIndex cat = getCategory(i);
if (cat.isValid()) {
list << cat;
}
}
return list;
}
QModelIndex AssetFilter::getModelIndex(QModelIndex current)
{
QModelIndex sourceIndex = mapToSource(current);
return sourceIndex; // this returns an integer
}
QModelIndex AssetFilter::getProxyIndex(QModelIndex current)
{
QModelIndex sourceIndex = mapFromSource(current);
return sourceIndex; // this returns an integer
}
diff --git a/src/assets/assetlist/model/assetfilter.hpp b/src/assets/assetlist/model/assetfilter.hpp
index 11172e5a4..9c556a202 100644
--- a/src/assets/assetlist/model/assetfilter.hpp
+++ b/src/assets/assetlist/model/assetfilter.hpp
@@ -1,70 +1,70 @@
/***************************************************************************
* 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 ASSETFILTER_H
#define ASSETFILTER_H
#include
#include
/* @brief This class is used as a proxy model to filter an asset list based on given criterion (name, ...)
*/
class TreeItem;
class AssetFilter : public QSortFilterProxyModel
{
Q_OBJECT
public:
AssetFilter(QObject *parent = nullptr);
/* @brief Manage the name filter
@param enabled whether to enable this filter
@param pattern to match against effects' names
*/
void setFilterName(bool enabled, const QString &pattern);
/** @brief Returns true if the ModelIndex in the source model is visible after filtering
*/
bool isVisible(const QModelIndex &sourceIndex);
/** @brief If we are in favorite view, invalidate filter to refresh. Call this after a favorite has changed
*/
virtual void reloadFilterOnFavorite() = 0;
QVariantList getCategories();
Q_INVOKABLE QModelIndex getNextChild(const QModelIndex ¤t);
Q_INVOKABLE QModelIndex getPreviousChild(const QModelIndex ¤t);
Q_INVOKABLE QModelIndex firstVisibleItem(const QModelIndex ¤t);
Q_INVOKABLE QModelIndex getCategory(int catRow);
Q_INVOKABLE QModelIndex getModelIndex(QModelIndex current);
Q_INVOKABLE QModelIndex getProxyIndex(QModelIndex current);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool filterName(const std::shared_ptr &item) const;
/* @brief Apply all filter and returns true if the object should be kept after filtering */
virtual bool applyAll(std::shared_ptr item) const;
- bool m_name_enabled;
+ bool m_name_enabled{false};
QString m_name_value;
};
#endif
diff --git a/src/assets/assetlist/model/assettreemodel.hpp b/src/assets/assetlist/model/assettreemodel.hpp
index db6af30ca..42a4137c5 100644
--- a/src/assets/assetlist/model/assettreemodel.hpp
+++ b/src/assets/assetlist/model/assettreemodel.hpp
@@ -1,58 +1,58 @@
/***************************************************************************
* 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 ASSETTREEMODEL_H
#define ASSETTREEMODEL_H
#include "abstractmodel/abstracttreemodel.hpp"
/* @brief This class represents an effect hierarchy to be displayed as a tree
*/
class TreeItem;
class QMenu;
class KActionCategory;
class AssetTreeModel : public AbstractTreeModel
{
public:
- explicit AssetTreeModel(QObject *parent = 0);
+ explicit AssetTreeModel(QObject *parent = nullptr);
enum { IdRole = Qt::UserRole + 1, NameRole, FavoriteRole };
// Helper function to retrieve name
QString getName(const QModelIndex &index) const;
// Helper function to retrieve description
QString getDescription(const QModelIndex &index) const;
// Helper function to retrieve if an effect is categorized as favorite
bool isFavorite(const QModelIndex &index) const;
void setFavorite(const QModelIndex &index, bool favorite, bool isEffect);
QHash roleNames() const override;
QVariant data(const QModelIndex &index, int role) const override;
virtual void reloadAssetMenu(QMenu *effectsMenu, KActionCategory *effectActions) = 0;
// for convenience, we store the column of each data field
static int nameCol, idCol, favCol, typeCol;
protected:
};
#endif
diff --git a/src/assets/assetlist/view/assetlistwidget.cpp b/src/assets/assetlist/view/assetlistwidget.cpp
index 980c0a152..918bacc90 100644
--- a/src/assets/assetlist/view/assetlistwidget.cpp
+++ b/src/assets/assetlist/view/assetlistwidget.cpp
@@ -1,109 +1,109 @@
/***************************************************************************
* 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 "assetlistwidget.hpp"
#include "assets/assetlist/model/assetfilter.hpp"
#include "assets/assetlist/model/assettreemodel.hpp"
#include "assets/assetlist/view/qmltypes/asseticonprovider.hpp"
#include
#include
#include
#include
#include
AssetListWidget::AssetListWidget(QWidget *parent)
: QQuickWidget(parent)
- , m_assetIconProvider(nullptr)
+
{
KDeclarative::KDeclarative kdeclarative;
kdeclarative.setDeclarativeEngine(engine());
#if KDECLARATIVE_VERSION >= QT_VERSION_CHECK(5, 45, 0)
kdeclarative.setupEngine(engine());
kdeclarative.setupContext();
#else
kdeclarative.setupBindings();
#endif
}
AssetListWidget::~AssetListWidget()
{
// clear source
setSource(QUrl());
}
void AssetListWidget::setup()
{
setResizeMode(QQuickWidget::SizeRootObjectToView);
engine()->addImageProvider(QStringLiteral("asseticon"), m_assetIconProvider);
setSource(QUrl(QStringLiteral("qrc:/qml/assetList.qml")));
setFocusPolicy(Qt::StrongFocus);
}
void AssetListWidget::reset()
{
setSource(QUrl(QStringLiteral("qrc:/qml/assetList.qml")));
}
QString AssetListWidget::getName(const QModelIndex &index) const
{
return m_model->getName(m_proxyModel->mapToSource(index));
}
bool AssetListWidget::isFavorite(const QModelIndex &index) const
{
return m_model->isFavorite(m_proxyModel->mapToSource(index));
}
void AssetListWidget::setFavorite(const QModelIndex &index, bool favorite, bool isEffect)
{
m_model->setFavorite(m_proxyModel->mapToSource(index), favorite, isEffect);
}
QString AssetListWidget::getDescription(const QModelIndex &index) const
{
return m_model->getDescription(m_proxyModel->mapToSource(index));
}
void AssetListWidget::setFilterName(const QString &pattern)
{
m_proxyModel->setFilterName(!pattern.isEmpty(), pattern);
if (!pattern.isEmpty()) {
QVariantList mapped = m_proxyModel->getCategories();
QMetaObject::invokeMethod(rootObject(), "expandNodes", Qt::DirectConnection, Q_ARG(QVariant, mapped));
}
}
QVariantMap AssetListWidget::getMimeData(const QString &assetId) const
{
QVariantMap mimeData;
mimeData.insert(getMimeType(assetId), assetId);
return mimeData;
}
void AssetListWidget::activate(const QModelIndex &ix)
{
if (!ix.isValid()) {
return;
}
const QString assetId = m_model->data(m_proxyModel->mapToSource(ix), AssetTreeModel::IdRole).toString();
emit activateAsset(getMimeData(assetId));
}
diff --git a/src/assets/assetlist/view/assetlistwidget.hpp b/src/assets/assetlist/view/assetlistwidget.hpp
index 61a90039d..463fd1b08 100644
--- a/src/assets/assetlist/view/assetlistwidget.hpp
+++ b/src/assets/assetlist/view/assetlistwidget.hpp
@@ -1,83 +1,83 @@
/***************************************************************************
* 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 ASSETLISTWIDGET_H
#define ASSETLISTWIDGET_H
#include "effects/effectsrepository.hpp"
#include
#include
/* @brief This class is a generic widget that display the list of available assets
*/
class AssetIconProvider;
class AssetFilter;
class AssetTreeModel;
class AssetListWidget : public QQuickWidget
{
Q_OBJECT
/* @brief Should the descriptive info box be displayed
*/
public:
AssetListWidget(QWidget *parent = Q_NULLPTR);
- virtual ~AssetListWidget();
+ ~AssetListWidget() override;
/* @brief Returns the name of the asset given its model index */
QString getName(const QModelIndex &index) const;
/* @brief Returns true if this effect belongs to favorites */
bool isFavorite(const QModelIndex &index) const;
/* @brief Returns true if this effect belongs to favorites */
void setFavorite(const QModelIndex &index, bool favorite = true, bool isEffect = true);
/* @brief Returns the description of the asset given its model index */
QString getDescription(const QModelIndex &index) const;
/* @brief Sets the pattern against which the assets' names are filtered */
void setFilterName(const QString &pattern);
/*@brief Return mime type used for drag and drop. It can be kdenlive/effect,
kdenlive/composition or kdenlive/transition*/
virtual QString getMimeType(const QString &assetId) const = 0;
QVariantMap getMimeData(const QString &assetId) const;
void activate(const QModelIndex &ix);
/* @brief Rebuild the view by resetting the source. Is there a better way? */
void reset();
protected:
void setup();
std::shared_ptr m_model;
std::unique_ptr m_proxyModel;
// the QmlEngine takes ownership of the image provider
- AssetIconProvider *m_assetIconProvider;
+ AssetIconProvider *m_assetIconProvider{nullptr};
signals:
void activateAsset(const QVariantMap data);
};
#endif
diff --git a/src/assets/assetpanel.cpp b/src/assets/assetpanel.cpp
index 6ea3f0269..c6ff3ecd3 100644
--- a/src/assets/assetpanel.cpp
+++ b/src/assets/assetpanel.cpp
@@ -1,360 +1,360 @@
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
***************************************************************************/
#include "assetpanel.hpp"
#include "core.h"
#include "definitions.h"
#include "effects/effectstack/model/effectitemmodel.hpp"
#include "effects/effectstack/model/effectstackmodel.hpp"
#include "effects/effectstack/view/effectstackview.hpp"
#include "kdenlivesettings.h"
#include "model/assetparametermodel.hpp"
#include "transitions/transitionsrepository.hpp"
#include "transitions/view/transitionstackview.hpp"
#include "view/assetparameterview.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
AssetPanel::AssetPanel(QWidget *parent)
: QWidget(parent)
, m_lay(new QVBoxLayout(this))
, m_assetTitle(new KSqueezedTextLabel(this))
, m_container(new QWidget(this))
, m_transitionWidget(new TransitionStackView(this))
, m_effectStackWidget(new EffectStackView(this))
{
- QToolBar *buttonToolbar = new QToolBar(this);
+ auto *buttonToolbar = new QToolBar(this);
buttonToolbar->addWidget(m_assetTitle);
int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
QSize iconSize(size, size);
buttonToolbar->setIconSize(iconSize);
// spacer
QWidget *empty = new QWidget();
empty->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
buttonToolbar->addWidget(empty);
m_switchBuiltStack = new QToolButton(this);
m_switchBuiltStack->setIcon(QIcon::fromTheme(QStringLiteral("adjustlevels")));
m_switchBuiltStack->setToolTip(i18n("Adjust clip"));
m_switchBuiltStack->setCheckable(true);
m_switchBuiltStack->setChecked(KdenliveSettings::showbuiltstack());
m_switchBuiltStack->setVisible(false);
// connect(m_switchBuiltStack, &QToolButton::toggled, m_effectStackWidget, &EffectStackView::switchBuiltStack);
buttonToolbar->addWidget(m_switchBuiltStack);
m_splitButton = new KDualAction(i18n("Normal view"), i18n("Compare effect"), this);
m_splitButton->setActiveIcon(QIcon::fromTheme(QStringLiteral("view-right-close")));
m_splitButton->setInactiveIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
m_splitButton->setToolTip(i18n("Compare effect"));
m_splitButton->setVisible(false);
connect(m_splitButton, &KDualAction::activeChangedByUser, this, &AssetPanel::processSplitEffect);
buttonToolbar->addAction(m_splitButton);
m_enableStackButton = new KDualAction(i18n("Effects disabled"), i18n("Effects enabled"), this);
m_enableStackButton->setInactiveIcon(QIcon::fromTheme(QStringLiteral("hint")));
m_enableStackButton->setActiveIcon(QIcon::fromTheme(QStringLiteral("visibility")));
connect(m_enableStackButton, &KDualAction::activeChangedByUser, this, &AssetPanel::enableStack);
m_enableStackButton->setVisible(false);
buttonToolbar->addAction(m_enableStackButton);
m_timelineButton = new KDualAction(i18n("Hide keyframes"), i18n("Display keyframes in timeline"), this);
m_timelineButton->setInactiveIcon(QIcon::fromTheme(QStringLiteral("adjustlevels")));
m_timelineButton->setActiveIcon(QIcon::fromTheme(QStringLiteral("adjustlevels")));
m_timelineButton->setToolTip(i18n("Display keyframes in timeline"));
m_timelineButton->setVisible(false);
connect(m_timelineButton, &KDualAction::activeChangedByUser, this, &AssetPanel::showKeyframes);
buttonToolbar->addAction(m_timelineButton);
m_lay->addWidget(buttonToolbar);
m_lay->setContentsMargins(0, 0, 0, 0);
m_lay->setSpacing(0);
- QVBoxLayout *lay = new QVBoxLayout(m_container);
+ auto *lay = new QVBoxLayout(m_container);
lay->setContentsMargins(0, 0, 0, 0);
lay->addWidget(m_transitionWidget);
lay->addWidget(m_effectStackWidget);
- QScrollArea *sc = new QScrollArea;
+ auto *sc = new QScrollArea;
sc->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
sc->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
sc->setFrameStyle(QFrame::NoFrame);
sc->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding));
m_container->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding));
sc->setWidgetResizable(true);
m_lay->addWidget(sc);
sc->setWidget(m_container);
m_transitionWidget->setVisible(false);
m_effectStackWidget->setVisible(false);
updatePalette();
connect(m_effectStackWidget, &EffectStackView::seekToPos, this, &AssetPanel::seekToPos);
connect(m_effectStackWidget, &EffectStackView::reloadEffect, this, &AssetPanel::reloadEffect);
connect(m_transitionWidget, &TransitionStackView::seekToTransPos, this, &AssetPanel::seekToPos);
connect(m_effectStackWidget, &EffectStackView::updateEnabledState, [this]() { m_enableStackButton->setActive(m_effectStackWidget->isStackEnabled()); });
}
void AssetPanel::showTransition(int tid, const std::shared_ptr &transitionModel)
{
Q_UNUSED(tid)
ObjectId id = transitionModel->getOwnerId();
if (m_transitionWidget->stackOwner() == id) {
// already on this effect stack, do nothing
return;
}
clear();
QString transitionId = transitionModel->getAssetId();
QString transitionName = TransitionsRepository::get()->getName(transitionId);
m_assetTitle->setText(i18n("%1 properties", transitionName));
m_transitionWidget->setVisible(true);
m_timelineButton->setVisible(true);
m_enableStackButton->setVisible(false);
m_transitionWidget->setModel(transitionModel, QSize(), true);
}
void AssetPanel::showEffectStack(const QString &itemName, const std::shared_ptr &effectsModel, QSize frameSize, bool showKeyframes)
{
m_splitButton->setActive(false);
if (effectsModel == nullptr) {
// Item is not ready
m_splitButton->setVisible(false);
m_enableStackButton->setVisible(false);
clear();
return;
}
ObjectId id = effectsModel->getOwnerId();
if (m_effectStackWidget->stackOwner() == id) {
// already on this effect stack, do nothing
return;
}
clear();
QString title;
bool showSplit = false;
bool enableKeyframes = false;
switch (id.first) {
case ObjectType::TimelineClip:
title = i18n("%1 effects", itemName);
showSplit = true;
enableKeyframes = true;
break;
case ObjectType::TimelineComposition:
title = i18n("%1 parameters", itemName);
enableKeyframes = true;
break;
case ObjectType::TimelineTrack:
title = i18n("Track %1 effects", itemName);
// TODO: track keyframes
// enableKeyframes = true;
break;
case ObjectType::BinClip:
title = i18n("Bin %1 effects", itemName);
showSplit = true;
break;
default:
title = itemName;
break;
}
m_assetTitle->setText(title);
m_splitButton->setVisible(showSplit);
m_enableStackButton->setVisible(id.first != ObjectType::TimelineComposition);
m_enableStackButton->setActive(effectsModel->isStackEnabled());
if (showSplit) {
m_splitButton->setEnabled(effectsModel->rowCount() > 0);
QObject::connect(effectsModel.get(), &EffectStackModel::dataChanged, [&]() {
if (m_effectStackWidget->isEmpty()) {
m_splitButton->setActive(false);
}
m_splitButton->setEnabled(!m_effectStackWidget->isEmpty());
});
}
m_timelineButton->setVisible(enableKeyframes);
m_timelineButton->setActive(showKeyframes);
// Disable built stack until properly implemented
// m_switchBuiltStack->setVisible(true);
m_effectStackWidget->setVisible(true);
m_effectStackWidget->setModel(effectsModel, frameSize);
}
void AssetPanel::clearAssetPanel(int itemId)
{
ObjectId id = m_effectStackWidget->stackOwner();
if (id.first == ObjectType::TimelineClip && id.second == itemId) {
clear();
} else {
id = m_transitionWidget->stackOwner();
if (id.first == ObjectType::TimelineComposition && id.second == itemId) {
clear();
}
}
}
void AssetPanel::clear()
{
m_transitionWidget->setVisible(false);
m_transitionWidget->unsetModel();
m_effectStackWidget->setVisible(false);
m_splitButton->setVisible(false);
m_timelineButton->setVisible(false);
m_switchBuiltStack->setVisible(false);
m_effectStackWidget->unsetModel();
m_assetTitle->setText(QString());
}
void AssetPanel::updatePalette()
{
QString styleSheet = getStyleSheet();
setStyleSheet(styleSheet);
m_transitionWidget->setStyleSheet(styleSheet);
m_effectStackWidget->setStyleSheet(styleSheet);
}
// static
const QString AssetPanel::getStyleSheet()
{
KColorScheme scheme(QApplication::palette().currentColorGroup(), KColorScheme::View);
QColor selected_bg = scheme.decoration(KColorScheme::FocusColor).color();
QColor hgh = KColorUtils::mix(QApplication::palette().window().color(), selected_bg, 0.2);
QColor hover_bg = scheme.decoration(KColorScheme::HoverColor).color();
QColor light_bg = scheme.shade(KColorScheme::LightShade);
QColor alt_bg = scheme.background(KColorScheme::NormalBackground).color();
QString stylesheet;
// effect background
stylesheet.append(QStringLiteral("QFrame#decoframe {border-bottom:2px solid "
"palette(mid);background: transparent} QFrame#decoframe[active=\"true\"] {background: %1;}")
.arg(hgh.name()));
// effect in group background
stylesheet.append(
QStringLiteral("QFrame#decoframesub {border-top:1px solid palette(light);} QFrame#decoframesub[active=\"true\"] {background: %1;}").arg(hgh.name()));
// group background
stylesheet.append(QStringLiteral("QFrame#decoframegroup {border:2px solid palette(dark);margin:0px;margin-top:2px;} "));
// effect title bar
stylesheet.append(QStringLiteral("QFrame#frame {margin-bottom:2px;} QFrame#frame[target=\"true\"] "
"{background: palette(highlight);}"));
// group effect title bar
stylesheet.append(QStringLiteral("QFrame#framegroup {background: palette(dark);} "
"QFrame#framegroup[target=\"true\"] {background: palette(highlight);} "));
// draggable effect bar content
stylesheet.append(QStringLiteral("QProgressBar::chunk:horizontal {background: palette(button);border-top-left-radius: 4px;border-bottom-left-radius: 4px;} "
"QProgressBar::chunk:horizontal#dragOnly {background: %1;border-top-left-radius: 4px;border-bottom-left-radius: 4px;} "
"QProgressBar::chunk:horizontal:hover {background: %2;}")
.arg(alt_bg.name(), selected_bg.name()));
// draggable effect bar
stylesheet.append(QStringLiteral("QProgressBar:horizontal {border: 1px solid palette(dark);border-top-left-radius: 4px;border-bottom-left-radius: "
"4px;border-right:0px;background:%3;padding: 0px;text-align:left center} QProgressBar:horizontal:disabled {border: 1px "
"solid palette(button)} QProgressBar:horizontal#dragOnly {background: %3} QProgressBar:horizontal[inTimeline=\"true\"] { "
"border: 1px solid %1;border-right: 0px;background: %2;padding: 0px;text-align:left center } "
"QProgressBar::chunk:horizontal[inTimeline=\"true\"] {background: %1;}")
.arg(hover_bg.name(), light_bg.name(), alt_bg.name()));
// spin box for draggable widget
stylesheet.append(
QStringLiteral("QAbstractSpinBox#dragBox {border: 1px solid palette(dark);border-top-right-radius: 4px;border-bottom-right-radius: "
"4px;padding-right:0px;} QAbstractSpinBox::down-button#dragBox {width:0px;padding:0px;} QAbstractSpinBox:disabled#dragBox {border: 1px "
"solid palette(button);} QAbstractSpinBox::up-button#dragBox {width:0px;padding:0px;} QAbstractSpinBox[inTimeline=\"true\"]#dragBox { "
"border: 1px solid %1;} QAbstractSpinBox:hover#dragBox {border: 1px solid %2;} ")
.arg(hover_bg.name(), selected_bg.name()));
// group editable labels
stylesheet.append(QStringLiteral("MyEditableLabel { background-color: transparent; color: palette(bright-text); border-radius: 2px;border: 1px solid "
"transparent;} MyEditableLabel:hover {border: 1px solid palette(highlight);} "));
// transparent qcombobox
stylesheet.append(QStringLiteral("QComboBox { background-color: transparent;} "));
return stylesheet;
}
void AssetPanel::processSplitEffect(bool enable)
{
ObjectType id = m_effectStackWidget->stackOwner().first;
if (id == ObjectType::TimelineClip) {
emit doSplitEffect(enable);
} else if (id == ObjectType::BinClip) {
emit doSplitBinEffect(enable);
}
}
void AssetPanel::showKeyframes(bool enable)
{
if (m_transitionWidget->isVisible()) {
pCore->showClipKeyframes(m_transitionWidget->stackOwner(), enable);
} else {
pCore->showClipKeyframes(m_effectStackWidget->stackOwner(), enable);
}
}
ObjectId AssetPanel::effectStackOwner()
{
if (m_transitionWidget->isVisible()) {
return m_transitionWidget->stackOwner();
}
if (!m_effectStackWidget->isVisible()) {
return ObjectId(ObjectType::NoItem, -1);
}
return m_effectStackWidget->stackOwner();
}
void AssetPanel::parameterChanged(QString name, int value)
{
Q_UNUSED(name)
emit changeSpeed(value);
}
bool AssetPanel::addEffect(const QString &effectId)
{
if (!m_effectStackWidget->isVisible()) {
return false;
}
return m_effectStackWidget->addEffect(effectId);
}
void AssetPanel::enableStack(bool enable)
{
if (!m_effectStackWidget->isVisible()) {
return;
}
m_effectStackWidget->enableStack(enable);
}
void AssetPanel::deleteCurrentEffect()
{
if (m_effectStackWidget->isVisible()) {
m_effectStackWidget->removeCurrentEffect();
}
}
diff --git a/src/assets/keyframes/model/corners/cornershelper.cpp b/src/assets/keyframes/model/corners/cornershelper.cpp
index 83ec196f4..875b2bccd 100644
--- a/src/assets/keyframes/model/corners/cornershelper.cpp
+++ b/src/assets/keyframes/model/corners/cornershelper.cpp
@@ -1,99 +1,99 @@
/*
Copyright (C) 2018 Jean-Baptiste Mardelle