]> git.sesse.net Git - kdenlive/blobdiff - src/customtrackview.cpp
Improve manipulation of small items in timeline (Don't allow resize on very small...
[kdenlive] / src / customtrackview.cpp
index cc75b20f515da8153416e123f9eac89b9ccfe2e0..2f08b8e7381c77daae54ed82322b65ad4d79d2d9 100644 (file)
@@ -48,6 +48,7 @@
 #include "commands/insertspacecommand.h"
 #include "spacerdialog.h"
 #include "commands/addtrackcommand.h"
+#include "commands/changeeffectstatecommand.h"
 #include "commands/movegroupcommand.h"
 #include "ui_addtrack_ui.h"
 #include "initeffects.h"
@@ -62,6 +63,9 @@
 #include "commands/razorgroupcommand.h"
 #include "profilesdialog.h"
 
+#include "lib/audio/audioEnvelope.h"
+#include "lib/audio/audioCorrelation.h"
+
 #include <KDebug>
 #include <KLocale>
 #include <KUrl>
@@ -83,6 +87,8 @@
 #include <QGraphicsDropShadowEffect>
 #endif
 
+//#define DEBUG
+
 bool sortGuidesList(const Guide *g1 , const Guide *g2)
 {
     return (*g1).position() < (*g2).position();
@@ -98,38 +104,41 @@ bool sortGuidesList(const Guide *g1 , const Guide *g2)
 // const int duration = animate ? 1500 : 1;
 
 CustomTrackView::CustomTrackView(KdenliveDoc *doc, CustomTrackScene* projectscene, QWidget *parent) :
-        QGraphicsView(projectscene, parent),
-        m_tracksHeight(KdenliveSettings::trackheight()),
-        m_projectDuration(0),
-        m_cursorPos(0),
-        m_document(doc),
-        m_scene(projectscene),
-        m_cursorLine(NULL),
-        m_operationMode(NONE),
-        m_moveOpMode(NONE),
-        m_dragItem(NULL),
-        m_dragGuide(NULL),
-        m_visualTip(NULL),
-        m_animation(NULL),
-        m_clickPoint(),
-        m_autoScroll(KdenliveSettings::autoscroll()),
-        m_pasteEffectsAction(NULL),
-        m_ungroupAction(NULL),
-        m_scrollOffset(0),
-        m_clipDrag(false),
-        m_findIndex(0),
-        m_tool(SELECTTOOL),
-        m_copiedItems(),
-        m_menuPosition(),
-        m_blockRefresh(false),
-        m_selectionGroup(NULL),
-        m_selectedTrack(0),
-        m_controlModifier(false)
-{
-    if (doc)
+    QGraphicsView(projectscene, parent),
+    m_tracksHeight(KdenliveSettings::trackheight()),
+    m_projectDuration(0),
+    m_cursorPos(0),
+    m_document(doc),
+    m_scene(projectscene),
+    m_cursorLine(NULL),
+    m_operationMode(NONE),
+    m_moveOpMode(NONE),
+    m_dragItem(NULL),
+    m_dragGuide(NULL),
+    m_visualTip(NULL),
+    m_animation(NULL),
+    m_clickPoint(),
+    m_autoScroll(KdenliveSettings::autoscroll()),
+    m_pasteEffectsAction(NULL),
+    m_ungroupAction(NULL),
+    m_scrollOffset(0),
+    m_clipDrag(false),
+    m_findIndex(0),
+    m_tool(SELECTTOOL),
+    m_copiedItems(),
+    m_menuPosition(),
+    m_blockRefresh(false),
+    m_selectionGroup(NULL),
+    m_selectedTrack(0),
+    m_audioCorrelator(NULL),
+    m_audioAlignmentReference(NULL),
+    m_controlModifier(false)
+{
+    if (doc) {
         m_commandStack = doc->commandStack();
-    else
+    } else {
         m_commandStack = NULL;
+    }
     m_ct = 0;
     setMouseTracking(true);
     setAcceptDrops(true);
@@ -612,7 +621,11 @@ void CustomTrackView::mouseMoveEvent(QMouseEvent * event)
             // all other modes break the selection, so the user probably wants to move it
             opMode = MOVE;
         } else {
-            opMode = clip->operationMode(mapToScene(event->pos()));
+           if (clip->rect().width() * transform().m11() < 15) {
+               // If the item is very small, only allow move
+               opMode = MOVE;
+           }
+            else opMode = clip->operationMode(mapToScene(event->pos()));
         }
 
         const double size = 5;
@@ -1001,7 +1014,11 @@ void CustomTrackView::mousePressEvent(QMouseEvent * event)
         // all other modes break the selection, so the user probably wants to move it
         m_operationMode = MOVE;
     } else {
-        m_operationMode = m_dragItem->operationMode(mapToScene(event->pos()));
+       if (m_dragItem->rect().width() * transform().m11() < 15) {
+           // If the item is very small, only allow move
+           m_operationMode = MOVE;
+       }
+        else m_operationMode = m_dragItem->operationMode(mapToScene(event->pos()));
     }
     m_controlModifier = (event->modifiers() == Qt::ControlModifier);
 
@@ -1283,14 +1300,14 @@ void CustomTrackView::mouseDoubleClickEvent(QMouseEvent *event)
             ClipItem * item = static_cast <ClipItem *>(m_dragItem);
             //QString previous = item->keyframes(item->selectedEffectIndex());
             QDomElement oldEffect = item->selectedEffect().cloneNode().toElement();
-            item->insertKeyframe(item->getEffectAt(item->selectedEffectIndex()), keyFramePos.frames(m_document->fps()), val);
+            item->insertKeyframe(item->getEffectAtIndex(item->selectedEffectIndex()), keyFramePos.frames(m_document->fps()), val);
             //item->updateKeyframeEffect();
             //QString next = item->keyframes(item->selectedEffectIndex());
             QDomElement newEffect = item->selectedEffect().cloneNode().toElement();
-            EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), oldEffect, newEffect, item->selectedEffectIndex(), false);
+            EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), oldEffect, newEffect, item->selectedEffectIndex(), false, false);
             //EditKeyFrameCommand *command = new EditKeyFrameCommand(this, m_dragItem->track(), m_dragItem->startPos(), item->selectedEffectIndex(), previous, next, false);
             m_commandStack->push(command);
-            updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
+            updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect());
             emit clipItemSelected(item, item->selectedEffectIndex());
         }
     } else if (m_dragItem && !m_dragItem->isItemLocked()) {
@@ -1597,7 +1614,13 @@ bool CustomTrackView::insertDropClips(const QMimeData *data, const QPoint &pos)
 void CustomTrackView::dragEnterEvent(QDragEnterEvent * event)
 {
     if (insertDropClips(event->mimeData(), event->pos())) {
-        event->acceptProposedAction();
+      if (event->source() == this) {
+             event->setDropAction(Qt::MoveAction);
+             event->accept();
+         } else {
+            event->setDropAction(Qt::MoveAction);
+             event->acceptProposedAction();
+        }
     } else QGraphicsView::dragEnterEvent(event);
 }
 
@@ -1630,7 +1653,7 @@ void CustomTrackView::slotRefreshEffects(ClipItem *clip)
     }
     bool success = true;
     for (int i = 0; i < clip->effectsCount(); i++) {
-        if (!m_document->renderer()->mltAddEffect(track, pos, getEffectArgs(clip->effectAt(i)), false)) success = false;
+        if (!m_document->renderer()->mltAddEffect(track, pos, getEffectArgs(clip->effect(i)), false)) success = false;
     }
     if (!success) emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
     m_document->renderer()->doRefresh();
@@ -1672,6 +1695,7 @@ void CustomTrackView::addEffect(int track, GenTime pos, QDomElement effect)
         EffectsParameterList params = clip->addEffect(effect);
         if (!m_document->renderer()->mltAddEffect(track, pos, params))
             emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
+       clip->setSelectedEffect(params.paramValue("kdenlive_ix").toInt());
         if (clip->isSelected()) emit clipItemSelected(clip);
     } else emit displayMessage(i18n("Cannot find clip to add effect"), ErrorMessage);
 }
