]> git.sesse.net Git - kdenlive/blobdiff - src/customtrackview.cpp
make slot names more kdenlive conform
[kdenlive] / src / customtrackview.cpp
index cf1ac2294f639750d4c89a083dd5bc059db64500..de183f879c83acb42a65431e0f46ad5d0a019762 100644 (file)
@@ -26,6 +26,7 @@
 #include "commands/movetransitioncommand.h"
 #include "commands/resizeclipcommand.h"
 #include "commands/editguidecommand.h"
+#include "commands/addextradatacommand.h"
 #include "commands/addtimelineclipcommand.h"
 #include "commands/addeffectcommand.h"
 #include "commands/editeffectcommand.h"
@@ -51,6 +52,7 @@
 #include "commands/changeeffectstatecommand.h"
 #include "commands/movegroupcommand.h"
 #include "ui_addtrack_ui.h"
+#include "ui_importkeyframesdialog_ui.h"
 #include "initeffects.h"
 #include "commands/locktrackcommand.h"
 #include "commands/groupclipscommand.h"
@@ -73,6 +75,7 @@
 #include <KCursor>
 #include <KMessageBox>
 #include <KIO/NetAccess>
+#include <KFileDialog>
 
 #include <QMouseEvent>
 #include <QStylePainter>
@@ -401,6 +404,15 @@ void CustomTrackView::slotCheckPositionScrolling()
     }
 }
 
+void CustomTrackView::slotAlignPlayheadToMousePos()
+{
+       /* get curser point ref in screen coord */
+       QPoint ps = QCursor::pos();
+       /* get xPos in scene coord */
+       int mappedXPos = qMax((int)(mapToScene(mapFromGlobal(ps)).x() + 0.5), 0);
+       /* move playhead to new xPos*/
+       seekCursorPos(mappedXPos);
+}
 
 // virtual
 void CustomTrackView::mouseMoveEvent(QMouseEvent * event)
@@ -415,7 +427,7 @@ void CustomTrackView::mouseMoveEvent(QMouseEvent * event)
     if (dragMode() == QGraphicsView::RubberBandDrag || (event->modifiers() == Qt::ControlModifier && m_tool != SPACERTOOL && m_operationMode != RESIZESTART && m_operationMode != RESIZEEND)) {
         event->setAccepted(true);
         m_moveOpMode = NONE;
-        QGraphicsView::mouseMoveEvent(event);
+        if (event->modifiers() != Qt::ControlModifier || dragMode() == QGraphicsView::RubberBandDrag) QGraphicsView::mouseMoveEvent(event);
         return;
     }
 
@@ -738,6 +750,10 @@ void CustomTrackView::mousePressEvent(QMouseEvent * event)
         setDragMode(QGraphicsView::RubberBandDrag);
         if (!(event->modifiers() & Qt::ControlModifier)) {
             resetSelectionGroup();
+           if (m_dragItem) {
+               emit clipItemSelected(NULL);
+               m_dragItem = NULL;
+           }
             scene()->clearSelection();
         }
         m_blockRefresh = false;
@@ -979,27 +995,34 @@ void CustomTrackView::mousePressEvent(QMouseEvent * event)
     else if (dragGroup && dragGroup->isSelected())
         itemSelected = true;
 
