X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fcustomtrackview.cpp;h=d03e745dbc32830696c0262b513685e29ddf4685;hb=71e41ad0c197e2e2eb033dff7800427f64d3428f;hp=8313d813ea0db6c14859d5436d381d2427c21a99;hpb=9716fdce57126712fc4cd32a2312c4250a1028d2;p=kdenlive diff --git a/src/customtrackview.cpp b/src/customtrackview.cpp index 8313d813..d03e745d 100644 --- a/src/customtrackview.cpp +++ b/src/customtrackview.cpp @@ -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 #include #include +#include #include #include @@ -87,6 +90,8 @@ #include #endif +#define SEEK_INACTIVE (-1) + //#define DEBUG bool sortGuidesList(const Guide *g1 , const Guide *g2) @@ -388,16 +393,26 @@ void CustomTrackView::slotCheckPositionScrolling() // If mouse is at a border of the view, scroll if (m_moveOpMode != SEEK) return; if (mapFromScene(m_cursorPos, 0).x() < 3) { + if (horizontalScrollBar()->value() == 0) return; horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 2); QTimer::singleShot(200, this, SLOT(slotCheckPositionScrolling())); - setCursorPos(mapToScene(QPoint(-2, 0)).x()); + seekCursorPos(mapToScene(QPoint(-2, 0)).x()); } else if (viewport()->width() - 3 < mapFromScene(m_cursorPos + 1, 0).x()) { horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 2); - setCursorPos(mapToScene(QPoint(viewport()->width(), 0)).x() + 1); + seekCursorPos(mapToScene(QPoint(viewport()->width(), 0)).x() + 1); QTimer::singleShot(200, this, SLOT(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) @@ -412,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; } @@ -420,6 +435,7 @@ void CustomTrackView::mouseMoveEvent(QMouseEvent * event) bool move = (event->pos() - m_clickEvent).manhattanLength() >= QApplication::startDragDistance(); if (m_dragItem && m_tool == SELECTTOOL) { if (m_operationMode == MOVE && move) { + //m_dragItem->setProperty("y_absolute", event->pos().y()); QGraphicsView::mouseMoveEvent(event); // If mouse is at a border of the view, scroll if (pos < 5) { @@ -706,7 +722,7 @@ void CustomTrackView::mouseMoveEvent(QMouseEvent * event) if (event->buttons() != Qt::NoButton && event->modifiers() == Qt::NoModifier) { QGraphicsView::mouseMoveEvent(event); m_moveOpMode = SEEK; - setCursorPos(mappedXPos); + seekCursorPos(mappedXPos); slotCheckPositionScrolling(); return; } else m_moveOpMode = NONE; @@ -734,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; @@ -797,6 +817,7 @@ void CustomTrackView::mousePressEvent(QMouseEvent * event) else m_dragItem = collisionClip; found = true; + m_dragItem->setProperty("y_absolute", mapToScene(m_clickEvent).y() - m_dragItem->scenePos().y()); m_dragItemInfo = m_dragItem->info(); if (m_dragItem->parentItem() && m_dragItem->parentItem()->type() == GROUPWIDGET && m_dragItem->parentItem() != m_selectionGroup) { // kDebug()<<"// KLIK FOUND GRP: "<sceneBoundingRect(); @@ -938,7 +959,7 @@ void CustomTrackView::mousePressEvent(QMouseEvent * event) } m_operationMode = SPACER; } else { - setCursorPos((int)(mapToScene(event->x(), 0).x())); + seekCursorPos((int)(mapToScene(event->x(), 0).x())); } //QGraphicsView::mousePressEvent(event); event->ignore(); @@ -974,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 (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 (m_dragItem); - updateClipTypeActions(dragGroup == NULL ? clip : NULL); - m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1); + if (m_dragItem) { + ClipItem *clip = static_cast (m_dragItem); + updateClipTypeActions(dragGroup == NULL ? clip : NULL); + m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1); + } + else updateClipTypeActions(NULL); } if (collisionClip != NULL || m_dragItem == NULL) { @@ -1007,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; @@ -1020,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 @@ -1197,6 +1228,9 @@ void CustomTrackView::groupSelectedItems(bool force, bool createNewGroup) return; } QList 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 @@ -1406,8 +1440,8 @@ void CustomTrackView::editItemDuration() updateTrackDuration(clipInfo.track, moveCommand); m_commandStack->push(moveCommand); } - delete d; } + delete d; } else { emit displayMessage(i18n("Item is locked"), ErrorMessage); } @@ -1483,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; } @@ -1560,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; @@ -1571,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; @@ -1583,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); @@ -1738,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 itemList = group->childItems(); QUndoCommand *effectCommand = new QUndoCommand(); @@ -1777,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(dropTarget)); + } } void CustomTrackView::slotAddEffect(ClipItem *clip, QDomElement effect) @@ -1853,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(itemList.at(i)); + if (clip->isMainSelectedClip()) { + emit clipItemSelected(clip); + break; + } + } + } + } } else delete effectCommand; } @@ -2200,7 +2279,13 @@ ClipItem *CustomTrackView::cutClip(ItemInfo info, GenTime cutTime, bool cut, boo return NULL; } - if (execute) m_document->renderer()->mltCutClip(m_document->tracksCount() - info.track, cutTime); + if (execute) { + if (!m_document->renderer()->mltCutClip(m_document->tracksCount() - info.track, cutTime)) { + // Error cuting clip in playlist + m_blockRefresh = false; + return NULL; + } + } int cutPos = (int) cutTime.frames(m_document->fps()); ItemInfo newPos; newPos.startPos = cutTime; @@ -2608,7 +2693,7 @@ void CustomTrackView::dropEvent(QDropEvent * event) groupSelectedItems(true); } else if (items.count() == 1) { m_dragItem = static_cast (items.at(0)); - emit clipItemSelected((ClipItem*)m_dragItem, false); + emit clipItemSelected((ClipItem*) m_dragItem, false); } event->setDropAction(Qt::MoveAction); event->accept(); @@ -3336,14 +3421,27 @@ void CustomTrackView::deleteClip(const QString &clipId) } } -void CustomTrackView::setCursorPos(int pos, bool seek) +void CustomTrackView::seekCursorPos(int pos) { - if (pos == m_cursorPos) return; - emit cursorMoved((int)(m_cursorPos), (int)(pos)); - m_cursorPos = pos; - if (seek) m_document->renderer()->seek(m_cursorPos); - else if (m_autoScroll) checkScrolling(); - m_cursorLine->setPos(m_cursorPos, 0); + m_document->renderer()->seek(qMax(pos, 0)); + emit updateRuler(); +} + +int CustomTrackView::seekPosition() const +{ + return m_document->renderer()->requestedSeekPosition; +} + + +void CustomTrackView::setCursorPos(int pos) +{ + if (pos != m_cursorPos) { + emit cursorMoved((int)(m_cursorPos), (int)(pos)); + m_cursorPos = pos; + m_cursorLine->setPos(m_cursorPos, 0); + if (m_autoScroll) checkScrolling(); + } + else emit updateRuler(); } void CustomTrackView::updateCursorPos() @@ -3358,11 +3456,15 @@ int CustomTrackView::cursorPos() void CustomTrackView::moveCursorPos(int delta) { - if (m_cursorPos + delta < 0) delta = 0 - m_cursorPos; - emit cursorMoved((int)(m_cursorPos), (int)((m_cursorPos + delta))); - m_cursorPos += delta; - m_cursorLine->setPos(m_cursorPos, 0); - m_document->renderer()->seek(m_cursorPos); + int currentPos = m_document->renderer()->requestedSeekPosition; + if (currentPos == SEEK_INACTIVE) { + currentPos = m_document->renderer()->seekPosition().frames(m_document->fps()) + delta; + } + else { + currentPos += delta; + } + m_document->renderer()->seek(qMax(0, currentPos)); + emit updateRuler(); } void CustomTrackView::initCursorPos(int pos) @@ -3381,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); @@ -3413,6 +3515,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) track = -1; } else track = (int)(mapToScene(m_clickEvent).y() / m_tracksHeight); GenTime timeOffset = GenTime((int)(m_selectionGroup->scenePos().x()), m_document->fps()) - m_selectionGroupInfo.startPos; + QList groups; if (timeOffset != GenTime()) { QList items = m_selectionGroup->childItems(); @@ -3430,8 +3533,11 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) } for (int i = 0; i < items.count(); i++) { - if (items.at(i)->type() == GROUPWIDGET) - items += items.at(i)->childItems(); + if (items.at(i)->type() == GROUPWIDGET) { + AbstractGroupItem* group = (AbstractGroupItem*)items.at(i); + if (!groups.contains(group)) groups.append(group); + items += items.at(i)->childItems(); + } } for (int i = 0; i < items.count(); i++) { @@ -3461,7 +3567,13 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) setDocumentModified(); } } - resetSelectionGroup(false); + for (int i = 0; i < groups.count(); i++) + rebuildGroup(groups.at(i)); + + resetSelectionGroup(); + + clearSelection(); + m_operationMode = NONE; } else if (m_operationMode == RUBBERSELECTION) { resetSelectionGroup(); @@ -3635,7 +3747,6 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) else group = static_cast (m_dragItem->parentItem()); QList items = group->childItems(); - QList clipsToMove; QList transitionsToMove; @@ -3719,13 +3830,19 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) m_selectionGroupInfo.startPos = GenTime(m_selectionGroup->scenePos().x(), m_document->fps()); m_selectionGroupInfo.track = m_selectionGroup->track(); + QList groupList; for (int i = 0; i < items.count(); ++i) { if (items.at(i)->type() == GROUPWIDGET) { - rebuildGroup((AbstractGroupItem*)items.at(i)); + AbstractGroupItem* group = (AbstractGroupItem*)items.at(i); + if (!groupList.contains(group)) groupList.append(group); items.removeAt(i); --i; } } + for (int i = 0; i < groupList.count(); ++i) { + rebuildGroup(groupList.at(i)); + } + for (int i = 0; i < items.count(); ++i) { if (items.at(i)) { items.at(i)->setSelected(true); @@ -4327,7 +4444,7 @@ void CustomTrackView::slotUpdateClip(const QString &clipId, bool reload) ItemInfo info = clip->info(); Mlt::Producer *prod = NULL; if (clip->isAudioOnly()) prod = baseClip->audioProducer(info.track); - else if (clip->isVideoOnly()) prod = baseClip->videoProducer(); + else if (clip->isVideoOnly()) prod = baseClip->videoProducer(info.track); else prod = baseClip->getProducer(info.track); if (reload && !m_document->renderer()->mltUpdateClip(tractor, info, clip->xml(), prod)) { emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", info.startPos.frames(m_document->fps()), info.track), ErrorMessage); @@ -4570,6 +4687,7 @@ void CustomTrackView::moveGroup(QList startClip, QList sta //m_selectionGroup->moveBy(offset.frames(m_document->fps()), trackOffset *(qreal) m_tracksHeight); QList children = m_selectionGroup->childItems(); + QList groupList; // Expand groups int max = children.count(); for (int i = 0; i < max; i++) { @@ -4579,6 +4697,8 @@ void CustomTrackView::moveGroup(QList startClip, QList sta //grp->moveBy(offset.frames(m_document->fps()), trackOffset *(qreal) m_tracksHeight); /*m_document->clipManager()->removeGroup(grp); m_scene->destroyItemGroup(grp);*/ + AbstractGroupItem *group = (AbstractGroupItem*) children.at(i); + if (!groupList.contains(group)) groupList.append(group); children.removeAll(children.at(i)); i--; } @@ -4620,11 +4740,9 @@ void CustomTrackView::moveGroup(QList startClip, QList sta } resetSelectionGroup(false); + for (int i = 0; i < groupList.count(); i++) + rebuildGroup(groupList.at(i)); - for (int i = 0; i < children.count(); i++) { - if (children.at(i)->parentItem()) - rebuildGroup((AbstractGroupItem*)children.at(i)->parentItem()); - } clearSelection(); KdenliveSettings::setSnaptopoints(snap); @@ -5121,7 +5239,7 @@ void CustomTrackView::slotSeekToPreviousSnap() { updateSnapPoints(NULL); GenTime res = m_scene->previousSnapPoint(GenTime(m_cursorPos, m_document->fps())); - setCursorPos((int) res.frames(m_document->fps())); + seekCursorPos((int) res.frames(m_document->fps())); checkScrolling(); } @@ -5129,7 +5247,7 @@ void CustomTrackView::slotSeekToNextSnap() { updateSnapPoints(NULL); GenTime res = m_scene->nextSnapPoint(GenTime(m_cursorPos, m_document->fps())); - setCursorPos((int) res.frames(m_document->fps())); + seekCursorPos((int) res.frames(m_document->fps())); checkScrolling(); } @@ -5137,7 +5255,7 @@ void CustomTrackView::clipStart() { AbstractClipItem *item = getMainActiveClip(); if (item != NULL) { - setCursorPos((int) item->startPos().frames(m_document->fps())); + seekCursorPos((int) item->startPos().frames(m_document->fps())); checkScrolling(); } } @@ -5146,21 +5264,54 @@ void CustomTrackView::clipEnd() { AbstractClipItem *item = getMainActiveClip(); if (item != NULL) { - setCursorPos((int) item->endPos().frames(m_document->fps()) - 1); + seekCursorPos((int) item->endPos().frames(m_document->fps()) - 1); checkScrolling(); } } -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 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 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); } @@ -5177,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); - if (!comment.isEmpty()) base->addSnapMarker(pos, comment); - else base->deleteSnapMarker(pos); + 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 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 (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++) { @@ -5208,7 +5494,7 @@ void CustomTrackView::buildGuidesMenu(QMenu *goMenu) const goMenu->clear(); double fps = m_document->fps(); for (int i = 0; i < m_guides.count(); i++) { - act = goMenu->addAction(m_guides.at(i)->label() + "/" + Timecode::getStringTimecode(m_guides.at(i)->position().frames(fps), fps)); + act = goMenu->addAction(m_guides.at(i)->label() + '/' + Timecode::getStringTimecode(m_guides.at(i)->position().frames(fps), fps)); act->setData(m_guides.at(i)->position().frames(m_document->fps())); } goMenu->setEnabled(!m_guides.isEmpty()); @@ -5403,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(); @@ -5430,7 +5718,7 @@ bool CustomTrackView::findString(const QString &text) for (int i = 0; i < m_searchPoints.size(); ++i) { marker = m_searchPoints.at(i).comment(); if (marker.contains(text, Qt::CaseInsensitive)) { - setCursorPos(m_searchPoints.at(i).time().frames(m_document->fps()), true); + seekCursorPos(m_searchPoints.at(i).time().frames(m_document->fps())); int vert = verticalScrollBar()->value(); int hor = cursorPos(); ensureVisible(hor, vert + 10, 2, 2, 50, 0); @@ -5443,7 +5731,7 @@ bool CustomTrackView::findString(const QString &text) void CustomTrackView::selectFound(QString track, QString pos) { - setCursorPos(m_document->timecode().getFrameCount(pos), true); + seekCursorPos(m_document->timecode().getFrameCount(pos)); slotSelectTrack(track.toInt()); selectClip(true); int vert = verticalScrollBar()->value(); @@ -5457,7 +5745,7 @@ bool CustomTrackView::findNextString(const QString &text) for (int i = m_findIndex + 1; i < m_searchPoints.size(); ++i) { marker = m_searchPoints.at(i).comment(); if (marker.contains(text, Qt::CaseInsensitive)) { - setCursorPos(m_searchPoints.at(i).time().frames(m_document->fps()), true); + seekCursorPos(m_searchPoints.at(i).time().frames(m_document->fps())); int vert = verticalScrollBar()->value(); int hor = cursorPos(); ensureVisible(hor, vert + 10, 2, 2, 50, 0); @@ -5754,7 +6042,7 @@ void CustomTrackView::adjustKeyfames(GenTime oldstart, GenTime newstart, GenTime if (!e.isNull() && (e.attribute("type") == "keyframe" || e.attribute("type") == "simplekeyframe")) { QString def = e.attribute("default"); // Effect has a keyframe type parameter, we need to adjust the values - QStringList keys = e.attribute("keyframes").split(";", QString::SkipEmptyParts); + QStringList keys = e.attribute("keyframes").split(';', QString::SkipEmptyParts); QStringList newKeyFrames; foreach(const QString &str, keys) { int pos = str.section(':', 0, 0).toInt(); @@ -5777,7 +6065,8 @@ ClipItem *CustomTrackView::getClipUnderCursor() const QList 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; @@ -5836,7 +6125,26 @@ void CustomTrackView::setInPoint() return; } } - prepareResizeClipStart(clip, clip->info(), m_cursorPos, true); + + AbstractGroupItem *parent = static_cast (clip->parentItem()); + if (parent) { + // Resizing a group + QUndoCommand *resizeCommand = new QUndoCommand(); + resizeCommand->setText(i18n("Resize group")); + QList items = parent->childItems(); + for (int i = 0; i < items.count(); ++i) { + AbstractClipItem *item = static_cast(items.at(i)); + if (item && item->type() == AVWIDGET) { + prepareResizeClipStart(item, item->info(), m_cursorPos, true, resizeCommand); + } + } + if (resizeCommand->childCount() > 0) m_commandStack->push(resizeCommand); + else { + //TODO warn user of failed resize + delete resizeCommand; + } + } + else prepareResizeClipStart(clip, clip->info(), m_cursorPos, true); } void CustomTrackView::setOutPoint() @@ -5850,7 +6158,25 @@ void CustomTrackView::setOutPoint() return; } } - prepareResizeClipEnd(clip, clip->info(), m_cursorPos, true); + AbstractGroupItem *parent = static_cast (clip->parentItem()); + if (parent) { + // Resizing a group + QUndoCommand *resizeCommand = new QUndoCommand(); + resizeCommand->setText(i18n("Resize group")); + QList items = parent->childItems(); + for (int i = 0; i < items.count(); ++i) { + AbstractClipItem *item = static_cast(items.at(i)); + if (item && item->type() == AVWIDGET) { + prepareResizeClipEnd(item, item->info(), m_cursorPos, true, resizeCommand); + } + } + if (resizeCommand->childCount() > 0) m_commandStack->push(resizeCommand); + else { + //TODO warn user of failed resize + delete resizeCommand; + } + } + else prepareResizeClipEnd(clip, clip->info(), m_cursorPos, true); } void CustomTrackView::slotUpdateAllThumbs() @@ -5933,21 +6259,21 @@ void CustomTrackView::saveThumbnails() void CustomTrackView::slotInsertTrack(int ix) { - TrackDialog d(m_document, parentWidget()); - d.comboTracks->setCurrentIndex(ix); - d.label->setText(i18n("Insert track")); - d.setWindowTitle(i18n("Insert New Track")); + QPointer d = new TrackDialog(m_document, parentWidget()); + d->comboTracks->setCurrentIndex(ix); + d->label->setText(i18n("Insert track")); + d->setWindowTitle(i18n("Insert New Track")); - if (d.exec() == QDialog::Accepted) { - ix = d.comboTracks->currentIndex(); - if (d.before_select->currentIndex() == 1) + if (d->exec() == QDialog::Accepted) { + ix = d->comboTracks->currentIndex(); + if (d->before_select->currentIndex() == 1) ix++; TrackInfo info; info.duration = 0; info.isMute = false; info.isLocked = false; info.effectsList = EffectsList(true); - if (d.video_track->isChecked()) { + if (d->video_track->isChecked()) { info.type = VIDEOTRACK; info.isBlind = false; } else { @@ -5958,41 +6284,45 @@ void CustomTrackView::slotInsertTrack(int ix) m_commandStack->push(addTrack); setDocumentModified(); } + delete d; } void CustomTrackView::slotDeleteTrack(int ix) { if (m_document->tracksCount() < 2) return; - TrackDialog d(m_document, parentWidget()); - d.comboTracks->setCurrentIndex(ix); - d.label->setText(i18n("Delete track")); - d.before_select->setHidden(true); - d.setWindowTitle(i18n("Delete Track")); - d.video_track->setHidden(true); - d.audio_track->setHidden(true); - if (d.exec() == QDialog::Accepted) { - ix = d.comboTracks->currentIndex(); + QPointer d = new TrackDialog(m_document, parentWidget()); + d->comboTracks->setCurrentIndex(ix); + d->label->setText(i18n("Delete track")); + d->before_select->setHidden(true); + d->setWindowTitle(i18n("Delete Track")); + d->video_track->setHidden(true); + d->audio_track->setHidden(true); + if (d->exec() == QDialog::Accepted) { + ix = d->comboTracks->currentIndex(); TrackInfo info = m_document->trackInfoAt(m_document->tracksCount() - ix - 1); deleteTimelineTrack(ix, info); setDocumentModified(); /*AddTrackCommand* command = new AddTrackCommand(this, ix, info, false); m_commandStack->push(command);*/ } + delete d; } void CustomTrackView::slotConfigTracks(int ix) { - TracksConfigDialog d(m_document, ix, parentWidget()); - if (d.exec() == QDialog::Accepted) { - ConfigTracksCommand *configTracks = new ConfigTracksCommand(this, m_document->tracksList(), d.tracksList()); + QPointer d = new TracksConfigDialog(m_document, + ix, parentWidget()); + if (d->exec() == QDialog::Accepted) { + ConfigTracksCommand *configTracks = new ConfigTracksCommand(this, m_document->tracksList(), d->tracksList()); m_commandStack->push(configTracks); - QList toDelete = d.deletedTracks(); + QList toDelete = d->deletedTracks(); for (int i = 0; i < toDelete.count(); ++i) { TrackInfo info = m_document->trackInfoAt(m_document->tracksCount() - toDelete.at(i) + i - 1); deleteTimelineTrack(toDelete.at(i) - i, info); } setDocumentModified(); } + delete d; } void CustomTrackView::deleteTimelineTrack(int ix, TrackInfo trackinfo) @@ -6153,7 +6483,7 @@ void CustomTrackView::setAudioAlignReference() if (clip->clipType() == AV || clip->clipType() == AUDIO) { m_audioAlignmentReference = clip; - AudioEnvelope *envelope = new AudioEnvelope(clip->getProducer(clip->track())); + AudioEnvelope *envelope = new AudioEnvelope(clip->baseClip()->fileURL().path(), clip->getProducer(clip->track())); m_audioCorrelator = new AudioCorrelation(envelope); @@ -6198,9 +6528,7 @@ void CustomTrackView::alignAudio() } if (clip->clipType() == AV || clip->clipType() == AUDIO) { - AudioEnvelope *envelope = new AudioEnvelope(clip->getProducer(clip->track()), - clip->info().cropStart.frames(m_document->fps()), - clip->info().cropDuration.frames(m_document->fps())); + AudioEnvelope *envelope = new AudioEnvelope(clip->baseClip()->fileURL().path(), clip->getProducer(clip->track()), clip->info().cropStart.frames(m_document->fps()), clip->info().cropDuration.frames(m_document->fps())); // FFT only for larger vectors. We could use it all time, but for small vectors // the (anyway not noticeable) overhead is smaller with a nested for loop correlation. @@ -6316,7 +6644,7 @@ void CustomTrackView::doSplitAudio(const GenTime &pos, int track, EffectsList ef if (audioClip) { Mlt::Tractor *tractor = m_document->renderer()->lockService(); clip->setVideoOnly(true); - if (m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - track, start, clip->baseClip()->videoProducer()) == false) { + if (m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - track, start, clip->baseClip()->videoProducer(info.track)) == false) { emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, track), ErrorMessage); } if (m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - info.track, start, clip->baseClip()->audioProducer(info.track)) == false) { @@ -6476,7 +6804,7 @@ void CustomTrackView::doChangeClipType(const GenTime &pos, int track, bool video int start = pos.frames(m_document->fps()); clip->setVideoOnly(true); clip->setAudioOnly(false); - if (m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - track, start, clip->baseClip()->videoProducer()) == false) { + if (m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - track, start, clip->baseClip()->videoProducer(track)) == false) { emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, track), ErrorMessage); } } else if (audioOnly) { @@ -6522,7 +6850,7 @@ void CustomTrackView::updateClipTypeActions(ClipItem *clip) void CustomTrackView::slotGoToMarker(QAction *action) { int pos = action->data().toInt(); - setCursorPos(pos, true); + seekCursorPos(pos); } void CustomTrackView::reloadTransitionLumas() @@ -7000,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 ¶meters, QDomNodeList params, const QString &prefix) +void CustomTrackView::adjustEffectParameters(EffectsParameterList ¶meters, QDomNodeList params, MltVideoProfile profile, const QString &prefix) { QLocale locale; for (int i = 0; i < params.count(); i++) { @@ -7022,13 +7350,13 @@ void CustomTrackView::adjustEffectParameters(EffectsParameterList ¶meters, Q parameters.addParam("_sync_in_out", "1"); } if (e.attribute("type") == "simplekeyframe") { - QStringList values = e.attribute("keyframes").split(";", QString::SkipEmptyParts); + QStringList values = e.attribute("keyframes").split(';', QString::SkipEmptyParts); double factor = e.attribute("factor", "1").toDouble(); double offset = e.attribute("offset", "0").toDouble(); for (int j = 0; j < values.count(); j++) { QString pos = values.at(j).section(':', 0, 0); double val = (values.at(j).section(':', 1, 1).toDouble() - offset) / factor; - values[j] = pos + "=" + locale.toString(val); + values[j] = pos + '=' + locale.toString(val); } // kDebug() << "/ / / /SENDING KEYFR:" << values; parameters.addParam(paramname, values.join(";")); @@ -7062,7 +7390,7 @@ void CustomTrackView::adjustEffectParameters(EffectsParameterList ¶meters, 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(); } @@ -7172,16 +7500,16 @@ 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, stringMap filterParams, stringMap extra) { ClipItem *clip = getClipItemAt(GenTime(startPos, m_document->fps()), track); if (clip == NULL) { - emit displayMessage(i18n("Cannot find clip for effect update %1.", filter), ErrorMessage); + emit displayMessage(i18n("Cannot find clip for effect update %1.", extra.value("finalfilter")), ErrorMessage); return; } QDomElement newEffect; QDomElement effect = clip->getEffectAtIndex(clip->selectedEffectIndex()); - if (effect.attribute("id") == filter) { + if (effect.attribute("id") == extra.value("finalfilter")) { newEffect = effect.cloneNode().toElement(); QMap::const_iterator i = filterParams.constBegin(); while (i != filterParams.constEnd()) { @@ -7192,8 +7520,77 @@ void CustomTrackView::slotGotFilterJobResults(const QString &/*id*/, int startPo EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effect, newEffect, clip->selectedEffectIndex(), true, true); m_commandStack->push(command); emit clipItemSelected(clip); - } - + } } +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 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 data = item->baseClip()->analysisData(); + if (data.isEmpty()) { + emit displayMessage(i18n("No keyframe data found in clip"), ErrorMessage); + return; + } + QPointer d = new QDialog(this); + Ui::ImportKeyframesDialog_UI ui; + ui.setupUi(d); + + // Set up data + int ix = 0; + QMap::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; +} +