@@ -1721,45 +1745,54 @@ void CustomTrackView::slotAddGroupEffect(QDomElement effect, AbstractGroupItem *
     if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data());
     else effectName = i18n("effect");
     effectCommand->setText(i18n("Add %1", effectName));
-    int count = 0;
     for (int i = 0; i < itemList.count(); i++) {
         if (itemList.at(i)->type() == AVWIDGET) {
             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
-            if (effect.attribute("type") == "audio") {
-                // Don't add audio effects on video clips
-                if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) continue;
-            } else if (effect.hasAttribute("type") == false) {
-                // Don't add video effect on audio clips
-                if (item->isAudioOnly() || item->clipType() == AUDIO) continue;
-            }
-
-            if (effect.attribute("unique", "0") != "0" && item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1) {
-                emit displayMessage(i18n("Effect already present in clip"), ErrorMessage);
-                continue;
-            }
-            if (item->isItemLocked()) {
-                continue;
-            }
-            item->initEffect(effect);
-
-            new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand);
-            count++;
+            if (effect.tagName() == "effectgroup") {
+               QDomNodeList effectlist = effect.elementsByTagName("effect");
+               for (int j = 0; j < effectlist.count(); j++) {
+                   QDomElement subeffect = effectlist.at(j).toElement();
+                   if (subeffect.hasAttribute("kdenlive_info")) {
+                       // effect is in a group
+                       EffectInfo effectInfo;
+                       effectInfo.fromString(subeffect.attribute("kdenlive_info"));
+                       if (effectInfo.groupIndex < 0) {
+                           // group needs to be appended
+                           effectInfo.groupIndex = item->nextFreeEffectGroupIndex();
+                           subeffect.setAttribute("kdenlive_info", effectInfo.toString());
+                       }
+                   }
+                   processEffect(item, subeffect, effectCommand);
+               }
+           }
+            else {
+               processEffect(item, effect, effectCommand);
+           }
         }
     }
-    if (count > 0) {
+    if (effectCommand->childCount() > 0) {
         m_commandStack->push(effectCommand);
         setDocumentModified();
     } else delete effectCommand;
 }
 
+void CustomTrackView::slotAddEffect(ClipItem *clip, QDomElement effect)
+{
+    if (clip) slotAddEffect(effect, clip->startPos(), clip->track());
+}
+
 void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track)
 {
     QList<QGraphicsItem *> itemList;
     QUndoCommand *effectCommand = new QUndoCommand();
     QString effectName;
-    QDomElement namenode = effect.firstChildElement("name");
-    if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data());
-    else effectName = i18n("effect");
+    if (effect.tagName() == "effectgroup") {
+       effectName = effect.attribute("name");
+    } else {
+       QDomElement namenode = effect.firstChildElement("name");
+       if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data());
+       else effectName = i18n("effect");
+    }
     effectCommand->setText(i18n("Add %1", effectName));
 
     if (track == -1) itemList = scene()->selectedItems();
@@ -1782,39 +1815,24 @@ void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track)
     for (int i = 0; i < itemList.count(); i++) {
         if (itemList.at(i)->type() == AVWIDGET) {
             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
-            if (effect.attribute("type") == "audio") {
-                // Don't add audio effects on video clips
-                if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) {
-                    /* do not show error message when item is part of a group as the user probably knows what he does then
-                     * and the message is annoying when working with the split audio feature */
-                    if (!item->parentItem() || item->parentItem() == m_selectionGroup)
-                        emit displayMessage(i18n("Cannot add an audio effect to this clip"), ErrorMessage);
-                    continue;
-                }
-            } else if (effect.attribute("type") == "video" || !effect.hasAttribute("type")) {
-                // Don't add video effect on audio clips
-                if (item->isAudioOnly() || item->clipType() == AUDIO) {
-                    /* do not show error message when item is part of a group as the user probably knows what he does then
-                     * and the message is annoying when working with the split audio feature */
-                    if (!item->parentItem() || item->parentItem() == m_selectionGroup)
-                        emit displayMessage(i18n("Cannot add a video effect to this clip"), ErrorMessage);
-                    continue;
-                }
-            }
-            if (item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1 && effect.attribute("unique", "0") != "0") {
-                emit displayMessage(i18n("Effect already present in clip"), ErrorMessage);
-                continue;
-            }
-            if (item->isItemLocked()) {
-                continue;
-            }
-
-            if (effect.attribute("id") == "freeze" && m_cursorPos > item->startPos().frames(m_document->fps()) && m_cursorPos < item->endPos().frames(m_document->fps())) {
-                item->initEffect(effect, m_cursorPos - item->startPos().frames(m_document->fps()));
-            } else {
-                item->initEffect(effect);
-            }
-            new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand);
+           if (effect.tagName() == "effectgroup") {
+               QDomNodeList effectlist = effect.elementsByTagName("effect");
+               for (int j = 0; j < effectlist.count(); j++) {
+                   QDomElement subeffect = effectlist.at(j).toElement();
+                   if (subeffect.hasAttribute("kdenlive_info")) {
+                       // effect is in a group
+                       EffectInfo effectInfo;
+                       effectInfo.fromString(subeffect.attribute("kdenlive_info"));
+                       if (effectInfo.groupIndex < 0) {
+                           // group needs to be appended
+                           effectInfo.groupIndex = item->nextFreeEffectGroupIndex();
+                           subeffect.setAttribute("kdenlive_info", effectInfo.toString());
+                       }
+                   }
+                   processEffect(item, subeffect, effectCommand);
+               }
+           }
+            else processEffect(item, effect, effectCommand);
         }
     }
     if (effectCommand->childCount() > 0) {
@@ -1825,12 +1843,12 @@ void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track)
            for (int i = 0; i < itemList.count(); i++) {
                if (itemList.at(i)->type() == AVWIDGET) {
                    ClipItem *clip = static_cast<ClipItem *>(itemList.at(i));
+                   clip->setSelectedEffect(clip->effectsCount());
                    if (!clip->isSelected()) {
-                       clearSelection();
+                       clearSelection(false);
                        clip->setSelected(true);
+                       emit clipItemSelected(clip);
                    }
-                   clip->setSelectedEffect(clip->effectsCount() - 1);
-                   emit clipItemSelected(clip, clip->effectsCount() - 1);
                    break;
                }
            }
@@ -1838,6 +1856,43 @@ void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track)
     } else delete effectCommand;
 }
 
+void CustomTrackView::processEffect(ClipItem *item, QDomElement effect, QUndoCommand *effectCommand)
+{
+    if (effect.attribute("type") == "audio") {
+       // Don't add audio effects on video clips
+        if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) {
+           /* do not show error message when item is part of a group as the user probably knows what he does then
+            * and the message is annoying when working with the split audio feature */
+            if (!item->parentItem() || item->parentItem() == m_selectionGroup)
+               emit displayMessage(i18n("Cannot add an audio effect to this clip"), ErrorMessage);
+            return;
+        }
+    } else if (effect.attribute("type") == "video" || !effect.hasAttribute("type")) {
+       // Don't add video effect on audio clips
+        if (item->isAudioOnly() || item->clipType() == AUDIO) {
+           /* do not show error message when item is part of a group as the user probably knows what he does then
+            * and the message is annoying when working with the split audio feature */
+            if (!item->parentItem() || item->parentItem() == m_selectionGroup)
+               emit displayMessage(i18n("Cannot add a video effect to this clip"), ErrorMessage);
+            return;
+        }
+    }
+    if (effect.attribute("unique", "0") != "0" && item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1) {
+       emit displayMessage(i18n("Effect already present in clip"), ErrorMessage);
+        return;
+    }
+    if (item->isItemLocked()) {
+       return;
+    }
+
+    if (effect.attribute("id") == "freeze" && m_cursorPos > item->startPos().frames(m_document->fps()) && m_cursorPos < item->endPos().frames(m_document->fps())) {
+       item->initEffect(effect, m_cursorPos - item->startPos().frames(m_document->fps()));
+    } else {
+       item->initEffect(effect);
+    }
+    new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand);
+}
+
 void CustomTrackView::slotDeleteEffect(ClipItem *clip, int track, QDomElement effect, bool affectGroup)
 {
     if (clip == NULL) {
@@ -1872,7 +1927,7 @@ void CustomTrackView::slotDeleteEffect(ClipItem *clip, int track, QDomElement ef
                 ClipItem *item = static_cast <ClipItem *>(items.at(i));
                 int ix = item->hasEffect(effect.attribute("tag"), effect.attribute("id"));
                 if (ix != -1) {
-                    QDomElement eff = item->effectAt(ix);
+                    QDomElement eff = item->effectAtIndex(ix);
                     new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), eff, false, delCommand);
                 }
             }
@@ -1889,12 +1944,14 @@ void CustomTrackView::slotDeleteEffect(ClipItem *clip, int track, QDomElement ef
     setDocumentModified();
 }
 
-void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedEffect, int ix, bool triggeredByUser)
+void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedEffect, bool updateEffectStack)
 {
     if (insertedEffect.isNull()) {
+       kDebug()<<"// Trying to add null effect";
         emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
         return;
     }
+    int ix = insertedEffect.attribute("kdenlive_ix").toInt();
     QDomElement effect = insertedEffect.cloneNode().toElement();
     //kDebug() << "// update effect ix: " << effect.attribute("kdenlive_ix")<<", GAIN: "<<EffectsList::parameter(effect, "gain");
     if (pos < GenTime()) {
@@ -1905,8 +1962,9 @@ void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedE
             clip->initEffect(effect);
             effectParams = getEffectArgs(effect);
         }*/
-        if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - track, pos, effectParams))
+        if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - track, pos, effectParams)) {
             emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
+       }
         m_document->setTrackEffect(m_document->tracksCount() - track - 1, ix, effect);
         emit updateTrackEffectState(track);
         setDocumentModified();
@@ -1926,12 +1984,13 @@ void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedE
                 if (strobe == 0) strobe = 1;
                 doChangeClipSpeed(clip->info(), clip->speedIndependantInfo(), speed, clip->speed(), strobe, clip->baseClip()->getId());
             }
-            clip->setEffectAt(ix, effect);
-            if (ix == clip->selectedEffectIndex()) {
-                clip->setSelectedEffect(ix);
-                if (!triggeredByUser)
-                    emit clipItemSelected(clip, ix);
-            }
+            clip->updateEffect(effect);
+           if (updateEffectStack && clip->isSelected())
+               emit clipItemSelected(clip);
+           if (ix == clip->selectedEffectIndex()) {
+               // make sure to update display of clip keyframes
+               clip->setSelectedEffect(ix);
+           }
             return;
         }
 
@@ -1953,74 +2012,146 @@ void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedE
                 clip->setFadeOut(pos);
             }
         }
+       bool success = m_document->renderer()->mltEditEffect(m_document->tracksCount() - clip->track(), clip->startPos(), effectParams);
 
-        if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - clip->track(), clip->startPos(), effectParams))
-            emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
-
-        clip->setEffectAt(ix, effect);
-        if (ix == clip->selectedEffectIndex()) {
-            clip->setSelectedEffect(ix);
-            if (!triggeredByUser)
-                emit clipItemSelected(clip, ix);
-        }
+        if (success) {
+           clip->updateEffect(effect);
+           if (updateEffectStack && clip->isSelected()) {
+               emit clipItemSelected(clip);
+           }
+           if (ix == clip->selectedEffectIndex()) {
+               // make sure to update display of clip keyframes
+               clip->setSelectedEffect(ix);
+           }
+       }
+       else emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
     }
     else emit displayMessage(i18n("Cannot find clip to update effect"), ErrorMessage);
     setDocumentModified();
 }
 
-void CustomTrackView::moveEffect(int track, GenTime pos, int oldPos, int newPos)
+void CustomTrackView::updateEffectState(int track, GenTime pos, QList <int> effectIndexes, bool disable, bool updateEffectStack)
+{
+    if (pos < GenTime()) {
+        // editing a track effect
+        if (!m_document->renderer()->mltEnableEffects(m_document->tracksCount() - track, pos, effectIndexes, disable)) {
+            emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
+           return;
+       }
+        m_document->enableTrackEffects(m_document->tracksCount() - track - 1, effectIndexes, disable);
+        emit updateTrackEffectState(track);
+        setDocumentModified();
+        return;
+    }
+    // editing a clip effect
+    ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
+    if (clip) {
+       bool success = m_document->renderer()->mltEnableEffects(m_document->tracksCount() - clip->track(), clip->startPos(), effectIndexes, disable);
+       if (success) {
+           clip->enableEffects(effectIndexes, disable);
+           if (updateEffectStack && clip->isSelected()) {
+               emit clipItemSelected(clip);
+           }
+           if (effectIndexes.contains(clip->selectedEffectIndex())) {
+               // make sure to update display of clip keyframes
+               clip->setSelectedEffect(clip->selectedEffectIndex());
+           }
+       }
+       else emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
+    }
+    else emit displayMessage(i18n("Cannot find clip to update effect"), ErrorMessage);
+}
+
+void CustomTrackView::moveEffect(int track, GenTime pos, QList <int> oldPos, QList <int> newPos)
 {
     if (pos < GenTime()) {
         // Moving track effect
-        QDomElement act = m_document->getTrackEffect(m_document->tracksCount() - track - 1, newPos - 1);
-        QDomElement before = m_document->getTrackEffect(m_document->tracksCount() - track - 1, oldPos - 1);
-
-        if (!act.isNull() && !before.isNull()) {
-            m_document->setTrackEffect(m_document->tracksCount() - track - 1, oldPos - 1, act);
-            m_document->setTrackEffect(m_document->tracksCount() - track - 1, newPos - 1, before);
-            m_document->renderer()->mltMoveEffect(m_document->tracksCount() - track, pos, oldPos, newPos);
-            emit showTrackEffects(m_document->tracksCount() - track, m_document->trackInfoAt(m_document->tracksCount() - track - 1));
-        } else emit displayMessage(i18n("Cannot move effect"), ErrorMessage);
+        int documentTrack = m_document->tracksCount() - track - 1;
+        int max = m_document->getTrackEffects(documentTrack).count();
+       int new_position = newPos.at(0);
+       if (new_position > max) {
+           new_position = max;
+       }
+       int old_position = oldPos.at(0);
+       for (int i = 0; i < newPos.count(); i++) {
+           QDomElement act = m_document->getTrackEffect(documentTrack, new_position);
+           if (old_position > new_position) {
+               // Moving up, we need to adjust index
+               old_position = oldPos.at(i);
+               new_position = newPos.at(i);
+           }
+           QDomElement before = m_document->getTrackEffect(documentTrack, old_position);
+           if (!act.isNull() && !before.isNull()) {
+               m_document->setTrackEffect(documentTrack, new_position, before);
+               m_document->renderer()->mltMoveEffect(m_document->tracksCount() - track, pos, old_position, new_position);
+           } else emit displayMessage(i18n("Cannot move effect"), ErrorMessage);
+       }
+       emit showTrackEffects(m_document->tracksCount() - track, m_document->trackInfoAt(documentTrack));
         return;
     }
     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
-    if (clip && !clip->effectAt(newPos - 1).isNull() && !clip->effectAt(oldPos - 1).isNull()) {
-        QDomElement act = clip->effectAt(newPos - 1);
-        QDomElement before = clip->effectAt(oldPos - 1);
-        clip->setEffectAt(oldPos - 1, act);
-        clip->setEffectAt(newPos - 1, before);
-        // special case: speed effect, which is a pseudo-effect, not appearing in MLT's effects
-        if (act.attribute("id") == "speed") {
-            m_document->renderer()->mltUpdateEffectPosition(track, pos, oldPos, newPos);
-        } else if (before.attribute("id") == "speed") {
-            m_document->renderer()->mltUpdateEffectPosition(track, pos, newPos, oldPos);
-        } else m_document->renderer()->mltMoveEffect(track, pos, oldPos, newPos);
-        emit clipItemSelected(clip, newPos - 1);
+    if (clip) {
+       int new_position = newPos.at(0);
+       if (new_position > clip->effectsCount()) {
+           new_position = clip->effectsCount();
+       }
+       int old_position = oldPos.at(0);
+       for (int i = 0; i < newPos.count(); i++) {
+           QDomElement act = clip->effectAtIndex(new_position);
+           if (old_position > new_position) {
+               // Moving up, we need to adjust index
+               old_position = oldPos.at(i);
+               new_position = newPos.at(i);
+           }
+           QDomElement before = clip->effectAtIndex(old_position);
+           if (act.isNull() || before.isNull()) {
+               emit displayMessage(i18n("Cannot move effect"), ErrorMessage);
+               return;
+           }
+           clip->moveEffect(before, new_position);
+           // special case: speed effect, which is a pseudo-effect, not appearing in MLT's effects
+           if (act.attribute("id") == "speed") {
+               m_document->renderer()->mltUpdateEffectPosition(track, pos, old_position, new_position);
+           } else if (before.attribute("id") == "speed") {
+               m_document->renderer()->mltUpdateEffectPosition(track, pos, new_position, old_position);
+           } else m_document->renderer()->mltMoveEffect(track, pos, old_position, new_position);
+       }
+       clip->setSelectedEffect(newPos.at(0));
+       emit clipItemSelected(clip);
         setDocumentModified();
     } else emit displayMessage(i18n("Cannot move effect"), ErrorMessage);
 }
 
-void CustomTrackView::slotChangeEffectState(ClipItem *clip, int track, int effectPos, bool disable)
+void CustomTrackView::slotChangeEffectState(ClipItem *clip, int track, QList <int> effectIndexes, bool disable)
 {
-    EditEffectCommand *command;
-    QDomElement effect;
-    if (clip == NULL) effect = m_document->getTrackEffect(track - 1, effectPos);
-    else effect = clip->effectAt(effectPos);
-    QDomElement oldEffect = effect.cloneNode().toElement();
-    effect.setAttribute("disable", (int) disable);
-
-
+    ChangeEffectStateCommand *command;
     if (clip == NULL) {
         // editing track effect
-        command = new EditEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), oldEffect, effect, effectPos, true);
+        command = new ChangeEffectStateCommand(this, m_document->tracksCount() - track, GenTime(-1), effectIndexes, disable, false, true);
     } else {
-        command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldEffect, effect, effectPos, true);
+       // Check if we have a speed effect, disabling / enabling it needs a special procedure since it is a pseudoo effect
+       QList <int> speedEffectIndexes;
+       for (int i = 0; i < effectIndexes.count(); i++) {
+           QDomElement effect = clip->effectAtIndex(effectIndexes.at(i));
+           if (effect.attribute("id") == "speed") {
+               // speed effect
+               speedEffectIndexes << effectIndexes.at(i);
+               QDomElement newEffect = effect.cloneNode().toElement();
+               newEffect.setAttribute("disable", (int) disable);
+               EditEffectCommand *editcommand = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effect, newEffect, effectIndexes.at(i), false, true);
+               m_commandStack->push(editcommand);
+           }
+       }
+       for (int j = 0; j < speedEffectIndexes.count(); j++) {
+           effectIndexes.removeAll(speedEffectIndexes.at(j));
+       }
+        command = new ChangeEffectStateCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effectIndexes, disable, false, true);
     }
     m_commandStack->push(command);
-    setDocumentModified();;
+    setDocumentModified();
 }
 
-void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int track, int currentPos, int newPos)
+void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int track, QList <int> currentPos, int newPos)
 {
     MoveEffectCommand *command;
     if (clip == NULL) {
@@ -2031,20 +2162,20 @@ void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int track, int cu
     setDocumentModified();
 }
 
-void CustomTrackView::slotUpdateClipEffect(ClipItem *clip, int track, QDomElement oldeffect, QDomElement effect, int ix)
+void CustomTrackView::slotUpdateClipEffect(ClipItem *clip, int track, QDomElement oldeffect, QDomElement effect, int ix, bool refreshEffectStack)
 {
     EditEffectCommand *command;
-    if (clip) command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, true);
-    else command = new EditEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), oldeffect, effect, ix, true);
+    if (clip) command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, refreshEffectStack, true);
+    else command = new EditEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), oldeffect, effect, ix, refreshEffectStack, true);
     m_commandStack->push(command);
 }
 
 void CustomTrackView::slotUpdateClipRegion(ClipItem *clip, int ix, QString region)
 {
-    QDomElement effect = clip->getEffectAt(ix);
+    QDomElement effect = clip->getEffectAtIndex(ix);
     QDomElement oldeffect = effect.cloneNode().toElement();
     effect.setAttribute("region", region);
-    EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, true);
+    EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, true, true);
     m_commandStack->push(command);
 }
 
@@ -2080,23 +2211,23 @@ ClipItem *CustomTrackView::cutClip(ItemInfo info, GenTime cutTime, bool cut, boo
         // fade in from 2nd part of the clip
         int ix = dup->hasEffect(QString(), "fadein");
         if (ix != -1) {
-            QDomElement oldeffect = dup->effectAt(ix);
+            QDomElement oldeffect = dup->effectAtIndex(ix);
             dup->deleteEffect(oldeffect.attribute("kdenlive_ix"));
         }
         ix = dup->hasEffect(QString(), "fade_from_black");
         if (ix != -1) {
-            QDomElement oldeffect = dup->effectAt(ix);
+            QDomElement oldeffect = dup->effectAtIndex(ix);
             dup->deleteEffect(oldeffect.attribute("kdenlive_ix"));
         }
         // fade out from 1st part of the clip
         ix = item->hasEffect(QString(), "fadeout");
         if (ix != -1) {
-            QDomElement oldeffect = item->effectAt(ix);
+            QDomElement oldeffect = item->effectAtIndex(ix);
             item->deleteEffect(oldeffect.attribute("kdenlive_ix"));
         }
         ix = item->hasEffect(QString(), "fade_to_black");
         if (ix != -1) {
-            QDomElement oldeffect = item->effectAt(ix);
+            QDomElement oldeffect = item->effectAtIndex(ix);
             item->deleteEffect(oldeffect.attribute("kdenlive_ix"));
         }
 
@@ -2137,12 +2268,12 @@ ClipItem *CustomTrackView::cutClip(ItemInfo info, GenTime cutTime, bool cut, boo
         // join fade effects again
         int ix = dup->hasEffect(QString(), "fadeout");
         if (ix != -1) {
-            QDomElement effect = dup->effectAt(ix);
+            QDomElement effect = dup->effectAtIndex(ix);
             item->addEffect(effect);
         }
         ix = dup->hasEffect(QString(), "fade_to_black");
         if (ix != -1) {
-            QDomElement effect = dup->effectAt(ix);
+            QDomElement effect = dup->effectAtIndex(ix);
             item->addEffect(effect);
         }
 
@@ -2471,10 +2602,14 @@ void CustomTrackView::dropEvent(QDropEvent * event)
             groupSelectedItems(true);
         } else if (items.count() == 1) {
             m_dragItem = static_cast <AbstractClipItem *>(items.at(0));
-            emit clipItemSelected((ClipItem*)m_dragItem, -1, false);
+            emit clipItemSelected((ClipItem*)m_dragItem, false);
         }
         event->setDropAction(Qt::MoveAction);
         event->accept();
+
+        /// \todo enable when really working
+//        alignAudio();
+
     } else QGraphicsView::dropEvent(event);
     setFocus();
 }
@@ -2638,13 +2773,14 @@ int CustomTrackView::duration() const
 
 void CustomTrackView::addTrack(TrackInfo type, int ix)
 {
+    QList <TransitionInfo> transitionInfos;
     if (ix == -1 || ix == m_document->tracksCount()) {
         m_document->insertTrack(0, type);
-        m_document->renderer()->mltInsertTrack(1, type.type == VIDEOTRACK);
+        transitionInfos = m_document->renderer()->mltInsertTrack(1, type.type == VIDEOTRACK);
     } else {
         m_document->insertTrack(m_document->tracksCount() - ix, type);
         // insert track in MLT playlist
-        m_document->renderer()->mltInsertTrack(m_document->tracksCount() - ix, type.type == VIDEOTRACK);
+        transitionInfos = m_document->renderer()->mltInsertTrack(m_document->tracksCount() - ix, type.type == VIDEOTRACK);
 
         double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
         QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY);
@@ -2686,14 +2822,24 @@ void CustomTrackView::addTrack(TrackInfo type, int ix)
                         emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", clipinfo.startPos.frames(m_document->fps()), clipinfo.track), ErrorMessage);
                     }
                 }
-            } else if (item->type() == TRANSITIONWIDGET) {
+            } /*else if (item->type() == TRANSITIONWIDGET) {
                 Transition *tr = static_cast <Transition *>(item);
                 int track = tr->transitionEndTrack();
                 if (track >= ix) {
                     tr->updateTransitionEndTrack(getPreviousVideoTrack(clipinfo.track));
                 }
-            }
-        }
+            }*/
+        }
+        // Sync transition tracks with MLT playlist
+        Transition *tr;        
+       TransitionInfo info;
+       for (int i = 0; i < transitionInfos.count(); i++) {
+           info = transitionInfos.at(i);
+           tr = getTransitionItem(info);
+           if (tr) tr->setForcedTrack(info.forceTrack, info.a_track);
+           else kDebug()<<"// Cannot update TRANSITION AT: "<<info.b_track<<" / "<<info.startPos.frames(m_document->fps()); 
+       }
+       
         resetSelectionGroup(false);
         m_document->renderer()->unlockService(tractor);
     }
@@ -3443,7 +3589,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
                     KdenliveSettings::setSnaptopoints(false);
                     item->setPos((int) m_dragItemInfo.startPos.frames(m_document->fps()), (int)(m_dragItemInfo.track * m_tracksHeight + 1));
                     KdenliveSettings::setSnaptopoints(snap);
-                    emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(m_dragItemInfo.startPos.frames(m_document->fps()))), ErrorMessage);
+                    emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(info.startPos.frames(m_document->fps()))), ErrorMessage);
                 }
                 setDocumentModified();
             } else if (m_dragItem->type() == TRANSITIONWIDGET && (m_dragItemInfo.startPos != info.startPos || m_dragItemInfo.track != info.track)) {
@@ -3532,7 +3678,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
                         adjustTimelineClips(m_scene->editMode(), clip, ItemInfo(), moveGroup);
                         m_document->renderer()->mltInsertClip(info, clip->xml(), clip->getProducer(trackProducer), m_scene->editMode() == OVERWRITEEDIT, m_scene->editMode() == INSERTEDIT);
                         for (int i = 0; i < clip->effectsCount(); i++) {
-                            m_document->renderer()->mltAddEffect(info.track, info.startPos, getEffectArgs(clip->effectAt(i)), false);
+                            m_document->renderer()->mltAddEffect(info.track, info.startPos, getEffectArgs(clip->effect(i)), false);
                         }
                     } else {
                         Transition *tr = static_cast <Transition*>(item);
@@ -3636,7 +3782,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
         int ix = item->hasEffect("volume", "fadein");
         int ix2 = item->hasEffect("", "fade_from_black");
         if (ix != -1) {
-            QDomElement oldeffect = item->effectAt(ix);
+            QDomElement oldeffect = item->effectAtIndex(ix);
             int start = item->cropStart().frames(m_document->fps());
             int end = item->fadeIn();
             if (end == 0) {
@@ -3647,7 +3793,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
                 slotUpdateClipEffect(item, -1, effect, oldeffect, ix);
-                emit clipItemSelected(item, ix);
+                emit clipItemSelected(item);
             }
         } else if (item->fadeIn() != 0 && ix2 == -1) {
             QDomElement effect;
@@ -3659,7 +3805,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
             slotAddEffect(effect, m_dragItem->startPos(), m_dragItem->track());
         }
         if (ix2 != -1) {
-            QDomElement oldeffect = item->effectAt(ix2);
+            QDomElement oldeffect = item->effectAtIndex(ix2);
             int start = item->cropStart().frames(m_document->fps());
             int end = item->fadeIn();
             if (end == 0) {
@@ -3670,7 +3816,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
                 slotUpdateClipEffect(item, -1, effect, oldeffect, ix2);
-                emit clipItemSelected(item, ix2);
+                emit clipItemSelected(item);
             }
         }
     } else if (m_operationMode == FADEOUT) {
@@ -3679,7 +3825,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
         int ix = item->hasEffect("volume", "fadeout");
         int ix2 = item->hasEffect("", "fade_to_black");
         if (ix != -1) {
-            QDomElement oldeffect = item->effectAt(ix);
+            QDomElement oldeffect = item->effectAtIndex(ix);
             int end = (item->cropDuration() + item->cropStart()).frames(m_document->fps());
             int start = item->fadeOut();
             if (start == 0) {
@@ -3691,7 +3837,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
                 // kDebug()<<"EDIT FADE OUT : "<<start<<"x"<<end;
                 slotUpdateClipEffect(item, -1, effect, oldeffect, ix);
-                emit clipItemSelected(item, ix);
+                emit clipItemSelected(item);
             }
         } else if (item->fadeOut() != 0 && ix2 == -1) {
             QDomElement effect;
@@ -3704,7 +3850,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
             slotAddEffect(effect, m_dragItem->startPos(), m_dragItem->track());
         }
         if (ix2 != -1) {
-            QDomElement oldeffect = item->effectAt(ix2);
+            QDomElement oldeffect = item->effectAtIndex(ix2);
             int end = (item->cropDuration() + item->cropStart()).frames(m_document->fps());
             int start = item->fadeOut();
             if (start == 0) {
@@ -3716,7 +3862,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
                 // kDebug()<<"EDIT FADE OUT : "<<start<<"x"<<end;
                 slotUpdateClipEffect(item, -1, effect, oldeffect, ix2);
-                emit clipItemSelected(item, ix2);
+                emit clipItemSelected(item);
             }
         }
     } else if (m_operationMode == KEYFRAME) {
@@ -3734,20 +3880,20 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
 
         if ((val < -50 || val > 150) && item->editedKeyFramePos() != start && item->editedKeyFramePos() != end && item->keyFrameNumber() > 1) {
             //delete keyframe
-            item->movedKeyframe(item->getEffectAt(item->selectedEffectIndex()), item->selectedKeyFramePos(), -1, 0);
+            item->movedKeyframe(item->getEffectAtIndex(item->selectedEffectIndex()), item->selectedKeyFramePos(), -1, 0);
         } else {
-            item->movedKeyframe(item->getEffectAt(item->selectedEffectIndex()), item->selectedKeyFramePos(), item->editedKeyFramePos(), item->editedKeyFrameValue());
+            item->movedKeyframe(item->getEffectAtIndex(item->selectedEffectIndex()), item->selectedKeyFramePos(), item->editedKeyFramePos(), item->editedKeyFrameValue());
         }
 
         QDomElement newEffect = item->selectedEffect().cloneNode().toElement();
         //item->updateKeyframeEffect();
         //QString next = item->keyframes(item->selectedEffectIndex());
         //EditKeyFrameCommand *command = new EditKeyFrameCommand(this, item->track(), item->startPos(), item->selectedEffectIndex(), previous, next, false);
-        EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), oldEffect, newEffect, item->selectedEffectIndex(), false);
+        EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), oldEffect, newEffect, item->selectedEffectIndex(), false, false);
 
         m_commandStack->push(command);
-        updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
-        emit clipItemSelected(item, item->selectedEffectIndex());
+        updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect());
+        emit clipItemSelected(item);
     }
     if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET && m_dragItem->isSelected()) {
         // A transition is selected
@@ -4136,7 +4282,7 @@ void CustomTrackView::addClip(QDomElement xml, const QString &clipId, ItemInfo i
     info.track = m_document->tracksCount() - info.track;
     m_document->renderer()->mltInsertClip(info, xml, item->getProducer(producerTrack), overwrite, push);
     for (int i = 0; i < item->effectsCount(); i++) {
-        m_document->renderer()->mltAddEffect(info.track, info.startPos, getEffectArgs(item->effectAt(i)), false);
+        m_document->renderer()->mltAddEffect(info.track, info.startPos, getEffectArgs(item->effect(i)), false);
     }
     setDocumentModified();
     if (refresh)
@@ -4229,6 +4375,14 @@ ClipItem *CustomTrackView::getClipItemAt(GenTime pos, int track)
     return getClipItemAt((int) pos.frames(m_document->fps()), track);
 }
 
+
+Transition *CustomTrackView::getTransitionItem(TransitionInfo info)
+{
+    int pos = info.startPos.frames(m_document->fps());
+    int track = m_document->tracksCount() - info.b_track;
+    return getTransitionItemAt(pos, track);
+}
+
 Transition *CustomTrackView::getTransitionItemAt(int pos, int track)
 {
     const QPointF p(pos, track * m_tracksHeight + Transition::itemOffset() + 1);
@@ -4282,18 +4436,26 @@ Transition *CustomTrackView::getTransitionItemAtStart(GenTime pos, int track)
     return clip;
 }
 
-void CustomTrackView::moveClip(const ItemInfo &start, const ItemInfo &end, bool refresh)
+bool CustomTrackView::moveClip(const ItemInfo &start, const ItemInfo &end, bool refresh, ItemInfo *out_actualEnd)
 {
     if (m_selectionGroup) resetSelectionGroup(false);
     ClipItem *item = getClipItemAt((int) start.startPos.frames(m_document->fps()), start.track);
     if (!item) {
         emit displayMessage(i18n("Cannot move clip at time: %1 on track %2", m_document->timecode().getTimecodeFromFrames(start.startPos.frames(m_document->fps())), start.track), ErrorMessage);
         kDebug() << "----------------  ERROR, CANNOT find clip to move at.. ";
-        return;
+        return false;
     }
     Mlt::Producer *prod = item->getProducer(end.track);
 
-    bool success = m_document->renderer()->mltMoveClip((int)(m_document->tracksCount() - start.track), (int)(m_document->tracksCount() - end.track), (int) start.startPos.frames(m_document->fps()), (int)end.startPos.frames(m_document->fps()), prod);
+#ifdef DEBUG
+    qDebug() << "Moving item " << (long)item << " from .. to:";
+    qDebug() << item->info();
+    qDebug() << start;
+    qDebug() << end;
+#endif
+    bool success = m_document->renderer()->mltMoveClip((int)(m_document->tracksCount() - start.track), (int)(m_document->tracksCount() - end.track),
+                                                       (int) start.startPos.frames(m_document->fps()), (int)end.startPos.frames(m_document->fps()),
+                                                       prod);
     if (success) {
         bool snap = KdenliveSettings::snaptopoints();
         KdenliveSettings::setSnaptopoints(false);
@@ -4323,6 +4485,16 @@ void CustomTrackView::moveClip(const ItemInfo &start, const ItemInfo &end, bool
         emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(end.startPos.frames(m_document->fps()))), ErrorMessage);
     }
     if (refresh) m_document->renderer()->doRefresh();
+    if (out_actualEnd != NULL) {
+        *out_actualEnd = item->info();
+#ifdef DEBUG
+        qDebug() << "Actual end position updated:" << *out_actualEnd;
+#endif
+    }
+#ifdef DEBUG
+    qDebug() << item->info();
+#endif
+    return success;
 }
 
 void CustomTrackView::moveGroup(QList <ItemInfo> startClip, QList <ItemInfo> startTransition, const GenTime &offset, const int trackOffset, bool reverseMove)
@@ -4414,7 +4586,7 @@ void CustomTrackView::moveGroup(QList <ItemInfo> startClip, QList <ItemInfo> sta
                 info.track = m_document->tracksCount() - info.track;
                 m_document->renderer()->mltInsertClip(info, clip->xml(), clip->getProducer(trackProducer));
                 for (int i = 0; i < clip->effectsCount(); i++) {
-                    m_document->renderer()->mltAddEffect(info.track, info.startPos, getEffectArgs(clip->effectAt(i)), false);
+                    m_document->renderer()->mltAddEffect(info.track, info.startPos, getEffectArgs(clip->effect(i)), false);
                 }
             } else if (item->type() == TRANSITIONWIDGET) {
                 Transition *tr = static_cast <Transition*>(item);
@@ -4743,7 +4915,7 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool
         // there is a fade in effect
         int effectPos = item->hasEffect("volume", "fadein");
         if (effectPos != -1) {
-            QDomElement effect = item->getEffectAt(effectPos);
+            QDomElement effect = item->getEffectAtIndex(effectPos);
             int start = item->cropStart().frames(m_document->fps());
             int max = item->cropDuration().frames(m_document->fps());
             if (end > max) {
@@ -4759,12 +4931,12 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool
                     emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
                 // if fade effect is displayed, update the effect edit widget with new clip duration
                 if (item->isSelected() && effectPos == item->selectedEffectIndex())
-                    emit clipItemSelected(item, effectPos);
+                    emit clipItemSelected(item);
             }
         }
         effectPos = item->hasEffect("brightness", "fade_from_black");
         if (effectPos != -1) {
-            QDomElement effect = item->getEffectAt(effectPos);
+            QDomElement effect = item->getEffectAtIndex(effectPos);
             int start = item->cropStart().frames(m_document->fps());
             int max = item->cropDuration().frames(m_document->fps());
             if (end > max) {
@@ -4780,7 +4952,7 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool
                     emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
                 // if fade effect is displayed, update the effect edit widget with new clip duration
                 if (item->isSelected() && effectPos == item->selectedEffectIndex())
-                    emit clipItemSelected(item, effectPos);
+                    emit clipItemSelected(item);
             }
         }
     }
@@ -4790,7 +4962,7 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool
         // there is a fade out effect
         int effectPos = item->hasEffect("volume", "fadeout");
         if (effectPos != -1) {
-            QDomElement effect = item->getEffectAt(effectPos);
+            QDomElement effect = item->getEffectAtIndex(effectPos);
             int max = item->cropDuration().frames(m_document->fps());
             int end = max + item->cropStart().frames(m_document->fps());
             if (start > max) {
@@ -4806,12 +4978,12 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool
                     emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
                 // if fade effect is displayed, update the effect edit widget with new clip duration
                 if (item->isSelected() && effectPos == item->selectedEffectIndex())
-                    emit clipItemSelected(item, effectPos);
+                    emit clipItemSelected(item);
             }
         }
         effectPos = item->hasEffect("brightness", "fade_to_black");
         if (effectPos != -1) {
-            QDomElement effect = item->getEffectAt(effectPos);
+            QDomElement effect = item->getEffectAtIndex(effectPos);
             int max = item->cropDuration().frames(m_document->fps());
             int end = max + item->cropStart().frames(m_document->fps());
             if (start > max) {
@@ -4827,7 +4999,7 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool
                     emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
                 // if fade effect is displayed, update the effect edit widget with new clip duration
                 if (item->isSelected() && effectPos == item->selectedEffectIndex())
-                    emit clipItemSelected(item, effectPos);
+                    emit clipItemSelected(item);
             }
         }
     }
@@ -4836,13 +5008,13 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool
     if (effectPos != -1) {
         // Freeze effect needs to be adjusted with clip resize
         int diff = (info.startPos - item->startPos()).frames(m_document->fps());
-        QDomElement eff = item->getEffectAt(effectPos);
+        QDomElement eff = item->getEffectAtIndex(effectPos);
         if (!eff.isNull() && diff != 0) {
             int freeze_pos = EffectsList::parameter(eff, "frame").toInt() + diff;
             EffectsList::setParameter(eff, "frame", QString::number(freeze_pos));
             if (standalone) {
                 if (item->isSelected() && item->selectedEffect().attribute("id") == "freeze") {
-                    emit clipItemSelected(item, item->selectedEffectIndex());
+                    emit clipItemSelected(item);
                 }
             }
         }
@@ -5527,7 +5699,7 @@ void CustomTrackView::pasteClipEffects()
         if (clips.at(i)->type() == AVWIDGET) {
             ClipItem *item = static_cast < ClipItem *>(clips.at(i));
             for (int j = 0; j < clip->effectsCount(); j++) {
-                QDomElement eff = clip->effectAt(j);
+                QDomElement eff = clip->effect(j);
                 if (eff.attribute("unique", "0") == "0" || item->hasEffect(eff.attribute("tag"), eff.attribute("id")) == -1) {
                     adjustKeyfames(clip->cropStart(), item->cropStart(), item->cropDuration(), eff);
                     new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), eff, true, paste);
@@ -5752,6 +5924,7 @@ void CustomTrackView::slotInsertTrack(int ix)
         info.duration = 0;
         info.isMute = false;
         info.isLocked = false;
+       info.effectsList = EffectsList(true);
         if (d.video_track->isChecked()) {
             info.type = VIDEOTRACK;
             info.isBlind = false;
@@ -5943,6 +6116,146 @@ void CustomTrackView::splitAudio()
     }
 }
 
+void CustomTrackView::setAudioAlignReference()
+{
+    QList<QGraphicsItem *> selection = scene()->selectedItems();
+    if (selection.isEmpty() || selection.size() > 1) {
+        emit displayMessage(i18n("You must select exactly one clip for the audio reference."), ErrorMessage);
+        return;
+    }
+    if (m_audioCorrelator != NULL) {
+        delete m_audioCorrelator;
+    }
+    if (selection.at(0)->type() == AVWIDGET) {
+        ClipItem *clip = static_cast<ClipItem*>(selection.at(0));
+        if (clip->clipType() == AV || clip->clipType() == AUDIO) {
+            m_audioAlignmentReference = clip;
+
+            AudioEnvelope *envelope = new AudioEnvelope(clip->getProducer(clip->track()));
+            m_audioCorrelator = new AudioCorrelation(envelope);
+
+
+#ifdef DEBUG
+            envelope->drawEnvelope().save("kdenlive-audio-reference-envelope.png");
+            envelope->dumpInfo();
+#endif
+
+
+            emit displayMessage(i18n("Audio align reference set."), InformationMessage);
+        }
+        return;
+    }
+    emit displayMessage(i18n("Reference for audio alignment must contain audio data."), ErrorMessage);
+}
+
+void CustomTrackView::alignAudio()
+{
+    bool referenceOK = true;
+    if (m_audioCorrelator == NULL) {
+        referenceOK = false;
+    }
+    if (referenceOK) {
+        if (!scene()->items().contains(m_audioAlignmentReference)) {
+            // The reference item has been deleted from the timeline (or so)
+            referenceOK = false;
+        }
+    }
+    if (!referenceOK) {
+        emit displayMessage(i18n("Audio alignment reference not yet set."), InformationMessage);
+        return;
+    }
+
+    int counter = 0;
+    QList<QGraphicsItem *> selection = scene()->selectedItems();
+    foreach (QGraphicsItem *item, selection) {
+        if (item->type() == AVWIDGET) {
+
+            ClipItem *clip = static_cast<ClipItem*>(item);
+            if (clip == m_audioAlignmentReference) {
+                continue;
+            }
+
+            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()));
+
+                // 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.
+                int index = m_audioCorrelator->addChild(envelope, envelope->envelopeSize() > 200);
+                int shift = m_audioCorrelator->getShift(index);
+                counter++;
+
+
+#ifdef DEBUG
+                m_audioCorrelator->info(index)->toImage().save("kdenlive-audio-align-cross-correlation.png");
+                envelope->drawEnvelope().save("kdenlive-audio-align-envelope.png");
+                envelope->dumpInfo();
+
+                int targetPos = m_audioAlignmentReference->startPos().frames(m_document->fps()) + shift;
+                qDebug() << "Reference starts at " << m_audioAlignmentReference->startPos().frames(m_document->fps());
+                qDebug() << "We will start at " << targetPos;
+                qDebug() << "to shift by " << shift;
+                qDebug() << "(eventually)";
+                qDebug() << "(maybe)";
+#endif
+
+
+                QUndoCommand *moveCommand = new QUndoCommand();
+
+                GenTime add(shift, m_document->fps());
+                ItemInfo start = clip->info();
+
+                ItemInfo end = start;
+                end.startPos = m_audioAlignmentReference->startPos() + add - m_audioAlignmentReference->cropStart();
+                end.endPos = end.startPos + start.cropDuration;
+
+                if ( end.startPos.seconds() < 0 ) {
+                    // Clip would start before 0, so crop it first
+                    GenTime cropBy = -end.startPos;
+
+#ifdef DEBUG
+                    qDebug() << "Need to crop clip. " << start;
+                    qDebug() << "end.startPos: " << end.startPos.toString() << ", cropBy: " << cropBy.toString();
+#endif
+
+                    ItemInfo resized = start;
+                    resized.startPos += cropBy;
+
+                    resizeClip(start, resized);
+                    new ResizeClipCommand(this, start, resized, false, false, moveCommand);
+
+                    start = clip->info();
+                    end.startPos += cropBy;
+
+#ifdef DEBUG
+                    qDebug() << "Clip cropped. " << start;
+                    qDebug() << "Moving to: " << end;
+#endif
+                }
+
+                if (itemCollision(clip, end)) {
+                    delete moveCommand;
+                    emit displayMessage(i18n("Unable to move clip due to collision."), ErrorMessage);
+                    return;
+                }
+
+                moveCommand->setText(i18n("Auto-align clip"));
+                new MoveClipCommand(this, start, end, true, moveCommand);
+                updateTrackDuration(clip->track(), moveCommand);
+                m_commandStack->push(moveCommand);
+
+            }
+        }
+    }
+
+    if (counter == 0) {
+        emit displayMessage(i18n("No audio clips selected."), ErrorMessage);
+    } else {
+        emit displayMessage(i18n("Auto-aligned %1 clips.").arg(counter), InformationMessage);
+    }
+}
+
 void CustomTrackView::doSplitAudio(const GenTime &pos, int track, EffectsList effects, bool split)
 {
     ClipItem *clip = getClipItemAt(pos, track);
@@ -5996,10 +6309,10 @@ void CustomTrackView::doSplitAudio(const GenTime &pos, int track, EffectsList ef
                 int audioIx = 0;
                 for (int i = 0; i < effects.count(); ++i) {
                     if (effects.at(i).attribute("type") == "audio") {
-                        deleteEffect(m_document->tracksCount() - track, pos, clip->effectAt(videoIx));
+                        deleteEffect(m_document->tracksCount() - track, pos, clip->effect(videoIx));
                         audioIx++;
                     } else {
-                        deleteEffect(freetrack, pos, audioClip->effectAt(audioIx));
+                        deleteEffect(freetrack, pos, audioClip->effect(audioIx));
                         videoIx++;
                     }
                 }
@@ -6026,8 +6339,15 @@ void CustomTrackView::doSplitAudio(const GenTime &pos, int track, EffectsList ef
                 deleteClip(clp->info());
                 clip->setVideoOnly(false);
                 Mlt::Tractor *tractor = m_document->renderer()->lockService();
-                if (!m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - info.track, info.startPos.frames(m_document->fps()), clip->baseClip()->getProducer(info.track))) {
-                    emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", info.startPos.frames(m_document->fps()), info.track), ErrorMessage);
+                if (!m_document->renderer()->mltUpdateClipProducer(
+                            tractor,
+                            m_document->tracksCount() - info.track,
+                            info.startPos.frames(m_document->fps()),
+                            clip->baseClip()->getProducer(info.track))) {
+
+                    emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)",
+                                             info.startPos.frames(m_document->fps()), info.track),
+                                        ErrorMessage);
                 }
                 m_document->renderer()->unlockService(tractor);
 
@@ -6474,12 +6794,12 @@ void CustomTrackView::insertZoneOverwrite(QStringList data, int in)
         splitAudio();
 }
 
-void CustomTrackView::clearSelection()
+void CustomTrackView::clearSelection(bool emitInfo)
 {
     resetSelectionGroup();
     scene()->clearSelection();
     m_dragItem = NULL;
-    emit clipItemSelected(NULL);
+    if (emitInfo) emit clipItemSelected(NULL);
 }
 
 void CustomTrackView::updatePalette()
@@ -6491,7 +6811,6 @@ void CustomTrackView::updatePalette()
         pen1.setColor(palette().text().color());
         m_cursorLine->setPen(pen1);
     }
-    emit tracksChanged();
 }
 
 void CustomTrackView::removeTipAnimation()
@@ -6598,9 +6917,42 @@ bool CustomTrackView::hasAudio(int track) const
 
 void CustomTrackView::slotAddTrackEffect(const QDomElement &effect, int ix)
 {
-    AddEffectCommand *command = new AddEffectCommand(this, m_document->tracksCount() - ix, GenTime(-1), effect, true);
-    m_commandStack->push(command);
-    setDocumentModified();
+    
+    QUndoCommand *effectCommand = new QUndoCommand();
+    QString effectName;
+    if (effect.tagName() == "effectgroup") {
+        effectName = effect.attribute("name");
+    } else {
+        QDomElement namenode = effect.firstChildElement("name");
+        if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data());
+        else effectName = i18n("effect");
+    }
+    effectCommand->setText(i18n("Add %1", effectName));
+    if (effect.tagName() == "effectgroup") {
+        QDomNodeList effectlist = effect.elementsByTagName("effect");
+        for (int j = 0; j < effectlist.count(); j++) {
+            QDomElement trackeffect = effectlist.at(j).toElement();
+            if (trackeffect.attribute("unique", "0") != "0" && m_document->hasTrackEffect(m_document->tracksCount() - ix - 1, trackeffect.attribute("tag"), trackeffect.attribute("id")) != -1) {
+                emit displayMessage(i18n("Effect already present in track"), ErrorMessage);
+                continue;
+            }
+            new AddEffectCommand(this, m_document->tracksCount() - ix, GenTime(-1), trackeffect, true, effectCommand);
+        }
+    }
+    else {
+        if (effect.attribute("unique", "0") != "0" && m_document->hasTrackEffect(m_document->tracksCount() - ix - 1, effect.attribute("tag"), effect.attribute("id")) != -1) {
+            emit displayMessage(i18n("Effect already present in track"), ErrorMessage);
+            delete effectCommand;
+            return;
+        }
+        new AddEffectCommand(this, m_document->tracksCount() - ix, GenTime(-1), effect, true, effectCommand);
+    }
+
+    if (effectCommand->childCount() > 0) {
+        m_commandStack->push(effectCommand);
+        setDocumentModified();
+    }
+    else delete effectCommand;
 }
 
 
@@ -6609,23 +6961,45 @@ EffectsParameterList CustomTrackView::getEffectArgs(const QDomElement &effect)
     EffectsParameterList parameters;
     QLocale locale;
     parameters.addParam("tag", effect.attribute("tag"));
-    if (effect.hasAttribute("region")) parameters.addParam("region", effect.attribute("region"));
+    //if (effect.hasAttribute("region")) parameters.addParam("region", effect.attribute("region"));
     parameters.addParam("kdenlive_ix", effect.attribute("kdenlive_ix"));
+    parameters.addParam("kdenlive_info", effect.attribute("kdenlive_info"));
     parameters.addParam("id", effect.attribute("id"));
     if (effect.hasAttribute("src")) parameters.addParam("src", effect.attribute("src"));
     if (effect.hasAttribute("disable")) parameters.addParam("disable", effect.attribute("disable"));
     if (effect.hasAttribute("in")) parameters.addParam("in", effect.attribute("in"));
     if (effect.hasAttribute("out")) parameters.addParam("out", effect.attribute("out"));
+    if (effect.attribute("id") == "region") {
+       QDomNodeList subeffects = effect.elementsByTagName("effect");
+       for (int i = 0; i < subeffects.count(); i++) {
+           QDomElement subeffect = subeffects.at(i).toElement();
+           int subeffectix = subeffect.attribute("region_ix").toInt();
+           parameters.addParam(QString("filter%1").arg(subeffectix), subeffect.attribute("id"));
+           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));
+       }
+    }
 
     QDomNodeList params = effect.elementsByTagName("parameter");
-    for (int i = 0; i < params.count(); i++) {
+    adjustEffectParameters(parameters, params);
+    
+    return parameters;
+}
+
+
+void CustomTrackView::adjustEffectParameters(EffectsParameterList &parameters, QDomNodeList params, const QString &prefix)
+{
+  QLocale locale;
+  for (int i = 0; i < params.count(); i++) {
         QDomElement e = params.item(i).toElement();
+       QString paramname = prefix + e.attribute("name");
         if (e.attribute("type") == "geometry" && !e.hasAttribute("fixed")) {
             // effects with geometry param need in / out synced with the clip, request it...
             parameters.addParam("_sync_in_out", "1");
         }
         if (e.attribute("type") == "simplekeyframe") {
-
             QStringList values = e.attribute("keyframes").split(";", QString::SkipEmptyParts);
             double factor = e.attribute("factor", "1").toDouble();
             double offset = e.attribute("offset", "0").toDouble();
@@ -6635,7 +7009,7 @@ EffectsParameterList CustomTrackView::getEffectArgs(const QDomElement &effect)
                 values[j] = pos + "=" + locale.toString(val);
             }
             // kDebug() << "/ / / /SENDING KEYFR:" << values;
-            parameters.addParam(e.attribute("name"), values.join(";"));
+            parameters.addParam(paramname, values.join(";"));
             /*parameters.addParam(e.attribute("name"), e.attribute("keyframes").replace(":", "="));
             parameters.addParam("max", e.attribute("max"));
             parameters.addParam("min", e.attribute("min"));
@@ -6671,15 +7045,15 @@ EffectsParameterList CustomTrackView::getEffectArgs(const QDomElement &effect)
                     fact = e.attribute("factor", "1").toDouble();
                 }
                 double offset = e.attribute("offset", "0").toDouble();
-                parameters.addParam(e.attribute("name"), locale.toString((e.attribute("value").toDouble() - offset) / fact));
+                parameters.addParam(paramname, locale.toString((e.attribute("value").toDouble() - offset) / fact));
             } else {
-                parameters.addParam(e.attribute("name"), e.attribute("value"));
+                parameters.addParam(paramname, e.attribute("value"));
             }
         }
     }
-    return parameters;
 }
 
+
 void CustomTrackView::updateTrackNames(int track, bool added)
 {
     QList <TrackInfo> tracks = m_document->tracksList();
@@ -6769,7 +7143,7 @@ void CustomTrackView::adjustEffects(ClipItem* item, ItemInfo oldInfo, QUndoComma
     if (effects.count()) {
         QMap<int, QDomElement>::const_iterator i = effects.constBegin();
         while (i != effects.constEnd()) {
-            new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), i.value(), item->effectAt(i.key()), i.key(), false, command);
+            new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), i.value(), item->effect(i.key()), i.key(), false, false, command);
             ++i;
         }
     }
@@ -6784,7 +7158,7 @@ void CustomTrackView::slotGotFilterJobResults(const QString &/*id*/, int startPo
         return;
     }
     QDomElement newEffect;
-    QDomElement effect = clip->getEffectAt(clip->selectedEffectIndex());
+    QDomElement effect = clip->getEffectAtIndex(clip->selectedEffectIndex());
     if (effect.attribute("id") == filter) {
         newEffect = effect.cloneNode().toElement();
         QMap<QString, QString>::const_iterator i = filterParams.constBegin();
@@ -6793,9 +7167,9 @@ void CustomTrackView::slotGotFilterJobResults(const QString &/*id*/, int startPo
             kDebug()<<"// RESULT FILTER: "<<i.key()<<"="<< i.value();
             ++i;
         }
-        EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effect, newEffect, clip->selectedEffectIndex(), true);
+        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, clip->selectedEffectIndex());
+        emit clipItemSelected(clip);
     }
     
 }