-    if (event->modifiers() == Qt::ControlModifier || itemSelected == false) {
+    if ((event->modifiers() == Qt::ControlModifier) || itemSelected == false) {
         if (event->modifiers() != Qt::ControlModifier) {
             resetSelectionGroup(false);
             m_scene->clearSelection();
             // A refresh seems necessary otherwise in zoomed mode, some clips disappear
             viewport()->update();
-        } else resetSelectionGroup();
+        } else {
+           resetSelectionGroup();
+       }
         dragGroup = NULL;
         if (m_dragItem->parentItem() && m_dragItem->parentItem()->type() == GROUPWIDGET) {
             dragGroup = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
         }
         bool selected = !m_dragItem->isSelected();
-        if (dragGroup)
+        /*if (dragGroup)
             dragGroup->setSelected(selected);
-        else
+        else*/
             m_dragItem->setSelected(selected);
-
+       if (selected == false) {
+           m_dragItem = NULL;
+       }
         groupSelectedItems();
-        ClipItem *clip = static_cast <ClipItem *>(m_dragItem);
-        updateClipTypeActions(dragGroup == NULL ? clip : NULL);
-        m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
+       if (m_dragItem) { 
+           ClipItem *clip = static_cast <ClipItem *>(m_dragItem);
+           updateClipTypeActions(dragGroup == NULL ? clip : NULL);
+           m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
+       }
+       else updateClipTypeActions(NULL);
     }
 
     if (collisionClip != NULL || m_dragItem == NULL) {
@@ -1012,9 +1035,11 @@ void CustomTrackView::mousePressEvent(QMouseEvent * event)
     }
 
     // If clicked item is selected, allow move
-    if (event->modifiers() != Qt::ControlModifier && m_operationMode == NONE) QGraphicsView::mousePressEvent(event);
+    //if (!(event->modifiers() | Qt::ControlModifier) && m_operationMode == NONE)
+    QGraphicsView::mousePressEvent(event);
 
-    m_clickPoint = QPoint((int)(mapToScene(event->pos()).x() - m_dragItem->startPos().frames(m_document->fps())), (int)(event->pos().y() - m_dragItem->pos().y()));
+    if (m_dragItem) {
+       m_clickPoint = QPoint((int)(mapToScene(event->pos()).x() - m_dragItem->startPos().frames(m_document->fps())), (int)(event->pos().y() - m_dragItem->pos().y()));
     if (m_selectionGroup && m_dragItem->parentItem() == m_selectionGroup) {
         // all other modes break the selection, so the user probably wants to move it
         m_operationMode = MOVE;
@@ -1025,6 +1050,7 @@ void CustomTrackView::mousePressEvent(QMouseEvent * event)
        }
         else m_operationMode = m_dragItem->operationMode(mapToScene(event->pos()));
     }
+    } else m_operationMode = NONE;
     m_controlModifier = (event->modifiers() == Qt::ControlModifier);
 
     // Update snap points
@@ -1202,6 +1228,9 @@ void CustomTrackView::groupSelectedItems(bool force, bool createNewGroup)
         return;
     }
     QList<QGraphicsItem *> selection = m_scene->selectedItems();
+    if (m_dragItem && !selection.contains(m_dragItem)) {
+       selection << m_dragItem;
+    }
     if (selection.isEmpty()) return;
     QRectF rectUnion;
     // Find top left position of selection
@@ -1488,7 +1517,15 @@ void CustomTrackView::insertClipCut(DocClipBase *clip, int in, int out)
     pasteInfo.startPos = GenTime(m_cursorPos, m_document->fps());
     pasteInfo.endPos = pasteInfo.startPos + info.endPos;
     pasteInfo.track = selectedTrack();
-    if (!canBePastedTo(pasteInfo, AVWIDGET)) {
+    bool ok = canBePastedTo(pasteInfo, AVWIDGET);
+    if (!ok) {
+       // Cannot be inserted at cursor pos, insert at end of track
+       int duration = m_document->renderer()->mltTrackDuration(m_document->tracksCount() - pasteInfo.track) + 1;
+       pasteInfo.startPos = GenTime(duration, m_document->fps());
+       pasteInfo.endPos = pasteInfo.startPos + info.endPos;
+       ok = canBePastedTo(pasteInfo, AVWIDGET);
+    }
+    if (!ok) {
         emit displayMessage(i18n("Cannot insert clip in timeline"), ErrorMessage);
         return;
     }
@@ -1565,7 +1602,8 @@ bool CustomTrackView::insertDropClips(const QMimeData *data, const QPoint &pos)
 
         // Check if clips can be inserted at that position
         for (int i = 0; i < ids.size(); ++i) {
-            DocClipBase *clip = m_document->getBaseClip(ids.at(i));
+           QString clipData = ids.at(i);
+            DocClipBase *clip = m_document->getBaseClip(clipData.section('/', 0, 0));
             if (clip == NULL) {
                 kDebug() << " WARNING))))))))) CLIP NOT FOUND : " << ids.at(i);
                 return false;
@@ -1576,11 +1614,20 @@ bool CustomTrackView::insertDropClips(const QMimeData *data, const QPoint &pos)
             }
             ItemInfo info;
             info.startPos = start;
-            info.cropDuration = clip->duration();
-            info.endPos = info.startPos + info.cropDuration;
+           if (clipData.contains('/')) {
+               // this is a clip zone, set in / out
+               int in = clipData.section('/', 1, 1).toInt();
+               int out = clipData.section('/', 2, 2).toInt();
+               info.cropStart = GenTime(in, m_document->fps());
+               info.cropDuration = GenTime(out - in, m_document->fps());
+           }
+            else {
+               info.cropDuration = clip->duration();
+           }
+           info.endPos = info.startPos + info.cropDuration;
             info.track = track;
             infoList.append(info);
-            start += clip->duration();
+            start += info.cropDuration;
         }
         if (!canBePastedTo(infoList, AVWIDGET)) {
             return true;
@@ -1588,11 +1635,21 @@ bool CustomTrackView::insertDropClips(const QMimeData *data, const QPoint &pos)
         m_selectionGroup = new AbstractGroupItem(m_document->fps());
         start = GenTime();
         for (int i = 0; i < ids.size(); ++i) {
-            DocClipBase *clip = m_document->getBaseClip(ids.at(i));
+            QString clipData = ids.at(i);
+            DocClipBase *clip = m_document->getBaseClip(clipData.section('/', 0, 0));
             ItemInfo info;
             info.startPos = start;
-            info.cropDuration = clip->duration();
-            info.endPos = info.startPos + info.cropDuration;
+           if (clipData.contains('/')) {
+               // this is a clip zone, set in / out
+               int in = clipData.section('/', 1, 1).toInt();
+               int out = clipData.section('/', 2, 2).toInt();
+               info.cropStart = GenTime(in, m_document->fps());
+               info.cropDuration = GenTime(out - in, m_document->fps());
+           }
+            else {
+               info.cropDuration = clip->duration();
+           }
+           info.endPos = info.startPos + info.cropDuration;
             info.track = 0;
             start += info.cropDuration;
             offsetList.append(start);
@@ -1743,7 +1800,7 @@ void CustomTrackView::deleteEffect(int track, GenTime pos, QDomElement effect)
     }
 }
 
-void CustomTrackView::slotAddGroupEffect(QDomElement effect, AbstractGroupItem *group)
+void CustomTrackView::slotAddGroupEffect(QDomElement effect, AbstractGroupItem *group, AbstractClipItem *dropTarget)
 {
     QList<QGraphicsItem *> itemList = group->childItems();
     QUndoCommand *effectCommand = new QUndoCommand();
@@ -1782,6 +1839,12 @@ void CustomTrackView::slotAddGroupEffect(QDomElement effect, AbstractGroupItem *
         m_commandStack->push(effectCommand);
         setDocumentModified();
     } else delete effectCommand;
+    if (dropTarget) {
+       clearSelection(false);
+       m_dragItem = dropTarget;
+       m_dragItem->setSelected(true);
+       emit clipItemSelected(static_cast<ClipItem *>(dropTarget));
+    }
 }
 
 void CustomTrackView::slotAddEffect(ClipItem *clip, QDomElement effect)
@@ -1858,12 +1921,23 @@ void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track)
                        clearSelection(false);
                        clip->setSelected(true);
                        m_dragItem = clip;
-                       emit clipItemSelected(clip);
                    }
+                   emit clipItemSelected(clip);
                    break;
                }
            }
        }
+       else {
+           for (int i = 0; i < itemList.count(); i++) {
+               if (itemList.at(i)->type() == AVWIDGET) {
+                   ClipItem *clip = static_cast<ClipItem *>(itemList.at(i));
+                   if (clip->isMainSelectedClip()) {
+                       emit clipItemSelected(clip);
+                       break;
+                   }
+               }
+           }
+       }
     } else delete effectCommand;
 }
 
