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

Commit 046e20b0 authored by Nicolas Carion's avatar Nicolas Carion

filewatcher now belongs to projectItemModel and takes undoable insert/remove into account

parent 1eeaa528
......@@ -1111,7 +1111,6 @@ void Bin::setDocument(KdenliveDoc *project)
// Cleanup previous project
m_itemModel->clean();
m_fileWatcher.clear();
delete m_itemView;
m_itemView = nullptr;
m_doc = project;
......@@ -1767,15 +1766,6 @@ void Bin::slotRemoveInvalidClip(const QString &id, bool replace, const QString &
emit requesteInvalidRemoval(id, clip->url(), errorMessage);
}
void Bin::addWatchFile(const QString &binId, const QString &url)
{
m_fileWatcher.addFile(binId, url);
}
void Bin::removeWatchFile(const QString &binId, const QString &url)
{
m_fileWatcher.removeFile(binId, url);
}
// TODO refac cleanup
/*
......
......@@ -24,7 +24,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define KDENLIVE_BIN_H
#include "abstractprojectitem.h"
#include "filewatcher.hpp"
#include "timecode.h"
#include <KMessageWidget>
......@@ -277,8 +276,6 @@ public:
QString getCurrentFolder();
/** @brief Save a clip zone as MLT playlist */
void saveZone(const QStringList &info, const QDir &dir);
void addWatchFile(const QString &binId, const QString &url);
void removeWatchFile(const QString &binId, const QString &url);
// TODO refac: remove this and call directly the function in ProjectItemModel
void cleanup();
......@@ -395,7 +392,6 @@ private:
ProjectSortProxyModel *m_proxyModel;
QToolBar *m_toolbar;
KdenliveDoc *m_doc;
FileWatcher m_fileWatcher;
QLineEdit *m_searchLine;
QToolButton *m_addButton;
QMenu *m_extractAudioAction;
......
......@@ -20,57 +20,58 @@
***************************************************************************/
#include "filewatcher.hpp"
#include "bin/bin.h"
#include "core.h"
#include <QDebug>
#include <QFileInfo>
FileWatcher::FileWatcher(QObject *parent)
: QObject(parent)
, m_fileWatcher(new KDirWatch())
{
m_fileWatcher = KDirWatch::self();
// Init clip modification tracker
m_modifiedTimer.setInterval(1500);
connect(m_fileWatcher, &KDirWatch::dirty, this, &FileWatcher::slotUrlModified);
connect(m_fileWatcher, &KDirWatch::deleted, this, &FileWatcher::slotUrlMissing);
connect(m_fileWatcher.get(), &KDirWatch::dirty, this, &FileWatcher::slotUrlModified);
connect(m_fileWatcher.get(), &KDirWatch::deleted, this, &FileWatcher::slotUrlMissing);
connect(&m_modifiedTimer, &QTimer::timeout, this, &FileWatcher::slotProcessModifiedUrls);
}
void FileWatcher::addFile(const QString &binId, const QString &url)
{
if (m_occurences.contains(url)) {
QStringList currentIds = m_occurences.value(url);
if (!currentIds.contains(binId)) {
currentIds << binId;
m_occurences[url] = currentIds;
}
} else {
// Unknown file, add to list
m_occurences.insert(url, QStringList() << binId);
if (url.isEmpty()) {
return;
}
QFileInfo check_file(url);
// check if file exists and if yes: Is it really a file and no directory?
if (!check_file.exists() || !check_file.isFile()) {
return;
}
if (m_occurences.count(url) == 0) {
m_fileWatcher->addFile(url);
}
m_occurences[url].insert(binId);
m_binClipPaths[binId] = url;
}
void FileWatcher::removeFile(const QString &binId, const QString &url)
void FileWatcher::removeFile(const QString &binId)
{
if (!m_occurences.contains(url)) {
if (m_binClipPaths.count(binId) == 0) {
return;
}
QStringList currentIds = m_occurences.value(url);
currentIds.removeAll(binId);
if (currentIds.isEmpty()) {
QString url = m_binClipPaths[binId];
m_occurences[url].erase(binId);
m_binClipPaths.erase(binId);
if (m_occurences[url].empty()) {
m_fileWatcher->removeFile(url);
m_occurences.remove(url);
} else {
m_occurences[url] = currentIds;
m_occurences.erase(url);
}
}
void FileWatcher::slotUrlModified(const QString &path)
{
if (!m_modifiedUrls.contains(path)) {
m_modifiedUrls << path;
const QStringList ids = m_occurences.value(path);
for (const QString &id : ids) {
pCore->bin()->setWaitingStatus(id);
if (m_modifiedUrls.count(path) == 0) {
m_modifiedUrls.insert(path);
for (const QString &id : m_occurences[path]) {
emit binClipWaiting(id);
}
}
if (!m_modifiedTimer.isActive()) {
......@@ -81,7 +82,7 @@ void FileWatcher::slotUrlModified(const QString &path)
void FileWatcher::slotUrlMissing(const QString &path)
{
// TODO handle missing clips by replacing producer with an invalid producer
const QStringList ids = m_occurences.value(path);
// const QStringList ids = m_occurences.value(path);
/*for (const QString &id : ids) {
emit missingClip(id);
}*/
......@@ -89,17 +90,16 @@ void FileWatcher::slotUrlMissing(const QString &path)
void FileWatcher::slotProcessModifiedUrls()
{
QStringList checkList = m_modifiedUrls;
auto checkList = m_modifiedUrls;
for (const QString &path : checkList) {
if (m_fileWatcher->ctime(path).msecsTo(QDateTime::currentDateTime()) > 1000) {
const QStringList ids = m_occurences.value(path);
for (const QString &id : ids) {
pCore->bin()->reloadClip(id);
for (const QString &id : m_occurences[path]) {
emit binClipModified(id);
}
m_modifiedUrls.removeAll(path);
m_modifiedUrls.erase(path);
}
}
if (m_modifiedUrls.isEmpty()) {
if (m_modifiedUrls.empty()) {
m_modifiedTimer.stop();
}
}
......@@ -107,10 +107,11 @@ void FileWatcher::slotProcessModifiedUrls()
void FileWatcher::clear()
{
m_fileWatcher->stopScan();
const QList<QString> files = m_occurences.keys();
for (const QString &url : files) {
m_fileWatcher->removeFile(url);
for (const auto &f : m_occurences) {
m_fileWatcher->removeFile(f.first);
}
m_occurences.clear();
m_modifiedUrls.clear();
m_binClipPaths.clear();
m_fileWatcher->startScan();
}
......@@ -22,9 +22,11 @@
#ifndef FILEWATCHER_H
#define FILEWATCHER_H
#include "definitions.h"
#include <KDirWatch>
#include <QMap>
#include <QTimer>
#include <unordered_map>
#include <unordered_set>
/** @brief This class is responsible for watching all files used in the project
and triggers a reload notification when a file changes.
......@@ -32,27 +34,41 @@
class FileWatcher : public QObject
{
Q_OBJECT
public:
// Constructor
explicit FileWatcher(QObject *parent = nullptr);
// Add a file to the list of watched items
void addFile(const QString &binId, const QString &url);
// Remove a file from the list of watched items
void removeFile(const QString &binId, const QString &url);
// Remove a binId from the list of watched items
void removeFile(const QString &binId);
// Reset all watched files
void clear();
signals:
/** @brief This signal is triggered whenever the file corresponding to a bin clip has been modified and should be reloaded. Note that this signal is sent no
* more than every 1500 ms. We also make sure that at least 1000ms has passed since the last modification of the file. */
void binClipModified(const QString &binId);
/** @brief Same signal than binClipModified, but triggers immediately. Can be useful to refresh UI without actually reloading the file (yet)*/
void binClipWaiting(const QString &binId);
private slots:
void slotUrlModified(const QString &path);
void slotUrlMissing(const QString &path);
void slotProcessModifiedUrls();
private:
KDirWatch *m_fileWatcher;
// This is a handle to the watcher singleton, not owned by this class.
std::unique_ptr<KDirWatch> m_fileWatcher;
// A list with urls as keys, and the corresponding clip ids as value
QMap<QString, QStringList> m_occurences;
QStringList m_modifiedUrls;
std::unordered_map<QString, std::unordered_set<QString>> m_occurences;
// keys are binId, keys are stored paths
std::unordered_map<QString, QString> m_binClipPaths;
// List of files for which we received an update since the last send
std::unordered_set<QString> m_modifiedUrls;
QTimer m_modifiedTimer;
};
......
......@@ -54,6 +54,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "kdenlive_debug.h"
#include <KLocalizedString>
#include <KMessageBox>
#include <QApplication>
#include <QCryptographicHash>
#include <QDir>
#include <QDomElement>
......@@ -77,10 +78,6 @@ ProjectClip::ProjectClip(const QString &id, const QIcon &thumb, std::shared_ptr<
} else {
m_thumbnail = thumb;
}
if (m_clipType == ClipType::AV || m_clipType == ClipType::Audio || m_clipType == ClipType::Image || m_clipType == ClipType::Video ||
m_clipType == ClipType::Playlist || m_clipType == ClipType::TextTemplate) {
pCore->bin()->addWatchFile(id, clipUrl());
}
// Make sure we have a hash for this clip
hash();
connect(m_markerModel.get(), &MarkerListModel::modelChanged, [&]() { setProducerProperty(QStringLiteral("kdenlive:markers"), m_markerModel->toJson()); });
......@@ -139,10 +136,6 @@ std::shared_ptr<ProjectClip> ProjectClip::construct(const QString &id, const QDo
ProjectClip::~ProjectClip()
{
// controller is deleted in bincontroller
if (m_clipType == ClipType::AV || m_clipType == ClipType::Audio || m_clipType == ClipType::Image || m_clipType == ClipType::Video ||
m_clipType == ClipType::Playlist || m_clipType == ClipType::TextTemplate) {
pCore->bin()->removeWatchFile(clipId(), clipUrl());
}
m_thumbMutex.lock();
m_requestedThumbs.clear();
m_thumbMutex.unlock();
......@@ -379,13 +372,10 @@ bool ProjectClip::setProducer(std::shared_ptr<Mlt::Producer> producer, bool repl
if (auto ptr = m_model.lock()) {
std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()),
AbstractProjectItem::DataDuration);
std::static_pointer_cast<ProjectItemModel>(ptr)->updateWatcher(std::static_pointer_cast<ProjectClip>(shared_from_this()));
}
// Make sure we have a hash for this clip
getFileHash();
if (m_clipType == ClipType::AV || m_clipType == ClipType::Audio || m_clipType == ClipType::Image || m_clipType == ClipType::Video ||
m_clipType == ClipType::Playlist || m_clipType == ClipType::TextTemplate) {
pCore->bin()->addWatchFile(clipId(), clipUrl());
}
// set parent again (some info need to be stored in producer)
updateParent(parentItem().lock());
return true;
......
/*
Copyright (C) 2012 Till Theato <root@ttill.de>
Copyright (C) 2014 Jean-Baptiste Mardelle <jb@kdenlive.org>
Copyright (C) 2017 Nicolas Carion
This file is part of Kdenlive. See www.kdenlive.org.
This program is free software; you can redistribute it and/or
......@@ -25,6 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "binplaylist.hpp"
#include "core.h"
#include "doc/kdenlivedoc.h"
#include "filewatcher.hpp"
#include "jobs/audiothumbjob.hpp"
#include "jobs/jobmanager.h"
#include "jobs/loadjob.hpp"
......@@ -49,12 +51,15 @@ ProjectItemModel::ProjectItemModel(QObject *parent)
: AbstractTreeModel(parent)
, m_lock(QReadWriteLock::Recursive)
, m_binPlaylist(new BinPlaylist())
, m_fileWatcher(new FileWatcher())
, m_nextId(1)
, m_blankThumb()
{
QPixmap pix(QSize(160, 90));
pix.fill(Qt::lightGray);
m_blankThumb.addPixmap(pix);
connect(m_fileWatcher.get(), &FileWatcher::binClipModified, this, &ProjectItemModel::reloadClip);
connect(m_fileWatcher.get(), &FileWatcher::binClipWaiting, this, &ProjectItemModel::setClipWaiting);
}
std::shared_ptr<ProjectItemModel> ProjectItemModel::construct(QObject *parent)
......@@ -385,6 +390,7 @@ void ProjectItemModel::clean()
}
Q_ASSERT(rootItem->childCount() == 0);
m_nextId = 1;
m_fileWatcher->clear();
}
std::shared_ptr<ProjectFolder> ProjectItemModel::getRootFolder() const
......@@ -448,6 +454,10 @@ void ProjectItemModel::registerItem(const std::shared_ptr<TreeItem> &item)
auto clip = std::static_pointer_cast<AbstractProjectItem>(item);
m_binPlaylist->manageBinItemInsertion(clip);
AbstractTreeModel::registerItem(item);
if (clip->itemType() == AbstractProjectItem::ClipItem) {
auto clipItem = std::static_pointer_cast<ProjectClip>(clip);
updateWatcher(clipItem);
}
}
void ProjectItemModel::deregisterItem(int id, TreeItem *item)
{
......@@ -455,6 +465,10 @@ void ProjectItemModel::deregisterItem(int id, TreeItem *item)
m_binPlaylist->manageBinItemDeletion(clip);
// TODO : here, we should suspend jobs belonging to the item we delete. They can be restarted if the item is reinserted by undo
AbstractTreeModel::deregisterItem(id, item);
if (clip->itemType() == AbstractProjectItem::ClipItem) {
auto clipItem = static_cast<ProjectClip *>(clip);
m_fileWatcher->addFile(clipItem->clipId(), clipItem->clipUrl());
}
}
int ProjectItemModel::getFreeFolderId()
......@@ -841,3 +855,28 @@ QMap<QString, QString> ProjectItemModel::getProxies(const QString &root)
{
return m_binPlaylist->getProxies(root);
}
void ProjectItemModel::reloadClip(const QString &binId)
{
std::shared_ptr<ProjectClip> clip = getClipByBinID(binId);
if (clip) {
clip->reloadProducer();
}
}
void ProjectItemModel::setClipWaiting(const QString &binId)
{
std::shared_ptr<ProjectClip> clip = getClipByBinID(binId);
if (clip) {
clip->setClipStatus(AbstractProjectItem::StatusWaiting);
}
}
void ProjectItemModel::updateWatcher(std::shared_ptr<ProjectClip> clipItem)
{
if (clipItem->clipType() == ClipType::AV || clipItem->clipType() == ClipType::Audio || clipItem->clipType() == ClipType::Image ||
clipItem->clipType() == ClipType::Video || clipItem->clipType() == ClipType::Playlist || clipItem->clipType() == ClipType::TextTemplate) {
m_fileWatcher->removeFile(clipItem->clipId());
m_fileWatcher->addFile(clipItem->clipId(), clipItem->clipUrl());
}
}
/*
Copyright (C) 2012 Till Theato <root@ttill.de>
Copyright (C) 2014 Jean-Baptiste Mardelle <jb@kdenlive.org>
Copyright (C) 2017 by Nicolas Carion
Copyright (C) 2017 Nicolas Carion
This file is part of Kdenlive. See www.kdenlive.org.
This program is free software; you can redistribute it and/or
......@@ -34,6 +34,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class AbstractProjectItem;
class BinPlaylist;
class FileWatcher;
class MarkerListModel;
class ProjectClip;
class ProjectFolder;
......@@ -62,24 +63,19 @@ public:
friend class ProjectClip;
/** @brief Returns a clip from the hierarchy, given its id
*/
/** @brief Returns a clip from the hierarchy, given its id */
std::shared_ptr<ProjectClip> getClipByBinID(const QString &binId);
/** @brief Helper to check whether a clip with a given id exists
*/
/** @brief Helper to check whether a clip with a given id exists */
bool hasClip(const QString &binId);
/** @brief Gets a folder by its id. If none is found, the root is returned
*/
/** @brief Gets a folder by its id. If none is found, the root is returned */
std::shared_ptr<ProjectFolder> getFolderByBinId(const QString &binId);
/** @brief Gets any item by its id.
*/
/** @brief Gets any item by its id. */
std::shared_ptr<AbstractProjectItem> getItemByBinId(const QString &binId);
/** @brief This function change the global enabled state of the bin effects
*/
/** @brief This function change the global enabled state of the bin effects */
void setBinEffectsEnabled(bool enabled);
/** @brief Returns some info about the folder containing the given index */
......@@ -190,6 +186,12 @@ public:
/** @brief Retrieve a list of proxy/original urls */
QMap<QString, QString> getProxies(const QString &root);
/** @brief Request that the producer of a given clip is reloaded */
void reloadClip(const QString &binId);
/** @brief Set the status of the clip to "waiting". This happens when the corresponding file has changed*/
void setClipWaiting(const QString &binId);
protected:
/* @brief Register the existence of a new element
*/
......@@ -203,6 +205,9 @@ protected:
/* @brief Helper function to add a given item to the tree */
bool addItem(std::shared_ptr<AbstractProjectItem> item, const QString &parentId, Fun &undo, Fun &redo);
/* @brief Function to be called when the url of a clip changes */
void updateWatcher(std::shared_ptr<ProjectClip> item);
public slots:
/** @brief An item in the list was modified, notify */
void onItemUpdated(std::shared_ptr<AbstractProjectItem> item, int role);
......@@ -219,6 +224,8 @@ private:
std::unique_ptr<BinPlaylist> m_binPlaylist;
std::unique_ptr<FileWatcher> m_fileWatcher;
int m_nextId;
QIcon m_blankThumb;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment