]> git.sesse.net Git - kdenlive/blob - src/customtrackview.cpp
Merge branch 'master' into audioAlign
[kdenlive] / src / customtrackview.cpp
1 /***************************************************************************
2  *   Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20
21 #include "customtrackview.h"
22 #include "docclipbase.h"
23 #include "clipitem.h"
24 #include "definitions.h"
25 #include "commands/moveclipcommand.h"
26 #include "commands/movetransitioncommand.h"
27 #include "commands/resizeclipcommand.h"
28 #include "commands/editguidecommand.h"
29 #include "commands/addtimelineclipcommand.h"
30 #include "commands/addeffectcommand.h"
31 #include "commands/editeffectcommand.h"
32 #include "commands/moveeffectcommand.h"
33 #include "commands/addtransitioncommand.h"
34 #include "commands/edittransitioncommand.h"
35 #include "commands/editkeyframecommand.h"
36 #include "commands/changespeedcommand.h"
37 #include "commands/addmarkercommand.h"
38 #include "commands/razorclipcommand.h"
39 #include "kdenlivesettings.h"
40 #include "transition.h"
41 #include "clipmanager.h"
42 #include "renderer.h"
43 #include "markerdialog.h"
44 #include "mainwindow.h"
45 #include "ui_keyframedialog_ui.h"
46 #include "clipdurationdialog.h"
47 #include "abstractgroupitem.h"
48 #include "commands/insertspacecommand.h"
49 #include "spacerdialog.h"
50 #include "commands/addtrackcommand.h"
51 #include "commands/movegroupcommand.h"
52 #include "ui_addtrack_ui.h"
53 #include "initeffects.h"
54 #include "commands/locktrackcommand.h"
55 #include "commands/groupclipscommand.h"
56 #include "commands/splitaudiocommand.h"
57 #include "commands/changecliptypecommand.h"
58 #include "trackdialog.h"
59 #include "tracksconfigdialog.h"
60 #include "commands/configtrackscommand.h"
61 #include "commands/rebuildgroupcommand.h"
62 #include "commands/razorgroupcommand.h"
63 #include "profilesdialog.h"
64
65 #include "lib/audio/audioEnvelope.h"
66 #include "lib/audio/audioCorrelation.h"
67
68 #include <KDebug>
69 #include <KLocale>
70 #include <KUrl>
71 #include <KIcon>
72 #include <KCursor>
73 #include <KMessageBox>
74 #include <KIO/NetAccess>
75
76 #include <QMouseEvent>
77 #include <QStylePainter>
78 #include <QGraphicsItem>
79 #include <QDomDocument>
80 #include <QScrollBar>
81 #include <QApplication>
82 #include <QInputDialog>
83
84
85 #if QT_VERSION >= 0x040600
86 #include <QGraphicsDropShadowEffect>
87 #endif
88
89 //#define DEBUG
90
91 bool sortGuidesList(const Guide *g1 , const Guide *g2)
92 {
93     return (*g1).position() < (*g2).position();
94 }
95
96
97 //TODO:
98 // disable animation if user asked it in KDE's global settings
99 // http://lists.kde.org/?l=kde-commits&m=120398724717624&w=2
100 // needs something like below (taken from dolphin)
101 // #include <kglobalsettings.h>
102 // const bool animate = KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects;
103 // const int duration = animate ? 1500 : 1;
104
105 CustomTrackView::CustomTrackView(KdenliveDoc *doc, CustomTrackScene* projectscene, QWidget *parent) :
106     QGraphicsView(projectscene, parent),
107     m_tracksHeight(KdenliveSettings::trackheight()),
108     m_projectDuration(0),
109     m_cursorPos(0),
110     m_document(doc),
111     m_scene(projectscene),
112     m_cursorLine(NULL),
113     m_operationMode(NONE),
114     m_moveOpMode(NONE),
115     m_dragItem(NULL),
116     m_dragGuide(NULL),
117     m_visualTip(NULL),
118     m_animation(NULL),
119     m_clickPoint(),
120     m_autoScroll(KdenliveSettings::autoscroll()),
121     m_pasteEffectsAction(NULL),
122     m_ungroupAction(NULL),
123     m_scrollOffset(0),
124     m_clipDrag(false),
125     m_findIndex(0),
126     m_tool(SELECTTOOL),
127     m_copiedItems(),
128     m_menuPosition(),
129     m_blockRefresh(false),
130     m_selectionGroup(NULL),
131     m_selectedTrack(0),
132     m_audioCorrelator(NULL),
133     m_audioAlignmentReference(NULL),
134     m_controlModifier(false)
135 {
136     if (doc) {
137         m_commandStack = doc->commandStack();
138     } else {
139         m_commandStack = NULL;
140     }
141     m_ct = 0;
142     setMouseTracking(true);
143     setAcceptDrops(true);
144     setFrameShape(QFrame::NoFrame);
145     setLineWidth(0);
146     //setCacheMode(QGraphicsView::CacheBackground);
147     setAutoFillBackground(false);
148     setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
149     setContentsMargins(0, 0, 0, 0);
150
151     m_activeTrackBrush = KStatefulBrush(KColorScheme::View, KColorScheme::ActiveBackground, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
152
153     m_animationTimer = new QTimeLine(800);
154     m_animationTimer->setFrameRange(0, 5);
155     m_animationTimer->setUpdateInterval(100);
156     m_animationTimer->setLoopCount(0);
157
158     m_tipColor = QColor(0, 192, 0, 200);
159     m_tipPen.setColor(QColor(255, 255, 255, 100));
160     m_tipPen.setWidth(3);
161
162     const int maxHeight = m_tracksHeight * m_document->tracksCount();
163     setSceneRect(0, 0, sceneRect().width(), maxHeight);
164     verticalScrollBar()->setMaximum(maxHeight);
165     verticalScrollBar()->setTracking(true);
166     // repaint guides when using vertical scroll
167     connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotRefreshGuides()));
168
169     m_cursorLine = projectscene->addLine(0, 0, 0, maxHeight);
170     m_cursorLine->setZValue(1000);
171     QPen pen1 = QPen();
172     pen1.setWidth(1);
173     pen1.setColor(palette().text().color());
174     m_cursorLine->setPen(pen1);
175     m_cursorLine->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
176
177     connect(&m_scrollTimer, SIGNAL(timeout()), this, SLOT(slotCheckMouseScrolling()));
178     m_scrollTimer.setInterval(100);
179     m_scrollTimer.setSingleShot(true);
180
181     connect(&m_thumbsTimer, SIGNAL(timeout()), this, SLOT(slotFetchNextThumbs()));
182     m_thumbsTimer.setInterval(500);
183     m_thumbsTimer.setSingleShot(true);
184
185     KIcon razorIcon("edit-cut");
186     m_razorCursor = QCursor(razorIcon.pixmap(22, 22));
187
188     KIcon spacerIcon("kdenlive-spacer-tool");
189     m_spacerCursor = QCursor(spacerIcon.pixmap(22, 22));
190 }
191
192 CustomTrackView::~CustomTrackView()
193 {
194     qDeleteAll(m_guides);
195     m_guides.clear();
196     m_waitingThumbs.clear();
197     delete m_animationTimer;
198 }
199
200 //virtual
201 void CustomTrackView::keyPressEvent(QKeyEvent * event)
202 {
203     if (event->key() == Qt::Key_Up) {
204         slotTrackUp();
205         event->accept();
206     } else if (event->key() == Qt::Key_Down) {
207         slotTrackDown();
208         event->accept();
209     } else QWidget::keyPressEvent(event);
210 }
211
212 void CustomTrackView::setDocumentModified()
213 {
214     m_document->setModified(true);
215 }
216
217 void CustomTrackView::setContextMenu(QMenu *timeline, QMenu *clip, QMenu *transition, QActionGroup *clipTypeGroup, QMenu *markermenu)
218 {
219     m_timelineContextMenu = timeline;
220     m_timelineContextClipMenu = clip;
221     m_clipTypeGroup = clipTypeGroup;
222     connect(m_timelineContextMenu, SIGNAL(aboutToHide()), this, SLOT(slotResetMenuPosition()));
223
224     m_markerMenu = new QMenu(i18n("Go to marker..."), this);
225     m_markerMenu->setEnabled(false);
226     markermenu->addMenu(m_markerMenu);
227     connect(m_markerMenu, SIGNAL(triggered(QAction *)), this, SLOT(slotGoToMarker(QAction *)));
228     QList <QAction *> list = m_timelineContextClipMenu->actions();
229     for (int i = 0; i < list.count(); i++) {
230         if (list.at(i)->data().toString() == "paste_effects") m_pasteEffectsAction = list.at(i);
231         else if (list.at(i)->data().toString() == "ungroup_clip") m_ungroupAction = list.at(i);
232     }
233
234     m_timelineContextTransitionMenu = transition;
235     list = m_timelineContextTransitionMenu->actions();
236     for (int i = 0; i < list.count(); i++) {
237         if (list.at(i)->data().toString() == "auto") {
238             m_autoTransition = list.at(i);
239             break;
240         }
241     }
242
243     m_timelineContextMenu->addSeparator();
244     m_deleteGuide = new KAction(KIcon("edit-delete"), i18n("Delete Guide"), this);
245     connect(m_deleteGuide, SIGNAL(triggered()), this, SLOT(slotDeleteTimeLineGuide()));
246     m_timelineContextMenu->addAction(m_deleteGuide);
247
248     m_editGuide = new KAction(KIcon("document-properties"), i18n("Edit Guide"), this);
249     connect(m_editGuide, SIGNAL(triggered()), this, SLOT(slotEditTimeLineGuide()));
250     m_timelineContextMenu->addAction(m_editGuide);
251 }
252
253 void CustomTrackView::slotDoResetMenuPosition()
254 {
255     m_menuPosition = QPoint();
256 }
257
258 void CustomTrackView::slotResetMenuPosition()
259 {
260     // after a short time (so that the action is triggered / or menu is closed, we reset the menu pos
261     QTimer::singleShot(300, this, SLOT(slotDoResetMenuPosition()));
262 }
263
264 void CustomTrackView::checkAutoScroll()
265 {
266     m_autoScroll = KdenliveSettings::autoscroll();
267 }
268
269 /*sQList <TrackInfo> CustomTrackView::tracksList() const {
270     return m_scene->m_tracksList;
271 }*/
272
273
274 int CustomTrackView::getFrameWidth()
275 {
276     return (int) (m_tracksHeight * m_document->mltProfile().display_aspect_num / m_document->mltProfile().display_aspect_den + 0.5);
277 }
278
279 void CustomTrackView::updateSceneFrameWidth()
280 {
281     int frameWidth = getFrameWidth();
282     QList<QGraphicsItem *> itemList = items();
283     ClipItem *item;
284     for (int i = 0; i < itemList.count(); i++) {
285         if (itemList.at(i)->type() == AVWIDGET) {
286             item = (ClipItem*) itemList.at(i);
287             item->resetFrameWidth(frameWidth);
288             item->resetThumbs(true);
289         }
290     }
291 }
292
293 bool CustomTrackView::checkTrackHeight()
294 {
295     if (m_tracksHeight == KdenliveSettings::trackheight()) return false;
296     m_tracksHeight = KdenliveSettings::trackheight();
297     emit trackHeightChanged();
298     QList<QGraphicsItem *> itemList = items();
299     ClipItem *item;
300     Transition *transitionitem;
301     int frameWidth = getFrameWidth();
302     bool snap = KdenliveSettings::snaptopoints();
303     KdenliveSettings::setSnaptopoints(false);
304     for (int i = 0; i < itemList.count(); i++) {
305         if (itemList.at(i)->type() == AVWIDGET) {
306             item = (ClipItem*) itemList.at(i);
307             item->setRect(0, 0, item->rect().width(), m_tracksHeight - 1);
308             item->setPos((qreal) item->startPos().frames(m_document->fps()), (qreal) item->track() * m_tracksHeight + 1);
309             item->resetFrameWidth(frameWidth);
310             item->resetThumbs(true);
311         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
312             transitionitem = (Transition*) itemList.at(i);
313             transitionitem->setRect(0, 0, transitionitem->rect().width(), m_tracksHeight / 3 * 2 - 1);
314             transitionitem->setPos((qreal) transitionitem->startPos().frames(m_document->fps()), (qreal) transitionitem->track() * m_tracksHeight + m_tracksHeight / 3 * 2);
315         }
316     }
317     double newHeight = m_tracksHeight * m_document->tracksCount() * matrix().m22();
318     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), newHeight - 1);
319
320     for (int i = 0; i < m_guides.count(); i++) {
321         QLineF l = m_guides.at(i)->line();
322         l.setP2(QPointF(l.x2(), newHeight));
323         m_guides.at(i)->setLine(l);
324     }
325
326     setSceneRect(0, 0, sceneRect().width(), m_tracksHeight * m_document->tracksCount());
327 //     verticalScrollBar()->setMaximum(m_tracksHeight * m_document->tracksCount());
328     KdenliveSettings::setSnaptopoints(snap);
329     viewport()->update();
330     return true;
331 }
332
333 /** Zoom or move viewport on mousewheel
334  *
335  * If mousewheel+Ctrl, zooms in/out on the timeline.
336  *
337  * With Ctrl, moves viewport towards end of timeline if down/back,
338  * opposite on up/forward.
339  *
340  * See also http://www.kdenlive.org/mantis/view.php?id=265 */
341 void CustomTrackView::wheelEvent(QWheelEvent * e)
342 {
343     if (e->modifiers() == Qt::ControlModifier) {
344         if (e->delta() > 0) emit zoomIn();
345         else emit zoomOut();
346     } else {
347         if (e->delta() <= 0) horizontalScrollBar()->setValue(horizontalScrollBar()->value() + horizontalScrollBar()->singleStep());
348         else  horizontalScrollBar()->setValue(horizontalScrollBar()->value() - horizontalScrollBar()->singleStep());
349     }
350 }
351
352 int CustomTrackView::getPreviousVideoTrack(int track)
353 {
354     track = m_document->tracksCount() - track - 1;
355     track --;
356     for (int i = track; i > -1; i--) {
357         if (m_document->trackInfoAt(i).type == VIDEOTRACK) return i + 1;
358     }
359     return 0;
360 }
361
362
363 void CustomTrackView::slotFetchNextThumbs()
364 {
365     if (!m_waitingThumbs.isEmpty()) {
366         ClipItem *item = m_waitingThumbs.takeFirst();
367         while (item == NULL && !m_waitingThumbs.isEmpty()) {
368             item = m_waitingThumbs.takeFirst();
369         }
370         if (item) item->slotFetchThumbs();
371         if (!m_waitingThumbs.isEmpty()) m_thumbsTimer.start();
372     }
373 }
374
375 void CustomTrackView::slotCheckMouseScrolling()
376 {
377     if (m_scrollOffset == 0) {
378         m_scrollTimer.stop();
379         return;
380     }
381     horizontalScrollBar()->setValue(horizontalScrollBar()->value() + m_scrollOffset);
382     m_scrollTimer.start();
383 }
384
385 void CustomTrackView::slotCheckPositionScrolling()
386 {
387     // If mouse is at a border of the view, scroll
388     if (m_moveOpMode != SEEK) return;
389     if (mapFromScene(m_cursorPos, 0).x() < 3) {
390         horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 2);
391         QTimer::singleShot(200, this, SLOT(slotCheckPositionScrolling()));
392         setCursorPos(mapToScene(QPoint(-2, 0)).x());
393     } else if (viewport()->width() - 3 < mapFromScene(m_cursorPos + 1, 0).x()) {
394         horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 2);
395         setCursorPos(mapToScene(QPoint(viewport()->width(), 0)).x() + 1);
396         QTimer::singleShot(200, this, SLOT(slotCheckPositionScrolling()));
397     }
398 }
399
400
401 // virtual
402 void CustomTrackView::mouseMoveEvent(QMouseEvent * event)
403 {
404     int pos = event->x();
405     int mappedXPos = qMax((int)(mapToScene(event->pos()).x() + 0.5), 0);
406    
407     double snappedPos = getSnapPointForPos(mappedXPos);
408     emit mousePosition(mappedXPos);
409
410     if (event->buttons() & Qt::MidButton) return;
411     if (dragMode() == QGraphicsView::RubberBandDrag || (event->modifiers() == Qt::ControlModifier && m_tool != SPACERTOOL && m_operationMode != RESIZESTART && m_operationMode != RESIZEEND)) {
412         event->setAccepted(true);
413         m_moveOpMode = NONE;
414         QGraphicsView::mouseMoveEvent(event);
415         return;
416     }
417
418     if (event->buttons() != Qt::NoButton) {
419         bool move = (event->pos() - m_clickEvent).manhattanLength() >= QApplication::startDragDistance();
420         if (m_dragItem && m_tool == SELECTTOOL) {
421             if (m_operationMode == MOVE && move) {
422                 QGraphicsView::mouseMoveEvent(event);
423                 // If mouse is at a border of the view, scroll
424                 if (pos < 5) {
425                     m_scrollOffset = -30;
426                     m_scrollTimer.start();
427                 } else if (viewport()->width() - pos < 10) {
428                     m_scrollOffset = 30;
429                     m_scrollTimer.start();
430                 } else if (m_scrollTimer.isActive()) {
431                     m_scrollTimer.stop();
432                 }
433             } else if (m_operationMode == RESIZESTART && move) {
434                 m_document->renderer()->pause();
435                 if (!m_controlModifier && m_dragItem->type() == AVWIDGET && m_dragItem->parentItem() && m_dragItem->parentItem() != m_selectionGroup) {
436                     AbstractGroupItem *parent = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
437                     if (parent)
438                         parent->resizeStart((int)(snappedPos - m_dragItemInfo.startPos.frames(m_document->fps())));
439                 } else {
440                     m_dragItem->resizeStart((int)(snappedPos));
441                 }
442                 QString crop = m_document->timecode().getDisplayTimecode(m_dragItem->cropStart(), KdenliveSettings::frametimecode());
443                 QString duration = m_document->timecode().getDisplayTimecode(m_dragItem->cropDuration(), KdenliveSettings::frametimecode());
444                 QString offset = m_document->timecode().getDisplayTimecode(m_dragItem->cropStart() - m_dragItemInfo.cropStart, KdenliveSettings::frametimecode());
445                 emit displayMessage(i18n("Crop from start:") + ' ' + crop + ' ' + i18n("Duration:") + ' ' + duration + ' ' + i18n("Offset:") + ' ' + offset, InformationMessage);
446             } else if (m_operationMode == RESIZEEND && move) {
447                 m_document->renderer()->pause();
448                 if (!m_controlModifier && m_dragItem->type() == AVWIDGET && m_dragItem->parentItem() && m_dragItem->parentItem() != m_selectionGroup) {
449                     AbstractGroupItem *parent = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
450                     if (parent)
451                         parent->resizeEnd((int)(snappedPos - m_dragItemInfo.endPos.frames(m_document->fps())));
452                 } else {
453                     m_dragItem->resizeEnd((int)(snappedPos));
454                 }
455                 QString duration = m_document->timecode().getDisplayTimecode(m_dragItem->cropDuration(), KdenliveSettings::frametimecode());
456                 QString offset = m_document->timecode().getDisplayTimecode(m_dragItem->cropDuration() - m_dragItemInfo.cropDuration, KdenliveSettings::frametimecode());
457                 emit displayMessage(i18n("Duration:") + ' ' + duration + ' ' + i18n("Offset:") + ' ' + offset, InformationMessage);
458             } else if (m_operationMode == FADEIN && move) {
459                 ((ClipItem*) m_dragItem)->setFadeIn((int)(mappedXPos - m_dragItem->startPos().frames(m_document->fps())));
460             } else if (m_operationMode == FADEOUT && move) {
461                 ((ClipItem*) m_dragItem)->setFadeOut((int)(m_dragItem->endPos().frames(m_document->fps()) - mappedXPos));
462             } else if (m_operationMode == KEYFRAME && move) {
463                 GenTime keyFramePos = GenTime(mappedXPos, m_document->fps()) - m_dragItem->startPos() + m_dragItem->cropStart();
464                 double pos = mapToScene(event->pos()).toPoint().y();
465                 QRectF br = m_dragItem->sceneBoundingRect();
466                 double maxh = 100.0 / br.height();
467                 pos = (br.bottom() - pos) * maxh;
468                 m_dragItem->updateKeyFramePos(keyFramePos, pos);
469                 QString position = m_document->timecode().getDisplayTimecodeFromFrames(m_dragItem->editedKeyFramePos(), KdenliveSettings::frametimecode());
470                 emit displayMessage(position + " : " + QString::number(m_dragItem->editedKeyFrameValue()), InformationMessage);
471             }
472             removeTipAnimation();
473             return;
474         } else if (m_operationMode == MOVEGUIDE) {
475             removeTipAnimation();
476             QGraphicsView::mouseMoveEvent(event);
477             return;
478         } else if (m_operationMode == SPACER && move && m_selectionGroup) {
479             // spacer tool
480             snappedPos = getSnapPointForPos(mappedXPos + m_spacerOffset);
481             if (snappedPos < 0) snappedPos = 0;
482
483             // Make sure there is no collision
484             QList<QGraphicsItem *> children = m_selectionGroup->childItems();
485             QPainterPath shape = m_selectionGroup->clipGroupShape(QPointF(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0));
486             QList<QGraphicsItem*> collidingItems = scene()->items(shape, Qt::IntersectsItemShape);
487             collidingItems.removeAll(m_selectionGroup);
488             for (int i = 0; i < children.count(); i++) {
489                 if (children.at(i)->type() == GROUPWIDGET) {
490                     QList<QGraphicsItem *> subchildren = children.at(i)->childItems();
491                     for (int j = 0; j < subchildren.count(); j++)
492                         collidingItems.removeAll(subchildren.at(j));
493                 }
494                 collidingItems.removeAll(children.at(i));
495             }
496             bool collision = false;
497             int offset = 0;
498             for (int i = 0; i < collidingItems.count(); i++) {
499                 if (!collidingItems.at(i)->isEnabled()) continue;
500                 if (collidingItems.at(i)->type() == AVWIDGET && snappedPos < m_selectionGroup->sceneBoundingRect().left()) {
501                     AbstractClipItem *item = static_cast <AbstractClipItem *>(collidingItems.at(i));
502                     // Moving backward, determine best pos
503                     QPainterPath clipPath;
504                     clipPath.addRect(item->sceneBoundingRect());
505                     QPainterPath res = shape.intersected(clipPath);
506                     offset = qMax(offset, (int)(res.boundingRect().width() + 0.5));
507                 }
508             }
509             snappedPos += offset;
510             // make sure we have no collision
511             shape = m_selectionGroup->clipGroupShape(QPointF(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0));
512             collidingItems = scene()->items(shape, Qt::IntersectsItemShape);
513             collidingItems.removeAll(m_selectionGroup);
514             for (int i = 0; i < children.count(); i++) {
515                 if (children.at(i)->type() == GROUPWIDGET) {
516                     QList<QGraphicsItem *> subchildren = children.at(i)->childItems();
517                     for (int j = 0; j < subchildren.count(); j++)
518                         collidingItems.removeAll(subchildren.at(j));
519                 }
520                 collidingItems.removeAll(children.at(i));
521             }
522
523             for (int i = 0; i < collidingItems.count(); i++) {
524                 if (!collidingItems.at(i)->isEnabled()) continue;
525                 if (collidingItems.at(i)->type() == AVWIDGET) {
526                     collision = true;
527                     break;
528                 }
529             }
530
531
532             if (!collision) {
533                 // Check transitions
534                 shape = m_selectionGroup->transitionGroupShape(QPointF(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0));
535                 collidingItems = scene()->items(shape, Qt::IntersectsItemShape);
536                 collidingItems.removeAll(m_selectionGroup);
537                 for (int i = 0; i < children.count(); i++) {
538                     if (children.at(i)->type() == GROUPWIDGET) {
539                         QList<QGraphicsItem *> subchildren = children.at(i)->childItems();
540                         for (int j = 0; j < subchildren.count(); j++)
541                             collidingItems.removeAll(subchildren.at(j));
542                     }
543                     collidingItems.removeAll(children.at(i));
544                 }
545                 offset = 0;
546
547                 for (int i = 0; i < collidingItems.count(); i++) {
548                     if (collidingItems.at(i)->type() == TRANSITIONWIDGET && snappedPos < m_selectionGroup->sceneBoundingRect().left()) {
549                         AbstractClipItem *item = static_cast <AbstractClipItem *>(collidingItems.at(i));
550                         // Moving backward, determine best pos
551                         QPainterPath clipPath;
552                         clipPath.addRect(item->sceneBoundingRect());
553                         QPainterPath res = shape.intersected(clipPath);
554                         offset = qMax(offset, (int)(res.boundingRect().width() + 0.5));
555                     }
556                 }
557
558                 snappedPos += offset;
559                 // make sure we have no collision
560                 shape = m_selectionGroup->transitionGroupShape(QPointF(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0));
561                 collidingItems = scene()->items(shape, Qt::IntersectsItemShape);
562                 collidingItems.removeAll(m_selectionGroup);
563                 for (int i = 0; i < children.count(); i++) {
564                     if (children.at(i)->type() == GROUPWIDGET) {
565                         QList<QGraphicsItem *> subchildren = children.at(i)->childItems();
566                         for (int j = 0; j < subchildren.count(); j++)
567                             collidingItems.removeAll(subchildren.at(j));
568                     }
569                     collidingItems.removeAll(children.at(i));
570                 }
571                 for (int i = 0; i < collidingItems.count(); i++) {
572                     if (collidingItems.at(i)->type() == TRANSITIONWIDGET) {
573                         collision = true;
574                         break;
575                     }
576                 }
577             }
578
579             if (!collision)
580                 m_selectionGroup->translate(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0);
581             //m_selectionGroup->setPos(mappedXPos + (((int) m_selectionGroup->boundingRect().topLeft().x() + 0.5) - mappedClick) , m_selectionGroup->pos().y());
582         }
583     }
584
585     if (m_tool == RAZORTOOL) {
586         setCursor(m_razorCursor);
587         //QGraphicsView::mouseMoveEvent(event);
588         //return;
589     } else if (m_tool == SPACERTOOL) {
590         setCursor(m_spacerCursor);
591         return;
592     }
593
594     QList<QGraphicsItem *> itemList = items(event->pos());
595     QGraphicsRectItem *item = NULL;
596     OPERATIONTYPE opMode = NONE;
597
598     if (itemList.count() == 1 && itemList.at(0)->type() == GUIDEITEM) {
599         opMode = MOVEGUIDE;
600     } else for (int i = 0; i < itemList.count(); i++) {
601             if (itemList.at(i)->type() == AVWIDGET || itemList.at(i)->type() == TRANSITIONWIDGET) {
602                 item = (QGraphicsRectItem*) itemList.at(i);
603                 break;
604             }
605         }
606
607     if (item && event->buttons() == Qt::NoButton) {
608         AbstractClipItem *clip = static_cast <AbstractClipItem*>(item);
609         if (m_tool == RAZORTOOL) {
610             // razor tool over a clip, display current frame in monitor
611             if (false && !m_blockRefresh && item->type() == AVWIDGET) {
612                 //TODO: solve crash when showing frame when moving razor over clip
613                 emit showClipFrame(((ClipItem *) item)->baseClip(), QPoint(), false, mappedXPos - (clip->startPos() - clip->cropStart()).frames(m_document->fps()));
614             }
615             event->accept();
616             return;
617         }
618
619         if (m_selectionGroup && clip->parentItem() == m_selectionGroup) {
620             // all other modes break the selection, so the user probably wants to move it
621             opMode = MOVE;
622         } else {
623             opMode = clip->operationMode(mapToScene(event->pos()));
624         }
625
626         const double size = 5;
627         if (opMode == m_moveOpMode) {
628             QGraphicsView::mouseMoveEvent(event);
629             return;
630         } else {
631             removeTipAnimation();
632         }
633         m_moveOpMode = opMode;
634         setTipAnimation(clip, opMode, size);
635         ClipItem *ci = NULL;
636         if (item->type() == AVWIDGET)
637             ci = static_cast <ClipItem *>(item);
638         QString message;
639         if (opMode == MOVE) {
640             setCursor(Qt::OpenHandCursor);
641             if (ci) {
642                 message = ci->clipName() + i18n(":");
643                 message.append(i18n(" Position:") + m_document->timecode().getDisplayTimecode(ci->info().startPos, KdenliveSettings::frametimecode()));
644                 message.append(i18n(" Duration:") + m_document->timecode().getDisplayTimecode(ci->cropDuration(),  KdenliveSettings::frametimecode()));
645                 if (clip->parentItem() && clip->parentItem()->type() == GROUPWIDGET) {
646                     AbstractGroupItem *parent = static_cast <AbstractGroupItem *>(clip->parentItem());
647                     if (clip->parentItem() == m_selectionGroup)
648                         message.append(i18n(" Selection duration:"));
649                     else
650                         message.append(i18n(" Group duration:"));
651                     message.append(m_document->timecode().getDisplayTimecode(parent->duration(), KdenliveSettings::frametimecode()));
652                     if (parent->parentItem() && parent->parentItem()->type() == GROUPWIDGET) {
653                         AbstractGroupItem *parent2 = static_cast <AbstractGroupItem *>(parent->parentItem());
654                         message.append(i18n(" Selection duration:") + m_document->timecode().getDisplayTimecode(parent2->duration(), KdenliveSettings::frametimecode()));
655                     }
656                 }
657             }
658         } else if (opMode == RESIZESTART) {
659             setCursor(KCursor("left_side", Qt::SizeHorCursor));
660             if (ci)
661                 message = i18n("Crop from start: ") + m_document->timecode().getDisplayTimecode(ci->cropStart(), KdenliveSettings::frametimecode());
662             if (item->type() == AVWIDGET && item->parentItem() && item->parentItem() != m_selectionGroup)
663                 message.append(i18n("Use Ctrl to resize only current item, otherwise all items in this group will be resized at once."));
664         } else if (opMode == RESIZEEND) {
665             setCursor(KCursor("right_side", Qt::SizeHorCursor));
666             if (ci)
667                 message = i18n("Duration: ") + m_document->timecode().getDisplayTimecode(ci->cropDuration(), KdenliveSettings::frametimecode());
668             if (item->type() == AVWIDGET && item->parentItem() && item->parentItem() != m_selectionGroup)
669                 message.append(i18n("Use Ctrl to resize only current item, otherwise all items in this group will be resized at once."));
670         } else if (opMode == FADEIN || opMode == FADEOUT) {
671             setCursor(Qt::PointingHandCursor);
672             if (ci && opMode == FADEIN && ci->fadeIn()) {
673                 message = i18n("Fade in duration: ");
674                 message.append(m_document->timecode().getDisplayTimecodeFromFrames(ci->fadeIn(), KdenliveSettings::frametimecode()));
675             } else if (ci && opMode == FADEOUT && ci->fadeOut()) {
676                 message = i18n("Fade out duration: ");
677                 message.append(m_document->timecode().getDisplayTimecodeFromFrames(ci->fadeOut(), KdenliveSettings::frametimecode()));
678             } else {
679                 message = i18n("Drag to add or resize a fade effect.");
680             }
681         } else if (opMode == TRANSITIONSTART || opMode == TRANSITIONEND) {
682             setCursor(Qt::PointingHandCursor);
683             message = i18n("Click to add a transition.");
684         } else if (opMode == KEYFRAME) {
685             setCursor(Qt::PointingHandCursor);
686             emit displayMessage(i18n("Move keyframe above or below clip to remove it, double click to add a new one."), InformationMessage);
687         }
688
689         if (!message.isEmpty())
690             emit displayMessage(message, InformationMessage);
691     } // no clip under mouse
692     else if (m_tool == RAZORTOOL) {
693         event->accept();
694         return;
695     } else if (opMode == MOVEGUIDE) {
696         m_moveOpMode = opMode;
697         setCursor(Qt::SplitHCursor);
698     } else {
699         removeTipAnimation();
700         setCursor(Qt::ArrowCursor);
701         if (event->buttons() != Qt::NoButton && event->modifiers() == Qt::NoModifier) {
702             QGraphicsView::mouseMoveEvent(event);
703             m_moveOpMode = SEEK;
704             setCursorPos(mappedXPos);
705             slotCheckPositionScrolling();
706             return;
707         } else m_moveOpMode = NONE;
708     }
709     QGraphicsView::mouseMoveEvent(event);
710 }
711
712 // virtual
713 void CustomTrackView::mousePressEvent(QMouseEvent * event)
714 {
715     setFocus(Qt::MouseFocusReason);
716     m_menuPosition = QPoint();
717
718     // special cases (middle click button or ctrl / shift click
719     if (event->button() == Qt::MidButton) {
720         emit playMonitor();
721         m_blockRefresh = false;
722         m_operationMode = NONE;
723         return;
724     }
725
726     if (event->modifiers() & Qt::ShiftModifier) {
727         // Rectangle selection
728         setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
729         setDragMode(QGraphicsView::RubberBandDrag);
730         if (!(event->modifiers() & Qt::ControlModifier)) {
731             resetSelectionGroup();
732             scene()->clearSelection();
733         }
734         m_blockRefresh = false;
735         m_operationMode = RUBBERSELECTION;
736         QGraphicsView::mousePressEvent(event);
737         return;
738     }
739
740     m_blockRefresh = true;
741     m_dragGuide = NULL;
742
743     if (m_tool != RAZORTOOL) activateMonitor();
744     else if (m_document->renderer()->playSpeed() != 0.0) {
745         m_document->renderer()->pause();
746         return;
747     }
748     m_clickEvent = event->pos();
749
750     // check item under mouse
751     QList<QGraphicsItem *> collisionList = items(m_clickEvent);
752     if (event->modifiers() == Qt::ControlModifier && m_tool != SPACERTOOL && collisionList.count() == 0) {
753         // Pressing Ctrl + left mouse button in an empty area scrolls the timeline
754         setDragMode(QGraphicsView::ScrollHandDrag);
755         QGraphicsView::mousePressEvent(event);
756         m_blockRefresh = false;
757         m_operationMode = NONE;
758         return;
759     }
760
761     // if a guide and a clip were pressed, just select the guide
762     for (int i = 0; i < collisionList.count(); ++i) {
763         if (collisionList.at(i)->type() == GUIDEITEM) {
764             // a guide item was pressed
765             m_dragGuide = (Guide *) collisionList.at(i);
766             if (event->button() == Qt::LeftButton) { // move it
767                 m_dragGuide->setFlag(QGraphicsItem::ItemIsMovable, true);
768                 m_operationMode = MOVEGUIDE;
769                 // deselect all clips so that only the guide will move
770                 m_scene->clearSelection();
771                 resetSelectionGroup(false);
772                 updateSnapPoints(NULL);
773                 QGraphicsView::mousePressEvent(event);
774                 return;
775             } else // show context menu
776                 break;
777         }
778     }
779
780     // Find first clip, transition or group under mouse (when no guides selected)
781     int ct = 0;
782     AbstractGroupItem *dragGroup = NULL;
783     AbstractClipItem *collisionClip = NULL;
784     bool found = false;
785     while (!m_dragGuide && ct < collisionList.count()) {
786         if (collisionList.at(ct)->type() == AVWIDGET || collisionList.at(ct)->type() == TRANSITIONWIDGET) {
787             collisionClip = static_cast <AbstractClipItem *>(collisionList.at(ct));
788             if (collisionClip->isItemLocked())
789                 break;
790             if (collisionClip == m_dragItem)
791                 collisionClip = NULL;
792             else
793                 m_dragItem = collisionClip;
794             found = true;
795             m_dragItemInfo = m_dragItem->info();
796             if (m_dragItem->parentItem() && m_dragItem->parentItem()->type() == GROUPWIDGET && m_dragItem->parentItem() != m_selectionGroup) {
797                 // kDebug()<<"// KLIK FOUND GRP: "<<m_dragItem->sceneBoundingRect();
798                 dragGroup = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
799             }
800             break;
801         }
802         ct++;
803     }
804     if (!found) {
805         if (m_dragItem) emit clipItemSelected(NULL);
806         m_dragItem = NULL;
807     }
808 #if QT_VERSION >= 0x040600
809     // Add shadow to dragged item, currently disabled because of painting artifacts
810     //TODO: re-enable when fixed
811     /*QGraphicsDropShadowEffect *eff = new QGraphicsDropShadowEffect();
812     eff->setBlurRadius(5);
813     eff->setOffset(3, 3);
814     m_dragItem->setGraphicsEffect(eff);*/
815 #endif
816     if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET) {
817         // update transition menu action
818         m_autoTransition->setChecked(static_cast<Transition *>(m_dragItem)->isAutomatic());
819         m_autoTransition->setEnabled(true);
820     } else m_autoTransition->setEnabled(false);
821
822     // context menu requested
823     if (event->button() == Qt::RightButton) {
824         if (m_dragItem) {
825             if (dragGroup) dragGroup->setSelected(true);
826             else if (!m_dragItem->isSelected()) {
827                 resetSelectionGroup(false);
828                 m_scene->clearSelection();
829                 m_dragItem->setSelected(true);
830             }
831         } else if (!m_dragGuide) {
832             // check if there is a guide close to mouse click
833             QList<QGraphicsItem *> guidesCollisionList = items(event->pos().x() - 5, event->pos().y(), 10, 2); // a rect of height < 2 does not always collide with the guide
834             for (int i = 0; i < guidesCollisionList.count(); i++) {
835                 if (guidesCollisionList.at(i)->type() == GUIDEITEM) {
836                     m_dragGuide = static_cast <Guide *>(guidesCollisionList.at(i));
837                     break;
838                 }
839             }
840             // keep this to support multiple guides context menu in the future (?)
841             /*if (guidesCollisionList.at(0)->type() != GUIDEITEM)
842                 guidesCollisionList.removeAt(0);
843             }
844             if (!guidesCollisionList.isEmpty())
845             m_dragGuide = static_cast <Guide *>(guidesCollisionList.at(0));*/
846         }
847
848         m_operationMode = NONE;
849         displayContextMenu(event->globalPos(), m_dragItem, dragGroup);
850         m_menuPosition = m_clickEvent;
851         m_dragItem = NULL;
852         event->accept();
853         return;
854     }
855
856     // No item under click
857     if (m_dragItem == NULL || m_tool == SPACERTOOL) {
858         resetSelectionGroup(false);
859         setCursor(Qt::ArrowCursor);
860         m_scene->clearSelection();
861         //event->accept();
862         updateClipTypeActions(NULL);
863         if (m_tool == SPACERTOOL) {
864             QList<QGraphicsItem *> selection;
865             if (event->modifiers() == Qt::ControlModifier) {
866                 // Ctrl + click, select all items on track after click position
867                 int track = (int)(mapToScene(m_clickEvent).y() / m_tracksHeight);
868                 if (m_document->trackInfoAt(m_document->tracksCount() - track - 1).isLocked) {
869                     // Cannot use spacer on locked track
870                     emit displayMessage(i18n("Cannot use spacer in a locked track"), ErrorMessage);
871                     return;
872                 }
873
874                 QRectF rect(mapToScene(m_clickEvent).x(), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - mapToScene(m_clickEvent).x(), m_tracksHeight / 2 - 2);
875
876                 bool isOk;
877                 selection = checkForGroups(rect, &isOk);
878                 if (!isOk) {
879                     // groups found on track, do not allow the move
880                     emit displayMessage(i18n("Cannot use spacer in a track with a group"), ErrorMessage);
881                     return;
882                 }
883
884                 kDebug() << "SPACER TOOL + CTRL, SELECTING ALL CLIPS ON TRACK " << track << " WITH SELECTION RECT " << m_clickEvent.x() << "/" <<  track * m_tracksHeight + 1 << "; " << mapFromScene(sceneRect().width(), 0).x() - m_clickEvent.x() << "/" << m_tracksHeight - 2;
885             } else {
886                 // Select all items on all tracks after click position
887                 selection = items(event->pos().x(), 1, mapFromScene(sceneRect().width(), 0).x() - event->pos().x(), sceneRect().height());
888                 kDebug() << "SELELCTING ELEMENTS WITHIN =" << event->pos().x() << "/" <<  1 << ", " << mapFromScene(sceneRect().width(), 0).x() - event->pos().x() << "/" << sceneRect().height();
889             }
890
891             QList <GenTime> offsetList;
892             // create group to hold selected items
893             m_selectionGroup = new AbstractGroupItem(m_document->fps());
894             scene()->addItem(m_selectionGroup);
895
896             for (int i = 0; i < selection.count(); i++) {
897                 if (selection.at(i)->parentItem() == 0 && (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET)) {
898                     AbstractClipItem *item = static_cast<AbstractClipItem *>(selection.at(i));
899                     if (item->isItemLocked()) continue;
900                     offsetList.append(item->startPos());
901                     offsetList.append(item->endPos());
902                     m_selectionGroup->addToGroup(selection.at(i));
903                     selection.at(i)->setFlag(QGraphicsItem::ItemIsMovable, false);
904                 } else if (/*selection.at(i)->parentItem() == 0 && */selection.at(i)->type() == GROUPWIDGET) {
905                     if (static_cast<AbstractGroupItem *>(selection.at(i))->isItemLocked()) continue;
906                     QList<QGraphicsItem *> children = selection.at(i)->childItems();
907                     for (int j = 0; j < children.count(); j++) {
908                         AbstractClipItem *item = static_cast<AbstractClipItem *>(children.at(j));
909                         offsetList.append(item->startPos());
910                         offsetList.append(item->endPos());
911                     }
912                     m_selectionGroup->addToGroup(selection.at(i));
913                     selection.at(i)->setFlag(QGraphicsItem::ItemIsMovable, false);
914                 } else if (selection.at(i)->parentItem() && !selection.contains(selection.at(i)->parentItem())) {
915                     if (static_cast<AbstractGroupItem *>(selection.at(i)->parentItem())->isItemLocked()) continue;
916                     //AbstractGroupItem *grp = static_cast<AbstractGroupItem *>(selection.at(i)->parentItem());
917                     m_selectionGroup->addToGroup(selection.at(i)->parentItem());
918                     selection.at(i)->parentItem()->setFlag(QGraphicsItem::ItemIsMovable, false);
919                 }
920             }
921             m_spacerOffset = m_selectionGroup->sceneBoundingRect().left() - (int)(mapToScene(m_clickEvent).x());
922             if (!offsetList.isEmpty()) {
923                 qSort(offsetList);
924                 QList <GenTime> cleandOffsetList;
925                 GenTime startOffset = offsetList.takeFirst();
926                 for (int k = 0; k < offsetList.size(); k++) {
927                     GenTime newoffset = offsetList.at(k) - startOffset;
928                     if (newoffset != GenTime() && !cleandOffsetList.contains(newoffset)) {
929                         cleandOffsetList.append(newoffset);
930                     }
931                 }
932                 updateSnapPoints(NULL, cleandOffsetList, true);
933             }
934             m_operationMode = SPACER;
935         } else {
936             setCursorPos((int)(mapToScene(event->x(), 0).x()));
937         }
938         //QGraphicsView::mousePressEvent(event);
939         event->ignore();
940         return;
941     }
942
943     // Razor tool
944     if (m_tool == RAZORTOOL && m_dragItem) {
945         GenTime cutPos = GenTime((int)(mapToScene(event->pos()).x()), m_document->fps());
946         if (m_dragItem->type() == TRANSITIONWIDGET) {
947             emit displayMessage(i18n("Cannot cut a transition"), ErrorMessage);
948         } else {
949             m_document->renderer()->pause();
950             if (m_dragItem->parentItem() && m_dragItem->parentItem() != m_selectionGroup) {
951                 razorGroup((AbstractGroupItem *)m_dragItem->parentItem(), cutPos);
952             } else {
953                 AbstractClipItem *clip = static_cast <AbstractClipItem *>(m_dragItem);
954                 RazorClipCommand* command = new RazorClipCommand(this, clip->info(), cutPos);
955                 m_commandStack->push(command);
956             }
957             setDocumentModified();
958         }
959         m_dragItem = NULL;
960         event->accept();
961         return;
962     }
963
964     bool itemSelected = false;
965     if (m_dragItem->isSelected())
966         itemSelected = true;
967     else if (m_dragItem->parentItem() && m_dragItem->parentItem()->isSelected())
968         itemSelected = true;
969     else if (dragGroup && dragGroup->isSelected())
970         itemSelected = true;
971
972     if (event->modifiers() == Qt::ControlModifier || itemSelected == false) {
973         if (event->modifiers() != Qt::ControlModifier) {
974             resetSelectionGroup(false);
975             m_scene->clearSelection();
976             // A refresh seems necessary otherwise in zoomed mode, some clips disappear
977             viewport()->update();
978         } else resetSelectionGroup();
979         dragGroup = NULL;
980         if (m_dragItem->parentItem() && m_dragItem->parentItem()->type() == GROUPWIDGET) {
981             dragGroup = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
982         }
983         bool selected = !m_dragItem->isSelected();
984         if (dragGroup)
985             dragGroup->setSelected(selected);
986         else
987             m_dragItem->setSelected(selected);
988
989         groupSelectedItems();
990         ClipItem *clip = static_cast <ClipItem *>(m_dragItem);
991         updateClipTypeActions(dragGroup == NULL ? clip : NULL);
992         m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
993     }
994
995     if (collisionClip != NULL || m_dragItem == NULL) {
996         if (m_dragItem && m_dragItem->type() == AVWIDGET && !m_dragItem->isItemLocked()) {
997             ClipItem *selected = static_cast <ClipItem*>(m_dragItem);
998             emit clipItemSelected(selected);
999         } else {
1000             emit clipItemSelected(NULL);
1001         }
1002     }
1003
1004     // If clicked item is selected, allow move
1005     if (event->modifiers() != Qt::ControlModifier && m_operationMode == NONE) QGraphicsView::mousePressEvent(event);
1006
1007     m_clickPoint = QPoint((int)(mapToScene(event->pos()).x() - m_dragItem->startPos().frames(m_document->fps())), (int)(event->pos().y() - m_dragItem->pos().y()));
1008     if (m_selectionGroup && m_dragItem->parentItem() == m_selectionGroup) {
1009         // all other modes break the selection, so the user probably wants to move it
1010         m_operationMode = MOVE;
1011     } else {
1012         m_operationMode = m_dragItem->operationMode(mapToScene(event->pos()));
1013     }
1014     m_controlModifier = (event->modifiers() == Qt::ControlModifier);
1015
1016     // Update snap points
1017     if (m_selectionGroup == NULL) {
1018         if (m_operationMode == RESIZEEND || m_operationMode == RESIZESTART)
1019             updateSnapPoints(NULL);
1020         else
1021             updateSnapPoints(m_dragItem);
1022     } else {
1023         QList <GenTime> offsetList;
1024         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
1025         for (int i = 0; i < children.count(); i++) {
1026             if (children.at(i)->type() == AVWIDGET || children.at(i)->type() == TRANSITIONWIDGET) {
1027                 AbstractClipItem *item = static_cast <AbstractClipItem *>(children.at(i));
1028                 offsetList.append(item->startPos());
1029                 offsetList.append(item->endPos());
1030             }
1031         }
1032         if (!offsetList.isEmpty()) {
1033             qSort(offsetList);
1034             GenTime startOffset = offsetList.takeFirst();
1035             QList <GenTime> cleandOffsetList;
1036             for (int k = 0; k < offsetList.size(); k++) {
1037                 GenTime newoffset = offsetList.at(k) - startOffset;
1038                 if (newoffset != GenTime() && !cleandOffsetList.contains(newoffset)) {
1039                     cleandOffsetList.append(newoffset);
1040                 }
1041             }
1042             updateSnapPoints(NULL, cleandOffsetList, true);
1043         }
1044     }
1045
1046     if (m_operationMode == KEYFRAME) {
1047         m_dragItem->updateSelectedKeyFrame();
1048         m_blockRefresh = false;
1049         return;
1050     } else if (m_operationMode == MOVE) {
1051         setCursor(Qt::ClosedHandCursor);
1052     } else if (m_operationMode == TRANSITIONSTART && event->modifiers() != Qt::ControlModifier) {
1053         ItemInfo info;
1054         info.startPos = m_dragItem->startPos();
1055         info.track = m_dragItem->track();
1056         int transitiontrack = getPreviousVideoTrack(info.track);
1057         ClipItem *transitionClip = NULL;
1058         if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.startPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
1059         if (transitionClip && transitionClip->endPos() < m_dragItem->endPos()) {
1060             info.endPos = transitionClip->endPos();
1061         } else {
1062             GenTime transitionDuration(65, m_document->fps());
1063             if (m_dragItem->cropDuration() < transitionDuration)
1064                 info.endPos = m_dragItem->endPos();
1065             else
1066                 info.endPos = info.startPos + transitionDuration;
1067         }
1068         if (info.endPos == info.startPos) info.endPos = info.startPos + GenTime(65, m_document->fps());
1069         // Check there is no other transition at that place
1070         double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
1071         QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
1072         QList<QGraphicsItem *> selection = m_scene->items(r);
1073         bool transitionAccepted = true;
1074         for (int i = 0; i < selection.count(); i++) {
1075             if (selection.at(i)->type() == TRANSITIONWIDGET) {
1076                 Transition *tr = static_cast <Transition *>(selection.at(i));
1077                 if (tr->startPos() - info.startPos > GenTime(5, m_document->fps())) {
1078                     if (tr->startPos() < info.endPos) info.endPos = tr->startPos();
1079                 } else transitionAccepted = false;
1080             }
1081         }
1082         if (transitionAccepted) slotAddTransition((ClipItem *) m_dragItem, info, transitiontrack);
1083         else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1084     } else if (m_operationMode == TRANSITIONEND && event->modifiers() != Qt::ControlModifier) {
1085         ItemInfo info;
1086         info.endPos = GenTime(m_dragItem->endPos().frames(m_document->fps()), m_document->fps());
1087         info.track = m_dragItem->track();
1088         int transitiontrack = getPreviousVideoTrack(info.track);
1089         ClipItem *transitionClip = NULL;
1090         if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.endPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
1091         if (transitionClip && transitionClip->startPos() > m_dragItem->startPos()) {
1092             info.startPos = transitionClip->startPos();
1093         } else {
1094             GenTime transitionDuration(65, m_document->fps());
1095             if (m_dragItem->cropDuration() < transitionDuration) info.startPos = m_dragItem->startPos();
1096             else info.startPos = info.endPos - transitionDuration;
1097         }
1098         if (info.endPos == info.startPos) info.startPos = info.endPos - GenTime(65, m_document->fps());
1099         QDomElement transition = MainWindow::transitions.getEffectByTag("luma", "dissolve").cloneNode().toElement();
1100         EffectsList::setParameter(transition, "reverse", "1");
1101
1102         // Check there is no other transition at that place
1103         double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
1104         QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
1105         QList<QGraphicsItem *> selection = m_scene->items(r);
1106         bool transitionAccepted = true;
1107         for (int i = 0; i < selection.count(); i++) {
1108             if (selection.at(i)->type() == TRANSITIONWIDGET) {
1109                 Transition *tr = static_cast <Transition *>(selection.at(i));
1110                 if (info.endPos - tr->endPos() > GenTime(5, m_document->fps())) {
1111                     if (tr->endPos() > info.startPos) info.startPos = tr->endPos();
1112                 } else transitionAccepted = false;
1113             }
1114         }
1115         if (transitionAccepted) slotAddTransition((ClipItem *) m_dragItem, info, transitiontrack, transition);
1116         else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1117
1118     } else if ((m_operationMode == RESIZESTART || m_operationMode == RESIZEEND) && m_selectionGroup) {
1119         resetSelectionGroup(false);
1120         m_dragItem->setSelected(true);
1121     }
1122
1123     m_blockRefresh = false;
1124     //kDebug()<<pos;
1125     QGraphicsView::mousePressEvent(event);
1126 }
1127
1128 void CustomTrackView::rebuildGroup(int childTrack, GenTime childPos)
1129 {
1130     const QPointF p((int)childPos.frames(m_document->fps()), childTrack * m_tracksHeight + m_tracksHeight / 2);
1131     QList<QGraphicsItem *> list = scene()->items(p);
1132     AbstractGroupItem *group = NULL;
1133     for (int i = 0; i < list.size(); i++) {
1134         if (!list.at(i)->isEnabled()) continue;
1135         if (list.at(i)->type() == GROUPWIDGET) {
1136             group = static_cast <AbstractGroupItem *>(list.at(i));
1137             break;
1138         }
1139     }
1140     rebuildGroup(group);
1141 }
1142
1143 void CustomTrackView::rebuildGroup(AbstractGroupItem *group)
1144 {
1145     if (group) {
1146         resetSelectionGroup(false);
1147         m_scene->clearSelection();
1148
1149         QList <QGraphicsItem *> children = group->childItems();
1150         m_document->clipManager()->removeGroup(group);
1151         scene()->destroyItemGroup(group);
1152         for (int i = 0; i < children.count(); i++)
1153             children.at(i)->setSelected(true);
1154         groupSelectedItems(false, true);
1155     }
1156 }
1157
1158 void CustomTrackView::resetSelectionGroup(bool selectItems)
1159 {
1160     if (m_selectionGroup) {
1161         // delete selection group
1162         bool snap = KdenliveSettings::snaptopoints();
1163         KdenliveSettings::setSnaptopoints(false);
1164
1165         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
1166         scene()->destroyItemGroup(m_selectionGroup);
1167         for (int i = 0; i < children.count(); i++) {
1168             if (children.at(i)->parentItem() == 0 && (children.at(i)->type() == AVWIDGET || children.at(i)->type() == TRANSITIONWIDGET)) {
1169                 if (!static_cast <AbstractClipItem *>(children.at(i))->isItemLocked()) {
1170                     children.at(i)->setFlag(QGraphicsItem::ItemIsMovable, true);
1171                     children.at(i)->setSelected(selectItems);
1172                 }
1173             } else if (children.at(i)->type() == GROUPWIDGET) {
1174                 children.at(i)->setFlag(QGraphicsItem::ItemIsMovable, true);
1175                 children.at(i)->setSelected(selectItems);
1176             }
1177         }
1178
1179         m_selectionGroup = NULL;
1180         KdenliveSettings::setSnaptopoints(snap);
1181     }
1182 }
1183
1184 void CustomTrackView::groupSelectedItems(bool force, bool createNewGroup)
1185 {
1186     if (m_selectionGroup) {
1187         kDebug() << "///// ERROR, TRYING TO OVERRIDE EXISTING GROUP";
1188         return;
1189     }
1190     QList<QGraphicsItem *> selection = m_scene->selectedItems();
1191     if (selection.isEmpty()) return;
1192     QRectF rectUnion;
1193     // Find top left position of selection
1194     for (int i = 0; i < selection.count(); i++) {
1195         if (selection.at(i)->parentItem() == 0 && (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET || selection.at(i)->type() == GROUPWIDGET)) {
1196             rectUnion = rectUnion.united(selection.at(i)->sceneBoundingRect());
1197         } else if (selection.at(i)->parentItem()) {
1198             rectUnion = rectUnion.united(selection.at(i)->parentItem()->sceneBoundingRect());
1199         }
1200     }
1201
1202     if (force || selection.count() > 1) {
1203         bool snap = KdenliveSettings::snaptopoints();
1204         KdenliveSettings::setSnaptopoints(false);
1205         if (createNewGroup) {
1206             AbstractGroupItem *newGroup = m_document->clipManager()->createGroup();
1207             newGroup->setPos(rectUnion.left(), rectUnion.top() - 1);
1208             QPointF diff = newGroup->pos();
1209             newGroup->translate(-diff.x(), -diff.y());
1210             //newGroup->translate((int) -rectUnion.left(), (int) -rectUnion.top() + 1);
1211
1212             scene()->addItem(newGroup);
1213
1214             // Check if we are trying to include a group in a group
1215             QList <AbstractGroupItem *> groups;
1216             for (int i = 0; i < selection.count(); i++) {
1217                 if (selection.at(i)->type() == GROUPWIDGET && !groups.contains(static_cast<AbstractGroupItem *>(selection.at(i))))
1218                     groups.append(static_cast<AbstractGroupItem *>(selection.at(i)));
1219                 else if (selection.at(i)->parentItem() && !groups.contains(static_cast<AbstractGroupItem *>(selection.at(i)->parentItem())))
1220                     groups.append(static_cast<AbstractGroupItem *>(selection.at(i)->parentItem()));
1221             }
1222             if (!groups.isEmpty()) {
1223                 // ungroup previous groups
1224                 while (!groups.isEmpty()) {
1225                     AbstractGroupItem *grp = groups.takeFirst();
1226                     m_document->clipManager()->removeGroup(grp);
1227                     scene()->destroyItemGroup(grp);
1228                 }
1229                 selection = m_scene->selectedItems();
1230             }
1231
1232             for (int i = 0; i < selection.count(); i++) {
1233                 if (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET) {
1234                     newGroup->addToGroup(selection.at(i));
1235                     selection.at(i)->setFlag(QGraphicsItem::ItemIsMovable, false);
1236                 }
1237             }
1238             KdenliveSettings::setSnaptopoints(snap);
1239         } else {
1240             m_selectionGroup = new AbstractGroupItem(m_document->fps());
1241             m_selectionGroup->setPos(rectUnion.left(), rectUnion.top() - 1);
1242             QPointF diff = m_selectionGroup->pos();
1243             //m_selectionGroup->translate((int) - rectUnion.left(), (int) -rectUnion.top() + 1);
1244             m_selectionGroup->translate(- diff.x(), -diff.y());
1245
1246             scene()->addItem(m_selectionGroup);
1247             for (int i = 0; i < selection.count(); i++) {
1248                 if (selection.at(i)->parentItem() == 0 && (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET || selection.at(i)->type() == GROUPWIDGET)) {
1249                     m_selectionGroup->addToGroup(selection.at(i));
1250                     selection.at(i)->setFlag(QGraphicsItem::ItemIsMovable, false);
1251                 }
1252             }
1253             KdenliveSettings::setSnaptopoints(snap);
1254             if (m_selectionGroup) {
1255                 m_selectionGroupInfo.startPos = GenTime(m_selectionGroup->scenePos().x(), m_document->fps());
1256                 m_selectionGroupInfo.track = m_selectionGroup->track();
1257             }
1258         }
1259     } else resetSelectionGroup();
1260 }
1261
1262 void CustomTrackView::mouseDoubleClickEvent(QMouseEvent *event)
1263 {
1264     if (m_dragItem && m_dragItem->hasKeyFrames()) {
1265         /*if (m_moveOpMode == KEYFRAME) {
1266             // user double clicked on a keyframe, open edit dialog
1267             //TODO: update for effects with several values per keyframe
1268             QDialog d(parentWidget());
1269             Ui::KeyFrameDialog_UI view;
1270             view.setupUi(&d);
1271             view.kfr_position->setText(m_document->timecode().getTimecode(GenTime(m_dragItem->selectedKeyFramePos(), m_document->fps()) - m_dragItem->cropStart()));
1272             view.kfr_value->setValue(m_dragItem->selectedKeyFrameValue());
1273             view.kfr_value->setFocus();
1274             if (d.exec() == QDialog::Accepted) {
1275                 int pos = m_document->timecode().getFrameCount(view.kfr_position->text());
1276                 m_dragItem->updateKeyFramePos(GenTime(pos, m_document->fps()) + m_dragItem->cropStart(), (double) view.kfr_value->value() * m_dragItem->keyFrameFactor());
1277                 ClipItem *item = static_cast <ClipItem *>(m_dragItem);
1278                 QString previous = item->keyframes(item->selectedEffectIndex());
1279                 item->updateKeyframeEffect();
1280                 QString next = item->keyframes(item->selectedEffectIndex());
1281                 EditKeyFrameCommand *command = new EditKeyFrameCommand(this, item->track(), item->startPos(), item->selectedEffectIndex(), previous, next, false);
1282                 m_commandStack->push(command);
1283                 updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
1284                 emit clipItemSelected(item, item->selectedEffectIndex());
1285             }
1286
1287         } else*/  {
1288             // add keyframe
1289             GenTime keyFramePos = GenTime((int)(mapToScene(event->pos()).x()), m_document->fps()) - m_dragItem->startPos() + m_dragItem->cropStart();
1290             int val = m_dragItem->addKeyFrame(keyFramePos, mapToScene(event->pos()).toPoint().y());
1291             ClipItem * item = static_cast <ClipItem *>(m_dragItem);
1292             //QString previous = item->keyframes(item->selectedEffectIndex());
1293             QDomElement oldEffect = item->selectedEffect().cloneNode().toElement();
1294             item->insertKeyframe(item->getEffectAt(item->selectedEffectIndex()), keyFramePos.frames(m_document->fps()), val);
1295             //item->updateKeyframeEffect();
1296             //QString next = item->keyframes(item->selectedEffectIndex());
1297             QDomElement newEffect = item->selectedEffect().cloneNode().toElement();
1298             EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), oldEffect, newEffect, item->selectedEffectIndex(), false, false);
1299             //EditKeyFrameCommand *command = new EditKeyFrameCommand(this, m_dragItem->track(), m_dragItem->startPos(), item->selectedEffectIndex(), previous, next, false);
1300             m_commandStack->push(command);
1301             updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
1302             emit clipItemSelected(item, item->selectedEffectIndex());
1303         }
1304     } else if (m_dragItem && !m_dragItem->isItemLocked()) {
1305         editItemDuration();
1306     } else {
1307         QList<QGraphicsItem *> collisionList = items(event->pos());
1308         if (collisionList.count() == 1 && collisionList.at(0)->type() == GUIDEITEM) {
1309             Guide *editGuide = (Guide *) collisionList.at(0);
1310             if (editGuide) slotEditGuide(editGuide->info());
1311         }
1312     }
1313 }
1314
1315 void CustomTrackView::editItemDuration()
1316 {
1317     AbstractClipItem *item;
1318     if (m_dragItem) {
1319         item = m_dragItem;
1320     } else {
1321         if (m_scene->selectedItems().count() == 1) {
1322             item = static_cast <AbstractClipItem *>(m_scene->selectedItems().at(0));
1323         } else {
1324             if (m_scene->selectedItems().empty())
1325                 emit displayMessage(i18n("Cannot find clip to edit"), ErrorMessage);
1326             else
1327                 emit displayMessage(i18n("Cannot edit the duration of multiple items"), ErrorMessage);
1328             return;
1329         }
1330     }
1331
1332     if (!item) {
1333         emit displayMessage(i18n("Cannot find clip to edit"), ErrorMessage);
1334         return;
1335     }
1336
1337     if (item->type() == GROUPWIDGET || (item->parentItem() && item->parentItem()->type() == GROUPWIDGET)) {
1338         emit displayMessage(i18n("Cannot edit an item in a group"), ErrorMessage);
1339         return;
1340     }
1341
1342     if (!item->isItemLocked()) {
1343         GenTime minimum;
1344         GenTime maximum;
1345         if (item->type() == TRANSITIONWIDGET)
1346             getTransitionAvailableSpace(item, minimum, maximum);
1347         else
1348             getClipAvailableSpace(item, minimum, maximum);
1349
1350         ClipDurationDialog d(item, m_document->timecode(), minimum, maximum, this);
1351         if (d.exec() == QDialog::Accepted) {
1352             ItemInfo clipInfo = item->info();
1353             ItemInfo startInfo = clipInfo;
1354             if (item->type() == TRANSITIONWIDGET) {
1355                 // move & resize transition
1356                 clipInfo.startPos = d.startPos();
1357                 clipInfo.endPos = clipInfo.startPos + d.duration();
1358                 clipInfo.track = item->track();
1359                 MoveTransitionCommand *command = new MoveTransitionCommand(this, startInfo, clipInfo, true);
1360                 updateTrackDuration(clipInfo.track, command);
1361                 m_commandStack->push(command);
1362             } else {
1363                 // move and resize clip
1364                 ClipItem *clip = static_cast<ClipItem *>(item);
1365                 QUndoCommand *moveCommand = new QUndoCommand();
1366                 moveCommand->setText(i18n("Edit clip"));
1367                 if (d.duration() < item->cropDuration() || d.cropStart() != clipInfo.cropStart) {
1368                     // duration was reduced, so process it first
1369                     clipInfo.endPos = clipInfo.startPos + d.duration();
1370                     clipInfo.cropStart = d.cropStart();
1371
1372                     resizeClip(startInfo, clipInfo);
1373                     new ResizeClipCommand(this, startInfo, clipInfo, false, true, moveCommand);
1374                     adjustEffects(clip, startInfo, moveCommand);
1375                     new ResizeClipCommand(this, startInfo, clipInfo, false, true, moveCommand);
1376                 }
1377
1378                 if (d.startPos() != clipInfo.startPos) {
1379                     startInfo = clipInfo;
1380                     clipInfo.startPos = d.startPos();
1381                     clipInfo.endPos = item->endPos() + (clipInfo.startPos - startInfo.startPos);
1382                     new MoveClipCommand(this, startInfo, clipInfo, true, moveCommand);
1383                 }
1384
1385                 if (d.duration() > item->cropDuration()) {
1386                     // duration was increased, so process it after move
1387                     startInfo = clipInfo;
1388                     clipInfo.endPos = clipInfo.startPos + d.duration();
1389                     clipInfo.cropStart = d.cropStart();
1390
1391                     resizeClip(startInfo, clipInfo);
1392                     new ResizeClipCommand(this, startInfo, clipInfo, false, true, moveCommand);
1393                     adjustEffects(clip, startInfo, moveCommand);
1394                     new ResizeClipCommand(this, startInfo, clipInfo, false, true, moveCommand);
1395                 }
1396                 updateTrackDuration(clipInfo.track, moveCommand);
1397                 m_commandStack->push(moveCommand);
1398             }
1399         }
1400     } else {
1401         emit displayMessage(i18n("Item is locked"), ErrorMessage);
1402     }
1403 }
1404
1405 void CustomTrackView::editKeyFrame(const GenTime & /*pos*/, const int /*track*/, const int /*index*/, const QString & /*keyframes*/)
1406 {
1407     /*ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), track);
1408     if (clip) {
1409         clip->setKeyframes(index, keyframes);
1410         updateEffect(m_document->tracksCount() - clip->track(), clip->startPos(), clip->effectAt(index), index, false);
1411     } else emit displayMessage(i18n("Cannot find clip with keyframe"), ErrorMessage);*/
1412 }
1413
1414
1415 void CustomTrackView::displayContextMenu(QPoint pos, AbstractClipItem *clip, AbstractGroupItem *group)
1416 {
1417     m_deleteGuide->setEnabled(m_dragGuide != NULL);
1418     m_editGuide->setEnabled(m_dragGuide != NULL);
1419     m_markerMenu->clear();
1420     m_markerMenu->setEnabled(false);
1421     if (clip == NULL) {
1422         m_timelineContextMenu->popup(pos);
1423     } else if (group != NULL) {
1424         m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
1425         m_ungroupAction->setEnabled(true);
1426         updateClipTypeActions(NULL);
1427         m_timelineContextClipMenu->popup(pos);
1428     } else {
1429         m_ungroupAction->setEnabled(false);
1430         if (clip->type() == AVWIDGET) {
1431             ClipItem *item = static_cast <ClipItem*>(clip);
1432             //build go to marker menu
1433             if (item->baseClip()) {
1434                 QList <CommentedTime> markers = item->baseClip()->commentedSnapMarkers();
1435                 int offset = item->startPos().frames(m_document->fps());
1436                 if (!markers.isEmpty()) {
1437                     for (int i = 0; i < markers.count(); i++) {
1438                         int pos = (int) markers.at(i).time().frames(m_document->timecode().fps());
1439                         QString position = m_document->timecode().getTimecode(markers.at(i).time()) + ' ' + markers.at(i).comment();
1440                         QAction *go = m_markerMenu->addAction(position);
1441                         go->setData(pos + offset);
1442                     }
1443                 }
1444                 m_markerMenu->setEnabled(!m_markerMenu->isEmpty());
1445             }
1446             updateClipTypeActions(item);
1447             m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
1448             m_timelineContextClipMenu->popup(pos);
1449         } else if (clip->type() == TRANSITIONWIDGET) {
1450             m_timelineContextTransitionMenu->popup(pos);
1451         }
1452     }
1453 }
1454
1455 void CustomTrackView::activateMonitor()
1456 {
1457     emit activateDocumentMonitor();
1458 }
1459
1460 void CustomTrackView::insertClipCut(DocClipBase *clip, int in, int out)
1461 {
1462     resetSelectionGroup();
1463     ItemInfo info;
1464     info.startPos = GenTime();
1465     info.cropStart = GenTime(in, m_document->fps());
1466     info.endPos = GenTime(out - in, m_document->fps());
1467     info.cropDuration = info.endPos - info.startPos;
1468     info.track = 0;
1469
1470     // Check if clip can be inserted at that position
1471     ItemInfo pasteInfo = info;
1472     pasteInfo.startPos = GenTime(m_cursorPos, m_document->fps());
1473     pasteInfo.endPos = pasteInfo.startPos + info.endPos;
1474     pasteInfo.track = selectedTrack();
1475     if (!canBePastedTo(pasteInfo, AVWIDGET)) {
1476         emit displayMessage(i18n("Cannot insert clip in timeline"), ErrorMessage);
1477         return;
1478     }
1479
1480     AddTimelineClipCommand *command = new AddTimelineClipCommand(this, clip->toXML(), clip->getId(), pasteInfo, EffectsList(), m_scene->editMode() == OVERWRITEEDIT, m_scene->editMode() == INSERTEDIT, true, false);
1481     updateTrackDuration(pasteInfo.track, command);
1482     m_commandStack->push(command);
1483
1484     selectClip(true, false);
1485     // Automatic audio split
1486     if (KdenliveSettings::splitaudio())
1487         splitAudio();
1488 }
1489
1490 bool CustomTrackView::insertDropClips(const QMimeData *data, const QPoint &pos)
1491 {
1492     if (data->hasFormat("kdenlive/clip")) {
1493         m_clipDrag = true;
1494         m_scene->clearSelection();
1495         resetSelectionGroup(false);
1496         QStringList list = QString(data->data("kdenlive/clip")).split(';');
1497         DocClipBase *clip = m_document->getBaseClip(list.at(0));
1498         if (clip == NULL) {
1499             kDebug() << " WARNING))))))))) CLIP NOT FOUND : " << list.at(0);
1500             return false;
1501         }
1502         if (clip->getProducer() == NULL) {
1503             emit displayMessage(i18n("Clip not ready"), ErrorMessage);
1504             return false;
1505         }
1506         QPointF framePos = mapToScene(pos);
1507         ItemInfo info;
1508         info.startPos = GenTime();
1509         info.cropStart = GenTime(list.at(1).toInt(), m_document->fps());
1510         info.endPos = GenTime(list.at(2).toInt() - list.at(1).toInt(), m_document->fps());
1511         info.cropDuration = info.endPos - info.startPos;
1512         info.track = 0;
1513
1514         // Check if clip can be inserted at that position
1515         ItemInfo pasteInfo = info;
1516         pasteInfo.startPos = GenTime((int)(framePos.x() + 0.5), m_document->fps());
1517         pasteInfo.endPos = pasteInfo.startPos + info.endPos;
1518         pasteInfo.track = (int)(framePos.y() / m_tracksHeight);
1519         framePos.setX((int)(framePos.x() + 0.5));
1520         framePos.setY(pasteInfo.track * m_tracksHeight);
1521         if (!canBePastedTo(pasteInfo, AVWIDGET)) {
1522             return true;
1523         }
1524         m_selectionGroup = new AbstractGroupItem(m_document->fps());
1525         ClipItem *item = new ClipItem(clip, info, m_document->fps(), 1.0, 1, getFrameWidth());
1526         m_selectionGroup->addToGroup(item);
1527         item->setFlag(QGraphicsItem::ItemIsMovable, false);
1528
1529         QList <GenTime> offsetList;
1530         offsetList.append(info.endPos);
1531         updateSnapPoints(NULL, offsetList);
1532         m_selectionGroup->setPos(framePos);
1533         scene()->addItem(m_selectionGroup);
1534         m_selectionGroup->setSelected(true);
1535         return true;
1536     } else if (data->hasFormat("kdenlive/producerslist")) {
1537         m_clipDrag = true;
1538         QStringList ids = QString(data->data("kdenlive/producerslist")).split(';');
1539         m_scene->clearSelection();
1540         resetSelectionGroup(false);
1541
1542         QList <GenTime> offsetList;
1543         QList <ItemInfo> infoList;
1544         QPointF framePos = mapToScene(pos);
1545         GenTime start = GenTime((int)(framePos.x() + 0.5), m_document->fps());
1546         int track = (int)(framePos.y() / m_tracksHeight);
1547         framePos.setX((int)(framePos.x() + 0.5));
1548         framePos.setY(track * m_tracksHeight);
1549
1550         // Check if clips can be inserted at that position
1551         for (int i = 0; i < ids.size(); ++i) {
1552             DocClipBase *clip = m_document->getBaseClip(ids.at(i));
1553             if (clip == NULL) {
1554                 kDebug() << " WARNING))))))))) CLIP NOT FOUND : " << ids.at(i);
1555                 return false;
1556             }
1557             if (clip->getProducer() == NULL) {
1558                 emit displayMessage(i18n("Clip not ready"), ErrorMessage);
1559                 return false;
1560             }
1561             ItemInfo info;
1562             info.startPos = start;
1563             info.cropDuration = clip->duration();
1564             info.endPos = info.startPos + info.cropDuration;
1565             info.track = track;
1566             infoList.append(info);
1567             start += clip->duration();
1568         }
1569         if (!canBePastedTo(infoList, AVWIDGET)) {
1570             return true;
1571         }
1572         m_selectionGroup = new AbstractGroupItem(m_document->fps());
1573         start = GenTime();
1574         for (int i = 0; i < ids.size(); ++i) {
1575             DocClipBase *clip = m_document->getBaseClip(ids.at(i));
1576             ItemInfo info;
1577             info.startPos = start;
1578             info.cropDuration = clip->duration();
1579             info.endPos = info.startPos + info.cropDuration;
1580             info.track = 0;
1581             start += info.cropDuration;
1582             offsetList.append(start);
1583             ClipItem *item = new ClipItem(clip, info, m_document->fps(), 1.0, 1, getFrameWidth(), false);
1584             item->setFlag(QGraphicsItem::ItemIsMovable, false);
1585             m_selectionGroup->addToGroup(item);
1586             if (!clip->isPlaceHolder()) m_waitingThumbs.append(item);
1587         }
1588
1589         updateSnapPoints(NULL, offsetList);
1590         m_selectionGroup->setPos(framePos);
1591         scene()->addItem(m_selectionGroup);
1592         //m_selectionGroup->setZValue(10);
1593         m_thumbsTimer.start();
1594         return true;
1595
1596     } else {
1597         // the drag is not a clip (may be effect, ...)
1598         m_clipDrag = false;
1599         return false;
1600     }
1601 }
1602
1603
1604
1605 void CustomTrackView::dragEnterEvent(QDragEnterEvent * event)
1606 {
1607     if (insertDropClips(event->mimeData(), event->pos())) {
1608       if (event->source() == this) {
1609              event->setDropAction(Qt::MoveAction);
1610              event->accept();
1611          } else {
1612              event->setDropAction(Qt::MoveAction);
1613              event->acceptProposedAction();
1614          }
1615     } else QGraphicsView::dragEnterEvent(event);
1616 }
1617
1618 bool CustomTrackView::itemCollision(AbstractClipItem *item, ItemInfo newPos)
1619 {
1620     QRectF shape = QRectF(newPos.startPos.frames(m_document->fps()), newPos.track * m_tracksHeight + 1, (newPos.endPos - newPos.startPos).frames(m_document->fps()) - 0.02, m_tracksHeight - 1);
1621     QList<QGraphicsItem*> collindingItems = scene()->items(shape, Qt::IntersectsItemShape);
1622     collindingItems.removeAll(item);
1623     if (collindingItems.isEmpty()) return false;
1624     else {
1625         for (int i = 0; i < collindingItems.count(); i++) {
1626             QGraphicsItem *collision = collindingItems.at(i);
1627             if (collision->type() == item->type()) {
1628                 // Collision
1629                 kDebug() << "// COLLISIION DETECTED";
1630                 return true;
1631             }
1632         }
1633         return false;
1634     }
1635 }
1636
1637 void CustomTrackView::slotRefreshEffects(ClipItem *clip)
1638 {
1639     int track = m_document->tracksCount() - clip->track();
1640     GenTime pos = clip->startPos();
1641     if (!m_document->renderer()->mltRemoveEffect(track, pos, -1, false, false)) {
1642         emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
1643         return;
1644     }
1645     bool success = true;
1646     for (int i = 0; i < clip->effectsCount(); i++) {
1647         if (!m_document->renderer()->mltAddEffect(track, pos, getEffectArgs(clip->effectAt(i)), false)) success = false;
1648     }
1649     if (!success) emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
1650     m_document->renderer()->doRefresh();
1651 }
1652
1653 void CustomTrackView::addEffect(int track, GenTime pos, QDomElement effect)
1654 {
1655     if (pos < GenTime()) {
1656         // Add track effect
1657         if (effect.attribute("id") == "speed") {
1658             emit displayMessage(i18n("Cannot add speed effect to track"), ErrorMessage);
1659             return;
1660         }
1661         clearSelection();
1662         m_document->addTrackEffect(track - 1, effect);
1663         m_document->renderer()->mltAddTrackEffect(track, getEffectArgs(effect));
1664         emit updateTrackEffectState(track - 1);
1665         emit showTrackEffects(track, m_document->trackInfoAt(track - 1));
1666         return;
1667     }
1668     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
1669     if (clip) {
1670         // Special case: speed effect
1671         if (effect.attribute("id") == "speed") {
1672             if (clip->clipType() != VIDEO && clip->clipType() != AV && clip->clipType() != PLAYLIST) {
1673                 emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
1674                 return;
1675             }
1676             QLocale locale;
1677             double speed = locale.toDouble(EffectsList::parameter(effect, "speed")) / 100.0;
1678             int strobe = EffectsList::parameter(effect, "strobe").toInt();
1679             if (strobe == 0) strobe = 1;
1680             doChangeClipSpeed(clip->info(), clip->speedIndependantInfo(), speed, 1.0, strobe, clip->baseClip()->getId());
1681             EffectsParameterList params = clip->addEffect(effect);
1682             m_document->renderer()->mltAddEffect(track, pos, params);
1683             if (clip->isSelected()) emit clipItemSelected(clip);
1684             return;
1685         }
1686         EffectsParameterList params = clip->addEffect(effect);
1687         if (!m_document->renderer()->mltAddEffect(track, pos, params))
1688             emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
1689         clip->setSelectedEffect(params.paramValue("kdenlive_ix").toInt());
1690         if (clip->isSelected()) emit clipItemSelected(clip);
1691     } else emit displayMessage(i18n("Cannot find clip to add effect"), ErrorMessage);
1692 }
1693
1694 void CustomTrackView::deleteEffect(int track, GenTime pos, QDomElement effect)
1695 {
1696     QString index = effect.attribute("kdenlive_ix");
1697     if (pos < GenTime()) {
1698         // Delete track effect
1699         if (m_document->renderer()->mltRemoveTrackEffect(track, index.toInt(), true)) {
1700             m_document->removeTrackEffect(track - 1, effect);
1701         }
1702         else emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
1703         emit updateTrackEffectState(track - 1);
1704         emit showTrackEffects(track, m_document->trackInfoAt(track - 1));
1705         return;
1706     }
1707     // Special case: speed effect
1708     if (effect.attribute("id") == "speed") {
1709         ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
1710         if (clip) {
1711             doChangeClipSpeed(clip->info(), clip->speedIndependantInfo(), 1.0, clip->speed(), 1, clip->baseClip()->getId());
1712             clip->deleteEffect(index);
1713             emit clipItemSelected(clip);
1714             m_document->renderer()->mltRemoveEffect(track, pos, index.toInt(), true);
1715             return;
1716         }
1717     }
1718     if (!m_document->renderer()->mltRemoveEffect(track, pos, index.toInt(), true)) {
1719         kDebug() << "// ERROR REMOV EFFECT: " << index << ", DISABLE: " << effect.attribute("disable");
1720         emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
1721         return;
1722     }
1723     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
1724     if (clip) {
1725         clip->deleteEffect(index);
1726         emit clipItemSelected(clip);
1727     }
1728 }
1729
1730 void CustomTrackView::slotAddGroupEffect(QDomElement effect, AbstractGroupItem *group)
1731 {
1732     QList<QGraphicsItem *> itemList = group->childItems();
1733     QUndoCommand *effectCommand = new QUndoCommand();
1734     QString effectName;
1735     QDomElement namenode = effect.firstChildElement("name");
1736     if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data());
1737     else effectName = i18n("effect");
1738     effectCommand->setText(i18n("Add %1", effectName));
1739     for (int i = 0; i < itemList.count(); i++) {
1740         if (itemList.at(i)->type() == AVWIDGET) {
1741             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
1742             if (effect.tagName() == "effectgroup") {
1743                 QDomNodeList effectlist = effect.elementsByTagName("effect");
1744                 for (int j = 0; j < effectlist.count(); j++) {
1745                     QDomElement subeffect = effectlist.at(j).toElement();
1746                     if (subeffect.hasAttribute("kdenlive_info")) {
1747                         // effect is in a group
1748                         EffectInfo effectInfo;
1749                         effectInfo.fromString(subeffect.attribute("kdenlive_info"));
1750                         if (effectInfo.groupIndex < 0) {
1751                             // group needs to be appended
1752                             effectInfo.groupIndex = item->nextFreeEffectGroupIndex();
1753                             subeffect.setAttribute("kdenlive_info", effectInfo.toString());
1754                         }
1755                     }
1756                     processEffect(item, subeffect, effectCommand);
1757                 }
1758             }
1759             else {
1760                 processEffect(item, effect, effectCommand);
1761             }
1762         }
1763     }
1764     if (effectCommand->childCount() > 0) {
1765         m_commandStack->push(effectCommand);
1766         setDocumentModified();
1767     } else delete effectCommand;
1768 }
1769
1770 void CustomTrackView::slotAddEffect(ClipItem *clip, QDomElement effect)
1771 {
1772     if (clip) slotAddEffect(effect, clip->startPos(), clip->track());
1773 }
1774
1775 void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track)
1776 {
1777     QList<QGraphicsItem *> itemList;
1778     QUndoCommand *effectCommand = new QUndoCommand();
1779     QString effectName;
1780     if (effect.tagName() == "effectgroup") {
1781         effectName = effect.attribute("name");
1782     } else {
1783         QDomElement namenode = effect.firstChildElement("name");
1784         if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data());
1785         else effectName = i18n("effect");
1786     }
1787     effectCommand->setText(i18n("Add %1", effectName));
1788
1789     if (track == -1) itemList = scene()->selectedItems();
1790     if (itemList.isEmpty()) {
1791         ClipItem *clip = getClipItemAt((int) pos.frames(m_document->fps()), track);
1792         if (clip) itemList.append(clip);
1793         else emit displayMessage(i18n("Select a clip if you want to apply an effect"), ErrorMessage);
1794     }
1795
1796     //expand groups
1797     for (int i = 0; i < itemList.count(); i++) {
1798         if (itemList.at(i)->type() == GROUPWIDGET) {
1799             QList<QGraphicsItem *> subitems = itemList.at(i)->childItems();
1800             for (int j = 0; j < subitems.count(); j++) {
1801                 if (!itemList.contains(subitems.at(j))) itemList.append(subitems.at(j));
1802             }
1803         }
1804     }
1805
1806     for (int i = 0; i < itemList.count(); i++) {
1807         if (itemList.at(i)->type() == AVWIDGET) {
1808             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
1809             if (effect.tagName() == "effectgroup") {
1810                 QDomNodeList effectlist = effect.elementsByTagName("effect");
1811                 for (int j = 0; j < effectlist.count(); j++) {
1812                     QDomElement subeffect = effectlist.at(j).toElement();
1813                     if (subeffect.hasAttribute("kdenlive_info")) {
1814                         // effect is in a group
1815                         EffectInfo effectInfo;
1816                         effectInfo.fromString(subeffect.attribute("kdenlive_info"));
1817                         if (effectInfo.groupIndex < 0) {
1818                             // group needs to be appended
1819                             effectInfo.groupIndex = item->nextFreeEffectGroupIndex();
1820                             subeffect.setAttribute("kdenlive_info", effectInfo.toString());
1821                         }
1822                     }
1823                     processEffect(item, subeffect, effectCommand);
1824                 }
1825             }
1826             else processEffect(item, effect, effectCommand);
1827         }
1828     }
1829     if (effectCommand->childCount() > 0) {
1830         m_commandStack->push(effectCommand);
1831         setDocumentModified();
1832         if (effectCommand->childCount() == 1) {
1833             // Display newly added clip effect
1834             for (int i = 0; i < itemList.count(); i++) {
1835                 if (itemList.at(i)->type() == AVWIDGET) {
1836                     ClipItem *clip = static_cast<ClipItem *>(itemList.at(i));
1837                     clip->setSelectedEffect(clip->effectsCount());
1838                     if (!clip->isSelected()) {
1839                         clearSelection(false);
1840                         clip->setSelected(true);
1841                         emit clipItemSelected(clip);
1842                     }
1843                     break;
1844                 }
1845             }
1846         }
1847     } else delete effectCommand;
1848 }
1849
1850 void CustomTrackView::processEffect(ClipItem *item, QDomElement effect, QUndoCommand *effectCommand)
1851 {
1852     if (effect.attribute("type") == "audio") {
1853         // Don't add audio effects on video clips
1854         if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) {
1855             /* do not show error message when item is part of a group as the user probably knows what he does then
1856             * and the message is annoying when working with the split audio feature */
1857             if (!item->parentItem() || item->parentItem() == m_selectionGroup)
1858                 emit displayMessage(i18n("Cannot add an audio effect to this clip"), ErrorMessage);
1859             return;
1860         }
1861     } else if (effect.attribute("type") == "video" || !effect.hasAttribute("type")) {
1862         // Don't add video effect on audio clips
1863         if (item->isAudioOnly() || item->clipType() == AUDIO) {
1864             /* do not show error message when item is part of a group as the user probably knows what he does then
1865             * and the message is annoying when working with the split audio feature */
1866             if (!item->parentItem() || item->parentItem() == m_selectionGroup)
1867                 emit displayMessage(i18n("Cannot add a video effect to this clip"), ErrorMessage);
1868             return;
1869         }
1870     }
1871     if (effect.attribute("unique", "0") != "0" && item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1) {
1872         emit displayMessage(i18n("Effect already present in clip"), ErrorMessage);
1873         return;
1874     }
1875     if (item->isItemLocked()) {
1876         return;
1877     }
1878
1879     if (effect.attribute("id") == "freeze" && m_cursorPos > item->startPos().frames(m_document->fps()) && m_cursorPos < item->endPos().frames(m_document->fps())) {
1880         item->initEffect(effect, m_cursorPos - item->startPos().frames(m_document->fps()));
1881     } else {
1882         item->initEffect(effect);
1883     }
1884     new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand);
1885 }
1886
1887 void CustomTrackView::slotDeleteEffect(ClipItem *clip, int track, QDomElement effect, bool affectGroup)
1888 {
1889     if (clip == NULL) {
1890         // delete track effect
1891         AddEffectCommand *command = new AddEffectCommand(this, track, GenTime(-1), effect, false);
1892         m_commandStack->push(command);
1893         setDocumentModified();
1894         return;
1895     }
1896     if (affectGroup && clip->parentItem() && clip->parentItem() == m_selectionGroup) {
1897         //clip is in a group, also remove the effect in other clips of the group
1898         QList<QGraphicsItem *> items = m_selectionGroup->childItems();
1899         QUndoCommand *delCommand = new QUndoCommand();
1900         QString effectName;
1901         QDomElement namenode = effect.firstChildElement("name");
1902         if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data());
1903         else effectName = i18n("effect");
1904         delCommand->setText(i18n("Delete %1", effectName));
1905
1906         //expand groups
1907         for (int i = 0; i < items.count(); i++) {
1908             if (items.at(i)->type() == GROUPWIDGET) {
1909                 QList<QGraphicsItem *> subitems = items.at(i)->childItems();
1910                 for (int j = 0; j < subitems.count(); j++) {
1911                     if (!items.contains(subitems.at(j))) items.append(subitems.at(j));
1912                 }
1913             }
1914         }
1915
1916         for (int i = 0; i < items.count(); i++) {
1917             if (items.at(i)->type() == AVWIDGET) {
1918                 ClipItem *item = static_cast <ClipItem *>(items.at(i));
1919                 int ix = item->hasEffect(effect.attribute("tag"), effect.attribute("id"));
1920                 if (ix != -1) {
1921                     QDomElement eff = item->effectAt(ix);
1922                     new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), eff, false, delCommand);
1923                 }
1924             }
1925         }
1926         if (delCommand->childCount() > 0)
1927             m_commandStack->push(delCommand);
1928         else
1929             delete delCommand;
1930         setDocumentModified();
1931         return;
1932     }
1933     AddEffectCommand *command = new AddEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effect, false);
1934     m_commandStack->push(command);
1935     setDocumentModified();
1936 }
1937
1938 void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedEffect, int ix, bool updateEffectStack)
1939 {
1940     if (insertedEffect.isNull()) {
1941         emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
1942         return;
1943     }
1944     QDomElement effect = insertedEffect.cloneNode().toElement();
1945     //kDebug() << "// update effect ix: " << effect.attribute("kdenlive_ix")<<", GAIN: "<<EffectsList::parameter(effect, "gain");
1946     if (pos < GenTime()) {
1947         // editing a track effect
1948         EffectsParameterList effectParams = getEffectArgs(effect);
1949         // check if we are trying to reset a keyframe effect
1950         /*if (effectParams.hasParam("keyframes") && effectParams.paramValue("keyframes").isEmpty()) {
1951             clip->initEffect(effect);
1952             effectParams = getEffectArgs(effect);
1953         }*/
1954         if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - track, pos, effectParams))
1955             emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
1956         m_document->setTrackEffect(m_document->tracksCount() - track - 1, ix, effect);
1957         emit updateTrackEffectState(track);
1958         setDocumentModified();
1959         return;
1960
1961     }
1962     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
1963     if (clip) {
1964         // Special case: speed effect
1965         if (effect.attribute("id") == "speed") {
1966             if (effect.attribute("disable") == "1") {
1967                 doChangeClipSpeed(clip->info(), clip->speedIndependantInfo(), 1.0, clip->speed(), 1, clip->baseClip()->getId());
1968             } else {
1969                 QLocale locale;
1970                 double speed = locale.toDouble(EffectsList::parameter(effect, "speed")) / 100.0;
1971                 int strobe = EffectsList::parameter(effect, "strobe").toInt();
1972                 if (strobe == 0) strobe = 1;
1973                 doChangeClipSpeed(clip->info(), clip->speedIndependantInfo(), speed, clip->speed(), strobe, clip->baseClip()->getId());
1974             }
1975             clip->updateEffect(effect);
1976             if (updateEffectStack && clip->isSelected())
1977                 emit clipItemSelected(clip);
1978             if (ix == clip->selectedEffectIndex()) {
1979                 // make sure to update display of clip keyframes
1980                 clip->setSelectedEffect(ix);
1981             } else emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
1982             return;
1983         }
1984
1985         EffectsParameterList effectParams = getEffectArgs(effect);
1986         // check if we are trying to reset a keyframe effect
1987         if (effectParams.hasParam("keyframes") && effectParams.paramValue("keyframes").isEmpty()) {
1988             clip->initEffect(effect);
1989             effectParams = getEffectArgs(effect);
1990         }
1991
1992         if (effect.attribute("tag") == "volume" || effect.attribute("tag") == "brightness") {
1993             // A fade effect was modified, update the clip
1994             if (effect.attribute("id") == "fadein" || effect.attribute("id") == "fade_from_black") {
1995                 int pos = effectParams.paramValue("out").toInt() - effectParams.paramValue("in").toInt();
1996                 clip->setFadeIn(pos);
1997             }
1998             if (effect.attribute("id") == "fadeout" || effect.attribute("id") == "fade_to_black") {
1999                 int pos = effectParams.paramValue("out").toInt() - effectParams.paramValue("in").toInt();
2000                 clip->setFadeOut(pos);
2001             }
2002         }
2003         bool success = m_document->renderer()->mltEditEffect(m_document->tracksCount() - clip->track(), clip->startPos(), effectParams);
2004
2005         if (success) {
2006             clip->updateEffect(effect);
2007             if (updateEffectStack && clip->isSelected()) {
2008                 emit clipItemSelected(clip);
2009             }
2010             if (ix == clip->selectedEffectIndex()) {
2011                 // make sure to update display of clip keyframes
2012                 clip->setSelectedEffect(ix);
2013             }
2014         }
2015         else emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
2016     }
2017     else emit displayMessage(i18n("Cannot find clip to update effect"), ErrorMessage);
2018     setDocumentModified();
2019 }
2020
2021 void CustomTrackView::moveEffect(int track, GenTime pos, QList <int> oldPos, QList <int> newPos)
2022 {
2023     if (pos < GenTime()) {
2024         // Moving track effect
2025         int documentTrack = m_document->tracksCount() - track - 1;
2026         int max = m_document->getTrackEffects(documentTrack).count();
2027         int new_position = newPos.at(0);
2028         if (new_position > max) {
2029             new_position = max;
2030         }
2031         int old_position = oldPos.at(0);
2032         for (int i = 0; i < newPos.count(); i++) {
2033             QDomElement act = m_document->getTrackEffect(documentTrack, new_position);
2034             if (old_position > new_position) {
2035                 // Moving up, we need to adjust index
2036                 old_position = oldPos.at(i);
2037                 new_position = newPos.at(i);
2038             }
2039             QDomElement before = m_document->getTrackEffect(documentTrack, old_position);
2040             if (!act.isNull() && !before.isNull()) {
2041                 m_document->setTrackEffect(documentTrack, new_position, before);
2042                 m_document->renderer()->mltMoveEffect(m_document->tracksCount() - track, pos, old_position, new_position);
2043             } else emit displayMessage(i18n("Cannot move effect"), ErrorMessage);
2044         }
2045         emit showTrackEffects(m_document->tracksCount() - track, m_document->trackInfoAt(documentTrack));
2046         return;
2047     }
2048     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
2049     if (clip) {
2050         int new_position = newPos.at(0);
2051         if (new_position > clip->effectsCount()) {
2052             new_position = clip->effectsCount();
2053         }
2054         int old_position = oldPos.at(0);
2055         for (int i = 0; i < newPos.count(); i++) {
2056             QDomElement act = clip->effectAt(new_position);
2057             if (old_position > new_position) {
2058                 // Moving up, we need to adjust index
2059                 old_position = oldPos.at(i);
2060                 new_position = newPos.at(i);
2061             }
2062             QDomElement before = clip->effectAt(old_position);
2063             if (act.isNull() || before.isNull()) {
2064                 emit displayMessage(i18n("Cannot move effect"), ErrorMessage);
2065                 return;
2066             }
2067             clip->moveEffect(before, new_position);
2068             // special case: speed effect, which is a pseudo-effect, not appearing in MLT's effects
2069             if (act.attribute("id") == "speed") {
2070                 m_document->renderer()->mltUpdateEffectPosition(track, pos, old_position, new_position);
2071             } else if (before.attribute("id") == "speed") {
2072                 m_document->renderer()->mltUpdateEffectPosition(track, pos, new_position, old_position);
2073             } else m_document->renderer()->mltMoveEffect(track, pos, old_position, new_position);
2074         }
2075         clip->setSelectedEffect(newPos.at(0));
2076         emit clipItemSelected(clip);
2077         setDocumentModified();
2078     } else emit displayMessage(i18n("Cannot move effect"), ErrorMessage);
2079 }
2080
2081 void CustomTrackView::slotChangeEffectState(ClipItem *clip, int track, int effectPos, bool disable)
2082 {
2083     EditEffectCommand *command;
2084     QDomElement effect;
2085     if (clip == NULL) effect = m_document->getTrackEffect(track - 1, effectPos);
2086     else effect = clip->effectAt(effectPos);
2087     QDomElement oldEffect = effect.cloneNode().toElement();
2088     effect.setAttribute("disable", (int) disable);
2089
2090
2091     if (clip == NULL) {
2092         // editing track effect
2093         command = new EditEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), oldEffect, effect, effectPos, false, true);
2094     } else {
2095         command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldEffect, effect, effectPos, false, true);
2096     }
2097     m_commandStack->push(command);
2098     setDocumentModified();;
2099 }
2100
2101 void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int track, QList <int> currentPos, int newPos)
2102 {
2103     MoveEffectCommand *command;
2104     if (clip == NULL) {
2105         // editing track effect
2106         command = new MoveEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), currentPos, newPos);
2107     } else command = new MoveEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), currentPos, newPos);
2108     m_commandStack->push(command);
2109     setDocumentModified();
2110 }
2111
2112 void CustomTrackView::slotUpdateClipEffect(ClipItem *clip, int track, QDomElement oldeffect, QDomElement effect, int ix, bool refreshEffectStack)
2113 {
2114     EditEffectCommand *command;
2115     if (clip) command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, refreshEffectStack, true);
2116     else command = new EditEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), oldeffect, effect, ix, refreshEffectStack, true);
2117     m_commandStack->push(command);
2118 }
2119
2120 void CustomTrackView::slotUpdateClipRegion(ClipItem *clip, int ix, QString region)
2121 {
2122     QDomElement effect = clip->getEffectAt(ix);
2123     QDomElement oldeffect = effect.cloneNode().toElement();
2124     effect.setAttribute("region", region);
2125     EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, true, true);
2126     m_commandStack->push(command);
2127 }
2128
2129 ClipItem *CustomTrackView::cutClip(ItemInfo info, GenTime cutTime, bool cut, bool execute)
2130 {
2131     if (cut) {
2132         // cut clip
2133         ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()), info.track);
2134         if (!item || cutTime >= item->endPos() || cutTime <= item->startPos()) {
2135             emit displayMessage(i18n("Cannot find clip to cut"), ErrorMessage);
2136             if (item)
2137                 kDebug() << "/////////  ERROR CUTTING CLIP : (" << item->startPos().frames(25) << "-" << item->endPos().frames(25) << "), INFO: (" << info.startPos.frames(25) << "-" << info.endPos.frames(25) << ")" << ", CUT: " << cutTime.frames(25);
2138             else
2139                 kDebug() << "/// ERROR NO CLIP at: " << info.startPos.frames(m_document->fps()) << ", track: " << info.track;
2140             m_blockRefresh = false;
2141             return NULL;
2142         }
2143
2144         if (execute) m_document->renderer()->mltCutClip(m_document->tracksCount() - info.track, cutTime);
2145         int cutPos = (int) cutTime.frames(m_document->fps());
2146         ItemInfo newPos;
2147         newPos.startPos = cutTime;
2148         newPos.endPos = info.endPos;
2149         newPos.cropStart = item->info().cropStart + (cutTime - info.startPos);
2150         newPos.track = info.track;
2151         newPos.cropDuration = newPos.endPos - newPos.startPos;
2152
2153         bool snap = KdenliveSettings::snaptopoints();
2154         KdenliveSettings::setSnaptopoints(false);
2155         ClipItem *dup = item->clone(newPos);
2156
2157         // remove unwanted effects
2158         // fade in from 2nd part of the clip
2159         int ix = dup->hasEffect(QString(), "fadein");
2160         if (ix != -1) {
2161             QDomElement oldeffect = dup->effectAt(ix);
2162             dup->deleteEffect(oldeffect.attribute("kdenlive_ix"));
2163         }
2164         ix = dup->hasEffect(QString(), "fade_from_black");
2165         if (ix != -1) {
2166             QDomElement oldeffect = dup->effectAt(ix);
2167             dup->deleteEffect(oldeffect.attribute("kdenlive_ix"));
2168         }
2169         // fade out from 1st part of the clip
2170         ix = item->hasEffect(QString(), "fadeout");
2171         if (ix != -1) {
2172             QDomElement oldeffect = item->effectAt(ix);
2173             item->deleteEffect(oldeffect.attribute("kdenlive_ix"));
2174         }
2175         ix = item->hasEffect(QString(), "fade_to_black");
2176         if (ix != -1) {
2177             QDomElement oldeffect = item->effectAt(ix);
2178             item->deleteEffect(oldeffect.attribute("kdenlive_ix"));
2179         }
2180
2181
2182         item->resizeEnd(cutPos);
2183         scene()->addItem(dup);
2184         if (item->checkKeyFrames())
2185             slotRefreshEffects(item);
2186         if (dup->checkKeyFrames())
2187             slotRefreshEffects(dup);
2188
2189         item->baseClip()->addReference();
2190         m_document->updateClip(item->baseClip()->getId());
2191         setDocumentModified();
2192         KdenliveSettings::setSnaptopoints(snap);
2193         if (execute && item->isSelected())
2194             emit clipItemSelected(item);
2195         return dup;
2196     } else {
2197         // uncut clip
2198
2199         ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()), info.track);
2200         ClipItem *dup = getClipItemAt((int) cutTime.frames(m_document->fps()), info.track);
2201
2202         if (!item || !dup || item == dup) {
2203             emit displayMessage(i18n("Cannot find clip to uncut"), ErrorMessage);
2204             m_blockRefresh = false;
2205             return NULL;
2206         }
2207         if (m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, cutTime) == false) {
2208             emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(cutTime.frames(m_document->fps())), info.track), ErrorMessage);
2209             return NULL;
2210         }
2211
2212         bool snap = KdenliveSettings::snaptopoints();
2213         KdenliveSettings::setSnaptopoints(false);
2214
2215         // join fade effects again
2216         int ix = dup->hasEffect(QString(), "fadeout");
2217         if (ix != -1) {
2218             QDomElement effect = dup->effectAt(ix);
2219             item->addEffect(effect);
2220         }
2221         ix = dup->hasEffect(QString(), "fade_to_black");
2222         if (ix != -1) {
2223             QDomElement effect = dup->effectAt(ix);
2224             item->addEffect(effect);
2225         }
2226
2227         m_waitingThumbs.removeAll(dup);
2228         bool selected = item->isSelected();
2229         if (dup->isSelected()) {
2230             selected = true;
2231             item->setSelected(true);
2232             emit clipItemSelected(NULL);
2233         }
2234         dup->baseClip()->removeReference();
2235         m_document->updateClip(dup->baseClip()->getId());
2236         scene()->removeItem(dup);
2237         delete dup;
2238         dup = NULL;
2239
2240         ItemInfo clipinfo = item->info();
2241         clipinfo.track = m_document->tracksCount() - clipinfo.track;
2242         bool success = m_document->renderer()->mltResizeClipEnd(clipinfo, info.endPos - info.startPos);
2243         if (success) {
2244             item->resizeEnd((int) info.endPos.frames(m_document->fps()));
2245             setDocumentModified();
2246         } else {
2247             emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
2248         }
2249         KdenliveSettings::setSnaptopoints(snap);
2250         if (execute && selected)
2251             emit clipItemSelected(item);
2252         return item;
2253     }
2254     //QTimer::singleShot(3000, this, SLOT(slotEnableRefresh()));
2255 }
2256
2257 void CustomTrackView::slotEnableRefresh()
2258 {
2259     m_blockRefresh = false;
2260 }
2261
2262 void CustomTrackView::slotAddTransitionToSelectedClips(QDomElement transition)
2263 {
2264     QList<QGraphicsItem *> itemList = scene()->selectedItems();
2265     if (itemList.count() == 1) {
2266         if (itemList.at(0)->type() == AVWIDGET) {
2267             ClipItem *item = (ClipItem *) itemList.at(0);
2268             ItemInfo info;
2269             info.track = item->track();
2270             ClipItem *transitionClip = NULL;
2271             const int transitiontrack = getPreviousVideoTrack(info.track);
2272             GenTime pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
2273             if (pos < item->startPos() + item->cropDuration() / 2) {
2274                 // add transition to clip start
2275                 info.startPos = item->startPos();
2276                 if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.startPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
2277                 if (transitionClip && transitionClip->endPos() < item->endPos()) {
2278                     info.endPos = transitionClip->endPos();
2279                 } else info.endPos = info.startPos + GenTime(65, m_document->fps());
2280                 // Check there is no other transition at that place
2281                 double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
2282                 QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
2283                 QList<QGraphicsItem *> selection = m_scene->items(r);
2284                 bool transitionAccepted = true;
2285                 for (int i = 0; i < selection.count(); i++) {
2286                     if (selection.at(i)->type() == TRANSITIONWIDGET) {
2287                         Transition *tr = static_cast <Transition *>(selection.at(i));
2288                         if (tr->startPos() - info.startPos > GenTime(5, m_document->fps())) {
2289                             if (tr->startPos() < info.endPos) info.endPos = tr->startPos();
2290                         } else transitionAccepted = false;
2291                     }
2292                 }
2293                 if (transitionAccepted) slotAddTransition(item, info, transitiontrack, transition);
2294                 else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
2295
2296             } else {
2297                 // add transition to clip  end
2298                 info.endPos = item->endPos();
2299                 if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.endPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
2300                 if (transitionClip && transitionClip->startPos() > item->startPos()) {
2301                     info.startPos = transitionClip->startPos();
2302                 } else info.startPos = info.endPos - GenTime(65, m_document->fps());
2303                 if (transition.attribute("tag") == "luma") EffectsList::setParameter(transition, "reverse", "1");
2304                 else if (transition.attribute("id") == "slide") EffectsList::setParameter(transition, "invert", "1");
2305
2306                 // Check there is no other transition at that place
2307                 double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
2308                 QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
2309                 QList<QGraphicsItem *> selection = m_scene->items(r);
2310                 bool transitionAccepted = true;
2311                 for (int i = 0; i < selection.count(); i++) {
2312                     if (selection.at(i)->type() == TRANSITIONWIDGET) {
2313                         Transition *tr = static_cast <Transition *>(selection.at(i));
2314                         if (info.endPos - tr->endPos() > GenTime(5, m_document->fps())) {
2315                             if (tr->endPos() > info.startPos) info.startPos = tr->endPos();
2316                         } else transitionAccepted = false;
2317                     }
2318                 }
2319                 if (transitionAccepted) slotAddTransition(item, info, transitiontrack, transition);
2320                 else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
2321             }
2322         }
2323     } else for (int i = 0; i < itemList.count(); i++) {
2324             if (itemList.at(i)->type() == AVWIDGET) {
2325                 ClipItem *item = (ClipItem *) itemList.at(i);
2326                 ItemInfo info;
2327                 info.startPos = item->startPos();
2328                 info.endPos = info.startPos + GenTime(65, m_document->fps());
2329                 info.track = item->track();
2330
2331                 // Check there is no other transition at that place
2332                 double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
2333                 QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
2334                 QList<QGraphicsItem *> selection = m_scene->items(r);
2335                 bool transitionAccepted = true;
2336                 for (int i = 0; i < selection.count(); i++) {
2337                     if (selection.at(i)->type() == TRANSITIONWIDGET) {
2338                         Transition *tr = static_cast <Transition *>(selection.at(i));
2339                         if (tr->startPos() - info.startPos > GenTime(5, m_document->fps())) {
2340                             if (tr->startPos() < info.endPos) info.endPos = tr->startPos();
2341                         } else transitionAccepted = false;
2342                     }
2343                 }
2344                 int transitiontrack = getPreviousVideoTrack(info.track);
2345                 if (transitionAccepted) slotAddTransition(item, info, transitiontrack, transition);
2346                 else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
2347             }
2348         }
2349 }
2350
2351 void CustomTrackView::slotAddTransition(ClipItem* /*clip*/, ItemInfo transitionInfo, int endTrack, QDomElement transition)
2352 {
2353     if (transitionInfo.startPos >= transitionInfo.endPos) {
2354         emit displayMessage(i18n("Invalid transition"), ErrorMessage);
2355         return;
2356     }
2357     AddTransitionCommand* command = new AddTransitionCommand(this, transitionInfo, endTrack, transition, false, true);
2358     m_commandStack->push(command);
2359     setDocumentModified();
2360 }
2361
2362 void CustomTrackView::addTransition(ItemInfo transitionInfo, int endTrack, QDomElement params, bool refresh)
2363 {
2364     Transition *tr = new Transition(transitionInfo, endTrack, m_document->fps(), params, true);
2365     //kDebug() << "---- ADDING transition " << params.attribute("value");
2366     if (m_document->renderer()->mltAddTransition(tr->transitionTag(), endTrack, m_document->tracksCount() - transitionInfo.track, transitionInfo.startPos, transitionInfo.endPos, tr->toXML(), refresh)) {
2367         scene()->addItem(tr);
2368         setDocumentModified();
2369     } else {
2370         emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
2371         delete tr;
2372     }
2373 }
2374
2375 void CustomTrackView::deleteTransition(ItemInfo transitionInfo, int endTrack, QDomElement /*params*/, bool refresh)
2376 {
2377     Transition *item = getTransitionItemAt(transitionInfo.startPos, transitionInfo.track);
2378     if (!item) {
2379         emit displayMessage(i18n("Select clip to delete"), ErrorMessage);
2380         return;
2381     }
2382     m_document->renderer()->mltDeleteTransition(item->transitionTag(), endTrack, m_document->tracksCount() - transitionInfo.track, transitionInfo.startPos, transitionInfo.endPos, item->toXML(), refresh);
2383     if (m_dragItem == item) m_dragItem = NULL;
2384
2385 #if QT_VERSION >= 0x040600
2386     // animate item deletion
2387     item->closeAnimation();
2388 #else
2389     delete item;
2390 #endif
2391     emit transitionItemSelected(NULL);
2392     setDocumentModified();
2393 }
2394
2395 void CustomTrackView::slotTransitionUpdated(Transition *tr, QDomElement old)
2396 {
2397     //kDebug() << "TRANS UPDATE, TRACKS: " << old.attribute("transition_btrack") << ", NEW: " << tr->toXML().attribute("transition_btrack");
2398     QDomElement xml = tr->toXML();
2399     if (old.isNull() || xml.isNull()) {
2400         emit displayMessage(i18n("Cannot update transition"), ErrorMessage);
2401         return;
2402     }
2403     EditTransitionCommand *command = new EditTransitionCommand(this, tr->track(), tr->startPos(), old, xml, false);
2404     updateTrackDuration(tr->track(), command);
2405     m_commandStack->push(command);
2406     setDocumentModified();
2407 }
2408
2409 void CustomTrackView::updateTransition(int track, GenTime pos, QDomElement oldTransition, QDomElement transition, bool updateTransitionWidget)
2410 {
2411     Transition *item = getTransitionItemAt(pos, track);
2412     if (!item) {
2413         kWarning() << "Unable to find transition at pos :" << pos.frames(m_document->fps()) << ", ON track: " << track;
2414         return;
2415     }
2416     
2417     bool force = false;
2418     if (oldTransition.attribute("transition_atrack") != transition.attribute("transition_atrack") || oldTransition.attribute("transition_btrack") != transition.attribute("transition_btrack"))
2419         force = true;
2420     m_document->renderer()->mltUpdateTransition(oldTransition.attribute("tag"), transition.attribute("tag"), transition.attribute("transition_btrack").toInt(), m_document->tracksCount() - transition.attribute("transition_atrack").toInt(), item->startPos(), item->endPos(), transition, force);
2421     //kDebug() << "ORIGINAL TRACK: "<< oldTransition.attribute("transition_btrack") << ", NEW TRACK: "<<transition.attribute("transition_btrack");
2422     item->setTransitionParameters(transition);
2423     if (updateTransitionWidget) {
2424         ItemInfo info = item->info();
2425         QPoint p;
2426         ClipItem *transitionClip = getClipItemAt(info.startPos, info.track);
2427         if (transitionClip && transitionClip->baseClip()) {
2428             QString size = transitionClip->baseClip()->getProperty("frame_size");
2429             double factor = transitionClip->baseClip()->getProperty("aspect_ratio").toDouble();
2430             if (factor == 0) factor = 1.0;
2431             p.setX((int)(size.section('x', 0, 0).toInt() * factor + 0.5));
2432             p.setY(size.section('x', 1, 1).toInt());
2433         }
2434         emit transitionItemSelected(item, getPreviousVideoTrack(info.track), p, true);
2435     }
2436     setDocumentModified();
2437 }
2438
2439 void CustomTrackView::dragMoveEvent(QDragMoveEvent * event)
2440 {
2441     if (m_clipDrag) {
2442         const QPointF pos = mapToScene(event->pos());
2443         if (m_selectionGroup) {
2444             m_selectionGroup->setPos(pos);
2445             emit mousePosition((int)(m_selectionGroup->scenePos().x() + 0.5));
2446             event->acceptProposedAction();
2447         } else {
2448             // Drag enter was not possible, try again at mouse position
2449             insertDropClips(event->mimeData(), event->pos());
2450             event->accept();
2451         }
2452     } else {
2453         QGraphicsView::dragMoveEvent(event);
2454     }
2455 }
2456
2457 void CustomTrackView::dragLeaveEvent(QDragLeaveEvent * event)
2458 {
2459     if (m_selectionGroup && m_clipDrag) {
2460         m_thumbsTimer.stop();
2461         m_waitingThumbs.clear();
2462         QList<QGraphicsItem *> items = m_selectionGroup->childItems();
2463         qDeleteAll(items);
2464         scene()->destroyItemGroup(m_selectionGroup);
2465         m_selectionGroup = NULL;
2466     } else QGraphicsView::dragLeaveEvent(event);
2467 }
2468
2469 void CustomTrackView::dropEvent(QDropEvent * event)
2470 {
2471     if (m_selectionGroup && m_clipDrag) {
2472         QList<QGraphicsItem *> items = m_selectionGroup->childItems();
2473         resetSelectionGroup();
2474         m_scene->clearSelection();
2475         bool hasVideoClip = false;
2476         QUndoCommand *addCommand = new QUndoCommand();
2477         addCommand->setText(i18n("Add timeline clip"));
2478         QList <ClipItem *> brokenClips;
2479
2480         for (int i = 0; i < items.count(); i++) {
2481             ClipItem *item = static_cast <ClipItem *>(items.at(i));
2482             if (!hasVideoClip && (item->clipType() == AV || item->clipType() == VIDEO)) hasVideoClip = true;
2483             if (items.count() == 1) {
2484                 updateClipTypeActions(item);
2485             } else {
2486                 updateClipTypeActions(NULL);
2487             }
2488
2489             //TODO: take care of edit mode for undo
2490             item->baseClip()->addReference();
2491             //item->setZValue(item->defaultZValue());
2492             m_document->updateClip(item->baseClip()->getId());
2493             ItemInfo info = item->info();
2494
2495             int tracknumber = m_document->tracksCount() - info.track - 1;
2496             bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
2497             if (isLocked) item->setItemLocked(true);
2498             ItemInfo clipInfo = info;
2499             clipInfo.track = m_document->tracksCount() - item->track();
2500
2501             int worked = m_document->renderer()->mltInsertClip(clipInfo, item->xml(), item->baseClip()->getProducer(item->track()), m_scene->editMode() == OVERWRITEEDIT, m_scene->editMode() == INSERTEDIT);
2502             if (worked == -1) {
2503                 emit displayMessage(i18n("Cannot insert clip in timeline"), ErrorMessage);
2504                 brokenClips.append(item);
2505                 continue;
2506             }
2507             adjustTimelineClips(m_scene->editMode(), item, ItemInfo(), addCommand);
2508
2509             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), m_scene->editMode() == OVERWRITEEDIT, m_scene->editMode() == INSERTEDIT, false, false, addCommand);
2510             updateTrackDuration(info.track, addCommand);
2511
2512             if (item->baseClip()->isTransparent() && getTransitionItemAtStart(info.startPos, info.track) == NULL) {
2513                 // add transparency transition
2514                 QDomElement trans = MainWindow::transitions.getEffectByTag("composite", "composite").cloneNode().toElement();
2515                 new AddTransitionCommand(this, info, getPreviousVideoTrack(info.track), trans, false, true, addCommand);
2516             }
2517             item->setSelected(true);
2518         }
2519         qDeleteAll(brokenClips);
2520         brokenClips.clear();
2521         if (addCommand->childCount() > 0) m_commandStack->push(addCommand);
2522         else delete addCommand;
2523
2524         // Automatic audio split
2525         if (KdenliveSettings::splitaudio())
2526             splitAudio();
2527         setDocumentModified();
2528
2529         /*
2530         // debug info
2531         QRectF rect(0, 1 * m_tracksHeight + m_tracksHeight / 2, sceneRect().width(), 2);
2532         QList<QGraphicsItem *> selection = m_scene->items(rect);
2533         QStringList timelineList;
2534
2535         kDebug()<<"// ITEMS on TRACK: "<<selection.count();
2536         for (int i = 0; i < selection.count(); i++) {
2537                if (selection.at(i)->type() == AVWIDGET) {
2538                    ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
2539                    int start = clip->startPos().frames(m_document->fps());
2540                    int end = clip->endPos().frames(m_document->fps());
2541                    timelineList.append(QString::number(start) + "-" + QString::number(end));
2542             }
2543         }
2544         kDebug() << "// COMPARE:\n" << timelineList << "\n-------------------";
2545         */
2546
2547         m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
2548         if (items.count() > 1) {
2549             groupSelectedItems(true);
2550         } else if (items.count() == 1) {
2551             m_dragItem = static_cast <AbstractClipItem *>(items.at(0));
2552             emit clipItemSelected((ClipItem*)m_dragItem, false);
2553         }
2554         event->setDropAction(Qt::MoveAction);
2555         event->accept();
2556
2557         /// \todo enable when really working
2558 //        alignAudio();
2559
2560     } else QGraphicsView::dropEvent(event);
2561     setFocus();
2562 }
2563
2564 void CustomTrackView::adjustTimelineClips(EDITMODE mode, ClipItem *item, ItemInfo posinfo, QUndoCommand *command)
2565 {
2566     bool snap = KdenliveSettings::snaptopoints();
2567     KdenliveSettings::setSnaptopoints(false);
2568     if (mode == OVERWRITEEDIT) {
2569         // if we are in overwrite mode, move clips accordingly
2570         ItemInfo info;
2571         if (item == NULL) info = posinfo;
2572         else info = item->info();
2573         QRectF rect(info.startPos.frames(m_document->fps()), info.track * m_tracksHeight + m_tracksHeight / 2, (info.endPos - info.startPos).frames(m_document->fps()) - 1, 5);
2574         QList<QGraphicsItem *> selection = m_scene->items(rect);
2575         if (item) selection.removeAll(item);
2576         for (int i = 0; i < selection.count(); i++) {
2577             if (!selection.at(i)->isEnabled()) continue;
2578             if (selection.at(i)->type() == AVWIDGET) {
2579                 ClipItem *clip = static_cast<ClipItem *>(selection.at(i));
2580                 if (clip->startPos() < info.startPos) {
2581                     if (clip->endPos() > info.endPos) {
2582                         ItemInfo clipInfo = clip->info();
2583                         ItemInfo dupInfo = clipInfo;
2584                         GenTime diff = info.startPos - clipInfo.startPos;
2585                         dupInfo.startPos = info.startPos;
2586                         dupInfo.cropStart += diff;
2587                         dupInfo.cropDuration = clipInfo.endPos - info.startPos;
2588                         ItemInfo newdupInfo = dupInfo;
2589                         GenTime diff2 = info.endPos - info.startPos;
2590                         newdupInfo.startPos = info.endPos;
2591                         newdupInfo.cropStart += diff2;
2592                         newdupInfo.cropDuration = clipInfo.endPos - info.endPos;
2593                         new RazorClipCommand(this, clipInfo, info.startPos, false, command);
2594                         new ResizeClipCommand(this, dupInfo, newdupInfo, false, false, command);
2595                         ClipItem *dup = cutClip(clipInfo, info.startPos, true, false);
2596                         if (dup) dup->resizeStart(info.endPos.frames(m_document->fps()));
2597                     } else {
2598                         ItemInfo newclipInfo = clip->info();
2599                         newclipInfo.endPos = info.startPos;
2600                         new ResizeClipCommand(this, clip->info(), newclipInfo, false, false, command);
2601                         clip->resizeEnd(info.startPos.frames(m_document->fps()));
2602                     }
2603                 } else if (clip->endPos() <= info.endPos) {
2604                     new AddTimelineClipCommand(this, clip->xml(), clip->clipProducer(), clip->info(), clip->effectList(), false, false, false, true, command);
2605                     m_waitingThumbs.removeAll(clip);
2606                     scene()->removeItem(clip);
2607                     delete clip;
2608                     clip = NULL;
2609                 } else {
2610                     ItemInfo newclipInfo = clip->info();
2611                     newclipInfo.startPos = info.endPos;
2612                     new ResizeClipCommand(this, clip->info(), newclipInfo, false, false, command);
2613                     clip->resizeStart(info.endPos.frames(m_document->fps()));
2614                 }
2615             }
2616         }
2617     } else if (mode == INSERTEDIT) {
2618         // if we are in push mode, move clips accordingly
2619         ItemInfo info;
2620         if (item == NULL) info = posinfo;
2621         else info = item->info();
2622         QRectF rect(info.startPos.frames(m_document->fps()), info.track * m_tracksHeight + m_tracksHeight / 2, (info.endPos - info.startPos).frames(m_document->fps()) - 1, 5);
2623         QList<QGraphicsItem *> selection = m_scene->items(rect);
2624         if (item) selection.removeAll(item);
2625         for (int i = 0; i < selection.count(); i++) {
2626             if (selection.at(i)->type() == AVWIDGET) {
2627                 ClipItem *clip = static_cast<ClipItem *>(selection.at(i));
2628                 if (clip->startPos() < info.startPos) {
2629                     if (clip->endPos() > info.startPos) {
2630                         ItemInfo clipInfo = clip->info();
2631                         ItemInfo dupInfo = clipInfo;
2632                         GenTime diff = info.startPos - clipInfo.startPos;
2633                         dupInfo.startPos = info.startPos;
2634                         dupInfo.cropStart += diff;
2635                         dupInfo.cropDuration = clipInfo.endPos - info.startPos;
2636                         new RazorClipCommand(this, clipInfo, info.startPos, false, command);
2637                         // Commented out; variable dup unused. --granjow
2638                         //ClipItem *dup = cutClip(clipInfo, info.startPos, true, false);
2639                         cutClip(clipInfo, info.startPos, true, false);
2640                     }
2641                 }
2642                 // TODO: add insertspacecommand
2643             }
2644         }
2645     }
2646
2647     KdenliveSettings::setSnaptopoints(snap);
2648 }
2649
2650
2651 void CustomTrackView::adjustTimelineTransitions(EDITMODE mode, Transition *item, QUndoCommand *command)
2652 {
2653     if (mode == OVERWRITEEDIT) {
2654         // if we are in overwrite or push mode, move clips accordingly
2655         bool snap = KdenliveSettings::snaptopoints();
2656         KdenliveSettings::setSnaptopoints(false);
2657         ItemInfo info = item->info();
2658         QRectF rect(info.startPos.frames(m_document->fps()), info.track * m_tracksHeight + m_tracksHeight, (info.endPos - info.startPos).frames(m_document->fps()) - 1, 5);
2659         QList<QGraphicsItem *> selection = m_scene->items(rect);
2660         selection.removeAll(item);
2661         for (int i = 0; i < selection.count(); i++) {
2662             if (!selection.at(i)->isEnabled()) continue;
2663             if (selection.at(i)->type() == TRANSITIONWIDGET) {
2664                 Transition *tr = static_cast<Transition *>(selection.at(i));
2665                 if (tr->startPos() < info.startPos) {
2666                     ItemInfo firstPos = tr->info();
2667                     ItemInfo newPos = firstPos;
2668                     firstPos.endPos = item->startPos();
2669                     newPos.startPos = item->endPos();
2670                     new MoveTransitionCommand(this, tr->info(), firstPos, true, command);
2671                     if (tr->endPos() > info.endPos) {
2672                         // clone transition
2673                         new AddTransitionCommand(this, newPos, tr->transitionEndTrack(), tr->toXML(), false, true, command);
2674                     }
2675                 } else if (tr->endPos() > info.endPos) {
2676                     // just resize
2677                     ItemInfo firstPos = tr->info();
2678                     firstPos.startPos = item->endPos();
2679                     new MoveTransitionCommand(this, tr->info(), firstPos, true, command);
2680                 } else {
2681                     // remove transition
2682                     new AddTransitionCommand(this, tr->info(), tr->transitionEndTrack(), tr->toXML(), true, true, command);
2683                 }
2684             }
2685         }
2686         KdenliveSettings::setSnaptopoints(snap);
2687     }
2688 }
2689
2690 QStringList CustomTrackView::mimeTypes() const
2691 {
2692     QStringList qstrList;
2693     // list of accepted mime types for drop
2694     qstrList.append("text/plain");
2695     qstrList.append("kdenlive/producerslist");
2696     qstrList.append("kdenlive/clip");
2697     return qstrList;
2698 }
2699
2700 Qt::DropActions CustomTrackView::supportedDropActions() const
2701 {
2702     // returns what actions are supported when dropping
2703     return Qt::MoveAction;
2704 }
2705
2706 void CustomTrackView::setDuration(int duration)
2707 {
2708     int diff = qAbs(duration - sceneRect().width());
2709     if (diff * matrix().m11() > -50) {
2710         if (matrix().m11() < 0.4) setSceneRect(0, 0, (duration + 100 / matrix().m11()), sceneRect().height());
2711         else setSceneRect(0, 0, (duration + 300), sceneRect().height());
2712     }
2713     m_projectDuration = duration;
2714 }
2715
2716 int CustomTrackView::duration() const
2717 {
2718     return m_projectDuration;
2719 }
2720
2721 void CustomTrackView::addTrack(TrackInfo type, int ix)
2722 {
2723     QList <TransitionInfo> transitionInfos;
2724     if (ix == -1 || ix == m_document->tracksCount()) {
2725         m_document->insertTrack(0, type);
2726         transitionInfos = m_document->renderer()->mltInsertTrack(1, type.type == VIDEOTRACK);
2727     } else {
2728         m_document->insertTrack(m_document->tracksCount() - ix, type);
2729         // insert track in MLT playlist
2730         transitionInfos = m_document->renderer()->mltInsertTrack(m_document->tracksCount() - ix, type.type == VIDEOTRACK);
2731
2732         double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
2733         QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY);
2734         QList<QGraphicsItem *> selection = m_scene->items(r);
2735         resetSelectionGroup();
2736
2737         m_selectionGroup = new AbstractGroupItem(m_document->fps());
2738         scene()->addItem(m_selectionGroup);
2739         for (int i = 0; i < selection.count(); i++) {
2740             if ((!selection.at(i)->parentItem()) && (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET || selection.at(i)->type() == GROUPWIDGET)) {
2741                 m_selectionGroup->addToGroup(selection.at(i));
2742                 selection.at(i)->setFlag(QGraphicsItem::ItemIsMovable, false);
2743             }
2744         }
2745         // Move graphic items
2746         m_selectionGroup->translate(0, m_tracksHeight);
2747
2748         // adjust track number
2749         Mlt::Tractor *tractor = m_document->renderer()->lockService();
2750         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
2751         for (int i = 0; i < children.count(); i++) {
2752             if (children.at(i)->type() == GROUPWIDGET) {
2753                 AbstractGroupItem *grp = static_cast<AbstractGroupItem*>(children.at(i));
2754                 children << grp->childItems();
2755                 continue;
2756             }
2757             AbstractClipItem *item = static_cast <AbstractClipItem *>(children.at(i));
2758             item->updateItem();
2759             ItemInfo clipinfo = item->info();
2760             if (item->type() == AVWIDGET) {
2761                 ClipItem *clip = static_cast <ClipItem *>(item);
2762                 // slowmotion clips are not track dependant, so no need to update them
2763                 if (clip->speed() != 1.0) continue;
2764                 // We add a move clip command so that we get the correct producer for new track number
2765                 if (clip->clipType() == AV || clip->clipType() == AUDIO) {
2766                     Mlt::Producer *prod = clip->getProducer(clipinfo.track);
2767                     if (m_document->renderer()->mltUpdateClipProducer(tractor, (int)(m_document->tracksCount() - clipinfo.track), clipinfo.startPos.frames(m_document->fps()), prod) == false) {
2768                         // problem updating clip
2769                         emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", clipinfo.startPos.frames(m_document->fps()), clipinfo.track), ErrorMessage);
2770                     }
2771                 }
2772             } /*else if (item->type() == TRANSITIONWIDGET) {
2773                 Transition *tr = static_cast <Transition *>(item);
2774                 int track = tr->transitionEndTrack();
2775                 if (track >= ix) {
2776                     tr->updateTransitionEndTrack(getPreviousVideoTrack(clipinfo.track));
2777                 }
2778             }*/
2779         }
2780         // Sync transition tracks with MLT playlist
2781         Transition *tr;        
2782         TransitionInfo info;
2783         for (int i = 0; i < transitionInfos.count(); i++) {
2784             info = transitionInfos.at(i);
2785             tr = getTransitionItem(info);
2786             if (tr) tr->setForcedTrack(info.forceTrack, info.a_track);
2787             else kDebug()<<"// Cannot update TRANSITION AT: "<<info.b_track<<" / "<<info.startPos.frames(m_document->fps()); 
2788         }
2789         
2790         resetSelectionGroup(false);
2791         m_document->renderer()->unlockService(tractor);
2792     }
2793
2794     int maxHeight = m_tracksHeight * m_document->tracksCount() * matrix().m22();
2795     for (int i = 0; i < m_guides.count(); i++) {
2796         QLineF l = m_guides.at(i)->line();
2797         l.setP2(QPointF(l.x2(), maxHeight));
2798         m_guides.at(i)->setLine(l);
2799     }
2800
2801     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), maxHeight - 1);
2802     setSceneRect(0, 0, sceneRect().width(), m_tracksHeight * m_document->tracksCount());
2803     viewport()->update();
2804     //QTimer::singleShot(500, this, SIGNAL(trackHeightChanged()));
2805     //setFixedHeight(50 * m_tracksCount);
2806
2807     updateTrackNames(ix, true);
2808 }
2809
2810 void CustomTrackView::removeTrack(int ix)
2811 {
2812     // Delete track in MLT playlist
2813     m_document->renderer()->mltDeleteTrack(m_document->tracksCount() - ix);
2814     m_document->deleteTrack(m_document->tracksCount() - ix - 1);
2815
2816     double startY = ix * (m_tracksHeight + 1) + m_tracksHeight / 2;
2817     QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY);
2818     QList<QGraphicsItem *> selection = m_scene->items(r);
2819
2820     resetSelectionGroup();
2821
2822     m_selectionGroup = new AbstractGroupItem(m_document->fps());
2823     scene()->addItem(m_selectionGroup);
2824     for (int i = 0; i < selection.count(); i++) {
2825         if ((!selection.at(i)->parentItem()) && (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET || selection.at(i)->type() == GROUPWIDGET)) {
2826             m_selectionGroup->addToGroup(selection.at(i));
2827             selection.at(i)->setFlag(QGraphicsItem::ItemIsMovable, false);
2828         }
2829     }
2830     // Move graphic items
2831     qreal ydiff = 0 - (int) m_tracksHeight;
2832     m_selectionGroup->translate(0, ydiff);
2833     Mlt::Tractor *tractor = m_document->renderer()->lockService();
2834
2835     // adjust track number
2836     QList<QGraphicsItem *> children = m_selectionGroup->childItems();
2837     //kDebug() << "// FOUND CLIPS TO MOVE: " << children.count();
2838     for (int i = 0; i < children.count(); i++) {
2839         if (children.at(i)->type() == GROUPWIDGET) {
2840             AbstractGroupItem *grp = static_cast<AbstractGroupItem*>(children.at(i));
2841             children << grp->childItems();
2842             continue;
2843         }
2844         if (children.at(i)->type() == AVWIDGET) {
2845             ClipItem *clip = static_cast <ClipItem *>(children.at(i));
2846             clip->updateItem();
2847             ItemInfo clipinfo = clip->info();
2848             // We add a move clip command so that we get the correct producer for new track number
2849             if (clip->clipType() == AV || clip->clipType() == AUDIO || clip->clipType() == PLAYLIST) {
2850                 Mlt::Producer *prod = clip->getProducer(clipinfo.track);
2851                 if (prod == NULL || !m_document->renderer()->mltUpdateClipProducer(tractor, (int)(m_document->tracksCount() - clipinfo.track), clipinfo.startPos.frames(m_document->fps()), prod)) {
2852                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", clipinfo.startPos.frames(m_document->fps()), clipinfo.track), ErrorMessage);
2853                 }
2854             }
2855         } else if (children.at(i)->type() == TRANSITIONWIDGET) {
2856             Transition *tr = static_cast <Transition *>(children.at(i));
2857             tr->updateItem();
2858             int track = tr->transitionEndTrack();
2859             if (track >= ix) {
2860                 ItemInfo clipinfo = tr->info();
2861                 tr->updateTransitionEndTrack(getPreviousVideoTrack(clipinfo.track));
2862             }
2863         }
2864     }
2865     resetSelectionGroup(false);
2866     m_document->renderer()->unlockService(tractor);
2867
2868     int maxHeight = m_tracksHeight * m_document->tracksCount() * matrix().m22();
2869     for (int i = 0; i < m_guides.count(); i++) {
2870         QLineF l = m_guides.at(i)->line();
2871         l.setP2(QPointF(l.x2(), maxHeight));
2872         m_guides.at(i)->setLine(l);
2873     }
2874     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), maxHeight - 1);
2875     setSceneRect(0, 0, sceneRect().width(), m_tracksHeight * m_document->tracksCount());
2876
2877     m_selectedTrack = qMin(m_selectedTrack, m_document->tracksCount() - 1);
2878     viewport()->update();
2879
2880     updateTrackNames(ix, false);
2881     //QTimer::singleShot(500, this, SIGNAL(trackHeightChanged()));
2882 }
2883
2884 void CustomTrackView::configTracks(QList < TrackInfo > trackInfos)
2885 {
2886     for (int i = 0; i < trackInfos.count(); ++i) {
2887         m_document->setTrackType(i, trackInfos.at(i));
2888         m_document->renderer()->mltChangeTrackState(i + 1, m_document->trackInfoAt(i).isMute, m_document->trackInfoAt(i).isBlind);
2889         lockTrack(m_document->tracksCount() - i - 1, m_document->trackInfoAt(i).isLocked, false);
2890     }
2891
2892     viewport()->update();
2893     emit trackHeightChanged();
2894 }
2895
2896 void CustomTrackView::slotSwitchTrackAudio(int ix)
2897 {
2898     /*for (int i = 0; i < m_document->tracksCount(); i++)
2899         kDebug() << "TRK " << i << " STATE: " << m_document->trackInfoAt(i).isMute << m_document->trackInfoAt(i).isBlind;*/
2900     int tracknumber = m_document->tracksCount() - ix - 1;
2901     m_document->switchTrackAudio(tracknumber, !m_document->trackInfoAt(tracknumber).isMute);
2902     kDebug() << "NEXT TRK STATE: " << m_document->trackInfoAt(tracknumber).isMute << m_document->trackInfoAt(tracknumber).isBlind;
2903     m_document->renderer()->mltChangeTrackState(tracknumber + 1, m_document->trackInfoAt(tracknumber).isMute, m_document->trackInfoAt(tracknumber).isBlind);
2904     setDocumentModified();
2905 }
2906
2907 void CustomTrackView::slotSwitchTrackLock(int ix)
2908 {
2909     int tracknumber = m_document->tracksCount() - ix - 1;
2910     LockTrackCommand *command = new LockTrackCommand(this, ix, !m_document->trackInfoAt(tracknumber).isLocked);
2911     m_commandStack->push(command);
2912 }
2913
2914
2915 void CustomTrackView::lockTrack(int ix, bool lock, bool requestUpdate)
2916 {
2917     int tracknumber = m_document->tracksCount() - ix - 1;
2918     m_document->switchTrackLock(tracknumber, lock);
2919     if (requestUpdate)
2920         emit doTrackLock(ix, lock);
2921     AbstractClipItem *clip = NULL;
2922     QList<QGraphicsItem *> selection = m_scene->items(0, ix * m_tracksHeight + m_tracksHeight / 2, sceneRect().width(), m_tracksHeight / 2 - 2);
2923
2924     for (int i = 0; i < selection.count(); i++) {
2925         if (selection.at(i)->type() == GROUPWIDGET && (AbstractGroupItem *)selection.at(i) != m_selectionGroup) {
2926             if (selection.at(i)->parentItem() && m_selectionGroup) {
2927                 selection.removeAll((QGraphicsItem*)m_selectionGroup);
2928                 resetSelectionGroup();
2929             }
2930
2931             bool changeGroupLock = true;
2932             bool hasClipOnTrack = false;
2933             QList <QGraphicsItem *> children =  selection.at(i)->childItems();
2934             for (int j = 0; j < children.count(); ++j) {
2935                 if (children.at(j)->isSelected()) {
2936                     if (children.at(j)->type() == AVWIDGET)
2937                         emit clipItemSelected(NULL);
2938                     else if (children.at(j)->type() == TRANSITIONWIDGET)
2939                         emit transitionItemSelected(NULL);
2940                     else
2941                         continue;
2942                 }
2943
2944                 AbstractClipItem * child = static_cast <AbstractClipItem *>(children.at(j));
2945                 if (child == m_dragItem)
2946                     m_dragItem = NULL;
2947
2948                 // only unlock group, if it is not locked by another track too
2949                 if (!lock && child->track() != ix && m_document->trackInfoAt(m_document->tracksCount() - child->track() - 1).isLocked)
2950                     changeGroupLock = false;
2951                 
2952                 // only (un-)lock if at least one clip is on the track
2953                 if (child->track() == ix)
2954                     hasClipOnTrack = true;
2955             }
2956             if (changeGroupLock && hasClipOnTrack)
2957                 ((AbstractGroupItem*)selection.at(i))->setItemLocked(lock);
2958         } else if((selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET)) {
2959             if (selection.at(i)->parentItem()) {
2960                 if (selection.at(i)->parentItem() == m_selectionGroup) {
2961                     selection.removeAll((QGraphicsItem*)m_selectionGroup);
2962                     resetSelectionGroup();
2963                 } else {
2964                     // groups are handled separately
2965                     continue;
2966                 }
2967             }
2968
2969             if (selection.at(i)->isSelected()) {
2970                 if (selection.at(i)->type() == AVWIDGET)
2971                     emit clipItemSelected(NULL);
2972                 else
2973                     emit transitionItemSelected(NULL);
2974             }
2975             clip = static_cast <AbstractClipItem *>(selection.at(i));
2976             clip->setItemLocked(lock);
2977             if (clip == m_dragItem)
2978                 m_dragItem = NULL;
2979         }
2980     }
2981     kDebug() << "NEXT TRK STATE: " << m_document->trackInfoAt(tracknumber).isLocked;
2982     viewport()->update();
2983     setDocumentModified();
2984 }
2985
2986 void CustomTrackView::slotSwitchTrackVideo(int ix)
2987 {
2988     int tracknumber = m_document->tracksCount() - ix;
2989     m_document->switchTrackVideo(tracknumber - 1, !m_document->trackInfoAt(tracknumber - 1).isBlind);
2990     m_document->renderer()->mltChangeTrackState(tracknumber, m_document->trackInfoAt(tracknumber - 1).isMute, m_document->trackInfoAt(tracknumber - 1).isBlind);
2991     setDocumentModified();
2992 }
2993
2994 QList<QGraphicsItem *> CustomTrackView::checkForGroups(const QRectF &rect, bool *ok)
2995 {
2996     // Check there is no group going over several tracks there, or that would result in timeline corruption
2997     QList<QGraphicsItem *> selection = scene()->items(rect);
2998     *ok = true;
2999     int maxHeight = m_tracksHeight * 1.5;
3000     for (int i = 0; i < selection.count(); i++) {
3001         // Check that we don't try to move a group with clips on other tracks
3002         if (selection.at(i)->type() == GROUPWIDGET && (selection.at(i)->boundingRect().height() >= maxHeight)) {
3003             *ok = false;
3004             break;
3005         } else if (selection.at(i)->parentItem() && (selection.at(i)->parentItem()->boundingRect().height() >= maxHeight)) {
3006             *ok = false;
3007             break;
3008         }
3009     }
3010     return selection;
3011 }
3012
3013 void CustomTrackView::slotRemoveSpace()
3014 {
3015     GenTime pos;
3016     int track = 0;
3017     if (m_menuPosition.isNull()) {
3018         pos = GenTime(cursorPos(), m_document->fps());
3019
3020         TrackDialog d(m_document, parentWidget());
3021         d.comboTracks->setCurrentIndex(m_selectedTrack);
3022         d.label->setText(i18n("Track"));
3023         d.before_select->setHidden(true);
3024         d.setWindowTitle(i18n("Remove Space"));
3025         d.video_track->setHidden(true);
3026         d.audio_track->setHidden(true);
3027         if (d.exec() != QDialog::Accepted) return;
3028         track = d.comboTracks->currentIndex();
3029     } else {
3030         pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
3031         track = (int)(mapToScene(m_menuPosition).y() / m_tracksHeight);
3032     }
3033
3034     if (m_document->isTrackLocked(m_document->tracksCount() - track - 1)) {
3035         emit displayMessage(i18n("Cannot remove space in a locked track"), ErrorMessage);
3036         return;
3037     }
3038
3039     ClipItem *item = getClipItemAt(pos, track);
3040     if (item) {
3041         emit displayMessage(i18n("You must be in an empty space to remove space (time: %1, track: %2)", m_document->timecode().getTimecodeFromFrames(mapToScene(m_menuPosition).x()), track), ErrorMessage);
3042         return;
3043     }
3044     int length = m_document->renderer()->mltGetSpaceLength(pos, m_document->tracksCount() - track, true);
3045     if (length <= 0) {
3046         emit displayMessage(i18n("You must be in an empty space to remove space (time: %1, track: %2)", m_document->timecode().getTimecodeFromFrames(mapToScene(m_menuPosition).x()), track), ErrorMessage);
3047         return;
3048     }
3049
3050     // Make sure there is no group in the way
3051     QRectF rect(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - pos.frames(m_document->fps()), m_tracksHeight / 2 - 2);
3052
3053     bool isOk;
3054     QList<QGraphicsItem *> items = checkForGroups(rect, &isOk);
3055     if (!isOk) {
3056         // groups found on track, do not allow the move
3057         emit displayMessage(i18n("Cannot remove space in a track with a group"), ErrorMessage);
3058         return;
3059     }
3060
3061     QList<ItemInfo> clipsToMove;
3062     QList<ItemInfo> transitionsToMove;
3063
3064     for (int i = 0; i < items.count(); i++) {
3065         if (items.at(i)->type() == AVWIDGET || items.at(i)->type() == TRANSITIONWIDGET) {
3066             AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
3067             ItemInfo info = item->info();
3068             if (item->type() == AVWIDGET) {
3069                 clipsToMove.append(info);
3070             } else if (item->type() == TRANSITIONWIDGET) {
3071                 transitionsToMove.append(info);
3072             }
3073         }
3074     }
3075
3076     if (!transitionsToMove.isEmpty()) {
3077         // Make sure that by moving the items, we don't get a transition collision
3078         // Find first transition
3079         ItemInfo info = transitionsToMove.at(0);
3080         for (int i = 1; i < transitionsToMove.count(); i++)
3081             if (transitionsToMove.at(i).startPos < info.startPos) info = transitionsToMove.at(i);
3082
3083         // make sure there are no transitions on the way
3084         QRectF rect(info.startPos.frames(m_document->fps()) - length, track * m_tracksHeight + m_tracksHeight / 2, length - 1, m_tracksHeight / 2 - 2);
3085         items = scene()->items(rect);
3086         int transitionCorrection = -1;
3087         for (int i = 0; i < items.count(); i++) {
3088             if (items.at(i)->type() == TRANSITIONWIDGET) {
3089                 // There is a transition on the way
3090                 AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
3091                 int transitionEnd = item->endPos().frames(m_document->fps());
3092                 if (transitionEnd > transitionCorrection) transitionCorrection = transitionEnd;
3093             }
3094         }
3095
3096         if (transitionCorrection > 0) {
3097             // We need to fix the move length
3098             length = info.startPos.frames(m_document->fps()) - transitionCorrection;
3099         }
3100             
3101         // Make sure we don't send transition before 0
3102         if (info.startPos.frames(m_document->fps()) < length) {
3103             // reduce length to maximum possible
3104             length = info.startPos.frames(m_document->fps());
3105         }           
3106     }
3107
3108     InsertSpaceCommand *command = new InsertSpaceCommand(this, clipsToMove, transitionsToMove, track, GenTime(-length, m_document->fps()), true);
3109     updateTrackDuration(track, command);
3110     m_commandStack->push(command);
3111 }
3112
3113 void CustomTrackView::slotInsertSpace()
3114 {
3115     GenTime pos;
3116     int track = 0;
3117     if (m_menuPosition.isNull()) {
3118         pos = GenTime(cursorPos(), m_document->fps());
3119     } else {
3120         pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
3121         track = (int)(mapToScene(m_menuPosition).y() / m_tracksHeight) + 1;
3122     }
3123     SpacerDialog d(GenTime(65, m_document->fps()), m_document->timecode(), track, m_document->tracksList(), this);
3124     if (d.exec() != QDialog::Accepted) return;
3125     GenTime spaceDuration = d.selectedDuration();
3126     track = d.selectedTrack();
3127
3128     QList<QGraphicsItem *> items;
3129     if (track >= 0) {
3130         if (m_document->isTrackLocked(m_document->tracksCount() - track - 1)) {
3131             emit displayMessage(i18n("Cannot insert space in a locked track"), ErrorMessage);
3132             return;
3133         }
3134
3135         ClipItem *item = getClipItemAt(pos, track);
3136         if (item) pos = item->startPos();
3137
3138         // Make sure there is no group in the way
3139         QRectF rect(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - pos.frames(m_document->fps()), m_tracksHeight / 2 - 2);
3140         bool isOk;
3141         items = checkForGroups(rect, &isOk);
3142         if (!isOk) {
3143             // groups found on track, do not allow the move
3144             emit displayMessage(i18n("Cannot insert space in a track with a group"), ErrorMessage);
3145             return;
3146         }
3147     } else {
3148         QRectF rect(pos.frames(m_document->fps()), 0, sceneRect().width() - pos.frames(m_document->fps()), m_document->tracksCount() * m_tracksHeight);
3149         items = scene()->items(rect);
3150     }
3151
3152     QList<ItemInfo> clipsToMove;
3153     QList<ItemInfo> transitionsToMove;
3154
3155     for (int i = 0; i < items.count(); i++) {
3156         if (items.at(i)->type() == AVWIDGET || items.at(i)->type() == TRANSITIONWIDGET) {
3157             AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
3158             ItemInfo info = item->info();
3159             if (item->type() == AVWIDGET)
3160                 clipsToMove.append(info);
3161             else if (item->type() == TRANSITIONWIDGET)
3162                 transitionsToMove.append(info);
3163         }
3164     }
3165
3166     if (!clipsToMove.isEmpty() || !transitionsToMove.isEmpty()) {
3167         InsertSpaceCommand *command = new InsertSpaceCommand(this, clipsToMove, transitionsToMove, track, spaceDuration, true);
3168         updateTrackDuration(track, command);
3169         m_commandStack->push(command);
3170     }
3171 }
3172
3173 void CustomTrackView::insertSpace(QList<ItemInfo> clipsToMove, QList<ItemInfo> transToMove, int track, const GenTime &duration, const GenTime &offset)
3174 {
3175     int diff = duration.frames(m_document->fps());
3176     resetSelectionGroup();
3177     m_selectionGroup = new AbstractGroupItem(m_document->fps());
3178     scene()->addItem(m_selectionGroup);
3179     ClipItem *clip;
3180     Transition *transition;
3181
3182     // Create lists with start pos for each track
3183     QMap <int, int> trackClipStartList;
3184     QMap <int, int> trackTransitionStartList;
3185
3186     for (int i = 1; i < m_document->tracksCount() + 1; i++) {
3187         trackClipStartList[i] = -1;
3188         trackTransitionStartList[i] = -1;
3189     }
3190
3191     if (!clipsToMove.isEmpty()) for (int i = 0; i < clipsToMove.count(); i++) {
3192             clip = getClipItemAtStart(clipsToMove.at(i).startPos + offset, clipsToMove.at(i).track);
3193             if (clip) {
3194                 if (clip->parentItem()) {
3195                     m_selectionGroup->addToGroup(clip->parentItem());
3196                     clip->parentItem()->setFlag(QGraphicsItem::ItemIsMovable, false);
3197                 } else {
3198                     m_selectionGroup->addToGroup(clip);
3199                     clip->setFlag(QGraphicsItem::ItemIsMovable, false);
3200                 }
3201                 if (trackClipStartList.value(m_document->tracksCount() - clipsToMove.at(i).track) == -1 || clipsToMove.at(i).startPos.frames(m_document->fps()) < trackClipStartList.value(m_document->tracksCount() - clipsToMove.at(i).track))
3202                     trackClipStartList[m_document->tracksCount() - clipsToMove.at(i).track] = clipsToMove.at(i).startPos.frames(m_document->fps());
3203             } else emit {
3204                     displayMessage(i18n("Cannot move clip at position %1, track %2", m_document->timecode().getTimecodeFromFrames((clipsToMove.at(i).startPos + offset).frames(m_document->fps())), clipsToMove.at(i).track), ErrorMessage);
3205                 }
3206             }
3207     if (!transToMove.isEmpty()) for (int i = 0; i < transToMove.count(); i++) {
3208             transition = getTransitionItemAtStart(transToMove.at(i).startPos + offset, transToMove.at(i).track);
3209             if (transition) {
3210                 if (transition->parentItem()) {
3211                     m_selectionGroup->addToGroup(transition->parentItem());
3212                     transition->parentItem()->setFlag(QGraphicsItem::ItemIsMovable, false);
3213                 } else {
3214                     m_selectionGroup->addToGroup(transition);
3215                     transition->setFlag(QGraphicsItem::ItemIsMovable, false);
3216                 }
3217                 if (trackTransitionStartList.value(m_document->tracksCount() - transToMove.at(i).track) == -1 || transToMove.at(i).startPos.frames(m_document->fps()) < trackTransitionStartList.value(m_document->tracksCount() - transToMove.at(i).track))
3218                     trackTransitionStartList[m_document->tracksCount() - transToMove.at(i).track] = transToMove.at(i).startPos.frames(m_document->fps());
3219             } else emit displayMessage(i18n("Cannot move transition at position %1, track %2", m_document->timecode().getTimecodeFromFrames(transToMove.at(i).startPos.frames(m_document->fps())), transToMove.at(i).track), ErrorMessage);
3220         }
3221     m_selectionGroup->translate(diff, 0);
3222
3223     // update items coordinates
3224     QList<QGraphicsItem *> itemList = m_selectionGroup->childItems();
3225
3226     for (int i = 0; i < itemList.count(); i++) {
3227         if (itemList.at(i)->type() == AVWIDGET || itemList.at(i)->type() == TRANSITIONWIDGET) {
3228             static_cast < AbstractClipItem *>(itemList.at(i))->updateItem();
3229         } else if (itemList.at(i)->type() == GROUPWIDGET) {
3230             QList<QGraphicsItem *> children = itemList.at(i)->childItems();
3231             for (int j = 0; j < children.count(); j++) {
3232                 AbstractClipItem * clp = static_cast < AbstractClipItem *>(children.at(j));
3233                 clp->updateItem();
3234             }
3235         }
3236     }
3237     resetSelectionGroup(false);
3238     if (track != -1)
3239         track = m_document->tracksCount() - track;
3240     m_document->renderer()->mltInsertSpace(trackClipStartList, trackTransitionStartList, track, duration, offset);
3241 }
3242
3243 void CustomTrackView::deleteClip(const QString &clipId)
3244 {
3245     resetSelectionGroup();
3246     QList<QGraphicsItem *> itemList = items();
3247     QUndoCommand *deleteCommand = new QUndoCommand();
3248     int count = 0;
3249     for (int i = 0; i < itemList.count(); i++) {
3250         if (itemList.at(i)->type() == AVWIDGET) {
3251             ClipItem *item = (ClipItem *)itemList.at(i);
3252             if (item->clipProducer() == clipId) {
3253                 count++;
3254                 if (item->parentItem()) {
3255                     // Clip is in a group, destroy the group
3256                     new GroupClipsCommand(this, QList<ItemInfo>() << item->info(), QList<ItemInfo>(), false, deleteCommand);
3257                 }
3258                 new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), false, false, true, true, deleteCommand);
3259             }
3260         }
3261     }
3262     deleteCommand->setText(i18np("Delete timeline clip", "Delete timeline clips", count));
3263     if (count == 0) {
3264         delete deleteCommand;
3265     } else {
3266         updateTrackDuration(-1, deleteCommand);
3267         m_commandStack->push(deleteCommand);
3268     }
3269 }
3270
3271 void CustomTrackView::setCursorPos(int pos, bool seek)
3272 {
3273     if (pos == m_cursorPos) return;
3274     emit cursorMoved((int)(m_cursorPos), (int)(pos));
3275     m_cursorPos = pos;
3276     if (seek) m_document->renderer()->seek(m_cursorPos);
3277     else if (m_autoScroll) checkScrolling();
3278     m_cursorLine->setPos(m_cursorPos, 0);
3279 }
3280
3281 void CustomTrackView::updateCursorPos()
3282 {
3283     m_cursorLine->setPos(m_cursorPos, 0);
3284 }
3285
3286 int CustomTrackView::cursorPos()
3287 {
3288     return (int)(m_cursorPos);
3289 }
3290
3291 void CustomTrackView::moveCursorPos(int delta)
3292 {
3293     if (m_cursorPos + delta < 0) delta = 0 - m_cursorPos;
3294     emit cursorMoved((int)(m_cursorPos), (int)((m_cursorPos + delta)));
3295     m_cursorPos += delta;
3296     m_cursorLine->setPos(m_cursorPos, 0);
3297     m_document->renderer()->seek(m_cursorPos);
3298 }
3299
3300 void CustomTrackView::initCursorPos(int pos)
3301 {
3302     emit cursorMoved((int)(m_cursorPos), (int)(pos));
3303     m_cursorPos = pos;
3304     m_cursorLine->setPos(pos, 0);
3305     checkScrolling();
3306 }
3307
3308 void CustomTrackView::checkScrolling()
3309 {
3310     ensureVisible(m_cursorPos, verticalScrollBar()->value() + 10, 2, 2, 50, 0);
3311 }
3312
3313 void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
3314 {
3315     if (m_moveOpMode == SEEK) m_moveOpMode = NONE;
3316     QGraphicsView::mouseReleaseEvent(event);
3317     setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
3318 #if QT_VERSION >= 0x040600
3319     if (m_dragItem) m_dragItem->setGraphicsEffect(NULL);
3320 #endif
3321     if (m_scrollTimer.isActive()) m_scrollTimer.stop();
3322     if (event->button() == Qt::MidButton) {
3323         return;
3324     }
3325     setDragMode(QGraphicsView::NoDrag);
3326     if (m_operationMode == MOVEGUIDE) {
3327         setCursor(Qt::ArrowCursor);
3328         m_operationMode = NONE;
3329         m_dragGuide->setFlag(QGraphicsItem::ItemIsMovable, false);
3330         GenTime newPos = GenTime(m_dragGuide->pos().x(), m_document->fps());
3331         if (newPos != m_dragGuide->position()) {
3332             EditGuideCommand *command = new EditGuideCommand(this, m_dragGuide->position(), m_dragGuide->label(), newPos, m_dragGuide->label(), false);
3333             m_commandStack->push(command);
3334             m_dragGuide->updateGuide(GenTime(m_dragGuide->pos().x(), m_document->fps()));
3335             qSort(m_guides.begin(), m_guides.end(), sortGuidesList);
3336             m_document->syncGuides(m_guides);
3337         }
3338         m_dragGuide = NULL;
3339         m_dragItem = NULL;
3340         return;
3341     } else if (m_operationMode == SPACER && m_selectionGroup) {
3342         int track;
3343         if (event->modifiers() != Qt::ControlModifier) {
3344             // We are moving all tracks
3345             track = -1;
3346         } else track = (int)(mapToScene(m_clickEvent).y() / m_tracksHeight);
3347         GenTime timeOffset = GenTime((int)(m_selectionGroup->scenePos().x()), m_document->fps()) - m_selectionGroupInfo.startPos;
3348
3349         if (timeOffset != GenTime()) {
3350             QList<QGraphicsItem *> items = m_selectionGroup->childItems();
3351
3352             QList<ItemInfo> clipsToMove;
3353             QList<ItemInfo> transitionsToMove;
3354
3355             // Create lists with start pos for each track
3356             QMap <int, int> trackClipStartList;
3357             QMap <int, int> trackTransitionStartList;
3358
3359             for (int i = 1; i < m_document->tracksCount() + 1; i++) {
3360                 trackClipStartList[i] = -1;
3361                 trackTransitionStartList[i] = -1;
3362             }
3363
3364             for (int i = 0; i < items.count(); i++) {
3365                 if (items.at(i)->type() == GROUPWIDGET)
3366                     items += items.at(i)->childItems();
3367             }
3368
3369             for (int i = 0; i < items.count(); i++) {
3370                 if (items.at(i)->type() == AVWIDGET) {
3371                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
3372                     ItemInfo info = item->info();
3373                     clipsToMove.append(info);
3374                     item->updateItem();
3375                     if (trackClipStartList.value(m_document->tracksCount() - info.track) == -1 || info.startPos.frames(m_document->fps()) < trackClipStartList.value(m_document->tracksCount() - info.track))
3376                         trackClipStartList[m_document->tracksCount() - info.track] = info.startPos.frames(m_document->fps());
3377                 } else if (items.at(i)->type() == TRANSITIONWIDGET) {
3378                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
3379                     ItemInfo info = item->info();
3380                     transitionsToMove.append(info);
3381                     item->updateItem();
3382                     if (trackTransitionStartList.value(m_document->tracksCount() - info.track) == -1 || info.startPos.frames(m_document->fps()) < trackTransitionStartList.value(m_document->tracksCount() - info.track))
3383                         trackTransitionStartList[m_document->tracksCount() - info.track] = info.startPos.frames(m_document->fps());
3384                 }
3385             }
3386             if (!clipsToMove.isEmpty() || !transitionsToMove.isEmpty()) {
3387                 InsertSpaceCommand *command = new InsertSpaceCommand(this, clipsToMove, transitionsToMove, track, timeOffset, false);
3388                 updateTrackDuration(track, command);
3389                 m_commandStack->push(command);
3390                 if (track != -1) track = m_document->tracksCount() - track;
3391                 kDebug() << "SPACER TRACK:" << track;
3392                 m_document->renderer()->mltInsertSpace(trackClipStartList, trackTransitionStartList, track, timeOffset, GenTime());
3393                 setDocumentModified();
3394             }
3395         }
3396         resetSelectionGroup(false);
3397         m_operationMode = NONE;
3398     } else if (m_operationMode == RUBBERSELECTION) {
3399         resetSelectionGroup();
3400         groupSelectedItems();
3401         m_operationMode = NONE;
3402     }
3403
3404     if (m_dragItem == NULL && m_selectionGroup == NULL) {
3405         emit transitionItemSelected(NULL);
3406         return;
3407     }
3408     ItemInfo info;
3409     if (m_dragItem) info = m_dragItem->info();
3410
3411     if (m_operationMode == MOVE) {
3412         setCursor(Qt::OpenHandCursor);
3413         if (m_dragItem->parentItem() == 0) {
3414             // we are moving one clip, easy
3415             if (m_dragItem->type() == AVWIDGET && (m_dragItemInfo.startPos != info.startPos || m_dragItemInfo.track != info.track)) {
3416                 ClipItem *item = static_cast <ClipItem *>(m_dragItem);
3417                 Mlt::Producer *prod = item->getProducer(info.track);
3418                 bool success = m_document->renderer()->mltMoveClip((int)(m_document->tracksCount() - m_dragItemInfo.track), (int)(m_document->tracksCount() - info.track), (int) m_dragItemInfo.startPos.frames(m_document->fps()), (int)(info.startPos.frames(m_document->fps())), prod, m_scene->editMode() == OVERWRITEEDIT, m_scene->editMode() == INSERTEDIT);
3419
3420                 if (success) {
3421                     QUndoCommand *moveCommand = new QUndoCommand();
3422                     moveCommand->setText(i18n("Move clip"));
3423                     adjustTimelineClips(m_scene->editMode(), item, ItemInfo(), moveCommand);
3424
3425                     int tracknumber = m_document->tracksCount() - item->track() - 1;
3426                     bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
3427                     if (isLocked) item->setItemLocked(true);
3428                     new MoveClipCommand(this, m_dragItemInfo, info, false, moveCommand);
3429                     // Also move automatic transitions (on lower track)
3430                     Transition *startTransition = getTransitionItemAtStart(m_dragItemInfo.startPos, m_dragItemInfo.track);
3431                     ItemInfo startTrInfo;
3432                     ItemInfo newStartTrInfo;
3433                     bool moveStartTrans = false;
3434                     bool moveEndTrans = false;
3435                     if (startTransition && startTransition->isAutomatic()) {
3436                         startTrInfo = startTransition->info();
3437                         newStartTrInfo = startTrInfo;
3438                         newStartTrInfo.track = info.track;
3439                         newStartTrInfo.startPos = info.startPos;
3440                         if (m_dragItemInfo.track == info.track && !item->baseClip()->isTransparent() && getClipItemAtEnd(newStartTrInfo.endPos, m_document->tracksCount() - startTransition->transitionEndTrack())) {
3441                             // transition end should stay the same
3442                         } else {
3443                             // transition end should be adjusted to clip
3444                             newStartTrInfo.endPos = newStartTrInfo.endPos + (newStartTrInfo.startPos - startTrInfo.startPos);
3445                         }
3446                         if (newStartTrInfo.startPos < newStartTrInfo.endPos) moveStartTrans = true;
3447                     }
3448                     if (startTransition == NULL || startTransition->endPos() < m_dragItemInfo.endPos) {
3449                         // Check if there is a transition at clip end
3450                         Transition *tr = getTransitionItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track);
3451                         if (tr && tr->isAutomatic()) {
3452                             ItemInfo trInfo = tr->info();
3453                             ItemInfo newTrInfo = trInfo;
3454                             newTrInfo.track = info.track;
3455                             newTrInfo.endPos = m_dragItem->endPos();
3456                             if (m_dragItemInfo.track == info.track && !item->baseClip()->isTransparent() && getClipItemAtStart(trInfo.startPos, m_document->tracksCount() - tr->transitionEndTrack())) {
3457                                 // transition start should stay the same
3458                             } else {
3459                                 // transition start should be moved
3460                                 newTrInfo.startPos = newTrInfo.startPos + (newTrInfo.endPos - trInfo.endPos);
3461                             }
3462                             if (newTrInfo.startPos < newTrInfo.endPos) {
3463                                 moveEndTrans = true;
3464                                 if (moveStartTrans) {
3465                                     // we have to move both transitions, remove the start one so that there is no collision
3466                                     new AddTransitionCommand(this, startTrInfo, startTransition->transitionEndTrack(), startTransition->toXML(), true, true, moveCommand);
3467                                 }
3468                                 adjustTimelineTransitions(m_scene->editMode(), tr, moveCommand);
3469                                 new MoveTransitionCommand(this, trInfo, newTrInfo, true, moveCommand);
3470                                 if (moveStartTrans) {
3471                                     // re-add transition in correct place
3472                                     int transTrack = startTransition->transitionEndTrack();
3473                                     if (m_dragItemInfo.track != info.track && !startTransition->forcedTrack()) {
3474                                         transTrack = getPreviousVideoTrack(info.track);
3475                                     }
3476                                     adjustTimelineTransitions(m_scene->editMode(), startTransition, moveCommand);
3477                                     new AddTransitionCommand(this, newStartTrInfo, transTrack, startTransition->toXML(), false, true, moveCommand);
3478                                 }
3479                             }
3480                         }
3481                     }
3482
3483                     if (moveStartTrans && !moveEndTrans) {
3484                         adjustTimelineTransitions(m_scene->editMode(), startTransition, moveCommand);
3485                         new MoveTransitionCommand(this, startTrInfo, newStartTrInfo, true, moveCommand);
3486                     }
3487
3488                     // Also move automatic transitions (on upper track)
3489                     Transition *tr = getTransitionItemAtStart(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
3490                     if (m_dragItemInfo.track == info.track && tr && tr->isAutomatic() && (m_document->tracksCount() - tr->transitionEndTrack()) == m_dragItemInfo.track) {
3491                         ItemInfo trInfo = tr->info();
3492                         ItemInfo newTrInfo = trInfo;
3493                         newTrInfo.startPos = m_dragItem->startPos();
3494                         ClipItem * upperClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
3495                         if (!upperClip || !upperClip->baseClip()->isTransparent()) {
3496                             if (!getClipItemAtEnd(newTrInfo.endPos, tr->track())) {
3497                                 // transition end should be adjusted to clip on upper track
3498                                 newTrInfo.endPos = newTrInfo.endPos + (newTrInfo.startPos - trInfo.startPos);
3499                             }
3500                             if (newTrInfo.startPos < newTrInfo.endPos) {
3501                                 adjustTimelineTransitions(m_scene->editMode(), tr, moveCommand);
3502                                 new MoveTransitionCommand(this, trInfo, newTrInfo, true, moveCommand);
3503                             }
3504                         }
3505                     }
3506                     if (m_dragItemInfo.track == info.track && (tr == NULL || tr->endPos() < m_dragItemInfo.endPos)) {
3507                         // Check if there is a transition at clip end
3508                         tr = getTransitionItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track - 1);
3509                         if (tr && tr->isAutomatic() && (m_document->tracksCount() - tr->transitionEndTrack()) == m_dragItemInfo.track) {
3510                             ItemInfo trInfo = tr->info();
3511                             ItemInfo newTrInfo = trInfo;
3512                             newTrInfo.endPos = m_dragItem->endPos();
3513                             kDebug() << "CLIP ENDS AT: " << newTrInfo.endPos.frames(25);
3514                             kDebug() << "CLIP STARTS AT: " << newTrInfo.startPos.frames(25);
3515                             ClipItem * upperClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
3516                             if (!upperClip || !upperClip->baseClip()->isTransparent()) {
3517                                 if (!getClipItemAtStart(trInfo.startPos, tr->track())) {
3518                                     // transition start should be moved
3519                                     newTrInfo.startPos = newTrInfo.startPos + (newTrInfo.endPos - trInfo.endPos);
3520                                 }
3521                                 if (newTrInfo.startPos < newTrInfo.endPos) {
3522                                     adjustTimelineTransitions(m_scene->editMode(), tr, moveCommand);
3523                                     new MoveTransitionCommand(this, trInfo, newTrInfo, true, moveCommand);
3524                                 }
3525                             }
3526                         }
3527                     }
3528                     updateTrackDuration(info.track, moveCommand);
3529                     if (m_dragItemInfo.track != info.track)
3530                         updateTrackDuration(m_dragItemInfo.track, moveCommand);
3531                     m_commandStack->push(moveCommand);
3532                     //checkTrackSequence(m_dragItem->track());
3533                 } else {
3534                     // undo last move and emit error message
3535                     bool snap = KdenliveSettings::snaptopoints();
3536                     KdenliveSettings::setSnaptopoints(false);
3537                     item->setPos((int) m_dragItemInfo.startPos.frames(m_document->fps()), (int)(m_dragItemInfo.track * m_tracksHeight + 1));
3538                     KdenliveSettings::setSnaptopoints(snap);
3539                     emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(m_dragItemInfo.startPos.frames(m_document->fps()))), ErrorMessage);
3540                 }
3541                 setDocumentModified();
3542             } else if (m_dragItem->type() == TRANSITIONWIDGET && (m_dragItemInfo.startPos != info.startPos || m_dragItemInfo.track != info.track)) {
3543                 Transition *transition = static_cast <Transition *>(m_dragItem);
3544                 transition->updateTransitionEndTrack(getPreviousVideoTrack(m_dragItem->track()));
3545                 if (!m_document->renderer()->mltMoveTransition(transition->transitionTag(), (int)(m_document->tracksCount() - m_dragItemInfo.track), (int)(m_document->tracksCount() - m_dragItem->track()), transition->transitionEndTrack(), m_dragItemInfo.startPos, m_dragItemInfo.endPos, info.startPos, info.endPos)) {
3546                     // Moving transition failed, revert to previous position
3547                     emit displayMessage(i18n("Cannot move transition"), ErrorMessage);
3548                     transition->setPos((int) m_dragItemInfo.startPos.frames(m_document->fps()), (m_dragItemInfo.track) * m_tracksHeight + 1);
3549                 } else {
3550                     QUndoCommand *moveCommand = new QUndoCommand();
3551                     moveCommand->setText(i18n("Move transition"));
3552                     adjustTimelineTransitions(m_scene->editMode(), transition, moveCommand);
3553                     new MoveTransitionCommand(this, m_dragItemInfo, info, false, moveCommand);
3554                     updateTrackDuration(info.track, moveCommand);
3555                     if (m_dragItemInfo.track != info.track)
3556                         updateTrackDuration(m_dragItemInfo.track, moveCommand);
3557                     m_commandStack->push(moveCommand);
3558                     setDocumentModified();
3559                 }
3560             }
3561         } else {
3562             // Moving several clips. We need to delete them and readd them to new position,
3563             // or they might overlap each other during the move
3564             QGraphicsItemGroup *group;
3565             if (m_selectionGroup)
3566                 group = static_cast <QGraphicsItemGroup *>(m_selectionGroup);
3567             else
3568                 group = static_cast <QGraphicsItemGroup *>(m_dragItem->parentItem());
3569             QList<QGraphicsItem *> items = group->childItems();
3570
3571             QList<ItemInfo> clipsToMove;
3572             QList<ItemInfo> transitionsToMove;
3573
3574             GenTime timeOffset = GenTime(m_dragItem->scenePos().x(), m_document->fps()) - m_dragItemInfo.startPos;
3575             const int trackOffset = (int)(m_dragItem->scenePos().y() / m_tracksHeight) - m_dragItemInfo.track;
3576
3577             QUndoCommand *moveGroup = new QUndoCommand();
3578             moveGroup->setText(i18n("Move group"));
3579             if (timeOffset != GenTime() || trackOffset != 0) {
3580                 // remove items in MLT playlist
3581
3582                 // Expand groups
3583                 int max = items.count();
3584                 for (int i = 0; i < max; i++) {
3585                     if (items.at(i)->type() == GROUPWIDGET) {
3586                         items += items.at(i)->childItems();
3587                     }
3588                 }
3589                 m_document->renderer()->blockSignals(true);
3590                 for (int i = 0; i < items.count(); i++) {
3591                     if (items.at(i)->type() != AVWIDGET && items.at(i)->type() != TRANSITIONWIDGET) continue;
3592                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
3593                     ItemInfo info = item->info();
3594                     if (item->type() == AVWIDGET) {
3595                         if (m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, info.startPos) == false) {
3596                             // error, clip cannot be removed from playlist
3597                             emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(info.startPos.frames(m_document->fps())), info.track), ErrorMessage);
3598                         } else {
3599                             clipsToMove.append(info);
3600                         }
3601                     } else {
3602                         transitionsToMove.append(info);
3603                         Transition *tr = static_cast <Transition*>(item);
3604                         m_document->renderer()->mltDeleteTransition(tr->transitionTag(), tr->transitionEndTrack(), m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
3605                     }
3606                 }
3607                 m_document->renderer()->blockSignals(false);
3608                 for (int i = 0; i < items.count(); i++) {
3609                     // re-add items in correct place
3610                     if (items.at(i)->type() != AVWIDGET && items.at(i)->type() != TRANSITIONWIDGET) continue;
3611                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
3612                     item->updateItem();
3613                     ItemInfo info = item->info();
3614                     int tracknumber = m_document->tracksCount() - info.track - 1;
3615                     bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
3616                     if (isLocked) {
3617                         group->removeFromGroup(item);
3618                         item->setItemLocked(true);
3619                     }
3620
3621                     if (item->type() == AVWIDGET) {
3622                         ClipItem *clip = static_cast <ClipItem*>(item);
3623                         int trackProducer = info.track;
3624                         info.track = m_document->tracksCount() - info.track;
3625                         adjustTimelineClips(m_scene->editMode(), clip, ItemInfo(), moveGroup);
3626                         m_document->renderer()->mltInsertClip(info, clip->xml(), clip->getProducer(trackProducer), m_scene->editMode() == OVERWRITEEDIT, m_scene->editMode() == INSERTEDIT);
3627                         for (int i = 0; i < clip->effectsCount(); i++) {
3628                             m_document->renderer()->mltAddEffect(info.track, info.startPos, getEffectArgs(clip->effectAt(i)), false);
3629                         }
3630                     } else {
3631                         Transition *tr = static_cast <Transition*>(item);
3632                         int newTrack = tr->transitionEndTrack();
3633                         if (!tr->forcedTrack()) {
3634                             newTrack = getPreviousVideoTrack(info.track);
3635                         }
3636                         tr->updateTransitionEndTrack(newTrack);
3637                         adjustTimelineTransitions(m_scene->editMode(), tr, moveGroup);
3638                         m_document->renderer()->mltAddTransition(tr->transitionTag(), newTrack, m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
3639                     }
3640                 }
3641                 new MoveGroupCommand(this, clipsToMove, transitionsToMove, timeOffset, trackOffset, false, moveGroup);
3642                 updateTrackDuration(-1, moveGroup);
3643                 m_commandStack->push(moveGroup);
3644
3645                 //QPointF top = group->sceneBoundingRect().topLeft();
3646                 //QPointF oldpos = m_selectionGroup->scenePos();
3647                 //kDebug()<<"SELECTION GRP POS: "<<m_selectionGroup->scenePos()<<", TOP: "<<top;
3648                 //group->setPos(top);
3649                 //TODO: get rid of the 3 lines below
3650                 if (m_selectionGroup) {
3651                     m_selectionGroupInfo.startPos = GenTime(m_selectionGroup->scenePos().x(), m_document->fps());
3652                     m_selectionGroupInfo.track = m_selectionGroup->track();
3653
3654                     for (int i = 0; i < items.count(); ++i) {
3655                         if (items.at(i)->type() == GROUPWIDGET) {
3656                             rebuildGroup((AbstractGroupItem*)items.at(i));
3657                             items.removeAt(i);
3658                             --i;
3659                         }
3660                     }
3661                     for (int i = 0; i < items.count(); ++i) {
3662                         if (items.at(i)) {
3663                             items.at(i)->setSelected(true);
3664                             if (items.at(i)->parentItem())
3665                                 items.at(i)->parentItem()->setSelected(true);
3666                         }
3667                     }
3668                     resetSelectionGroup();
3669                     groupSelectedItems();
3670                 } else {
3671                     rebuildGroup((AbstractGroupItem *)group);
3672                 }
3673                 setDocumentModified();
3674             }
3675         }
3676         m_document->renderer()->doRefresh();
3677     } else if (m_operationMode == RESIZESTART && m_dragItem->startPos() != m_dragItemInfo.startPos) {
3678         // resize start
3679         if (!m_controlModifier && m_dragItem->type() == AVWIDGET && m_dragItem->parentItem() && m_dragItem->parentItem() != m_selectionGroup) {
3680             AbstractGroupItem *parent = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
3681             if (parent) {
3682                 QUndoCommand *resizeCommand = new QUndoCommand();
3683                 resizeCommand->setText(i18n("Resize group"));
3684                 QList <QGraphicsItem *> items = parent->childItems();
3685                 QList <ItemInfo> infos = parent->resizeInfos();
3686                 parent->clearResizeInfos();
3687                 int itemcount = 0;
3688                 for (int i = 0; i < items.count(); ++i) {
3689                     AbstractClipItem *item = static_cast<AbstractClipItem *>(items.at(i));
3690                     if (item && item->type() == AVWIDGET) {
3691                         ItemInfo info = infos.at(itemcount);
3692                         prepareResizeClipStart(item, info, item->startPos().frames(m_document->fps()), false, resizeCommand);
3693                         ++itemcount;
3694                     }
3695                 }
3696                 m_commandStack->push(resizeCommand);
3697             }
3698         } else {
3699             prepareResizeClipStart(m_dragItem, m_dragItemInfo, m_dragItem->startPos().frames(m_document->fps()));
3700         }
3701     } else if (m_operationMode == RESIZEEND && m_dragItem->endPos() != m_dragItemInfo.endPos) {
3702         // resize end
3703         if (!m_controlModifier && m_dragItem->type() == AVWIDGET && m_dragItem->parentItem() && m_dragItem->parentItem() != m_selectionGroup) {
3704             AbstractGroupItem *parent = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
3705             if (parent) {
3706                 QUndoCommand *resizeCommand = new QUndoCommand();
3707                 resizeCommand->setText(i18n("Resize group"));
3708                 QList <QGraphicsItem *> items = parent->childItems();
3709                 QList <ItemInfo> infos = parent->resizeInfos();
3710                 parent->clearResizeInfos();
3711                 int itemcount = 0;
3712                 for (int i = 0; i < items.count(); ++i) {
3713                     AbstractClipItem *item = static_cast<AbstractClipItem *>(items.at(i));
3714                     if (item && item->type() == AVWIDGET) {
3715                         ItemInfo info = infos.at(itemcount);
3716                         prepareResizeClipEnd(item, info, item->endPos().frames(m_document->fps()), false, resizeCommand);
3717                         ++itemcount;
3718                     }
3719                 }
3720                 updateTrackDuration(-1, resizeCommand);
3721                 m_commandStack->push(resizeCommand);
3722             }
3723         } else {
3724             prepareResizeClipEnd(m_dragItem, m_dragItemInfo, m_dragItem->endPos().frames(m_document->fps()));
3725         }
3726     } else if (m_operationMode == FADEIN) {
3727         // resize fade in effect
3728         ClipItem * item = static_cast <ClipItem *>(m_dragItem);
3729         int ix = item->hasEffect("volume", "fadein");
3730         int ix2 = item->hasEffect("", "fade_from_black");
3731         if (ix != -1) {
3732             QDomElement oldeffect = item->effectAt(ix);
3733             int start = item->cropStart().frames(m_document->fps());
3734             int end = item->fadeIn();
3735             if (end == 0) {
3736                 slotDeleteEffect(item, -1, oldeffect, false);
3737             } else {
3738                 end += start;
3739                 QDomElement effect = oldeffect.cloneNode().toElement();
3740                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
3741                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
3742                 slotUpdateClipEffect(item, -1, effect, oldeffect, ix);
3743                 emit clipItemSelected(item);
3744             }
3745         } else if (item->fadeIn() != 0 && ix2 == -1) {
3746             QDomElement effect;
3747             if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) {
3748                 // add video fade
3749                 effect = MainWindow::videoEffects.getEffectByTag("", "fade_from_black").cloneNode().toElement();
3750             } else effect = MainWindow::audioEffects.getEffectByTag("volume", "fadein").cloneNode().toElement();
3751             EffectsList::setParameter(effect, "out", QString::number(item->fadeIn()));
3752             slotAddEffect(effect, m_dragItem->startPos(), m_dragItem->track());
3753         }
3754         if (ix2 != -1) {
3755             QDomElement oldeffect = item->effectAt(ix2);
3756             int start = item->cropStart().frames(m_document->fps());
3757             int end = item->fadeIn();
3758             if (end == 0) {
3759                 slotDeleteEffect(item, -1, oldeffect, false);
3760             } else {
3761                 end += start;
3762                 QDomElement effect = oldeffect.cloneNode().toElement();
3763                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
3764                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
3765                 slotUpdateClipEffect(item, -1, effect, oldeffect, ix2);
3766                 emit clipItemSelected(item);
3767             }
3768         }
3769     } else if (m_operationMode == FADEOUT) {
3770         // resize fade in effect
3771         ClipItem * item = static_cast <ClipItem *>(m_dragItem);
3772         int ix = item->hasEffect("volume", "fadeout");
3773         int ix2 = item->hasEffect("", "fade_to_black");
3774         if (ix != -1) {
3775             QDomElement oldeffect = item->effectAt(ix);
3776             int end = (item->cropDuration() + item->cropStart()).frames(m_document->fps());
3777             int start = item->fadeOut();
3778             if (start == 0) {
3779                 slotDeleteEffect(item, -1, oldeffect, false);
3780             } else {
3781                 start = end - start;
3782                 QDomElement effect = oldeffect.cloneNode().toElement();
3783                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
3784                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
3785                 // kDebug()<<"EDIT FADE OUT : "<<start<<"x"<<end;
3786                 slotUpdateClipEffect(item, -1, effect, oldeffect, ix);
3787                 emit clipItemSelected(item);
3788             }
3789         } else if (item->fadeOut() != 0 && ix2 == -1) {
3790             QDomElement effect;
3791             if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) {
3792                 // add video fade
3793                 effect = MainWindow::videoEffects.getEffectByTag("", "fade_to_black").cloneNode().toElement();
3794             } else effect = MainWindow::audioEffects.getEffectByTag("volume", "fadeout").cloneNode().toElement();
3795             EffectsList::setParameter(effect, "in", QString::number(item->fadeOut()));
3796             EffectsList::setParameter(effect, "out", QString::number(0));
3797             slotAddEffect(effect, m_dragItem->startPos(), m_dragItem->track());
3798         }
3799         if (ix2 != -1) {
3800             QDomElement oldeffect = item->effectAt(ix2);
3801             int end = (item->cropDuration() + item->cropStart()).frames(m_document->fps());
3802             int start = item->fadeOut();
3803             if (start == 0) {
3804                 slotDeleteEffect(item, -1, oldeffect, false);
3805             } else {
3806                 start = end - start;
3807                 QDomElement effect = oldeffect.cloneNode().toElement();
3808                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
3809                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
3810                 // kDebug()<<"EDIT FADE OUT : "<<start<<"x"<<end;
3811                 slotUpdateClipEffect(item, -1, effect, oldeffect, ix2);
3812                 emit clipItemSelected(item);
3813             }
3814         }
3815     } else if (m_operationMode == KEYFRAME) {
3816         // update the MLT effect
3817         ClipItem * item = static_cast <ClipItem *>(m_dragItem);
3818         QDomElement oldEffect = item->selectedEffect().cloneNode().toElement();
3819
3820         // check if we want to remove keyframe
3821         double val = mapToScene(event->pos()).toPoint().y();
3822         QRectF br = item->sceneBoundingRect();
3823         double maxh = 100.0 / br.height();
3824         val = (br.bottom() - val) * maxh;
3825         int start = item->cropStart().frames(m_document->fps());
3826         int end = (item->cropStart() + item->cropDuration()).frames(m_document->fps()) - 1;
3827
3828         if ((val < -50 || val > 150) && item->editedKeyFramePos() != start && item->editedKeyFramePos() != end && item->keyFrameNumber() > 1) {
3829             //delete keyframe
3830             item->movedKeyframe(item->getEffectAt(item->selectedEffectIndex()), item->selectedKeyFramePos(), -1, 0);
3831         } else {
3832             item->movedKeyframe(item->getEffectAt(item->selectedEffectIndex()), item->selectedKeyFramePos(), item->editedKeyFramePos(), item->editedKeyFrameValue());
3833         }
3834
3835         QDomElement newEffect = item->selectedEffect().cloneNode().toElement();
3836         //item->updateKeyframeEffect();
3837         //QString next = item->keyframes(item->selectedEffectIndex());
3838         //EditKeyFrameCommand *command = new EditKeyFrameCommand(this, item->track(), item->startPos(), item->selectedEffectIndex(), previous, next, false);
3839         EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), oldEffect, newEffect, item->selectedEffectIndex(), false, false);
3840
3841         m_commandStack->push(command);
3842         updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
3843         emit clipItemSelected(item);
3844     }
3845     if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET && m_dragItem->isSelected()) {
3846         // A transition is selected
3847         QPoint p;
3848         ClipItem *transitionClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track);
3849         if (transitionClip && transitionClip->baseClip()) {
3850             QString size = transitionClip->baseClip()->getProperty("frame_size");
3851             double factor = transitionClip->baseClip()->getProperty("aspect_ratio").toDouble();
3852             if (factor == 0) factor = 1.0;
3853             p.setX((int)(size.section('x', 0, 0).toInt() * factor + 0.5));
3854             p.setY(size.section('x', 1, 1).toInt());
3855         }
3856         emit transitionItemSelected(static_cast <Transition *>(m_dragItem), getPreviousVideoTrack(m_dragItem->track()), p);
3857     } else emit transitionItemSelected(NULL);
3858     if (m_operationMode != NONE && m_operationMode != MOVE) setDocumentModified();
3859     m_operationMode = NONE;
3860 }
3861
3862 void CustomTrackView::deleteClip(ItemInfo info, bool refresh)
3863 {
3864     ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()), info.track);
3865     m_ct++;
3866     if (!item) kDebug()<<"// PROBLEM FINDING CLIP ITEM TO REMOVVE!!!!!!!!!";
3867     else kDebug()<<"// deleting CLIP: "<<info.startPos.frames(m_document->fps())<<", "<<item->baseClip()->fileURL();
3868     //m_document->renderer()->saveSceneList(QString("/tmp/error%1.mlt").arg(m_ct), QDomElement());
3869     if (!item || m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, info.startPos) == false) {
3870         emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(info.startPos.frames(m_document->fps())), info.track), ErrorMessage);
3871         kDebug()<<"CANNOT REMOVE: "<<info.startPos.frames(m_document->fps())<<", TK: "<<info.track;
3872         //m_document->renderer()->saveSceneList(QString("/tmp/error%1.mlt").arg(m_ct), QDomElement());
3873         return;
3874     }
3875     m_waitingThumbs.removeAll(item);
3876     item->stopThumbs();
3877     if (item->isSelected()) emit clipItemSelected(NULL);
3878     item->baseClip()->removeReference();
3879     m_document->updateClip(item->baseClip()->getId());
3880
3881     /*if (item->baseClip()->isTransparent()) {
3882         // also remove automatic transition
3883         Transition *tr = getTransitionItemAt(info.startPos, info.track);
3884         if (tr && tr->isAutomatic()) {
3885             m_document->renderer()->mltDeleteTransition(tr->transitionTag(), tr->transitionEndTrack(), m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
3886             scene()->removeItem(tr);
3887             delete tr;
3888         }
3889     }*/
3890
3891     if (m_dragItem == item) m_dragItem = NULL;
3892
3893 #if QT_VERSION >= 0x040600
3894     // animate item deletion
3895     item->closeAnimation();
3896     /*if (refresh) item->closeAnimation();
3897     else {
3898         // no refresh, means we have several operations chained, we need to delete clip immediatly
3899         // so that it does not get in the way of the other
3900         delete item;
3901         item = NULL;
3902     }*/
3903 #else
3904     delete item;
3905     item = NULL;
3906 #endif
3907
3908     setDocumentModified();
3909     if (refresh) m_document->renderer()->doRefresh();
3910 }
3911
3912 void CustomTrackView::deleteSelectedClips()
3913 {
3914     resetSelectionGroup();
3915     QList<QGraphicsItem *> itemList = scene()->selectedItems();
3916     if (itemList.count() == 0) {
3917         emit displayMessage(i18n("Select clip to delete"), ErrorMessage);
3918         return;
3919     }
3920     scene()->clearSelection();
3921     QUndoCommand *deleteSelected = new QUndoCommand();
3922
3923     int groupCount = 0;
3924     int clipCount = 0;
3925     int transitionCount = 0;
3926     // expand & destroy groups
3927     for (int i = 0; i < itemList.count(); i++) {
3928         if (itemList.at(i)->type() == GROUPWIDGET) {
3929             groupCount++;
3930             QList<QGraphicsItem *> children = itemList.at(i)->childItems();
3931             QList <ItemInfo> clipInfos;
3932             QList <ItemInfo> transitionInfos;
3933             for (int j = 0; j < children.count(); j++) {
3934                 if (children.at(j)->type() == AVWIDGET) {
3935                     AbstractClipItem *clip = static_cast <AbstractClipItem *>(children.at(j));
3936                     if (!clip->isItemLocked()) clipInfos.append(clip->info());
3937                 } else if (children.at(j)->type() == TRANSITIONWIDGET) {
3938                     AbstractClipItem *clip = static_cast <AbstractClipItem *>(children.at(j));
3939                     if (!clip->isItemLocked()) transitionInfos.append(clip->info());
3940                 }
3941                 if (itemList.contains(children.at(j))) {
3942                     children.removeAt(j);
3943                     j--;
3944                 }
3945             }
3946             itemList += children;
3947             if (clipInfos.count() > 0)
3948                 new GroupClipsCommand(this, clipInfos, transitionInfos, false, deleteSelected);
3949
3950         } else if (itemList.at(i)->parentItem() && itemList.at(i)->parentItem()->type() == GROUPWIDGET)
3951             itemList.insert(i + 1, itemList.at(i)->parentItem());
3952     }
3953
3954     for (int i = 0; i < itemList.count(); i++) {
3955         if (itemList.at(i)->type() == AVWIDGET) {
3956             clipCount++;
3957             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
3958             //kDebug()<<"// DELETE CLP AT: "<<item->info().startPos.frames(25);
3959             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), false, false, true, true, deleteSelected);
3960             emit clipItemSelected(NULL);
3961         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
3962             transitionCount++;
3963             Transition *item = static_cast <Transition *>(itemList.at(i));
3964             //kDebug()<<"// DELETE TRANS AT: "<<item->info().startPos.frames(25);
3965             new AddTransitionCommand(this, item->info(), item->transitionEndTrack(), item->toXML(), true, true, deleteSelected);
3966             emit transitionItemSelected(NULL);
3967         }
3968     }
3969     if (groupCount > 0 && clipCount == 0 && transitionCount == 0)
3970         deleteSelected->setText(i18np("Delete selected group", "Delete selected groups", groupCount));
3971     else if (clipCount > 0 && groupCount == 0 && transitionCount == 0)
3972         deleteSelected->setText(i18np("Delete selected clip", "Delete selected clips", clipCount));
3973     else if (transitionCount > 0 && groupCount == 0 && clipCount == 0)
3974         deleteSelected->setText(i18np("Delete selected transition", "Delete selected transitions", transitionCount));
3975     else deleteSelected->setText(i18n("Delete selected items"));
3976     updateTrackDuration(-1, deleteSelected);
3977     m_commandStack->push(deleteSelected);
3978 }
3979
3980
3981 void CustomTrackView::doChangeClipSpeed(ItemInfo info, ItemInfo speedIndependantInfo, const double speed, const double oldspeed, int strobe, const QString &id)
3982 {
3983     Q_UNUSED(id)
3984     //DocClipBase *baseclip = m_document->clipManager()->getClipById(id);
3985
3986     ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()), info.track);
3987     if (!item) {
3988         kDebug() << "ERROR: Cannot find clip for speed change";
3989         emit displayMessage(i18n("Cannot find clip for speed change"), ErrorMessage);
3990         return;
3991     }
3992     info.track = m_document->tracksCount() - item->track();
3993     int endPos = m_document->renderer()->mltChangeClipSpeed(info, speedIndependantInfo, speed, oldspeed, strobe, item->getProducer(item->track(), false));
3994     if (endPos >= 0) {
3995         item->setSpeed(speed, strobe);
3996         item->updateRectGeometry();
3997         if (item->cropDuration().frames(m_document->fps()) != endPos)
3998             item->resizeEnd((int) info.startPos.frames(m_document->fps()) + endPos - 1);
3999         updatePositionEffects(item, info);
4000         setDocumentModified();
4001     } else {
4002         emit displayMessage(i18n("Invalid clip"), ErrorMessage);
4003     }
4004 }
4005
4006 void CustomTrackView::cutSelectedClips()
4007 {
4008     QList<QGraphicsItem *> itemList = scene()->selectedItems();
4009     QList<AbstractGroupItem *> groups;
4010     GenTime currentPos = GenTime(m_cursorPos, m_document->fps());
4011     for (int i = 0; i < itemList.count(); ++i) {
4012         if (!itemList.at(i))
4013             continue;
4014         if (itemList.at(i)->type() == AVWIDGET) {
4015             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
4016             if (item->parentItem() && item->parentItem() != m_selectionGroup) {
4017                 AbstractGroupItem *group = static_cast <AbstractGroupItem *>(item->parentItem());
4018                 if (!groups.contains(group))
4019                     groups << group;
4020             } else if (currentPos > item->startPos() && currentPos < item->endPos()) {
4021                 RazorClipCommand *command = new RazorClipCommand(this, item->info(), currentPos);
4022                 m_commandStack->push(command);
4023             }
4024         } else if (itemList.at(i)->type() == GROUPWIDGET && itemList.at(i) != m_selectionGroup) {
4025             AbstractGroupItem *group = static_cast<AbstractGroupItem *>(itemList.at(i));
4026             if (!groups.contains(group))
4027                 groups << group;
4028         }
4029     }
4030
4031     for (int i = 0; i < groups.count(); ++i)
4032         razorGroup(groups.at(i), currentPos);
4033 }
4034
4035 void CustomTrackView::razorGroup(AbstractGroupItem* group, GenTime cutPos)
4036 {
4037     if (group) {
4038         QList <QGraphicsItem *> children = group->childItems();
4039         QList <ItemInfo> clips1, transitions1;
4040         QList <ItemInfo> clipsCut, transitionsCut;
4041         QList <ItemInfo> clips2, transitions2;
4042         for (int i = 0; i < children.count(); ++i) {
4043             children.at(i)->setSelected(false);
4044             AbstractClipItem *child = static_cast <AbstractClipItem *>(children.at(i));
4045             if (child->type() == AVWIDGET) {
4046                 if (cutPos > child->endPos())
4047                     clips1 << child->info();
4048                 else if (cutPos < child->startPos())
4049                     clips2 << child->info();
4050                 else
4051                     clipsCut << child->info();
4052             } else {
4053                 if (cutPos > child->endPos())
4054                     transitions1 << child->info();
4055                 else if (cutPos < child->startPos())
4056                     transitions2 << child->info();
4057                 else
4058                     transitionsCut << child->info();
4059             }
4060         }
4061         if (clipsCut.isEmpty() && transitionsCut.isEmpty() && ((clips1.isEmpty() && transitions1.isEmpty()) || (clips2.isEmpty() && transitions2.isEmpty())))
4062             return;
4063         RazorGroupCommand *command = new RazorGroupCommand(this, clips1, transitions1, clipsCut, transitionsCut, clips2, transitions2, cutPos);
4064         m_commandStack->push(command);
4065     }
4066 }
4067
4068 void CustomTrackView::slotRazorGroup(QList <ItemInfo> clips1, QList <ItemInfo> transitions1, QList <ItemInfo> clipsCut, QList <ItemInfo> transitionsCut, QList <ItemInfo> clips2, QList <ItemInfo> transitions2, GenTime cutPos, bool cut)
4069 {
4070     if (cut) {
4071         for (int i = 0; i < clipsCut.count(); ++i) {
4072             ClipItem *clip = getClipItemAt(clipsCut.at(i).startPos.frames(m_document->fps()), clipsCut.at(i).track);
4073             if (clip) {
4074                 ClipItem *clipBehind = cutClip(clipsCut.at(i), cutPos, true);
4075                 clips1 << clip->info();
4076                 if (clipBehind != NULL)
4077                     clips2 << clipBehind->info();
4078             }
4079         }
4080         /* TODO: cut transitionsCut
4081          * For now just append them to group1 */
4082         transitions1 << transitionsCut;
4083         doGroupClips(clips1, transitions1, true);
4084         doGroupClips(clips2, transitions2, true);
4085     } else {
4086         /* we might also just use clipsCut.at(0)->parentItem().
4087          * Do this loop just in case something went wrong during cut */
4088         for (int i = 0; i < clipsCut.count(); ++i) {
4089             ClipItem *clip = getClipItemAt(cutPos.frames(m_document->fps()), clipsCut.at(i).track);
4090             if (clip && clip->parentItem() && clip->parentItem()->type() == GROUPWIDGET) {
4091                 AbstractGroupItem *group = static_cast <AbstractGroupItem *>(clip->parentItem());
4092                 QList <QGraphicsItem *> children = group->childItems();
4093                 QList <ItemInfo> groupClips;
4094                 QList <ItemInfo> groupTrans;
4095                 for (int j = 0; j < children.count(); ++j) {
4096                     if (children.at(j)->type() == AVWIDGET)
4097                         groupClips << ((AbstractClipItem *)children.at(j))->info();
4098                     else if (children.at(j)->type() == TRANSITIONWIDGET)
4099                         groupTrans << ((AbstractClipItem *)children.at(j))->info();
4100                 }
4101                 doGroupClips(groupClips, groupTrans, false);
4102                 break;
4103             }
4104         }
4105         for (int i = 0; i < clipsCut.count(); ++i)
4106             cutClip(clipsCut.at(i), cutPos, false);
4107         // TODO: uncut transitonsCut
4108         doGroupClips(QList <ItemInfo>() << clips1 << clipsCut << clips2, QList <ItemInfo>() << transitions1 << transitionsCut << transitions2, true);
4109     }
4110 }
4111
4112 void CustomTrackView::groupClips(bool group)
4113 {
4114     QList<QGraphicsItem *> itemList = scene()->selectedItems();
4115     QList <ItemInfo> clipInfos;
4116     QList <ItemInfo> transitionInfos;
4117
4118     // Expand groups
4119     int max = itemList.count();
4120     for (int i = 0; i < max; i++) {
4121         if (itemList.at(i)->type() == GROUPWIDGET) {
4122             itemList += itemList.at(i)->childItems();
4123         }
4124     }
4125
4126     for (int i = 0; i < itemList.count(); i++) {
4127         if (itemList.at(i)->type() == AVWIDGET) {
4128             AbstractClipItem *clip = static_cast <AbstractClipItem *>(itemList.at(i));
4129             if (!clip->isItemLocked()) clipInfos.append(clip->info());
4130         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
4131             AbstractClipItem *clip = static_cast <AbstractClipItem *>(itemList.at(i));
4132             if (!clip->isItemLocked()) transitionInfos.append(clip->info());
4133         }
4134     }
4135     if (clipInfos.count() > 0) {
4136         GroupClipsCommand *command = new GroupClipsCommand(this, clipInfos, transitionInfos, group);
4137         m_commandStack->push(command);
4138     }
4139 }
4140
4141 void CustomTrackView::doGroupClips(QList <ItemInfo> clipInfos, QList <ItemInfo> transitionInfos, bool group)
4142 {
4143     resetSelectionGroup();
4144     m_scene->clearSelection();
4145     if (!group) {
4146         for (int i = 0; i < clipInfos.count(); i++) {
4147             ClipItem *clip = getClipItemAt(clipInfos.at(i).startPos, clipInfos.at(i).track);
4148             if (clip == NULL) continue;
4149             if (clip->parentItem() && clip->parentItem()->type() == GROUPWIDGET) {
4150                 AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(clip->parentItem());
4151                 m_document->clipManager()->removeGroup(grp);
4152                 scene()->destroyItemGroup(grp);
4153             }
4154             clip->setFlag(QGraphicsItem::ItemIsMovable, true);
4155         }
4156         for (int i = 0; i < transitionInfos.count(); i++) {
4157             Transition *tr = getTransitionItemAt(transitionInfos.at(i).startPos, transitionInfos.at(i).track);
4158             if (tr == NULL) continue;
4159             if (tr->parentItem() && tr->parentItem()->type() == GROUPWIDGET) {
4160                 AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(tr->parentItem());
4161                 m_document->clipManager()->removeGroup(grp);
4162                 scene()->destroyItemGroup(grp);
4163             }
4164             tr->setFlag(QGraphicsItem::ItemIsMovable, true);
4165         }
4166         setDocumentModified();
4167         return;
4168     }
4169
4170     QList <QGraphicsItemGroup *> groups;
4171     for (int i = 0; i < clipInfos.count(); i++) {
4172         ClipItem *clip = getClipItemAt(clipInfos.at(i).startPos, clipInfos.at(i).track);
4173         if (clip) {
4174             clip->setSelected(true);
4175         }
4176     }
4177     for (int i = 0; i < transitionInfos.count(); i++) {
4178         Transition *clip = getTransitionItemAt(transitionInfos.at(i).startPos, transitionInfos.at(i).track);
4179         if (clip) {
4180             clip->setSelected(true);
4181         }
4182     }
4183     groupSelectedItems(false, true);
4184     setDocumentModified();
4185 }
4186
4187 void CustomTrackView::addClip(QDomElement xml, const QString &clipId, ItemInfo info, EffectsList effects, bool overwrite, bool push, bool refresh)
4188 {
4189     DocClipBase *baseclip = m_document->clipManager()->getClipById(clipId);
4190     if (baseclip == NULL) {
4191         emit displayMessage(i18n("No clip copied"), ErrorMessage);
4192         return;
4193     }
4194
4195     if (baseclip->getProducer() == NULL) {
4196         // If the clip has no producer, we must wait until it is created...
4197         m_mutex.lock();
4198         emit displayMessage(i18n("Waiting for clip..."), InformationMessage);
4199         emit forceClipProcessing(clipId);
4200         qApp->processEvents();
4201         for (int i = 0; i < 10; i++) {
4202             if (baseclip->getProducer() == NULL) {
4203                 qApp->processEvents();
4204                 m_producerNotReady.wait(&m_mutex, 200);
4205             } else break;
4206         }
4207         if (baseclip->getProducer() == NULL) {
4208             emit displayMessage(i18n("Cannot insert clip..."), ErrorMessage);
4209             m_mutex.unlock();
4210             return;
4211         }
4212         emit displayMessage(QString(), InformationMessage);
4213         m_mutex.unlock();
4214     }
4215
4216     ClipItem *item = new ClipItem(baseclip, info, m_document->fps(), xml.attribute("speed", "1").toDouble(), xml.attribute("strobe", "1").toInt(), getFrameWidth());
4217     item->setEffectList(effects);
4218     if (xml.hasAttribute("audio_only")) item->setAudioOnly(true);
4219     else if (xml.hasAttribute("video_only")) item->setVideoOnly(true);
4220     scene()->addItem(item);
4221
4222     int producerTrack = info.track;
4223     int tracknumber = m_document->tracksCount() - info.track - 1;
4224     bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
4225     if (isLocked) item->setItemLocked(true);
4226
4227     baseclip->addReference();
4228     m_document->updateClip(baseclip->getId());
4229     info.track = m_document->tracksCount() - info.track;
4230     m_document->renderer()->mltInsertClip(info, xml, item->getProducer(producerTrack), overwrite, push);
4231     for (int i = 0; i < item->effectsCount(); i++) {
4232         m_document->renderer()->mltAddEffect(info.track, info.startPos, getEffectArgs(item->effectAt(i)), false);
4233     }
4234     setDocumentModified();
4235     if (refresh)
4236         m_document->renderer()->doRefresh();
4237     if (!baseclip->isPlaceHolder())
4238         m_waitingThumbs.append(item);
4239     m_thumbsTimer.start();
4240 }
4241
4242 void CustomTrackView::slotUpdateClip(const QString &clipId, bool reload)
4243 {
4244     QMutexLocker locker(&m_mutex);
4245     QList<QGraphicsItem *> list = scene()->items();
4246     QList <ClipItem *>clipList;
4247     ClipItem *clip = NULL;
4248     DocClipBase *baseClip = NULL;
4249     Mlt::Tractor *tractor = m_document->renderer()->lockService();
4250     for (int i = 0; i < list.size(); ++i) {
4251         if (list.at(i)->type() == AVWIDGET) {
4252             clip = static_cast <ClipItem *>(list.at(i));
4253             if (clip->clipProducer() == clipId) {
4254                 if (baseClip == NULL) {
4255                     baseClip = clip->baseClip();
4256                 }
4257                 ItemInfo info = clip->info();
4258                 Mlt::Producer *prod = NULL;
4259                 if (clip->isAudioOnly()) prod = baseClip->audioProducer(info.track);
4260                 else if (clip->isVideoOnly()) prod = baseClip->videoProducer();
4261                 else prod = baseClip->getProducer(info.track);
4262                 if (reload && !m_document->renderer()->mltUpdateClip(tractor, info, clip->xml(), prod)) {
4263                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", info.startPos.frames(m_document->fps()), info.track), ErrorMessage);
4264                 }
4265                 else clipList.append(clip);
4266             }
4267         }
4268     }
4269     for (int i = 0; i < clipList.count(); i++)
4270         clipList.at(i)->refreshClip(true, true);
4271     m_document->renderer()->unlockService(tractor);
4272 }
4273
4274 ClipItem *CustomTrackView::getClipItemAtEnd(GenTime pos, int track)
4275 {
4276     int framepos = (int)(pos.frames(m_document->fps()));
4277     QList<QGraphicsItem *> list = scene()->items(QPointF(framepos - 1, track * m_tracksHeight + m_tracksHeight / 2));
4278     ClipItem *clip = NULL;
4279     for (int i = 0; i < list.size(); i++) {
4280         if (!list.at(i)->isEnabled()) continue;
4281         if (list.at(i)->type() == AVWIDGET) {
4282             ClipItem *test = static_cast <ClipItem *>(list.at(i));
4283             if (test->endPos() == pos) clip = test;
4284             break;
4285         }
4286     }
4287     return clip;
4288 }
4289
4290 ClipItem *CustomTrackView::getClipItemAtStart(GenTime pos, int track)
4291 {
4292     QList<QGraphicsItem *> list = scene()->items(QPointF(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2));
4293     ClipItem *clip = NULL;
4294     for (int i = 0; i < list.size(); i++) {
4295         if (!list.at(i)->isEnabled()) continue;
4296         if (list.at(i)->type() == AVWIDGET) {
4297             ClipItem *test = static_cast <ClipItem *>(list.at(i));
4298             if (test->startPos() == pos) clip = test;
4299             break;
4300         }
4301     }
4302     return clip;
4303 }
4304
4305 ClipItem *CustomTrackView::getClipItemAt(int pos, int track)
4306 {
4307     const QPointF p(pos, track * m_tracksHeight + m_tracksHeight / 2);
4308     QList<QGraphicsItem *> list = scene()->items(p);
4309     ClipItem *clip = NULL;
4310     for (int i = 0; i < list.size(); i++) {
4311         if (!list.at(i)->isEnabled()) continue;
4312         if (list.at(i)->type() == AVWIDGET) {
4313             clip = static_cast <ClipItem *>(list.at(i));
4314             break;
4315         }
4316     }
4317     return clip;
4318 }
4319
4320 ClipItem *CustomTrackView::getClipItemAt(GenTime pos, int track)
4321 {
4322     return getClipItemAt((int) pos.frames(m_document->fps()), track);
4323 }
4324
4325
4326 Transition *CustomTrackView::getTransitionItem(TransitionInfo info)
4327 {
4328     int pos = info.startPos.frames(m_document->fps());
4329     int track = m_document->tracksCount() - info.b_track;
4330     return getTransitionItemAt(pos, track);
4331 }
4332
4333 Transition *CustomTrackView::getTransitionItemAt(int pos, int track)
4334 {
4335     const QPointF p(pos, track * m_tracksHeight + Transition::itemOffset() + 1);
4336     QList<QGraphicsItem *> list = scene()->items(p);
4337     Transition *clip = NULL;
4338     for (int i = 0; i < list.size(); i++) {
4339         if (!list.at(i)->isEnabled()) continue;
4340         if (list.at(i)->type() == TRANSITIONWIDGET) {
4341             clip = static_cast <Transition *>(list.at(i));
4342             break;
4343         }
4344     }
4345     return clip;
4346 }
4347
4348 Transition *CustomTrackView::getTransitionItemAt(GenTime pos, int track)
4349 {
4350     return getTransitionItemAt(pos.frames(m_document->fps()), track);
4351 }
4352
4353 Transition *CustomTrackView::getTransitionItemAtEnd(GenTime pos, int track)
4354 {
4355     int framepos = (int)(pos.frames(m_document->fps()));
4356     const QPointF p(framepos - 1, track * m_tracksHeight + Transition::itemOffset() + 1);
4357     QList<QGraphicsItem *> list = scene()->items(p);
4358     Transition *clip = NULL;
4359     for (int i = 0; i < list.size(); i++) {
4360         if (!list.at(i)->isEnabled()) continue;
4361         if (list.at(i)->type() == TRANSITIONWIDGET) {
4362             Transition *test = static_cast <Transition *>(list.at(i));
4363             if (test->endPos() == pos) clip = test;
4364             break;
4365         }
4366     }
4367     return clip;
4368 }
4369
4370 Transition *CustomTrackView::getTransitionItemAtStart(GenTime pos, int track)
4371 {
4372     const QPointF p(pos.frames(m_document->fps()), track * m_tracksHeight + Transition::itemOffset() + 1);
4373     QList<QGraphicsItem *> list = scene()->items(p);
4374     Transition *clip = NULL;
4375     for (int i = 0; i < list.size(); ++i) {
4376         if (!list.at(i)->isEnabled()) continue;
4377         if (list.at(i)->type() == TRANSITIONWIDGET) {
4378             Transition *test = static_cast <Transition *>(list.at(i));
4379             if (test->startPos() == pos) clip = test;
4380             break;
4381         }
4382     }
4383     return clip;
4384 }
4385
4386 bool CustomTrackView::moveClip(const ItemInfo &start, const ItemInfo &end, bool refresh, ItemInfo *out_actualEnd)
4387 {
4388     if (m_selectionGroup) resetSelectionGroup(false);
4389     ClipItem *item = getClipItemAt((int) start.startPos.frames(m_document->fps()), start.track);
4390     if (!item) {
4391         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);
4392         kDebug() << "----------------  ERROR, CANNOT find clip to move at.. ";
4393         return false;
4394     }
4395     Mlt::Producer *prod = item->getProducer(end.track);
4396
4397 #ifdef DEBUG
4398     qDebug() << "Moving item " << (long)item << " from .. to:";
4399     qDebug() << item->info();
4400     qDebug() << start;
4401     qDebug() << end;
4402 #endif
4403     bool success = m_document->renderer()->mltMoveClip((int)(m_document->tracksCount() - start.track), (int)(m_document->tracksCount() - end.track),
4404                                                        (int) start.startPos.frames(m_document->fps()), (int)end.startPos.frames(m_document->fps()),
4405                                                        prod);
4406     if (success) {
4407         bool snap = KdenliveSettings::snaptopoints();
4408         KdenliveSettings::setSnaptopoints(false);
4409         item->setPos((int) end.startPos.frames(m_document->fps()), (int)(end.track * m_tracksHeight + 1));
4410
4411         int tracknumber = m_document->tracksCount() - end.track - 1;
4412         bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
4413         m_scene->clearSelection();
4414         if (isLocked) item->setItemLocked(true);
4415         else {
4416             if (item->isItemLocked()) item->setItemLocked(false);
4417             item->setSelected(true);
4418         }
4419         if (item->baseClip()->isTransparent()) {
4420             // Also move automatic transition
4421             Transition *tr = getTransitionItemAt(start.startPos, start.track);
4422             if (tr && tr->isAutomatic()) {
4423                 tr->updateTransitionEndTrack(getPreviousVideoTrack(end.track));
4424                 m_document->renderer()->mltMoveTransition(tr->transitionTag(), m_document->tracksCount() - start.track, m_document->tracksCount() - end.track, tr->transitionEndTrack(), start.startPos, start.endPos, end.startPos, end.endPos);
4425                 tr->setPos((int) end.startPos.frames(m_document->fps()), (int)(end.track * m_tracksHeight + 1));
4426             }
4427         }
4428         KdenliveSettings::setSnaptopoints(snap);
4429         setDocumentModified();
4430     } else {
4431         // undo last move and emit error message
4432         emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(end.startPos.frames(m_document->fps()))), ErrorMessage);
4433     }
4434     if (refresh) m_document->renderer()->doRefresh();
4435     if (out_actualEnd != NULL) {
4436         *out_actualEnd = item->info();
4437 #ifdef DEBUG
4438         qDebug() << "Actual end position updated:" << *out_actualEnd;
4439 #endif
4440     }
4441 #ifdef DEBUG
4442     qDebug() << item->info();
4443 #endif
4444     return success;
4445 }
4446
4447 void CustomTrackView::moveGroup(QList <ItemInfo> startClip, QList <ItemInfo> startTransition, const GenTime &offset, const int trackOffset, bool reverseMove)
4448 {
4449     // Group Items
4450     resetSelectionGroup();
4451     m_scene->clearSelection();
4452
4453     m_selectionGroup = new AbstractGroupItem(m_document->fps());
4454     scene()->addItem(m_selectionGroup);
4455
4456     m_document->renderer()->blockSignals(true);
4457     for (int i = 0; i < startClip.count(); i++) {
4458         if (reverseMove) {
4459             startClip[i].startPos = startClip.at(i).startPos - offset;
4460             startClip[i].track = startClip.at(i).track - trackOffset;
4461         }
4462         ClipItem *clip = getClipItemAt(startClip.at(i).startPos, startClip.at(i).track);
4463         if (clip) {
4464             clip->setItemLocked(false);
4465             if (clip->parentItem()) {
4466                 m_selectionGroup->addToGroup(clip->parentItem());
4467                 clip->parentItem()->setFlag(QGraphicsItem::ItemIsMovable, false);
4468             } else {
4469                 m_selectionGroup->addToGroup(clip);
4470                 clip->setFlag(QGraphicsItem::ItemIsMovable, false);
4471             }
4472             m_document->renderer()->mltRemoveClip(m_document->tracksCount() - startClip.at(i).track, startClip.at(i).startPos);
4473         } else kDebug() << "//MISSING CLIP AT: " << startClip.at(i).startPos.frames(25);
4474     }
4475     for (int i = 0; i < startTransition.count(); i++) {
4476         if (reverseMove) {
4477             startTransition[i].startPos = startTransition.at(i).startPos - offset;
4478             startTransition[i].track = startTransition.at(i).track - trackOffset;
4479         }
4480         Transition *tr = getTransitionItemAt(startTransition.at(i).startPos, startTransition.at(i).track);
4481         if (tr) {
4482             tr->setItemLocked(false);
4483             if (tr->parentItem()) {
4484                 m_selectionGroup->addToGroup(tr->parentItem());
4485                 tr->parentItem()->setFlag(QGraphicsItem::ItemIsMovable, false);
4486             } else {
4487                 m_selectionGroup->addToGroup(tr);
4488                 tr->setFlag(QGraphicsItem::ItemIsMovable, false);
4489             }
4490             m_document->renderer()->mltDeleteTransition(tr->transitionTag(), tr->transitionEndTrack(), m_document->tracksCount() - startTransition.at(i).track, startTransition.at(i).startPos, startTransition.at(i).endPos, tr->toXML());
4491         } else kDebug() << "//MISSING TRANSITION AT: " << startTransition.at(i).startPos.frames(25);
4492     }
4493     m_document->renderer()->blockSignals(false);
4494
4495     if (m_selectionGroup) {
4496         bool snap = KdenliveSettings::snaptopoints();
4497         KdenliveSettings::setSnaptopoints(false);
4498
4499         m_selectionGroup->translate(offset.frames(m_document->fps()), trackOffset *(qreal) m_tracksHeight);
4500         //m_selectionGroup->moveBy(offset.frames(m_document->fps()), trackOffset *(qreal) m_tracksHeight);
4501
4502         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
4503         // Expand groups
4504         int max = children.count();
4505         for (int i = 0; i < max; i++) {
4506             if (children.at(i)->type() == GROUPWIDGET) {
4507                 children += children.at(i)->childItems();
4508                 //AbstractGroupItem *grp = static_cast<AbstractGroupItem *>(children.at(i));
4509                 //grp->moveBy(offset.frames(m_document->fps()), trackOffset *(qreal) m_tracksHeight);
4510                 /*m_document->clipManager()->removeGroup(grp);
4511                 m_scene->destroyItemGroup(grp);*/
4512                 children.removeAll(children.at(i));
4513                 i--;
4514             }
4515         }
4516
4517         for (int i = 0; i < children.count(); i++) {
4518             // re-add items in correct place
4519             if (children.at(i)->type() != AVWIDGET && children.at(i)->type() != TRANSITIONWIDGET) continue;
4520             AbstractClipItem *item = static_cast <AbstractClipItem *>(children.at(i));
4521             item->updateItem();
4522             ItemInfo info = item->info();
4523             int tracknumber = m_document->tracksCount() - info.track - 1;
4524             bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
4525             if (isLocked)
4526                 item->setItemLocked(true);
4527             else if (item->isItemLocked())
4528                 item->setItemLocked(false);
4529
4530             if (item->type() == AVWIDGET) {
4531                 ClipItem *clip = static_cast <ClipItem*>(item);
4532                 int trackProducer = info.track;
4533                 info.track = m_document->tracksCount() - info.track;
4534                 m_document->renderer()->mltInsertClip(info, clip->xml(), clip->getProducer(trackProducer));
4535                 for (int i = 0; i < clip->effectsCount(); i++) {
4536                     m_document->renderer()->mltAddEffect(info.track, info.startPos, getEffectArgs(clip->effectAt(i)), false);
4537                 }
4538             } else if (item->type() == TRANSITIONWIDGET) {
4539                 Transition *tr = static_cast <Transition*>(item);
4540                 int newTrack;
4541                 if (!tr->forcedTrack()) {
4542                     newTrack = getPreviousVideoTrack(info.track);
4543                 } else {
4544                     newTrack = tr->transitionEndTrack() + trackOffset;
4545                     if (newTrack < 0 || newTrack > m_document->tracksCount()) newTrack = getPreviousVideoTrack(info.track);
4546                 }
4547                 tr->updateTransitionEndTrack(newTrack);
4548                 m_document->renderer()->mltAddTransition(tr->transitionTag(), newTrack, m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
4549             }
4550         }
4551
4552         resetSelectionGroup(false);
4553
4554         for (int i = 0; i < children.count(); i++) {
4555             if (children.at(i)->parentItem())
4556                 rebuildGroup((AbstractGroupItem*)children.at(i)->parentItem());
4557         }
4558         clearSelection();
4559
4560         KdenliveSettings::setSnaptopoints(snap);
4561         m_document->renderer()->doRefresh();
4562     } else kDebug() << "///////// WARNING; NO GROUP TO MOVE";
4563 }
4564
4565 void CustomTrackView::moveTransition(const ItemInfo &start, const ItemInfo &end, bool refresh)
4566 {
4567     Transition *item = getTransitionItemAt(start.startPos, start.track);
4568     if (!item) {
4569         emit displayMessage(i18n("Cannot move transition at time: %1 on track %2", m_document->timecode().getTimecodeFromFrames(start.startPos.frames(m_document->fps())), start.track), ErrorMessage);
4570         kDebug() << "----------------  ERROR, CANNOT find transition to move... ";// << startPos.x() * m_scale * FRAME_SIZE + 1 << ", " << startPos.y() * m_tracksHeight + m_tracksHeight / 2;
4571         return;
4572     }
4573
4574     bool snap = KdenliveSettings::snaptopoints();
4575     KdenliveSettings::setSnaptopoints(false);
4576
4577     if (end.endPos - end.startPos == start.endPos - start.startPos) {
4578         // Transition was moved
4579         item->setPos((int) end.startPos.frames(m_document->fps()), (end.track) * m_tracksHeight + 1);
4580     } else if (end.endPos == start.endPos) {
4581         // Transition start resize
4582         item->resizeStart((int) end.startPos.frames(m_document->fps()));
4583     } else if (end.startPos == start.startPos) {
4584         // Transition end resize;
4585         kDebug() << "// resize END: " << end.endPos.frames(m_document->fps());
4586         item->resizeEnd((int) end.endPos.frames(m_document->fps()));
4587     } else {
4588         // Move & resize
4589         item->setPos((int) end.startPos.frames(m_document->fps()), (end.track) * m_tracksHeight + 1);
4590         item->resizeStart((int) end.startPos.frames(m_document->fps()));
4591         item->resizeEnd((int) end.endPos.frames(m_document->fps()));
4592     }
4593     //item->moveTransition(GenTime((int) (endPos.x() - startPos.x()), m_document->fps()));
4594     KdenliveSettings::setSnaptopoints(snap);
4595     item->updateTransitionEndTrack(getPreviousVideoTrack(end.track));
4596     m_document->renderer()->mltMoveTransition(item->transitionTag(), m_document->tracksCount() - start.track, m_document->tracksCount() - item->track(), item->transitionEndTrack(), start.startPos, start.endPos, item->startPos(), item->endPos());
4597     if (m_dragItem && m_dragItem == item) {
4598         QPoint p;
4599         ClipItem *transitionClip = getClipItemAt(item->startPos(), item->track());
4600         if (transitionClip && transitionClip->baseClip()) {
4601             QString size = transitionClip->baseClip()->getProperty("frame_size");
4602             double factor = transitionClip->baseClip()->getProperty("aspect_ratio").toDouble();
4603             if (factor == 0) factor = 1.0;
4604             p.setX((int)(size.section('x', 0, 0).toInt() * factor + 0.5));
4605             p.setY(size.section('x', 1, 1).toInt());
4606         }
4607         emit transitionItemSelected(item, getPreviousVideoTrack(item->track()), p);
4608     }
4609     if (refresh) m_document->renderer()->doRefresh();
4610     setDocumentModified();
4611 }
4612
4613 void CustomTrackView::resizeClip(const ItemInfo &start, const ItemInfo &end, bool dontWorry)
4614 {
4615     bool resizeClipStart = (start.startPos != end.startPos);
4616     ClipItem *item = getClipItemAtStart(start.startPos, start.track);
4617     if (!item) {
4618         if (dontWorry) return;
4619         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);
4620         kDebug() << "----------------  ERROR, CANNOT find clip to resize at... "; // << startPos;
4621         return;
4622     }
4623     if (item->parentItem()) {
4624         // Item is part of a group, reset group
4625         resetSelectionGroup();
4626     }
4627
4628     bool snap = KdenliveSettings::snaptopoints();
4629     KdenliveSettings::setSnaptopoints(false);
4630
4631     if (resizeClipStart) {
4632         ItemInfo clipinfo = item->info();
4633         clipinfo.track = m_document->tracksCount() - clipinfo.track;
4634         bool success = m_document->renderer()->mltResizeClipStart(clipinfo, end.startPos - clipinfo.startPos);
4635         if (success)
4636             item->resizeStart((int) end.startPos.frames(m_document->fps()));
4637         else
4638             emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
4639     } else {
4640         ItemInfo clipinfo = item->info();
4641         clipinfo.track = m_document->tracksCount() - clipinfo.track;
4642         bool success = m_document->renderer()->mltResizeClipEnd(clipinfo, end.endPos - clipinfo.startPos);
4643         if (success)
4644             item->resizeEnd((int) end.endPos.frames(m_document->fps()));
4645         else
4646             emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
4647     }
4648     if (!resizeClipStart && end.cropStart != start.cropStart) {
4649         kDebug() << "// RESIZE CROP, DIFF: " << (end.cropStart - start.cropStart).frames(25);
4650         ItemInfo clipinfo = end;
4651         clipinfo.track = m_document->tracksCount() - end.track;
4652         bool success = m_document->renderer()->mltResizeClipCrop(clipinfo, end.cropStart - start.cropStart);
4653         if (success) {
4654             item->setCropStart(end.cropStart);
4655             item->resetThumbs(true);
4656         }
4657     }
4658     m_document->renderer()->doRefresh();
4659     KdenliveSettings::setSnaptopoints(snap);
4660     setDocumentModified();
4661 }
4662
4663 void CustomTrackView::prepareResizeClipStart(AbstractClipItem* item, ItemInfo oldInfo, int pos, bool check, QUndoCommand *command)
4664 {
4665     if (pos == oldInfo.startPos.frames(m_document->fps()))
4666         return;
4667     bool snap = KdenliveSettings::snaptopoints();
4668     if (check) {
4669         KdenliveSettings::setSnaptopoints(false);
4670         item->resizeStart(pos);
4671         if (item->startPos().frames(m_document->fps()) != pos) {
4672             item->resizeStart(oldInfo.startPos.frames(m_document->fps()));
4673             emit displayMessage(i18n("Not possible to resize"), ErrorMessage);
4674             KdenliveSettings::setSnaptopoints(snap);
4675             return;
4676         }
4677         KdenliveSettings::setSnaptopoints(snap);
4678     }
4679
4680     bool hasParentCommand = false;
4681     if (command) {
4682         hasParentCommand = true;
4683     } else {
4684         command = new QUndoCommand();
4685         command->setText(i18n("Resize clip start"));
4686     }
4687
4688     // do this here, too, because otherwise undo won't update the group
4689     if (item->parentItem() && item->parentItem() != m_selectionGroup)
4690         new RebuildGroupCommand(this, item->info().track, item->endPos() - GenTime(1, m_document->fps()), command);
4691
4692     ItemInfo info = item->info();
4693     if (item->type() == AVWIDGET) {
4694         ItemInfo resizeinfo = oldInfo;
4695         resizeinfo.track = m_document->tracksCount() - resizeinfo.track;
4696         bool success = m_document->renderer()->mltResizeClipStart(resizeinfo, item->startPos() - oldInfo.startPos);
4697         if (success) {
4698             // Check if there is an automatic transition on that clip (lower track)
4699             Transition *transition = getTransitionItemAtStart(oldInfo.startPos, oldInfo.track);
4700             if (transition && transition->isAutomatic()) {
4701                 ItemInfo trInfo = transition->info();
4702                 ItemInfo newTrInfo = trInfo;
4703                 newTrInfo.startPos = item->startPos();
4704                 if (newTrInfo.startPos < newTrInfo.endPos)
4705                     new MoveTransitionCommand(this, trInfo, newTrInfo, true, command);
4706             }
4707             // Check if there is an automatic transition on that clip (upper track)
4708             transition = getTransitionItemAtStart(oldInfo.startPos, oldInfo.track - 1);
4709             if (transition && transition->isAutomatic() && (m_document->tracksCount() - transition->transitionEndTrack()) == oldInfo.track) {
4710                 ItemInfo trInfo = transition->info();
4711                 ItemInfo newTrInfo = trInfo;
4712                 newTrInfo.startPos = item->startPos();
4713                 ClipItem * upperClip = getClipItemAt(oldInfo.startPos, oldInfo.track - 1);
4714                 if ((!upperClip || !upperClip->baseClip()->isTransparent()) && newTrInfo.startPos < newTrInfo.endPos)
4715                     new MoveTransitionCommand(this, trInfo, newTrInfo, true, command);
4716             }
4717
4718             ClipItem *clip = static_cast < ClipItem * >(item);
4719
4720             // Hack:
4721             // Since we must always resize clip before updating the keyframes, we
4722             // put a resize command before & after checking keyframes so that
4723             // we are sure the resize is performed before whenever we do or undo the action
4724             new ResizeClipCommand(this, oldInfo, info, false, true, command);
4725             adjustEffects(clip, oldInfo, command);
4726             new ResizeClipCommand(this, oldInfo, info, false, true, command);
4727             emit clipItemSelected(clip);
4728         } else {
4729             KdenliveSettings::setSnaptopoints(false);
4730             item->resizeStart((int) oldInfo.startPos.frames(m_document->fps()));
4731             KdenliveSettings::setSnaptopoints(snap);
4732             emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
4733         }
4734     } else if (item->type() == TRANSITIONWIDGET) {
4735         Transition *transition = static_cast <Transition *>(item);
4736         if (!m_document->renderer()->mltMoveTransition(transition->transitionTag(), (int)(m_document->tracksCount() - oldInfo.track), (int)(m_document->tracksCount() - oldInfo.track), transition->transitionEndTrack(), oldInfo.startPos, oldInfo.endPos, info.startPos, info.endPos)) {
4737             // Cannot resize transition
4738             KdenliveSettings::setSnaptopoints(false);
4739             transition->resizeStart((int) oldInfo.startPos.frames(m_document->fps()));
4740             KdenliveSettings::setSnaptopoints(snap);
4741             emit displayMessage(i18n("Cannot resize transition"), ErrorMessage);
4742         } else {
4743             MoveTransitionCommand *moveCommand = new MoveTransitionCommand(this, oldInfo, info, false, command);
4744             if (command == NULL)
4745                 m_commandStack->push(moveCommand);
4746         }
4747
4748     }
4749     if (item->parentItem() && item->parentItem() != m_selectionGroup)
4750         new RebuildGroupCommand(this, item->info().track, item->endPos() - GenTime(1, m_document->fps()), command);
4751
4752     if (!hasParentCommand)
4753         m_commandStack->push(command);
4754 }
4755
4756 void CustomTrackView::prepareResizeClipEnd(AbstractClipItem* item, ItemInfo oldInfo, int pos, bool check, QUndoCommand *command)
4757 {
4758     if (pos == oldInfo.endPos.frames(m_document->fps()))
4759         return;
4760     bool snap = KdenliveSettings::snaptopoints();
4761     if (check) {
4762         KdenliveSettings::setSnaptopoints(false);
4763         item->resizeEnd(pos);
4764         if (item->endPos().frames(m_document->fps()) != pos) {
4765             item->resizeEnd(oldInfo.endPos.frames(m_document->fps()));
4766             emit displayMessage(i18n("Not possible to resize"), ErrorMessage);
4767             KdenliveSettings::setSnaptopoints(snap);
4768             return;
4769         }
4770         KdenliveSettings::setSnaptopoints(snap);
4771     }
4772
4773     bool hasParentCommand = false;
4774     if (command) {
4775         hasParentCommand = true;
4776     } else {
4777         command = new QUndoCommand();
4778     }
4779
4780     // do this here, too, because otherwise undo won't update the group
4781     if (item->parentItem() && item->parentItem() != m_selectionGroup)
4782         new RebuildGroupCommand(this, item->info().track, item->startPos(), command);
4783
4784     ItemInfo info = item->info();
4785     if (item->type() == AVWIDGET) {
4786         if (!hasParentCommand) command->setText(i18n("Resize clip end"));
4787         ItemInfo resizeinfo = info;
4788         resizeinfo.track = m_document->tracksCount() - resizeinfo.track;
4789         bool success = m_document->renderer()->mltResizeClipEnd(resizeinfo, resizeinfo.cropDuration);
4790         if (success) {
4791             // Check if there is an automatic transition on that clip (lower track)
4792             Transition *tr = getTransitionItemAtEnd(oldInfo.endPos, oldInfo.track);
4793             if (tr && tr->isAutomatic()) {
4794                 ItemInfo trInfo = tr->info();
4795                 ItemInfo newTrInfo = trInfo;
4796                 newTrInfo.endPos = item->endPos();
4797                 if (newTrInfo.endPos > newTrInfo.startPos)
4798                     new MoveTransitionCommand(this, trInfo, newTrInfo, true, command);
4799             }
4800
4801             // Check if there is an automatic transition on that clip (upper track)
4802             tr = getTransitionItemAtEnd(oldInfo.endPos, oldInfo.track - 1);
4803             if (tr && tr->isAutomatic() && (m_document->tracksCount() - tr->transitionEndTrack()) == oldInfo.track) {
4804                 ItemInfo trInfo = tr->info();
4805                 ItemInfo newTrInfo = trInfo;
4806                 newTrInfo.endPos = item->endPos();
4807                 ClipItem * upperClip = getClipItemAtEnd(oldInfo.endPos, oldInfo.track - 1);
4808                 if ((!upperClip || !upperClip->baseClip()->isTransparent()) && newTrInfo.endPos > newTrInfo.startPos)
4809                     new MoveTransitionCommand(this, trInfo, newTrInfo, true, command);
4810
4811             }
4812
4813             ClipItem *clip = static_cast < ClipItem * >(item);
4814
4815             // Hack:
4816             // Since we must always resize clip before updating the keyframes, we
4817             // put a resize command before & after checking keyframes so that
4818             // we are sure the resize is performed before whenever we do or undo the action
4819             new ResizeClipCommand(this, oldInfo, info, false, true, command);
4820             adjustEffects(clip, oldInfo, command);
4821             new ResizeClipCommand(this, oldInfo, info, false, true, command);
4822             emit clipItemSelected(clip);
4823         } else {
4824             KdenliveSettings::setSnaptopoints(false);
4825             item->resizeEnd((int) oldInfo.endPos.frames(m_document->fps()));
4826             KdenliveSettings::setSnaptopoints(true);
4827             emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
4828         }
4829     } else if (item->type() == TRANSITIONWIDGET) {
4830         if (!hasParentCommand) command->setText(i18n("Resize transition end"));
4831         Transition *transition = static_cast <Transition *>(item);
4832         if (!m_document->renderer()->mltMoveTransition(transition->transitionTag(), (int)(m_document->tracksCount() - oldInfo.track), (int)(m_document->tracksCount() - oldInfo.track), transition->transitionEndTrack(), oldInfo.startPos, oldInfo.endPos, info.startPos, info.endPos)) {
4833             // Cannot resize transition
4834             KdenliveSettings::setSnaptopoints(false);
4835             transition->resizeEnd((int) oldInfo.endPos.frames(m_document->fps()));
4836             KdenliveSettings::setSnaptopoints(true);
4837             emit displayMessage(i18n("Cannot resize transition"), ErrorMessage);
4838         } else {
4839             // Check transition keyframes
4840             QDomElement old = transition->toXML();
4841             if (transition->updateKeyframes()) {
4842                 QDomElement xml = transition->toXML();
4843                 m_document->renderer()->mltUpdateTransition(xml.attribute("tag"), xml.attribute("tag"), xml.attribute("transition_btrack").toInt(), m_document->tracksCount() - xml.attribute("transition_atrack").toInt(), transition->startPos(), transition->endPos(), xml);
4844                 new EditTransitionCommand(this, transition->track(), transition->startPos(), old, xml, false, command);
4845             }
4846             new MoveTransitionCommand(this, oldInfo, info, false, command);
4847         }
4848     }
4849     if (item->parentItem() && item->parentItem() != m_selectionGroup)
4850         new RebuildGroupCommand(this, item->info().track, item->startPos(), command);
4851
4852     if (!hasParentCommand) {
4853         updateTrackDuration(oldInfo.track, command);
4854         m_commandStack->push(command);
4855     }
4856 }
4857
4858 void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool standalone)
4859 {
4860     int end = item->fadeIn();
4861     if (end != 0) {
4862         // there is a fade in effect
4863         int effectPos = item->hasEffect("volume", "fadein");
4864         if (effectPos != -1) {
4865             QDomElement effect = item->getEffectAt(effectPos);
4866             int start = item->cropStart().frames(m_document->fps());
4867             int max = item->cropDuration().frames(m_document->fps());
4868             if (end > max) {
4869                 // Make sure the fade effect is not longer than the clip
4870                 item->setFadeIn(max);
4871                 end = item->fadeIn();
4872             }
4873             end += start;
4874             EffectsList::setParameter(effect, "in", QString::number(start));
4875             EffectsList::setParameter(effect, "out", QString::number(end));
4876             if (standalone) {
4877                 if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), getEffectArgs(effect)))
4878                     emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
4879                 // if fade effect is displayed, update the effect edit widget with new clip duration
4880                 if (item->isSelected() && effectPos == item->selectedEffectIndex())
4881                     emit clipItemSelected(item);
4882             }
4883         }
4884         effectPos = item->hasEffect("brightness", "fade_from_black");
4885         if (effectPos != -1) {
4886             QDomElement effect = item->getEffectAt(effectPos);
4887             int start = item->cropStart().frames(m_document->fps());
4888             int max = item->cropDuration().frames(m_document->fps());
4889             if (end > max) {
4890                 // Make sure the fade effect is not longer than the clip
4891                 item->setFadeIn(max);
4892                 end = item->fadeIn();
4893             }
4894             end += start;
4895             EffectsList::setParameter(effect, "in", QString::number(start));
4896             EffectsList::setParameter(effect, "out", QString::number(end));
4897             if (standalone) {
4898                 if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), getEffectArgs(effect)))
4899                     emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
4900                 // if fade effect is displayed, update the effect edit widget with new clip duration
4901                 if (item->isSelected() && effectPos == item->selectedEffectIndex())
4902                     emit clipItemSelected(item);
4903             }
4904         }
4905     }
4906
4907     int start = item->fadeOut();
4908     if (start != 0) {
4909         // there is a fade out effect
4910         int effectPos = item->hasEffect("volume", "fadeout");
4911         if (effectPos != -1) {
4912             QDomElement effect = item->getEffectAt(effectPos);
4913             int max = item->cropDuration().frames(m_document->fps());
4914             int end = max + item->cropStart().frames(m_document->fps());
4915             if (start > max) {
4916                 // Make sure the fade effect is not longer than the clip
4917                 item->setFadeOut(max);
4918                 start = item->fadeOut();
4919             }
4920             start = end - start;
4921             EffectsList::setParameter(effect, "in", QString::number(start));
4922             EffectsList::setParameter(effect, "out", QString::number(end));
4923             if (standalone) {
4924                 if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), getEffectArgs(effect)))
4925                     emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
4926                 // if fade effect is displayed, update the effect edit widget with new clip duration
4927                 if (item->isSelected() && effectPos == item->selectedEffectIndex())
4928                     emit clipItemSelected(item);
4929             }
4930         }
4931         effectPos = item->hasEffect("brightness", "fade_to_black");
4932         if (effectPos != -1) {
4933             QDomElement effect = item->getEffectAt(effectPos);
4934             int max = item->cropDuration().frames(m_document->fps());
4935             int end = max + item->cropStart().frames(m_document->fps());
4936             if (start > max) {
4937                 // Make sure the fade effect is not longer than the clip
4938                 item->setFadeOut(max);
4939                 start = item->fadeOut();
4940             }
4941             start = end - start;
4942             EffectsList::setParameter(effect, "in", QString::number(start));
4943             EffectsList::setParameter(effect, "out", QString::number(end));
4944             if (standalone) {
4945                 if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), getEffectArgs(effect)))
4946                     emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
4947                 // if fade effect is displayed, update the effect edit widget with new clip duration
4948                 if (item->isSelected() && effectPos == item->selectedEffectIndex())
4949                     emit clipItemSelected(item);
4950             }
4951         }
4952     }
4953
4954     int effectPos = item->hasEffect("freeze", "freeze");
4955     if (effectPos != -1) {
4956         // Freeze effect needs to be adjusted with clip resize
4957         int diff = (info.startPos - item->startPos()).frames(m_document->fps());
4958         QDomElement eff = item->getEffectAt(effectPos);
4959         if (!eff.isNull() && diff != 0) {
4960             int freeze_pos = EffectsList::parameter(eff, "frame").toInt() + diff;
4961             EffectsList::setParameter(eff, "frame", QString::number(freeze_pos));
4962             if (standalone) {
4963                 if (item->isSelected() && item->selectedEffect().attribute("id") == "freeze") {
4964                     emit clipItemSelected(item);
4965                 }
4966             }
4967         }
4968     }
4969 }
4970
4971 double CustomTrackView::getSnapPointForPos(double pos)
4972 {
4973     return m_scene->getSnapPointForPos(pos, KdenliveSettings::snaptopoints());
4974 }
4975
4976 void CustomTrackView::updateSnapPoints(AbstractClipItem *selected, QList <GenTime> offsetList, bool skipSelectedItems)
4977 {
4978     QList <GenTime> snaps;
4979     if (selected && offsetList.isEmpty()) offsetList.append(selected->cropDuration());
4980     QList<QGraphicsItem *> itemList = items();
4981     for (int i = 0; i < itemList.count(); i++) {
4982         if (itemList.at(i) == selected) continue;
4983         if (skipSelectedItems && itemList.at(i)->isSelected()) continue;
4984         if (itemList.at(i)->type() == AVWIDGET) {
4985             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
4986             GenTime start = item->startPos();
4987             GenTime end = item->endPos();
4988             snaps.append(start);
4989             snaps.append(end);
4990             if (!offsetList.isEmpty()) {
4991                 for (int j = 0; j < offsetList.size(); j++) {
4992                     if (start > offsetList.at(j)) snaps.append(start - offsetList.at(j));
4993                     if (end > offsetList.at(j)) snaps.append(end - offsetList.at(j));
4994                 }
4995             }
4996             // Add clip markers
4997             QList < GenTime > markers = item->snapMarkers();
4998             for (int j = 0; j < markers.size(); ++j) {
4999                 GenTime t = markers.at(j);
5000                 snaps.append(t);
5001                 if (!offsetList.isEmpty()) {
5002                     for (int k = 0; k < offsetList.size(); k++) {
5003                         if (t > offsetList.at(k)) snaps.append(t - offsetList.at(k));
5004                     }
5005                 }
5006             }
5007         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
5008             Transition *transition = static_cast <Transition*>(itemList.at(i));
5009             GenTime start = transition->startPos();
5010             GenTime end = transition->endPos();
5011             snaps.append(start);
5012             snaps.append(end);
5013             if (!offsetList.isEmpty()) {
5014                 for (int j = 0; j < offsetList.size(); j++) {
5015                     if (start > offsetList.at(j)) snaps.append(start - offsetList.at(j));
5016                     if (end > offsetList.at(j)) snaps.append(end - offsetList.at(j));
5017                 }
5018             }
5019         }
5020     }
5021
5022     // add cursor position
5023     GenTime pos = GenTime(m_cursorPos, m_document->fps());
5024     snaps.append(pos);
5025     if (!offsetList.isEmpty()) {
5026         for (int j = 0; j < offsetList.size(); j++) {
5027             snaps.append(pos - offsetList.at(j));
5028         }
5029     }
5030
5031     // add guides
5032     for (int i = 0; i < m_guides.count(); i++) {
5033         snaps.append(m_guides.at(i)->position());
5034         if (!offsetList.isEmpty()) {
5035             for (int j = 0; j < offsetList.size(); j++) {
5036                 snaps.append(m_guides.at(i)->position() - offsetList.at(j));
5037             }
5038         }
5039     }
5040
5041     // add render zone
5042     QPoint z = m_document->zone();
5043     snaps.append(GenTime(z.x(), m_document->fps()));
5044     snaps.append(GenTime(z.y(), m_document->fps()));
5045
5046     qSort(snaps);
5047     m_scene->setSnapList(snaps);
5048     //for (int i = 0; i < m_snapPoints.size(); ++i)
5049     //    kDebug() << "SNAP POINT: " << m_snapPoints.at(i).frames(25);
5050 }
5051
5052 void CustomTrackView::slotSeekToPreviousSnap()
5053 {
5054     updateSnapPoints(NULL);
5055     GenTime res = m_scene->previousSnapPoint(GenTime(m_cursorPos, m_document->fps()));
5056     setCursorPos((int) res.frames(m_document->fps()));
5057     checkScrolling();
5058 }
5059
5060 void CustomTrackView::slotSeekToNextSnap()
5061 {
5062     updateSnapPoints(NULL);
5063     GenTime res = m_scene->nextSnapPoint(GenTime(m_cursorPos, m_document->fps()));
5064     setCursorPos((int) res.frames(m_document->fps()));
5065     checkScrolling();
5066 }
5067
5068 void CustomTrackView::clipStart()
5069 {
5070     AbstractClipItem *item = getMainActiveClip();
5071     if (item != NULL) {
5072         setCursorPos((int) item->startPos().frames(m_document->fps()));
5073         checkScrolling();
5074     }
5075 }
5076
5077 void CustomTrackView::clipEnd()
5078 {
5079     AbstractClipItem *item = getMainActiveClip();
5080     if (item != NULL) {
5081         setCursorPos((int) item->endPos().frames(m_document->fps()) - 1);
5082         checkScrolling();
5083     }
5084 }
5085
5086 void CustomTrackView::slotAddClipMarker(const QString &id, GenTime t, QString c)
5087 {
5088     QString oldcomment = m_document->clipManager()->getClipById(id)->markerComment(t);
5089     AddMarkerCommand *command = new AddMarkerCommand(this, oldcomment, c, id, t);
5090     m_commandStack->push(command);
5091 }
5092
5093 void CustomTrackView::slotDeleteClipMarker(const QString &comment, const QString &id, const GenTime &position)
5094 {
5095     AddMarkerCommand *command = new AddMarkerCommand(this, comment, QString(), id, position);
5096     m_commandStack->push(command);
5097 }
5098
5099 void CustomTrackView::slotDeleteAllClipMarkers(const QString &id)
5100 {
5101     DocClipBase *base = m_document->clipManager()->getClipById(id);
5102     QList <CommentedTime> markers = base->commentedSnapMarkers();
5103
5104     if (markers.isEmpty()) {
5105         emit displayMessage(i18n("Clip has no markers"), ErrorMessage);
5106         return;
5107     }
5108     QUndoCommand *deleteMarkers = new QUndoCommand();
5109     deleteMarkers->setText("Delete clip markers");
5110
5111     for (int i = 0; i < markers.size(); i++) {
5112         new AddMarkerCommand(this, markers.at(i).comment(), QString(), id, markers.at(i).time(), deleteMarkers);
5113     }
5114     m_commandStack->push(deleteMarkers);
5115 }
5116
5117 void CustomTrackView::addMarker(const QString &id, const GenTime &pos, const QString &comment)
5118 {
5119     DocClipBase *base = m_document->clipManager()->getClipById(id);
5120     if (!comment.isEmpty()) base->addSnapMarker(pos, comment);
5121     else base->deleteSnapMarker(pos);
5122     emit updateClipMarkers(base);
5123     setDocumentModified();
5124     viewport()->update();
5125 }
5126
5127 int CustomTrackView::hasGuide(int pos, int offset)
5128 {
5129     for (int i = 0; i < m_guides.count(); i++) {
5130         int guidePos = m_guides.at(i)->position().frames(m_document->fps());
5131         if (qAbs(guidePos - pos) <= offset) return guidePos;
5132         else if (guidePos > pos) return -1;
5133     }
5134     return -1;
5135 }
5136
5137 void CustomTrackView::buildGuidesMenu(QMenu *goMenu) const
5138 {
5139     QAction *act;
5140     goMenu->clear();
5141     double fps = m_document->fps();
5142     for (int i = 0; i < m_guides.count(); i++) {
5143         act = goMenu->addAction(m_guides.at(i)->label() + "/" + Timecode::getStringTimecode(m_guides.at(i)->position().frames(fps), fps));
5144         act->setData(m_guides.at(i)->position().frames(m_document->fps()));
5145     }
5146     goMenu->setEnabled(!m_guides.isEmpty());
5147 }
5148
5149 void CustomTrackView::editGuide(const GenTime &oldPos, const GenTime &pos, const QString &comment)
5150 {
5151     if (oldPos > GenTime() && pos > GenTime()) {
5152         // move guide
5153         for (int i = 0; i < m_guides.count(); i++) {
5154             if (m_guides.at(i)->position() == oldPos) {
5155                 Guide *item = m_guides.at(i);
5156                 item->updateGuide(pos, comment);
5157                 break;
5158             }
5159         }
5160     } else if (pos > GenTime()) addGuide(pos, comment);
5161     else {
5162         // remove guide
5163         bool found = false;
5164         for (int i = 0; i < m_guides.count(); i++) {
5165             if (m_guides.at(i)->position() == oldPos) {
5166                 delete m_guides.takeAt(i);
5167                 found = true;
5168                 break;
5169             }
5170         }
5171         if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
5172     }
5173     qSort(m_guides.begin(), m_guides.end(), sortGuidesList);
5174     m_document->syncGuides(m_guides);
5175 }
5176
5177 bool CustomTrackView::addGuide(const GenTime &pos, const QString &comment)
5178 {
5179     for (int i = 0; i < m_guides.count(); i++) {
5180         if (m_guides.at(i)->position() == pos) {
5181             emit displayMessage(i18n("A guide already exists at position %1", m_document->timecode().getTimecodeFromFrames(pos.frames(m_document->fps()))), ErrorMessage);
5182             return false;
5183         }
5184     }
5185     Guide *g = new Guide(this, pos, comment, m_tracksHeight * m_document->tracksCount() * matrix().m22());
5186     scene()->addItem(g);
5187     m_guides.append(g);
5188     qSort(m_guides.begin(), m_guides.end(), sortGuidesList);
5189     m_document->syncGuides(m_guides);
5190     return true;
5191 }
5192
5193 void CustomTrackView::slotAddGuide(bool dialog)
5194 {
5195     CommentedTime marker(GenTime(m_cursorPos, m_document->fps()), i18n("Guide"));
5196     if (dialog) {
5197         MarkerDialog d(NULL, marker, m_document->timecode(), i18n("Add Guide"), this);
5198         if (d.exec() != QDialog::Accepted) return;
5199         marker = d.newMarker();
5200         
5201     } else {
5202         marker.setComment(m_document->timecode().getDisplayTimecodeFromFrames(m_cursorPos, false));
5203     }
5204     if (addGuide(marker.time(), marker.comment())) {
5205         EditGuideCommand *command = new EditGuideCommand(this, GenTime(), QString(), marker.time(), marker.comment(), false);
5206         m_commandStack->push(command);
5207     }
5208 }
5209
5210 void CustomTrackView::slotEditGuide(int guidePos)
5211 {
5212     GenTime pos;
5213     if (guidePos == -1) pos = GenTime(m_cursorPos, m_document->fps());
5214     else pos = GenTime(guidePos, m_document->fps());
5215     bool found = false;
5216     for (int i = 0; i < m_guides.count(); i++) {
5217         if (m_guides.at(i)->position() == pos) {
5218             slotEditGuide(m_guides.at(i)->info());
5219             found = true;
5220             break;
5221         }
5222     }
5223     if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
5224 }
5225
5226 void CustomTrackView::slotEditGuide(CommentedTime guide)
5227 {
5228     MarkerDialog d(NULL, guide, m_document->timecode(), i18n("Edit Guide"), this);
5229     if (d.exec() == QDialog::Accepted) {
5230         EditGuideCommand *command = new EditGuideCommand(this, guide.time(), guide.comment(), d.newMarker().time(), d.newMarker().comment(), true);
5231         m_commandStack->push(command);
5232     }
5233 }
5234
5235
5236 void CustomTrackView::slotEditTimeLineGuide()
5237 {
5238     if (m_dragGuide == NULL) return;
5239     CommentedTime guide = m_dragGuide->info();
5240     MarkerDialog d(NULL, guide, m_document->timecode(), i18n("Edit Guide"), this);
5241     if (d.exec() == QDialog::Accepted) {
5242         EditGuideCommand *command = new EditGuideCommand(this, guide.time(), guide.comment(), d.newMarker().time(), d.newMarker().comment(), true);
5243         m_commandStack->push(command);
5244     }
5245 }
5246
5247 void CustomTrackView::slotDeleteGuide(int guidePos)
5248 {
5249     GenTime pos;
5250     if (guidePos == -1) pos = GenTime(m_cursorPos, m_document->fps());
5251     else pos = GenTime(guidePos, m_document->fps());
5252     bool found = false;
5253     for (int i = 0; i < m_guides.count(); i++) {
5254         if (m_guides.at(i)->position() == pos) {
5255             EditGuideCommand *command = new EditGuideCommand(this, m_guides.at(i)->position(), m_guides.at(i)->label(), GenTime(), QString(), true);
5256             m_commandStack->push(command);
5257             found = true;
5258             break;
5259         }
5260     }
5261     if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
5262 }
5263
5264
5265 void CustomTrackView::slotDeleteTimeLineGuide()
5266 {
5267     if (m_dragGuide == NULL) return;
5268     EditGuideCommand *command = new EditGuideCommand(this, m_dragGuide->position(), m_dragGuide->label(), GenTime(), QString(), true);
5269     m_commandStack->push(command);
5270 }
5271
5272
5273 void CustomTrackView::slotDeleteAllGuides()
5274 {
5275     QUndoCommand *deleteAll = new QUndoCommand();
5276     deleteAll->setText("Delete all guides");
5277     for (int i = 0; i < m_guides.count(); i++) {
5278         new EditGuideCommand(this, m_guides.at(i)->position(), m_guides.at(i)->label(), GenTime(), QString(), true, deleteAll);
5279     }
5280     m_commandStack->push(deleteAll);
5281 }
5282
5283 void CustomTrackView::setTool(PROJECTTOOL tool)
5284 {
5285     m_tool = tool;
5286 }
5287
5288 void CustomTrackView::setScale(double scaleFactor, double verticalScale)
5289 {
5290     QMatrix newmatrix;
5291     newmatrix = newmatrix.scale(scaleFactor, verticalScale);
5292     m_scene->setScale(scaleFactor, verticalScale);
5293     removeTipAnimation();
5294     bool adjust = false;
5295     if (verticalScale != matrix().m22()) adjust = true;
5296     setMatrix(newmatrix);
5297     if (adjust) {
5298         double newHeight = m_tracksHeight * m_document->tracksCount() * matrix().m22();
5299         m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), newHeight - 1);
5300         for (int i = 0; i < m_guides.count(); i++) {
5301             QLineF l = m_guides.at(i)->line();
5302             l.setP2(QPointF(l.x2(), newHeight));
5303             m_guides.at(i)->setLine(l);
5304         }
5305         setSceneRect(0, 0, sceneRect().width(), m_tracksHeight * m_document->tracksCount());
5306     }
5307
5308     int diff = sceneRect().width() - m_projectDuration;
5309     if (diff * newmatrix.m11() < 50) {
5310         if (newmatrix.m11() < 0.4)
5311             setSceneRect(0, 0, (m_projectDuration + 100 / newmatrix.m11()), sceneRect().height());
5312         else
5313             setSceneRect(0, 0, (m_projectDuration + 300), sceneRect().height());
5314     }
5315     double verticalPos = mapToScene(QPoint(0, viewport()->height() / 2)).y();
5316     centerOn(QPointF(cursorPos(), verticalPos));
5317 }
5318
5319 void CustomTrackView::slotRefreshGuides()
5320 {
5321     if (KdenliveSettings::showmarkers()) {
5322         for (int i = 0; i < m_guides.count(); i++)
5323             m_guides.at(i)->update();
5324     }
5325 }
5326
5327 void CustomTrackView::drawBackground(QPainter * painter, const QRectF &rect)
5328 {
5329     painter->setClipRect(rect);
5330     QPen pen1 = painter->pen();
5331     pen1.setColor(palette().dark().color());
5332     painter->setPen(pen1);
5333     double min = rect.left();
5334     double max = rect.right();
5335     painter->drawLine(QPointF(min, 0), QPointF(max, 0));
5336     int maxTrack = m_document->tracksCount();
5337     QColor lockedColor = palette().button().color();
5338     QColor audioColor = palette().alternateBase().color();
5339     for (int i = 0; i < maxTrack; i++) {
5340         TrackInfo info = m_document->trackInfoAt(maxTrack - i - 1);
5341         if (info.isLocked || info.type == AUDIOTRACK || i == m_selectedTrack) {
5342             const QRectF track(min, m_tracksHeight * i + 1, max - min, m_tracksHeight - 1);
5343             if (i == m_selectedTrack)
5344                 painter->fillRect(track, m_activeTrackBrush.brush(this));
5345             else
5346                 painter->fillRect(track, info.isLocked ? lockedColor : audioColor);
5347         }
5348         painter->drawLine(QPointF(min, m_tracksHeight *(i + 1)), QPointF(max, m_tracksHeight *(i + 1)));
5349     }
5350 }
5351
5352 bool CustomTrackView::findString(const QString &text)
5353 {
5354     QString marker;
5355     for (int i = 0; i < m_searchPoints.size(); ++i) {
5356         marker = m_searchPoints.at(i).comment();
5357         if (marker.contains(text, Qt::CaseInsensitive)) {
5358             setCursorPos(m_searchPoints.at(i).time().frames(m_document->fps()), true);
5359             int vert = verticalScrollBar()->value();
5360             int hor = cursorPos();
5361             ensureVisible(hor, vert + 10, 2, 2, 50, 0);
5362             m_findIndex = i;
5363             return true;
5364         }
5365     }
5366     return false;
5367 }
5368
5369 void CustomTrackView::selectFound(QString track, QString pos)
5370 {
5371     setCursorPos(m_document->timecode().getFrameCount(pos), true);
5372     slotSelectTrack(track.toInt());
5373     selectClip(true);
5374     int vert = verticalScrollBar()->value();
5375     int hor = cursorPos();
5376     ensureVisible(hor, vert + 10, 2, 2, 50, 0);
5377 }
5378
5379 bool CustomTrackView::findNextString(const QString &text)
5380 {
5381     QString marker;
5382     for (int i = m_findIndex + 1; i < m_searchPoints.size(); ++i) {
5383         marker = m_searchPoints.at(i).comment();
5384         if (marker.contains(text, Qt::CaseInsensitive)) {
5385             setCursorPos(m_searchPoints.at(i).time().frames(m_document->fps()), true);
5386             int vert = verticalScrollBar()->value();
5387             int hor = cursorPos();
5388             ensureVisible(hor, vert + 10, 2, 2, 50, 0);
5389             m_findIndex = i;
5390             return true;
5391         }
5392     }
5393     m_findIndex = -1;
5394     return false;
5395 }
5396
5397 void CustomTrackView::initSearchStrings()
5398 {
5399     m_searchPoints.clear();
5400     QList<QGraphicsItem *> itemList = items();
5401     for (int i = 0; i < itemList.count(); i++) {
5402         // parse all clip names
5403         if (itemList.at(i)->type() == AVWIDGET) {
5404             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
5405             GenTime start = item->startPos();
5406             CommentedTime t(start, item->clipName());
5407             m_searchPoints.append(t);
5408             // add all clip markers
5409             QList < CommentedTime > markers = item->commentedSnapMarkers();
5410             m_searchPoints += markers;
5411         }
5412     }
5413
5414     // add guides
5415     for (int i = 0; i < m_guides.count(); i++)
5416         m_searchPoints.append(m_guides.at(i)->info());
5417
5418     qSort(m_searchPoints);
5419 }
5420
5421 void CustomTrackView::clearSearchStrings()
5422 {
5423     m_searchPoints.clear();
5424     m_findIndex = 0;
5425 }
5426
5427 QList<ItemInfo> CustomTrackView::findId(const QString &clipId)
5428 {
5429     QList<ItemInfo> matchingInfo;
5430     QList<QGraphicsItem *> itemList = items();
5431     for (int i = 0; i < itemList.count(); i++) {
5432         if (itemList.at(i)->type() == AVWIDGET) {
5433             ClipItem *item = (ClipItem *)itemList.at(i);
5434             if (item->clipProducer() == clipId)
5435                 matchingInfo << item->info();
5436         }
5437     }
5438     return matchingInfo;
5439 }
5440
5441 void CustomTrackView::copyClip()
5442 {
5443     qDeleteAll(m_copiedItems);
5444     m_copiedItems.clear();
5445     QList<QGraphicsItem *> itemList = scene()->selectedItems();
5446     if (itemList.count() == 0) {
5447         emit displayMessage(i18n("Select a clip before copying"), ErrorMessage);
5448         return;
5449     }
5450     for (int i = 0; i < itemList.count(); i++) {
5451         if (itemList.at(i)->type() == AVWIDGET) {
5452             ClipItem *clip = static_cast <ClipItem *>(itemList.at(i));
5453             ClipItem *clone = clip->clone(clip->info());
5454             m_copiedItems.append(clone);
5455         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
5456             Transition *dup = static_cast <Transition *>(itemList.at(i));
5457             m_copiedItems.append(dup->clone());
5458         }
5459     }
5460 }
5461
5462 bool CustomTrackView::canBePastedTo(ItemInfo info, int type) const
5463 {
5464     if (m_scene->editMode() != NORMALEDIT) {
5465         // If we are in overwrite mode, always allow the move
5466         return true;
5467     }
5468     int height = m_tracksHeight - 2;
5469     int offset = 0;
5470     if (type == TRANSITIONWIDGET) {
5471         height = Transition::itemHeight();
5472         offset = Transition::itemOffset();
5473     }
5474     else if (type == AVWIDGET) {
5475         height = ClipItem::itemHeight();
5476         offset = ClipItem::itemOffset();
5477     }
5478     QRectF rect((double) info.startPos.frames(m_document->fps()), (double)(info.track * m_tracksHeight + 1 + offset), (double)(info.endPos - info.startPos).frames(m_document->fps()), (double) height);
5479     QList<QGraphicsItem *> collisions = scene()->items(rect, Qt::IntersectsItemBoundingRect);
5480     for (int i = 0; i < collisions.count(); i++) {
5481         if (collisions.at(i)->type() == type) {
5482             return false;
5483         }
5484     }
5485     return true;
5486 }
5487
5488 bool CustomTrackView::canBePastedTo(QList <ItemInfo> infoList, int type) const
5489 {
5490     QPainterPath path;
5491     for (int i = 0; i < infoList.count(); i++) {
5492         const QRectF rect((double) infoList.at(i).startPos.frames(m_document->fps()), (double)(infoList.at(i).track * m_tracksHeight + 1), (double)(infoList.at(i).endPos - infoList.at(i).startPos).frames(m_document->fps()), (double)(m_tracksHeight - 1));
5493         path.addRect(rect);
5494     }
5495     QList<QGraphicsItem *> collisions = scene()->items(path);
5496     for (int i = 0; i < collisions.count(); i++) {
5497         if (collisions.at(i)->type() == type) return false;
5498     }
5499     return true;
5500 }
5501
5502 bool CustomTrackView::canBePasted(QList<AbstractClipItem *> items, GenTime offset, int trackOffset) const
5503 {
5504     for (int i = 0; i < items.count(); i++) {
5505         ItemInfo info = items.at(i)->info();
5506         info.startPos += offset;
5507         info.endPos += offset;
5508         info.track += trackOffset;
5509         if (!canBePastedTo(info, items.at(i)->type())) return false;
5510     }
5511     return true;
5512 }
5513
5514 bool CustomTrackView::canBeMoved(QList<AbstractClipItem *> items, GenTime offset, int trackOffset) const
5515 {
5516     QPainterPath movePath;
5517     movePath.moveTo(0, 0);
5518
5519     for (int i = 0; i < items.count(); i++) {
5520         ItemInfo info = items.at(i)->info();
5521         info.startPos = info.startPos + offset;
5522         info.endPos = info.endPos + offset;
5523         info.track = info.track + trackOffset;
5524         if (info.startPos < GenTime()) {
5525             // No clip should go below 0
5526             return false;
5527         }
5528         QRectF rect((double) info.startPos.frames(m_document->fps()), (double)(info.track * m_tracksHeight + 1), (double)(info.endPos - info.startPos).frames(m_document->fps()), (double)(m_tracksHeight - 1));
5529         movePath.addRect(rect);
5530     }
5531     QList<QGraphicsItem *> collisions = scene()->items(movePath, Qt::IntersectsItemBoundingRect);
5532     for (int i = 0; i < collisions.count(); i++) {
5533         if ((collisions.at(i)->type() == AVWIDGET || collisions.at(i)->type() == TRANSITIONWIDGET) && !items.contains(static_cast <AbstractClipItem *>(collisions.at(i)))) {
5534             kDebug() << "  ////////////   CLIP COLLISION, MOVE NOT ALLOWED";
5535             return false;
5536         }
5537     }
5538     return true;
5539 }
5540
5541
5542 void CustomTrackView::pasteClip()
5543 {
5544     if (m_copiedItems.count() == 0) {
5545         emit displayMessage(i18n("No clip copied"), ErrorMessage);
5546         return;
5547     }
5548     QPoint position;
5549     int track = -1;
5550     GenTime pos = GenTime(-1);
5551     if (m_menuPosition.isNull()) {
5552         position = mapFromGlobal(QCursor::pos());
5553         if (!contentsRect().contains(position) || mapToScene(position).y() / m_tracksHeight > m_document->tracksCount()) {
5554             track = m_selectedTrack;
5555             pos = GenTime(m_cursorPos, m_document->fps());
5556             /*emit displayMessage(i18n("Cannot paste selected clips"), ErrorMessage);
5557             return;*/
5558         }
5559     } else {
5560         position = m_menuPosition;
5561     }
5562
5563     if (pos == GenTime(-1))
5564         pos = GenTime((int)(mapToScene(position).x()), m_document->fps());
5565     if (track == -1)
5566         track = (int)(mapToScene(position).y() / m_tracksHeight);
5567
5568     GenTime leftPos = m_copiedItems.at(0)->startPos();
5569     int lowerTrack = m_copiedItems.at(0)->track();
5570     int upperTrack = m_copiedItems.at(0)->track();
5571     for (int i = 1; i < m_copiedItems.count(); i++) {
5572         if (m_copiedItems.at(i)->startPos() < leftPos) leftPos = m_copiedItems.at(i)->startPos();
5573         if (m_copiedItems.at(i)->track() < lowerTrack) lowerTrack = m_copiedItems.at(i)->track();
5574         if (m_copiedItems.at(i)->track() > upperTrack) upperTrack = m_copiedItems.at(i)->track();
5575     }
5576
5577     GenTime offset = pos - leftPos;
5578     int trackOffset = track - lowerTrack;
5579
5580     if (lowerTrack + trackOffset < 0) trackOffset = 0 - lowerTrack;
5581     if (upperTrack + trackOffset > m_document->tracksCount() - 1) trackOffset = m_document->tracksCount() - upperTrack - 1;
5582     if (!canBePasted(m_copiedItems, offset, trackOffset)) {
5583         emit displayMessage(i18n("Cannot paste selected clips"), ErrorMessage);
5584         return;
5585     }
5586     QUndoCommand *pasteClips = new QUndoCommand();
5587     pasteClips->setText("Paste clips");
5588
5589     for (int i = 0; i < m_copiedItems.count(); i++) {
5590         // parse all clip names
5591         if (m_copiedItems.at(i) && m_copiedItems.at(i)->type() == AVWIDGET) {
5592             ClipItem *clip = static_cast <ClipItem *>(m_copiedItems.at(i));
5593             ItemInfo info = clip->info();
5594             info.startPos += offset;
5595             info.endPos += offset;
5596             info.track += trackOffset;
5597             if (canBePastedTo(info, AVWIDGET)) {
5598                 new AddTimelineClipCommand(this, clip->xml(), clip->clipProducer(), info, clip->effectList(), m_scene->editMode() == OVERWRITEEDIT, m_scene->editMode() == INSERTEDIT, true, false, pasteClips);
5599             } else emit displayMessage(i18n("Cannot paste clip to selected place"), ErrorMessage);
5600         } else if (m_copiedItems.at(i) && m_copiedItems.at(i)->type() == TRANSITIONWIDGET) {
5601             Transition *tr = static_cast <Transition *>(m_copiedItems.at(i));
5602             ItemInfo info;
5603             info.startPos = tr->startPos() + offset;
5604             info.endPos = tr->endPos() + offset;
5605             info.track = tr->track() + trackOffset;
5606             int transitionEndTrack;
5607             if (!tr->forcedTrack()) transitionEndTrack = getPreviousVideoTrack(info.track);
5608             else transitionEndTrack = tr->transitionEndTrack();
5609             if (canBePastedTo(info, TRANSITIONWIDGET)) {
5610                 if (info.startPos >= info.endPos) {
5611                     emit displayMessage(i18n("Invalid transition"), ErrorMessage);
5612                 } else new AddTransitionCommand(this, info, transitionEndTrack, tr->toXML(), false, true, pasteClips);
5613             } else emit displayMessage(i18n("Cannot paste transition to selected place"), ErrorMessage);
5614         }
5615     }
5616     updateTrackDuration(-1, pasteClips);
5617     m_commandStack->push(pasteClips);
5618 }
5619
5620 void CustomTrackView::pasteClipEffects()
5621 {
5622     if (m_copiedItems.count() != 1 || m_copiedItems.at(0)->type() != AVWIDGET) {
5623         emit displayMessage(i18n("You must copy exactly one clip before pasting effects"), ErrorMessage);
5624         return;
5625     }
5626     ClipItem *clip = static_cast < ClipItem *>(m_copiedItems.at(0));
5627
5628     QUndoCommand *paste = new QUndoCommand();
5629     paste->setText("Paste effects");
5630
5631     QList<QGraphicsItem *> clips = scene()->selectedItems();
5632
5633     // expand groups
5634     for (int i = 0; i < clips.count(); ++i) {
5635         if (clips.at(i)->type() == GROUPWIDGET) {
5636             QList<QGraphicsItem *> children = clips.at(i)->childItems();
5637             for (int j = 0; j < children.count(); j++) {
5638                 if (children.at(j)->type() == AVWIDGET && !clips.contains(children.at(j))) {
5639                     clips.append(children.at(j));
5640                 }
5641             }
5642         }
5643     }
5644
5645     for (int i = 0; i < clips.count(); ++i) {
5646         if (clips.at(i)->type() == AVWIDGET) {
5647             ClipItem *item = static_cast < ClipItem *>(clips.at(i));
5648             for (int j = 1; j <= clip->effectsCount(); j++) {
5649                 QDomElement eff = clip->effectAt(j);
5650                 if (eff.attribute("unique", "0") == "0" || item->hasEffect(eff.attribute("tag"), eff.attribute("id")) == -1) {
5651                     adjustKeyfames(clip->cropStart(), item->cropStart(), item->cropDuration(), eff);
5652                     new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), eff, true, paste);
5653                 }
5654             }
5655         }
5656     }
5657     if (paste->childCount() > 0) m_commandStack->push(paste);
5658     else delete paste;
5659
5660     //adjust effects (fades, ...)
5661     for (int i = 0; i < clips.count(); ++i) {
5662         if (clips.at(i)->type() == AVWIDGET) {
5663             ClipItem *item = static_cast < ClipItem *>(clips.at(i));
5664             updatePositionEffects(item, item->info());
5665         }
5666     }
5667 }
5668
5669
5670 void CustomTrackView::adjustKeyfames(GenTime oldstart, GenTime newstart, GenTime duration, QDomElement xml)
5671 {
5672     // parse parameters to check if we need to adjust to the new crop start
5673     int diff = (newstart - oldstart).frames(m_document->fps());
5674     int max = (newstart + duration).frames(m_document->fps());
5675     QLocale locale;
5676     QDomNodeList params = xml.elementsByTagName("parameter");
5677     for (int i = 0; i < params.count(); i++) {
5678         QDomElement e = params.item(i).toElement();
5679         if (!e.isNull() && (e.attribute("type") == "keyframe" || e.attribute("type") == "simplekeyframe")) {
5680             QString def = e.attribute("default");
5681             // Effect has a keyframe type parameter, we need to adjust the values
5682             QStringList keys = e.attribute("keyframes").split(";", QString::SkipEmptyParts);
5683             QStringList newKeyFrames;
5684             foreach(const QString &str, keys) {
5685                 int pos = str.section(':', 0, 0).toInt();
5686                 double val = str.section(':', 1, 1).toDouble();
5687                 pos += diff;
5688                 if (pos > max) {
5689                     newKeyFrames.append(QString::number(max) + ':' + locale.toString(val));
5690                     break;
5691                 } else newKeyFrames.append(QString::number(pos) + ':' + locale.toString(val));
5692             }
5693             //kDebug()<<"ORIGIN: "<<keys<<", FIXED: "<<newKeyFrames;
5694             e.setAttribute("keyframes", newKeyFrames.join(";"));
5695         }
5696     }
5697 }
5698
5699 ClipItem *CustomTrackView::getClipUnderCursor() const
5700 {
5701     QRectF rect((double) m_cursorPos, 0.0, 1.0, (double)(m_tracksHeight * m_document->tracksCount()));
5702     QList<QGraphicsItem *> collisions = scene()->items(rect, Qt::IntersectsItemBoundingRect);
5703     for (int i = 0; i < collisions.count(); i++) {
5704         if (collisions.at(i)->type() == AVWIDGET) {
5705             return static_cast < ClipItem *>(collisions.at(i));
5706         }
5707     }
5708     return NULL;
5709 }
5710
5711 AbstractClipItem *CustomTrackView::getMainActiveClip() const
5712 {
5713     QList<QGraphicsItem *> clips = scene()->selectedItems();
5714     if (clips.isEmpty()) {
5715         return getClipUnderCursor();
5716     } else {
5717         AbstractClipItem *item = NULL;
5718         for (int i = 0; i < clips.count(); ++i) {
5719             if (clips.at(i)->type() == AVWIDGET) {
5720                 item = static_cast < AbstractClipItem *>(clips.at(i));
5721                 if (clips.count() > 1 && item->startPos().frames(m_document->fps()) <= m_cursorPos && item->endPos().frames(m_document->fps()) >= m_cursorPos) break;
5722             }
5723         }
5724         if (item) return item;
5725     }
5726     return NULL;
5727 }
5728
5729 ClipItem *CustomTrackView::getActiveClipUnderCursor(bool allowOutsideCursor) const
5730 {
5731     QList<QGraphicsItem *> clips = scene()->selectedItems();
5732     if (clips.isEmpty()) {
5733         return getClipUnderCursor();
5734     } else {
5735         ClipItem *item;
5736         // remove all items in the list that are not clips
5737         for (int i = 0; i < clips.count();) {
5738             if (clips.at(i)->type() != AVWIDGET) clips.removeAt(i);
5739             else i++;
5740         }
5741         if (clips.count() == 1 && allowOutsideCursor) return static_cast < ClipItem *>(clips.at(0));
5742         for (int i = 0; i < clips.count(); ++i) {
5743             if (clips.at(i)->type() == AVWIDGET) {
5744                 item = static_cast < ClipItem *>(clips.at(i));
5745                 if (item->startPos().frames(m_document->fps()) <= m_cursorPos && item->endPos().frames(m_document->fps()) >= m_cursorPos)
5746                     return item;
5747             }
5748         }
5749     }
5750     return NULL;
5751 }
5752
5753 void CustomTrackView::setInPoint()
5754 {
5755     AbstractClipItem *clip = getActiveClipUnderCursor(true);
5756     if (clip == NULL) {
5757         if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET) {
5758             clip = m_dragItem;
5759         } else {
5760             emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
5761             return;
5762         }
5763     }
5764     prepareResizeClipStart(clip, clip->info(), m_cursorPos, true);
5765 }
5766
5767 void CustomTrackView::setOutPoint()
5768 {
5769     AbstractClipItem *clip = getActiveClipUnderCursor(true);
5770     if (clip == NULL) {
5771         if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET) {
5772             clip = m_dragItem;
5773         } else {
5774             emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
5775             return;
5776         }
5777     }
5778     prepareResizeClipEnd(clip, clip->info(), m_cursorPos, true);
5779 }
5780
5781 void CustomTrackView::slotUpdateAllThumbs()
5782 {
5783     if (!isEnabled()) return;
5784     QList<QGraphicsItem *> itemList = items();
5785     //if (itemList.isEmpty()) return;
5786     ClipItem *item;
5787     const QString thumbBase = m_document->projectFolder().path() + "/thumbs/";
5788     for (int i = 0; i < itemList.count(); i++) {
5789         if (itemList.at(i)->type() == AVWIDGET) {
5790             item = static_cast <ClipItem *>(itemList.at(i));
5791             if (item && item->isEnabled() && item->clipType() != COLOR && item->clipType() != AUDIO) {
5792                 // Check if we have a cached thumbnail
5793                 if (item->clipType() == IMAGE || item->clipType() == TEXT) {
5794                     QString thumb = thumbBase + item->baseClip()->getClipHash() + "_0.png";
5795                     if (QFile::exists(thumb)) {
5796                         QPixmap pix(thumb);
5797                         if (pix.isNull()) KIO::NetAccess::del(KUrl(thumb), this);
5798                         item->slotSetStartThumb(pix);
5799                     }
5800                 } else {
5801                     QString startThumb = thumbBase + item->baseClip()->getClipHash() + '_';
5802                     QString endThumb = startThumb;
5803                     startThumb.append(QString::number((int) item->speedIndependantCropStart().frames(m_document->fps())) + ".png");
5804                     endThumb.append(QString::number((int) (item->speedIndependantCropStart() + item->speedIndependantCropDuration()).frames(m_document->fps()) - 1) + ".png");
5805                     if (QFile::exists(startThumb)) {
5806                         QPixmap pix(startThumb);
5807                         if (pix.isNull()) KIO::NetAccess::del(KUrl(startThumb), this);
5808                         item->slotSetStartThumb(pix);
5809                     }
5810                     if (QFile::exists(endThumb)) {
5811                         QPixmap pix(endThumb);
5812                         if (pix.isNull()) KIO::NetAccess::del(KUrl(endThumb), this);
5813                         item->slotSetEndThumb(pix);
5814                     }
5815                 }
5816                 item->refreshClip(false, false);
5817             }
5818         }
5819     }
5820     viewport()->update();
5821 }
5822
5823 void CustomTrackView::saveThumbnails()
5824 {
5825     QList<QGraphicsItem *> itemList = items();
5826     ClipItem *item;
5827     QString thumbBase = m_document->projectFolder().path() + "/thumbs/";
5828     for (int i = 0; i < itemList.count(); i++) {
5829         if (itemList.at(i)->type() == AVWIDGET) {
5830             item = static_cast <ClipItem *>(itemList.at(i));
5831             if (item->clipType() != COLOR) {
5832                 // Check if we have a cached thumbnail
5833                 if (item->clipType() == IMAGE || item->clipType() == TEXT || item->clipType() == AUDIO) {
5834                     QString thumb = thumbBase + item->baseClip()->getClipHash() + "_0.png";
5835                     if (!QFile::exists(thumb)) {
5836                         QPixmap pix(item->startThumb());
5837                         pix.save(thumb);
5838                     }
5839                 } else {
5840                     QString startThumb = thumbBase + item->baseClip()->getClipHash() + '_';
5841                     QString endThumb = startThumb;
5842                     startThumb.append(QString::number((int) item->speedIndependantCropStart().frames(m_document->fps())) + ".png");
5843                     endThumb.append(QString::number((int) (item->speedIndependantCropStart() + item->speedIndependantCropDuration()).frames(m_document->fps()) - 1) + ".png");
5844                     if (!QFile::exists(startThumb)) {
5845                         QPixmap pix(item->startThumb());
5846                         pix.save(startThumb);
5847                     }
5848                     if (!QFile::exists(endThumb)) {
5849                         QPixmap pix(item->endThumb());
5850                         pix.save(endThumb);
5851                     }
5852                 }
5853             }
5854         }
5855     }
5856 }
5857
5858
5859 void CustomTrackView::slotInsertTrack(int ix)
5860 {
5861     TrackDialog d(m_document, parentWidget());
5862     d.comboTracks->setCurrentIndex(ix);
5863     d.label->setText(i18n("Insert track"));
5864     d.setWindowTitle(i18n("Insert New Track"));
5865
5866     if (d.exec() == QDialog::Accepted) {
5867         ix = d.comboTracks->currentIndex();
5868         if (d.before_select->currentIndex() == 1)
5869             ix++;
5870         TrackInfo info;
5871         info.duration = 0;
5872         info.isMute = false;
5873         info.isLocked = false;
5874         info.effectsList = EffectsList(true);
5875         if (d.video_track->isChecked()) {
5876             info.type = VIDEOTRACK;
5877             info.isBlind = false;
5878         } else {
5879             info.type = AUDIOTRACK;
5880             info.isBlind = true;
5881         }
5882         AddTrackCommand *addTrack = new AddTrackCommand(this, ix, info, true);
5883         m_commandStack->push(addTrack);
5884         setDocumentModified();
5885     }
5886 }
5887
5888 void CustomTrackView::slotDeleteTrack(int ix)
5889 {
5890     if (m_document->tracksCount() < 2) return;
5891     TrackDialog d(m_document, parentWidget());
5892     d.comboTracks->setCurrentIndex(ix);
5893     d.label->setText(i18n("Delete track"));
5894     d.before_select->setHidden(true);
5895     d.setWindowTitle(i18n("Delete Track"));
5896     d.video_track->setHidden(true);
5897     d.audio_track->setHidden(true);
5898     if (d.exec() == QDialog::Accepted) {
5899         ix = d.comboTracks->currentIndex();
5900         TrackInfo info = m_document->trackInfoAt(m_document->tracksCount() - ix - 1);
5901         deleteTimelineTrack(ix, info);
5902         setDocumentModified();
5903         /*AddTrackCommand* command = new AddTrackCommand(this, ix, info, false);
5904         m_commandStack->push(command);*/
5905     }
5906 }
5907
5908 void CustomTrackView::slotConfigTracks(int ix)
5909 {
5910     TracksConfigDialog d(m_document, ix, parentWidget());
5911     if (d.exec() == QDialog::Accepted) {
5912         ConfigTracksCommand *configTracks = new ConfigTracksCommand(this, m_document->tracksList(), d.tracksList());
5913         m_commandStack->push(configTracks);
5914         QList <int> toDelete = d.deletedTracks();
5915         for (int i = 0; i < toDelete.count(); ++i) {
5916             TrackInfo info = m_document->trackInfoAt(m_document->tracksCount() - toDelete.at(i) + i - 1);
5917             deleteTimelineTrack(toDelete.at(i) - i, info);
5918         }
5919         setDocumentModified();
5920     }
5921 }
5922
5923 void CustomTrackView::deleteTimelineTrack(int ix, TrackInfo trackinfo)
5924 {
5925     if (m_document->tracksCount() < 2) return;
5926     double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
5927     QRectF r(0, startY, sceneRect().width(), m_tracksHeight / 2 - 1);
5928     QList<QGraphicsItem *> selection = m_scene->items(r);
5929     QUndoCommand *deleteTrack = new QUndoCommand();
5930     deleteTrack->setText("Delete track");
5931
5932     // Delete all clips in selected track
5933     for (int i = 0; i < selection.count(); i++) {
5934         if (selection.at(i)->type() == AVWIDGET) {
5935             ClipItem *item =  static_cast <ClipItem *>(selection.at(i));
5936             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), false, false, false, true, deleteTrack);
5937             m_waitingThumbs.removeAll(item);
5938             m_scene->removeItem(item);
5939             delete item;
5940             item = NULL;
5941         } else if (selection.at(i)->type() == TRANSITIONWIDGET) {
5942             Transition *item =  static_cast <Transition *>(selection.at(i));
5943             new AddTransitionCommand(this, item->info(), item->transitionEndTrack(), item->toXML(), true, false, deleteTrack);
5944             m_scene->removeItem(item);
5945             delete item;
5946             item = NULL;
5947         }
5948     }
5949
5950     new AddTrackCommand(this, ix, trackinfo, false, deleteTrack);
5951     m_commandStack->push(deleteTrack);
5952 }
5953
5954 void CustomTrackView::autoTransition()
5955 {
5956     QList<QGraphicsItem *> itemList = scene()->selectedItems();
5957     if (itemList.count() != 1 || itemList.at(0)->type() != TRANSITIONWIDGET) {
5958         emit displayMessage(i18n("You must select one transition for this action"), ErrorMessage);
5959         return;
5960     }
5961     Transition *tr = static_cast <Transition*>(itemList.at(0));
5962     tr->setAutomatic(!tr->isAutomatic());
5963     QDomElement transition = tr->toXML();
5964     m_document->renderer()->mltUpdateTransition(transition.attribute("tag"), transition.attribute("tag"), transition.attribute("transition_btrack").toInt(), m_document->tracksCount() - transition.attribute("transition_atrack").toInt(), tr->startPos(), tr->endPos(), transition);
5965     setDocumentModified();
5966 }
5967
5968 void CustomTrackView::clipNameChanged(const QString &id, const QString &name)
5969 {
5970     QList<QGraphicsItem *> list = scene()->items();
5971     ClipItem *clip = NULL;
5972     for (int i = 0; i < list.size(); ++i) {
5973         if (list.at(i)->type() == AVWIDGET) {
5974             clip = static_cast <ClipItem *>(list.at(i));
5975             if (clip->clipProducer() == id) {
5976                 clip->setClipName(name);
5977             }
5978         }
5979     }
5980     viewport()->update();
5981 }
5982
5983 void CustomTrackView::getClipAvailableSpace(AbstractClipItem *item, GenTime &minimum, GenTime &maximum)
5984 {
5985     minimum = GenTime();
5986     maximum = GenTime();
5987     QList<QGraphicsItem *> selection;
5988     selection = m_scene->items(0, item->track() * m_tracksHeight + m_tracksHeight / 2, sceneRect().width(), 2);
5989     selection.removeAll(item);
5990     for (int i = 0; i < selection.count(); i++) {
5991         AbstractClipItem *clip = static_cast <AbstractClipItem *>(selection.at(i));
5992         if (clip && clip->type() == AVWIDGET) {
5993             if (clip->endPos() <= item->startPos() && clip->endPos() > minimum) minimum = clip->endPos();
5994             if (clip->startPos() > item->startPos() && (clip->startPos() < maximum || maximum == GenTime())) maximum = clip->startPos();
5995         }
5996     }
5997 }
5998
5999 void CustomTrackView::getTransitionAvailableSpace(AbstractClipItem *item, GenTime &minimum, GenTime &maximum)
6000 {
6001     minimum = GenTime();
6002     maximum = GenTime();
6003     QList<QGraphicsItem *> selection;
6004     selection = m_scene->items(0, (item->track() + 1) * m_tracksHeight, sceneRect().width(), 2);
6005     selection.removeAll(item);
6006     for (int i = 0; i < selection.count(); i++) {
6007         AbstractClipItem *clip = static_cast <AbstractClipItem *>(selection.at(i));
6008         if (clip && clip->type() == TRANSITIONWIDGET) {
6009             if (clip->endPos() <= item->startPos() && clip->endPos() > minimum) minimum = clip->endPos();
6010             if (clip->startPos() > item->startPos() && (clip->startPos() < maximum || maximum == GenTime())) maximum = clip->startPos();
6011         }
6012     }
6013 }
6014
6015 void CustomTrackView::loadGroups(const QDomNodeList &groups)
6016 {
6017     for (int i = 0; i < groups.count(); i++) {
6018         QDomNodeList children = groups.at(i).childNodes();
6019         scene()->clearSelection();
6020         for (int nodeindex = 0; nodeindex < children.count(); nodeindex++) {
6021             QDomElement elem = children.item(nodeindex).toElement();
6022             int pos = elem.attribute("position").toInt();
6023             int track = elem.attribute("track").toInt();
6024             if (elem.tagName() == "clipitem") {
6025                 ClipItem *clip = getClipItemAt(pos, track); //m_document->tracksCount() - transitiontrack);
6026                 if (clip) clip->setSelected(true);
6027             } else {
6028                 Transition *clip = getTransitionItemAt(pos, track); //m_document->tracksCount() - transitiontrack);
6029                 if (clip) clip->setSelected(true);
6030             }
6031         }
6032         groupSelectedItems(false, true);
6033     }
6034 }
6035
6036 void CustomTrackView::splitAudio()
6037 {
6038     resetSelectionGroup();
6039     QList<QGraphicsItem *> selection = scene()->selectedItems();
6040     if (selection.isEmpty()) {
6041         emit displayMessage(i18n("You must select at least one clip for this action"), ErrorMessage);
6042         return;
6043     }
6044     QUndoCommand *splitCommand = new QUndoCommand();
6045     splitCommand->setText(i18n("Split audio"));
6046     for (int i = 0; i < selection.count(); i++) {
6047         if (selection.at(i)->type() == AVWIDGET) {
6048             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
6049             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
6050                 if (clip->parentItem()) {
6051                     emit displayMessage(i18n("Cannot split audio of grouped clips"), ErrorMessage);
6052                 } else {
6053                     EffectsList effects;
6054                     effects.clone(clip->effectList());
6055                     new SplitAudioCommand(this, clip->track(), clip->startPos(), effects, splitCommand);
6056                 }
6057             }
6058         }
6059     }
6060     if (splitCommand->childCount()) {
6061         updateTrackDuration(-1, splitCommand);
6062         m_commandStack->push(splitCommand);
6063     }
6064 }
6065
6066 void CustomTrackView::setAudioAlignReference()
6067 {
6068     QList<QGraphicsItem *> selection = scene()->selectedItems();
6069     if (selection.isEmpty() || selection.size() > 1) {
6070         emit displayMessage(i18n("You must select exactly one clip for the audio reference."), ErrorMessage);
6071         return;
6072     }
6073     if (m_audioCorrelator != NULL) {
6074         delete m_audioCorrelator;
6075     }
6076     if (selection.at(0)->type() == AVWIDGET) {
6077         ClipItem *clip = static_cast<ClipItem*>(selection.at(0));
6078         if (clip->clipType() == AV || clip->clipType() == AUDIO) {
6079             m_audioAlignmentReference = clip;
6080
6081             AudioEnvelope *envelope = new AudioEnvelope(clip->getProducer(clip->track()));
6082             m_audioCorrelator = new AudioCorrelation(envelope);
6083
6084             envelope->drawEnvelope().save("kdenlive-audio-reference-envelope.png");
6085             envelope->dumpInfo();
6086
6087             emit displayMessage(i18n("Audio align reference set."), InformationMessage);
6088         }
6089         return;
6090     }
6091     emit displayMessage(i18n("Reference for audio alignment must contain audio data."), ErrorMessage);
6092 }
6093
6094 void CustomTrackView::alignAudio()
6095 {
6096     bool referenceOK = true;
6097     if (m_audioCorrelator == NULL) {
6098         referenceOK = false;
6099     }
6100     if (referenceOK) {
6101         if (!scene()->items().contains(m_audioAlignmentReference)) {
6102             // The reference item has been deleted from the timeline (or so)
6103             referenceOK = false;
6104         }
6105     }
6106     if (!referenceOK) {
6107         emit displayMessage(i18n("Audio alignment reference not yet set."), InformationMessage);
6108         return;
6109     }
6110
6111     int counter = 0;
6112     QList<QGraphicsItem *> selection = scene()->selectedItems();
6113     foreach (QGraphicsItem *item, selection) {
6114         if (item->type() == AVWIDGET) {
6115
6116             ClipItem *clip = static_cast<ClipItem*>(item);
6117             if (clip == m_audioAlignmentReference) {
6118                 continue;
6119             }
6120
6121             if (clip->clipType() == AV || clip->clipType() == AUDIO) {
6122                 AudioEnvelope *envelope = new AudioEnvelope(clip->getProducer(clip->track()),
6123                                                             clip->info().cropStart.frames(m_document->fps()),
6124                                                             clip->info().cropDuration.frames(m_document->fps()));
6125
6126                 // FFT only for larger vectors. We could use it all time, but for small vectors
6127                 // the (anyway not noticeable) overhead is smaller with a nested for loop correlation.
6128                 int index = m_audioCorrelator->addChild(envelope, envelope->envelopeSize() > 200);
6129                 int shift = m_audioCorrelator->getShift(index);
6130                 counter++;
6131
6132                 m_audioCorrelator->info(index)->toImage().save("kdenlive-audio-align-cross-correlation.png");
6133                 envelope->drawEnvelope().save("kdenlive-audio-align-envelope.png");
6134                 envelope->dumpInfo();
6135
6136 #ifdef DEBUG
6137                 int targetPos = m_audioAlignmentReference->startPos().frames(m_document->fps()) + shift;
6138                 qDebug() << "Reference starts at " << m_audioAlignmentReference->startPos().frames(m_document->fps());
6139                 qDebug() << "We will start at " << targetPos;
6140                 qDebug() << "to shift by " << shift;
6141                 qDebug() << "(eventually)";
6142                 qDebug() << "(maybe)";
6143 #endif
6144
6145
6146                 QUndoCommand *moveCommand = new QUndoCommand();
6147
6148                 GenTime add(shift, m_document->fps());
6149                 ItemInfo start = clip->info();
6150
6151                 ItemInfo end = start;
6152                 end.startPos = m_audioAlignmentReference->startPos() + add - m_audioAlignmentReference->cropStart();
6153                 end.endPos = end.startPos + start.cropDuration;
6154
6155                 if ( end.startPos.seconds() < 0 ) {
6156                     // Clip would start before 0, so crop it first
6157                     GenTime cropBy = -end.startPos;
6158
6159 #ifdef DEBUG
6160                     qDebug() << "Need to crop clip. " << start;
6161                     qDebug() << "end.startPos: " << end.startPos.toString() << ", cropBy: " << cropBy.toString();
6162 #endif
6163
6164                     ItemInfo resized = start;
6165                     resized.startPos += cropBy;
6166
6167                     resizeClip(start, resized);
6168                     new ResizeClipCommand(this, start, resized, false, false, moveCommand);
6169
6170                     start = clip->info();
6171                     end.startPos += cropBy;
6172
6173 #ifdef DEBUG
6174                     qDebug() << "Clip cropped. " << start;
6175                     qDebug() << "Moving to: " << end;
6176 #endif
6177                 }
6178
6179                 if (itemCollision(clip, end)) {
6180                     delete moveCommand;
6181                     emit displayMessage(i18n("Unable to move clip due to collision."), ErrorMessage);
6182                     return;
6183                 }
6184
6185                 moveCommand->setText(i18n("Auto-align clip"));
6186                 new MoveClipCommand(this, start, end, true, moveCommand);
6187                 updateTrackDuration(clip->track(), moveCommand);
6188                 m_commandStack->push(moveCommand);
6189
6190             }
6191         }
6192     }
6193
6194     if (counter == 0) {
6195         emit displayMessage(i18n("No audio clips selected."), ErrorMessage);
6196     } else {
6197         emit displayMessage(i18n("Auto-aligned %1 clips.").arg(counter), InformationMessage);
6198     }
6199 }
6200
6201 void CustomTrackView::doSplitAudio(const GenTime &pos, int track, EffectsList effects, bool split)
6202 {
6203     ClipItem *clip = getClipItemAt(pos, track);
6204     if (clip == NULL) {
6205         kDebug() << "// Cannot find clip to split!!!";
6206         return;
6207     }
6208     if (split) {
6209         int start = pos.frames(m_document->fps());
6210         int freetrack = m_document->tracksCount() - track - 1;
6211
6212         // do not split audio when we are on an audio track
6213         if (m_document->trackInfoAt(freetrack).type == AUDIOTRACK)
6214             return;
6215
6216         for (; freetrack > 0; freetrack--) {
6217             //kDebug() << "// CHK DOC TRK:" << freetrack << ", DUR:" << m_document->renderer()->mltTrackDuration(freetrack);
6218             if (m_document->trackInfoAt(freetrack - 1).type == AUDIOTRACK && !m_document->trackInfoAt(freetrack - 1).isLocked) {
6219                 //kDebug() << "// CHK DOC TRK:" << freetrack << ", DUR:" << m_document->renderer()->mltTrackDuration(freetrack);
6220                 if (m_document->renderer()->mltTrackDuration(freetrack) < start || m_document->renderer()->mltGetSpaceLength(pos, freetrack, false) >= clip->cropDuration().frames(m_document->fps())) {
6221                     //kDebug() << "FOUND SPACE ON TRK: " << freetrack;
6222                     break;
6223                 }
6224             }
6225         }
6226         kDebug() << "GOT TRK: " << track;
6227         if (freetrack == 0) {
6228             emit displayMessage(i18n("No empty space to put clip audio"), ErrorMessage);
6229         } else {
6230             ItemInfo info = clip->info();
6231             info.track = m_document->tracksCount() - freetrack;
6232             addClip(clip->xml(), clip->clipProducer(), info, clip->effectList());
6233             scene()->clearSelection();
6234             clip->setSelected(true);
6235             ClipItem *audioClip = getClipItemAt(start, info.track);
6236             if (audioClip) {
6237                 Mlt::Tractor *tractor = m_document->renderer()->lockService();
6238                 clip->setVideoOnly(true);
6239                 if (m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - track, start, clip->baseClip()->videoProducer()) == false) {
6240                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, track), ErrorMessage);
6241                 }
6242                 if (m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - info.track, start, clip->baseClip()->audioProducer(info.track)) == false) {
6243                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, info.track), ErrorMessage);
6244                 }
6245                 m_document->renderer()->unlockService(tractor);
6246                 audioClip->setSelected(true);
6247                 audioClip->setAudioOnly(true);
6248
6249                 // keep video effects, move audio effects to audio clip
6250                 int videoIx = 0;
6251                 int audioIx = 0;
6252                 for (int i = 0; i < effects.count(); ++i) {
6253                     if (effects.at(i).attribute("type") == "audio") {
6254                         deleteEffect(m_document->tracksCount() - track, pos, clip->effectAt(videoIx));
6255                         audioIx++;
6256                     } else {
6257                         deleteEffect(freetrack, pos, audioClip->effectAt(audioIx));
6258                         videoIx++;
6259                     }
6260                 }
6261
6262                 groupSelectedItems(false, true);
6263             }
6264         }
6265     } else {
6266         // unsplit clip: remove audio part and change video part to normal clip
6267         if (clip->parentItem() == NULL || clip->parentItem()->type() != GROUPWIDGET) {
6268             kDebug() << "//CANNOT FIND CLP GRP";
6269             return;
6270         }
6271         AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(clip->parentItem());
6272         QList<QGraphicsItem *> children = grp->childItems();
6273         if (children.count() != 2) {
6274             kDebug() << "//SOMETHING IS WRONG WITH CLP GRP";
6275             return;
6276         }
6277         for (int i = 0; i < children.count(); i++) {
6278             if (children.at(i) != clip) {
6279                 ClipItem *clp = static_cast <ClipItem *>(children.at(i));
6280                 ItemInfo info = clip->info();
6281                 deleteClip(clp->info());
6282                 clip->setVideoOnly(false);
6283                 Mlt::Tractor *tractor = m_document->renderer()->lockService();
6284                 if (!m_document->renderer()->mltUpdateClipProducer(
6285                             tractor,
6286                             m_document->tracksCount() - info.track,
6287                             info.startPos.frames(m_document->fps()),
6288                             clip->baseClip()->getProducer(info.track))) {
6289
6290                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)",
6291                                              info.startPos.frames(m_document->fps()), info.track),
6292                                         ErrorMessage);
6293                 }
6294                 m_document->renderer()->unlockService(tractor);
6295
6296                 // re-add audio effects
6297                 for (int i = 0; i < effects.count(); ++i) {
6298                     if (effects.at(i).attribute("type") == "audio") {
6299                         addEffect(m_document->tracksCount() - track, pos, effects.at(i));
6300                     }
6301                 }
6302
6303                 break;
6304             }
6305         }
6306         clip->setFlag(QGraphicsItem::ItemIsMovable, true);
6307         m_document->clipManager()->removeGroup(grp);
6308         scene()->destroyItemGroup(grp);
6309     }
6310 }
6311
6312 void CustomTrackView::setVideoOnly()
6313 {
6314     resetSelectionGroup();
6315     QList<QGraphicsItem *> selection = scene()->selectedItems();
6316     if (selection.isEmpty()) {
6317         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
6318         return;
6319     }
6320     QUndoCommand *videoCommand = new QUndoCommand();
6321     videoCommand->setText(i18n("Video only"));
6322     for (int i = 0; i < selection.count(); i++) {
6323         if (selection.at(i)->type() == AVWIDGET) {
6324             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
6325             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
6326                 if (clip->parentItem()) {
6327                     emit displayMessage(i18n("Cannot change grouped clips"), ErrorMessage);
6328                 } else {
6329                     new ChangeClipTypeCommand(this, clip->track(), clip->startPos(), true, false, clip->isVideoOnly(), clip->isAudioOnly(), videoCommand);
6330                 }
6331             }
6332         }
6333     }
6334     m_commandStack->push(videoCommand);
6335 }
6336
6337 void CustomTrackView::setAudioOnly()
6338 {
6339     resetSelectionGroup();
6340     QList<QGraphicsItem *> selection = scene()->selectedItems();
6341     if (selection.isEmpty()) {
6342         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
6343         return;
6344     }
6345     QUndoCommand *videoCommand = new QUndoCommand();
6346     videoCommand->setText(i18n("Audio only"));
6347     for (int i = 0; i < selection.count(); i++) {
6348         if (selection.at(i)->type() == AVWIDGET) {
6349             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
6350             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
6351                 if (clip->parentItem()) {
6352                     emit displayMessage(i18n("Cannot change grouped clips"), ErrorMessage);
6353                 } else {
6354                     new ChangeClipTypeCommand(this, clip->track(), clip->startPos(), false, true, clip->isVideoOnly(), clip->isAudioOnly(), videoCommand);
6355                 }
6356             }
6357         }
6358     }
6359     m_commandStack->push(videoCommand);
6360 }
6361
6362 void CustomTrackView::setAudioAndVideo()
6363 {
6364     resetSelectionGroup();
6365     QList<QGraphicsItem *> selection = scene()->selectedItems();
6366     if (selection.isEmpty()) {
6367         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
6368         return;
6369     }
6370     QUndoCommand *videoCommand = new QUndoCommand();
6371     videoCommand->setText(i18n("Audio and Video"));
6372     for (int i = 0; i < selection.count(); i++) {
6373         if (selection.at(i)->type() == AVWIDGET) {
6374             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
6375             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
6376                 if (clip->parentItem()) {
6377                     emit displayMessage(i18n("Cannot change grouped clips"), ErrorMessage);
6378                 } else {
6379                     new ChangeClipTypeCommand(this, clip->track(), clip->startPos(), false, false, clip->isVideoOnly(), clip->isAudioOnly(), videoCommand);
6380                 }
6381             }
6382         }
6383     }
6384     m_commandStack->push(videoCommand);
6385 }
6386
6387 void CustomTrackView::doChangeClipType(const GenTime &pos, int track, bool videoOnly, bool audioOnly)
6388 {
6389     ClipItem *clip = getClipItemAt(pos, track);
6390     if (clip == NULL) {
6391         kDebug() << "// Cannot find clip to split!!!";
6392         return;
6393     }
6394     Mlt::Tractor *tractor = m_document->renderer()->lockService();
6395     if (videoOnly) {
6396         int start = pos.frames(m_document->fps());
6397         clip->setVideoOnly(true);
6398         clip->setAudioOnly(false);
6399         if (m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - track, start, clip->baseClip()->videoProducer()) == false) {
6400             emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, track), ErrorMessage);
6401         }
6402     } else if (audioOnly) {
6403         int start = pos.frames(m_document->fps());
6404         clip->setAudioOnly(true);
6405         clip->setVideoOnly(false);
6406         if (m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - track, start, clip->baseClip()->audioProducer(track)) == false) {
6407             emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, track), ErrorMessage);
6408         }
6409     } else {
6410         int start = pos.frames(m_document->fps());
6411         clip->setAudioOnly(false);
6412         clip->setVideoOnly(false);
6413         if (m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - track, start, clip->baseClip()->getProducer(track)) == false) {
6414             emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, track), ErrorMessage);
6415         }
6416     }
6417     m_document->renderer()->unlockService(tractor);
6418     clip->update();
6419     setDocumentModified();
6420 }
6421
6422 void CustomTrackView::updateClipTypeActions(ClipItem *clip)
6423 {
6424     if (clip == NULL || (clip->clipType() != AV && clip->clipType() != PLAYLIST)) {
6425         m_clipTypeGroup->setEnabled(false);
6426     } else {
6427         m_clipTypeGroup->setEnabled(true);
6428         QList <QAction *> actions = m_clipTypeGroup->actions();
6429         QString lookup;
6430         if (clip->isAudioOnly()) lookup = "clip_audio_only";
6431         else if (clip->isVideoOnly()) lookup = "clip_video_only";
6432         else  lookup = "clip_audio_and_video";
6433         for (int i = 0; i < actions.count(); i++) {
6434             if (actions.at(i)->data().toString() == lookup) {
6435                 actions.at(i)->setChecked(true);
6436                 break;
6437             }
6438         }
6439     }
6440 }
6441
6442 void CustomTrackView::slotGoToMarker(QAction *action)
6443 {
6444     int pos = action->data().toInt();
6445     setCursorPos(pos, true);
6446 }
6447
6448 void CustomTrackView::reloadTransitionLumas()
6449 {
6450     QString lumaNames;
6451     QString lumaFiles;
6452     QDomElement lumaTransition = MainWindow::transitions.getEffectByTag("luma", "luma");
6453     QDomNodeList params = lumaTransition.elementsByTagName("parameter");
6454     for (int i = 0; i < params.count(); i++) {
6455         QDomElement e = params.item(i).toElement();
6456         if (e.attribute("tag") == "resource") {
6457             lumaNames = e.attribute("paramlistdisplay");
6458             lumaFiles = e.attribute("paramlist");
6459             break;
6460         }
6461     }
6462
6463     QList<QGraphicsItem *> itemList = items();
6464     Transition *transitionitem;
6465     QDomElement transitionXml;
6466     for (int i = 0; i < itemList.count(); i++) {
6467         if (itemList.at(i)->type() == TRANSITIONWIDGET) {
6468             transitionitem = static_cast <Transition*>(itemList.at(i));
6469             transitionXml = transitionitem->toXML();
6470             if (transitionXml.attribute("id") == "luma" && transitionXml.attribute("tag") == "luma") {
6471                 QDomNodeList params = transitionXml.elementsByTagName("parameter");
6472                 for (int i = 0; i < params.count(); i++) {
6473                     QDomElement e = params.item(i).toElement();
6474                     if (e.attribute("tag") == "resource") {
6475                         e.setAttribute("paramlistdisplay", lumaNames);
6476                         e.setAttribute("paramlist", lumaFiles);
6477                         break;
6478                     }
6479                 }
6480             }
6481             if (transitionXml.attribute("id") == "composite" && transitionXml.attribute("tag") == "composite") {
6482                 QDomNodeList params = transitionXml.elementsByTagName("parameter");
6483                 for (int i = 0; i < params.count(); i++) {
6484                     QDomElement e = params.item(i).toElement();
6485                     if (e.attribute("tag") == "luma") {
6486                         e.setAttribute("paramlistdisplay", lumaNames);
6487                         e.setAttribute("paramlist", lumaFiles);
6488                         break;
6489                     }
6490                 }
6491             }
6492         }
6493     }
6494     emit transitionItemSelected(NULL);
6495 }
6496
6497 double CustomTrackView::fps() const
6498 {
6499     return m_document->fps();
6500 }
6501
6502 void CustomTrackView::updateProjectFps()
6503 {
6504     // update all clips to the new fps
6505     resetSelectionGroup();
6506     scene()->clearSelection();
6507     m_dragItem = NULL;
6508     QList<QGraphicsItem *> itemList = items();
6509     for (int i = 0; i < itemList.count(); i++) {
6510         // remove all items and re-add them one by one
6511         if (itemList.at(i) != m_cursorLine && itemList.at(i)->parentItem() == NULL) m_scene->removeItem(itemList.at(i));
6512     }
6513     for (int i = 0; i < itemList.count(); i++) {
6514         if (itemList.at(i)->parentItem() == 0 && (itemList.at(i)->type() == AVWIDGET || itemList.at(i)->type() == TRANSITIONWIDGET)) {
6515             AbstractClipItem *clip = static_cast <AbstractClipItem *>(itemList.at(i));
6516             clip->updateFps(m_document->fps());
6517             m_scene->addItem(clip);
6518         } else if (itemList.at(i)->type() == GROUPWIDGET) {
6519             AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(itemList.at(i));
6520             QList<QGraphicsItem *> children = grp->childItems();
6521             for (int j = 0; j < children.count(); j++) {
6522                 if (children.at(j)->type() == AVWIDGET || children.at(j)->type() == TRANSITIONWIDGET) {
6523                     AbstractClipItem *clip = static_cast <AbstractClipItem *>(children.at(j));
6524                     clip->setFlag(QGraphicsItem::ItemIsMovable, true);
6525                     clip->updateFps(m_document->fps());
6526                 }
6527             }
6528             m_document->clipManager()->removeGroup(grp);
6529             m_scene->addItem(grp);
6530             scene()->destroyItemGroup(grp);
6531             scene()->clearSelection();
6532             for (int j = 0; j < children.count(); j++) {
6533                 if (children.at(j)->type() == AVWIDGET || children.at(j)->type() == TRANSITIONWIDGET) {
6534                     //children.at(j)->setParentItem(0);
6535                     children.at(j)->setSelected(true);
6536                 }
6537             }
6538             groupSelectedItems(true, true);
6539         } else if (itemList.at(i)->type() == GUIDEITEM) {
6540             Guide *g = static_cast<Guide *>(itemList.at(i));
6541             g->updatePos();
6542             m_scene->addItem(g);
6543         }
6544     }
6545     viewport()->update();
6546 }
6547
6548 void CustomTrackView::slotTrackDown()
6549 {
6550     if (m_selectedTrack > m_document->tracksCount() - 2) m_selectedTrack = 0;
6551     else m_selectedTrack++;
6552     emit updateTrackHeaders();
6553     QRectF rect(mapToScene(QPoint(10, 0)).x(), m_selectedTrack * m_tracksHeight, 10, m_tracksHeight);
6554     ensureVisible(rect, 0, 0);
6555     viewport()->update();
6556 }
6557
6558 void CustomTrackView::slotTrackUp()
6559 {
6560     if (m_selectedTrack > 0) m_selectedTrack--;
6561     else m_selectedTrack = m_document->tracksCount() - 1;
6562     emit updateTrackHeaders();
6563     QRectF rect(mapToScene(QPoint(10, 0)).x(), m_selectedTrack * m_tracksHeight, 10, m_tracksHeight);
6564     ensureVisible(rect, 0, 0);
6565     viewport()->update();
6566 }
6567
6568 int CustomTrackView::selectedTrack() const
6569 {
6570     return m_selectedTrack;
6571 }
6572
6573 QStringList CustomTrackView::selectedClips() const
6574 {
6575     QStringList clipIds;
6576     QList<QGraphicsItem *> selection = m_scene->selectedItems();
6577     for (int i = 0; i < selection.count(); i++) {
6578         if (selection.at(i)->type() == AVWIDGET) {
6579             ClipItem *item = (ClipItem *)selection.at(i);
6580             clipIds << item->clipProducer();
6581         }
6582     }
6583     return clipIds;
6584 }
6585
6586 QList<ClipItem *> CustomTrackView::selectedClipItems() const
6587 {
6588     QList<ClipItem *> clips;
6589     QList<QGraphicsItem *> selection = m_scene->selectedItems();
6590     for (int i = 0; i < selection.count(); i++) {
6591         if (selection.at(i)->type() == AVWIDGET) {
6592             clips.append((ClipItem *)selection.at(i));
6593         }
6594     }
6595     return clips;
6596 }
6597
6598 void CustomTrackView::slotSelectTrack(int ix)
6599 {
6600     m_selectedTrack = qMax(0, ix);
6601     m_selectedTrack = qMin(ix, m_document->tracksCount() - 1);
6602     emit updateTrackHeaders();
6603     QRectF rect(mapToScene(QPoint(10, 0)).x(), m_selectedTrack * m_tracksHeight, 10, m_tracksHeight);
6604     ensureVisible(rect, 0, 0);
6605     viewport()->update();
6606 }
6607
6608 void CustomTrackView::slotSelectClipsInTrack()
6609 {
6610     QRectF rect(0, m_selectedTrack * m_tracksHeight + m_tracksHeight / 2, sceneRect().width(), m_tracksHeight / 2 - 1);
6611     QList<QGraphicsItem *> selection = m_scene->items(rect);
6612     m_scene->clearSelection();
6613     for (int i = 0; i < selection.count(); i++) {
6614         if (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET || selection.at(i)->type() == GROUPWIDGET) {
6615             selection.at(i)->setSelected(true);
6616         }
6617     }    
6618     resetSelectionGroup();
6619     groupSelectedItems();
6620 }
6621
6622 void CustomTrackView::slotSelectAllClips()
6623 {
6624     QList<QGraphicsItem *> selection = m_scene->items();
6625     m_scene->clearSelection();
6626     for (int i = 0; i < selection.count(); i++) {
6627         if (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET  || selection.at(i)->type() == GROUPWIDGET) {
6628             selection.at(i)->setSelected(true);
6629         }
6630     }
6631     resetSelectionGroup();
6632     groupSelectedItems();
6633 }
6634
6635 void CustomTrackView::selectClip(bool add, bool group, int track, int pos)
6636 {
6637     QRectF rect;
6638     if (track != -1 && pos != -1)
6639         rect = QRectF(pos, track * m_tracksHeight + m_tracksHeight / 2, 1, 1);
6640     else
6641         rect = QRectF(m_cursorPos, m_selectedTrack * m_tracksHeight + m_tracksHeight / 2, 1, 1);
6642     QList<QGraphicsItem *> selection = m_scene->items(rect);
6643     resetSelectionGroup(group);
6644     if (!group) m_scene->clearSelection();
6645     for (int i = 0; i < selection.count(); i++) {
6646         if (selection.at(i)->type() == AVWIDGET) {
6647             selection.at(i)->setSelected(add);
6648             break;
6649         }
6650     }
6651     if (group) groupSelectedItems();
6652 }
6653
6654 void CustomTrackView::selectTransition(bool add, bool group)
6655 {
6656     QRectF rect(m_cursorPos, m_selectedTrack * m_tracksHeight + m_tracksHeight, 1, 1);
6657     QList<QGraphicsItem *> selection = m_scene->items(rect);
6658     resetSelectionGroup(group);
6659     if (!group) m_scene->clearSelection();
6660     for (int i = 0; i < selection.count(); i++) {
6661         if (selection.at(i)->type() == TRANSITIONWIDGET) {
6662             selection.at(i)->setSelected(add);
6663             break;
6664         }
6665     }
6666     if (group) groupSelectedItems();
6667 }
6668
6669 QStringList CustomTrackView::extractTransitionsLumas()
6670 {
6671     QStringList urls;
6672     QList<QGraphicsItem *> itemList = items();
6673     Transition *transitionitem;
6674     QDomElement transitionXml;
6675     for (int i = 0; i < itemList.count(); i++) {
6676         if (itemList.at(i)->type() == TRANSITIONWIDGET) {
6677             transitionitem = static_cast <Transition*>(itemList.at(i));
6678             transitionXml = transitionitem->toXML();
6679             // luma files in transitions can be in "resource" or "luma" property
6680             QString luma = EffectsList::parameter(transitionXml, "luma");
6681             if (luma.isEmpty()) luma = EffectsList::parameter(transitionXml, "resource");
6682             if (!luma.isEmpty()) urls << KUrl(luma).path();
6683         }
6684     }
6685     urls.removeDuplicates();
6686     return urls;
6687 }
6688
6689 void CustomTrackView::setEditMode(EDITMODE mode)
6690 {
6691     m_scene->setEditMode(mode);
6692 }
6693
6694 void CustomTrackView::checkTrackSequence(int track)
6695 {
6696     QList <int> times = m_document->renderer()->checkTrackSequence(m_document->tracksCount() - track);
6697     //track = m_document->tracksCount() -track;
6698     QRectF rect(0, track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width(), 2);
6699     QList<QGraphicsItem *> selection = m_scene->items(rect);
6700     QList <int> timelineList;
6701     timelineList.append(0);
6702     for (int i = 0; i < selection.count(); i++) {
6703         if (selection.at(i)->type() == AVWIDGET) {
6704             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
6705             int start = clip->startPos().frames(m_document->fps());
6706             int end = clip->endPos().frames(m_document->fps());
6707             if (!timelineList.contains(start)) timelineList.append(start);
6708             if (!timelineList.contains(end)) timelineList.append(end);
6709         }
6710     }
6711     qSort(timelineList);
6712     kDebug() << "// COMPARE:\n" << times << "\n" << timelineList << "\n-------------------";
6713     if (times != timelineList) KMessageBox::sorry(this, i18n("error"), i18n("TRACTOR"));
6714 }
6715
6716 void CustomTrackView::insertZoneOverwrite(QStringList data, int in)
6717 {
6718     if (data.isEmpty()) return;
6719     DocClipBase *clip = m_document->getBaseClip(data.at(0));
6720     ItemInfo info;
6721     info.startPos = GenTime(in, m_document->fps());
6722     info.cropStart = GenTime(data.at(1).toInt(), m_document->fps());
6723     info.endPos = info.startPos + GenTime(data.at(2).toInt(), m_document->fps()) - info.cropStart;
6724     info.cropDuration = info.endPos - info.startPos;
6725     info.track = m_selectedTrack;
6726     QUndoCommand *addCommand = new QUndoCommand();
6727     addCommand->setText(i18n("Insert clip"));
6728     adjustTimelineClips(OVERWRITEEDIT, NULL, info, addCommand);
6729     new AddTimelineClipCommand(this, clip->toXML(), clip->getId(), info, EffectsList(), true, false, true, false, addCommand);
6730     updateTrackDuration(info.track, addCommand);
6731     m_commandStack->push(addCommand);
6732
6733     selectClip(true, false, m_selectedTrack, in);
6734     // Automatic audio split
6735     if (KdenliveSettings::splitaudio())
6736         splitAudio();
6737 }
6738
6739 void CustomTrackView::clearSelection(bool emitInfo)
6740 {
6741     resetSelectionGroup();
6742     scene()->clearSelection();
6743     m_dragItem = NULL;
6744     if (emitInfo) emit clipItemSelected(NULL);
6745 }
6746
6747 void CustomTrackView::updatePalette()
6748 {
6749     m_activeTrackBrush = KStatefulBrush(KColorScheme::View, KColorScheme::ActiveBackground, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
6750     if (m_cursorLine) {
6751         QPen pen1 = QPen();
6752         pen1.setWidth(1);
6753         pen1.setColor(palette().text().color());
6754         m_cursorLine->setPen(pen1);
6755     }
6756 }
6757
6758 void CustomTrackView::removeTipAnimation()
6759 {
6760     if (m_visualTip) {
6761         scene()->removeItem(m_visualTip);
6762         m_animationTimer->stop();
6763         delete m_animation;
6764         m_animation = NULL;
6765         delete m_visualTip;
6766         m_visualTip = NULL;
6767     }
6768 }
6769
6770 void CustomTrackView::setTipAnimation(AbstractClipItem *clip, OPERATIONTYPE mode, const double size)
6771 {
6772     if (m_visualTip == NULL) {
6773         QRectF rect = clip->sceneBoundingRect();
6774         m_animation = new QGraphicsItemAnimation;
6775         m_animation->setTimeLine(m_animationTimer);
6776         m_animation->setScaleAt(1, 1, 1);
6777         QPolygon polygon;
6778         switch (mode) {
6779         case FADEIN:
6780         case FADEOUT:
6781             m_visualTip = new QGraphicsEllipseItem(-size, -size, size * 2, size * 2);
6782             ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
6783             ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
6784             if (mode == FADEIN)
6785                 m_visualTip->setPos(rect.x() + ((ClipItem *) clip)->fadeIn(), rect.y());
6786             else
6787                 m_visualTip->setPos(rect.right() - ((ClipItem *) clip)->fadeOut(), rect.y());
6788
6789             m_animation->setScaleAt(.5, 2, 2);
6790             break;
6791         case RESIZESTART:
6792         case RESIZEEND:
6793             polygon << QPoint(0, - size * 2);
6794             if (mode == RESIZESTART)
6795                 polygon << QPoint(size * 2, 0);
6796             else
6797                 polygon << QPoint(- size * 2, 0);
6798             polygon << QPoint(0, size * 2);
6799             polygon << QPoint(0, - size * 2);
6800
6801             m_visualTip = new QGraphicsPolygonItem(polygon);
6802             ((QGraphicsPolygonItem*) m_visualTip)->setBrush(m_tipColor);
6803             ((QGraphicsPolygonItem*) m_visualTip)->setPen(m_tipPen);
6804             if (mode == RESIZESTART)
6805                 m_visualTip->setPos(rect.x(), rect.y() + rect.height() / 2);
6806             else
6807                 m_visualTip->setPos(rect.right(), rect.y() + rect.height() / 2);
6808
6809             m_animation->setScaleAt(.5, 2, 1);
6810             break;
6811         case TRANSITIONSTART:
6812         case TRANSITIONEND:
6813             polygon << QPoint(0, - size * 2);
6814             if (mode == TRANSITIONSTART)
6815                 polygon << QPoint(size * 2, 0);
6816             else
6817                 polygon << QPoint(- size * 2, 0);
6818             polygon << QPoint(0, 0);
6819             polygon << QPoint(0, - size * 2);
6820
6821             m_visualTip = new QGraphicsPolygonItem(polygon);
6822             ((QGraphicsPolygonItem*) m_visualTip)->setBrush(m_tipColor);
6823             ((QGraphicsPolygonItem*) m_visualTip)->setPen(m_tipPen);
6824             if (mode == TRANSITIONSTART)
6825                 m_visualTip->setPos(rect.x(), rect.bottom());
6826             else
6827                 m_visualTip->setPos(rect.right(), rect.bottom());
6828
6829             m_animation->setScaleAt(.5, 2, 2);
6830             break;
6831         default:
6832             delete m_animation;
6833             return;
6834         }
6835
6836         m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
6837         m_visualTip->setZValue(100);
6838         scene()->addItem(m_visualTip);
6839         m_animation->setItem(m_visualTip);
6840         m_animationTimer->start();
6841     }
6842 }
6843
6844 bool CustomTrackView::hasAudio(int track) const
6845 {
6846     QRectF rect(0, (double)(track * m_tracksHeight + 1), (double) sceneRect().width(), (double)(m_tracksHeight - 1));
6847     QList<QGraphicsItem *> collisions = scene()->items(rect, Qt::IntersectsItemBoundingRect);
6848     QGraphicsItem *item;
6849     for (int i = 0; i < collisions.count(); i++) {
6850         item = collisions.at(i);
6851         if (!item->isEnabled()) continue;
6852         if (item->type() == AVWIDGET) {
6853             ClipItem *clip = static_cast <ClipItem *>(item);
6854             if (!clip->isVideoOnly() && (clip->clipType() == AUDIO || clip->clipType() == AV || clip->clipType() == PLAYLIST)) return true;
6855         }
6856     }
6857     return false;
6858 }
6859
6860 void CustomTrackView::slotAddTrackEffect(const QDomElement &effect, int ix)
6861 {
6862     
6863     QUndoCommand *effectCommand = new QUndoCommand();
6864     QString effectName;
6865     if (effect.tagName() == "effectgroup") {
6866         effectName = effect.attribute("name");
6867     } else {
6868         QDomElement namenode = effect.firstChildElement("name");
6869         if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data());
6870         else effectName = i18n("effect");
6871     }
6872     effectCommand->setText(i18n("Add %1", effectName));
6873     if (effect.tagName() == "effectgroup") {
6874         QDomNodeList effectlist = effect.elementsByTagName("effect");
6875         for (int j = 0; j < effectlist.count(); j++) {
6876             QDomElement trackeffect = effectlist.at(j).toElement();
6877             if (trackeffect.attribute("unique", "0") != "0" && m_document->hasTrackEffect(m_document->tracksCount() - ix - 1, trackeffect.attribute("tag"), trackeffect.attribute("id")) != -1) {
6878                 emit displayMessage(i18n("Effect already present in track"), ErrorMessage);
6879                 continue;
6880             }
6881             new AddEffectCommand(this, m_document->tracksCount() - ix, GenTime(-1), trackeffect, true, effectCommand);
6882         }
6883     }
6884     else {
6885         if (effect.attribute("unique", "0") != "0" && m_document->hasTrackEffect(m_document->tracksCount() - ix - 1, effect.attribute("tag"), effect.attribute("id")) != -1) {
6886             emit displayMessage(i18n("Effect already present in track"), ErrorMessage);
6887             delete effectCommand;
6888             return;
6889         }
6890         new AddEffectCommand(this, m_document->tracksCount() - ix, GenTime(-1), effect, true, effectCommand);
6891     }
6892
6893     if (effectCommand->childCount() > 0) {
6894         m_commandStack->push(effectCommand);
6895         setDocumentModified();
6896     }
6897     else delete effectCommand;
6898 }
6899
6900
6901 EffectsParameterList CustomTrackView::getEffectArgs(const QDomElement &effect)
6902 {
6903     EffectsParameterList parameters;
6904     QLocale locale;
6905     parameters.addParam("tag", effect.attribute("tag"));
6906     //if (effect.hasAttribute("region")) parameters.addParam("region", effect.attribute("region"));
6907     parameters.addParam("kdenlive_ix", effect.attribute("kdenlive_ix"));
6908     parameters.addParam("kdenlive_info", effect.attribute("kdenlive_info"));
6909     parameters.addParam("id", effect.attribute("id"));
6910     if (effect.hasAttribute("src")) parameters.addParam("src", effect.attribute("src"));
6911     if (effect.hasAttribute("disable")) parameters.addParam("disable", effect.attribute("disable"));
6912     if (effect.hasAttribute("in")) parameters.addParam("in", effect.attribute("in"));
6913     if (effect.hasAttribute("out")) parameters.addParam("out", effect.attribute("out"));
6914     if (effect.attribute("id") == "region") {
6915         QDomNodeList subeffects = effect.elementsByTagName("effect");
6916         for (int i = 0; i < subeffects.count(); i++) {
6917             QDomElement subeffect = subeffects.at(i).toElement();
6918             int subeffectix = subeffect.attribute("region_ix").toInt();
6919             parameters.addParam(QString("filter%1").arg(subeffectix), subeffect.attribute("id"));
6920             parameters.addParam(QString("filter%1.tag").arg(subeffectix), subeffect.attribute("tag"));
6921             parameters.addParam(QString("filter%1.kdenlive_info").arg(subeffectix), subeffect.attribute("kdenlive_info"));
6922             QDomNodeList subparams = subeffect.elementsByTagName("parameter");
6923             adjustEffectParameters(parameters, subparams, QString("filter%1.").arg(subeffectix));
6924         }
6925     }
6926
6927     QDomNodeList params = effect.elementsByTagName("parameter");
6928     adjustEffectParameters(parameters, params);
6929     
6930     return parameters;
6931 }
6932
6933
6934 void CustomTrackView::adjustEffectParameters(EffectsParameterList &parameters, QDomNodeList params, const QString &prefix)
6935 {
6936   QLocale locale;
6937   for (int i = 0; i < params.count(); i++) {
6938         QDomElement e = params.item(i).toElement();
6939         QString paramname = prefix + e.attribute("name");
6940         if (e.attribute("type") == "geometry" && !e.hasAttribute("fixed")) {
6941             // effects with geometry param need in / out synced with the clip, request it...
6942             parameters.addParam("_sync_in_out", "1");
6943         }
6944         if (e.attribute("type") == "simplekeyframe") {
6945             QStringList values = e.attribute("keyframes").split(";", QString::SkipEmptyParts);
6946             double factor = e.attribute("factor", "1").toDouble();
6947             double offset = e.attribute("offset", "0").toDouble();
6948             for (int j = 0; j < values.count(); j++) {
6949                 QString pos = values.at(j).section(':', 0, 0);
6950                 double val = (values.at(j).section(':', 1, 1).toDouble() - offset) / factor;
6951                 values[j] = pos + "=" + locale.toString(val);
6952             }
6953             // kDebug() << "/ / / /SENDING KEYFR:" << values;
6954             parameters.addParam(paramname, values.join(";"));
6955             /*parameters.addParam(e.attribute("name"), e.attribute("keyframes").replace(":", "="));
6956             parameters.addParam("max", e.attribute("max"));
6957             parameters.addParam("min", e.attribute("min"));
6958             parameters.addParam("factor", e.attribute("factor", "1"));*/
6959         } else if (e.attribute("type") == "keyframe") {
6960             kDebug() << "/ / / /SENDING KEYFR EFFECT TYPE";
6961             parameters.addParam("keyframes", e.attribute("keyframes"));
6962             parameters.addParam("max", e.attribute("max"));
6963             parameters.addParam("min", e.attribute("min"));
6964             parameters.addParam("factor", e.attribute("factor", "1"));
6965             parameters.addParam("offset", e.attribute("offset", "0"));
6966             parameters.addParam("starttag", e.attribute("starttag", "start"));
6967             parameters.addParam("endtag", e.attribute("endtag", "end"));
6968         } else if (e.attribute("namedesc").contains(';')) {
6969             QString format = e.attribute("format");
6970             QStringList separators = format.split("%d", QString::SkipEmptyParts);
6971             QStringList values = e.attribute("value").split(QRegExp("[,:;x]"));
6972             QString neu;
6973             QTextStream txtNeu(&neu);
6974             if (values.size() > 0)
6975                 txtNeu << (int)values[0].toDouble();
6976             for (int i = 0; i < separators.size() && i + 1 < values.size(); i++) {
6977                 txtNeu << separators[i];
6978                 txtNeu << (int)(values[i+1].toDouble());
6979             }
6980             parameters.addParam("start", neu);
6981         } else {
6982             if (e.attribute("factor", "1") != "1" || e.attribute("offset", "0") != "0") {
6983                 double fact;
6984                 if (e.attribute("factor").contains('%')) {
6985                     fact = ProfilesDialog::getStringEval(m_document->mltProfile(), e.attribute("factor"));
6986                 } else {
6987                     fact = e.attribute("factor", "1").toDouble();
6988                 }
6989                 double offset = e.attribute("offset", "0").toDouble();
6990                 parameters.addParam(paramname, locale.toString((e.attribute("value").toDouble() - offset) / fact));
6991             } else {
6992                 parameters.addParam(paramname, e.attribute("value"));
6993             }
6994         }
6995     }
6996 }
6997
6998
6999 void CustomTrackView::updateTrackNames(int track, bool added)
7000 {
7001     QList <TrackInfo> tracks = m_document->tracksList();
7002     int max = tracks.count();
7003     int docTrack = max - track - 1;
7004
7005     // count number of tracks of each type
7006     int videoTracks = 0;
7007     int audioTracks = 0;
7008     for (int i = max - 1; i >= 0; --i) {
7009         TrackInfo info = tracks.at(i);
7010         if (info.type == VIDEOTRACK)
7011             videoTracks++;
7012         else
7013             audioTracks++;
7014
7015         if (i <= docTrack) {
7016             QString type = (info.type == VIDEOTRACK ? "Video " : "Audio ");
7017             int typeNumber = (info.type == VIDEOTRACK ? videoTracks : audioTracks);
7018
7019             if (added) {
7020                 if (i == docTrack || info.trackName == type + QString::number(typeNumber - 1)) {
7021                     info.trackName = type + QString::number(typeNumber);
7022                     m_document->setTrackType(i, info);
7023                 }
7024             } else {
7025                 if (info.trackName == type + QString::number(typeNumber + 1)) {
7026                     info.trackName = type + QString::number(typeNumber);
7027                     m_document->setTrackType(i, info);
7028                 }
7029             }
7030         }
7031     }
7032     emit tracksChanged();
7033 }
7034
7035 void CustomTrackView::updateTrackDuration(int track, QUndoCommand *command)
7036 {
7037     Q_UNUSED(command)
7038
7039     QList<int> tracks;
7040     if (track >= 0) {
7041         tracks << m_document->tracksCount() - track - 1;
7042     } else {
7043         // negative track number -> update all tracks
7044         for (int i = 0; i < m_document->tracksCount(); ++i)
7045             tracks << i;
7046     }
7047     int t, duration;
7048     for (int i = 0; i < tracks.count(); ++i) {
7049         t = tracks.at(i);
7050         // t + 1 because of black background track
7051         duration = m_document->renderer()->mltTrackDuration(t + 1);
7052         if (duration != m_document->trackDuration(t)) {
7053             m_document->setTrackDuration(t, duration);
7054
7055             // update effects
7056             EffectsList effects = m_document->getTrackEffects(t);
7057             for (int j = 0; j < effects.count(); ++j) {
7058                 /* TODO
7059                  * - lookout for keyframable parameters and update them so all keyframes are in the new range (0 - duration)
7060                  * - update the effectstack if necessary
7061                  */
7062             }
7063         }
7064     }
7065 }
7066
7067 void CustomTrackView::slotRefreshThumbs(const QString &id, bool resetThumbs)
7068 {
7069     QList<QGraphicsItem *> list = scene()->items();
7070     ClipItem *clip = NULL;
7071     for (int i = 0; i < list.size(); ++i) {
7072         if (list.at(i)->type() == AVWIDGET) {
7073             clip = static_cast <ClipItem *>(list.at(i));
7074             if (clip->clipProducer() == id) {
7075                 clip->refreshClip(true, resetThumbs);
7076             }
7077         }
7078     }
7079 }
7080
7081 void CustomTrackView::adjustEffects(ClipItem* item, ItemInfo oldInfo, QUndoCommand* command)
7082 {
7083     QMap<int, QDomElement> effects = item->adjustEffectsToDuration(m_document->width(), m_document->height(), oldInfo);
7084
7085     if (effects.count()) {
7086         QMap<int, QDomElement>::const_iterator i = effects.constBegin();
7087         while (i != effects.constEnd()) {
7088             new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), i.value(), item->effectAt(i.key()), i.key(), false, false, command);
7089             ++i;
7090         }
7091     }
7092 }
7093
7094
7095 void CustomTrackView::slotGotFilterJobResults(const QString &/*id*/, int startPos, int track, const QString &filter, stringMap filterParams)
7096 {
7097     ClipItem *clip = getClipItemAt(GenTime(startPos, m_document->fps()), track);
7098     if (clip == NULL) {
7099         emit displayMessage(i18n("Cannot find clip for effect update %1.", filter), ErrorMessage);
7100         return;
7101     }
7102     QDomElement newEffect;
7103     QDomElement effect = clip->getEffectAt(clip->selectedEffectIndex());
7104     if (effect.attribute("id") == filter) {
7105         newEffect = effect.cloneNode().toElement();
7106         QMap<QString, QString>::const_iterator i = filterParams.constBegin();
7107         while (i != filterParams.constEnd()) {
7108             EffectsList::setParameter(newEffect, i.key(), i.value());
7109             kDebug()<<"// RESULT FILTER: "<<i.key()<<"="<< i.value();
7110             ++i;
7111         }
7112         EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effect, newEffect, clip->selectedEffectIndex(), true, true);
7113         m_commandStack->push(command);
7114         emit clipItemSelected(clip);
7115     }
7116     
7117 }
7118
7119