@@ -3359,7 +3433,7 @@ int CustomTrackView::seekPosition() const
 }
 
 
-void CustomTrackView::setCursorPos(int pos, bool seek)
+void CustomTrackView::setCursorPos(int pos)
 {
     if (pos != m_cursorPos) {
        emit cursorMoved((int)(m_cursorPos), (int)(pos));
@@ -3409,7 +3483,7 @@ void CustomTrackView::checkScrolling()
 void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
 {
     if (m_moveOpMode == SEEK) m_moveOpMode = NONE;
-    QGraphicsView::mouseReleaseEvent(event);
+    if (!m_controlModifier) QGraphicsView::mouseReleaseEvent(event);
     setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
 #if QT_VERSION >= 0x040600
     if (m_dragItem) m_dragItem->setGraphicsEffect(NULL);
@@ -5195,16 +5269,49 @@ void CustomTrackView::clipEnd()
     }
 }
 
-void CustomTrackView::slotAddClipMarker(const QString &id, GenTime t, QString c)
+void CustomTrackView::slotAddClipExtraData(const QString &id, const QString &key, const QString &data, QUndoCommand *groupCommand)
 {
-    QString oldcomment = m_document->clipManager()->getClipById(id)->markerComment(t);
-    AddMarkerCommand *command = new AddMarkerCommand(this, oldcomment, c, id, t);
-    m_commandStack->push(command);
+    DocClipBase *base = m_document->clipManager()->getClipById(id);
+    if (!base) return;
+    QMap <QString, QString> extraData = base->analysisData();
+    QString oldData = extraData.value(key);
+    AddExtraDataCommand *command = new AddExtraDataCommand(this, id, key, oldData, data, groupCommand);
+    if (!groupCommand) m_commandStack->push(command);
+}
+
+void CustomTrackView::slotAddClipMarker(const QString &id, QList <CommentedTime> newMarkers, QUndoCommand *groupCommand)
+{
+    QUndoCommand *subCommand = NULL;
+    if (newMarkers.count() > 1 && groupCommand == NULL) {
+       subCommand = new QUndoCommand;
+       subCommand->setText("Add markers");
+    }
+    for (int i = 0; i < newMarkers.count(); i++) {
+       CommentedTime oldMarker = m_document->clipManager()->getClipById(id)->markerAt(newMarkers.at(i).time());
+       if (oldMarker == CommentedTime()) {
+           oldMarker = newMarkers.at(i);
+           oldMarker.setMarkerType(-1);
+       }
+       if (newMarkers.count() == 1 && !groupCommand) {
+           AddMarkerCommand *command = new AddMarkerCommand(this, oldMarker, newMarkers.at(i), id, groupCommand);
+           m_commandStack->push(command);
+       }
+       else if (groupCommand) {
+           (void) new AddMarkerCommand(this, oldMarker, newMarkers.at(i), id, groupCommand);
+       }
+       else {
+           (void) new AddMarkerCommand(this, oldMarker, newMarkers.at(i), id, subCommand);
+       }
+    }
+    if (subCommand) m_commandStack->push(subCommand);
 }
 
 void CustomTrackView::slotDeleteClipMarker(const QString &comment, const QString &id, const GenTime &position)
 {
-    AddMarkerCommand *command = new AddMarkerCommand(this, comment, QString(), id, position);
+    CommentedTime oldmarker(position, comment);
+    CommentedTime marker = oldmarker;
+    marker.setMarkerType(-1);
+    AddMarkerCommand *command = new AddMarkerCommand(this, oldmarker, marker, id);
     m_commandStack->push(command);
 }
 
@@ -5221,21 +5328,156 @@ void CustomTrackView::slotDeleteAllClipMarkers(const QString &id)
     deleteMarkers->setText("Delete clip markers");
 
     for (int i = 0; i < markers.size(); i++) {
-        new AddMarkerCommand(this, markers.at(i).comment(), QString(), id, markers.at(i).time(), deleteMarkers);
+       CommentedTime oldMarker = markers.at(i);
+       CommentedTime marker = oldMarker;
+       marker.setMarkerType(-1);
+        new AddMarkerCommand(this, oldMarker, marker, id, deleteMarkers);
     }
     m_commandStack->push(deleteMarkers);
 }
 
