]> git.sesse.net Git - kdenlive/blob - src/clipmanager.cpp
Merge branch 'master' into next
[kdenlive] / src / clipmanager.cpp
1 /***************************************************************************
2  *   Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20
21 #include "clipmanager.h"
22 #include "commands/addclipcommand.h"
23 #include "kdenlivesettings.h"
24 #include "docclipbase.h"
25 #include "kdenlivedoc.h"
26 #include "abstractclipitem.h"
27 #include "abstractgroupitem.h"
28 #include "titledocument.h"
29 #include "kthumb.h"
30
31 #include <mlt++/Mlt.h>
32
33 #include <KDebug>
34 #include <KMessageBox>
35 #include <KFileDialog>
36 #include <KApplication>
37 #include <kio/netaccess.h>
38
39 #include <QGraphicsItemGroup>
40 #include <QtConcurrentRun>
41
42 #include <KFileMetaInfo>
43
44 ClipManager::ClipManager(KdenliveDoc *doc) :
45     QObject(),
46     m_audioThumbsQueue(),
47     m_doc(doc),
48     m_generatingAudioId(),
49     m_abortThumb(false),
50     m_closing(false)
51 {
52     m_clipIdCounter = 1;
53     m_folderIdCounter = 1;
54     m_modifiedTimer.setInterval(1500);
55     connect(&m_fileWatcher, SIGNAL(dirty(const QString &)), this, SLOT(slotClipModified(const QString &)));
56     connect(&m_fileWatcher, SIGNAL(deleted(const QString &)), this, SLOT(slotClipMissing(const QString &)));
57     connect(&m_fileWatcher, SIGNAL(created(const QString &)), this, SLOT(slotClipAvailable(const QString &)));
58     connect(&m_modifiedTimer, SIGNAL(timeout()), this, SLOT(slotProcessModifiedClips()));
59
60 #if KDE_IS_VERSION(4,5,0)
61     KImageCache::deleteCache("kdenlive-thumbs");
62     pixmapCache = new KImageCache("kdenlive-thumbs", 1000000);
63     pixmapCache->setEvictionPolicy(KSharedDataCache::EvictOldest);
64 #endif
65 }
66
67 ClipManager::~ClipManager()
68 {
69     m_closing = true;
70     m_abortThumb = true;
71     m_thumbsThread.waitForFinished();
72     m_thumbsMutex.lock();
73     m_requestedThumbs.clear();
74     m_thumbsMutex.unlock();
75     m_audioThumbsQueue.clear();
76     m_generatingAudioId.clear();
77     m_thumbsMutex.lock();
78     m_requestedThumbs.clear();
79     m_thumbsMutex.unlock();
80     qDeleteAll(m_clipList);
81     m_clipList.clear();
82 #if KDE_IS_VERSION(4,5,0)
83     delete pixmapCache;
84 #endif
85 }
86
87 void ClipManager::clear()
88 {
89     m_abortThumb = true;
90     m_thumbsThread.waitForFinished();
91     m_thumbsMutex.lock();
92     m_requestedThumbs.clear();
93     m_thumbsMutex.unlock();
94     m_abortThumb = false;
95     m_folderList.clear();
96     m_audioThumbsQueue.clear();
97     m_modifiedClips.clear();
98     qDeleteAll(m_clipList);
99     m_clipList.clear();
100     m_clipIdCounter = 1;
101     m_folderIdCounter = 1;
102 #if KDE_IS_VERSION(4,5,0)
103     pixmapCache->clear();
104 #endif
105 }
106
107 void ClipManager::clearCache()
108 {
109 #if KDE_IS_VERSION(4,5,0)
110     pixmapCache->clear();
111 #endif
112 }
113
114 void ClipManager::requestThumbs(const QString id, QList <int> frames)
115 {
116     m_thumbsMutex.lock();
117     foreach (int frame, frames) {
118         m_requestedThumbs.insertMulti(id, frame);
119     }
120     m_thumbsMutex.unlock();
121     if (!m_thumbsThread.isRunning() && !m_abortThumb) {
122         m_thumbsThread = QtConcurrent::run(this, &ClipManager::slotGetThumbs);
123     }
124 }
125
126 void ClipManager::stopThumbs(const QString &id)
127 {
128     if (m_requestedThumbs.isEmpty() || m_closing) return;
129     m_abortThumb = true;
130     m_thumbsThread.waitForFinished();
131     m_thumbsMutex.lock();
132     m_requestedThumbs.remove(id);
133     m_thumbsMutex.unlock();
134     m_abortThumb = false;
135     if (!m_thumbsThread.isRunning()) {
136         m_thumbsThread = QtConcurrent::run(this, &ClipManager::slotGetThumbs);
137     }
138 }
139
140 void ClipManager::slotGetThumbs()
141 {
142     QMap<QString, int>::iterator i = m_requestedThumbs.begin();
143     while (i != m_requestedThumbs.end() && !m_abortThumb) {
144         QString producerId = i.key();
145         m_thumbsMutex.lock();
146         QList<int> values = m_requestedThumbs.values(producerId);
147         i = m_requestedThumbs.erase(i);
148         m_thumbsMutex.unlock();
149         qSort(values);
150         DocClipBase *clip = getClipById(producerId);
151         if (!clip) continue;
152         while (!values.isEmpty() && clip->thumbProducer() && !m_abortThumb) {
153             clip->thumbProducer()->getThumb(values.takeFirst());
154         }
155     }
156 }
157
158 void ClipManager::checkAudioThumbs()
159 {
160     if (!KdenliveSettings::audiothumbnails()) {
161         if (!m_generatingAudioId.isEmpty()) {
162             DocClipBase *clip = getClipById(m_generatingAudioId);
163             if (clip) clip->slotClearAudioCache();
164         }
165         m_audioThumbsQueue.clear();
166         m_generatingAudioId.clear();
167         return;
168     }
169
170     for (int i = 0; i < m_clipList.count(); i++) {
171         m_audioThumbsQueue.append(m_clipList.at(i)->getId());
172     }
173     if (m_generatingAudioId.isEmpty()) startAudioThumbsGeneration();
174 }
175
176 void ClipManager::askForAudioThumb(const QString &id)
177 {
178     DocClipBase *clip = getClipById(id);
179     if (clip && KdenliveSettings::audiothumbnails()) {
180         m_audioThumbsQueue.append(id);
181         if (m_generatingAudioId.isEmpty()) startAudioThumbsGeneration();
182     }
183 }
184
185 void ClipManager::startAudioThumbsGeneration()
186 {
187     if (!KdenliveSettings::audiothumbnails()) {
188         m_audioThumbsQueue.clear();
189         m_generatingAudioId.clear();
190         return;
191     }
192     if (!m_audioThumbsQueue.isEmpty()) {
193         m_generatingAudioId = m_audioThumbsQueue.takeFirst();
194         DocClipBase *clip = getClipById(m_generatingAudioId);
195         if (!clip || !clip->slotGetAudioThumbs())
196             endAudioThumbsGeneration(m_generatingAudioId);
197     } else {
198         m_generatingAudioId.clear();
199     }
200 }
201
202 void ClipManager::endAudioThumbsGeneration(const QString &requestedId)
203 {
204     if (!KdenliveSettings::audiothumbnails()) {
205         m_audioThumbsQueue.clear();
206         m_generatingAudioId.clear();
207         return;
208     }
209     if (!m_audioThumbsQueue.isEmpty()) {
210         if (m_generatingAudioId == requestedId) {
211             startAudioThumbsGeneration();
212         }
213     } else {
214         m_generatingAudioId.clear();
215     }
216 }
217
218 void ClipManager::setThumbsProgress(const QString &message, int progress)
219 {
220     m_doc->setThumbsProgress(message, progress);
221 }
222
223 QList <DocClipBase*> ClipManager::documentClipList() const
224 {
225     return m_clipList;
226 }
227
228 QMap <QString, QString> ClipManager::documentFolderList() const
229 {
230     return m_folderList;
231 }
232
233 void ClipManager::addClip(DocClipBase *clip)
234 {
235     m_clipList.append(clip);
236     if (clip->clipType() != COLOR && clip->clipType() != SLIDESHOW  && !clip->fileURL().isEmpty()) {
237         // listen for file change
238         //kDebug() << "// LISTEN FOR: " << clip->fileURL().path();
239         m_fileWatcher.addFile(clip->fileURL().path());
240     }
241     const QString id = clip->getId();
242     if (id.toInt() >= m_clipIdCounter) m_clipIdCounter = id.toInt() + 1;
243     const QString gid = clip->getProperty("groupid");
244     if (!gid.isEmpty() && gid.toInt() >= m_folderIdCounter) m_folderIdCounter = gid.toInt() + 1;
245 }
246
247 void ClipManager::slotDeleteClips(QStringList ids)
248 {
249     QUndoCommand *delClips = new QUndoCommand();
250     delClips->setText(i18np("Delete clip", "Delete clips", ids.size()));
251
252     for (int i = 0; i < ids.size(); i++) {
253         DocClipBase *clip = getClipById(ids.at(i));
254         if (clip) {
255             new AddClipCommand(m_doc, clip->toXML(), ids.at(i), false, delClips);
256         }
257     }
258     m_doc->commandStack()->push(delClips);
259 }
260
261 void ClipManager::deleteClip(const QString &clipId)
262 {
263     for (int i = 0; i < m_clipList.count(); i++) {
264         if (m_clipList.at(i)->getId() == clipId) {
265             if (m_clipList.at(i)->clipType() != COLOR && m_clipList.at(i)->clipType() != SLIDESHOW  && !m_clipList.at(i)->fileURL().isEmpty()) {
266                 //if (m_clipList.at(i)->clipType() == IMAGE || m_clipList.at(i)->clipType() == AUDIO || (m_clipList.at(i)->clipType() == TEXT && !m_clipList.at(i)->fileURL().isEmpty())) {
267                 // listen for file change
268                 m_fileWatcher.removeFile(m_clipList.at(i)->fileURL().path());
269             }
270             DocClipBase *clip = m_clipList.takeAt(i);
271             delete clip;
272             clip = NULL;
273             break;
274         }
275     }
276 }
277
278 DocClipBase *ClipManager::getClipAt(int pos)
279 {
280     return m_clipList.at(pos);
281 }
282
283 DocClipBase *ClipManager::getClipById(QString clipId)
284 {
285     //kDebug() << "++++  CLIP MAN, LOOKING FOR CLIP ID: " << clipId;
286     clipId = clipId.section('_', 0, 0);
287     for (int i = 0; i < m_clipList.count(); i++) {
288         if (m_clipList.at(i)->getId() == clipId) {
289             //kDebug() << "++++  CLIP MAN, FOUND FOR CLIP ID: " << clipId;
290             return m_clipList.at(i);
291         }
292     }
293     return NULL;
294 }
295
296 const QList <DocClipBase *> ClipManager::getClipByResource(QString resource)
297 {
298     QList <DocClipBase *> list;
299     QString clipResource;
300     QString proxyResource;
301     for (int i = 0; i < m_clipList.count(); i++) {
302         clipResource = m_clipList.at(i)->getProperty("resource");
303         proxyResource = m_clipList.at(i)->getProperty("proxy");
304         if (clipResource.isEmpty()) clipResource = m_clipList.at(i)->getProperty("colour");
305         if (clipResource == resource || proxyResource == resource) {
306             list.append(m_clipList.at(i));
307         }
308     }
309     return list;
310 }
311
312
313 void ClipManager::clearUnusedProducers()
314 {
315     for (int i = 0; i < m_clipList.count(); i++) {
316         if (m_clipList.at(i)->numReferences() == 0) m_clipList.at(i)->deleteProducers();
317     }
318 }
319
320 void ClipManager::resetProducersList(const QList <Mlt::Producer *> prods, bool displayRatioChanged, bool fpsChanged)
321 {
322     for (int i = 0; i < m_clipList.count(); i++) {
323         if (m_clipList.at(i)->numReferences() > 0 || displayRatioChanged || fpsChanged) {
324             m_clipList.at(i)->deleteProducers();
325         }
326     }
327     QString id;
328     for (int i = 0; i < prods.count(); i++) {
329         id = prods.at(i)->get("id");
330         if (id.contains('_')) id = id.section('_', 0, 0);
331         DocClipBase *clip = getClipById(id);
332         if (clip) {
333             clip->setProducer(prods.at(i), false, true);
334         }
335     }
336     emit checkAllClips(displayRatioChanged, fpsChanged);
337 }
338
339 void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, const QString &groupId)
340 {
341     QUndoCommand *addClips = new QUndoCommand();
342
343     foreach(const KUrl & file, urls) {
344         if (KIO::NetAccess::exists(file, KIO::NetAccess::SourceSide, NULL)) {
345             if (!getClipByResource(file.path()).empty()) {
346                 if (KMessageBox::warningContinueCancel(kapp->activeWindow(), i18n("Clip <b>%1</b><br />already exists in project, what do you want to do?", file.path()), i18n("Clip already exists")) == KMessageBox::Cancel)
347                     continue;
348             }
349             kDebug() << "Adding clip: " << file.path();
350             QDomDocument doc;
351             QDomElement prod = doc.createElement("producer");
352             doc.appendChild(prod);
353             prod.setAttribute("resource", file.path());
354             uint id = m_clipIdCounter++;
355             prod.setAttribute("id", QString::number(id));
356             if (!group.isEmpty()) {
357                 prod.setAttribute("groupname", group);
358                 prod.setAttribute("groupid", groupId);
359             }
360             KMimeType::Ptr type = KMimeType::findByUrl(file);
361             if (type->name().startsWith("image/")) {
362                 prod.setAttribute("type", (int) IMAGE);
363                 prod.setAttribute("in", 0);
364                 prod.setAttribute("out", m_doc->getFramePos(KdenliveSettings::image_duration()));
365                 if (KdenliveSettings::autoimagetransparency()) prod.setAttribute("transparency", 1);
366                 // Read EXIF metadata for JPEG
367                 if (type->is("image/jpeg")) {
368                     KFileMetaInfo metaInfo(file.path(), QString("image/jpeg"), KFileMetaInfo::TechnicalInfo);
369                     const QHash<QString, KFileMetaInfoItem> metaInfoItems = metaInfo.items();
370                     foreach(const KFileMetaInfoItem & metaInfoItem, metaInfoItems) {
371                         prod.setAttribute("meta.attr." + metaInfoItem.name().section("#", 1), metaInfoItem.value().toString());
372                     }
373                 }
374             } else if (type->is("application/x-kdenlivetitle")) {
375                 // opening a title file
376                 QDomDocument txtdoc("titledocument");
377                 QFile txtfile(file.path());
378                 if (txtfile.open(QIODevice::ReadOnly) && txtdoc.setContent(&txtfile)) {
379                     txtfile.close();
380                     prod.setAttribute("type", (int) TEXT);
381                     // extract embeded images
382                     QDomNodeList items = txtdoc.elementsByTagName("content");
383                     for (int i = 0; i < items.count() ; i++) {
384                         QDomElement content = items.item(i).toElement();
385                         if (content.hasAttribute("base64")) {
386                             QString titlesFolder = m_doc->projectFolder().path(KUrl::AddTrailingSlash) + "titles/";
387                             QString path = TitleDocument::extractBase64Image(titlesFolder, content.attribute("base64"));
388                             if (!path.isEmpty()) {
389                                 content.setAttribute("url", path);
390                                 content.removeAttribute("base64");
391                             }
392                         }
393                     }
394                     QString titleData = txtdoc.toString();
395                     prod.setAttribute("xmldata", titleData);
396                     prod.setAttribute("transparency", 1);
397                     prod.setAttribute("in", 0);
398                     int out = txtdoc.documentElement().attribute("out").toInt();
399                     if (out > 0)
400                         prod.setAttribute("out", out);
401                     else
402                         prod.setAttribute("out", m_doc->getFramePos(KdenliveSettings::title_duration()));
403                 } else
404                     txtfile.close();
405             }
406             new AddClipCommand(m_doc, doc.documentElement(), QString::number(id), true, addClips);
407         }
408     }
409     if (addClips->childCount() > 0) {
410         addClips->setText(i18np("Add clip", "Add clips", addClips->childCount()));
411         m_doc->commandStack()->push(addClips);
412     }
413 }
414
415 void ClipManager::slotAddClipFile(const KUrl &url, const QString &group, const QString &groupId)
416 {
417     slotAddClipList(KUrl::List(url), group, groupId);
418 }
419
420 void ClipManager::slotAddXmlClipFile(const QString &name, const QDomElement &xml, const QString &group, const QString &groupId)
421 {
422     QDomDocument doc;
423     doc.appendChild(doc.importNode(xml, true));
424     QDomElement prod = doc.documentElement();
425     prod.setAttribute("type", (int) PLAYLIST);
426     uint id = m_clipIdCounter++;
427     prod.setAttribute("id", QString::number(id));
428     prod.setAttribute("name", name);
429     if (!group.isEmpty()) {
430         prod.setAttribute("groupname", group);
431         prod.setAttribute("groupid", groupId);
432     }
433     AddClipCommand *command = new AddClipCommand(m_doc, doc.documentElement(), QString::number(id), true);
434     m_doc->commandStack()->push(command);
435 }
436
437 void ClipManager::slotAddColorClipFile(const QString &name, const QString &color, QString duration, const QString &group, const QString &groupId)
438 {
439     QDomDocument doc;
440     QDomElement prod = doc.createElement("producer");
441     doc.appendChild(prod);
442     prod.setAttribute("mlt_service", "colour");
443     prod.setAttribute("colour", color);
444     prod.setAttribute("type", (int) COLOR);
445     uint id = m_clipIdCounter++;
446     prod.setAttribute("id", QString::number(id));
447     prod.setAttribute("in", "0");
448     prod.setAttribute("out", m_doc->getFramePos(duration));
449     prod.setAttribute("name", name);
450     if (!group.isEmpty()) {
451         prod.setAttribute("groupname", group);
452         prod.setAttribute("groupid", groupId);
453     }
454     AddClipCommand *command = new AddClipCommand(m_doc, doc.documentElement(), QString::number(id), true);
455     m_doc->commandStack()->push(command);
456 }
457
458 void ClipManager::slotAddSlideshowClipFile(const QString &name, const QString &path, int count, const QString &duration,
459         const bool loop, const bool crop, const bool fade,
460         const QString &luma_duration, const QString &luma_file, const int softness,
461         const QString &animation, const QString &group, const QString &groupId)
462 {
463     QDomDocument doc;
464     QDomElement prod = doc.createElement("producer");
465     doc.appendChild(prod);
466     prod.setAttribute("resource", path);
467     prod.setAttribute("type", (int) SLIDESHOW);
468     uint id = m_clipIdCounter++;
469     prod.setAttribute("id", QString::number(id));
470     prod.setAttribute("in", "0");
471     prod.setAttribute("out", m_doc->getFramePos(duration) * count);
472     prod.setAttribute("ttl", m_doc->getFramePos(duration));
473     prod.setAttribute("luma_duration", m_doc->getFramePos(luma_duration));
474     prod.setAttribute("name", name);
475     prod.setAttribute("loop", loop);
476     prod.setAttribute("crop", crop);
477     prod.setAttribute("fade", fade);
478     prod.setAttribute("softness", QString::number(softness));
479     prod.setAttribute("luma_file", luma_file);
480     prod.setAttribute("animation", animation);
481     if (!group.isEmpty()) {
482         prod.setAttribute("groupname", group);
483         prod.setAttribute("groupid", groupId);
484     }
485     AddClipCommand *command = new AddClipCommand(m_doc, doc.documentElement(), QString::number(id), true);
486     m_doc->commandStack()->push(command);
487 }
488
489
490
491 void ClipManager::slotAddTextClipFile(const QString &titleName, int out, const QString &xml, const QString &group, const QString &groupId)
492 {
493     QDomDocument doc;
494     QDomElement prod = doc.createElement("producer");
495     doc.appendChild(prod);
496     //prod.setAttribute("resource", imagePath);
497     prod.setAttribute("name", titleName);
498     prod.setAttribute("xmldata", xml);
499     uint id = m_clipIdCounter++;
500     prod.setAttribute("id", QString::number(id));
501     if (!group.isEmpty()) {
502         prod.setAttribute("groupname", group);
503         prod.setAttribute("groupid", groupId);
504     }
505     prod.setAttribute("type", (int) TEXT);
506     prod.setAttribute("transparency", "1");
507     prod.setAttribute("in", "0");
508     prod.setAttribute("out", out);
509     AddClipCommand *command = new AddClipCommand(m_doc, doc.documentElement(), QString::number(id), true);
510     m_doc->commandStack()->push(command);
511 }
512
513 void ClipManager::slotAddTextTemplateClip(QString titleName, const KUrl &path, const QString &group, const QString &groupId)
514 {
515     QDomDocument doc;
516     QDomElement prod = doc.createElement("producer");
517     doc.appendChild(prod);
518     prod.setAttribute("name", titleName);
519     prod.setAttribute("resource", path.path());
520     uint id = m_clipIdCounter++;
521     prod.setAttribute("id", QString::number(id));
522     if (!group.isEmpty()) {
523         prod.setAttribute("groupname", group);
524         prod.setAttribute("groupid", groupId);
525     }
526     prod.setAttribute("type", (int) TEXT);
527     prod.setAttribute("transparency", "1");
528     prod.setAttribute("in", "0");
529
530     int out = 0;
531     QDomDocument titledoc;
532     QFile txtfile(path.path());
533     if (txtfile.open(QIODevice::ReadOnly) && titledoc.setContent(&txtfile)) {
534         txtfile.close();
535         out = titledoc.documentElement().attribute("out").toInt();
536     } else txtfile.close();
537
538     if (out == 0) out = m_doc->getFramePos(KdenliveSettings::image_duration());
539     prod.setAttribute("out", out);
540
541     AddClipCommand *command = new AddClipCommand(m_doc, doc.documentElement(), QString::number(id), true);
542     m_doc->commandStack()->push(command);
543 }
544
545 int ClipManager::getFreeClipId()
546 {
547     return m_clipIdCounter++;
548 }
549
550 int ClipManager::getFreeFolderId()
551 {
552     return m_folderIdCounter++;
553 }
554
555 int ClipManager::lastClipId() const
556 {
557     return m_clipIdCounter - 1;
558 }
559
560 QString ClipManager::projectFolder() const
561 {
562     return m_doc->projectFolder().path();
563 }
564
565 void ClipManager::addFolder(const QString &id, const QString &name)
566 {
567     m_folderList.insert(id, name);
568 }
569
570 void ClipManager::deleteFolder(const QString &id)
571 {
572     m_folderList.remove(id);
573 }
574
575 AbstractGroupItem *ClipManager::createGroup()
576 {
577     AbstractGroupItem *group = new AbstractGroupItem(m_doc->fps());
578     m_groupsList.append(group);
579     return group;
580 }
581
582 void ClipManager::removeGroup(AbstractGroupItem *group)
583 {
584     m_groupsList.removeAll(group);
585 }
586
587 QDomElement ClipManager::groupsXml() const
588 {
589     QDomDocument doc;
590     QDomElement groups = doc.createElement("groups");
591     doc.appendChild(groups);
592     for (int i = 0; i < m_groupsList.count(); i++) {
593         QDomElement group = doc.createElement("group");
594         groups.appendChild(group);
595         QList <QGraphicsItem *> children = m_groupsList.at(i)->childItems();
596         for (int j = 0; j < children.count(); j++) {
597             if (children.at(j)->type() == AVWIDGET || children.at(j)->type() == TRANSITIONWIDGET) {
598                 AbstractClipItem *item = static_cast <AbstractClipItem *>(children.at(j));
599                 ItemInfo info = item->info();
600                 if (item->type() == AVWIDGET) {
601                     QDomElement clip = doc.createElement("clipitem");
602                     clip.setAttribute("track", info.track);
603                     clip.setAttribute("position", info.startPos.frames(m_doc->fps()));
604                     group.appendChild(clip);
605                 } else if (item->type() == TRANSITIONWIDGET) {
606                     QDomElement clip = doc.createElement("transitionitem");
607                     clip.setAttribute("track", info.track);
608                     clip.setAttribute("position", info.startPos.frames(m_doc->fps()));
609                     group.appendChild(clip);
610                 }
611             }
612         }
613     }
614     return doc.documentElement();
615 }
616
617
618 void ClipManager::slotClipModified(const QString &path)
619 {
620     //kDebug() << "// CLIP: " << path << " WAS MODIFIED";
621     const QList <DocClipBase *> list = getClipByResource(path);
622     for (int i = 0; i < list.count(); i++) {
623         DocClipBase *clip = list.at(i);
624         if (clip != NULL) {
625             QString id = clip->getId();
626             if (!m_modifiedClips.contains(id))
627                 emit modifiedClip(id);
628             m_modifiedClips[id] = QTime::currentTime();
629         }
630     }
631     if (!m_modifiedTimer.isActive()) m_modifiedTimer.start();
632 }
633
634 void ClipManager::slotProcessModifiedClips()
635 {
636     if (!m_modifiedClips.isEmpty()) {
637         QMapIterator<QString, QTime> i(m_modifiedClips);
638         while (i.hasNext()) {
639             i.next();
640             if (QTime::currentTime().msecsTo(i.value()) <= -1500) {
641                 emit reloadClip(i.key());
642                 m_modifiedClips.remove(i.key());
643                 break;
644             }
645         }
646     }
647     if (m_modifiedClips.isEmpty()) m_modifiedTimer.stop();
648 }
649
650 void ClipManager::slotClipMissing(const QString &path)
651 {
652     // kDebug() << "// CLIP: " << path << " WAS MISSING";
653     const QList <DocClipBase *> list = getClipByResource(path);
654     for (int i = 0; i < list.count(); i++) {
655         DocClipBase *clip = list.at(i);
656         if (clip != NULL) emit missingClip(clip->getId());
657     }
658 }
659
660 void ClipManager::slotClipAvailable(const QString &path)
661 {
662     // kDebug() << "// CLIP: " << path << " WAS ADDED";
663     const QList <DocClipBase *> list = getClipByResource(path);
664     for (int i = 0; i < list.count(); i++) {
665         DocClipBase *clip = list.at(i);
666         if (clip != NULL) emit availableClip(clip->getId());
667     }
668 }
669
670 int ClipManager::clipsCount() const
671 {
672     return m_clipList.count();
673 }
674
675