]> git.sesse.net Git - kdenlive/blobdiff - src/projectlist.cpp
Improve reloading of externally modified clips (wait for 1.5 second before reloading...
[kdenlive] / src / projectlist.cpp
index b402bd7484ead10fa81359f57591f3ca7ac1a14d..5fb48f090eb8a31478e86ee0f5616378f1430aef 100644 (file)
@@ -79,6 +79,7 @@ ProjectList::ProjectList(QWidget *parent) :
     m_listView = new ProjectListView(this);;
     QVBoxLayout *layout = new QVBoxLayout;
     layout->setContentsMargins(0, 0, 0, 0);
+    layout->setSpacing(0);
 
     // setup toolbar
     KTreeWidgetSearchLine *searchView = new KTreeWidgetSearchLine(this);
@@ -172,7 +173,7 @@ void ProjectList::setupMenu(QMenu *addMenu, QAction *defaultAction)
     m_menu->addActions(addMenu->actions());
 }
 
-void ProjectList::setupGeneratorMenu(QMenu *addMenu, QMenu *transcodeMenu)
+void ProjectList::setupGeneratorMenu(QMenu *addMenu, QMenu *transcodeMenu, QMenu *inTimelineMenu)
 {
     if (!addMenu) return;
     QMenu *menu = m_addButton->menu();
@@ -185,6 +186,8 @@ void ProjectList::setupGeneratorMenu(QMenu *addMenu, QMenu *transcodeMenu)
     if (transcodeMenu->isEmpty()) transcodeMenu->setEnabled(false);
     m_transcodeAction = transcodeMenu;
     m_menu->addAction(m_reloadAction);
+    m_menu->addMenu(inTimelineMenu);
+    inTimelineMenu->setEnabled(false);
     m_menu->addAction(m_editAction);
     m_menu->addAction(m_openAction);
     m_menu->addAction(m_deleteAction);
@@ -202,6 +205,11 @@ void ProjectList::setHeaderInfo(const QByteArray &state)
     m_listView->header()->restoreState(state);
 }
 
+void ProjectList::updateProjectFormat(Timecode t)
+{
+    m_timecode = t;
+}
+
 void ProjectList::slotEditClip()
 {
     QList<QTreeWidgetItem *> list = m_listView->selectedItems();
@@ -350,7 +358,7 @@ void ProjectList::trashUnusedClips()
         it2++;
     }
 
-    m_doc->deleteProjectClip(ids);
+    emit deleteProjectClips(ids, QMap <QString, QString>());
     for (int i = 0; i < urls.count(); i++) {
         KIO::NetAccess::del(KUrl(urls.at(i)), this);
     }