-void CustomTrackView::addMarker(const QString &id, const GenTime &pos, const QString &comment)
+void CustomTrackView::slotSaveClipMarkers(const QString &id)
+{
+    DocClipBase *base = m_document->clipManager()->getClipById(id);
+    QList < CommentedTime > markers = base->commentedSnapMarkers();
+    if (!markers.isEmpty()) {
+       // Set  up categories
+       QComboBox *cbox = new QComboBox;
+       cbox->insertItem(0, i18n("All categories"));
+       for (int i = 0; i < 5; ++i) {
+           cbox->insertItem(i + 1, i18n("Category %1", i));
+           cbox->setItemData(i + 1, CommentedTime::markerColor(i), Qt::DecorationRole);
+       }
+       cbox->setCurrentIndex(0);
+       KFileDialog fd(KUrl("kfiledialog:///projectfolder"), "text/plain", this, cbox);
+       fd.setMode(KFile::File);
+       fd.setOperationMode(KFileDialog::Saving);
+       fd.exec();
+       QString url = fd.selectedFile();
+       //QString url = KFileDialog::getSaveFileName(KUrl("kfiledialog:///projectfolder"), "text/plain", this, i18n("Save markers"));
+       if (url.isEmpty()) return;
+
+       QString data;
+       int category = cbox->currentIndex() - 1;
+       for (int i = 0; i < markers.count(); i++) {
+           if (category >= 0) {
+               // Save only the markers in selected category
+               if (markers.at(i).markerType() != category) continue;
+           }
+           data.append(QString::number(markers.at(i).time().seconds()));
+           data.append("\t");
+           data.append(QString::number(markers.at(i).time().seconds()));
+           data.append("\t");
+           data.append(markers.at(i).comment());
+           data.append("\n");
+       }
+       delete cbox;
+       
+       QFile file(url);
+       if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+           emit displayMessage(i18n("Cannot open file %1", url), ErrorMessage);
+           return;
+       }
+       file.write(data.toUtf8());
+       file.close();
+    }
+}
+
+void CustomTrackView::slotLoadClipMarkers(const QString &id)
+{
+    QComboBox *cbox = new QComboBox;
+    for (int i = 0; i < 5; ++i) {
+       cbox->insertItem(i, i18n("Category %1", i));
+       cbox->setItemData(i, CommentedTime::markerColor(i), Qt::DecorationRole);
+    }
+    cbox->setCurrentIndex(KdenliveSettings::default_marker_type());
+    KFileDialog fd(KUrl("kfiledialog:///projectfolder"), "text/plain", this, cbox);
+    fd.setMode(KFile::File);
+    fd.setOperationMode(KFileDialog::Opening);
+    fd.exec();
+    QString url = fd.selectedFile();
+       
+    //KUrl url = KFileDialog::getOpenUrl(KUrl("kfiledialog:///projectfolder"), "text/plain", this, i18n("Load marker file"));
+    if (url.isEmpty()) return;
+    int category = cbox->currentIndex();
+    delete cbox;
+    QFile file(url);
+    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+       emit displayMessage(i18n("Cannot open file %1", KUrl(url).fileName()), ErrorMessage);
+       return;
+    }
+    QString data = QString::fromUtf8(file.readAll());
+    file.close();
+    QStringList lines = data.split("\n", QString::SkipEmptyParts);
+    QStringList values;
+    bool ok;
+    QUndoCommand *command = new QUndoCommand();
+    command->setText("Load markers");
+    QString markerText;
+    QList <CommentedTime> markersList;
+    foreach(QString line, lines) {
+       markerText.clear();
+       values = line.split("\t", QString::SkipEmptyParts);
+       double time1 = values.at(0).toDouble(&ok);
+       double time2 = -1;
+       if (!ok) continue;
+       if (values.count() >1) {
+           time2 = values.at(1).toDouble(&ok);
+           if (values.count() == 2) {
+               // Check if second value is a number or text
+               if (!ok) {
+                   time2 = -1;
+                   markerText = values.at(1);
+               }
+               else markerText = i18n("Marker");
+           }
+           else {
+               // We assume 3 values per line: in out name
+               if (!ok) {
+                   // 2nd value is not a number, drop
+               }
+               else {
+                   markerText = values.at(2);
+               }
+           }
+       }
+       if (!markerText.isEmpty()) {
+           // Marker found, add it
+           //TODO: allow user to set a marker category
+           CommentedTime marker1(GenTime(time1), markerText, category);
+           markersList << marker1;
+           if (time2 > 0 && time2 != time1) {
+               CommentedTime marker2(GenTime(time2), markerText, category);
+               markersList << marker2;
+           }
+       }
+    }
+    if (!markersList.isEmpty()) slotAddClipMarker(id, markersList, command);
+    if (command->childCount() > 0) m_commandStack->push(command);
+    else delete command;
+}
+
+void CustomTrackView::addMarker(const QString &id, const CommentedTime marker)
 {
     DocClipBase *base = m_document->clipManager()->getClipById(id);
-    if (!comment.isEmpty()) base->addSnapMarker(pos, comment);
-    else base->deleteSnapMarker(pos);
+    if (base == NULL) return;
+    if (marker.markerType() < 0) base->deleteSnapMarker(marker.time());
+    else base->addSnapMarker(marker);
     emit updateClipMarkers(base);
     setDocumentModified();
     viewport()->update();
 }
 
