]> git.sesse.net Git - kdenlive/blobdiff - src/projectlist.cpp
* Allow to edit transparent background for images in group properties
[kdenlive] / src / projectlist.cpp
index b41fc50e8d33f926283d8749aa4aaaeb597c6d7c..e6d9149893388ae8df9d464f57ef32d8cdbcab9c 100644 (file)
@@ -63,6 +63,7 @@
 #include <QProcess>
 #include <QHeaderView>
 #include <QInputDialog>
+#include <QtConcurrentRun>
 
 ProjectList::ProjectList(QWidget *parent) :
     QWidget(parent),
@@ -80,7 +81,7 @@ ProjectList::ProjectList(QWidget *parent) :
     QVBoxLayout *layout = new QVBoxLayout;
     layout->setContentsMargins(0, 0, 0, 0);
     layout->setSpacing(0);
-
+    qRegisterMetaType<QDomElement>("QDomElement");
     // setup toolbar
     QFrame *frame = new QFrame;
     frame->setFrameStyle(QFrame::NoFrame);
@@ -111,11 +112,11 @@ ProjectList::ProjectList(QWidget *parent) :
     setLayout(layout);
     searchView->setTreeWidget(m_listView);
 
-    m_queueTimer.setInterval(100);
-    connect(&m_queueTimer, SIGNAL(timeout()), this, SLOT(slotProcessNextClipInQueue()));
-    m_queueTimer.setSingleShot(true);
-
-
+    m_proxyAction = new QAction(i18n("Proxy clip"), this);
+    m_proxyAction->setCheckable(true);
+    m_proxyAction->setChecked(false);
+    connect(m_proxyAction, SIGNAL(toggled(bool)), this, SLOT(slotProxyCurrentItem(bool)));
+    connect(this, SIGNAL(processNextThumbnail()), this, SLOT(slotProcessNextThumbnail()));
     connect(m_listView, SIGNAL(projectModified()), this, SIGNAL(projectModified()));
     connect(m_listView, SIGNAL(itemSelectionChanged()), this, SLOT(slotClipSelected()));
     connect(m_listView, SIGNAL(focusMonitor()), this, SLOT(slotClipSelected()));