@@ -363,13 +371,22 @@ void ProjectList::slotReloadClip(const QString &id)
     else selected.append(getItemById(id));
     ProjectItem *item;
     for (int i = 0; i < selected.count(); i++) {
-        if (selected.at(i)->type() != PROJECTCLIPTYPE) continue;
+        if (selected.at(i)->type() != PROJECTCLIPTYPE) {
+            if (selected.at(i)->type() == PROJECTFOLDERTYPE) {
+                for (int j = 0; j < selected.at(i)->childCount(); j++)
+                    selected.append(selected.at(i)->child(j));
+            }
+            continue;
+        }
         item = static_cast <ProjectItem *>(selected.at(i));
         if (item) {
-            if (item->clipType() == IMAGE) {
-                item->referencedClip()->producer()->set("force_reload", 1);
-            } else if (item->clipType() == TEXT) {
+            if (item->clipType() == TEXT) {
                 if (!item->referencedClip()->getProperty("xmltemplate").isEmpty()) regenerateTemplate(item);
+            } else if (item->clipType() != COLOR && item->clipType() != SLIDESHOW && item->referencedClip() &&  item->referencedClip()->checkHash() == false) {
+                item->referencedClip()->setPlaceHolder(true);
+                item->setProperty("file_hash", QString());
+            } else if (item->clipType() == 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
@@ -378,6 +395,63 @@ void ProjectList::slotReloadClip(const QString &id)
     }
 }
 
+void ProjectList::slotModifiedClip(const QString &id)
+{
+    ProjectItem *item = getItemById(id);
+    if (item) {
+        item->setData(0, Qt::DecorationRole,  KIcon("view-refresh").pixmap(m_listView->iconSize()));
+    }
+}
+
+void ProjectList::slotMissingClip(const QString &id)
+{
+    ProjectItem *item = getItemById(id);
+    if (item) {
+        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+        if (item->referencedClip()) {
+            item->referencedClip()->setPlaceHolder(true);
+            if (m_render == NULL) kDebug() << "*********  ERROR, NULL RENDR";
+            item->referencedClip()->setProducer(m_render->invalidProducer(id), true);
+            item->slotSetToolTip();
+            emit clipNeedsReload(id, true);
+        }
+    }
+    update();
+    emit displayMessage(i18n("Check missing clips"), -2);
+    emit updateRenderStatus();
+}
+
+void ProjectList::slotAvailableClip(const QString &id)
+{
+    ProjectItem *item = getItemById(id);
+    if (item == NULL) return;
+    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable);
+    if (item->referencedClip()) { // && item->referencedClip()->checkHash() == false) {
+        item->setProperty("file_hash", QString());
+        slotReloadClip(id);
+    }
+    /*else {
+    item->referencedClip()->setValid();
+    item->slotSetToolTip();
+    }
+    update();*/
+    emit updateRenderStatus();
+}
+
+bool ProjectList::hasMissingClips()
+{
+    bool missing = false;
+    QTreeWidgetItemIterator it(m_listView);
+    while (*it) {
+        if ((*it)->type() == PROJECTCLIPTYPE && !((*it)->flags() & Qt::ItemIsDragEnabled)) {
+            missing = true;
+            break;
+        }
+        it++;
+    }
+    return missing;
+}
+
 void ProjectList::setRenderer(Render *projectRender)
 {
     m_render = projectRender;
@@ -386,6 +460,7 @@ void ProjectList::setRenderer(Render *projectRender)
 
 void ProjectList::slotClipSelected()
 {
+    if (!m_listView->isEnabled()) return;
     if (m_listView->currentItem()) {
         if (m_listView->currentItem()->type() == PROJECTFOLDERTYPE) {
             emit clipSelected(NULL);
@@ -421,6 +496,8 @@ void ProjectList::slotClipSelected()
             } else m_openAction->setEnabled(false);
             // Display relevant transcoding actions only
             adjustTranscodeActions(clip);
+            // Display uses in timeline
+            emit findInTimeline(clip->clipId());
         }
     } else {
         emit clipSelected(NULL);
@@ -434,7 +511,7 @@ void ProjectList::slotClipSelected()
 
 void ProjectList::adjustTranscodeActions(ProjectItem *clip) const
 {
-    if (clip == NULL || clip->type() != PROJECTCLIPTYPE || clip->clipType() == COLOR || clip->clipType() == TEXT || clip->clipType() == PLAYLIST) {
+    if (clip == NULL || clip->type() != PROJECTCLIPTYPE || clip->clipType() == COLOR || clip->clipType() == TEXT || clip->clipType() == PLAYLIST || clip->clipType() == SLIDESHOW || clip->clipType() == IMAGE) {
         m_transcodeAction->setEnabled(false);
         return;
     }
@@ -578,6 +655,8 @@ void ProjectList::slotContextMenu(const QPoint &pos, QTreeWidgetItem *item)
             clip = static_cast <ProjectItem*>(item);
             // Display relevant transcoding actions only
             adjustTranscodeActions(clip);
+            // Display uses in timeline
+            emit findInTimeline(clip->clipId());
         } else m_transcodeAction->setEnabled(false);
         if (clip && clip->clipType() == IMAGE && !KdenliveSettings::defaultimageapp().isEmpty()) {
             m_openAction->setIcon(KIcon(KdenliveSettings::defaultimageapp()));
@@ -613,7 +692,7 @@ void ProjectList::slotRemoveClip()
             folderids[folder->groupName()] = folder->clipId();
             int children = folder->childCount();
 
-            if (children > 0 && KMessageBox::questionYesNo(this, i18np("Delete folder <b>%2</b>?<br>This will also remove the clip in that folder", "Delete folder <b>%2</b>?<br>This will also remove the %1 clips in that folder",  children, folder->text(1)), i18n("Delete Folder")) != KMessageBox::Yes) return;
+            if (children > 0 && KMessageBox::questionYesNo(this, i18np("Delete folder <b>%2</b>?<br />This will also remove the clip in that folder", "Delete folder <b>%2</b>?<br />This will also remove the %1 clips in that folder",  children, folder->text(1)), i18n("Delete Folder")) != KMessageBox::Yes) return;
             for (int i = 0; i < children; ++i) {
                 ProjectItem *child = static_cast <ProjectItem *>(folder->child(i));
                 ids << child->clipId();
@@ -622,22 +701,37 @@ void ProjectList::slotRemoveClip()
             ProjectItem *item = static_cast <ProjectItem *>(selected.at(i));
             ids << item->clipId();
             if (item->numReferences() > 0) {
-                if (KMessageBox::questionYesNo(this, 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) return;
+                if (KMessageBox::questionYesNo(this, 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) return;
             }
         }
     }
 
     if (delCommand->childCount() == 0) delete delCommand;
     else m_commandStack->push(delCommand);
-    if (!ids.isEmpty()) m_doc->deleteProjectClip(ids);
-    if (!folderids.isEmpty()) deleteProjectFolder(folderids);
+    emit deleteProjectClips(ids, folderids);
+}
+
+void ProjectList::updateButtons() const
+{
     if (m_listView->topLevelItemCount() == 0) {
-        m_editAction->setEnabled(false);
         m_deleteAction->setEnabled(false);
-        m_openAction->setEnabled(false);
-        m_reloadAction->setEnabled(false);
-        m_transcodeAction->setEnabled(false);
+    } else {
+        m_deleteAction->setEnabled(true);
+        if (!m_listView->currentItem()) m_listView->setCurrentItem(m_listView->topLevelItem(0));
+        QTreeWidgetItem *item = m_listView->currentItem();
+        if (item && item->type() == PROJECTCLIPTYPE) {
+            m_editAction->setEnabled(true);
+            m_openAction->setEnabled(true);
+            m_reloadAction->setEnabled(true);
+            m_transcodeAction->setEnabled(true);
+            return;
+        }
     }
+
+    m_editAction->setEnabled(false);
+    m_openAction->setEnabled(false);
+    m_reloadAction->setEnabled(false);
+    m_transcodeAction->setEnabled(false);
 }
 
 void ProjectList::selectItemById(const QString &clipId)
@@ -655,10 +749,17 @@ void ProjectList::slotDeleteClip(const QString &clipId)
         return;
     }
     m_listView->blockSignals(true);
+    QTreeWidgetItem *newSelectedItem = m_listView->itemAbove(item);
+    if (!newSelectedItem) newSelectedItem = m_listView->itemBelow(item);
     delete item;
     m_doc->clipManager()->deleteClip(clipId);
     m_listView->blockSignals(false);
-    slotClipSelected();
+    if (newSelectedItem) {
+        m_listView->setCurrentItem(newSelectedItem);
+    } else {
+        updateButtons();
+        emit clipSelected(NULL);
+    }
 }
 
 
@@ -681,7 +782,11 @@ void ProjectList::slotAddFolder(const QString foldername, const QString &clipId,
         FolderProjectItem *item = getFolderItemById(clipId);
         if (item) {
             m_doc->clipManager()->deleteFolder(clipId);
+            QTreeWidgetItem *newSelectedItem = m_listView->itemAbove(item);
+            if (!newSelectedItem) newSelectedItem = m_listView->itemBelow(item);
             delete item;
+            if (newSelectedItem) m_listView->setCurrentItem(newSelectedItem);
+            else updateButtons();
         }
     } else {
         if (edit) {
@@ -698,13 +803,13 @@ void ProjectList::slotAddFolder(const QString foldername, const QString &clipId,
                 }
             }
         } else {
-            QStringList text;
-            text << foldername;
             m_listView->blockSignals(true);
-            m_listView->setCurrentItem(new FolderProjectItem(m_listView, text, clipId));
+            m_listView->setCurrentItem(new FolderProjectItem(m_listView, QStringList() << foldername, clipId));
             m_doc->clipManager()->addFolder(clipId, foldername);
             m_listView->blockSignals(false);
+            m_listView->editItem(m_listView->currentItem(), 0);
         }
+        updateButtons();
     }
     m_doc->setModified(true);
 }
@@ -735,6 +840,7 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties)
         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;
     if (!parent.isEmpty()) {
@@ -786,8 +892,10 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties)
             }
         }
     }