+void CustomTrackView::addData(const QString &id, const QString &key, const QString &data)
+{
+    DocClipBase *base = m_document->clipManager()->getClipById(id);
+    if (base == NULL) return;
+    base->setAnalysisData(key, data);
+    emit updateClipExtraData(base);
+    setDocumentModified();
+    viewport()->update();
+}
+
 int CustomTrackView::hasGuide(int pos, int offset)
 {
     for (int i = 0; i < m_guides.count(); i++) {
@@ -5447,7 +5689,9 @@ void CustomTrackView::drawBackground(QPainter * painter, const QRectF &rect)
 {
     painter->setClipRect(rect);
     QPen pen1 = painter->pen();
-    pen1.setColor(palette().dark().color());
+    QColor lineColor = palette().dark().color();
+    lineColor.setAlpha(100);
+    pen1.setColor(lineColor);
     painter->setPen(pen1);
     double min = rect.left();
     double max = rect.right();
@@ -5821,7 +6065,8 @@ ClipItem *CustomTrackView::getClipUnderCursor() const
     QList<QGraphicsItem *> collisions = scene()->items(rect, Qt::IntersectsItemBoundingRect);
     for (int i = 0; i < collisions.count(); i++) {
         if (collisions.at(i)->type() == AVWIDGET) {
-            return static_cast < ClipItem *>(collisions.at(i));
+           ClipItem *clip = static_cast < ClipItem *>(collisions.at(i));
+           if (!clip->isItemLocked()) return clip;
         }
     }
     return NULL;
@@ -7083,18 +7328,18 @@ EffectsParameterList CustomTrackView::getEffectArgs(const QDomElement &effect)
            parameters.addParam(QString("filter%1.tag").arg(subeffectix), subeffect.attribute("tag"));
            parameters.addParam(QString("filter%1.kdenlive_info").arg(subeffectix), subeffect.attribute("kdenlive_info"));
            QDomNodeList subparams = subeffect.elementsByTagName("parameter");
-           adjustEffectParameters(parameters, subparams, QString("filter%1.").arg(subeffectix));
+           adjustEffectParameters(parameters, subparams, m_document->mltProfile(), QString("filter%1.").arg(subeffectix));
        }
     }
 
     QDomNodeList params = effect.elementsByTagName("parameter");
-    adjustEffectParameters(parameters, params);
+    adjustEffectParameters(parameters, params, m_document->mltProfile());
     
     return parameters;
 }
 
 