@@ -200,6 +201,7 @@ void ProjectList::setupGeneratorMenu(QMenu *addMenu, QMenu *transcodeMenu, QMenu
         transcodeMenu->setEnabled(false);
     m_transcodeAction = transcodeMenu;
     m_menu->addAction(m_reloadAction);
+    m_menu->addAction(m_proxyAction);
     m_menu->addMenu(inTimelineMenu);
     inTimelineMenu->setEnabled(false);
     m_menu->addAction(m_editButton->defaultAction());
@@ -227,7 +229,8 @@ void ProjectList::updateProjectFormat(Timecode t)
 void ProjectList::slotEditClip()
 {
     QList<QTreeWidgetItem *> list = m_listView->selectedItems();
-    if (list.count() > 1) {
+    if (list.isEmpty()) return;
+    if (list.count() > 1 || list.at(0)->type() == PROJECTFOLDERTYPE) {
         editClipSelection(list);
         return;
     }
@@ -249,23 +252,33 @@ void ProjectList::editClipSelection(QList<QTreeWidgetItem *> list)
     // Gather all common properties
     QMap <QString, QString> commonproperties;
     QList <DocClipBase *> clipList;
-    commonproperties.insert("force_aspect_ratio", "-");
+    commonproperties.insert("force_aspect_num", "-");
+    commonproperties.insert("force_aspect_den", "-");
     commonproperties.insert("force_fps", "-");
     commonproperties.insert("force_progressive", "-");
+    commonproperties.insert("force_tff", "-");
     commonproperties.insert("threads", "-");
     commonproperties.insert("video_index", "-");
     commonproperties.insert("audio_index", "-");
     commonproperties.insert("force_colorspace", "-");
     commonproperties.insert("full_luma", "-");
+    QString transparency = "-";
 
     bool allowDurationChange = true;
     int commonDuration = -1;
+    bool hasImages = false;;
     ProjectItem *item;
     for (int i = 0; i < list.count(); i++) {
         item = NULL;
-        if (list.at(i)->type() == PROJECTFOLDERTYPE)
+        if (list.at(i)->type() == PROJECTFOLDERTYPE) {
+            // Add folder items to the list
+            int ct = list.at(i)->childCount();
+            for (int j = 0; j < ct; j++) {
+                list.append(list.at(i)->child(j));
+            }
             continue;
-        if (list.at(i)->type() == PROJECTSUBCLIPTYPE)
+        }
+        else if (list.at(i)->type() == PROJECTSUBCLIPTYPE)
             item = static_cast <ProjectItem*>(list.at(i)->parent());
         else
             item = static_cast <ProjectItem*>(list.at(i));
@@ -275,6 +288,29 @@ void ProjectList::editClipSelection(QList<QTreeWidgetItem *> list)
             // check properties
             DocClipBase *clip = item->referencedClip();
             if (clipList.contains(clip)) continue;
+            if (clip->clipType() == IMAGE) {
+                hasImages = true;
+                if (clip->getProperty("transparency").isEmpty() || clip->getProperty("transparency").toInt() == 0) {
+                    if (transparency == "-") {
+                        // first non transparent image
+                        transparency = "0";
+                    }
+                    else if (transparency == "1") {
+                        // we have transparent and non transparent clips
+                        transparency = "-1";
+                    }
+                }
+                else {
+                    if (transparency == "-") {
+                        // first transparent image
+                        transparency = "1";
+                    }
+                    else if (transparency == "0") {
+                        // we have transparent and non transparent clips
+                        transparency = "-1";
+                    }
+                }
+            }
             if (clip->clipType() != COLOR && clip->clipType() != IMAGE && clip->clipType() != TEXT)
                 allowDurationChange = false;
             if (allowDurationChange && commonDuration != 0) {
@@ -302,11 +338,13 @@ void ProjectList::editClipSelection(QList<QTreeWidgetItem *> list)
     }
     if (allowDurationChange)
         commonproperties.insert("out", QString::number(commonDuration));
-    QMapIterator<QString, QString> p(commonproperties);
+    if (hasImages)
+        commonproperties.insert("transparency", transparency);
+    /*QMapIterator<QString, QString> p(commonproperties);
     while (p.hasNext()) {
         p.next();
         kDebug() << "Result: " << p.key() << " = " << p.value();
-    }
+    }*/
     emit showClipProperties(clipList, commonproperties);
 }
 
@@ -399,8 +437,10 @@ void ProjectList::slotReloadClip(const QString &id)
     QList<QTreeWidgetItem *> selected;
     if (id.isEmpty())
         selected = m_listView->selectedItems();
-    else
-        selected.append(getItemById(id));
+    else {
+        ProjectItem *itemToReLoad = getItemById(id);
+        if (itemToReLoad) selected.append(itemToReLoad);
+    }
     ProjectItem *item;
     for (int i = 0; i < selected.count(); i++) {
         if (selected.at(i)->type() != PROJECTCLIPTYPE) {
@@ -412,18 +452,26 @@ void ProjectList::slotReloadClip(const QString &id)
         }
         item = static_cast <ProjectItem *>(selected.at(i));
         if (item) {
-            if (item->clipType() == TEXT) {
+            CLIPTYPE t = item->clipType();
+            if (t == TEXT) {
                 if (!item->referencedClip()->getProperty("xmltemplate").isEmpty())
                     regenerateTemplate(item);
-            } else if (item->clipType() != COLOR && item->clipType() != SLIDESHOW && item->referencedClip() &&  item->referencedClip()->checkHash() == false) {
+            } else if (t != COLOR && t != SLIDESHOW && item->referencedClip() &&  item->referencedClip()->checkHash() == false) {
                 item->referencedClip()->setPlaceHolder(true);
                 item->setProperty("file_hash", QString());
-            } else if (item->clipType() == IMAGE) {
+            } else if (t == IMAGE) {
                 item->referencedClip()->producer()->set("force_reload", 1);
             }
-            //requestClipInfo(item->toXml(), item->clipId(), true);
-            // Clear the file_hash value, which will cause a complete reload of the clip
-            emit getFileProperties(item->toXml(), item->clipId(), m_listView->iconSize().height(), true);
+
+            QDomElement e = item->toXml();
+            // Make sure we get the correct producer length if it was adjusted in timeline
+            if (t == COLOR || t == IMAGE || t == SLIDESHOW || t == TEXT) {
+                int length = QString(item->referencedClip()->producerProperty("length")).toInt();
+                if (length > 0 && !e.hasAttribute("length")) {
+                    e.setAttribute("length", length);
+                }
+            }            
+            emit getFileProperties(e, item->clipId(), m_listView->iconSize().height(), true, false);
         }
     }
 }
@@ -507,11 +555,12 @@ void ProjectList::slotClipSelected()
     if (m_listView->currentItem()) {
         if (m_listView->currentItem()->type() == PROJECTFOLDERTYPE) {
             emit clipSelected(NULL);
-            m_editButton->defaultAction()->setEnabled(false);
+            m_editButton->defaultAction()->setEnabled(m_listView->currentItem()->childCount() > 0);
             m_deleteButton->defaultAction()->setEnabled(true);
             m_openAction->setEnabled(false);
             m_reloadAction->setEnabled(false);
             m_transcodeAction->setEnabled(false);
+            m_proxyAction->setEnabled(false);
         } else {
             ProjectItem *clip;
             if (m_listView->currentItem()->type() == PROJECTSUBCLIPTYPE) {
@@ -525,7 +574,7 @@ void ProjectList::slotClipSelected()
                 return;
             }
             clip = static_cast <ProjectItem*>(m_listView->currentItem());
-            if (clip)
+            if (clip && clip->referencedClip())
                 emit clipSelected(clip->referencedClip());
             m_editButton->defaultAction()->setEnabled(true);
             m_deleteButton->defaultAction()->setEnabled(true);
@@ -555,6 +604,18 @@ void ProjectList::slotClipSelected()
     }
 }
 
+void ProjectList::adjustProxyActions(ProjectItem *clip) const
+{
+    if (clip == NULL || clip->type() != PROJECTCLIPTYPE || clip->clipType() == COLOR || clip->clipType() == TEXT || clip->clipType() == PLAYLIST || clip->clipType() == SLIDESHOW || clip->clipType() == AUDIO) {
+        m_proxyAction->setEnabled(false);
+        return;
+    }
+    m_proxyAction->setEnabled(useProxy());
+    m_proxyAction->blockSignals(true);
+    m_proxyAction->setChecked(clip->hasProxy());
+    m_proxyAction->blockSignals(false);
+}
+
 void ProjectList::adjustTranscodeActions(ProjectItem *clip) const
 {
     if (clip == NULL || clip->type() != PROJECTCLIPTYPE || clip->clipType() == COLOR || clip->clipType() == TEXT || clip->clipType() == PLAYLIST || clip->clipType() == SLIDESHOW) {
@@ -591,11 +652,15 @@ void ProjectList::slotUpdateClipProperties(const QString &id, QMap <QString, QSt
         slotUpdateClipProperties(item, properties);
         if (properties.contains("out") || properties.contains("force_fps") || properties.contains("resource")) {
             slotReloadClip(id);
-        } else if (properties.contains("colour") || properties.contains("xmldata") || properties.contains("force_aspect_ratio") || properties.contains("templatetext")) {
+        } else if (properties.contains("colour") ||
+                   properties.contains("xmldata") ||
+                   properties.contains("force_aspect_num") ||
+                   properties.contains("force_aspect_den") ||
+                   properties.contains("templatetext")) {
             slotRefreshClipThumbnail(item);
-            emit refreshClip();
-        } else if (properties.contains("full_luma") || properties.contains("force_colorspace")) {
-            emit refreshClip();
+            emit refreshClip(id, true);
+        } else if (properties.contains("full_luma") || properties.contains("force_colorspace") || properties.contains("loop")) {
+            emit refreshClip(id, false);
         }
     }
 }
@@ -606,16 +671,16 @@ void ProjectList::slotUpdateClipProperties(ProjectItem *clip, QMap <QString, QSt
         return;
     clip->setProperties(properties);
     if (properties.contains("name")) {
-        m_listView->blockSignals(true);
+        monitorItemEditing(false);
         clip->setText(0, properties.value("name"));
-        m_listView->blockSignals(false);
+        monitorItemEditing(true);
         emit clipNameChanged(clip->clipId(), properties.value("name"));
     }
     if (properties.contains("description")) {
         CLIPTYPE type = clip->clipType();
-        m_listView->blockSignals(true);
+        monitorItemEditing(false);
         clip->setText(1, properties.value("description"));
-        m_listView->blockSignals(false);
+        monitorItemEditing(true);
 #ifdef NEPOMUK
         if (KdenliveSettings::activate_nepomuk() && (type == AUDIO || type == VIDEO || type == AV || type == IMAGE || type == PLAYLIST)) {
             // Use Nepomuk system to store clip description
@@ -705,6 +770,7 @@ void ProjectList::slotContextMenu(const QPoint &pos, QTreeWidgetItem *item)
             clip = static_cast <ProjectItem*>(item);
             // Display relevant transcoding actions only
             adjustTranscodeActions(clip);
+            adjustProxyActions(clip);
             // Display uses in timeline
             emit findInTimeline(clip->clipId());
         } else {
@@ -736,7 +802,6 @@ void ProjectList::slotRemoveClip()
 
     QUndoCommand *delCommand = new QUndoCommand();
     delCommand->setText(i18n("Delete Clip Zone"));
-
     for (int i = 0; i < selected.count(); i++) {
         if (selected.at(i)->type() == PROJECTSUBCLIPTYPE) {
             // subitem
@@ -758,11 +823,13 @@ void ProjectList::slotRemoveClip()
         } else {
             ProjectItem *item = static_cast <ProjectItem *>(selected.at(i));
             ids << item->clipId();
-            if (item->numReferences() > 0 && KMessageBox::questionYesNo(kapp->activeWindow(), i18np("Delete clip <b>%2</b>?<br />This will also remove the clip in timeline", "Delete clip <b>%2</b>?<br />This will also remove its %1 clips in timeline", item->numReferences(), item->names().at(1)), i18n("Delete Clip")) != KMessageBox::Yes)
+            if (item->numReferences() > 0 && KMessageBox::questionYesNo(kapp->activeWindow(), i18np("Delete clip <b>%2</b>?<br />This will also remove the clip in timeline", "Delete clip <b>%2</b>?<br />This will also remove its %1 clips in timeline", item->numReferences(), item->names().at(1)), i18n("Delete Clip"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "DeleteAll") == KMessageBox::No) {
+                KMessageBox::enableMessage("DeleteAll");
                 return;
+            }
         }
     }
-
+    KMessageBox::enableMessage("DeleteAll");
     if (delCommand->childCount() == 0)
         delete delCommand;
     else
@@ -774,6 +841,7 @@ void ProjectList::updateButtons() const
 {
     if (m_listView->topLevelItemCount() == 0) {
         m_deleteButton->defaultAction()->setEnabled(false);
+        m_editButton->defaultAction()->setEnabled(false);
     } else {
         m_deleteButton->defaultAction()->setEnabled(true);
         if (!m_listView->currentItem())
@@ -784,14 +852,18 @@ void ProjectList::updateButtons() const
             m_openAction->setEnabled(true);
             m_reloadAction->setEnabled(true);
             m_transcodeAction->setEnabled(true);
+            m_proxyAction->setEnabled(useProxy());
             return;
         }
+        else if (item && item->type() == PROJECTFOLDERTYPE && item->childCount() > 0) {
+            m_editButton->defaultAction()->setEnabled(true);
+        }
+        else m_editButton->defaultAction()->setEnabled(false);
     }
-
-    m_editButton->defaultAction()->setEnabled(false);
     m_openAction->setEnabled(false);
     m_reloadAction->setEnabled(false);
     m_transcodeAction->setEnabled(false);
+    m_proxyAction->setEnabled(false);
 }
 
 void ProjectList::selectItemById(const QString &clipId)
@@ -896,18 +968,9 @@ void ProjectList::deleteProjectFolder(QMap <QString, QString> map)
 void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties)
 {
     m_listView->setEnabled(false);
-    if (getProperties) {
-        m_listView->blockSignals(true);
-        m_refreshed = false;
-        // remove file_hash so that we load all properties for the clip
-        QDomElement e = clip->toXML().cloneNode().toElement();
-        e.removeAttribute("file_hash");
-        m_infoQueue.insert(clip->getId(), e);
-        //m_render->getFileProperties(clip->toXML(), clip->getId(), true);
-    }
-    clip->askForAudioThumbs();
     const QString parent = clip->getProperty("groupid");
     ProjectItem *item = NULL;
+    monitorItemEditing(false);
     if (!parent.isEmpty()) {
         FolderProjectItem *parentitem = getFolderItemById(parent);
         if (!parentitem) {
@@ -922,10 +985,47 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties)
         if (parentitem)
             item = new ProjectItem(parentitem, clip);
     }
-    if (item == NULL)
+    if (item == NULL) {
         item = new ProjectItem(m_listView, clip);
+    }
+    if (item->data(0, DurationRole).isNull()) item->setData(0, DurationRole, i18n("Loading"));
+    if (getProperties) {
+        m_listView->processLayout();
+        m_refreshed = false;
+        // Proxy clips
+        CLIPTYPE t = clip->clipType();
+        if ((t == VIDEO || t == AV || t == UNKNOWN) && useProxy()) {
+            if (clip->getProperty("proxy").isEmpty()) {
+                
+                //connect(clip, SIGNAL(proxyReady(const QString&, bool)), this, SLOT(slotGotProxy(const QString&, bool)));
+                //setProxyStatus(item, 1);
+                //clip->generateProxy(m_doc->projectFolder(), proxyParams());
+            }
+            else {
+                // Proxy clip already created
+                setProxyStatus(item, 2);
+                QDomElement e = clip->toXML().cloneNode().toElement();
+                e.removeAttribute("file_hash");
+                m_infoQueue.insert(clip->getId(), e);
+            }
+        }
+        //else {
+            // We don't use proxies
+            // remove file_hash so that we load all properties for the clip
+            QDomElement e = clip->toXML().cloneNode().toElement();
+            e.removeAttribute("file_hash");
+            m_infoQueue.insert(clip->getId(), e);
+        //}
+        //m_render->getFileProperties(clip->toXML(), clip->getId(), true);
+    }
+    else if (!clip->getProperty("proxy").isEmpty()) {
+        connect(clip, SIGNAL(proxyReady(const QString&, bool)), this, SLOT(slotGotProxy(const QString&, bool)));
+        setProxyStatus(item, 1);
+        clip->generateProxy(m_doc->projectFolder(), proxyParams());
+    }
+    clip->askForAudioThumbs();
+    
     KUrl url = clip->fileURL();
-
     if (getProperties == false && !clip->getClipHash().isEmpty()) {
         QString cachedPixmap = m_doc->projectFolder().path(KUrl::AddTrailingSlash) + "thumbs/" + clip->getClipHash() + ".png";
         if (QFile::exists(cachedPixmap)) {
@@ -960,13 +1060,31 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties)
             }
         }
     }
+    monitorItemEditing(true);
     if (m_listView->isEnabled()) {
         updateButtons();
-        if (getProperties)
-            m_listView->blockSignals(false);
     }
-    if (getProperties && !m_queueTimer.isActive())
-        slotProcessNextClipInQueue();
+    
+    if (getProperties && m_processingClips.isEmpty())
+        m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
+}
+
+void ProjectList::slotGotProxy(const QString &id, bool success)
+{
+    ProjectItem *item = getItemById(id);
+    if (item) {
+        if (success) {
+            // Proxy clip successfully created
+            setProxyStatus(item, 2);
+            QDomElement e = item->referencedClip()->toXML().cloneNode().toElement();  
+            e.removeAttribute("file_hash");
+            e.setAttribute("replace", 1);
+            m_infoQueue.insert(id, e);
+            if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
+        }
+        else setProxyStatus(item, 0);
+        update();
+    }
 }
 
 void ProjectList::slotResetProjectList()
@@ -988,26 +1106,35 @@ void ProjectList::requestClipInfo(const QDomElement xml, const QString id)
 void ProjectList::slotProcessNextClipInQueue()
 {
     if (m_infoQueue.isEmpty()) {
-        slotProcessNextThumbnail();
+        emit processNextThumbnail();
         return;
     }
 
     QMap<QString, QDomElement>::const_iterator j = m_infoQueue.constBegin();
     if (j != m_infoQueue.constEnd()) {
-        const QDomElement dom = j.value();
+        QDomElement dom = j.value();
         const QString id = j.key();
-        m_infoQueue.remove(j.key());
-        emit getFileProperties(dom, id, m_listView->iconSize().height(), false);
+        m_infoQueue.remove(id);
+        m_processingClips.append(id);
+        bool replace;
+        if (dom.hasAttribute("replace")) {
+            // Proxy action was enabled / disabled and we want to replace current producer
+            dom.removeAttribute("replace");
+            replace = true;
+        }
+        else replace = false;
+        bool selectClip = !replace;
+        if (m_infoQueue.count() > 1) selectClip = false;
+        emit getFileProperties(dom, id, m_listView->iconSize().height(), replace, selectClip);
     }
-    if (!m_infoQueue.isEmpty()) m_queueTimer.start();
 }
 
 void ProjectList::slotUpdateClip(const QString &id)
 {
     ProjectItem *item = getItemById(id);
-    m_listView->blockSignals(true);
+    monitorItemEditing(false);
     if (item) item->setData(0, UsageRole, QString::number(item->numReferences()));
-    m_listView->blockSignals(false);
+    monitorItemEditing(true);
 }
 
 void ProjectList::updateAllClips()
@@ -1018,7 +1145,7 @@ void ProjectList::updateAllClips()
     QTreeWidgetItemIterator it(m_listView);
     DocClipBase *clip;
     ProjectItem *item;
-    m_listView->blockSignals(true);
+    monitorItemEditing(false);
     while (*it) {
         if ((*it)->type() == PROJECTSUBCLIPTYPE) {
             // subitem
@@ -1052,10 +1179,10 @@ void ProjectList::updateAllClips()
         //qApp->processEvents();
         ++it;
     }
-    if (!m_queueTimer.isActive())
-        m_queueTimer.start();
+
+    if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
     if (m_listView->isEnabled())
-        m_listView->blockSignals(false);
+        monitorItemEditing(true);
     m_listView->setSortingEnabled(true);
     if (m_infoQueue.isEmpty())
         slotProcessNextThumbnail();
@@ -1066,8 +1193,8 @@ QString ProjectList::getExtensions()
 {
     // Build list of mime types
     QStringList mimeTypes = QStringList() << "application/x-kdenlive" << "application/x-kdenlivetitle" << "video/mlt-playlist" << "text/plain"
-                            << "video/x-flv" << "application/vnd.rn-realmedia" << "video/x-dv" << "video/dv" << "video/x-msvideo" << "video/x-matroska" << "video/mpeg" << "video/ogg" << "video/x-ms-wmv" << "video/mp4" << "video/quicktime"
-                            << "audio/x-flac" << "audio/x-matroska" << "audio/mp4" << "audio/mpeg" << "audio/x-mp3" << "audio/ogg" << "audio/x-wav" << "application/ogg" << "application/mxf"
+                            << "video/x-flv" << "application/vnd.rn-realmedia" << "video/x-dv" << "video/dv" << "video/x-msvideo" << "video/x-matroska" << "video/mpeg" << "video/ogg" << "video/x-ms-wmv" << "video/mp4" << "video/quicktime" << "video/webm"
+                            << "audio/x-flac" << "audio/x-matroska" << "audio/mp4" << "audio/mpeg" << "audio/x-mp3" << "audio/ogg" << "audio/x-wav" << "audio/x-aiff" << "audio/aiff" << "application/ogg" << "application/mxf" << "application/x-shockwave-flash"
                             << "image/gif" << "image/jpeg" << "image/png" << "image/x-tga" << "image/x-bmp" << "image/svg+xml" << "image/tiff" << "image/x-xcf" << "image/x-xcf-gimp" << "image/x-vnd.adobe.photoshop" << "image/x-pcx" << "image/x-exr";
 
     QString allExtensions;
@@ -1092,10 +1219,21 @@ void ProjectList::slotAddClip(const QList <QUrl> givenList, const QString &group
         const QString dialogFilter = allExtensions + ' ' + QLatin1Char('|') + i18n("All Supported Files") + "\n* " + QLatin1Char('|') + i18n("All Files");
         QCheckBox *b = new QCheckBox(i18n("Import image sequence"));
         b->setChecked(KdenliveSettings::autoimagesequence());
-        KFileDialog *d = new KFileDialog(KUrl("kfiledialog:///clipfolder"), dialogFilter, kapp->activeWindow(), b);
+        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);
+        KFileDialog *d = new KFileDialog(KUrl("kfiledialog:///clipfolder"), dialogFilter, kapp->activeWindow(), f);
         d->setOperationMode(KFileDialog::Opening);
         d->setMode(KFile::Files);
-        d->exec();
+        if (d->exec() == QDialog::Accepted) {
+            KdenliveSettings::setAutoimagetransparency(c->isChecked());
+        }
         list = d->selectedUrls();
         if (b->isChecked() && list.count() == 1) {
             // Check for image sequence
@@ -1104,9 +1242,10 @@ void ProjectList::slotAddClip(const QList <QUrl> givenList, const QString &group
             if (fileName.at(fileName.size() - 1).isDigit()) {
                 KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
                 if (item.mimetype().startsWith("image")) {
-                    int count = 0;
                     // import as sequence if we found more than one image in the sequence
-                    QString pattern = SlideshowClip::selectedPath(url.path(), false, QString(), &count);
+                    QStringList list;
+                    QString pattern = SlideshowClip::selectedPath(url.path(), false, QString(), &list);
+                    int count = list.count();
                     if (count > 1) {
                         delete d;
                         QStringList groupInfo = getGroup();
@@ -1154,7 +1293,8 @@ void ProjectList::slotAddClip(const QList <QUrl> givenList, const QString &group
 void ProjectList::slotRemoveInvalidClip(const QString &id, bool replace)
 {
     ProjectItem *item = getItemById(id);
-    QTimer::singleShot(300, this, SLOT(slotProcessNextClipInQueue()));
+    m_processingClips.removeAll(id);
+    if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
     if (item) {
         const QString path = item->referencedClip()->fileURL().path();
         if (item->referencedClip()->isPlaceHolder()) replace = false;
@@ -1276,6 +1416,7 @@ void ProjectList::setDocument(KdenliveDoc *doc)
 {
     m_listView->blockSignals(true);
     m_listView->clear();
+    m_processingClips.clear();
     m_listView->setSortingEnabled(false);
     emit clipSelected(NULL);
     m_thumbnailQueue.clear();
@@ -1348,7 +1489,6 @@ void ProjectList::slotCheckForEmptyQueue()
 
 void ProjectList::reloadClipThumbnails()
 {
-    kDebug() << "//////////////  RELOAD CLIPS THUMBNAILS!!!";
     m_thumbnailQueue.clear();
     QTreeWidgetItemIterator it(m_listView);
     while (*it) {
@@ -1427,10 +1567,10 @@ void ProjectList::slotRefreshClipThumbnail(QTreeWidgetItem *it, bool update)
             pix = item->referencedClip()->thumbProducer()->extractImage(frame, width, height);
 
         if (!pix.isNull()) {
-            m_listView->blockSignals(true);
+            monitorItemEditing(false);
             it->setData(0, Qt::DecorationRole, pix);
-            if (m_listView->isEnabled())
-                m_listView->blockSignals(false);
+            monitorItemEditing(true);
+                
             if (!isSubItem)
                 m_doc->cachePixmap(item->getClipHash(), pix);
             else
@@ -1438,35 +1578,47 @@ void ProjectList::slotRefreshClipThumbnail(QTreeWidgetItem *it, bool update)
         }
         if (update)
             emit projectModified();
-        QTimer::singleShot(30, this, SLOT(slotProcessNextThumbnail()));
+
+        slotProcessNextThumbnail();
     }
 }
 
-void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Producer *producer, const QMap < QString, QString > &properties, const QMap < QString, QString > &metadata, bool replace)
+void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Producer *producer, const QMap < QString, QString > &properties, const QMap < QString, QString > &metadata, bool replace, bool selectClip)
 {
     QString toReload;
     ProjectItem *item = getItemById(clipId);
+    m_processingClips.removeAll(clipId);
+    if (m_infoQueue.isEmpty() && m_processingClips.isEmpty()) m_listView->setEnabled(true);
     if (item && producer) {
-        m_listView->blockSignals(true);
+        //m_listView->blockSignals(true);
+        monitorItemEditing(false);
         item->setProperties(properties, metadata);
         if (item->referencedClip()->isPlaceHolder() && producer->is_valid()) {
             item->referencedClip()->setValid();
             item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled);
             toReload = clipId;
         }
-        //Q_ASSERT_X(item->referencedClip(), "void ProjectList::slotReplyGetFileProperties", QString("Item with groupName %1 does not have a clip associated").arg(item->groupName()).toLatin1());
-        item->referencedClip()->setProducer(producer, replace);
-        item->referencedClip()->askForAudioThumbs();
+        if (!useProxy() && item->referencedClip()->getProperty("proxy").isEmpty()) setProxyStatus(item, 0);
+        QString size = properties.value("frame_size");
+        DocClipBase *clip = item->referencedClip();
+        if (useProxy() && (item->clipType() == AV || item->clipType() == VIDEO) && generateProxy() && size.section('x', 0, 0).toInt() > proxyMinSize()) {
+            if (clip->getProperty("proxy").isEmpty()) {
+                connect(clip, SIGNAL(proxyReady(const QString&, bool)), this, SLOT(slotGotProxy(const QString&, bool)));
+                setProxyStatus(item, 1);
+                clip->generateProxy(m_doc->projectFolder(), proxyParams());
+            }
+        }
+        clip->setProducer(producer, replace);
+        clip->askForAudioThumbs();
         if (!replace && item->data(0, Qt::DecorationRole).isNull())
             requestClipThumbnail(clipId);
         if (!toReload.isEmpty())
             item->slotSetToolTip();
 
-        //emit receivedClipDuration(clipId);
         if (m_listView->isEnabled() && replace) {
             // update clip in clip monitor
             emit clipSelected(NULL);
-            emit clipSelected(item->referencedClip());
+            emit clipSelected(clip);
             //TODO: Make sure the line below has no side effect
             toReload = clipId;
         }
@@ -1476,12 +1628,12 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce
             delete producer;
         }*/
         if (m_listView->isEnabled())
-            m_listView->blockSignals(false);
+            monitorItemEditing(true);
         /*if (item->icon(0).isNull()) {
             requestClipThumbnail(clipId);
         }*/
     } else kDebug() << "////////  COULD NOT FIND CLIP TO UPDATE PRPS...";
-    int max = m_doc->clipManager()->clipsCount();
+    if (selectClip && m_infoQueue.isEmpty()) {
     if (item && m_infoQueue.isEmpty() && m_thumbnailQueue.isEmpty()) {
         m_listView->setCurrentItem(item);
         bool updatedProfile = false;
@@ -1494,12 +1646,14 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce
         }
         if (updatedProfile == false) emit clipSelected(item->referencedClip());
     } else {
+        int max = m_doc->clipManager()->clipsCount();
         emit displayMessage(i18n("Loading clips"), (int)(100 *(max - m_infoQueue.count()) / max));
     }
+    }
     if (!toReload.isEmpty())
         emit clipNeedsReload(toReload, true);
-    // small delay so that the app can display the progress info
-    QTimer::singleShot(30, this, SLOT(slotProcessNextClipInQueue()));
+
+    if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
 }
 
 bool ProjectList::adjustProjectProfileToItem(ProjectItem *item)
@@ -1553,18 +1707,41 @@ bool ProjectList::adjustProjectProfileToItem(ProjectItem *item)
                 }
                 delete list;
                 delete label;
-            } else KMessageBox::information(kapp->activeWindow(), i18n("Your clip does not match current project's profile.\nNo existing profile found to match the clip's properties.\nClip size: %1\nFps: %2\n", size, fps));
+            } else if (fps > 0) {
+                KMessageBox::information(kapp->activeWindow(), i18n("Your clip does not match current project's profile.\nNo existing profile found to match the clip's properties.\nClip size: %1\nFps: %2\n", size, fps));
+            }
         }
     }
     return profileUpdated;
 }
 
+bool ProjectList::useProxy() const
+{
+    return m_doc->getDocumentProperty("enableproxy").toInt();
+}
+
+bool ProjectList::generateProxy() const
+{
+    return m_doc->getDocumentProperty("generateproxy").toInt();
+}
+
+int ProjectList::proxyMinSize() const
+{
+    return m_doc->getDocumentProperty("proxyminsize").toInt();
+}
+
+QString ProjectList::proxyParams() const
+{
+    return m_doc->getDocumentProperty("proxyparams").simplified();
+}
+
 void ProjectList::slotReplyGetImage(const QString &clipId, const QPixmap &pix)
 {
     ProjectItem *item = getItemById(clipId);
     if (item && !pix.isNull()) {
-        m_listView->blockSignals(true);
+        monitorItemEditing(false);
         item->setData(0, Qt::DecorationRole, pix);
+        monitorItemEditing(true);
         m_doc->cachePixmap(item->getClipHash(), pix);
         if (m_listView->isEnabled())
             m_listView->blockSignals(false);
@@ -1612,7 +1789,7 @@ ProjectItem *ProjectList::getItemById(const QString &id)
     QTreeWidgetItemIterator it(m_listView);
     while (*it) {
         if ((*it)->type() != PROJECTCLIPTYPE) {
-            // subitem
+            // subitem or folder
             ++it;
             continue;
         }
@@ -1649,6 +1826,7 @@ void ProjectList::slotSelectClip(const QString &ix)
         m_deleteButton->defaultAction()->setEnabled(true);
         m_reloadAction->setEnabled(true);
         m_transcodeAction->setEnabled(true);
+        m_proxyAction->setEnabled(useProxy());
         if (clip->clipType() == IMAGE && !KdenliveSettings::defaultimageapp().isEmpty()) {
             m_openAction->setIcon(KIcon(KdenliveSettings::defaultimageapp()));
             m_openAction->setEnabled(true);
@@ -1756,7 +1934,7 @@ void ProjectList::addClipCut(const QString &id, int in, int out, const QString d
     if (clip) {
         DocClipBase *base = clip->referencedClip();
         base->addCutZone(in, out);
-        m_listView->blockSignals(true);
+        monitorItemEditing(false);
         SubProjectItem *sub = new SubProjectItem(clip, in, out, desc);
         if (newItem && desc.isEmpty() && !m_listView->isColumnHidden(1)) {
             if (!clip->isExpanded())
@@ -1767,7 +1945,7 @@ void ProjectList::addClipCut(const QString &id, int in, int out, const QString d
         QPixmap p = clip->referencedClip()->thumbProducer()->extractImage(in, (int)(sub->sizeHint(0).height()  * m_render->dar()), sub->sizeHint(0).height() - 2);
         sub->setData(0, Qt::DecorationRole, p);
         m_doc->cachePixmap(clip->getClipHash() + '#' + QString::number(in), p);
-        m_listView->blockSignals(false);
+        monitorItemEditing(true);
     }
     emit projectModified();
 }
@@ -1780,9 +1958,9 @@ void ProjectList::removeClipCut(const QString &id, int in, int out)
         base->removeCutZone(in, out);
         SubProjectItem *sub = getSubItem(clip, QPoint(in, out));
         if (sub) {
-            m_listView->blockSignals(true);
+            monitorItemEditing(false);
             delete sub;
-            m_listView->blockSignals(false);
+            monitorItemEditing(true);
         }
     }
     emit projectModified();
@@ -1824,10 +2002,10 @@ void ProjectList::doUpdateClipCut(const QString &id, const QPoint oldzone, const
         return;
     DocClipBase *base = clip->referencedClip();
     base->updateCutZone(oldzone.x(), oldzone.y(), zone.x(), zone.y(), comment);
-    m_listView->blockSignals(true);
+    monitorItemEditing(false);
     sub->setZone(zone);
     sub->setDescription(comment);
-    m_listView->blockSignals(false);
+    monitorItemEditing(true);
     emit projectModified();
 }
 
@@ -1841,33 +2019,142 @@ void ProjectList::slotForceProcessing(const QString &id)
 void ProjectList::slotAddOrUpdateSequence(const QString frameName)
 {
     QString fileName = KUrl(frameName).fileName().section('_', 0, -2);
-    int count;
-    QString pattern = SlideshowClip::selectedPath(frameName, false, QString(), &count);
+    QStringList list;
+    QString pattern = SlideshowClip::selectedPath(frameName, false, QString(), &list);
+    int count = list.count();
     if (count > 1) {
-         const QList <DocClipBase *> existing = m_doc->clipManager()->getClipByResource(pattern);
-       if (!existing.isEmpty()) {
-           // Sequence already exists, update
-           QString id = existing.at(0)->getId();
-           //ProjectItem *item = getItemById(id);
+        const QList <DocClipBase *> existing = m_doc->clipManager()->getClipByResource(pattern);
+        if (!existing.isEmpty()) {
+            // Sequence already exists, update
+            QString id = existing.at(0)->getId();
+            //ProjectItem *item = getItemById(id);
             QMap <QString, QString> oldprops;
             QMap <QString, QString> newprops;
-           int ttl = existing.at(0)->getProperty("ttl").toInt();
+            int ttl = existing.at(0)->getProperty("ttl").toInt();
             oldprops["out"] = existing.at(0)->getProperty("out");
             newprops["out"] = QString::number(ttl * count - 1);
-           slotUpdateClipProperties(id, newprops);
+            slotUpdateClipProperties(id, newprops);
             EditClipCommand *command = new EditClipCommand(this, id, oldprops, newprops, false);
             m_commandStack->push(command);
-       }
-       else {
-           // Create sequence
-           QStringList groupInfo = getGroup();
-           m_doc->slotCreateSlideshowClipFile(fileName, pattern, count, m_timecode.reformatSeparators(KdenliveSettings::sequence_duration()),
-                                                           false, false, false,
-                                                           m_timecode.getTimecodeFromFrames(int(ceil(m_timecode.fps()))), QString(), 0,
-                                                           QString(), groupInfo.at(0), groupInfo.at(1));
-       }
+        } else {
+            // Create sequence
+            QStringList groupInfo = getGroup();
+            m_doc->slotCreateSlideshowClipFile(fileName, pattern, count, m_timecode.reformatSeparators(KdenliveSettings::sequence_duration()),
+                                               false, false, false,
+                                               m_timecode.getTimecodeFromFrames(int(ceil(m_timecode.fps()))), QString(), 0,
+                                               QString(), groupInfo.at(0), groupInfo.at(1));
+        }
+    } else emit displayMessage(i18n("Sequence not found"), -2);
+}
+
+QMap <QString, QString> ProjectList::getProxies()
+{
+    QMap <QString, QString> list;
+    ProjectItem *item;
+    QTreeWidgetItemIterator it(m_listView);
+    while (*it) {
+        if ((*it)->type() != PROJECTCLIPTYPE) {
+            ++it;
+            continue;
+        }
+        item = static_cast<ProjectItem *>(*it);
+        if (item && item->referencedClip() != NULL) {
+            QString proxy = item->referencedClip()->getProperty("proxy");
+            if (!proxy.isEmpty()) list.insert(proxy, item->clipUrl().path());
+        }
+        ++it;
     }
-    else emit displayMessage(i18n("Sequence not found"), -2);
+    return list;
+}
+
+void ProjectList::updateProxyConfig()
+{
+    ProjectItem *item;
+    QTreeWidgetItemIterator it(m_listView);
+    while (*it) {
+        if ((*it)->type() != PROJECTCLIPTYPE) {
+            ++it;
+            continue;
+        }
+        item = static_cast<ProjectItem *>(*it);
+        if (item == NULL) {
+            ++it;
+            continue;
+        }
+        CLIPTYPE t = item->clipType();
+        if ((t == VIDEO || t == AV || t == UNKNOWN) && item->referencedClip() != NULL) {
+            if  (generateProxy() && useProxy()) {
+                DocClipBase *clip = item->referencedClip();
+                if (clip->getProperty("frame_size").section('x', 0, 0).toInt() > proxyMinSize()) {
+                    connect(clip, SIGNAL(proxyReady(const QString &, bool)), this, SLOT(slotGotProxy(const QString &, bool)));
+                    setProxyStatus(item, 1);
+                    clip->generateProxy(m_doc->projectFolder(), proxyParams());
+                }
+            }
+            else if (!item->referencedClip()->getProperty("proxy").isEmpty()) {
+                // remove proxy
+                item->referencedClip()->clearProperty("proxy");
+                QDomElement e = item->toXml().cloneNode().toElement();
+                e.removeAttribute("file_hash");
+                e.setAttribute("replace", 1);
+                m_infoQueue.insert(item->clipId(), e);
+            }
+        }
+        ++it;
+    }
+    if (!m_infoQueue.isEmpty() && !m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
+}
+
+void ProjectList::slotProxyCurrentItem(bool doProxy)
+{
+    QList<QTreeWidgetItem *> list = m_listView->selectedItems();
+    QTreeWidgetItem *listItem;
+    for (int i = 0; i < list.count(); i++) {
+        listItem = list.at(i);
+        if (listItem->type() == PROJECTFOLDERTYPE) {
+            for (int j = 0; j < listItem->childCount(); j++) {
+                QTreeWidgetItem *sub = listItem->child(j);
+                if (!list.contains(sub)) list.append(sub);
+            }
+        }
+        if (listItem->type() == PROJECTCLIPTYPE) {
+            ProjectItem *item = static_cast <ProjectItem*>(listItem);
+            CLIPTYPE t = item->clipType();
+            if ((t == VIDEO || t == AV || t == UNKNOWN) && item->referencedClip()) {
+                if (doProxy) {
+                    DocClipBase *clip = item->referencedClip();
+                    connect(clip, SIGNAL(proxyReady(const QString&, bool)), this, SLOT(slotGotProxy(const QString&, bool)));
+                    setProxyStatus(item, 1);
+                    clip->generateProxy(m_doc->projectFolder(), proxyParams());
+                }
+                else if (!item->referencedClip()->getProperty("proxy").isEmpty()) {
+                    // remove proxy
+                    if (!item->isProxyRunning()) {
+                        setProxyStatus(item, 0);
+                        QDomElement e = item->toXml().cloneNode().toElement();
+                        e.removeAttribute("file_hash");
+                        e.setAttribute("replace", 1);
+                        m_infoQueue.insert(item->clipId(), e);
+                    }
+                    else setProxyStatus(item, 0);
+                }
+            }
+        }
+    }
+    if (!m_infoQueue.isEmpty() && !m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
+}
+
+void ProjectList::setProxyStatus(ProjectItem *item, int status)
+{
+    monitorItemEditing(false);
+    item->setProxyStatus(status);
+    monitorItemEditing(true);
+}
+
+void ProjectList::monitorItemEditing(bool enable)
+{
+    if (enable) connect(m_listView, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotItemEdited(QTreeWidgetItem *, int)));     
+    else disconnect(m_listView, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotItemEdited(QTreeWidgetItem *, int)));     
 }
 
 #include "projectlist.moc"