-
-    if (getProperties && m_listView->isEnabled()) m_listView->blockSignals(false);
+    if (m_listView->isEnabled()) {
+        updateButtons();
+        if (getProperties) m_listView->blockSignals(false);
+    }
     if (getProperties && !m_queueTimer.isActive()) m_queueTimer.start();
 }
 
@@ -827,7 +935,7 @@ void ProjectList::slotUpdateClip(const QString &id)
 {
     ProjectItem *item = getItemById(id);
     m_listView->blockSignals(true);
-    if (item) item->setData(1, UsageRole, QString::number(item->numReferences()));
+    if (item) item->setData(0, UsageRole, QString::number(item->numReferences()));
     m_listView->blockSignals(false);
 }
 
@@ -869,7 +977,7 @@ void ProjectList::updateAllClips()
                     item->changeDuration(item->referencedClip()->producer()->get_playtime());
                 }
             }
-            item->setData(1, UsageRole, QString::number(item->numReferences()));
+            item->setData(0, UsageRole, QString::number(item->numReferences()));
         }
         //qApp->processEvents();
         ++it;
@@ -888,7 +996,7 @@ void ProjectList::slotAddClip(const QList <QUrl> givenList, const QString &group
     KUrl::List list;
     if (givenList.isEmpty()) {
         // Build list of mime types
-        QStringList mimeTypes = QStringList() << "application/x-kdenlive" << "application/x-kdenlivetitle" << "video/x-flv" << "application/vnd.rn-realmedia" << "video/x-dv" << "video/dv" << "video/x-msvideo" << "video/x-matroska" << "video/mlt-playlist" << "video/mpeg" << "video/ogg" << "video/x-ms-wmv" << "audio/x-flac" << "audio/x-matroska" << "audio/mp4" << "audio/mpeg" << "audio/x-mp3" << "audio/ogg" << "audio/x-wav" << "application/ogg" << "video/mp4" << "video/quicktime" << "image/gif" << "image/jpeg" << "image/png" << "image/x-tga" << "image/x-bmp" << "image/svg+xml" << "image/tiff" << "image/x-xcf-gimp" << "image/x-vnd.adobe.photoshop" << "image/x-pcx" << "image/x-exr";
+        QStringList mimeTypes = QStringList() << "application/x-kdenlive" << "application/x-kdenlivetitle" << "video/x-flv" << "application/vnd.rn-realmedia" << "video/x-dv" << "video/dv" << "video/x-msvideo" << "video/x-matroska" << "video/mlt-playlist" << "video/mpeg" << "video/ogg" << "video/x-ms-wmv" << "audio/x-flac" << "audio/x-matroska" << "audio/mp4" << "audio/mpeg" << "audio/x-mp3" << "audio/ogg" << "audio/x-wav" << "application/ogg" << "video/mp4" << "video/quicktime" << "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;
         foreach(const QString& mimeType, mimeTypes) {
@@ -931,14 +1039,14 @@ void ProjectList::slotRemoveInvalidClip(const QString &id, bool replace)
         const QString path = item->referencedClip()->fileURL().path();
         if (item->referencedClip()->isPlaceHolder()) replace = false;
         if (!path.isEmpty()) {
-            if (replace) KMessageBox::sorry(this, i18n("Clip <b>%1</b><br>is invalid, will be removed from project.", path));
+            if (replace) KMessageBox::sorry(this, i18n("Clip <b>%1</b><br />is invalid, will be removed from project.", path));
             else {
-                if (KMessageBox::questionYesNo(this, i18n("Clip <b>%1</b><br>is missing or invalid. Remove it from project?", path), i18n("Invalid clip")) == KMessageBox::Yes) replace = true;
+                if (KMessageBox::questionYesNo(this, i18n("Clip <b>%1</b><br />is missing or invalid. Remove it from project?", path), i18n("Invalid clip")) == KMessageBox::Yes) replace = true;
             }
         }
         QStringList ids;
         ids << id;
-        if (replace) m_doc->deleteProjectClip(ids);
+        if (replace) emit deleteProjectClips(ids, QMap <QString, QString>());
     }
 }
 
@@ -952,7 +1060,9 @@ void ProjectList::slotAddColorClip()
     dia_ui.setupUi(dia);
     dia->setWindowTitle(i18n("Color Clip"));
     dia_ui.clip_name->setText(i18n("Color Clip"));
-    dia_ui.clip_duration->setText(KdenliveSettings::color_duration());
+    dia_ui.clip_duration->setInputMask("");
+    dia_ui.clip_duration->setValidator(m_timecode.validator());
+    dia_ui.clip_duration->setText(m_timecode.reformatSeparators(KdenliveSettings::color_duration()));
     if (dia->exec() == QDialog::Accepted) {
         QString color = dia_ui.clip_color->color().name();
         color = color.replace(0, 1, "0x") + "ff";
@@ -1002,6 +1112,8 @@ void ProjectList::slotAddTitleTemplateClip()
     for (int i = 0; i < templateFiles.size(); ++i) {
         dia_ui.template_list->comboBox()->addItem(templateFiles.at(i), path + templateFiles.at(i));
     }
+    if (!templateFiles.isEmpty())
+        dia_ui.buttonBox->button(QDialogButtonBox::Ok)->setFocus();
     dia_ui.template_list->fileDialog()->setFilter("*.kdenlivetitle");
     //warning: setting base directory doesn't work??
     KUrl startDir(path);
@@ -1061,6 +1173,9 @@ void ProjectList::setDocument(KdenliveDoc *doc)
     m_listView->blockSignals(false);
     m_toolbar->setEnabled(true);
     connect(m_doc->clipManager(), SIGNAL(reloadClip(const QString &)), this, SLOT(slotReloadClip(const QString &)));
+    connect(m_doc->clipManager(), SIGNAL(modifiedClip(const QString &)), this, SLOT(slotModifiedClip(const QString &)));
+    connect(m_doc->clipManager(), SIGNAL(missingClip(const QString &)), this, SLOT(slotMissingClip(const QString &)));
+    connect(m_doc->clipManager(), SIGNAL(availableClip(const QString &)), this, SLOT(slotAvailableClip(const QString &)));
     connect(m_doc->clipManager(), SIGNAL(checkAllClips()), this, SLOT(updateAllClips()));
 }
 
@@ -1097,6 +1212,7 @@ void ProjectList::slotCheckForEmptyQueue()
         emit displayMessage(QString(), -1);
         m_listView->blockSignals(false);
         m_listView->setEnabled(true);
+        updateButtons();
     } else if (!m_refreshed) QTimer::singleShot(300, this, SLOT(slotCheckForEmptyQueue()));
 }
 