-void CustomTrackView::adjustEffectParameters(EffectsParameterList &parameters, QDomNodeList params, const QString &prefix)
+void CustomTrackView::adjustEffectParameters(EffectsParameterList &parameters, QDomNodeList params, MltVideoProfile profile, const QString &prefix)
 {
   QLocale locale;
   for (int i = 0; i < params.count(); i++) {
@@ -7145,7 +7390,7 @@ void CustomTrackView::adjustEffectParameters(EffectsParameterList &parameters, Q
             if (e.attribute("factor", "1") != "1" || e.attribute("offset", "0") != "0") {
                 double fact;
                 if (e.attribute("factor").contains('%')) {
-                    fact = ProfilesDialog::getStringEval(m_document->mltProfile(), e.attribute("factor"));
+                    fact = ProfilesDialog::getStringEval(profile, e.attribute("factor"));
                 } else {
                     fact = e.attribute("factor", "1").toDouble();
                 }
@@ -7255,7 +7500,7 @@ void CustomTrackView::adjustEffects(ClipItem* item, ItemInfo oldInfo, QUndoComma
 }
 
 
-void CustomTrackView::slotGotFilterJobResults(const QString &/*id*/, int startPos, int track, const QString &filter, stringMap filterParams)
+void CustomTrackView::slotGotFilterJobResults(const QString &/*id*/, int startPos, int track, const QString &filter, stringMap filterParams, QStringList extra)
 {
     ClipItem *clip = getClipItemAt(GenTime(startPos, m_document->fps()), track);
     if (clip == NULL) {
@@ -7279,3 +7524,73 @@ void CustomTrackView::slotGotFilterJobResults(const QString &/*id*/, int startPo
 }
 
 
+void CustomTrackView::slotImportClipKeyframes(GRAPHICSRECTITEM type)
+{
+    if (!m_selectionGroup) {
+       emit displayMessage(i18n("You need to select one clip and one transition"), ErrorMessage);
+       return;
+    }
+    // Make sure there is no collision
+    QList<QGraphicsItem *> children = m_selectionGroup->childItems();
+    ClipItem *item = NULL;
+    for (int i = 0; i < children.count(); i++) {
+       if (children.at(i)->type() == AVWIDGET) {
+            item = (ClipItem*) children.at(i);
+            break;
+        }
+    }
+    if (!item) {
+       emit displayMessage(i18n("No clip found"), ErrorMessage);
+       return;
+    }
+    QMap <QString, QString> data = item->baseClip()->analysisData();
+    if (data.isEmpty()) {
+       emit displayMessage(i18n("No keyframe data found in clip"), ErrorMessage);
+       return;
+    }
+    QPointer<QDialog> d = new QDialog(this);
+    Ui::ImportKeyframesDialog_UI ui;
+    ui.setupUi(d);
+
+    // Set  up data
+    int ix = 0;
+    QMap<QString, QString>::const_iterator i = data.constBegin();
+    while (i != data.constEnd()) {
+       ui.data_list->insertItem(ix, i.key());
+       ui.data_list->setItemData(ix, i.value(), Qt::UserRole);
+       ++i;
+       ix++;
+    }
+
+    if (d->exec() != QDialog::Accepted) {
+       delete d;
+       return;
+    }
+    QString keyframeData = ui.data_list->itemData(ui.data_list->currentIndex()).toString();
+    QStringList keyframeList = keyframeData.split(';', QString::SkipEmptyParts);
+    QString result;
+    if (ui.import_position->isChecked()) {
+       if (ui.import_size->isChecked()) {
+           foreach(QString key, keyframeList) {
+               if (key.count(':') > 1) result.append(key.section(':', 0, 1));
+               else result.append(key);
+               result.append(';');
+           }
+       }
+       else {
+           foreach(QString key, keyframeList) {
+               result.append(key.section(':', 0, 0));
+               result.append(';');
+           }
+       }
+    }
+    else if (ui.import_size->isChecked()) {
+       foreach(QString key, keyframeList) {
+           result.append(key.section(':', 1, 1));
+           result.append(';');
+       }
+    }
+    emit importKeyframes(type, result);
+    delete d;
+}
+