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