@@ -1201,14 +1317,21 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce
         }
         //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 (!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());
+            //TODO: Make sure the line below has no side effect
+            toReload = clipId;
         }
         /*else {
             // Check if duration changed.
@@ -1221,13 +1344,12 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce
         }*/
     } else kDebug() << "////////  COULD NOT FIND CLIP TO UPDATE PRPS...";
     int max = m_doc->clipManager()->clipsCount();
-    emit displayMessage(i18n("Loading clips"), (int)(100 *(max - m_infoQueue.count()) / max));
-    // small delay so that the app can display the progress info
     if (item && m_infoQueue.isEmpty() && m_thumbnailQueue.isEmpty()) {
         m_listView->setCurrentItem(item);
         emit clipSelected(item->referencedClip());
-    }
+    } else 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()));
 }
 
@@ -1418,6 +1540,7 @@ void ProjectList::addClipCut(const QString &id, int in, int out, const QString d
         SubProjectItem *sub = new SubProjectItem(clip, in, out, desc);
         if (newItem && desc.isEmpty() && !m_listView->isColumnHidden(1)) {
             if (!clip->isExpanded()) clip->setExpanded(true);
+            m_listView->scrollToItem(sub);
             m_listView->editItem(sub, 1);
         }
         QPixmap p = clip->referencedClip()->thumbProducer()->extractImage(in, (int)(sub->sizeHint(0).height()  * m_render->dar()), sub->sizeHint(0).height() - 2);