Commit db7214d9 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Fix producer replacement when we want to reload a clip or enable/disable proxy

parent 0fe8482f
......@@ -186,6 +186,9 @@ QVariant AbstractProjectItem::data(DataType type) const
case JobMessage:
data = QVariant(m_jobMessage);
break;
case ClipStatus:
data = QVariant(m_clipStatus);
break;
default:
break;
}
......@@ -241,3 +244,8 @@ QPoint AbstractProjectItem::zone() const
return QPoint();
}
void AbstractProjectItem::setClipStatus(CLIPSTATUS status)
{
m_clipStatus = status;
}
......@@ -27,7 +27,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QObject>
#include <QPixmap>
#include <QUrl>
class ProjectClip;
......@@ -126,7 +125,16 @@ public:
JobType = Qt::UserRole + 1,
JobProgress,
JobMessage,
ClipStatus
};
enum CLIPSTATUS {
StatusReady = 0,
StatusMissing,
StatusWaiting
};
void setClipStatus(AbstractProjectItem::CLIPSTATUS status);
/** @brief Returns the data that describes this item.
* @param type type of data to return
......@@ -170,8 +178,10 @@ protected:
QString m_duration;
QPoint m_zone;
QString m_id;
CLIPSTATUS m_clipStatus;
AbstractClipJob::JOBTYPE m_jobType;
int m_jobProgress;
QString m_jobMessage;
PROJECTITEMTYPE m_itemType;
......
......@@ -232,7 +232,7 @@ void Bin::slotAddClip()
{
// Check if we are in a folder
QStringList folderInfo = getFolderInfo();
pCore->projectManager()->current()->clipManager()->slotAddClip(QString(), folderInfo);
ClipCreationDialogDialog::createClipsCommand(pCore->projectManager()->current(), folderInfo, this);
}
void Bin::deleteClip(const QString &id)
......@@ -264,13 +264,19 @@ void Bin::slotDeleteClip()
void Bin::slotReloadClip()
{
QModelIndex ix = m_proxyModel->selectionModel()->currentIndex();
if (ix.isValid() && m_proxyModel->selectionModel()->isSelected(ix)) {
AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(ix.internalPointer());
QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
foreach (const QModelIndex &ix, indexes) {
if (!ix.isValid()) {
continue;
}
AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(m_proxyModel->mapToSource(ix).internalPointer());
if (currentItem && !currentItem->isFolder()) {
reloadClip(currentItem->clipId());
m_monitor->openClip(NULL);
QDomDocument doc;
QDomElement xml = currentItem->toXml(doc);
currentItem->setClipStatus(AbstractProjectItem::StatusWaiting);
// We need to set a temporary id before all outdated producers are replaced;
pCore->projectManager()->current()->renderer()->getFileProperties(xml, currentItem->clipId(), 150, true);
}
}
......@@ -670,12 +676,11 @@ void Bin::showClipProperties(ProjectClip *clip)
void Bin::reloadClip(const QString &id)
{
/*pCore->projectManager()->current()->binMonitor()->prepareReload(id);
pCore->projectManager()->current()->bin()->reloadClip(id);
if (m_propertiesPanel && m_propertiesPanel->property("clipId").toString() == id) {
m_editedProducer->setProducer(pCore->projectManager()->current()->bin()->clipProducer(id));
}
pCore->projectManager()->current()->binMonitor()->open(pCore->projectManager()->current()->bin()->clipProducer(id));*/
ProjectClip *clip = m_rootFolder->clip(id);
if (!clip) return;
QDomDocument doc;
QDomElement xml = clip->toXml(doc);
pCore->projectManager()->current()->renderer()->getFileProperties(xml, id, 150, true);
}
void Bin::refreshEditedClip()
......@@ -703,12 +708,24 @@ ProjectClip *Bin::getBinClip(const QString &id)
return clip;
}
void Bin::setWaitingStatus(const QString &id)
{
ProjectClip *clip = m_rootFolder->clip(id);
if (clip) clip->setClipStatus(AbstractProjectItem::StatusWaiting);
}
void Bin::slotProducerReady(requestClipInfo info, ClipController *controller)
{
ProjectClip *clip = m_rootFolder->clip(info.clipId);
if (clip) {
clip->setProducer(controller, info.replaceProducer);
emit producerReady(info.clipId);
if (!clip->hasProxy()) {
// Check for file modifications
ClipType t = clip->clipType();
if (t == AV || t == Audio || t == Image || t == Video || t == Playlist) {
m_doc->watchFile(clip->url());
}
}
if (controller) clip->setProducer(controller, info.replaceProducer);
QString currentClip = m_monitor->activeClipId();
if (currentClip.isEmpty()) {
//No clip displayed in monitor, check if item is selected
......@@ -717,13 +734,15 @@ void Bin::slotProducerReady(requestClipInfo info, ClipController *controller)
ProjectClip *currentItem = static_cast<ProjectClip *>(m_proxyModel->mapToSource(ix).internalPointer());
if (currentItem->clipId() == info.clipId) {
// Item was selected, show it in monitor
m_monitor->openClip(controller);
currentItem->setCurrent(true);
break;
}
}
}
else if (currentClip == info.clipId) {
m_monitor->openClip(controller);
m_monitor->openClip(NULL);
clip->setCurrent(true);
//m_monitor->openClip(controller);
}
}
else {
......@@ -743,6 +762,7 @@ void Bin::slotProducerReady(requestClipInfo info, ClipController *controller)
ProjectClip *newItem = new ProjectClip(info.clipId, controller, parentFolder);
if (info.clipId.toInt() >= m_clipCounter) m_clipCounter = info.clipId.toInt() + 1;
}
emit producerReady(info.clipId);
}
void Bin::openProducer(ClipController *controller)
......@@ -814,7 +834,7 @@ JobManager *Bin::jobManager()
void Bin::updateJobStatus(const QString&id, int jobType, int status, const QString &label, const QString &actionName, const QString &details)
{
ProjectClip *clip = getBinClip(id);
ProjectClip *clip = m_rootFolder->clip(id);
if (clip) {
clip->setJobStatus((AbstractClipJob::JOBTYPE) jobType, (ClipJobStatus) status);
}
......@@ -822,7 +842,7 @@ void Bin::updateJobStatus(const QString&id, int jobType, int status, const QStri
void Bin::gotProxy(const QString &id)
{
ProjectClip *clip = getBinClip(id);
ProjectClip *clip = m_rootFolder->clip(id);
if (clip) {
QDomDocument doc;
QDomElement xml = clip->toXml(doc);
......@@ -888,7 +908,7 @@ void Bin::slotItemDropped(QStringList ids, const QModelIndex &parent)
parentItem = m_rootFolder;
}
foreach(const QString &id, ids) {
ProjectClip *currentItem = getBinClip(id);
ProjectClip *currentItem = m_rootFolder->clip(id);
AbstractProjectItem *currentParent = currentItem->parent();
if (currentParent != parentItem) {
// Item was dropped on a different folder
......@@ -902,6 +922,7 @@ void Bin::slotItemDropped(const QList<QUrl>&urls, const QModelIndex &parent)
{
QStringList folderInfo;
if (parent.isValid()) {
// Check if drop occured on a folder
AbstractProjectItem *parentItem = static_cast<AbstractProjectItem *>(parent.internalPointer());
while (!parentItem->isFolder()) {
parentItem = parentItem->parent();
......@@ -915,4 +936,3 @@ void Bin::slotItemDropped(const QList<QUrl>&urls, const QModelIndex &parent)
ClipCreationDialogDialog::createClipsCommand(pCore->projectManager()->current(), urls, folderInfo, this);
}
......@@ -98,6 +98,15 @@ public:
// Draw thumbnail
opt.icon.paint(painter, r);
// Overlay icon if necessary
/* WIP
int clipStatus = index.data(AbstractProjectItem::ClipStatus).toInt();
if (clipStatus == (int) AbstractProjectItem::StatusWaiting) {
QIcon reload = QIcon::fromTheme("media-playback-pause");
reload.paint(painter, r);
}
*/
int decoWidth = r.width() + 2 * textMargin;
QFont font = painter->font();
......@@ -255,6 +264,9 @@ public:
void emitAboutToRemoveItem(AbstractProjectItem* item);
void emitItemRemoved(AbstractProjectItem* item);
void setupMenu(QMenu *addMenu, QAction *defaultAction);
/** @brief The source file was modified, we will reload it soon, disable item in the meantime */
void setWaitingStatus(const QString &id);
/** @brief Update status for clip jobs */
void updateJobStatus(const QString&, int, int, const QString &label = QString(), const QString &actionName = QString(), const QString &details = QString());
......@@ -277,6 +289,9 @@ public:
/** @brief Returns the id of the last inserted clip */
int lastClipId() const;
/** @brief Ask MLT to reload this clip's producer */
void reloadClip(const QString &id);
/** @brief Defines the values for data roles */
enum DATATYPE {
......@@ -302,7 +317,6 @@ private slots:
void closeEditing();
void refreshEditedClip();
void slotMarkersNeedUpdate(const QString &id, const QList <int> &markers);
void reloadClip(const QString &id);
void slotSaveHeaders();
void slotItemDropped(QStringList ids, const QModelIndex &parent);
void slotItemDropped(const QList<QUrl>&urls, const QModelIndex &parent);
......
......@@ -40,10 +40,9 @@ ProjectClip::ProjectClip(const QString &id, ClipController *controller, ProjectF
, audioFrameCache()
, m_audioThumbCreated(false)
{
m_properties = QMap <QString, QString> ();
m_clipStatus = StatusReady;
m_name = m_controller->clipName();
m_duration = m_controller->getStringDuration();
getFileHash();
setParent(parent);
}
......@@ -55,7 +54,7 @@ ProjectClip::ProjectClip(const QDomElement& description, ProjectFolder* parent)
, m_audioThumbCreated(false)
{
Q_ASSERT(description.hasAttribute("id"));
m_properties = QMap <QString, QString> ();
m_clipStatus = StatusWaiting;
QString resource = getXmlProperty(description, "resource");
if (!resource.isEmpty()) {
m_name = QUrl::fromLocalFile(resource).fileName();
......@@ -69,7 +68,7 @@ ProjectClip::ProjectClip(const QDomElement& description, ProjectFolder* parent)
ProjectClip::~ProjectClip()
{
delete m_controller;
// controller is deleted in bincontroller
}
QString ProjectClip::getXmlProperty(const QDomElement &producer, const QString &propertyName)
......@@ -170,7 +169,7 @@ void ProjectClip::reloadProducer()
{
QDomDocument doc;
QDomElement xml = toXml(doc);
bin()->reloadProducer(m_id, doc.documentElement());
bin()->reloadProducer(m_id, xml);
}
void ProjectClip::setCurrent(bool current, bool notify)
......@@ -183,20 +182,11 @@ void ProjectClip::setCurrent(bool current, bool notify)
QDomElement ProjectClip::toXml(QDomDocument& document)
{
QDomElement prod = document.createElement("producer");
document.appendChild(prod);
QDomElement prop = document.createElement("property");
prop.setAttribute("name", "resource");
QString path = m_properties.value("proxy");
if (path.length() < 2) {
// No proxy
path = m_controller->clipUrl().toLocalFile();
if (m_controller) {
m_controller->getProducerXML(document);
return document.documentElement().firstChildElement("producer");
}
QDomText value = document.createTextNode(path);
prop.appendChild(value);
prod.appendChild(prop);
prod.setAttribute("id", m_id);
return prod;
return QDomElement();
}
QPixmap ProjectClip::thumbnail(bool force)
......@@ -237,7 +227,7 @@ void ProjectClip::setProducer(ClipController *controller, bool replaceProducer)
}
if (m_controller) {
// Replace clip for this controller
m_controller->updateProducer(controller->masterProducer());
//m_controller->updateProducer(m_id, &(controller->originalProducer()));
delete controller;
}
else {
......@@ -246,6 +236,7 @@ void ProjectClip::setProducer(ClipController *controller, bool replaceProducer)
if (m_name.isEmpty()) m_name = m_controller->clipName();
m_duration = m_controller->getStringDuration();
}
m_clipStatus = StatusReady;
bin()->emitItemUpdated(this);
getFileHash();
}
......@@ -380,14 +371,9 @@ const QString ProjectClip::getFileHash() const
return QString();
}
const QString ProjectClip::getProperty(const QString &key) const
{
return m_properties.value(key);
}
bool ProjectClip::hasProxy() const
{
QString proxy = m_properties.value("proxy");
QString proxy = getProducerProperty("proxy");
if (proxy.isEmpty() || proxy == "-") return false;
return true;
}
......@@ -398,15 +384,12 @@ void ProjectClip::setProperties(QMap <QString, QString> properties)
bool refreshProducer = false;
QStringList keys;
keys << "luma_duration" << "luma_file" << "fade" << "ttl" << "softness" << "crop" << "animation";
QString oldProxy = m_properties.value("proxy");
while (i.hasNext()) {
i.next();
setProducerProperty(i.key().toUtf8().data(), i.value().toUtf8().data());
m_properties.insert(i.key(), i.value());
if (clipType() == SlideShow && keys.contains(i.key())) refreshProducer = true;
}
if (properties.contains("proxy")) {
qDebug()<<"/// CLIP UPDATE, ASK PROXY";
QString value = properties.value("proxy");
// If value is "-", that means user manually disabled proxy on this clip
if (value.isEmpty() || value == "-") {
......@@ -414,10 +397,14 @@ void ProjectClip::setProperties(QMap <QString, QString> properties)
if (bin()->hasPendingJob(m_id, AbstractClipJob::PROXYJOB)) {
bin()->discardJobs(m_id, AbstractClipJob::PROXYJOB);
}
else reloadProducer();
else {
reloadProducer();
}
}
else {
bin()->startJob(m_id, AbstractClipJob::PROXYJOB);
// A proxy was requested, make sure to keep original url
setProducerProperty("kdenlive_originalUrl", url().toLocalFile().toUtf8().data());
bin()->startJob(m_id, AbstractClipJob::PROXYJOB);
}
}
//if (refreshProducer) slotRefreshProducer();
......@@ -437,3 +424,4 @@ void ProjectClip::setJobStatus(AbstractClipJob::JOBTYPE jobType, ClipJobStatus s
}
bin()->emitItemUpdated(this);
}
......@@ -136,9 +136,6 @@ public:
/** @brief Set properties on this clip. TODO: should we store all in MLT or use extra m_properties ?. */
void setProperties(QMap <QString, QString> properties);
/** @brief Get a property stored in m_properties. */
const QString getProperty(const QString &key) const;
/** @brief Get an XML property from MLT produced xml. */
static QString getXmlProperty(const QDomElement &producer, const QString &propertyName);
......@@ -165,6 +162,8 @@ public:
/** format is frame -> channel ->bytes */
QMap<int, QMap<int, QByteArray> > audioFrameCache;
bool audioThumbCreated() const;
void setWaitingStatus(const QString &id);
public slots:
//TODO
......@@ -172,14 +171,11 @@ public slots:
void updateAudioThumbnail(const audioByteArray& data);
protected:
QUrl m_url;
bool m_hasLimitedDuration;
private:
//TODO: clip markers
QList <int> m_markers;
//TODO: handle properties inside MLT ?
QMap <QString, QString> m_properties;
/** @brief The Clip controller for this clip. */
ClipController *m_controller;
/** @brief Generate and store file hash if not available. */
......
......@@ -33,6 +33,7 @@ ProjectFolder::ProjectFolder(const QString &id, const QString &name, ProjectFold
{
//loadChildren(description);
m_name = name;
m_clipStatus = StatusReady;
m_thumbnail = QIcon::fromTheme("folder").pixmap(40, 40);
setParent(parent);
}
......
......@@ -30,7 +30,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "project/dialogs/slideshowclip.h"
#include <KMessageBox>
#include <KRecentDirs>
#include "klocalizedstring.h"
#include <QDir>
#include <QUndoStack>
#include <QUndoCommand>
......@@ -149,6 +151,27 @@ void ClipCreationDialogDialog::createTitleClip(KdenliveDoc *doc, QStringList gro
void ClipCreationDialogDialog::createClipsCommand(KdenliveDoc *doc, const QList<QUrl> &urls, QStringList groupInfo, Bin *bin)
{
QUndoCommand *addClips = new QUndoCommand();
//TODO check folders
/*QList < QList<QUrl> > foldersList;
QMimeDatabase db;
foreach(const QUrl & file, list) {
// Check there is no folder here
QMimeType type = db.mimeTypeForUrl(file);
if (type.inherits("inode/directory")) {
// user dropped a folder, import its files
list.removeAll(file);
QDir dir(file.path());
QStringList result = dir.entryList(QDir::Files);
QList <QUrl> folderFiles;
folderFiles << file;
foreach(const QString & path, result) {
folderFiles.append(QUrl::fromLocalFile(dir.absoluteFilePath(path)));
}
if (folderFiles.count() > 1) foldersList.append(folderFiles);
}
}*/
foreach(const QUrl &file, urls) {
QDomDocument xml;
QDomElement prod = xml.createElement("producer");
......@@ -234,3 +257,84 @@ void ClipCreationDialogDialog::createClipsCommand(KdenliveDoc *doc, const QList<
doc->commandStack()->push(addClips);
}
}
void ClipCreationDialogDialog::createClipsCommand(KdenliveDoc *doc, QStringList groupInfo, Bin *bin)
{
QList <QUrl> list;
QString allExtensions = "*"; //getExtensions().join(" ");
const QString dialogFilter = i18n("All Supported Files") + "(" + allExtensions + ");;" + i18n("All Files") + "(*)";
QCheckBox *b = new QCheckBox(i18n("Import image sequence"));
b->setChecked(KdenliveSettings::autoimagesequence());
QCheckBox *c = new QCheckBox(i18n("Transparent background for images"));
c->setChecked(KdenliveSettings::autoimagetransparency());
QFrame *f = new QFrame();
f->setFrameShape(QFrame::NoFrame);
QHBoxLayout *l = new QHBoxLayout;
l->addWidget(b);
l->addWidget(c);
l->addStretch(5);
f->setLayout(l);
QString clipFolder = KRecentDirs::dir(":KdenliveClipFolder");
if (clipFolder.isEmpty()) {
clipFolder = QDir::homePath();
}
QPointer<QFileDialog> d = new QFileDialog(QApplication::activeWindow(), i18n("Open Clips"), clipFolder, dialogFilter);
//TODO: KF5, how to add a custom widget to file dialog
/*QGridLayout *layout = (QGridLayout*)d->layout();
layout->addWidget(f, 0, 0);*/
d->setFileMode(QFileDialog::ExistingFiles);
if (d->exec() == QDialog::Accepted) {
KdenliveSettings::setAutoimagetransparency(c->isChecked());
list = d->selectedUrls();
if (!list.isEmpty()) {
KRecentDirs::add(":KdenliveClipFolder", list.first().adjusted(QUrl::RemoveFilename).path());
}
if (b->isChecked() && list.count() == 1) {
// Check for image sequence
QUrl url = list.at(0);
QString fileName = url.fileName().section('.', 0, -2);
if (fileName.at(fileName.size() - 1).isDigit()) {
KFileItem item(url);
if (item.mimetype().startsWith(QLatin1String("image"))) {
// import as sequence if we found more than one image in the sequence
QStringList list;
QString pattern = SlideshowClip::selectedPath(url, false, QString(), &list);
int count = list.count();
if (count > 1) {
delete d;
/*QStringList groupInfo = getGroup(); */
// get image sequence base name
while (fileName.at(fileName.size() - 1).isDigit()) {
fileName.chop(1);
}
QDomDocument xml;
QDomElement prod = xml.createElement("producer");
xml.appendChild(prod);
prod.setAttribute("name", fileName);
prod.setAttribute("resource", pattern);
prod.setAttribute("in", "0");
QString duration = doc->timecode().reformatSeparators(KdenliveSettings::sequence_duration());
prod.setAttribute("out", QString::number(doc->getFramePos(duration) * count));
prod.setAttribute("ttl", QString::number(doc->getFramePos(duration)));
prod.setAttribute("loop", QString::number(false));
prod.setAttribute("crop", QString::number(false));
prod.setAttribute("fade", QString::number(false));
prod.setAttribute("luma_duration", QString::number(doc->getFramePos(doc->timecode().getTimecodeFromFrames(int(ceil(doc->timecode().fps()))))));
if (!groupInfo.isEmpty()) {
prod.setAttribute("group", groupInfo.at(0));
prod.setAttribute("groupid", groupInfo.at(1));
}
uint id = bin->getFreeClipId();
AddClipCommand *command = new AddClipCommand(doc, xml.documentElement(), QString::number(id), true);
doc->commandStack()->push(command);
return;
}
}
}
}
}
delete d;
if (!list.isEmpty()) {
ClipCreationDialogDialog::createClipsCommand(doc, list, groupInfo, bin);
}
}
\ No newline at end of file
......@@ -43,6 +43,7 @@ public:
static void createSlideshowClip(KdenliveDoc *doc, QStringList groupInfo, Bin *bin);
static void createTitleClip(KdenliveDoc *doc, QStringList groupInfo, QString templatePath, Bin *bin);
static void createClipsCommand(KdenliveDoc *doc, const QList<QUrl> &urls, QStringList groupInfo, Bin *bin);
static void createClipsCommand(KdenliveDoc *doc, QStringList groupInfo, Bin *bin);
};
......
......@@ -96,6 +96,13 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
connect(m_clipManager, SIGNAL(displayMessage(QString,int)), parent, SLOT(slotGotProgressInfo(QString,int)));
bool success = false;
// Init clip modification tracker
m_modifiedTimer.setInterval(1500);
connect(&m_fileWatcher, &KDirWatch::dirty, this, &KdenliveDoc::slotClipModified);
connect(&m_fileWatcher, &KDirWatch::deleted, this, &KdenliveDoc::slotClipMissing);
connect(&m_modifiedTimer, &QTimer::timeout, this, &KdenliveDoc::slotProcessModifiedClips);
// init default document properties
m_documentProperties["zoom"] = '7';
m_documentProperties["verticalzoom"] = '1';
......@@ -1273,8 +1280,16 @@ bool KdenliveDoc::addClipInfo(QDomElement elem, QDomElement orig, const QString
void KdenliveDoc::deleteClip(const QString &clipId)
{
//emit signalDeleteProjectClip(clipId);
qDebug()<<"++++++++++++++++++++++++++++\nDELETING CLIP ID: "<<clipId;
m_clipManager->deleteClip(clipId);
ClipController *controller = pCore->binController()->getController(clipId);
ClipType type = controller->clipType();
QString url = controller->clipUrl().toLocalFile();
if (type != Color && type != SlideShow && !url.isEmpty()) {
m_fileWatcher.removeFile(url);
}
// Delete clip in bin
pCore->bin()->deleteClip(clipId);
// Delete controller and Mlt::Producer
pCore->binController()->removeBinClip(clipId);
}
void KdenliveDoc::addClipList(const QList<QUrl> &urls, const QMap<QString, QString> &data)
......@@ -1913,3 +1928,61 @@ void KdenliveDoc::slotUpdateClipProperties(const QString &id, QMap <QString, QSt
}
}
//TODO put all file watching stuff in own class
void KdenliveDoc::watchFile(const QUrl &url)
{
m_fileWatcher.addFile(url.toLocalFile());
}
void KdenliveDoc::slotClipModified(const QString &path)
{
qDebug() << "// CLIP: " << path << " WAS MODIFIED";
QStringList ids = pCore->binController()->getBinIdsByResource(QUrl::fromLocalFile(path));
foreach (const QString &id, ids) {
if (!m_modifiedClips.contains(id)) {
pCore->bin()->setWaitingStatus(id);
}
m_modifiedClips[id] = QTime::currentTime();
}
if (!m_modifiedTimer.isActive()) m_modifiedTimer.start();
}
void KdenliveDoc::slotClipMissing(const QString &path)
{
qDebug() << "// CLIP: " << path << " WAS MISSING";
QStringList ids = pCore->binController()->getBinIdsByResource(QUrl::fromLocalFile(path));
foreach (const QString &id, ids) {
//TODO handle missing clips by replacing producer with an invalid producer
//emit missingClip(id);
}
}