]> git.sesse.net Git - kdenlive/blob - src/customtrackview.cpp
Fix uninitialized value in group checking:
[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 "customtrackscene.h"
23 #include "docclipbase.h"
24 #include "clipitem.h"
25 #include "definitions.h"
26 #include "moveclipcommand.h"
27 #include "movetransitioncommand.h"
28 #include "resizeclipcommand.h"
29 #include "editguidecommand.h"
30 #include "addtimelineclipcommand.h"
31 #include "addeffectcommand.h"
32 #include "editeffectcommand.h"
33 #include "moveeffectcommand.h"
34 #include "addtransitioncommand.h"
35 #include "edittransitioncommand.h"
36 #include "editkeyframecommand.h"
37 #include "changespeedcommand.h"
38 #include "addmarkercommand.h"
39 #include "razorclipcommand.h"
40 #include "kdenlivesettings.h"
41 #include "transition.h"
42 #include "clipmanager.h"
43 #include "renderer.h"
44 #include "markerdialog.h"
45 #include "mainwindow.h"
46 #include "ui_keyframedialog_ui.h"
47 #include "clipdurationdialog.h"
48 #include "abstractgroupitem.h"
49 #include "insertspacecommand.h"
50 #include "spacerdialog.h"
51 #include "addtrackcommand.h"
52 #include "changetrackcommand.h"
53 #include "movegroupcommand.h"
54 #include "ui_addtrack_ui.h"
55 #include "initeffects.h"
56 #include "locktrackcommand.h"
57 #include "groupclipscommand.h"
58 #include "splitaudiocommand.h"
59 #include "changecliptypecommand.h"
60 #include "trackdialog.h"
61
62 #include <KDebug>
63 #include <KLocale>
64 #include <KUrl>
65 #include <KIcon>
66 #include <KCursor>
67 #include <KColorScheme>
68
69 #include <QMouseEvent>
70 #include <QStylePainter>
71 #include <QGraphicsItem>
72 #include <QDomDocument>
73 #include <QScrollBar>
74 #include <QApplication>
75 #include <QInputDialog>
76
77
78 bool sortGuidesList(const Guide *g1 , const Guide *g2)
79 {
80     return (*g1).position() < (*g2).position();
81 }
82
83
84 //TODO:
85 // disable animation if user asked it in KDE's global settings
86 // http://lists.kde.org/?l=kde-commits&m=120398724717624&w=2
87 // needs something like below (taken from dolphin)
88 // #include <kglobalsettings.h>
89 // const bool animate = KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects;
90 // const int duration = animate ? 1500 : 1;
91
92 CustomTrackView::CustomTrackView(KdenliveDoc *doc, CustomTrackScene* projectscene, QWidget *parent) :
93         QGraphicsView(projectscene, parent),
94         m_tracksHeight(KdenliveSettings::trackheight()),
95         m_projectDuration(0),
96         m_cursorPos(0),
97         m_document(doc),
98         m_scene(projectscene),
99         m_cursorLine(NULL),
100         m_operationMode(NONE),
101         m_moveOpMode(NONE),
102         m_dragItem(NULL),
103         m_dragGuide(NULL),
104         m_visualTip(NULL),
105         m_animation(NULL),
106         m_clickPoint(),
107         m_autoScroll(KdenliveSettings::autoscroll()),
108         m_pasteEffectsAction(NULL),
109         m_ungroupAction(NULL),
110         m_scrollOffset(0),
111         m_clipDrag(false),
112         m_findIndex(0),
113         m_tool(SELECTTOOL),
114         m_copiedItems(),
115         m_menuPosition(),
116         m_blockRefresh(false),
117         m_selectionGroup(NULL),
118         m_selectedTrack(0)
119 {
120     if (doc) m_commandStack = doc->commandStack();
121     else m_commandStack = NULL;
122     setMouseTracking(true);
123     setAcceptDrops(true);
124     setFrameShape(QFrame::NoFrame);
125     setLineWidth(0);
126     //setCacheMode(QGraphicsView::CacheBackground);
127     //setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
128
129     KdenliveSettings::setTrackheight(m_tracksHeight);
130     m_animationTimer = new QTimeLine(800);
131     m_animationTimer->setFrameRange(0, 5);
132     m_animationTimer->setUpdateInterval(100);
133     m_animationTimer->setLoopCount(0);
134     m_tipColor = QColor(0, 192, 0, 200);
135     QColor border = QColor(255, 255, 255, 100);
136     m_tipPen.setColor(border);
137     m_tipPen.setWidth(3);
138     setContentsMargins(0, 0, 0, 0);
139     const int maxHeight = m_tracksHeight * m_document->tracksCount();
140     setSceneRect(0, 0, sceneRect().width(), maxHeight);
141     verticalScrollBar()->setMaximum(maxHeight);
142     m_cursorLine = projectscene->addLine(0, 0, 0, maxHeight);
143     m_cursorLine->setZValue(1000);
144
145     QPen pen1 = QPen();
146     pen1.setWidth(1);
147     pen1.setColor(palette().text().color());
148     m_cursorLine->setPen(pen1);
149     m_cursorLine->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
150
151     KIcon razorIcon("edit-cut");
152     m_razorCursor = QCursor(razorIcon.pixmap(22, 22));
153
154     KIcon spacerIcon("kdenlive-spacer-tool");
155     m_spacerCursor = QCursor(spacerIcon.pixmap(22, 22));
156     verticalScrollBar()->setTracking(true);
157     // Line below was supposed to scroll guides label with scrollbar, not implemented yet
158     //connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotRefreshGuides()));
159     connect(&m_scrollTimer, SIGNAL(timeout()), this, SLOT(slotCheckMouseScrolling()));
160     m_scrollTimer.setInterval(100);
161     m_scrollTimer.setSingleShot(true);
162
163     connect(&m_thumbsTimer, SIGNAL(timeout()), this, SLOT(slotFetchNextThumbs()));
164     m_thumbsTimer.setInterval(500);
165     m_thumbsTimer.setSingleShot(true);
166 }
167
168 CustomTrackView::~CustomTrackView()
169 {
170     qDeleteAll(m_guides);
171     m_waitingThumbs.clear();
172 }
173
174 //virtual
175 void CustomTrackView::keyPressEvent(QKeyEvent * event)
176 {
177     if (event->key() == Qt::Key_Up) {
178         slotTrackUp();
179         event->accept();
180     } else if (event->key() == Qt::Key_Down) {
181         slotTrackDown();
182         event->accept();
183     } else QWidget::keyPressEvent(event);
184 }
185
186 void CustomTrackView::setDocumentModified()
187 {
188     m_document->setModified(true);
189 }
190
191 void CustomTrackView::setContextMenu(QMenu *timeline, QMenu *clip, QMenu *transition, QActionGroup *clipTypeGroup)
192 {
193     m_timelineContextMenu = timeline;
194     m_timelineContextClipMenu = clip;
195     m_clipTypeGroup = clipTypeGroup;
196     QList <QAction *> list = m_timelineContextClipMenu->actions();
197     for (int i = 0; i < list.count(); i++) {
198         if (list.at(i)->data().toString() == "paste_effects") m_pasteEffectsAction = list.at(i);
199         else if (list.at(i)->data().toString() == "ungroup_clip") m_ungroupAction = list.at(i);
200     }
201
202     m_timelineContextTransitionMenu = transition;
203     list = m_timelineContextTransitionMenu->actions();
204     for (int i = 0; i < list.count(); i++) {
205         if (list.at(i)->data().toString() == "auto") {
206             m_autoTransition = list.at(i);
207             break;
208         }
209     }
210
211     m_timelineContextMenu->addSeparator();
212     m_deleteGuide = new KAction(KIcon("edit-delete"), i18n("Delete Guide"), this);
213     connect(m_deleteGuide, SIGNAL(triggered()), this, SLOT(slotDeleteTimeLineGuide()));
214     m_timelineContextMenu->addAction(m_deleteGuide);
215
216     m_editGuide = new KAction(KIcon("document-properties"), i18n("Edit Guide"), this);
217     connect(m_editGuide, SIGNAL(triggered()), this, SLOT(slotEditTimeLineGuide()));
218     m_timelineContextMenu->addAction(m_editGuide);
219 }
220
221 void CustomTrackView::checkAutoScroll()
222 {
223     m_autoScroll = KdenliveSettings::autoscroll();
224 }
225
226 /*sQList <TrackInfo> CustomTrackView::tracksList() const {
227     return m_scene->m_tracksList;
228 }*/
229
230 void CustomTrackView::checkTrackHeight()
231 {
232     if (m_tracksHeight == KdenliveSettings::trackheight()) return;
233     m_tracksHeight = KdenliveSettings::trackheight();
234     emit trackHeightChanged();
235     QList<QGraphicsItem *> itemList = items();
236     ClipItem *item;
237     Transition *transitionitem;
238     bool snap = KdenliveSettings::snaptopoints();
239     KdenliveSettings::setSnaptopoints(false);
240     for (int i = 0; i < itemList.count(); i++) {
241         if (itemList.at(i)->type() == AVWIDGET) {
242             item = (ClipItem*) itemList.at(i);
243             item->setRect(0, 0, item->rect().width(), m_tracksHeight - 1);
244             item->setPos((qreal) item->startPos().frames(m_document->fps()), (qreal) item->track() * m_tracksHeight + 1);
245             item->resetThumbs(true);
246         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
247             transitionitem = (Transition*) itemList.at(i);
248             transitionitem->setRect(0, 0, transitionitem->rect().width(), m_tracksHeight / 3 * 2 - 1);
249             transitionitem->setPos((qreal) transitionitem->startPos().frames(m_document->fps()), (qreal) transitionitem->track() * m_tracksHeight + m_tracksHeight / 3 * 2);
250         }
251     }
252     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), m_tracksHeight * m_document->tracksCount());
253
254     for (int i = 0; i < m_guides.count(); i++) {
255         QLineF l = m_guides.at(i)->line();
256         l.setP2(QPointF(l.x2(), m_tracksHeight * m_document->tracksCount()));
257         m_guides.at(i)->setLine(l);
258     }
259
260     setSceneRect(0, 0, sceneRect().width(), m_tracksHeight * m_document->tracksCount());
261 //     verticalScrollBar()->setMaximum(m_tracksHeight * m_document->tracksCount());
262     KdenliveSettings::setSnaptopoints(snap);
263     viewport()->update();
264 }
265
266 /** Zoom or move viewport on mousewheel
267  *
268  * If mousewheel+Ctrl, zooms in/out on the timeline.
269  *
270  * With Ctrl, moves viewport towards end of timeline if down/back,
271  * opposite on up/forward.
272  *
273  * See also http://www.kdenlive.org/mantis/view.php?id=265 */
274 void CustomTrackView::wheelEvent(QWheelEvent * e)
275 {
276     if (e->modifiers() == Qt::ControlModifier) {
277         if (e->delta() > 0) emit zoomIn();
278         else emit zoomOut();
279     } else {
280         if (e->delta() <= 0) horizontalScrollBar()->setValue(horizontalScrollBar()->value() + horizontalScrollBar()->singleStep());
281         else  horizontalScrollBar()->setValue(horizontalScrollBar()->value() - horizontalScrollBar()->singleStep());
282     }
283 }
284
285 int CustomTrackView::getPreviousVideoTrack(int track)
286 {
287     track = m_document->tracksCount() - track - 1;
288     track --;
289     for (int i = track; i > -1; i--) {
290         if (m_document->trackInfoAt(i).type == VIDEOTRACK) return i + 1;
291     }
292     return 0;
293 }
294
295
296 void CustomTrackView::slotFetchNextThumbs()
297 {
298     if (!m_waitingThumbs.isEmpty()) {
299         ClipItem *item = m_waitingThumbs.takeFirst();
300         while ((item == NULL) && !m_waitingThumbs.isEmpty()) {
301             item = m_waitingThumbs.takeFirst();
302         }
303         if (item) item->slotFetchThumbs();
304         if (!m_waitingThumbs.isEmpty()) m_thumbsTimer.start();
305     }
306 }
307
308 void CustomTrackView::slotCheckMouseScrolling()
309 {
310     if (m_scrollOffset == 0) {
311         m_scrollTimer.stop();
312         return;
313     }
314     horizontalScrollBar()->setValue(horizontalScrollBar()->value() + m_scrollOffset);
315     m_scrollTimer.start();
316 }
317
318 void CustomTrackView::slotCheckPositionScrolling()
319 {
320     // If mouse is at a border of the view, scroll
321     if (m_moveOpMode != SEEK) return;
322     if (mapFromScene(m_cursorPos, 0).x() < 3) {
323         horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 2);
324         QTimer::singleShot(200, this, SLOT(slotCheckPositionScrolling()));
325         setCursorPos(mapToScene(QPoint(-2, 0)).x());
326     } else if (viewport()->width() - 3 < mapFromScene(m_cursorPos + 1, 0).x()) {
327         horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 2);
328         setCursorPos(mapToScene(QPoint(viewport()->width(), 0)).x() + 1);
329         QTimer::singleShot(200, this, SLOT(slotCheckPositionScrolling()));
330     }
331 }
332
333
334 // virtual
335
336 void CustomTrackView::mouseMoveEvent(QMouseEvent * event)
337 {
338     int pos = event->x();
339     int mappedXPos = qMax((int)(mapToScene(event->pos()).x() + 0.5), 0);
340     double snappedPos = getSnapPointForPos(mappedXPos);
341     emit mousePosition(mappedXPos);
342
343     if (event->buttons() & Qt::MidButton) return;
344     if (dragMode() == QGraphicsView::RubberBandDrag || (event->modifiers() == Qt::ControlModifier && m_tool != SPACERTOOL)) {
345         event->setAccepted(true);
346         m_moveOpMode = NONE;
347         QGraphicsView::mouseMoveEvent(event);
348         return;
349     }
350
351     if (event->buttons() != Qt::NoButton) {
352         bool move = (event->pos() - m_clickEvent).manhattanLength() >= QApplication::startDragDistance();
353         if (m_dragItem && m_tool == SELECTTOOL) {
354             if (m_operationMode == MOVE && move) {
355                 QGraphicsView::mouseMoveEvent(event);
356                 // If mouse is at a border of the view, scroll
357                 if (pos < 5) {
358                     m_scrollOffset = -30;
359                     m_scrollTimer.start();
360                 } else if (viewport()->width() - pos < 10) {
361                     m_scrollOffset = 30;
362                     m_scrollTimer.start();
363                 } else if (m_scrollTimer.isActive()) m_scrollTimer.stop();
364
365             } else if (m_operationMode == RESIZESTART && move) {
366                 m_document->renderer()->pause();
367                 m_dragItem->resizeStart((int)(snappedPos));
368             } else if (m_operationMode == RESIZEEND && move) {
369                 m_document->renderer()->pause();
370                 m_dragItem->resizeEnd((int)(snappedPos));
371             } else if (m_operationMode == FADEIN && move) {
372                 ((ClipItem*) m_dragItem)->setFadeIn((int)(mappedXPos - m_dragItem->startPos().frames(m_document->fps())));
373             } else if (m_operationMode == FADEOUT && move) {
374                 ((ClipItem*) m_dragItem)->setFadeOut((int)(m_dragItem->endPos().frames(m_document->fps()) - mappedXPos));
375             } else if (m_operationMode == KEYFRAME && move) {
376                 GenTime keyFramePos = GenTime(mappedXPos, m_document->fps()) - m_dragItem->startPos() + m_dragItem->cropStart();
377                 double pos = mapToScene(event->pos()).toPoint().y();
378                 QRectF br = m_dragItem->sceneBoundingRect();
379                 double maxh = 100.0 / br.height();
380                 pos = (br.bottom() - pos) * maxh;
381                 m_dragItem->updateKeyFramePos(keyFramePos, pos);
382             }
383             if (m_visualTip) {
384                 scene()->removeItem(m_visualTip);
385                 m_animationTimer->stop();
386                 delete m_animation;
387                 m_animation = NULL;
388                 delete m_visualTip;
389                 m_visualTip = NULL;
390             }
391             return;
392         } else if (m_operationMode == MOVEGUIDE) {
393             if (m_visualTip) {
394                 scene()->removeItem(m_visualTip);
395                 m_animationTimer->stop();
396                 delete m_animation;
397                 m_animation = NULL;
398                 delete m_visualTip;
399                 m_visualTip = NULL;
400             }
401             QGraphicsView::mouseMoveEvent(event);
402             return;
403         } else if (m_operationMode == SPACER && move && m_selectionGroup) {
404             // spacer tool
405             if (snappedPos < 0) snappedPos = 0;
406             // Make sure there is no collision
407             QList<QGraphicsItem *> children = m_selectionGroup->childItems();
408             QPainterPath shape = m_selectionGroup->clipGroupShape(QPointF(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0));
409             QList<QGraphicsItem*> collidingItems = scene()->items(shape, Qt::IntersectsItemShape);
410             collidingItems.removeAll(m_selectionGroup);
411             for (int i = 0; i < children.count(); i++) {
412                 collidingItems.removeAll(children.at(i));
413             }
414             bool collision = false;
415             for (int i = 0; i < collidingItems.count(); i++) {
416                 if (collidingItems.at(i)->type() == AVWIDGET) {
417                     collision = true;
418                     break;
419                 }
420             }
421             if (!collision) {
422                 // Check transitions
423                 shape = m_selectionGroup->transitionGroupShape(QPointF(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0));
424                 collidingItems = scene()->items(shape, Qt::IntersectsItemShape);
425                 collidingItems.removeAll(m_selectionGroup);
426                 for (int i = 0; i < children.count(); i++) {
427                     collidingItems.removeAll(children.at(i));
428                 }
429                 for (int i = 0; i < collidingItems.count(); i++) {
430                     if (collidingItems.at(i)->type() == TRANSITIONWIDGET) {
431                         collision = true;
432                         break;
433                     }
434                 }
435             }
436
437             if (!collision)
438                 m_selectionGroup->translate(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0);
439             //m_selectionGroup->setPos(mappedXPos + (((int) m_selectionGroup->boundingRect().topLeft().x() + 0.5) - mappedClick) , m_selectionGroup->pos().y());
440         }
441     }
442
443     if (m_tool == RAZORTOOL) {
444         setCursor(m_razorCursor);
445         //QGraphicsView::mouseMoveEvent(event);
446         //return;
447     } else if (m_tool == SPACERTOOL) {
448         setCursor(m_spacerCursor);
449         return;
450     }
451
452     QList<QGraphicsItem *> itemList = items(event->pos());
453     QGraphicsRectItem *item = NULL;
454     OPERATIONTYPE opMode = NONE;
455
456     if (itemList.count() == 1 && itemList.at(0)->type() == GUIDEITEM) {
457         opMode = MOVEGUIDE;
458     } else for (int i = 0; i < itemList.count(); i++) {
459             if (itemList.at(i)->type() == AVWIDGET || itemList.at(i)->type() == TRANSITIONWIDGET) {
460                 item = (QGraphicsRectItem*) itemList.at(i);
461                 break;
462             }
463         }
464
465     if (item && event->buttons() == Qt::NoButton) {
466         AbstractClipItem *clip = static_cast <AbstractClipItem*>(item);
467         if (m_tool == RAZORTOOL) {
468             // razor tool over a clip, display current frame in monitor
469             if (false && !m_blockRefresh && item->type() == AVWIDGET) {
470                 //TODO: solve crash when showing frame when moving razor over clip
471                 emit showClipFrame(((ClipItem *) item)->baseClip(), mappedXPos - (clip->startPos() - clip->cropStart()).frames(m_document->fps()));
472             }
473             event->accept();
474             return;
475         }
476         opMode = clip->operationMode(mapToScene(event->pos()));
477         const double size = 5;
478         if (opMode == m_moveOpMode) {
479             QGraphicsView::mouseMoveEvent(event);
480             return;
481         } else {
482             if (m_visualTip) {
483                 scene()->removeItem(m_visualTip);
484                 m_animationTimer->stop();
485                 delete m_animation;
486                 m_animation = NULL;
487                 delete m_visualTip;
488                 m_visualTip = NULL;
489             }
490         }
491         m_moveOpMode = opMode;
492         if (opMode == MOVE) {
493             setCursor(Qt::OpenHandCursor);
494         } else if (opMode == RESIZESTART) {
495             setCursor(KCursor("left_side", Qt::SizeHorCursor));
496             if (m_visualTip == NULL) {
497                 QRectF rect = clip->sceneBoundingRect();
498                 QPolygon polygon;
499                 polygon << QPoint(0, - size * 2);
500                 polygon << QPoint(size * 2, 0);
501                 polygon << QPoint(0, size * 2);
502                 polygon << QPoint(0, - size * 2);
503
504                 m_visualTip = new QGraphicsPolygonItem(polygon);
505                 ((QGraphicsPolygonItem*) m_visualTip)->setBrush(m_tipColor);
506                 ((QGraphicsPolygonItem*) m_visualTip)->setPen(m_tipPen);
507                 m_visualTip->setPos(rect.x(), rect.y() + rect.height() / 2);
508                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
509                 m_visualTip->setZValue(100);
510                 m_animation = new QGraphicsItemAnimation;
511                 m_animation->setItem(m_visualTip);
512                 m_animation->setTimeLine(m_animationTimer);
513                 m_animation->setScaleAt(.5, 2, 1);
514                 m_animation->setScaleAt(1, 1, 1);
515                 scene()->addItem(m_visualTip);
516                 m_animationTimer->start();
517             }
518         } else if (opMode == RESIZEEND) {
519             setCursor(KCursor("right_side", Qt::SizeHorCursor));
520             if (m_visualTip == NULL) {
521                 QRectF rect = clip->sceneBoundingRect();
522                 QPolygon polygon;
523                 polygon << QPoint(0, - size * 2);
524                 polygon << QPoint(- size * 2, 0);
525                 polygon << QPoint(0, size * 2);
526                 polygon << QPoint(0, - size * 2);
527
528                 m_visualTip = new QGraphicsPolygonItem(polygon);
529                 ((QGraphicsPolygonItem*) m_visualTip)->setBrush(m_tipColor);
530                 ((QGraphicsPolygonItem*) m_visualTip)->setPen(m_tipPen);
531                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
532                 m_visualTip->setPos(rect.right(), rect.y() + rect.height() / 2);
533                 m_visualTip->setZValue(100);
534                 m_animation = new QGraphicsItemAnimation;
535                 m_animation->setItem(m_visualTip);
536                 m_animation->setTimeLine(m_animationTimer);
537                 m_animation->setScaleAt(.5, 2, 1);
538                 m_animation->setScaleAt(1, 1, 1);
539                 scene()->addItem(m_visualTip);
540                 m_animationTimer->start();
541             }
542         } else if (opMode == FADEIN) {
543             if (m_visualTip == NULL) {
544                 ClipItem *item = (ClipItem *) clip;
545                 QRectF rect = clip->sceneBoundingRect();
546                 m_visualTip = new QGraphicsEllipseItem(-size, -size, size * 2, size * 2);
547                 ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
548                 ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
549                 m_visualTip->setPos(rect.x() + item->fadeIn(), rect.y());
550                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
551                 m_visualTip->setZValue(100);
552                 m_animation = new QGraphicsItemAnimation;
553                 m_animation->setItem(m_visualTip);
554                 m_animation->setTimeLine(m_animationTimer);
555                 m_animation->setScaleAt(.5, 2, 2);
556                 m_animation->setScaleAt(1, 1, 1);
557                 scene()->addItem(m_visualTip);
558                 m_animationTimer->start();
559             }
560             setCursor(Qt::PointingHandCursor);
561         } else if (opMode == FADEOUT) {
562             if (m_visualTip == NULL) {
563                 ClipItem *item = (ClipItem *) clip;
564                 QRectF rect = clip->sceneBoundingRect();
565                 m_visualTip = new QGraphicsEllipseItem(-size, -size, size * 2, size * 2);
566                 ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
567                 ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
568                 m_visualTip->setPos(rect.right() - item->fadeOut(), rect.y());
569                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
570                 m_visualTip->setZValue(100);
571                 m_animation = new QGraphicsItemAnimation;
572                 m_animation->setItem(m_visualTip);
573                 m_animation->setTimeLine(m_animationTimer);
574                 m_animation->setScaleAt(.5, 2, 2);
575                 m_animation->setScaleAt(1, 1, 1);
576                 scene()->addItem(m_visualTip);
577                 m_animationTimer->start();
578             }
579             setCursor(Qt::PointingHandCursor);
580         } else if (opMode == TRANSITIONSTART) {
581             if (m_visualTip == NULL) {
582                 QRectF rect = clip->sceneBoundingRect();
583                 QPolygon polygon;
584                 polygon << QPoint(0, - size * 2);
585                 polygon << QPoint(size * 2, 0);
586                 polygon << QPoint(0, 0);
587                 polygon << QPoint(0, - size * 2);
588
589                 m_visualTip = new QGraphicsPolygonItem(polygon);
590                 ((QGraphicsPolygonItem*) m_visualTip)->setBrush(m_tipColor);
591                 ((QGraphicsPolygonItem*) m_visualTip)->setPen(m_tipPen);
592                 m_visualTip->setPos(rect.x(), rect.bottom());
593                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
594                 m_visualTip->setZValue(100);
595                 m_animation = new QGraphicsItemAnimation;
596                 m_animation->setItem(m_visualTip);
597                 m_animation->setTimeLine(m_animationTimer);
598                 m_animation->setScaleAt(.5, 2, 2);
599                 m_animation->setScaleAt(1, 1, 1);
600                 scene()->addItem(m_visualTip);
601                 m_animationTimer->start();
602             }
603             setCursor(Qt::PointingHandCursor);
604         } else if (opMode == TRANSITIONEND) {
605             if (m_visualTip == NULL) {
606                 QRectF rect = clip->sceneBoundingRect();
607                 QPolygon polygon;
608                 polygon << QPoint(0, - size * 2);
609                 polygon << QPoint(- size * 2, 0);
610                 polygon << QPoint(0, 0);
611                 polygon << QPoint(0, - size * 2);
612
613                 m_visualTip = new QGraphicsPolygonItem(polygon);
614                 ((QGraphicsPolygonItem*) m_visualTip)->setBrush(m_tipColor);
615                 ((QGraphicsPolygonItem*) m_visualTip)->setPen(m_tipPen);
616                 m_visualTip->setPos(rect.right(), rect.bottom());
617                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
618                 m_visualTip->setZValue(100);
619                 m_animation = new QGraphicsItemAnimation;
620                 m_animation->setItem(m_visualTip);
621                 m_animation->setTimeLine(m_animationTimer);
622                 m_animation->setScaleAt(.5, 2, 2);
623                 m_animation->setScaleAt(1, 1, 1);
624                 scene()->addItem(m_visualTip);
625                 m_animationTimer->start();
626             }
627             setCursor(Qt::PointingHandCursor);
628         } else if (opMode == KEYFRAME) {
629             setCursor(Qt::PointingHandCursor);
630         }
631     } // no clip under mouse
632     else if (m_tool == RAZORTOOL) {
633         event->accept();
634         return;
635     } else if (opMode == MOVEGUIDE) {
636         m_moveOpMode = opMode;
637         setCursor(Qt::SplitHCursor);
638     } else {
639         if (m_visualTip) {
640             scene()->removeItem(m_visualTip);
641             m_animationTimer->stop();
642             delete m_animation;
643             m_animation = NULL;
644             delete m_visualTip;
645             m_visualTip = NULL;
646
647         }
648         setCursor(Qt::ArrowCursor);
649         if (event->buttons() != Qt::NoButton && event->modifiers() == Qt::NoModifier) {
650             QGraphicsView::mouseMoveEvent(event);
651             m_moveOpMode = SEEK;
652             setCursorPos(mappedXPos);
653             slotCheckPositionScrolling();
654             return;
655         } else m_moveOpMode = NONE;
656     }
657     QGraphicsView::mouseMoveEvent(event);
658 }
659
660 // virtual
661 void CustomTrackView::mousePressEvent(QMouseEvent * event)
662 {
663     setFocus(Qt::MouseFocusReason);
664     m_menuPosition = QPoint();
665
666     // special cases (middle click button or ctrl / shift click
667     if (event->button() == Qt::MidButton) {
668         m_document->renderer()->switchPlay();
669         m_blockRefresh = false;
670         m_operationMode = NONE;
671         return;
672     }
673
674     if (event->modifiers() & Qt::ShiftModifier) {
675         // Rectangle selection
676         setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
677         setDragMode(QGraphicsView::RubberBandDrag);
678         if (!(event->modifiers() & Qt::ControlModifier)) {
679             resetSelectionGroup();
680             scene()->clearSelection();
681         }
682         m_blockRefresh = false;
683         m_operationMode = RUBBERSELECTION;
684         QGraphicsView::mousePressEvent(event);
685         return;
686     }
687
688     m_blockRefresh = true;
689     m_dragGuide = NULL;
690     bool collision = false;
691
692     if (m_tool != RAZORTOOL) activateMonitor();
693     else if (m_document->renderer()->playSpeed() != 0.0) {
694         m_document->renderer()->pause();
695         return;
696     }
697     m_clickEvent = event->pos();
698
699     // check item under mouse
700     QList<QGraphicsItem *> collisionList = items(m_clickEvent);
701
702     if (event->modifiers() == Qt::ControlModifier && m_tool != SPACERTOOL && collisionList.count() == 0) {
703         // Pressing Ctrl + left mouse button in an empty area scrolls the timeline
704         setDragMode(QGraphicsView::ScrollHandDrag);
705         QGraphicsView::mousePressEvent(event);
706         m_blockRefresh = false;
707         m_operationMode = NONE;
708         return;
709     }
710
711     // if a guide and a clip were pressed, just select the guide
712     for (int i = 0; i < collisionList.count(); ++i) {
713         if (collisionList.at(i)->type() == GUIDEITEM) {
714             // a guide item was pressed
715             m_dragGuide = (Guide *) collisionList.at(i);
716             if (event->button() == Qt::LeftButton) { // move it
717                 m_dragGuide->setFlag(QGraphicsItem::ItemIsMovable, true);
718                 collision = true;
719                 m_operationMode = MOVEGUIDE;
720                 // deselect all clips so that only the guide will move
721                 m_scene->clearSelection();
722                 resetSelectionGroup(false);
723                 updateSnapPoints(NULL);
724                 QGraphicsView::mousePressEvent(event);
725                 return;
726             } else // show context menu
727                 break;
728         }
729     }
730
731     // Find first clip, transition or group under mouse (when no guides selected)
732     int ct = 0;
733     AbstractGroupItem *dragGroup = NULL;
734     AbstractClipItem *collisionClip = NULL;
735     bool found = false;
736     while (!m_dragGuide && ct < collisionList.count()) {
737         if (collisionList.at(ct)->type() == AVWIDGET || collisionList.at(ct)->type() == TRANSITIONWIDGET) {
738             collisionClip = static_cast <AbstractClipItem *>(collisionList.at(ct));
739             if (collisionClip == m_dragItem) {
740                 collisionClip = NULL;
741             } else m_dragItem = collisionClip;
742             found = true;
743             m_dragItemInfo = m_dragItem->info();
744             if (m_dragItem->parentItem() && m_dragItem->parentItem()->type() == GROUPWIDGET && m_dragItem->parentItem() != m_selectionGroup) {
745                 // kDebug()<<"// KLIK FOUND GRP: "<<m_dragItem->sceneBoundingRect();
746                 dragGroup = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
747             }
748             break;
749         }
750         ct++;
751     }
752     if (!found) {
753         if (m_dragItem) emit clipItemSelected(NULL);
754         m_dragItem = NULL;
755     }
756
757     if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET) {
758         // update transition menu action
759         m_autoTransition->setChecked(static_cast<Transition *>(m_dragItem)->isAutomatic());
760         m_autoTransition->setEnabled(true);
761     } else m_autoTransition->setEnabled(false);
762
763     // context menu requested
764     if (event->button() == Qt::RightButton) {
765         if (m_dragItem) {
766             if (dragGroup) dragGroup->setSelected(true);
767             else if (!m_dragItem->isSelected()) {
768                 resetSelectionGroup(false);
769                 m_scene->clearSelection();
770                 m_dragItem->setSelected(true);
771             }
772         } else if (!m_dragGuide) {
773             // check if there is a guide close to mouse click
774             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
775             for (int i = 0; i < guidesCollisionList.count(); i++) {
776                 if (guidesCollisionList.at(i)->type() == GUIDEITEM) {
777                     m_dragGuide = static_cast <Guide *>(guidesCollisionList.at(i));
778                     break;
779                 }
780             }
781             // keep this to support multiple guides context menu in the future (?)
782             /*if (guidesCollisionList.at(0)->type() != GUIDEITEM)
783                 guidesCollisionList.removeAt(0);
784             }
785             if (!guidesCollisionList.isEmpty())
786             m_dragGuide = static_cast <Guide *>(guidesCollisionList.at(0));*/
787         }
788
789         m_operationMode = NONE;
790         displayContextMenu(event->globalPos(), m_dragItem, dragGroup);
791         m_menuPosition = m_clickEvent;
792         m_dragItem = NULL;
793         event->accept();
794         return;
795     }
796
797     // No item under click
798     if (m_dragItem == NULL || m_tool == SPACERTOOL) {
799         resetSelectionGroup(false);
800         setCursor(Qt::ArrowCursor);
801         m_scene->clearSelection();
802         //event->accept();
803         updateClipTypeActions(NULL);
804         if (m_tool == SPACERTOOL) {
805             QList<QGraphicsItem *> selection;
806             if (event->modifiers() == Qt::ControlModifier) {
807                 // Ctrl + click, select all items on track after click position
808                 int track = (int)(mapToScene(m_clickEvent).y() / m_tracksHeight);
809                 QRectF rect(mapToScene(m_clickEvent).x(), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - mapToScene(m_clickEvent).x(), m_tracksHeight / 2 - 2);
810
811                 bool isOk;
812                 selection = checkForGroups(rect, &isOk);
813                 if (!isOk) {
814                     // groups found on track, do not allow the move
815                     emit displayMessage(i18n("Cannot use spacer in a track with a group"), ErrorMessage);
816                     return;
817                 }
818
819                 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;
820             } else {
821                 // Select all items on all tracks after click position
822                 selection = items(event->pos().x(), 1, mapFromScene(sceneRect().width(), 0).x() - event->pos().x(), sceneRect().height());
823                 kDebug() << "SELELCTING ELEMENTS WITHIN =" << event->pos().x() << "/" <<  1 << ", " << mapFromScene(sceneRect().width(), 0).x() - event->pos().x() << "/" << sceneRect().height();
824             }
825
826             // create group to hold selected items
827             m_selectionGroup = new AbstractGroupItem(m_document->fps());
828             scene()->addItem(m_selectionGroup);
829             QList <GenTime> offsetList;
830             for (int i = 0; i < selection.count(); i++) {
831                 if (selection.at(i)->parentItem() == 0 && (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET)) {
832                     AbstractClipItem *item = static_cast<AbstractClipItem *>(selection.at(i));
833                     offsetList.append(item->startPos());
834                     offsetList.append(item->endPos());
835                     m_selectionGroup->addToGroup(selection.at(i));
836                     selection.at(i)->setFlags(QGraphicsItem::ItemIsSelectable);
837                 } else if (selection.at(i)->parentItem() == 0 && selection.at(i)->type() == GROUPWIDGET) {
838                     QList<QGraphicsItem *> children = selection.at(i)->childItems();
839                     for (int j = 0; j < children.count(); j++) {
840                         AbstractClipItem *item = static_cast<AbstractClipItem *>(children.at(j));
841                         offsetList.append(item->startPos());
842                         offsetList.append(item->endPos());
843                     }
844                     m_selectionGroup->addToGroup(selection.at(i));
845                     selection.at(i)->setFlags(QGraphicsItem::ItemIsSelectable);
846                 } else if (selection.at(i)->parentItem()) {
847                     m_selectionGroup->addToGroup(selection.at(i)->parentItem());
848                     selection.at(i)->parentItem()->setFlags(QGraphicsItem::ItemIsSelectable);
849                 }
850             }
851
852             if (!offsetList.isEmpty()) {
853                 qSort(offsetList);
854                 QList <GenTime> cleandOffsetList;
855                 GenTime startOffset = offsetList.takeFirst();
856                 for (int k = 0; k < offsetList.size(); k++) {
857                     GenTime newoffset = offsetList.at(k) - startOffset;
858                     if (newoffset != GenTime() && !cleandOffsetList.contains(newoffset)) {
859                         cleandOffsetList.append(newoffset);
860                     }
861                 }
862                 updateSnapPoints(NULL, cleandOffsetList, true);
863             }
864             m_operationMode = SPACER;
865         } else {
866             setCursorPos((int)(mapToScene(event->x(), 0).x()));
867         }
868         QGraphicsView::mousePressEvent(event);
869         return;
870     }
871
872     // Razor tool
873     if (m_tool == RAZORTOOL && m_dragItem) {
874         if (m_dragItem->type() == TRANSITIONWIDGET) {
875             emit displayMessage(i18n("Cannot cut a transition"), ErrorMessage);
876             event->accept();
877             m_dragItem = NULL;
878             return;
879         } else if (m_dragItem->parentItem() && m_dragItem->parentItem() != m_selectionGroup) {
880             emit displayMessage(i18n("Cannot cut a clip in a group"), ErrorMessage);
881             event->accept();
882             m_dragItem = NULL;
883             return;
884         }
885         AbstractClipItem *clip = static_cast <AbstractClipItem *>(m_dragItem);
886         RazorClipCommand* command = new RazorClipCommand(this, clip->info(), GenTime((int)(mapToScene(event->pos()).x()), m_document->fps()));
887         m_document->renderer()->pause();
888         m_commandStack->push(command);
889         setDocumentModified();
890         m_dragItem = NULL;
891         event->accept();
892         return;
893     }
894
895     bool itemSelected = false;
896     if (m_dragItem->isSelected()) itemSelected = true;
897     else if (m_dragItem->parentItem() && m_dragItem->parentItem()->isSelected()) itemSelected = true;
898     else if (dragGroup && dragGroup->isSelected()) itemSelected = true;
899
900     if (event->modifiers() == Qt::ControlModifier || itemSelected == false) {
901         if (event->modifiers() != Qt::ControlModifier) {
902             resetSelectionGroup(false);
903             m_scene->clearSelection();
904             // A refresh seems necessary otherwise in zoomed mode, some clips disappear
905             viewport()->update();
906         } else resetSelectionGroup();
907         dragGroup = NULL;
908         if (m_dragItem->parentItem() && m_dragItem->parentItem()->type() == GROUPWIDGET) {
909             //kDebug()<<"// KLIK FOUND GRP: "<<m_dragItem->sceneBoundingRect();
910             dragGroup = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
911         }
912         bool selected = !m_dragItem->isSelected();
913         if (dragGroup) dragGroup->setSelected(selected);
914         else m_dragItem->setSelected(selected);
915
916         groupSelectedItems();
917         ClipItem *clip = static_cast <ClipItem *>(m_dragItem);
918         updateClipTypeActions(dragGroup == NULL ? clip : NULL);
919         m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
920     }
921
922     if (collisionClip != NULL || m_dragItem == NULL) {
923         if (m_dragItem && m_dragItem->type() == AVWIDGET && !m_dragItem->isItemLocked()) {
924             ClipItem *selected = static_cast <ClipItem*>(m_dragItem);
925             emit clipItemSelected(selected);
926         } else emit clipItemSelected(NULL);
927     }
928
929     // If clicked item is selected, allow move
930     if (event->modifiers() != Qt::ControlModifier && m_operationMode == NONE) QGraphicsView::mousePressEvent(event);
931
932     m_clickPoint = QPoint((int)(mapToScene(event->pos()).x() - m_dragItem->startPos().frames(m_document->fps())), (int)(event->pos().y() - m_dragItem->pos().y()));
933     m_operationMode = m_dragItem->operationMode(mapToScene(event->pos()));
934
935     // Update snap points
936     if (m_selectionGroup == NULL) {
937         if (m_operationMode == RESIZEEND || m_operationMode == RESIZESTART) updateSnapPoints(NULL);
938         else updateSnapPoints(m_dragItem);
939     } else {
940         QList <GenTime> offsetList;
941         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
942         for (int i = 0; i < children.count(); i++) {
943             if (children.at(i)->type() == AVWIDGET || children.at(i)->type() == TRANSITIONWIDGET) {
944                 AbstractClipItem *item = static_cast <AbstractClipItem *>(children.at(i));
945                 offsetList.append(item->startPos());
946                 offsetList.append(item->endPos());
947             }
948         }
949         if (!offsetList.isEmpty()) {
950             qSort(offsetList);
951             GenTime startOffset = offsetList.takeFirst();
952             QList <GenTime> cleandOffsetList;
953             for (int k = 0; k < offsetList.size(); k++) {
954                 GenTime newoffset = offsetList.at(k) - startOffset;
955                 if (newoffset != GenTime() && !cleandOffsetList.contains(newoffset)) {
956                     cleandOffsetList.append(newoffset);
957                 }
958             }
959             updateSnapPoints(NULL, cleandOffsetList, true);
960         }
961     }
962
963     if (m_operationMode == KEYFRAME) {
964         m_dragItem->updateSelectedKeyFrame();
965         m_blockRefresh = false;
966         return;
967     } else if (m_operationMode == MOVE) {
968         setCursor(Qt::ClosedHandCursor);
969     } else if (m_operationMode == TRANSITIONSTART && event->modifiers() != Qt::ControlModifier) {
970         ItemInfo info;
971         info.startPos = m_dragItem->startPos();
972         info.track = m_dragItem->track();
973         int transitiontrack = getPreviousVideoTrack(info.track);
974         ClipItem *transitionClip = NULL;
975         if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.startPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
976         if (transitionClip && transitionClip->endPos() < m_dragItem->endPos()) {
977             info.endPos = transitionClip->endPos();
978         } else {
979             GenTime transitionDuration(65, m_document->fps());
980             if (m_dragItem->cropDuration() < transitionDuration) info.endPos = m_dragItem->endPos();
981             else info.endPos = info.startPos + transitionDuration;
982         }
983         if (info.endPos == info.startPos) info.endPos = info.startPos + GenTime(65, m_document->fps());
984         // Check there is no other transition at that place
985         double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
986         QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
987         QList<QGraphicsItem *> selection = m_scene->items(r);
988         bool transitionAccepted = true;
989         for (int i = 0; i < selection.count(); i++) {
990             if (selection.at(i)->type() == TRANSITIONWIDGET) {
991                 Transition *tr = static_cast <Transition *>(selection.at(i));
992                 if (tr->startPos() - info.startPos > GenTime(5, m_document->fps())) {
993                     if (tr->startPos() < info.endPos) info.endPos = tr->startPos();
994                 } else transitionAccepted = false;
995             }
996         }
997         if (transitionAccepted) slotAddTransition((ClipItem *) m_dragItem, info, transitiontrack);
998         else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
999     } else if (m_operationMode == TRANSITIONEND && event->modifiers() != Qt::ControlModifier) {
1000         ItemInfo info;
1001         info.endPos = GenTime(m_dragItem->endPos().frames(m_document->fps()), m_document->fps());
1002         info.track = m_dragItem->track();
1003         int transitiontrack = getPreviousVideoTrack(info.track);
1004         ClipItem *transitionClip = NULL;
1005         if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.endPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
1006         if (transitionClip && transitionClip->startPos() > m_dragItem->startPos()) {
1007             info.startPos = transitionClip->startPos();
1008         } else {
1009             GenTime transitionDuration(65, m_document->fps());
1010             if (m_dragItem->cropDuration() < transitionDuration) info.startPos = m_dragItem->startPos();
1011             else info.startPos = info.endPos - transitionDuration;
1012         }
1013         if (info.endPos == info.startPos) info.startPos = info.endPos - GenTime(65, m_document->fps());
1014         QDomElement transition = MainWindow::transitions.getEffectByTag("luma", "dissolve").cloneNode().toElement();
1015         EffectsList::setParameter(transition, "reverse", "1");
1016
1017         // Check there is no other transition at that place
1018         double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
1019         QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
1020         QList<QGraphicsItem *> selection = m_scene->items(r);
1021         bool transitionAccepted = true;
1022         for (int i = 0; i < selection.count(); i++) {
1023             if (selection.at(i)->type() == TRANSITIONWIDGET) {
1024                 Transition *tr = static_cast <Transition *>(selection.at(i));
1025                 if (info.endPos - tr->endPos() > GenTime(5, m_document->fps())) {
1026                     if (tr->endPos() > info.startPos) info.startPos = tr->endPos();
1027                 } else transitionAccepted = false;
1028             }
1029         }
1030         if (transitionAccepted) slotAddTransition((ClipItem *) m_dragItem, info, transitiontrack, transition);
1031         else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1032
1033     } else if ((m_operationMode == RESIZESTART || m_operationMode == RESIZEEND) && m_selectionGroup) {
1034         resetSelectionGroup(false);
1035         m_dragItem->setSelected(true);
1036     }
1037
1038     m_blockRefresh = false;
1039     //kDebug()<<pos;
1040     QGraphicsView::mousePressEvent(event);
1041 }
1042
1043 void CustomTrackView::resetSelectionGroup(bool selectItems)
1044 {
1045     if (m_selectionGroup) {
1046         // delete selection group
1047         bool snap = KdenliveSettings::snaptopoints();
1048         KdenliveSettings::setSnaptopoints(false);
1049
1050         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
1051         scene()->destroyItemGroup(m_selectionGroup);
1052         for (int i = 0; i < children.count(); i++) {
1053             if (children.at(i)->parentItem() == 0 && (children.at(i)->type() == AVWIDGET || children.at(i)->type() == TRANSITIONWIDGET)) {
1054                 if (!static_cast <AbstractClipItem *>(children.at(i))->isItemLocked()) {
1055                     children.at(i)->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
1056                     children.at(i)->setSelected(selectItems);
1057                 }
1058             } else if (children.at(i)->type() == GROUPWIDGET) {
1059                 children.at(i)->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
1060                 children.at(i)->setSelected(selectItems);
1061             }
1062         }
1063         m_selectionGroup = NULL;
1064         KdenliveSettings::setSnaptopoints(snap);
1065     }
1066 }
1067
1068 void CustomTrackView::groupSelectedItems(bool force, bool createNewGroup)
1069 {
1070     if (m_selectionGroup) {
1071         kDebug() << "///// ERROR, TRYING TO OVERRIDE EXISTING GROUP";
1072         return;
1073     }
1074     QList<QGraphicsItem *> selection = m_scene->selectedItems();
1075     if (selection.isEmpty()) return;
1076     QRectF rectUnion;
1077     // Find top left position of selection
1078     for (int i = 0; i < selection.count(); i++) {
1079         if (selection.at(i)->parentItem() == 0 && (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET || selection.at(i)->type() == GROUPWIDGET)) {
1080             rectUnion = rectUnion.united(selection.at(i)->sceneBoundingRect());
1081         } else if (selection.at(i)->parentItem()) {
1082             rectUnion = rectUnion.united(selection.at(i)->parentItem()->sceneBoundingRect());
1083         }
1084     }
1085
1086     if (force || selection.count() > 1) {
1087         bool snap = KdenliveSettings::snaptopoints();
1088         KdenliveSettings::setSnaptopoints(false);
1089         if (createNewGroup) {
1090             AbstractGroupItem *newGroup = m_document->clipManager()->createGroup();
1091             newGroup->setPos(rectUnion.left(), rectUnion.top() - 1);
1092             QPointF diff = newGroup->pos();
1093             newGroup->translate(-diff.x(), -diff.y());
1094             //newGroup->translate((int) -rectUnion.left(), (int) -rectUnion.top() + 1);
1095
1096             scene()->addItem(newGroup);
1097
1098             // CHeck if we are trying to include a group in a group
1099             QList <AbstractGroupItem *> groups;
1100             for (int i = 0; i < selection.count(); i++) {
1101                 if (selection.at(i)->type() == GROUPWIDGET && !groups.contains(static_cast<AbstractGroupItem *>(selection.at(i)))) {
1102                     groups.append(static_cast<AbstractGroupItem *>(selection.at(i)));
1103                 } else if (selection.at(i)->parentItem() && !groups.contains(static_cast<AbstractGroupItem *>(selection.at(i)->parentItem()))) groups.append(static_cast<AbstractGroupItem *>(selection.at(i)->parentItem()));
1104             }
1105             if (!groups.isEmpty()) {
1106                 // ungroup previous groups
1107                 while (!groups.isEmpty()) {
1108                     AbstractGroupItem *grp = groups.takeFirst();
1109                     m_document->clipManager()->removeGroup(grp);
1110                     scene()->destroyItemGroup(grp);
1111                 }
1112                 selection = m_scene->selectedItems();
1113             }
1114
1115             for (int i = 0; i < selection.count(); i++) {
1116                 if (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET) {
1117                     newGroup->addToGroup(selection.at(i));
1118                     selection.at(i)->setFlags(QGraphicsItem::ItemIsSelectable);
1119                 }
1120             }
1121             KdenliveSettings::setSnaptopoints(snap);
1122         } else {
1123             m_selectionGroup = new AbstractGroupItem(m_document->fps());
1124             m_selectionGroup->setPos(rectUnion.left(), rectUnion.top() - 1);
1125             QPointF diff = m_selectionGroup->pos();
1126             //m_selectionGroup->translate((int) - rectUnion.left(), (int) -rectUnion.top() + 1);
1127             m_selectionGroup->translate(- diff.x(), -diff.y());
1128
1129             scene()->addItem(m_selectionGroup);
1130             for (int i = 0; i < selection.count(); i++) {
1131                 if (selection.at(i)->parentItem() == 0 && (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET || selection.at(i)->type() == GROUPWIDGET)) {
1132                     m_selectionGroup->addToGroup(selection.at(i));
1133                     selection.at(i)->setFlags(QGraphicsItem::ItemIsSelectable);
1134                 }
1135             }
1136             KdenliveSettings::setSnaptopoints(snap);
1137             if (m_selectionGroup) {
1138                 m_selectionGroupInfo.startPos = GenTime(m_selectionGroup->scenePos().x(), m_document->fps());
1139                 m_selectionGroupInfo.track = m_selectionGroup->track();
1140             }
1141         }
1142     } else resetSelectionGroup();
1143 }
1144
1145 void CustomTrackView::mouseDoubleClickEvent(QMouseEvent *event)
1146 {
1147     if (m_dragItem && m_dragItem->hasKeyFrames()) {
1148         if (m_moveOpMode == KEYFRAME) {
1149             // user double clicked on a keyframe, open edit dialog
1150             QDialog d(parentWidget());
1151             Ui::KeyFrameDialog_UI view;
1152             view.setupUi(&d);
1153             view.kfr_position->setText(m_document->timecode().getTimecode(GenTime(m_dragItem->selectedKeyFramePos(), m_document->fps()) - m_dragItem->cropStart()));
1154             view.kfr_value->setValue(m_dragItem->selectedKeyFrameValue());
1155             view.kfr_value->setFocus();
1156             if (d.exec() == QDialog::Accepted) {
1157                 int pos = m_document->timecode().getFrameCount(view.kfr_position->text());
1158                 m_dragItem->updateKeyFramePos(GenTime(pos, m_document->fps()) + m_dragItem->cropStart(), (double) view.kfr_value->value() * m_dragItem->keyFrameFactor());
1159                 ClipItem *item = static_cast <ClipItem *>(m_dragItem);
1160                 QString previous = item->keyframes(item->selectedEffectIndex());
1161                 item->updateKeyframeEffect();
1162                 QString next = item->keyframes(item->selectedEffectIndex());
1163                 EditKeyFrameCommand *command = new EditKeyFrameCommand(this, item->track(), item->startPos(), item->selectedEffectIndex(), previous, next, false);
1164                 m_commandStack->push(command);
1165                 updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
1166                 emit clipItemSelected(item, item->selectedEffectIndex());
1167             }
1168
1169         } else  {
1170             // add keyframe
1171             GenTime keyFramePos = GenTime((int)(mapToScene(event->pos()).x()), m_document->fps()) - m_dragItem->startPos() + m_dragItem->cropStart();
1172             m_dragItem->addKeyFrame(keyFramePos, mapToScene(event->pos()).toPoint().y());
1173             ClipItem * item = static_cast <ClipItem *>(m_dragItem);
1174             QString previous = item->keyframes(item->selectedEffectIndex());
1175             item->updateKeyframeEffect();
1176             QString next = item->keyframes(item->selectedEffectIndex());
1177             EditKeyFrameCommand *command = new EditKeyFrameCommand(this, m_dragItem->track(), m_dragItem->startPos(), item->selectedEffectIndex(), previous, next, false);
1178             m_commandStack->push(command);
1179             updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
1180             emit clipItemSelected(item, item->selectedEffectIndex());
1181         }
1182     } else if (m_dragItem && !m_dragItem->isItemLocked()) {
1183         ClipDurationDialog d(m_dragItem, m_document->timecode(), this);
1184         GenTime minimum;
1185         GenTime maximum;
1186         if (m_dragItem->type() == TRANSITIONWIDGET) {
1187             getTransitionAvailableSpace(m_dragItem, minimum, maximum);
1188         } else {
1189             getClipAvailableSpace(m_dragItem, minimum, maximum);
1190         }
1191         //kDebug()<<"// GOT MOVE POS: "<<minimum.frames(25)<<" - "<<maximum.frames(25);
1192         d.setMargins(minimum, maximum);
1193         if (d.exec() == QDialog::Accepted) {
1194             if (m_dragItem->type() == TRANSITIONWIDGET) {
1195                 // move & resize transition
1196                 ItemInfo startInfo;
1197                 startInfo.startPos = m_dragItem->startPos();
1198                 startInfo.endPos = m_dragItem->endPos();
1199                 startInfo.track = m_dragItem->track();
1200                 ItemInfo endInfo;
1201                 endInfo.startPos = d.startPos();
1202                 endInfo.endPos = endInfo.startPos + d.duration();
1203                 endInfo.track = m_dragItem->track();
1204                 MoveTransitionCommand *command = new MoveTransitionCommand(this, startInfo, endInfo, true);
1205                 m_commandStack->push(command);
1206             } else {
1207                 // move and resize clip
1208                 QUndoCommand *moveCommand = new QUndoCommand();
1209                 moveCommand->setText(i18n("Edit clip"));
1210                 ItemInfo clipInfo = m_dragItem->info();
1211                 if (d.duration() < m_dragItem->cropDuration() || d.cropStart() != clipInfo.cropStart) {
1212                     // duration was reduced, so process it first
1213                     ItemInfo startInfo = clipInfo;
1214                     clipInfo.endPos = clipInfo.startPos + d.duration();
1215                     clipInfo.cropStart = d.cropStart();
1216                     new ResizeClipCommand(this, startInfo, clipInfo, true, false, moveCommand);
1217                 }
1218                 if (d.startPos() != clipInfo.startPos) {
1219                     ItemInfo startInfo = clipInfo;
1220                     clipInfo.startPos = d.startPos();
1221                     clipInfo.endPos = m_dragItem->endPos() + (clipInfo.startPos - startInfo.startPos);
1222                     new MoveClipCommand(this, startInfo, clipInfo, true, moveCommand);
1223                 }
1224                 if (d.duration() > m_dragItem->cropDuration()) {
1225                     // duration was increased, so process it after move
1226                     ItemInfo startInfo = clipInfo;
1227                     clipInfo.endPos = clipInfo.startPos + d.duration();
1228                     clipInfo.cropStart = d.cropStart();
1229                     new ResizeClipCommand(this, startInfo, clipInfo, true, false, moveCommand);
1230                 }
1231                 m_commandStack->push(moveCommand);
1232             }
1233         }
1234     } else {
1235         QList<QGraphicsItem *> collisionList = items(event->pos());
1236         if (collisionList.count() == 1 && collisionList.at(0)->type() == GUIDEITEM) {
1237             Guide *editGuide = (Guide *) collisionList.at(0);
1238             if (editGuide) slotEditGuide(editGuide->info());
1239         }
1240     }
1241 }
1242
1243
1244 void CustomTrackView::editKeyFrame(const GenTime pos, const int track, const int index, const QString keyframes)
1245 {
1246     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), track);
1247     if (clip) {
1248         clip->setKeyframes(index, keyframes);
1249         updateEffect(m_document->tracksCount() - clip->track(), clip->startPos(), clip->effectAt(index), index, false);
1250     } else emit displayMessage(i18n("Cannot find clip with keyframe"), ErrorMessage);
1251 }
1252
1253
1254 void CustomTrackView::displayContextMenu(QPoint pos, AbstractClipItem *clip, AbstractGroupItem *group)
1255 {
1256     m_deleteGuide->setEnabled(m_dragGuide != NULL);
1257     m_editGuide->setEnabled(m_dragGuide != NULL);
1258     if (clip == NULL) m_timelineContextMenu->popup(pos);
1259     else if (group != NULL) {
1260         m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
1261         m_ungroupAction->setEnabled(true);
1262         updateClipTypeActions(NULL);
1263         m_timelineContextClipMenu->popup(pos);
1264     } else {
1265         m_ungroupAction->setEnabled(false);
1266         if (clip->type() == AVWIDGET) {
1267             ClipItem *item = static_cast <ClipItem*>(clip);
1268             updateClipTypeActions(item);
1269             m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
1270             m_timelineContextClipMenu->popup(pos);
1271         } else if (clip->type() == TRANSITIONWIDGET) m_timelineContextTransitionMenu->popup(pos);
1272     }
1273 }
1274
1275 void CustomTrackView::activateMonitor()
1276 {
1277     emit activateDocumentMonitor();
1278 }
1279
1280 bool CustomTrackView::insertDropClips(const QMimeData *data, const QPoint pos)
1281 {
1282     if (data->hasFormat("kdenlive/clip")) {
1283         m_clipDrag = true;
1284         resetSelectionGroup();
1285         QStringList list = QString(data->data("kdenlive/clip")).split(';');
1286         DocClipBase *clip = m_document->getBaseClip(list.at(0));
1287         if (clip == NULL) {
1288             kDebug() << " WARNING))))))))) CLIP NOT FOUND : " << list.at(0);
1289             return false;
1290         }
1291         const QPointF framePos = mapToScene(pos);
1292         ItemInfo info;
1293         info.startPos = GenTime();
1294         info.cropStart = GenTime(list.at(1).toInt(), m_document->fps());
1295         info.endPos = GenTime(list.at(2).toInt() - list.at(1).toInt(), m_document->fps());
1296         info.cropDuration = info.endPos - info.startPos;
1297         info.track = 0;
1298
1299         // Check if clip can be inserted at that position
1300         ItemInfo pasteInfo = info;
1301         pasteInfo.startPos = GenTime((int)(framePos.x() + 0.5), m_document->fps());
1302         pasteInfo.endPos = pasteInfo.startPos + info.endPos;
1303         pasteInfo.track = (int)(framePos.y() / m_tracksHeight);
1304         if (!canBePastedTo(pasteInfo, AVWIDGET)) {
1305             return true;
1306         }
1307         m_selectionGroup = new AbstractGroupItem(m_document->fps());
1308         ClipItem *item = new ClipItem(clip, info, m_document->fps(), 1.0, 1);
1309         m_selectionGroup->addToGroup(item);
1310         item->setFlags(QGraphicsItem::ItemIsSelectable);
1311
1312         QList <GenTime> offsetList;
1313         offsetList.append(info.endPos);
1314         updateSnapPoints(NULL, offsetList);
1315         m_selectionGroup->setPos(framePos);
1316         scene()->addItem(m_selectionGroup);
1317         return true;
1318     } else if (data->hasFormat("kdenlive/producerslist")) {
1319         m_clipDrag = true;
1320         QStringList ids = QString(data->data("kdenlive/producerslist")).split(';');
1321         m_scene->clearSelection();
1322         resetSelectionGroup(false);
1323
1324         QList <GenTime> offsetList;
1325         QList <ItemInfo> infoList;
1326         const QPointF framePos = mapToScene(pos);
1327         GenTime start = GenTime((int)(framePos.x() + 0.5), m_document->fps());
1328         int track = (int)(framePos.y() / m_tracksHeight);
1329
1330         // Check if clips can be inserted at that position
1331         for (int i = 0; i < ids.size(); ++i) {
1332             DocClipBase *clip = m_document->getBaseClip(ids.at(i));
1333             if (clip == NULL) {
1334                 kDebug() << " WARNING))))))))) CLIP NOT FOUND : " << ids.at(i);
1335                 return false;
1336             }
1337             ItemInfo info;
1338             info.startPos = start;
1339             info.cropDuration = clip->duration();
1340             info.endPos = info.startPos + info.cropDuration;
1341             info.track = track;
1342             infoList.append(info);
1343             start += clip->duration();
1344         }
1345         if (!canBePastedTo(infoList, AVWIDGET)) {
1346             return true;
1347         }
1348         m_selectionGroup = new AbstractGroupItem(m_document->fps());
1349         start = GenTime();
1350         for (int i = 0; i < ids.size(); ++i) {
1351             DocClipBase *clip = m_document->getBaseClip(ids.at(i));
1352             ItemInfo info;
1353             info.startPos = start;
1354             info.cropDuration = clip->duration();
1355             info.endPos = info.startPos + info.cropDuration;
1356             info.track = 0;
1357             start += info.cropDuration;
1358             offsetList.append(start);
1359             ClipItem *item = new ClipItem(clip, info, m_document->fps(), 1.0, 1, false);
1360             item->setFlags(QGraphicsItem::ItemIsSelectable);
1361             m_selectionGroup->addToGroup(item);
1362             m_waitingThumbs.append(item);
1363         }
1364
1365         updateSnapPoints(NULL, offsetList);
1366         m_selectionGroup->setPos(framePos);
1367         scene()->addItem(m_selectionGroup);
1368         m_thumbsTimer.start();
1369         return true;
1370
1371     } else {
1372         // the drag is not a clip (may be effect, ...)
1373         m_clipDrag = false;
1374         return false;
1375     }
1376 }
1377
1378
1379
1380 void CustomTrackView::dragEnterEvent(QDragEnterEvent * event)
1381 {
1382     if (insertDropClips(event->mimeData(), event->pos())) {
1383         event->acceptProposedAction();
1384     } else QGraphicsView::dragEnterEvent(event);
1385 }
1386
1387 bool CustomTrackView::itemCollision(AbstractClipItem *item, ItemInfo newPos)
1388 {
1389     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);
1390     QList<QGraphicsItem*> collindingItems = scene()->items(shape, Qt::IntersectsItemShape);
1391     collindingItems.removeAll(item);
1392     if (collindingItems.isEmpty()) return false;
1393     else {
1394         for (int i = 0; i < collindingItems.count(); i++) {
1395             QGraphicsItem *collision = collindingItems.at(i);
1396             if (collision->type() == item->type()) {
1397                 // Collision
1398                 kDebug() << "// COLLISIION DETECTED";
1399                 return true;
1400             }
1401         }
1402         return false;
1403     }
1404 }
1405
1406 void CustomTrackView::slotRefreshEffects(ClipItem *clip)
1407 {
1408     int track = m_document->tracksCount() - clip->track();
1409     GenTime pos = clip->startPos();
1410     if (!m_document->renderer()->mltRemoveEffect(track, pos, "-1", false, false)) {
1411         emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
1412         return;
1413     }
1414     bool success = true;
1415     for (int i = 0; i < clip->effectsCount(); i++) {
1416         if (!m_document->renderer()->mltAddEffect(track, pos, clip->getEffectArgs(clip->effectAt(i)), false)) success = false;
1417     }
1418     if (!success) emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
1419     m_document->renderer()->doRefresh();
1420 }
1421
1422 void CustomTrackView::addEffect(int track, GenTime pos, QDomElement effect)
1423 {
1424     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
1425     if (clip) {
1426         // Special case: speed effect
1427         if (effect.attribute("id") == "speed") {
1428             if (clip->clipType() != VIDEO && clip->clipType() != AV && clip->clipType() != PLAYLIST) {
1429                 emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
1430                 return;
1431             }
1432             double speed = EffectsList::parameter(effect, "speed").toDouble() / 100.0;
1433             int strobe = EffectsList::parameter(effect, "strobe").toInt();
1434             if (strobe == 0) strobe = 1;
1435             doChangeClipSpeed(clip->info(), clip->speedIndependantInfo(), speed, 1.0, strobe, clip->baseClip()->getId());
1436             EffectsParameterList params = clip->addEffect(effect);
1437             m_document->renderer()->mltAddEffect(track, pos, params);
1438             if (clip->isSelected()) emit clipItemSelected(clip);
1439             return;
1440         }
1441         EffectsParameterList params = clip->addEffect(effect);
1442         if (effect.attribute("disabled") == "1") {
1443             // Effect is disabled, don't add it to MLT playlist
1444             if (clip->isSelected()) emit clipItemSelected(clip);
1445             return;
1446         }
1447         if (!m_document->renderer()->mltAddEffect(track, pos, params))
1448             emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
1449         if (clip->isSelected()) emit clipItemSelected(clip);
1450     } else emit displayMessage(i18n("Cannot find clip to add effect"), ErrorMessage);
1451 }
1452
1453 void CustomTrackView::deleteEffect(int track, GenTime pos, QDomElement effect)
1454 {
1455     QString index = effect.attribute("kdenlive_ix");
1456     // Special case: speed effect
1457     if (effect.attribute("id") == "speed") {
1458         ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
1459         if (clip) {
1460             doChangeClipSpeed(clip->info(), clip->speedIndependantInfo(), 1.0, clip->speed(), 1, clip->baseClip()->getId());
1461             clip->deleteEffect(index);
1462             emit clipItemSelected(clip);
1463             m_document->renderer()->mltRemoveEffect(track, pos, index, true);
1464             return;
1465         }
1466     }
1467     if (!m_document->renderer()->mltRemoveEffect(track, pos, index, true) && effect.attribute("disabled") != "1") {
1468         kDebug() << "// ERROR REMOV EFFECT: " << index << ", DISABLE: " << effect.attribute("disabled");
1469         emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
1470         return;
1471     }
1472     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
1473     if (clip) {
1474         clip->deleteEffect(index);
1475         emit clipItemSelected(clip);
1476     }
1477 }
1478
1479 void CustomTrackView::slotAddGroupEffect(QDomElement effect, AbstractGroupItem *group)
1480 {
1481     QList<QGraphicsItem *> itemList = group->childItems();
1482     QUndoCommand *effectCommand = new QUndoCommand();
1483     QString effectName;
1484     QDomNode namenode = effect.elementsByTagName("name").item(0);
1485     if (!namenode.isNull()) effectName = i18n(namenode.toElement().text().toUtf8().data());
1486     else effectName = i18n("effect");
1487     effectCommand->setText(i18n("Add %1", effectName));
1488     int count = 0;
1489     for (int i = 0; i < itemList.count(); i++) {
1490         if (itemList.at(i)->type() == AVWIDGET) {
1491             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
1492             if (effect.attribute("type") == "audio") {
1493                 // Don't add audio effects on video clips
1494                 if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) continue;
1495             } else if (effect.hasAttribute("type") == false) {
1496                 // Don't add video effect on audio clips
1497                 if (item->isAudioOnly() || item->clipType() == AUDIO) continue;
1498             }
1499
1500             if (effect.attribute("unique", "0") != "0" && item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1) {
1501                 emit displayMessage(i18n("Effect already present in clip"), ErrorMessage);
1502                 continue;
1503             }
1504             if (item->isItemLocked()) {
1505                 continue;
1506             }
1507             item->initEffect(effect);
1508             if (effect.attribute("tag") == "ladspa") {
1509                 QString ladpsaFile = m_document->getLadspaFile();
1510                 initEffects::ladspaEffectFile(ladpsaFile, effect.attribute("ladspaid").toInt(), getLadspaParams(effect));
1511                 effect.setAttribute("src", ladpsaFile);
1512             }
1513             new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand);
1514             count++;
1515         }
1516     }
1517     if (count > 0) {
1518         m_commandStack->push(effectCommand);
1519         setDocumentModified();
1520     } else delete effectCommand;
1521 }
1522
1523 void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track)
1524 {
1525     QList<QGraphicsItem *> itemList;
1526     QUndoCommand *effectCommand = new QUndoCommand();
1527     QString effectName;
1528     QDomNode namenode = effect.elementsByTagName("name").item(0);
1529     if (!namenode.isNull()) effectName = i18n(namenode.toElement().text().toUtf8().data());
1530     else effectName = i18n("effect");
1531     effectCommand->setText(i18n("Add %1", effectName));
1532     int count = 0;
1533     if (track == -1) itemList = scene()->selectedItems();
1534     if (itemList.isEmpty()) {
1535         ClipItem *clip = getClipItemAt((int) pos.frames(m_document->fps()), track);
1536         if (clip) itemList.append(clip);
1537         else emit displayMessage(i18n("Select a clip if you want to apply an effect"), ErrorMessage);
1538     }
1539     kDebug() << "// REQUESTING EFFECT ON CLIP: " << pos.frames(25) << ", TRK: " << track << "SELECTED ITEMS: " << itemList.count();
1540     for (int i = 0; i < itemList.count(); i++) {
1541         if (itemList.at(i)->type() == AVWIDGET) {
1542             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
1543             if (effect.attribute("type") == "audio") {
1544                 // Don't add audio effects on video clips
1545                 if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) {
1546                     emit displayMessage(i18n("Cannot add an audio effect to this clip"), ErrorMessage);
1547                     continue;
1548                 }
1549             } else if (effect.hasAttribute("type") == false) {
1550                 // Don't add video effect on audio clips
1551                 if (item->isAudioOnly() || item->clipType() == AUDIO) {
1552                     emit displayMessage(i18n("Cannot add a video effect to this clip"), ErrorMessage);
1553                     continue;
1554                 }
1555             }
1556             if (item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1 && effect.attribute("unique", "0") != "0") {
1557                 emit displayMessage(i18n("Effect already present in clip"), ErrorMessage);
1558                 continue;
1559             }
1560             if (item->isItemLocked()) {
1561                 continue;
1562             }
1563             if (effect.attribute("id") == "freeze" && m_cursorPos > item->startPos().frames(m_document->fps()) && m_cursorPos < item->endPos().frames(m_document->fps())) {
1564                 item->initEffect(effect, m_cursorPos - item->startPos().frames(m_document->fps()));
1565             } else item->initEffect(effect);
1566             if (effect.attribute("tag") == "ladspa") {
1567                 QString ladpsaFile = m_document->getLadspaFile();
1568                 initEffects::ladspaEffectFile(ladpsaFile, effect.attribute("ladspaid").toInt(), getLadspaParams(effect));
1569                 effect.setAttribute("src", ladpsaFile);
1570             }
1571             new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand);
1572             count++;
1573         }
1574     }
1575     if (count > 0) {
1576         m_commandStack->push(effectCommand);
1577         setDocumentModified();
1578     } else delete effectCommand;
1579 }
1580
1581 void CustomTrackView::slotDeleteEffect(ClipItem *clip, QDomElement effect)
1582 {
1583     AddEffectCommand *command = new AddEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effect, false);
1584     m_commandStack->push(command);
1585     setDocumentModified();
1586 }
1587
1588 void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedEffect, int ix, bool triggeredByUser)
1589 {
1590     if (insertedEffect.isNull()) {
1591         emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
1592         return;
1593     }
1594     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
1595     QDomElement effect = insertedEffect.cloneNode().toElement();
1596     if (clip) {
1597         // Special case: speed effect
1598         if (effect.attribute("id") == "speed") {
1599             if (effect.attribute("disabled") == "1") doChangeClipSpeed(clip->info(), clip->speedIndependantInfo(), 1.0, clip->speed(), 1, clip->baseClip()->getId());
1600             else {
1601                 double speed = EffectsList::parameter(effect, "speed").toDouble() / 100.0;
1602                 int strobe = EffectsList::parameter(effect, "strobe").toInt();
1603                 if (strobe == 0) strobe = 1;
1604                 doChangeClipSpeed(clip->info(), clip->speedIndependantInfo(), speed, clip->speed(), strobe, clip->baseClip()->getId());
1605             }
1606             clip->setEffectAt(ix, effect);
1607             if (ix == clip->selectedEffectIndex()) {
1608                 clip->setSelectedEffect(ix);
1609                 if (!triggeredByUser) emit clipItemSelected(clip, ix);
1610             }
1611             return;
1612         }
1613
1614         EffectsParameterList effectParams = clip->getEffectArgs(effect);
1615         if (effect.attribute("tag") == "ladspa") {
1616             // Update the ladspa affect file
1617             initEffects::ladspaEffectFile(effect.attribute("src"), effect.attribute("ladspaid").toInt(), getLadspaParams(effect));
1618         }
1619         // check if we are trying to reset a keyframe effect
1620         if (effectParams.hasParam("keyframes") && effectParams.paramValue("keyframes").isEmpty()) {
1621             clip->initEffect(effect);
1622             effectParams = clip->getEffectArgs(effect);
1623         }
1624         if (effectParams.paramValue("disabled") == "1") {
1625             if (m_document->renderer()->mltRemoveEffect(track, pos, effectParams.paramValue("kdenlive_ix"), false)) {
1626                 kDebug() << "//////  DISABLING EFFECT: " << ix << ", CURRENTLA: " << clip->selectedEffectIndex();
1627             } else emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
1628         } else if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - clip->track(), clip->startPos(), effectParams))
1629             emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
1630
1631         clip->setEffectAt(ix, effect);
1632         if (ix == clip->selectedEffectIndex()) {
1633             clip->setSelectedEffect(ix);
1634             if (!triggeredByUser) emit clipItemSelected(clip, ix);
1635         }
1636         if (effect.attribute("tag") == "volume" || effect.attribute("tag") == "brightness") {
1637             // A fade effect was modified, update the clip
1638             if (effect.attribute("id") == "fadein" || effect.attribute("id") == "fade_from_black") {
1639                 int pos = effectParams.paramValue("out").toInt() - effectParams.paramValue("in").toInt();
1640                 clip->setFadeIn(pos);
1641             }
1642             if (effect.attribute("id") == "fadeout" || effect.attribute("id") == "fade_to_black") {
1643                 int pos = effectParams.paramValue("out").toInt() - effectParams.paramValue("in").toInt();
1644                 clip->setFadeOut(pos);
1645             }
1646         }
1647     }
1648     setDocumentModified();
1649 }
1650
1651 void CustomTrackView::moveEffect(int track, GenTime pos, int oldPos, int newPos)
1652 {
1653     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
1654     if (clip && !clip->effectAt(newPos - 1).isNull() && !clip->effectAt(oldPos - 1).isNull()) {
1655         QDomElement act = clip->effectAt(newPos - 1);
1656         QDomElement before = clip->effectAt(oldPos - 1);
1657         clip->setEffectAt(oldPos - 1, act);
1658         clip->setEffectAt(newPos - 1, before);
1659         // special case: speed effect, which is a pseudo-effect, not appearing in MLT's effects
1660         if (act.attribute("id") == "speed") {
1661             m_document->renderer()->mltUpdateEffectPosition(track, pos, oldPos, newPos);
1662         } else if (before.attribute("id") == "speed") {
1663             m_document->renderer()->mltUpdateEffectPosition(track, pos, newPos, oldPos);
1664         } else m_document->renderer()->mltMoveEffect(track, pos, oldPos, newPos);
1665         emit clipItemSelected(clip, newPos - 1);
1666         setDocumentModified();
1667     } else emit displayMessage(i18n("Cannot move effect"), ErrorMessage);
1668 }
1669
1670 void CustomTrackView::slotChangeEffectState(ClipItem *clip, int effectPos, bool disable)
1671 {
1672     QDomElement effect = clip->effectAt(effectPos);
1673     QDomElement oldEffect = effect.cloneNode().toElement();
1674
1675     effect.setAttribute("disabled", (int) disable);
1676     EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldEffect, effect, effectPos, true);
1677     m_commandStack->push(command);
1678     setDocumentModified();;
1679 }
1680
1681 void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int currentPos, int newPos)
1682 {
1683     MoveEffectCommand *command = new MoveEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), currentPos, newPos);
1684     m_commandStack->push(command);
1685     setDocumentModified();
1686 }
1687
1688 void CustomTrackView::slotUpdateClipEffect(ClipItem *clip, QDomElement oldeffect, QDomElement effect, int ix)
1689 {
1690     EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, true);
1691     m_commandStack->push(command);
1692 }
1693
1694 void CustomTrackView::cutClip(ItemInfo info, GenTime cutTime, bool cut)
1695 {
1696     if (cut) {
1697         // cut clip
1698         ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()), info.track);
1699         if (!item || cutTime >= item->endPos() || cutTime <= item->startPos()) {
1700             emit displayMessage(i18n("Cannot find clip to cut"), ErrorMessage);
1701             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);
1702             m_blockRefresh = false;
1703             return;
1704         }
1705         if (item->parentItem()) {
1706             // Item is part of a group, reset group
1707             resetSelectionGroup();
1708         }
1709         kDebug() << "/////////  CUTTING CLIP : (" << item->startPos().frames(25) << "-" << item->endPos().frames(25) << "), INFO: (" << info.startPos.frames(25) << "-" << info.endPos.frames(25) << ")" << ", CUT: " << cutTime.frames(25);
1710
1711         m_document->renderer()->mltCutClip(m_document->tracksCount() - info.track, cutTime);
1712         int cutPos = (int) cutTime.frames(m_document->fps());
1713         ItemInfo newPos;
1714         newPos.startPos = cutTime;
1715         newPos.endPos = info.endPos;
1716         newPos.cropStart = item->info().cropStart + (cutTime - info.startPos);
1717         newPos.track = info.track;
1718         newPos.cropDuration = newPos.endPos - newPos.startPos;
1719
1720
1721         ClipItem *dup = item->clone(newPos);
1722         // remove unwanted effects (fade in) from 2nd part of cutted clip
1723         int ix = dup->hasEffect(QString(), "fadein");
1724         if (ix != -1) {
1725             QDomElement oldeffect = item->effectAt(ix);
1726             dup->deleteEffect(oldeffect.attribute("kdenlive_ix"));
1727         }
1728         ix = dup->hasEffect(QString(), "fade_from_black");
1729         if (ix != -1) {
1730             QDomElement oldeffect = item->effectAt(ix);
1731             dup->deleteEffect(oldeffect.attribute("kdenlive_ix"));
1732         }
1733         item->resizeEnd(cutPos);
1734         scene()->addItem(dup);
1735         if (item->checkKeyFrames()) slotRefreshEffects(item);
1736         if (dup->checkKeyFrames()) slotRefreshEffects(dup);
1737         item->baseClip()->addReference();
1738         m_document->updateClip(item->baseClip()->getId());
1739         setDocumentModified();
1740         //kDebug() << "/////////  CUTTING CLIP RESULT: (" << item->startPos().frames(25) << "-" << item->endPos().frames(25) << "), DUP: (" << dup->startPos().frames(25) << "-" << dup->endPos().frames(25) << ")" << ", CUT: " << cutTime.frames(25);
1741     } else {
1742         // uncut clip
1743
1744         ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()), info.track);
1745         ClipItem *dup = getClipItemAt((int) cutTime.frames(m_document->fps()), info.track);
1746         if (!item || !dup || item == dup) {
1747             emit displayMessage(i18n("Cannot find clip to uncut"), ErrorMessage);
1748             m_blockRefresh = false;
1749             return;
1750         }
1751         if (m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, cutTime) == false) {
1752             emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(cutTime.frames(m_document->fps())), info.track), ErrorMessage);
1753             return;
1754         }
1755
1756         /*kDebug() << "// UNCUTTING CLIPS: ITEM 1 (" << item->startPos().frames(25) << "x" << item->endPos().frames(25) << ")";
1757         kDebug() << "// UNCUTTING CLIPS: ITEM 2 (" << dup->startPos().frames(25) << "x" << dup->endPos().frames(25) << ")";
1758         kDebug() << "// UNCUTTING CLIPS, INFO (" << info.startPos.frames(25) << "x" << info.endPos.frames(25) << ") , CUT: " << cutTime.frames(25);;*/
1759         //deleteClip(dup->info());
1760
1761
1762         if (dup->isSelected()) emit clipItemSelected(NULL);
1763         dup->baseClip()->removeReference();
1764         m_document->updateClip(dup->baseClip()->getId());
1765         scene()->removeItem(dup);
1766         delete dup;
1767
1768         ItemInfo clipinfo = item->info();
1769         clipinfo.track = m_document->tracksCount() - clipinfo.track;
1770         bool success = m_document->renderer()->mltResizeClipEnd(clipinfo, info.endPos - info.startPos);
1771         if (success) {
1772             item->resizeEnd((int) info.endPos.frames(m_document->fps()));
1773             setDocumentModified();
1774         } else
1775             emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
1776
1777     }
1778     QTimer::singleShot(3000, this, SLOT(slotEnableRefresh()));
1779 }
1780
1781 void CustomTrackView::slotEnableRefresh()
1782 {
1783     m_blockRefresh = false;
1784 }
1785
1786 void CustomTrackView::slotAddTransitionToSelectedClips(QDomElement transition)
1787 {
1788     QList<QGraphicsItem *> itemList = scene()->selectedItems();
1789     if (itemList.count() == 1) {
1790         if (itemList.at(0)->type() == AVWIDGET) {
1791             ClipItem *item = (ClipItem *) itemList.at(0);
1792             ItemInfo info;
1793             info.track = item->track();
1794             ClipItem *transitionClip = NULL;
1795             const int transitiontrack = getPreviousVideoTrack(info.track);
1796             GenTime pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
1797             if (pos < item->startPos() + item->cropDuration() / 2) {
1798                 // add transition to clip start
1799                 info.startPos = item->startPos();
1800                 if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.startPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
1801                 if (transitionClip && transitionClip->endPos() < item->endPos()) {
1802                     info.endPos = transitionClip->endPos();
1803                 } else info.endPos = info.startPos + GenTime(65, m_document->fps());
1804                 // Check there is no other transition at that place
1805                 double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
1806                 QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
1807                 QList<QGraphicsItem *> selection = m_scene->items(r);
1808                 bool transitionAccepted = true;
1809                 for (int i = 0; i < selection.count(); i++) {
1810                     if (selection.at(i)->type() == TRANSITIONWIDGET) {
1811                         Transition *tr = static_cast <Transition *>(selection.at(i));
1812                         if (tr->startPos() - info.startPos > GenTime(5, m_document->fps())) {
1813                             if (tr->startPos() < info.endPos) info.endPos = tr->startPos();
1814                         } else transitionAccepted = false;
1815                     }
1816                 }
1817                 if (transitionAccepted) slotAddTransition(item, info, transitiontrack, transition);
1818                 else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1819
1820             } else {
1821                 // add transition to clip  end
1822                 info.endPos = item->endPos();
1823                 if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.endPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
1824                 if (transitionClip && transitionClip->startPos() > item->startPos()) {
1825                     info.startPos = transitionClip->startPos();
1826                 } else info.startPos = info.endPos - GenTime(65, m_document->fps());
1827                 if (transition.attribute("tag") == "luma") EffectsList::setParameter(transition, "reverse", "1");
1828                 else if (transition.attribute("id") == "slide") EffectsList::setParameter(transition, "invert", "1");
1829
1830                 // Check there is no other transition at that place
1831                 double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
1832                 QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
1833                 QList<QGraphicsItem *> selection = m_scene->items(r);
1834                 bool transitionAccepted = true;
1835                 for (int i = 0; i < selection.count(); i++) {
1836                     if (selection.at(i)->type() == TRANSITIONWIDGET) {
1837                         Transition *tr = static_cast <Transition *>(selection.at(i));
1838                         if (info.endPos - tr->endPos() > GenTime(5, m_document->fps())) {
1839                             if (tr->endPos() > info.startPos) info.startPos = tr->endPos();
1840                         } else transitionAccepted = false;
1841                     }
1842                 }
1843                 if (transitionAccepted) slotAddTransition(item, info, transitiontrack, transition);
1844                 else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1845             }
1846         }
1847     } else for (int i = 0; i < itemList.count(); i++) {
1848             if (itemList.at(i)->type() == AVWIDGET) {
1849                 ClipItem *item = (ClipItem *) itemList.at(i);
1850                 ItemInfo info;
1851                 info.startPos = item->startPos();
1852                 info.endPos = info.startPos + GenTime(65, m_document->fps());
1853                 info.track = item->track();
1854
1855                 // Check there is no other transition at that place
1856                 double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
1857                 QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
1858                 QList<QGraphicsItem *> selection = m_scene->items(r);
1859                 bool transitionAccepted = true;
1860                 for (int i = 0; i < selection.count(); i++) {
1861                     if (selection.at(i)->type() == TRANSITIONWIDGET) {
1862                         Transition *tr = static_cast <Transition *>(selection.at(i));
1863                         if (tr->startPos() - info.startPos > GenTime(5, m_document->fps())) {
1864                             if (tr->startPos() < info.endPos) info.endPos = tr->startPos();
1865                         } else transitionAccepted = false;
1866                     }
1867                 }
1868                 int transitiontrack = getPreviousVideoTrack(info.track);
1869                 if (transitionAccepted) slotAddTransition(item, info, transitiontrack, transition);
1870                 else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1871             }
1872         }
1873 }
1874
1875 void CustomTrackView::slotAddTransition(ClipItem* /*clip*/, ItemInfo transitionInfo, int endTrack, QDomElement transition)
1876 {
1877     if (transitionInfo.startPos >= transitionInfo.endPos) {
1878         emit displayMessage(i18n("Invalid transition"), ErrorMessage);
1879         return;
1880     }
1881     AddTransitionCommand* command = new AddTransitionCommand(this, transitionInfo, endTrack, transition, false, true);
1882     m_commandStack->push(command);
1883     setDocumentModified();
1884 }
1885
1886 void CustomTrackView::addTransition(ItemInfo transitionInfo, int endTrack, QDomElement params, bool refresh)
1887 {
1888     Transition *tr = new Transition(transitionInfo, endTrack, m_document->fps(), params, true);
1889     //kDebug() << "---- ADDING transition " << params.attribute("value");
1890     if (m_document->renderer()->mltAddTransition(tr->transitionTag(), endTrack, m_document->tracksCount() - transitionInfo.track, transitionInfo.startPos, transitionInfo.endPos, tr->toXML(), refresh)) {
1891         scene()->addItem(tr);
1892         setDocumentModified();
1893     } else {
1894         emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1895         delete tr;
1896     }
1897 }
1898
1899 void CustomTrackView::deleteTransition(ItemInfo transitionInfo, int endTrack, QDomElement /*params*/, bool refresh)
1900 {
1901     Transition *item = getTransitionItemAt(transitionInfo.startPos, transitionInfo.track);
1902     if (!item) {
1903         emit displayMessage(i18n("Select clip to delete"), ErrorMessage);
1904         return;
1905     }
1906     m_document->renderer()->mltDeleteTransition(item->transitionTag(), endTrack, m_document->tracksCount() - transitionInfo.track, transitionInfo.startPos, transitionInfo.endPos, item->toXML(), refresh);
1907     if (m_dragItem == item) m_dragItem = NULL;
1908     delete item;
1909     emit transitionItemSelected(NULL);
1910     setDocumentModified();
1911 }
1912
1913 void CustomTrackView::slotTransitionUpdated(Transition *tr, QDomElement old)
1914 {
1915     //kDebug() << "TRANS UPDATE, TRACKS: " << old.attribute("transition_btrack") << ", NEW: " << tr->toXML().attribute("transition_btrack");
1916     EditTransitionCommand *command = new EditTransitionCommand(this, tr->track(), tr->startPos(), old, tr->toXML(), false);
1917     m_commandStack->push(command);
1918     setDocumentModified();
1919 }
1920
1921 void CustomTrackView::updateTransition(int track, GenTime pos, QDomElement oldTransition, QDomElement transition, bool updateTransitionWidget)
1922 {
1923     Transition *item = getTransitionItemAt(pos, track);
1924     if (!item) {
1925         kWarning() << "Unable to find transition at pos :" << pos.frames(m_document->fps()) << ", ON track: " << track;
1926         return;
1927     }
1928     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);
1929     //kDebug() << "ORIGINAL TRACK: "<< oldTransition.attribute("transition_btrack") << ", NEW TRACK: "<<transition.attribute("transition_btrack");
1930     item->setTransitionParameters(transition);
1931     if (updateTransitionWidget) {
1932         ItemInfo info = item->info();
1933         QPoint p;
1934         ClipItem *transitionClip = getClipItemAt(info.startPos, info.track);
1935         if (transitionClip && transitionClip->baseClip()) {
1936             QString size = transitionClip->baseClip()->getProperty("frame_size");
1937             double factor = transitionClip->baseClip()->getProperty("aspect_ratio").toDouble();
1938             p.setX((int)(size.section('x', 0, 0).toInt() * factor + 0.5));
1939             p.setY(size.section('x', 1, 1).toInt());
1940         }
1941         emit transitionItemSelected(item, getPreviousVideoTrack(info.track), p, true);
1942     }
1943     setDocumentModified();
1944 }
1945
1946 void CustomTrackView::dragMoveEvent(QDragMoveEvent * event)
1947 {
1948     if (m_clipDrag) {
1949         const QPointF pos = mapToScene(event->pos());
1950         if (m_selectionGroup) {
1951             m_selectionGroup->setPos(pos);
1952             emit mousePosition((int)(m_selectionGroup->scenePos().x() + 0.5));
1953             event->acceptProposedAction();
1954         } else {
1955             // Drag enter was not possible, try again at mouse position
1956             insertDropClips(event->mimeData(), event->pos());
1957             event->accept();
1958         }
1959     } else {
1960         QGraphicsView::dragMoveEvent(event);
1961     }
1962 }
1963
1964 void CustomTrackView::dragLeaveEvent(QDragLeaveEvent * event)
1965 {
1966     if (m_selectionGroup && m_clipDrag) {
1967         m_thumbsTimer.stop();
1968         m_waitingThumbs.clear();
1969         QList<QGraphicsItem *> items = m_selectionGroup->childItems();
1970         qDeleteAll(items);
1971         scene()->destroyItemGroup(m_selectionGroup);
1972         m_selectionGroup = NULL;
1973     } else QGraphicsView::dragLeaveEvent(event);
1974 }
1975
1976 void CustomTrackView::dropEvent(QDropEvent * event)
1977 {
1978     if (m_selectionGroup && m_clipDrag) {
1979         QList<QGraphicsItem *> items = m_selectionGroup->childItems();
1980         resetSelectionGroup();
1981         m_scene->clearSelection();
1982         bool hasVideoClip = false;
1983         QUndoCommand *addCommand = new QUndoCommand();
1984         addCommand->setText(i18n("Add timeline clip"));
1985
1986         for (int i = 0; i < items.count(); i++) {
1987             ClipItem *item = static_cast <ClipItem *>(items.at(i));
1988             if (!hasVideoClip && (item->clipType() == AV || item->clipType() == VIDEO)) hasVideoClip = true;
1989             if (items.count() == 1) {
1990                 updateClipTypeActions(item);
1991             } else {
1992                 updateClipTypeActions(NULL);
1993             }
1994
1995             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), false, false, addCommand);
1996             item->baseClip()->addReference();
1997             m_document->updateClip(item->baseClip()->getId());
1998             ItemInfo info = item->info();
1999
2000             int tracknumber = m_document->tracksCount() - info.track - 1;
2001             bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
2002             if (isLocked) item->setItemLocked(true);
2003             ItemInfo clipInfo = info;
2004             clipInfo.track = m_document->tracksCount() - item->track();
2005             if (m_document->renderer()->mltInsertClip(clipInfo, item->xml(), item->baseClip()->producer(item->track())) == -1) {
2006                 emit displayMessage(i18n("Cannot insert clip in timeline"), ErrorMessage);
2007             }
2008
2009             if (item->baseClip()->isTransparent() && getTransitionItemAtStart(info.startPos, info.track) == NULL) {
2010                 // add transparency transition
2011                 QDomElement trans = MainWindow::transitions.getEffectByTag("composite", "composite").cloneNode().toElement();
2012                 new AddTransitionCommand(this, info, getPreviousVideoTrack(info.track), trans, false, true, addCommand);
2013             }
2014             item->setSelected(true);
2015         }
2016         m_commandStack->push(addCommand);
2017         setDocumentModified();
2018         m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
2019         if (items.count() > 1) groupSelectedItems(true);
2020         event->setDropAction(Qt::MoveAction);
2021         event->accept();
2022     } else QGraphicsView::dropEvent(event);
2023     setFocus();
2024 }
2025
2026
2027 QStringList CustomTrackView::mimeTypes() const
2028 {
2029     QStringList qstrList;
2030     // list of accepted mime types for drop
2031     qstrList.append("text/plain");
2032     qstrList.append("kdenlive/producerslist");
2033     qstrList.append("kdenlive/clip");
2034     return qstrList;
2035 }
2036
2037 Qt::DropActions CustomTrackView::supportedDropActions() const
2038 {
2039     // returns what actions are supported when dropping
2040     return Qt::MoveAction;
2041 }
2042
2043 void CustomTrackView::setDuration(int duration)
2044 {
2045     int diff = qAbs(duration - sceneRect().width());
2046     if (diff * matrix().m11() > -50) {
2047         if (matrix().m11() < 0.4) setSceneRect(0, 0, (duration + 100 / matrix().m11()), sceneRect().height());
2048         else setSceneRect(0, 0, (duration + 300), sceneRect().height());
2049     }
2050     m_projectDuration = duration;
2051 }
2052
2053 int CustomTrackView::duration() const
2054 {
2055     return m_projectDuration;
2056 }
2057
2058 void CustomTrackView::addTrack(TrackInfo type, int ix)
2059 {
2060     if (ix == -1 || ix == m_document->tracksCount()) {
2061         m_document->insertTrack(ix, type);
2062         m_document->renderer()->mltInsertTrack(1, type.type == VIDEOTRACK);
2063     } else {
2064         m_document->insertTrack(m_document->tracksCount() - ix, type);
2065         // insert track in MLT playlist
2066         m_document->renderer()->mltInsertTrack(m_document->tracksCount() - ix, type.type == VIDEOTRACK);
2067
2068         double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
2069         QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY);
2070         QList<QGraphicsItem *> selection = m_scene->items(r);
2071         resetSelectionGroup();
2072
2073         m_selectionGroup = new AbstractGroupItem(m_document->fps());
2074         scene()->addItem(m_selectionGroup);
2075         for (int i = 0; i < selection.count(); i++) {
2076             if ((!selection.at(i)->parentItem()) && (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET || selection.at(i)->type() == GROUPWIDGET)) {
2077                 m_selectionGroup->addToGroup(selection.at(i));
2078                 selection.at(i)->setFlags(QGraphicsItem::ItemIsSelectable);
2079             }
2080         }
2081         // Move graphic items
2082         m_selectionGroup->translate(0, m_tracksHeight);
2083
2084         // adjust track number
2085         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
2086         for (int i = 0; i < children.count(); i++) {
2087             if (children.at(i)->type() == GROUPWIDGET) {
2088                 AbstractGroupItem *grp = static_cast<AbstractGroupItem*>(children.at(i));
2089                 children << grp->childItems();
2090                 continue;
2091             }
2092             AbstractClipItem *item = static_cast <AbstractClipItem *>(children.at(i));
2093             item->updateItem();
2094             ItemInfo clipinfo = item->info();
2095             if (item->type() == AVWIDGET) {
2096                 ClipItem *clip = static_cast <ClipItem *>(item);
2097                 // We add a move clip command so that we get the correct producer for new track number
2098                 if (clip->clipType() == AV || clip->clipType() == AUDIO) {
2099                     Mlt::Producer *prod;
2100                     if (clip->isAudioOnly()) prod = clip->baseClip()->audioProducer(clipinfo.track);
2101                     else if (clip->isVideoOnly()) prod = clip->baseClip()->videoProducer();
2102                     else prod = clip->baseClip()->producer(clipinfo.track);
2103                     if (m_document->renderer()->mltUpdateClipProducer((int)(m_document->tracksCount() - clipinfo.track), clipinfo.startPos.frames(m_document->fps()), prod) == false) {
2104                         // problem updating clip
2105                         emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", clipinfo.startPos.frames(m_document->fps()), clipinfo.track), ErrorMessage);
2106                     }
2107                 }
2108             } else if (item->type() == TRANSITIONWIDGET) {
2109                 Transition *tr = static_cast <Transition *>(item);
2110                 int track = tr->transitionEndTrack();
2111                 if (track >= ix) {
2112                     tr->updateTransitionEndTrack(getPreviousVideoTrack(clipinfo.track));
2113                 }
2114             }
2115         }
2116         resetSelectionGroup(false);
2117     }
2118
2119     int maxHeight = m_tracksHeight * m_document->tracksCount();
2120     for (int i = 0; i < m_guides.count(); i++) {
2121         QLineF l = m_guides.at(i)->line();
2122         l.setP2(QPointF(l.x2(), maxHeight));
2123         m_guides.at(i)->setLine(l);
2124     }
2125     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), maxHeight);
2126     setSceneRect(0, 0, sceneRect().width(), maxHeight);
2127     viewport()->update();
2128     emit tracksChanged();
2129     //QTimer::singleShot(500, this, SIGNAL(trackHeightChanged()));
2130     //setFixedHeight(50 * m_tracksCount);
2131 }
2132
2133 void CustomTrackView::removeTrack(int ix)
2134 {
2135     // Delete track in MLT playlist
2136     m_document->renderer()->mltDeleteTrack(m_document->tracksCount() - ix);
2137     m_document->deleteTrack(m_document->tracksCount() - ix - 1);
2138
2139     double startY = ix * (m_tracksHeight + 1) + m_tracksHeight / 2;
2140     QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY);
2141     QList<QGraphicsItem *> selection = m_scene->items(r);
2142
2143     resetSelectionGroup();
2144
2145     m_selectionGroup = new AbstractGroupItem(m_document->fps());
2146     scene()->addItem(m_selectionGroup);
2147     for (int i = 0; i < selection.count(); i++) {
2148         if ((!selection.at(i)->parentItem()) && (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET || selection.at(i)->type() == GROUPWIDGET)) {
2149             m_selectionGroup->addToGroup(selection.at(i));
2150             selection.at(i)->setFlags(QGraphicsItem::ItemIsSelectable);
2151         }
2152     }
2153     // Move graphic items
2154     qreal ydiff = 0 - (int) m_tracksHeight;
2155     m_selectionGroup->translate(0, ydiff);
2156
2157     // adjust track number
2158     QList<QGraphicsItem *> children = m_selectionGroup->childItems();
2159     //kDebug() << "// FOUND CLIPS TO MOVE: " << children.count();
2160     for (int i = 0; i < children.count(); i++) {
2161         if (children.at(i)->type() == GROUPWIDGET) {
2162             AbstractGroupItem *grp = static_cast<AbstractGroupItem*>(children.at(i));
2163             children << grp->childItems();
2164             continue;
2165         }
2166         if (children.at(i)->type() == AVWIDGET) {
2167             ClipItem *clip = static_cast <ClipItem *>(children.at(i));
2168             clip->updateItem();
2169             ItemInfo clipinfo = clip->info();
2170             // We add a move clip command so that we get the correct producer for new track number
2171             if (clip->clipType() == AV || clip->clipType() == AUDIO) {
2172                 Mlt::Producer *prod;
2173                 if (clip->isAudioOnly()) prod = clip->baseClip()->audioProducer(clipinfo.track);
2174                 else if (clip->isVideoOnly()) prod = clip->baseClip()->videoProducer();
2175                 else prod = clip->baseClip()->producer(clipinfo.track);
2176                 if (!m_document->renderer()->mltUpdateClipProducer((int)(m_document->tracksCount() - clipinfo.track), clipinfo.startPos.frames(m_document->fps()), prod)) {
2177                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", clipinfo.startPos.frames(m_document->fps()), clipinfo.track), ErrorMessage);
2178                 }
2179             }
2180         } else if (children.at(i)->type() == TRANSITIONWIDGET) {
2181             Transition *tr = static_cast <Transition *>(children.at(i));
2182             tr->updateItem();
2183             int track = tr->transitionEndTrack();
2184             if (track >= ix) {
2185                 ItemInfo clipinfo = tr->info();
2186                 tr->updateTransitionEndTrack(getPreviousVideoTrack(clipinfo.track));
2187             }
2188         }
2189     }
2190     resetSelectionGroup(false);
2191
2192     int maxHeight = m_tracksHeight * m_document->tracksCount();
2193     for (int i = 0; i < m_guides.count(); i++) {
2194         QLineF l = m_guides.at(i)->line();
2195         l.setP2(QPointF(l.x2(), maxHeight));
2196         m_guides.at(i)->setLine(l);
2197     }
2198     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), maxHeight);
2199     setSceneRect(0, 0, sceneRect().width(), maxHeight);
2200
2201     m_selectedTrack = qMin(m_selectedTrack, m_document->tracksCount() - 1);
2202     viewport()->update();
2203     emit tracksChanged();
2204     //QTimer::singleShot(500, this, SIGNAL(trackHeightChanged()));
2205 }
2206
2207 void CustomTrackView::changeTrack(int ix, TrackInfo type)
2208 {
2209     int tracknumber = m_document->tracksCount() - ix;
2210     m_document->setTrackType(tracknumber - 1, type);
2211     m_document->renderer()->mltChangeTrackState(tracknumber, m_document->trackInfoAt(tracknumber - 1).isMute, m_document->trackInfoAt(tracknumber - 1).isBlind);
2212     QTimer::singleShot(300, this, SIGNAL(trackHeightChanged()));
2213     viewport()->update();
2214 }
2215
2216
2217 void CustomTrackView::slotSwitchTrackAudio(int ix)
2218 {
2219     /*for (int i = 0; i < m_document->tracksCount(); i++)
2220         kDebug() << "TRK " << i << " STATE: " << m_document->trackInfoAt(i).isMute << m_document->trackInfoAt(i).isBlind;*/
2221     int tracknumber = m_document->tracksCount() - ix - 1;
2222     m_document->switchTrackAudio(tracknumber, !m_document->trackInfoAt(tracknumber).isMute);
2223     kDebug() << "NEXT TRK STATE: " << m_document->trackInfoAt(tracknumber).isMute << m_document->trackInfoAt(tracknumber).isBlind;
2224     m_document->renderer()->mltChangeTrackState(tracknumber + 1, m_document->trackInfoAt(tracknumber).isMute, m_document->trackInfoAt(tracknumber).isBlind);
2225     setDocumentModified();
2226 }
2227
2228 void CustomTrackView::slotSwitchTrackLock(int ix)
2229 {
2230     int tracknumber = m_document->tracksCount() - ix - 1;
2231     LockTrackCommand *command = new LockTrackCommand(this, ix, !m_document->trackInfoAt(tracknumber).isLocked);
2232     m_commandStack->push(command);
2233 }
2234
2235
2236 void CustomTrackView::lockTrack(int ix, bool lock)
2237 {
2238     int tracknumber = m_document->tracksCount() - ix - 1;
2239     m_document->switchTrackLock(tracknumber, lock);
2240     emit doTrackLock(ix, lock);
2241     QList<QGraphicsItem *> selection = m_scene->items(0, ix * m_tracksHeight + m_tracksHeight / 2, sceneRect().width(), m_tracksHeight / 2 - 2);
2242
2243     for (int i = 0; i < selection.count(); i++) {
2244         if (selection.at(i)->type() != AVWIDGET && selection.at(i)->type() != TRANSITIONWIDGET) continue;
2245         if (selection.at(i)->isSelected()) {
2246             if (selection.at(i)->type() == AVWIDGET) emit clipItemSelected(NULL);
2247             else emit transitionItemSelected(NULL);
2248         }
2249         static_cast <AbstractClipItem *>(selection.at(i))->setItemLocked(lock);
2250     }
2251     kDebug() << "NEXT TRK STATE: " << m_document->trackInfoAt(tracknumber).isLocked;
2252     viewport()->update();
2253     setDocumentModified();
2254 }
2255
2256 void CustomTrackView::slotSwitchTrackVideo(int ix)
2257 {
2258     int tracknumber = m_document->tracksCount() - ix;
2259     m_document->switchTrackVideo(tracknumber - 1, !m_document->trackInfoAt(tracknumber - 1).isBlind);
2260     m_document->renderer()->mltChangeTrackState(tracknumber, m_document->trackInfoAt(tracknumber - 1).isMute, m_document->trackInfoAt(tracknumber - 1).isBlind);
2261     setDocumentModified();
2262 }
2263
2264 QList<QGraphicsItem *> CustomTrackView::checkForGroups(const QRectF &rect, bool *ok)
2265 {
2266     // Check there is no group going over several tracks there, or that would result in timeline corruption
2267     QList<QGraphicsItem *> selection = scene()->items(rect);
2268     *ok = true;
2269     int maxHeight = m_tracksHeight * 1.5;
2270     for (int i = 0; i < selection.count(); i++) {
2271         // Check that we don't try to move a group with clips on other tracks
2272         if (selection.at(i)->type() == GROUPWIDGET && (selection.at(i)->boundingRect().height() >= maxHeight)) {
2273             *ok = false;
2274             break;
2275         } else if (selection.at(i)->parentItem() && (selection.at(i)->parentItem()->boundingRect().height() >= maxHeight)) {
2276             *ok = false;
2277             break;
2278         }
2279     }
2280     return selection;
2281 }
2282
2283 void CustomTrackView::slotRemoveSpace()
2284 {
2285     GenTime pos;
2286     int track = 0;
2287     if (m_menuPosition.isNull()) {
2288         pos = GenTime(cursorPos(), m_document->fps());
2289         bool ok;
2290         track = QInputDialog::getInteger(this, i18n("Remove Space"), i18n("Track"), 0, 0, m_document->tracksCount() - 1, 1, &ok);
2291         if (!ok) return;
2292     } else {
2293         pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
2294         track = (int)(mapToScene(m_menuPosition).y() / m_tracksHeight);
2295     }
2296
2297     ClipItem *item = getClipItemAt(pos, track);
2298     if (item) {
2299         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);
2300         return;
2301     }
2302     int length = m_document->renderer()->mltGetSpaceLength(pos, m_document->tracksCount() - track, true);
2303     //kDebug() << "// GOT LENGT; " << length;
2304     if (length <= 0) {
2305         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);
2306         return;
2307     }
2308
2309     // Make sure there is no group in the way
2310     QRectF rect(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - pos.frames(m_document->fps()), m_tracksHeight / 2 - 2);
2311
2312     bool isOk;
2313     QList<QGraphicsItem *> items = checkForGroups(rect, &isOk);
2314     if (!isOk) {
2315         // groups found on track, do not allow the move
2316         emit displayMessage(i18n("Cannot remove space in a track with a group"), ErrorMessage);
2317         return;
2318     }
2319
2320     QList<ItemInfo> clipsToMove;
2321     QList<ItemInfo> transitionsToMove;
2322
2323     for (int i = 0; i < items.count(); i++) {
2324         if (items.at(i)->type() == AVWIDGET || items.at(i)->type() == TRANSITIONWIDGET) {
2325             AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2326             ItemInfo info = item->info();
2327             if (item->type() == AVWIDGET) {
2328                 clipsToMove.append(info);
2329             } else if (item->type() == TRANSITIONWIDGET) {
2330                 transitionsToMove.append(info);
2331             }
2332         }
2333     }
2334
2335     InsertSpaceCommand *command = new InsertSpaceCommand(this, clipsToMove, transitionsToMove, track, GenTime(-length, m_document->fps()), true);
2336     m_commandStack->push(command);
2337 }
2338
2339 void CustomTrackView::slotInsertSpace()
2340 {
2341     GenTime pos;
2342     int track = 0;
2343     if (m_menuPosition.isNull()) {
2344         pos = GenTime(cursorPos(), m_document->fps());
2345     } else {
2346         pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
2347         track = (int)(mapToScene(m_menuPosition).y() / m_tracksHeight) + 1;
2348     }
2349     SpacerDialog d(GenTime(65, m_document->fps()), m_document->timecode(), track, m_document->tracksCount(), this);
2350     if (d.exec() != QDialog::Accepted) return;
2351     GenTime spaceDuration = d.selectedDuration();
2352     track = d.selectedTrack();
2353
2354     ClipItem *item = getClipItemAt(pos, track);
2355     if (item) pos = item->startPos();
2356
2357     // Make sure there is no group in the way
2358     QRectF rect(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - pos.frames(m_document->fps()), m_tracksHeight / 2 - 2);
2359     bool isOk;
2360     QList<QGraphicsItem *> items = checkForGroups(rect, &isOk);
2361     if (!isOk) {
2362         // groups found on track, do not allow the move
2363         emit displayMessage(i18n("Cannot insert space in a track with a group"), ErrorMessage);
2364         return;
2365     }
2366
2367     QList<ItemInfo> clipsToMove;
2368     QList<ItemInfo> transitionsToMove;
2369
2370     for (int i = 0; i < items.count(); i++) {
2371         if (items.at(i)->type() == AVWIDGET || items.at(i)->type() == TRANSITIONWIDGET) {
2372             AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2373             ItemInfo info = item->info();
2374             if (item->type() == AVWIDGET) {
2375                 clipsToMove.append(info);
2376             } else if (item->type() == TRANSITIONWIDGET) {
2377                 transitionsToMove.append(info);
2378             }
2379         }
2380     }
2381
2382     InsertSpaceCommand *command = new InsertSpaceCommand(this, clipsToMove, transitionsToMove, track, spaceDuration, true);
2383     m_commandStack->push(command);
2384 }
2385
2386 void CustomTrackView::insertSpace(QList<ItemInfo> clipsToMove, QList<ItemInfo> transToMove, int track, const GenTime duration, const GenTime offset)
2387 {
2388     int diff = duration.frames(m_document->fps());
2389     resetSelectionGroup();
2390     m_selectionGroup = new AbstractGroupItem(m_document->fps());
2391     scene()->addItem(m_selectionGroup);
2392     ClipItem *clip;
2393     Transition *transition;
2394
2395     // Create lists with start pos for each track
2396     QMap <int, int> trackClipStartList;
2397     QMap <int, int> trackTransitionStartList;
2398
2399     for (int i = 1; i < m_document->tracksCount() + 1; i++) {
2400         trackClipStartList[i] = -1;
2401         trackTransitionStartList[i] = -1;
2402     }
2403
2404     if (!clipsToMove.isEmpty()) for (int i = 0; i < clipsToMove.count(); i++) {
2405             clip = getClipItemAtStart(clipsToMove.at(i).startPos + offset, clipsToMove.at(i).track);
2406             if (clip) {
2407                 if (clip->parentItem()) {
2408                     m_selectionGroup->addToGroup(clip->parentItem());
2409                     clip->parentItem()->setFlags(QGraphicsItem::ItemIsSelectable);
2410                 } else {
2411                     m_selectionGroup->addToGroup(clip);
2412                     clip->setFlags(QGraphicsItem::ItemIsSelectable);
2413                 }
2414                 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))
2415                     trackClipStartList[m_document->tracksCount() - clipsToMove.at(i).track] = clipsToMove.at(i).startPos.frames(m_document->fps());
2416             } else emit {
2417                     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);
2418                 }
2419             }
2420     if (!transToMove.isEmpty()) for (int i = 0; i < transToMove.count(); i++) {
2421             transition = getTransitionItemAtStart(transToMove.at(i).startPos + offset, transToMove.at(i).track);
2422             if (transition) {
2423                 if (transition->parentItem()) {
2424                     m_selectionGroup->addToGroup(transition->parentItem());
2425                     transition->parentItem()->setFlags(QGraphicsItem::ItemIsSelectable);
2426                 } else {
2427                     m_selectionGroup->addToGroup(transition);
2428                     transition->setFlags(QGraphicsItem::ItemIsSelectable);
2429                 }
2430                 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))
2431                     trackTransitionStartList[m_document->tracksCount() - transToMove.at(i).track] = transToMove.at(i).startPos.frames(m_document->fps());
2432             } 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);
2433         }
2434     m_selectionGroup->translate(diff, 0);
2435
2436     // update items coordinates
2437     QList<QGraphicsItem *> itemList = m_selectionGroup->childItems();
2438
2439     for (int i = 0; i < itemList.count(); i++) {
2440         if (itemList.at(i)->type() == AVWIDGET || itemList.at(i)->type() == TRANSITIONWIDGET) {
2441             static_cast < AbstractClipItem *>(itemList.at(i))->updateItem();
2442         } else if (itemList.at(i)->type() == GROUPWIDGET) {
2443             QList<QGraphicsItem *> children = itemList.at(i)->childItems();
2444             for (int j = 0; j < children.count(); j++) {
2445                 AbstractClipItem * clp = static_cast < AbstractClipItem *>(children.at(j));
2446                 clp->updateItem();
2447             }
2448         }
2449     }
2450     resetSelectionGroup(false);
2451     if (track != -1) track = m_document->tracksCount() - track;
2452     m_document->renderer()->mltInsertSpace(trackClipStartList, trackTransitionStartList, track, duration, offset);
2453 }
2454
2455 void CustomTrackView::deleteClip(const QString &clipId)
2456 {
2457     resetSelectionGroup();
2458     QList<QGraphicsItem *> itemList = items();
2459     QUndoCommand *deleteCommand = new QUndoCommand();
2460     int count = 0;
2461     for (int i = 0; i < itemList.count(); i++) {
2462         if (itemList.at(i)->type() == AVWIDGET) {
2463             ClipItem *item = (ClipItem *)itemList.at(i);
2464             if (item->clipProducer() == clipId) {
2465                 count++;
2466                 if (item->parentItem()) {
2467                     // Clip is in a group, destroy the group
2468                     new GroupClipsCommand(this, QList<ItemInfo>() << item->info(), QList<ItemInfo>(), false, deleteCommand);
2469                 }
2470                 new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), true, true, deleteCommand);
2471             }
2472         }
2473     }
2474     deleteCommand->setText(i18np("Delete timeline clip", "Delete timeline clips", count));
2475     if (count == 0) delete deleteCommand;
2476     else m_commandStack->push(deleteCommand);
2477 }
2478
2479 void CustomTrackView::setCursorPos(int pos, bool seek)
2480 {
2481     if (pos == m_cursorPos) return;
2482     emit cursorMoved((int)(m_cursorPos), (int)(pos));
2483     m_cursorPos = pos;
2484     if (seek) m_document->renderer()->seek(GenTime(m_cursorPos, m_document->fps()));
2485     else if (m_autoScroll) checkScrolling();
2486     m_cursorLine->setPos(m_cursorPos, 0);
2487 }
2488
2489 void CustomTrackView::updateCursorPos()
2490 {
2491     m_cursorLine->setPos(m_cursorPos, 0);
2492 }
2493
2494 int CustomTrackView::cursorPos()
2495 {
2496     return (int)(m_cursorPos);
2497 }
2498
2499 void CustomTrackView::moveCursorPos(int delta)
2500 {
2501     if (m_cursorPos + delta < 0) delta = 0 - m_cursorPos;
2502     emit cursorMoved((int)(m_cursorPos), (int)((m_cursorPos + delta)));
2503     m_cursorPos += delta;
2504     m_cursorLine->setPos(m_cursorPos, 0);
2505     m_document->renderer()->seek(GenTime(m_cursorPos, m_document->fps()));
2506 }
2507
2508 void CustomTrackView::initCursorPos(int pos)
2509 {
2510     emit cursorMoved((int)(m_cursorPos), (int)(pos));
2511     m_cursorPos = pos;
2512     m_cursorLine->setPos(pos, 0);
2513     checkScrolling();
2514 }
2515
2516 void CustomTrackView::checkScrolling()
2517 {
2518     ensureVisible(m_cursorPos, verticalScrollBar()->value() + 10, 2, 2, 50, 0);
2519 }
2520
2521 void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
2522 {
2523     if (m_moveOpMode == SEEK) m_moveOpMode = NONE;
2524     QGraphicsView::mouseReleaseEvent(event);
2525     setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
2526     if (m_scrollTimer.isActive()) m_scrollTimer.stop();
2527     if (event->button() == Qt::MidButton) {
2528         return;
2529     }
2530     setDragMode(QGraphicsView::NoDrag);
2531     if (m_operationMode == MOVEGUIDE) {
2532         setCursor(Qt::ArrowCursor);
2533         m_operationMode = NONE;
2534         m_dragGuide->setFlag(QGraphicsItem::ItemIsMovable, false);
2535         GenTime newPos = GenTime(m_dragGuide->pos().x(), m_document->fps());
2536         if (newPos != m_dragGuide->position()) {
2537             EditGuideCommand *command = new EditGuideCommand(this, m_dragGuide->position(), m_dragGuide->label(), newPos, m_dragGuide->label(), false);
2538             m_commandStack->push(command);
2539             m_dragGuide->updateGuide(GenTime(m_dragGuide->pos().x(), m_document->fps()));
2540             qSort(m_guides.begin(), m_guides.end(), sortGuidesList);
2541             m_document->syncGuides(m_guides);
2542         }
2543         m_dragGuide = NULL;
2544         m_dragItem = NULL;
2545         return;
2546     } else if (m_operationMode == SPACER && m_selectionGroup) {
2547         int track;
2548         if (event->modifiers() != Qt::ControlModifier) {
2549             // We are moving all tracks
2550             track = -1;
2551         } else track = (int)(mapToScene(m_clickEvent).y() / m_tracksHeight);
2552         GenTime timeOffset = GenTime((int)(m_selectionGroup->scenePos().x()), m_document->fps()) - m_selectionGroupInfo.startPos;
2553
2554         if (timeOffset != GenTime()) {
2555             QList<QGraphicsItem *> items = m_selectionGroup->childItems();
2556
2557             QList<ItemInfo> clipsToMove;
2558             QList<ItemInfo> transitionsToMove;
2559
2560             // Create lists with start pos for each track
2561             QMap <int, int> trackClipStartList;
2562             QMap <int, int> trackTransitionStartList;
2563
2564             for (int i = 1; i < m_document->tracksCount() + 1; i++) {
2565                 trackClipStartList[i] = -1;
2566                 trackTransitionStartList[i] = -1;
2567             }
2568
2569             for (int i = 0; i < items.count(); i++) {
2570                 if (items.at(i)->type() == GROUPWIDGET)
2571                     items += items.at(i)->childItems();
2572             }
2573
2574             for (int i = 0; i < items.count(); i++) {
2575                 if (items.at(i)->type() == AVWIDGET) {
2576                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2577                     ItemInfo info = item->info();
2578                     clipsToMove.append(info);
2579                     item->updateItem();
2580                     if (trackClipStartList.value(m_document->tracksCount() - info.track) == -1 || info.startPos.frames(m_document->fps()) < trackClipStartList.value(m_document->tracksCount() - info.track))
2581                         trackClipStartList[m_document->tracksCount() - info.track] = info.startPos.frames(m_document->fps());
2582                 } else if (items.at(i)->type() == TRANSITIONWIDGET) {
2583                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2584                     ItemInfo info = item->info();
2585                     transitionsToMove.append(info);
2586                     item->updateItem();
2587                     if (trackTransitionStartList.value(m_document->tracksCount() - info.track) == -1 || info.startPos.frames(m_document->fps()) < trackTransitionStartList.value(m_document->tracksCount() - info.track))
2588                         trackTransitionStartList[m_document->tracksCount() - info.track] = info.startPos.frames(m_document->fps());
2589                 }
2590             }
2591
2592             InsertSpaceCommand *command = new InsertSpaceCommand(this, clipsToMove, transitionsToMove, track, timeOffset, false);
2593             m_commandStack->push(command);
2594             if (track != -1) track = m_document->tracksCount() - track;
2595             kDebug() << "SPACER TRACK:" << track;
2596             m_document->renderer()->mltInsertSpace(trackClipStartList, trackTransitionStartList, track, timeOffset, GenTime());
2597         }
2598         resetSelectionGroup(false);
2599         m_operationMode = NONE;
2600     } else if (m_operationMode == RUBBERSELECTION) {
2601         //kDebug() << "// END RUBBER SELECT";
2602         resetSelectionGroup();
2603         groupSelectedItems();
2604         m_operationMode = NONE;
2605     }
2606
2607     if (m_dragItem == NULL && m_selectionGroup == NULL) {
2608         emit transitionItemSelected(NULL);
2609         return;
2610     }
2611     ItemInfo info;
2612     if (m_dragItem) info = m_dragItem->info();
2613
2614     if (m_operationMode == MOVE) {
2615         setCursor(Qt::OpenHandCursor);
2616
2617         if (m_dragItem->parentItem() == 0) {
2618             // we are moving one clip, easy
2619             if (m_dragItem->type() == AVWIDGET && (m_dragItemInfo.startPos != info.startPos || m_dragItemInfo.track != info.track)) {
2620                 ClipItem *item = static_cast <ClipItem *>(m_dragItem);
2621                 Mlt::Producer *prod;
2622                 if (item->isAudioOnly()) prod = item->baseClip()->audioProducer(m_dragItemInfo.track);
2623                 else if (item->isVideoOnly()) prod = item->baseClip()->videoProducer();
2624                 else prod = item->baseClip()->producer(m_dragItemInfo.track);
2625                 bool success = m_document->renderer()->mltMoveClip((int)(m_document->tracksCount() - m_dragItemInfo.track), (int)(m_document->tracksCount() - m_dragItem->track()), (int) m_dragItemInfo.startPos.frames(m_document->fps()), (int)(m_dragItem->startPos().frames(m_document->fps())), prod);
2626
2627                 if (success) {
2628                     int tracknumber = m_document->tracksCount() - item->track() - 1;
2629                     bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
2630                     if (isLocked) item->setItemLocked(true);
2631                     QUndoCommand *moveCommand = new QUndoCommand();
2632                     moveCommand->setText(i18n("Move clip"));
2633                     new MoveClipCommand(this, m_dragItemInfo, info, false, moveCommand);
2634                     // Also move automatic transitions (on lower track)
2635                     Transition *startTransition = getTransitionItemAtStart(m_dragItemInfo.startPos, m_dragItemInfo.track);
2636                     ItemInfo startTrInfo;
2637                     ItemInfo newStartTrInfo;
2638                     bool moveStartTrans = false;
2639                     bool moveEndTrans = false;
2640                     if (startTransition && startTransition->isAutomatic()) {
2641                         startTrInfo = startTransition->info();
2642                         newStartTrInfo = startTrInfo;
2643                         newStartTrInfo.track = info.track;
2644                         newStartTrInfo.startPos = info.startPos;
2645                         if (m_dragItemInfo.track == info.track && !item->baseClip()->isTransparent() && getClipItemAtEnd(newStartTrInfo.endPos, m_document->tracksCount() - startTransition->transitionEndTrack())) {
2646                             // transition end should stay the same
2647                         } else {
2648                             // transition end should be adjusted to clip
2649                             newStartTrInfo.endPos = newStartTrInfo.endPos + (newStartTrInfo.startPos - startTrInfo.startPos);
2650                         }
2651                         if (newStartTrInfo.startPos < newStartTrInfo.endPos) moveStartTrans = true;
2652                     }
2653                     if (startTransition == NULL || startTransition->endPos() < m_dragItemInfo.endPos) {
2654                         // Check if there is a transition at clip end
2655                         Transition *tr = getTransitionItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track);
2656                         if (tr && tr->isAutomatic()) {
2657                             ItemInfo trInfo = tr->info();
2658                             ItemInfo newTrInfo = trInfo;
2659                             newTrInfo.track = info.track;
2660                             newTrInfo.endPos = m_dragItem->endPos();
2661                             if (m_dragItemInfo.track == info.track && !item->baseClip()->isTransparent() && getClipItemAtStart(trInfo.startPos, m_document->tracksCount() - tr->transitionEndTrack())) {
2662                                 // transition start should stay the same
2663                             } else {
2664                                 // transition start should be moved
2665                                 newTrInfo.startPos = newTrInfo.startPos + (newTrInfo.endPos - trInfo.endPos);
2666                             }
2667                             if (newTrInfo.startPos < newTrInfo.endPos) {
2668                                 moveEndTrans = true;
2669                                 if (moveStartTrans) {
2670                                     // we have to move both transitions, remove the start one so that there is no collision
2671                                     new AddTransitionCommand(this, startTrInfo, startTransition->transitionEndTrack(), startTransition->toXML(), true, true, moveCommand);
2672                                 }
2673                                 new MoveTransitionCommand(this, trInfo, newTrInfo, true, moveCommand);
2674                                 if (moveStartTrans) {
2675                                     // re-add transition in correct place
2676                                     int transTrack = startTransition->transitionEndTrack();
2677                                     if (m_dragItemInfo.track != info.track && !startTransition->forcedTrack()) {
2678                                         transTrack = getPreviousVideoTrack(info.track);
2679                                     }
2680                                     new AddTransitionCommand(this, newStartTrInfo, transTrack, startTransition->toXML(), false, true, moveCommand);
2681                                 }
2682                             }
2683                         }
2684                     }
2685
2686                     if (moveStartTrans && !moveEndTrans) new MoveTransitionCommand(this, startTrInfo, newStartTrInfo, true, moveCommand);
2687
2688                     // Also move automatic transitions (on upper track)
2689                     Transition *tr = getTransitionItemAtStart(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
2690                     if (m_dragItemInfo.track == info.track && tr && tr->isAutomatic() && (m_document->tracksCount() - tr->transitionEndTrack()) == m_dragItemInfo.track) {
2691                         ItemInfo trInfo = tr->info();
2692                         ItemInfo newTrInfo = trInfo;
2693                         newTrInfo.startPos = m_dragItem->startPos();
2694                         ClipItem * upperClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
2695                         if (!upperClip || !upperClip->baseClip()->isTransparent()) {
2696                             if (!getClipItemAtEnd(newTrInfo.endPos, tr->track())) {
2697                                 // transition end should be adjusted to clip on upper track
2698                                 newTrInfo.endPos = newTrInfo.endPos + (newTrInfo.startPos - trInfo.startPos);
2699                             }
2700                             if (newTrInfo.startPos < newTrInfo.endPos) new MoveTransitionCommand(this, trInfo, newTrInfo, true, moveCommand);
2701                         }
2702                     }
2703                     if (m_dragItemInfo.track == info.track && (tr == NULL || tr->endPos() < m_dragItemInfo.endPos)) {
2704                         // Check if there is a transition at clip end
2705                         tr = getTransitionItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track - 1);
2706                         if (tr && tr->isAutomatic() && (m_document->tracksCount() - tr->transitionEndTrack()) == m_dragItemInfo.track) {
2707                             ItemInfo trInfo = tr->info();
2708                             ItemInfo newTrInfo = trInfo;
2709                             newTrInfo.endPos = m_dragItem->endPos();
2710                             kDebug() << "CLIP ENDS AT: " << newTrInfo.endPos.frames(25);
2711                             kDebug() << "CLIP STARTS AT: " << newTrInfo.startPos.frames(25);
2712                             ClipItem * upperClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
2713                             if (!upperClip || !upperClip->baseClip()->isTransparent()) {
2714                                 if (!getClipItemAtStart(trInfo.startPos, tr->track())) {
2715                                     // transition start should be moved
2716                                     newTrInfo.startPos = newTrInfo.startPos + (newTrInfo.endPos - trInfo.endPos);
2717                                 }
2718                                 if (newTrInfo.startPos < newTrInfo.endPos) new MoveTransitionCommand(this, trInfo, newTrInfo, true, moveCommand);
2719                             }
2720                         }
2721                     }
2722                     m_commandStack->push(moveCommand);
2723                 } else {
2724                     // undo last move and emit error message
2725                     bool snap = KdenliveSettings::snaptopoints();
2726                     KdenliveSettings::setSnaptopoints(false);
2727                     item->setPos((int) m_dragItemInfo.startPos.frames(m_document->fps()), (int)(m_dragItemInfo.track * m_tracksHeight + 1));
2728                     KdenliveSettings::setSnaptopoints(snap);
2729                     emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(m_dragItemInfo.startPos.frames(m_document->fps()))), ErrorMessage);
2730                 }
2731                 setDocumentModified();
2732             }
2733             if (m_dragItem->type() == TRANSITIONWIDGET && (m_dragItemInfo.startPos != info.startPos || m_dragItemInfo.track != info.track)) {
2734                 Transition *transition = static_cast <Transition *>(m_dragItem);
2735                 transition->updateTransitionEndTrack(getPreviousVideoTrack(m_dragItem->track()));
2736                 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)) {
2737                     // Moving transition failed, revert to previous position
2738                     emit displayMessage(i18n("Cannot move transition"), ErrorMessage);
2739                     transition->setPos((int) m_dragItemInfo.startPos.frames(m_document->fps()), (m_dragItemInfo.track) * m_tracksHeight + 1);
2740                 } else {
2741                     MoveTransitionCommand *command = new MoveTransitionCommand(this, m_dragItemInfo, info, false);
2742                     m_commandStack->push(command);
2743                 }
2744             }
2745         } else {
2746             // Moving several clips. We need to delete them and readd them to new position,
2747             // or they might overlap each other during the move
2748             QGraphicsItemGroup *group = static_cast <QGraphicsItemGroup *>(m_dragItem->parentItem());
2749             QList<QGraphicsItem *> items = group->childItems();
2750
2751             QList<ItemInfo> clipsToMove;
2752             QList<ItemInfo> transitionsToMove;
2753
2754             GenTime timeOffset = GenTime(m_dragItem->scenePos().x(), m_document->fps()) - m_dragItemInfo.startPos;
2755             const int trackOffset = (int)(m_dragItem->scenePos().y() / m_tracksHeight) - m_dragItemInfo.track;
2756             //kDebug() << "// MOVED SEVERAL CLIPS" << timeOffset.frames(25);
2757             if (timeOffset != GenTime() || trackOffset != 0) {
2758                 // remove items in MLT playlist
2759
2760                 // Expand groups
2761                 int max = items.count();
2762                 for (int i = 0; i < max; i++) {
2763                     if (items.at(i)->type() == GROUPWIDGET) {
2764                         items += items.at(i)->childItems();
2765                     }
2766                 }
2767                 m_document->renderer()->blockSignals(true);
2768                 for (int i = 0; i < items.count(); i++) {
2769                     if (items.at(i)->type() != AVWIDGET && items.at(i)->type() != TRANSITIONWIDGET) continue;
2770                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2771                     ItemInfo info = item->info();
2772                     if (item->type() == AVWIDGET) {
2773                         if (m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, info.startPos) == false) {
2774                             // error, clip cannot be removed from playlist
2775                             emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(info.startPos.frames(m_document->fps())), info.track), ErrorMessage);
2776                         } else {
2777                             clipsToMove.append(info);
2778                         }
2779                     } else {
2780                         transitionsToMove.append(info);
2781                         Transition *tr = static_cast <Transition*>(item);
2782                         m_document->renderer()->mltDeleteTransition(tr->transitionTag(), tr->transitionEndTrack(), m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
2783                     }
2784                 }
2785                 m_document->renderer()->blockSignals(false);
2786                 for (int i = 0; i < items.count(); i++) {
2787                     // re-add items in correct place
2788                     if (items.at(i)->type() != AVWIDGET && items.at(i)->type() != TRANSITIONWIDGET) continue;
2789                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2790                     item->updateItem();
2791                     ItemInfo info = item->info();
2792                     int tracknumber = m_document->tracksCount() - info.track - 1;
2793                     bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
2794                     if (isLocked) {
2795                         group->removeFromGroup(item);
2796                         item->setItemLocked(true);
2797                     }
2798
2799                     if (item->type() == AVWIDGET) {
2800                         ClipItem *clip = static_cast <ClipItem*>(item);
2801                         info.track = m_document->tracksCount() - info.track;
2802                         Mlt::Producer *prod;
2803                         if (clip->isAudioOnly()) prod = clip->baseClip()->audioProducer(info.track);
2804                         else if (clip->isVideoOnly()) prod = clip->baseClip()->videoProducer();
2805                         else prod = clip->baseClip()->producer(info.track);
2806                         m_document->renderer()->mltInsertClip(info, clip->xml(), prod);
2807                         for (int i = 0; i < clip->effectsCount(); i++) {
2808                             m_document->renderer()->mltAddEffect(info.track, info.startPos, clip->getEffectArgs(clip->effectAt(i)), false);
2809                         }
2810                     } else {
2811                         Transition *tr = static_cast <Transition*>(item);
2812                         int newTrack = tr->transitionEndTrack();
2813                         if (!tr->forcedTrack()) {
2814                             newTrack = getPreviousVideoTrack(info.track);
2815                         }
2816                         tr->updateTransitionEndTrack(newTrack);
2817                         m_document->renderer()->mltAddTransition(tr->transitionTag(), newTrack, m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
2818                     }
2819                 }
2820
2821                 MoveGroupCommand *move = new MoveGroupCommand(this, clipsToMove, transitionsToMove, timeOffset, trackOffset, false);
2822                 m_commandStack->push(move);
2823
2824                 //QPointF top = group->sceneBoundingRect().topLeft();
2825                 //QPointF oldpos = m_selectionGroup->scenePos();
2826                 //kDebug()<<"SELECTION GRP POS: "<<m_selectionGroup->scenePos()<<", TOP: "<<top;
2827                 //group->setPos(top);
2828                 //TODO: get rid of the 3 lines below
2829                 if (m_selectionGroup) {
2830                     m_selectionGroupInfo.startPos = GenTime(m_selectionGroup->scenePos().x(), m_document->fps());
2831                     m_selectionGroupInfo.track = m_selectionGroup->track();
2832                 }
2833                 setDocumentModified();
2834             }
2835         }
2836         m_document->renderer()->doRefresh();
2837     } else if (m_operationMode == RESIZESTART && m_dragItem->startPos() != m_dragItemInfo.startPos) {
2838         // resize start
2839         if (m_dragItem->type() == AVWIDGET) {
2840             ItemInfo resizeinfo = m_dragItemInfo;
2841             resizeinfo.track = m_document->tracksCount() - resizeinfo.track;
2842             bool success = m_document->renderer()->mltResizeClipStart(resizeinfo, m_dragItem->startPos() - m_dragItemInfo.startPos);
2843             if (success) {
2844                 QUndoCommand *resizeCommand = new QUndoCommand();
2845                 resizeCommand->setText(i18n("Resize clip"));
2846
2847                 // Check if there is an automatic transition on that clip (lower track)
2848                 Transition *transition = getTransitionItemAtStart(m_dragItemInfo.startPos, m_dragItemInfo.track);
2849                 if (transition && transition->isAutomatic()) {
2850                     ItemInfo trInfo = transition->info();
2851                     ItemInfo newTrInfo = trInfo;
2852                     newTrInfo.startPos = m_dragItem->startPos();
2853                     if (newTrInfo.startPos < newTrInfo.endPos)
2854                         new MoveTransitionCommand(this, trInfo, newTrInfo, true, resizeCommand);
2855                 }
2856                 // Check if there is an automatic transition on that clip (upper track)
2857                 transition = getTransitionItemAtStart(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
2858                 if (transition && transition->isAutomatic() && (m_document->tracksCount() - transition->transitionEndTrack()) == m_dragItemInfo.track) {
2859                     ItemInfo trInfo = transition->info();
2860                     ItemInfo newTrInfo = trInfo;
2861                     newTrInfo.startPos = m_dragItem->startPos();
2862                     ClipItem * upperClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
2863                     if ((!upperClip || !upperClip->baseClip()->isTransparent()) && newTrInfo.startPos < newTrInfo.endPos) {
2864                         new MoveTransitionCommand(this, trInfo, newTrInfo, true, resizeCommand);
2865                     }
2866                 }
2867
2868                 ClipItem *clip = static_cast < ClipItem * >(m_dragItem);
2869                 updatePositionEffects(clip, m_dragItemInfo);
2870
2871                 // check keyframes
2872                 QDomDocument doc;
2873                 QDomElement root = doc.createElement("list");
2874                 doc.appendChild(root);
2875                 QList <int> indexes;
2876                 for (int i = 0; i < clip->effectsCount(); i++) {
2877                     QDomElement effect = clip->effectAt(i);
2878                     if (EffectsList::hasKeyFrames(effect)) {
2879                         doc.appendChild(doc.importNode(effect, true));
2880                         indexes.append(i);
2881                     }
2882                 }
2883
2884                 if (clip->checkEffectsKeyframesPos(m_dragItemInfo.cropStart.frames(m_document->fps()), clip->cropStart().frames(m_document->fps()), true)) {
2885                     // Keyframes were modified, updateClip
2886                     QDomNodeList effs = doc.elementsByTagName("effect");
2887                     // Hack:
2888                     // Since we must always resize clip before updating the keyframes, we
2889                     // put a resize command before & after checking keyframes so that
2890                     // we are sure the resize is performed before whenever we do or undo the action
2891
2892                     new ResizeClipCommand(this, m_dragItemInfo, info, false, true, resizeCommand);
2893                     for (int i = 0; i < indexes.count(); i++) {
2894                         new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effs.at(i).cloneNode().toElement(), clip->effectAt(indexes.at(i)), indexes.at(i), false, resizeCommand);
2895                         updateEffect(m_document->tracksCount() - clip->track(), clip->startPos(), clip->effectAt(indexes.at(i)), indexes.at(i));
2896                     }
2897                     new ResizeClipCommand(this, m_dragItemInfo, info, false, true, resizeCommand);
2898                     emit clipItemSelected(clip);
2899                 } else new ResizeClipCommand(this, m_dragItemInfo, info, false, false, resizeCommand);
2900
2901                 m_commandStack->push(resizeCommand);
2902             } else {
2903                 m_dragItem->resizeStart((int) m_dragItemInfo.startPos.frames(m_document->fps()));
2904                 emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
2905             }
2906         } else if (m_dragItem->type() == TRANSITIONWIDGET) {
2907             Transition *transition = static_cast <Transition *>(m_dragItem);
2908             if (!m_document->renderer()->mltMoveTransition(transition->transitionTag(), (int)(m_document->tracksCount() - m_dragItemInfo.track), (int)(m_document->tracksCount() - m_dragItemInfo.track), transition->transitionEndTrack(), m_dragItemInfo.startPos, m_dragItemInfo.endPos, info.startPos, info.endPos)) {
2909                 // Cannot resize transition
2910                 transition->resizeStart((int) m_dragItemInfo.startPos.frames(m_document->fps()));
2911                 emit displayMessage(i18n("Cannot resize transition"), ErrorMessage);
2912             } else {
2913                 MoveTransitionCommand *command = new MoveTransitionCommand(this, m_dragItemInfo, info, false);
2914                 m_commandStack->push(command);
2915             }
2916
2917         }
2918         if (m_dragItem->parentItem() && m_dragItem->parentItem() != m_selectionGroup) {
2919             // Item was resized, rebuild group;
2920             AbstractGroupItem *group = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
2921             QList <QGraphicsItem *> children = group->childItems();
2922             m_document->clipManager()->removeGroup(group);
2923             scene()->destroyItemGroup(group);
2924             for (int i = 0; i < children.count(); i++) {
2925                 children.at(i)->setSelected(true);
2926             }
2927             groupSelectedItems(false, true);
2928         }
2929         //m_document->renderer()->doRefresh();
2930     } else if (m_operationMode == RESIZEEND && m_dragItem->endPos() != m_dragItemInfo.endPos) {
2931         // resize end
2932         if (m_dragItem->type() == AVWIDGET) {
2933             ItemInfo resizeinfo = info;
2934             resizeinfo.track = m_document->tracksCount() - resizeinfo.track;
2935             bool success = m_document->renderer()->mltResizeClipEnd(resizeinfo, resizeinfo.endPos - resizeinfo.startPos);
2936             if (success) {
2937                 QUndoCommand *resizeCommand = new QUndoCommand();
2938                 resizeCommand->setText(i18n("Resize clip"));
2939
2940                 // Check if there is an automatic transition on that clip (lower track)
2941                 Transition *tr = getTransitionItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track);
2942                 if (tr && tr->isAutomatic()) {
2943                     ItemInfo trInfo = tr->info();
2944                     ItemInfo newTrInfo = trInfo;
2945                     newTrInfo.endPos = m_dragItem->endPos();
2946                     if (newTrInfo.endPos > newTrInfo.startPos) new MoveTransitionCommand(this, trInfo, newTrInfo, true, resizeCommand);
2947                 }
2948
2949                 // Check if there is an automatic transition on that clip (upper track)
2950                 tr = getTransitionItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track - 1);
2951                 if (tr && tr->isAutomatic() && (m_document->tracksCount() - tr->transitionEndTrack()) == m_dragItemInfo.track) {
2952                     ItemInfo trInfo = tr->info();
2953                     ItemInfo newTrInfo = trInfo;
2954                     newTrInfo.endPos = m_dragItem->endPos();
2955                     ClipItem * upperClip = getClipItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track - 1);
2956                     if ((!upperClip || !upperClip->baseClip()->isTransparent()) && newTrInfo.endPos > newTrInfo.startPos) {
2957                         new MoveTransitionCommand(this, trInfo, newTrInfo, true, resizeCommand);
2958                     }
2959                 }
2960
2961                 // check keyframes
2962                 ClipItem *clip = static_cast < ClipItem * >(m_dragItem);
2963                 QDomDocument doc;
2964                 QDomElement root = doc.createElement("list");
2965                 doc.appendChild(root);
2966                 QList <int> indexes;
2967                 for (int i = 0; i < clip->effectsCount(); i++) {
2968                     QDomElement effect = clip->effectAt(i);
2969                     if (EffectsList::hasKeyFrames(effect)) {
2970                         doc.appendChild(doc.importNode(effect, true));
2971                         indexes.append(i);
2972                     }
2973                 }
2974
2975                 if (clip->checkEffectsKeyframesPos((m_dragItemInfo.cropStart + m_dragItemInfo.endPos - m_dragItemInfo.startPos).frames(m_document->fps()) - 1, (clip->cropStart() + clip->cropDuration()).frames(m_document->fps()) - 1, false)) {
2976                     // Keyframes were modified, updateClip
2977                     QDomNodeList effs = doc.elementsByTagName("effect");
2978                     // Hack:
2979                     // Since we must always resize clip before updating the keyframes, we
2980                     // put a resize command before & after checking keyframes so that
2981                     // we are sure the resize is performed before whenever we do or undo the action
2982
2983                     new ResizeClipCommand(this, m_dragItemInfo, info, false, true, resizeCommand);
2984                     for (int i = 0; i < indexes.count(); i++) {
2985                         new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effs.at(i).cloneNode().toElement(), clip->effectAt(indexes.at(i)), indexes.at(i), false, resizeCommand);
2986                         updateEffect(m_document->tracksCount() - clip->track(), clip->startPos(), clip->effectAt(indexes.at(i)), indexes.at(i));
2987                     }
2988                     new ResizeClipCommand(this, m_dragItemInfo, info, false, true, resizeCommand);
2989                     emit clipItemSelected(clip);
2990                 } else new ResizeClipCommand(this, m_dragItemInfo, info, false, false, resizeCommand);
2991
2992                 m_commandStack->push(resizeCommand);
2993                 updatePositionEffects(clip, m_dragItemInfo);
2994             } else {
2995                 m_dragItem->resizeEnd((int) m_dragItemInfo.endPos.frames(m_document->fps()));
2996                 emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
2997             }
2998         } else if (m_dragItem->type() == TRANSITIONWIDGET) {
2999             Transition *transition = static_cast <Transition *>(m_dragItem);
3000             if (!m_document->renderer()->mltMoveTransition(transition->transitionTag(), (int)(m_document->tracksCount() - m_dragItemInfo.track), (int)(m_document->tracksCount() - m_dragItemInfo.track), transition->transitionEndTrack(), m_dragItemInfo.startPos, m_dragItemInfo.endPos, info.startPos, info.endPos)) {
3001                 // Cannot resize transition
3002                 transition->resizeEnd((int) m_dragItemInfo.endPos.frames(m_document->fps()));
3003                 emit displayMessage(i18n("Cannot resize transition"), ErrorMessage);
3004             } else {
3005                 MoveTransitionCommand *command = new MoveTransitionCommand(this, m_dragItemInfo, info, false);
3006                 m_commandStack->push(command);
3007             }
3008         }
3009         if (m_dragItem->parentItem() && m_dragItem->parentItem() != m_selectionGroup) {
3010             // Item was resized, rebuild group;
3011             AbstractGroupItem *group = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
3012             QList <QGraphicsItem *> children = group->childItems();
3013             m_document->clipManager()->removeGroup(group);
3014             scene()->destroyItemGroup(group);
3015             for (int i = 0; i < children.count(); i++) {
3016                 children.at(i)->setSelected(true);
3017             }
3018             groupSelectedItems(false, true);
3019         }
3020         //m_document->renderer()->doRefresh();
3021     } else if (m_operationMode == FADEIN) {
3022         // resize fade in effect
3023         ClipItem * item = static_cast <ClipItem *>(m_dragItem);
3024         int ix = item->hasEffect("volume", "fadein");
3025         int ix2 = item->hasEffect("", "fade_from_black");
3026         if (ix != -1) {
3027             QDomElement oldeffect = item->effectAt(ix);
3028             int start = item->cropStart().frames(m_document->fps());
3029             int end = item->fadeIn();
3030             if (end == 0) {
3031                 slotDeleteEffect(item, oldeffect);
3032             } else {
3033                 end += start;
3034                 QDomElement effect = oldeffect.cloneNode().toElement();
3035                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
3036                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
3037                 slotUpdateClipEffect(item, effect, oldeffect, ix);
3038                 emit clipItemSelected(item, ix);
3039             }
3040         } else if (item->fadeIn() != 0 && ix2 == -1) {
3041             QDomElement effect;
3042             if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) {
3043                 // add video fade
3044                 effect = MainWindow::videoEffects.getEffectByTag("", "fade_from_black").cloneNode().toElement();
3045             } else effect = MainWindow::audioEffects.getEffectByTag("volume", "fadein").cloneNode().toElement();
3046             EffectsList::setParameter(effect, "out", QString::number(item->fadeIn()));
3047             slotAddEffect(effect, m_dragItem->startPos(), m_dragItem->track());
3048         }
3049         if (ix2 != -1) {
3050             QDomElement oldeffect = item->effectAt(ix2);
3051             int start = item->cropStart().frames(m_document->fps());
3052             int end = item->fadeIn();
3053             if (end == 0) {
3054                 slotDeleteEffect(item, oldeffect);
3055             } else {
3056                 end += start;
3057                 QDomElement effect = oldeffect.cloneNode().toElement();
3058                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
3059                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
3060                 slotUpdateClipEffect(item, effect, oldeffect, ix2);
3061                 emit clipItemSelected(item, ix2);
3062             }
3063         }
3064     } else if (m_operationMode == FADEOUT) {
3065         // resize fade in effect
3066         ClipItem * item = static_cast <ClipItem *>(m_dragItem);
3067         int ix = item->hasEffect("volume", "fadeout");
3068         int ix2 = item->hasEffect("", "fade_to_black");
3069         if (ix != -1) {
3070             QDomElement oldeffect = item->effectAt(ix);
3071             int end = (item->cropDuration() + item->cropStart()).frames(m_document->fps());
3072             int start = item->fadeOut();
3073             if (start == 0) {
3074                 slotDeleteEffect(item, oldeffect);
3075             } else {
3076                 start = end - start;
3077                 QDomElement effect = oldeffect.cloneNode().toElement();
3078                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
3079                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
3080                 // kDebug()<<"EDIT FADE OUT : "<<start<<"x"<<end;
3081                 slotUpdateClipEffect(item, effect, oldeffect, ix);
3082                 emit clipItemSelected(item, ix);
3083             }
3084         } else if (item->fadeOut() != 0 && ix2 == -1) {
3085             QDomElement effect;
3086             if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) {
3087                 // add video fade
3088                 effect = MainWindow::videoEffects.getEffectByTag("", "fade_to_black").cloneNode().toElement();
3089             } else effect = MainWindow::audioEffects.getEffectByTag("volume", "fadeout").cloneNode().toElement();
3090             EffectsList::setParameter(effect, "in", QString::number(item->fadeOut()));
3091             EffectsList::setParameter(effect, "out", QString::number(0));
3092             slotAddEffect(effect, m_dragItem->startPos(), m_dragItem->track());
3093         }
3094         if (ix2 != -1) {
3095             QDomElement oldeffect = item->effectAt(ix2);
3096             int end = (item->cropDuration() + item->cropStart()).frames(m_document->fps());
3097             int start = item->fadeOut();
3098             if (start == 0) {
3099                 slotDeleteEffect(item, oldeffect);
3100             } else {
3101                 start = end - start;
3102                 QDomElement effect = oldeffect.cloneNode().toElement();
3103                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
3104                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
3105                 // kDebug()<<"EDIT FADE OUT : "<<start<<"x"<<end;
3106                 slotUpdateClipEffect(item, effect, oldeffect, ix2);
3107                 emit clipItemSelected(item, ix2);
3108             }
3109         }
3110     } else if (m_operationMode == KEYFRAME) {
3111         // update the MLT effect
3112         ClipItem * item = static_cast <ClipItem *>(m_dragItem);
3113         QString previous = item->keyframes(item->selectedEffectIndex());
3114         item->updateKeyframeEffect();
3115         QString next = item->keyframes(item->selectedEffectIndex());
3116         EditKeyFrameCommand *command = new EditKeyFrameCommand(this, item->track(), item->startPos(), item->selectedEffectIndex(), previous, next, false);
3117         m_commandStack->push(command);
3118         updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
3119         emit clipItemSelected(item, item->selectedEffectIndex());
3120     }
3121     if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET && m_dragItem->isSelected()) {
3122         // A transition is selected
3123         QPoint p;
3124         ClipItem *transitionClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track);
3125         if (transitionClip && transitionClip->baseClip()) {
3126             QString size = transitionClip->baseClip()->getProperty("frame_size");
3127             double factor = transitionClip->baseClip()->getProperty("aspect_ratio").toDouble();
3128             p.setX((int)(size.section('x', 0, 0).toInt() * factor + 0.5));
3129             p.setY(size.section('x', 1, 1).toInt());
3130         }
3131         emit transitionItemSelected(static_cast <Transition *>(m_dragItem), getPreviousVideoTrack(m_dragItem->track()), p);
3132     } else emit transitionItemSelected(NULL);
3133     if (m_operationMode != NONE && m_operationMode != MOVE) setDocumentModified();
3134     m_operationMode = NONE;
3135 }
3136
3137 void CustomTrackView::deleteClip(ItemInfo info, bool refresh)
3138 {
3139     ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()), info.track);
3140
3141     if (!item || m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, info.startPos) == false) {
3142         emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(info.startPos.frames(m_document->fps())), info.track), ErrorMessage);
3143         return;
3144     }
3145     if (item->isSelected()) emit clipItemSelected(NULL);
3146     item->baseClip()->removeReference();
3147     m_document->updateClip(item->baseClip()->getId());
3148
3149     /*if (item->baseClip()->isTransparent()) {
3150         // also remove automatic transition
3151         Transition *tr = getTransitionItemAt(info.startPos, info.track);
3152         if (tr && tr->isAutomatic()) {
3153             m_document->renderer()->mltDeleteTransition(tr->transitionTag(), tr->transitionEndTrack(), m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
3154             scene()->removeItem(tr);
3155             delete tr;
3156         }
3157     }*/
3158     m_waitingThumbs.removeAll(item);
3159     if (m_dragItem == item) m_dragItem = NULL;
3160     scene()->removeItem(item);
3161     delete item;
3162     item = NULL;
3163     setDocumentModified();
3164     if (refresh) m_document->renderer()->doRefresh();
3165 }
3166
3167 void CustomTrackView::deleteSelectedClips()
3168 {
3169     resetSelectionGroup();
3170     QList<QGraphicsItem *> itemList = scene()->selectedItems();
3171     if (itemList.count() == 0) {
3172         emit displayMessage(i18n("Select clip to delete"), ErrorMessage);
3173         return;
3174     }
3175     scene()->clearSelection();
3176     QUndoCommand *deleteSelected = new QUndoCommand();
3177
3178     bool resetGroup = false;
3179     int groupCount = 0;
3180     int clipCount = 0;
3181     int transitionCount = 0;
3182     // expand & destroy groups
3183     for (int i = 0; i < itemList.count(); i++) {
3184         if (itemList.at(i)->type() == GROUPWIDGET) {
3185             groupCount++;
3186             QList<QGraphicsItem *> children = itemList.at(i)->childItems();
3187             itemList += children;
3188             QList <ItemInfo> clipInfos;
3189             QList <ItemInfo> transitionInfos;
3190             GenTime currentPos = GenTime(m_cursorPos, m_document->fps());
3191             for (int j = 0; j < children.count(); j++) {
3192                 if (children.at(j)->type() == AVWIDGET) {
3193                     AbstractClipItem *clip = static_cast <AbstractClipItem *>(children.at(j));
3194                     if (!clip->isItemLocked()) clipInfos.append(clip->info());
3195                 } else if (children.at(j)->type() == TRANSITIONWIDGET) {
3196                     AbstractClipItem *clip = static_cast <AbstractClipItem *>(children.at(j));
3197                     if (!clip->isItemLocked()) transitionInfos.append(clip->info());
3198                 }
3199             }
3200             if (clipInfos.count() > 0) {
3201                 new GroupClipsCommand(this, clipInfos, transitionInfos, false, deleteSelected);
3202             }
3203         }
3204     }
3205
3206     for (int i = 0; i < itemList.count(); i++) {
3207         if (itemList.at(i)->type() == AVWIDGET) {
3208             clipCount++;
3209             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
3210             if (item->parentItem()) resetGroup = true;
3211             //kDebug()<<"// DELETE CLP AT: "<<item->info().startPos.frames(25);
3212             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), true, true, deleteSelected);
3213             emit clipItemSelected(NULL);
3214         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
3215             transitionCount++;
3216             Transition *item = static_cast <Transition *>(itemList.at(i));
3217             //kDebug()<<"// DELETE TRANS AT: "<<item->info().startPos.frames(25);
3218             if (item->parentItem()) resetGroup = true;
3219             new AddTransitionCommand(this, item->info(), item->transitionEndTrack(), item->toXML(), true, true, deleteSelected);
3220             emit transitionItemSelected(NULL);
3221         }
3222     }
3223     if (groupCount > 0 && clipCount == 0 && transitionCount == 0)
3224         deleteSelected->setText(i18np("Delete selected group", "Delete selected groups", groupCount));
3225     else if (clipCount > 0 && groupCount == 0 && transitionCount == 0)
3226         deleteSelected->setText(i18np("Delete selected clip", "Delete selected clips", clipCount));
3227     else if (transitionCount > 0 && groupCount == 0 && clipCount == 0)
3228         deleteSelected->setText(i18np("Delete selected transition", "Delete selected transitions", transitionCount));
3229     else deleteSelected->setText(i18n("Delete selected items"));
3230     m_commandStack->push(deleteSelected);
3231 }
3232
3233 void CustomTrackView::changeClipSpeed()
3234 {
3235     QList<QGraphicsItem *> itemList = scene()->selectedItems();
3236     if (itemList.count() == 0) {
3237         emit displayMessage(i18n("Select clip to change speed"), ErrorMessage);
3238         return;
3239     }
3240     QUndoCommand *changeSelected = new QUndoCommand();
3241     changeSelected->setText("Edit clip speed");
3242     int count = 0;
3243     int percent = -1;
3244     bool ok;
3245     for (int i = 0; i < itemList.count(); i++) {
3246         if (itemList.at(i)->type() == AVWIDGET) {
3247             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
3248             ItemInfo info = item->info();
3249             if (percent == -1) percent = QInputDialog::getInteger(this, i18n("Edit Clip Speed"), i18n("New speed (percents)"), item->speed() * 100, 1, 10000, 1, &ok);
3250             if (!ok) break;
3251             double speed = (double) percent / 100.0;
3252             if (item->speed() != speed && (item->clipType() == VIDEO || item->clipType() == AV)) {
3253                 count++;
3254                 //new ChangeSpeedCommand(this, info, item->speed(), speed, item->clipProducer(), changeSelected);
3255             }
3256         }
3257     }
3258     if (count > 0) m_commandStack->push(changeSelected);
3259     else delete changeSelected;
3260 }
3261
3262 void CustomTrackView::doChangeClipSpeed(ItemInfo info, ItemInfo speedIndependantInfo, const double speed, const double oldspeed, int strobe, const QString &id)
3263 {
3264     DocClipBase *baseclip = m_document->clipManager()->getClipById(id);
3265     ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()), info.track);
3266     if (!item) {
3267         kDebug() << "ERROR: Cannot find clip for speed change";
3268         emit displayMessage(i18n("Cannot find clip for speed change"), ErrorMessage);
3269         return;
3270     }
3271     info.track = m_document->tracksCount() - item->track();
3272     int endPos = m_document->renderer()->mltChangeClipSpeed(info, speedIndependantInfo, speed, oldspeed, strobe, baseclip->producer());
3273     if (endPos >= 0) {
3274         item->setSpeed(speed, strobe);
3275         item->updateRectGeometry();
3276         if (item->cropDuration().frames(m_document->fps()) != endPos) {
3277             item->resizeEnd((int) info.startPos.frames(m_document->fps()) + endPos - 1);
3278         }
3279         updatePositionEffects(item, info);
3280         setDocumentModified();
3281     } else emit displayMessage(i18n("Invalid clip"), ErrorMessage);
3282 }
3283
3284 void CustomTrackView::cutSelectedClips()
3285 {
3286     QList<QGraphicsItem *> itemList = scene()->selectedItems();
3287     GenTime currentPos = GenTime(m_cursorPos, m_document->fps());
3288     for (int i = 0; i < itemList.count(); i++) {
3289         if (itemList.at(i)->type() == AVWIDGET) {
3290             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
3291             if (item->parentItem() && item->parentItem() != m_selectionGroup) {
3292                 emit displayMessage(i18n("Cannot cut a clip in a group"), ErrorMessage);
3293             } else if (currentPos > item->startPos() && currentPos <  item->endPos()) {
3294                 RazorClipCommand *command = new RazorClipCommand(this, item->info(), currentPos);
3295                 m_commandStack->push(command);
3296             }
3297         }
3298     }
3299 }
3300
3301 void CustomTrackView::groupClips(bool group)
3302 {
3303     QList<QGraphicsItem *> itemList = scene()->selectedItems();
3304     QList <ItemInfo> clipInfos;
3305     QList <ItemInfo> transitionInfos;
3306     GenTime currentPos = GenTime(m_cursorPos, m_document->fps());
3307
3308     // Expand groups
3309     int max = itemList.count();
3310     for (int i = 0; i < max; i++) {
3311         if (itemList.at(i)->type() == GROUPWIDGET) {
3312             itemList += itemList.at(i)->childItems();
3313         }
3314     }
3315
3316     for (int i = 0; i < itemList.count(); i++) {
3317         if (itemList.at(i)->type() == AVWIDGET) {
3318             AbstractClipItem *clip = static_cast <AbstractClipItem *>(itemList.at(i));
3319             if (!clip->isItemLocked()) clipInfos.append(clip->info());
3320         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
3321             AbstractClipItem *clip = static_cast <AbstractClipItem *>(itemList.at(i));
3322             if (!clip->isItemLocked()) transitionInfos.append(clip->info());
3323         }
3324     }
3325     if (clipInfos.count() > 0) {
3326         GroupClipsCommand *command = new GroupClipsCommand(this, clipInfos, transitionInfos, group);
3327         m_commandStack->push(command);
3328     }
3329 }
3330
3331 void CustomTrackView::doGroupClips(QList <ItemInfo> clipInfos, QList <ItemInfo> transitionInfos, bool group)
3332 {
3333     resetSelectionGroup();
3334     m_scene->clearSelection();
3335     if (!group) {
3336         for (int i = 0; i < clipInfos.count(); i++) {
3337             ClipItem *clip = getClipItemAt(clipInfos.at(i).startPos, clipInfos.at(i).track);
3338             if (clip == NULL) continue;
3339             if (clip->parentItem() && clip->parentItem()->type() == GROUPWIDGET) {
3340                 AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(clip->parentItem());
3341                 m_document->clipManager()->removeGroup(grp);
3342                 scene()->destroyItemGroup(grp);
3343             }
3344             clip->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
3345         }
3346         for (int i = 0; i < transitionInfos.count(); i++) {
3347             Transition *tr = getTransitionItemAt(transitionInfos.at(i).startPos, transitionInfos.at(i).track);
3348             if (tr == NULL) continue;
3349             if (tr->parentItem() && tr->parentItem()->type() == GROUPWIDGET) {
3350                 AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(tr->parentItem());
3351                 m_document->clipManager()->removeGroup(grp);
3352                 scene()->destroyItemGroup(grp);
3353             }
3354             tr->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
3355         }
3356         setDocumentModified();
3357         return;
3358     }
3359
3360     QList <QGraphicsItemGroup *> groups;
3361     for (int i = 0; i < clipInfos.count(); i++) {
3362         ClipItem *clip = getClipItemAt(clipInfos.at(i).startPos, clipInfos.at(i).track);
3363         if (clip) {
3364             clip->setSelected(true);
3365         }
3366     }
3367     for (int i = 0; i < transitionInfos.count(); i++) {
3368         Transition *clip = getTransitionItemAt(transitionInfos.at(i).startPos, transitionInfos.at(i).track);
3369         if (clip) {
3370             clip->setSelected(true);
3371         }
3372     }
3373
3374     groupSelectedItems(false, true);
3375     setDocumentModified();
3376 }
3377
3378 void CustomTrackView::addClip(QDomElement xml, const QString &clipId, ItemInfo info, EffectsList effects, bool refresh)
3379 {
3380     DocClipBase *baseclip = m_document->clipManager()->getClipById(clipId);
3381     if (baseclip == NULL) {
3382         emit displayMessage(i18n("No clip copied"), ErrorMessage);
3383         return;
3384     }
3385     ClipItem *item = new ClipItem(baseclip, info, m_document->fps(), xml.attribute("speed", "1").toDouble(), xml.attribute("strobe", "1").toInt());
3386     item->setEffectList(effects);
3387     if (xml.hasAttribute("audio_only")) item->setAudioOnly(true);
3388     else if (xml.hasAttribute("video_only")) item->setVideoOnly(true);
3389     scene()->addItem(item);
3390
3391     int tracknumber = m_document->tracksCount() - info.track - 1;
3392     bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
3393     if (isLocked) item->setItemLocked(true);
3394
3395     baseclip->addReference();
3396     m_document->updateClip(baseclip->getId());
3397     info.track = m_document->tracksCount() - info.track;
3398     Mlt::Producer *prod;
3399     if (item->isAudioOnly()) prod = baseclip->audioProducer(info.track);
3400     else if (item->isVideoOnly()) prod = baseclip->videoProducer();
3401     else prod = baseclip->producer(info.track);
3402     m_document->renderer()->mltInsertClip(info, xml, prod);
3403     for (int i = 0; i < item->effectsCount(); i++) {
3404         m_document->renderer()->mltAddEffect(info.track, info.startPos, item->getEffectArgs(item->effectAt(i)), false);
3405     }
3406     setDocumentModified();
3407     if (refresh) m_document->renderer()->doRefresh();
3408     m_waitingThumbs.append(item);
3409     m_thumbsTimer.start();
3410 }
3411
3412 void CustomTrackView::slotUpdateClip(const QString &clipId, bool reload)
3413 {
3414     QList<QGraphicsItem *> list = scene()->items();
3415     ClipItem *clip = NULL;
3416     for (int i = 0; i < list.size(); ++i) {
3417         if (list.at(i)->type() == AVWIDGET) {
3418             clip = static_cast <ClipItem *>(list.at(i));
3419             if (clip->clipProducer() == clipId) {
3420                 ItemInfo info = clip->info();
3421                 info.track = m_document->tracksCount() - clip->track();
3422                 if (reload && !m_document->renderer()->mltUpdateClip(info, clip->xml(), clip->baseClip()->producer())) {
3423                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", info.startPos.frames(m_document->fps()), info.track), ErrorMessage);
3424                 }
3425                 clip->refreshClip(true);
3426                 clip->update();
3427             }
3428         }
3429     }
3430 }
3431
3432 ClipItem *CustomTrackView::getClipItemAtEnd(GenTime pos, int track)
3433 {
3434     int framepos = (int)(pos.frames(m_document->fps()));
3435     QList<QGraphicsItem *> list = scene()->items(QPointF(framepos - 1, track * m_tracksHeight + m_tracksHeight / 2));
3436     ClipItem *clip = NULL;
3437     for (int i = 0; i < list.size(); i++) {
3438         if (list.at(i)->type() == AVWIDGET) {
3439             ClipItem *test = static_cast <ClipItem *>(list.at(i));
3440             if (test->endPos() == pos) clip = test;
3441             break;
3442         }
3443     }
3444     return clip;
3445 }
3446
3447 ClipItem *CustomTrackView::getClipItemAtStart(GenTime pos, int track)
3448 {
3449     QList<QGraphicsItem *> list = scene()->items(QPointF(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2));
3450     ClipItem *clip = NULL;
3451     for (int i = 0; i < list.size(); i++) {
3452         if (list.at(i)->type() == AVWIDGET) {
3453             ClipItem *test = static_cast <ClipItem *>(list.at(i));
3454             if (test->startPos() == pos) clip = test;
3455             break;
3456         }
3457     }
3458     return clip;
3459 }
3460
3461 ClipItem *CustomTrackView::getClipItemAt(int pos, int track)
3462 {
3463     const QPointF p(pos, track * m_tracksHeight + m_tracksHeight / 2);
3464     QList<QGraphicsItem *> list = scene()->items(p);
3465     ClipItem *clip = NULL;
3466     for (int i = 0; i < list.size(); i++) {
3467         if (list.at(i)->type() == AVWIDGET) {
3468             clip = static_cast <ClipItem *>(list.at(i));
3469             break;
3470         }
3471     }
3472     return clip;
3473 }
3474
3475 ClipItem *CustomTrackView::getClipItemAt(GenTime pos, int track)
3476 {
3477     return getClipItemAt((int) pos.frames(m_document->fps()), track);
3478 }
3479
3480 Transition *CustomTrackView::getTransitionItemAt(int pos, int track)
3481 {
3482     const QPointF p(pos, (track + 1) * m_tracksHeight);
3483     QList<QGraphicsItem *> list = scene()->items(p);
3484     Transition *clip = NULL;
3485     for (int i = 0; i < list.size(); i++) {
3486         if (list.at(i)->type() == TRANSITIONWIDGET) {
3487             clip = static_cast <Transition *>(list.at(i));
3488             break;
3489         }
3490     }
3491     return clip;
3492 }
3493
3494 Transition *CustomTrackView::getTransitionItemAt(GenTime pos, int track)
3495 {
3496     return getTransitionItemAt(pos.frames(m_document->fps()), track);
3497 }
3498
3499 Transition *CustomTrackView::getTransitionItemAtEnd(GenTime pos, int track)
3500 {
3501     int framepos = (int)(pos.frames(m_document->fps()));
3502     const QPointF p(framepos - 1, (track + 1) * m_tracksHeight);
3503     QList<QGraphicsItem *> list = scene()->items(p);
3504     Transition *clip = NULL;
3505     for (int i = 0; i < list.size(); i++) {
3506         if (list.at(i)->type() == TRANSITIONWIDGET) {
3507             Transition *test = static_cast <Transition *>(list.at(i));
3508             if (test->endPos() == pos) clip = test;
3509             break;
3510         }
3511     }
3512     return clip;
3513 }
3514
3515 Transition *CustomTrackView::getTransitionItemAtStart(GenTime pos, int track)
3516 {
3517     const QPointF p(pos.frames(m_document->fps()), (track + 1) * m_tracksHeight);
3518     QList<QGraphicsItem *> list = scene()->items(p);
3519     Transition *clip = NULL;
3520     for (int i = 0; i < list.size(); ++i) {
3521         if (list.at(i)->type() == TRANSITIONWIDGET) {
3522             Transition *test = static_cast <Transition *>(list.at(i));
3523             if (test->startPos() == pos) clip = test;
3524             break;
3525         }
3526     }
3527     return clip;
3528 }
3529
3530 void CustomTrackView::moveClip(const ItemInfo start, const ItemInfo end, bool refresh)
3531 {
3532     if (m_selectionGroup) resetSelectionGroup(false);
3533     ClipItem *item = getClipItemAt((int) start.startPos.frames(m_document->fps()), start.track);
3534     if (!item) {
3535         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);
3536         kDebug() << "----------------  ERROR, CANNOT find clip to move at.. ";
3537         return;
3538     }
3539     Mlt::Producer *prod;
3540     if (item->isAudioOnly()) prod = item->baseClip()->audioProducer(end.track);
3541     else if (item->isVideoOnly()) prod = item->baseClip()->videoProducer();
3542     else prod = item->baseClip()->producer(end.track);
3543
3544     bool success = m_document->renderer()->mltMoveClip((int)(m_document->tracksCount() - start.track), (int)(m_document->tracksCount() - end.track), (int) start.startPos.frames(m_document->fps()), (int)end.startPos.frames(m_document->fps()), prod);
3545     if (success) {
3546         bool snap = KdenliveSettings::snaptopoints();
3547         KdenliveSettings::setSnaptopoints(false);
3548         item->setPos((int) end.startPos.frames(m_document->fps()), (int)(end.track * m_tracksHeight + 1));
3549
3550         int tracknumber = m_document->tracksCount() - end.track - 1;
3551         bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
3552         m_scene->clearSelection();
3553         if (isLocked) item->setItemLocked(true);
3554         else {
3555             if (item->isItemLocked()) item->setItemLocked(false);
3556             item->setSelected(true);
3557         }
3558         if (item->baseClip()->isTransparent()) {
3559             // Also move automatic transition
3560             Transition *tr = getTransitionItemAt(start.startPos, start.track);
3561             if (tr && tr->isAutomatic()) {
3562                 tr->updateTransitionEndTrack(getPreviousVideoTrack(end.track));
3563                 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);
3564                 tr->setPos((int) end.startPos.frames(m_document->fps()), (int)(end.track * m_tracksHeight + 1));
3565             }
3566         }
3567         KdenliveSettings::setSnaptopoints(snap);
3568         setDocumentModified();
3569     } else {
3570         // undo last move and emit error message
3571         emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(end.startPos.frames(m_document->fps()))), ErrorMessage);
3572     }
3573     if (refresh) m_document->renderer()->doRefresh();
3574     //kDebug() << " // MOVED CLIP TO: " << end.startPos.frames(25) << ", ITEM START: " << item->startPos().frames(25);
3575 }
3576
3577 void CustomTrackView::moveGroup(QList <ItemInfo> startClip, QList <ItemInfo> startTransition, const GenTime offset, const int trackOffset, bool reverseMove)
3578 {
3579     // Group Items
3580     /*kDebug() << "//GRP MOVE, REVERS:" << reverseMove;
3581     kDebug() << "// GROUP MOV; OFFSET: " << offset.frames(25) << ", TK OFF: " << trackOffset;*/
3582     resetSelectionGroup();
3583     m_scene->clearSelection();
3584
3585     m_selectionGroup = new AbstractGroupItem(m_document->fps());
3586     scene()->addItem(m_selectionGroup);
3587
3588     m_document->renderer()->blockSignals(true);
3589     for (int i = 0; i < startClip.count(); i++) {
3590         if (reverseMove) {
3591             startClip[i].startPos = startClip.at(i).startPos - offset;
3592             startClip[i].track = startClip.at(i).track - trackOffset;
3593         }
3594         //kDebug()<<"//LKING FR CLIP AT:"<<startClip.at(i).startPos.frames(25)<<", TK:"<<startClip.at(i).track;
3595         ClipItem *clip = getClipItemAt(startClip.at(i).startPos, startClip.at(i).track);
3596         if (clip) {
3597             clip->setItemLocked(false);
3598             if (clip->parentItem()) {
3599                 m_selectionGroup->addToGroup(clip->parentItem());
3600                 clip->parentItem()->setFlags(QGraphicsItem::ItemIsSelectable);
3601             } else {
3602                 m_selectionGroup->addToGroup(clip);
3603                 clip->setFlags(QGraphicsItem::ItemIsSelectable);
3604             }
3605             m_document->renderer()->mltRemoveClip(m_document->tracksCount() - startClip.at(i).track, startClip.at(i).startPos);
3606         } else kDebug() << "//MISSING CLIP AT: " << startClip.at(i).startPos.frames(25);
3607     }
3608     for (int i = 0; i < startTransition.count(); i++) {
3609         if (reverseMove) {
3610             startTransition[i].startPos = startTransition.at(i).startPos - offset;
3611             startTransition[i].track = startTransition.at(i).track - trackOffset;
3612         }
3613         Transition *tr = getTransitionItemAt(startTransition.at(i).startPos, startTransition.at(i).track);
3614         if (tr) {
3615             tr->setItemLocked(false);
3616             if (tr->parentItem()) {
3617                 m_selectionGroup->addToGroup(tr->parentItem());
3618                 tr->parentItem()->setFlags(QGraphicsItem::ItemIsSelectable);
3619             } else {
3620                 m_selectionGroup->addToGroup(tr);
3621                 tr->setFlags(QGraphicsItem::ItemIsSelectable);
3622             }
3623             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());
3624         } else kDebug() << "//MISSING TRANSITION AT: " << startTransition.at(i).startPos.frames(25);
3625     }
3626     m_document->renderer()->blockSignals(false);
3627
3628     if (m_selectionGroup) {
3629         bool snap = KdenliveSettings::snaptopoints();
3630         KdenliveSettings::setSnaptopoints(false);
3631
3632         m_selectionGroup->translate(offset.frames(m_document->fps()), trackOffset *(qreal) m_tracksHeight);
3633         //m_selectionGroup->moveBy(offset.frames(m_document->fps()), trackOffset *(qreal) m_tracksHeight);
3634
3635         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
3636         // Expand groups
3637         int max = children.count();
3638         for (int i = 0; i < max; i++) {
3639             if (children.at(i)->type() == GROUPWIDGET) {
3640                 children += children.at(i)->childItems();
3641                 //AbstractGroupItem *grp = static_cast<AbstractGroupItem *>(children.at(i));
3642                 //grp->moveBy(offset.frames(m_document->fps()), trackOffset *(qreal) m_tracksHeight);
3643                 /*m_document->clipManager()->removeGroup(grp);
3644                 m_scene->destroyItemGroup(grp);*/
3645                 children.removeAll(children.at(i));
3646                 i--;
3647             }
3648         }
3649
3650         for (int i = 0; i < children.count(); i++) {
3651             // re-add items in correct place
3652             if (children.at(i)->type() != AVWIDGET && children.at(i)->type() != TRANSITIONWIDGET) continue;
3653             AbstractClipItem *item = static_cast <AbstractClipItem *>(children.at(i));
3654             item->updateItem();
3655             ItemInfo info = item->info();
3656             int tracknumber = m_document->tracksCount() - info.track - 1;
3657             bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
3658             if (isLocked) item->setItemLocked(true);
3659             else if (item->isItemLocked()) item->setItemLocked(false);
3660
3661             if (item->type() == AVWIDGET) {
3662                 ClipItem *clip = static_cast <ClipItem*>(item);
3663                 info.track = m_document->tracksCount() - info.track;
3664                 Mlt::Producer *prod;
3665                 if (clip->isAudioOnly()) prod = clip->baseClip()->audioProducer(info.track);
3666                 else if (clip->isVideoOnly()) prod = clip->baseClip()->videoProducer();
3667                 else prod = clip->baseClip()->producer(info.track);
3668                 m_document->renderer()->mltInsertClip(info, clip->xml(), prod);
3669             } else if (item->type() == TRANSITIONWIDGET) {
3670                 Transition *tr = static_cast <Transition*>(item);
3671                 int newTrack;
3672                 if (!tr->forcedTrack()) newTrack = getPreviousVideoTrack(info.track);
3673                 else {
3674                     newTrack = tr->transitionEndTrack() + trackOffset;
3675                     if (newTrack < 0 || newTrack > m_document->tracksCount()) newTrack = getPreviousVideoTrack(info.track);
3676                 }
3677                 tr->updateTransitionEndTrack(newTrack);
3678                 m_document->renderer()->mltAddTransition(tr->transitionTag(), newTrack, m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
3679             }
3680         }
3681         resetSelectionGroup(false);
3682         KdenliveSettings::setSnaptopoints(snap);
3683         m_document->renderer()->doRefresh();
3684     } else kDebug() << "///////// WARNING; NO GROUP TO MOVE";
3685 }
3686
3687 void CustomTrackView::moveTransition(const ItemInfo start, const ItemInfo end, bool m_refresh)
3688 {
3689     Transition *item = getTransitionItemAt(start.startPos, start.track);
3690     if (!item) {
3691         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);
3692         kDebug() << "----------------  ERROR, CANNOT find transition to move... ";// << startPos.x() * m_scale * FRAME_SIZE + 1 << ", " << startPos.y() * m_tracksHeight + m_tracksHeight / 2;
3693         return;
3694     }
3695     //kDebug() << "----------------  Move TRANSITION FROM: " << startPos.x() << ", END:" << endPos.x() << ",TRACKS: " << oldtrack << " TO " << newtrack;
3696     bool snap = KdenliveSettings::snaptopoints();
3697     KdenliveSettings::setSnaptopoints(false);
3698     //kDebug()<<"///  RESIZE TRANS START: ("<< startPos.x()<<"x"<< startPos.y()<<") / ("<<endPos.x()<<"x"<< endPos.y()<<")";
3699     if (end.endPos - end.startPos == start.endPos - start.startPos) {
3700         // Transition was moved
3701         item->setPos((int) end.startPos.frames(m_document->fps()), (end.track) * m_tracksHeight + 1);
3702     } else if (end.endPos == start.endPos) {
3703         // Transition start resize
3704         item->resizeStart((int) end.startPos.frames(m_document->fps()));
3705     } else if (end.startPos == start.startPos) {
3706         // Transition end resize;
3707         kDebug() << "// resize END: " << end.endPos.frames(m_document->fps());
3708         item->resizeEnd((int) end.endPos.frames(m_document->fps()));
3709     } else {
3710         // Move & resize
3711         item->setPos((int) end.startPos.frames(m_document->fps()), (end.track) * m_tracksHeight + 1);
3712         item->resizeStart((int) end.startPos.frames(m_document->fps()));
3713         item->resizeEnd((int) end.endPos.frames(m_document->fps()));
3714     }
3715     //item->moveTransition(GenTime((int) (endPos.x() - startPos.x()), m_document->fps()));
3716     KdenliveSettings::setSnaptopoints(snap);
3717     item->updateTransitionEndTrack(getPreviousVideoTrack(end.track));
3718     m_document->renderer()->mltMoveTransition(item->transitionTag(), m_document->tracksCount() - start.track, m_document->tracksCount() - end.track, item->transitionEndTrack(), start.startPos, start.endPos, end.startPos, end.endPos);
3719     if (m_dragItem && m_dragItem == item) {
3720         QPoint p;
3721         ClipItem *transitionClip = getClipItemAt(item->startPos(), item->track());
3722         if (transitionClip && transitionClip->baseClip()) {
3723             QString size = transitionClip->baseClip()->getProperty("frame_size");
3724             double factor = transitionClip->baseClip()->getProperty("aspect_ratio").toDouble();
3725             p.setX((int)(size.section('x', 0, 0).toInt() * factor + 0.5));
3726             p.setY(size.section('x', 1, 1).toInt());
3727         }
3728         emit transitionItemSelected(item, getPreviousVideoTrack(item->track()), p);
3729     }
3730     if (m_refresh) m_document->renderer()->doRefresh();
3731     setDocumentModified();
3732 }
3733
3734 void CustomTrackView::resizeClip(const ItemInfo start, const ItemInfo end, bool dontWorry)
3735 {
3736     bool resizeClipStart = (start.startPos != end.startPos);
3737     ClipItem *item = getClipItemAtStart(start.startPos, start.track);
3738     if (!item) {
3739         if (dontWorry) return;
3740         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);
3741         kDebug() << "----------------  ERROR, CANNOT find clip to resize at... "; // << startPos;
3742         return;
3743     }
3744     if (item->parentItem()) {
3745         // Item is part of a group, reset group
3746         resetSelectionGroup();
3747     }
3748
3749     bool snap = KdenliveSettings::snaptopoints();
3750     KdenliveSettings::setSnaptopoints(false);
3751     if (resizeClipStart) {
3752         ItemInfo clipinfo = item->info();
3753         clipinfo.track = m_document->tracksCount() - clipinfo.track;
3754         bool success = m_document->renderer()->mltResizeClipStart(clipinfo, end.startPos - clipinfo.startPos);
3755         if (success) {
3756             kDebug() << "RESIZE CLP STRAT TO:" << end.startPos.frames(m_document->fps()) << ", OLD ST: " << start.startPos.frames(25);
3757             item->resizeStart((int) end.startPos.frames(m_document->fps()));
3758             updatePositionEffects(item, clipinfo);
3759         } else emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
3760     } else {
3761         ItemInfo clipinfo = item->info();
3762         clipinfo.track = m_document->tracksCount() - clipinfo.track;
3763         bool success = m_document->renderer()->mltResizeClipEnd(clipinfo, end.endPos - clipinfo.startPos);
3764         if (success) {
3765             item->resizeEnd((int) end.endPos.frames(m_document->fps()));
3766             updatePositionEffects(item, clipinfo);
3767         } else emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
3768     }
3769     if (!resizeClipStart && end.cropStart != start.cropStart) {
3770         kDebug() << "// RESIZE CROP, DIFF: " << (end.cropStart - start.cropStart).frames(25);
3771         ItemInfo clipinfo = end;
3772         clipinfo.track = m_document->tracksCount() - end.track;
3773         bool success = m_document->renderer()->mltResizeClipCrop(clipinfo, end.cropStart - start.cropStart);
3774         if (success) {
3775             item->setCropStart(end.cropStart);
3776             item->resetThumbs(true);
3777         }
3778     }
3779     m_document->renderer()->doRefresh();
3780     KdenliveSettings::setSnaptopoints(snap);
3781     setDocumentModified();
3782 }
3783
3784 void CustomTrackView::updatePositionEffects(ClipItem * item, ItemInfo info)
3785 {
3786     int end = item->fadeIn();
3787     if (end != 0) {
3788         // there is a fade in effect
3789         int effectPos = item->hasEffect("volume", "fadein");
3790         if (effectPos != -1) {
3791             QDomElement oldeffect = item->effectAt(effectPos);
3792             int start = item->cropStart().frames(m_document->fps());
3793             int max = item->cropDuration().frames(m_document->fps());
3794             if (end > max) {
3795                 // Make sure the fade effect is not longer than the clip
3796                 item->setFadeIn(max);
3797                 end = item->fadeIn();
3798             }
3799             end += start;
3800             EffectsList::setParameter(oldeffect, "in", QString::number(start));
3801             EffectsList::setParameter(oldeffect, "out", QString::number(end));
3802             if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), item->getEffectArgs(oldeffect)))
3803                 emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
3804             // if fade effect is displayed, update the effect edit widget with new clip duration
3805             if (item->isSelected() && effectPos == item->selectedEffectIndex()) emit clipItemSelected(item, effectPos);
3806         }
3807         effectPos = item->hasEffect("brightness", "fade_from_black");
3808         if (effectPos != -1) {
3809             QDomElement oldeffect = item->effectAt(effectPos);
3810             int start = item->cropStart().frames(m_document->fps());
3811             int max = item->cropDuration().frames(m_document->fps());
3812             if (end > max) {
3813                 // Make sure the fade effect is not longer than the clip
3814                 item->setFadeIn(max);
3815                 end = item->fadeIn();
3816             }
3817             end += start;
3818             EffectsList::setParameter(oldeffect, "in", QString::number(start));
3819             EffectsList::setParameter(oldeffect, "out", QString::number(end));
3820             if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), item->getEffectArgs(oldeffect)))
3821                 emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
3822             // if fade effect is displayed, update the effect edit widget with new clip duration
3823             if (item->isSelected() && effectPos == item->selectedEffectIndex()) emit clipItemSelected(item, effectPos);
3824         }
3825     }
3826     int start = item->fadeOut();
3827     if (start != 0) {
3828         // there is a fade out effect
3829         int effectPos = item->hasEffect("volume", "fadeout");
3830         if (effectPos != -1) {
3831             QDomElement oldeffect = item->effectAt(effectPos);
3832             int max = item->cropDuration().frames(m_document->fps());
3833             int end = max + item->cropStart().frames(m_document->fps());
3834             if (start > max) {
3835                 // Make sure the fade effect is not longer than the clip
3836                 item->setFadeOut(max);
3837                 start = item->fadeOut();
3838             }
3839             start = end - start;
3840             EffectsList::setParameter(oldeffect, "in", QString::number(start));
3841             EffectsList::setParameter(oldeffect, "out", QString::number(end));
3842             if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), item->getEffectArgs(oldeffect)))
3843                 emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
3844             // if fade effect is displayed, update the effect edit widget with new clip duration
3845             if (item->isSelected() && effectPos == item->selectedEffectIndex()) emit clipItemSelected(item, effectPos);
3846         }
3847         effectPos = item->hasEffect("brightness", "fade_to_black");
3848         if (effectPos != -1) {
3849             QDomElement oldeffect = item->effectAt(effectPos);
3850             int max = item->cropDuration().frames(m_document->fps());
3851             int end = max + item->cropStart().frames(m_document->fps());
3852             if (start > max) {
3853                 // Make sure the fade effect is not longer than the clip
3854                 item->setFadeOut(max);
3855                 start = item->fadeOut();
3856             }
3857             start = end - start;
3858             EffectsList::setParameter(oldeffect, "in", QString::number(start));
3859             EffectsList::setParameter(oldeffect, "out", QString::number(end));
3860             if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), item->getEffectArgs(oldeffect)))
3861                 emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
3862             // if fade effect is displayed, update the effect edit widget with new clip duration
3863             if (item->isSelected() && effectPos == item->selectedEffectIndex()) emit clipItemSelected(item, effectPos);
3864         }
3865     }
3866
3867     int effectPos = item->hasEffect("freeze", "freeze");
3868     if (effectPos != -1) {
3869         // Freeze effect needs to be adjusted with clip resize
3870         int diff = (info.startPos - item->startPos()).frames(m_document->fps());
3871         QDomElement eff = item->getEffectAt(effectPos);
3872         if (!eff.isNull() && diff != 0) {
3873             int freeze_pos = EffectsList::parameter(eff, "frame").toInt() + diff;
3874             EffectsList::setParameter(eff, "frame", QString::number(freeze_pos));
3875             if (item->isSelected() && item->selectedEffect().attribute("id") == "freeze") {
3876                 emit clipItemSelected(item, item->selectedEffectIndex());
3877             }
3878         }
3879     }
3880 }
3881
3882 double CustomTrackView::getSnapPointForPos(double pos)
3883 {
3884     return m_scene->getSnapPointForPos(pos, KdenliveSettings::snaptopoints());
3885 }
3886
3887 void CustomTrackView::updateSnapPoints(AbstractClipItem *selected, QList <GenTime> offsetList, bool skipSelectedItems)
3888 {
3889     QList <GenTime> snaps;
3890     if (selected && offsetList.isEmpty()) offsetList.append(selected->cropDuration());
3891     QList<QGraphicsItem *> itemList = items();
3892     for (int i = 0; i < itemList.count(); i++) {
3893         if (itemList.at(i) == selected) continue;
3894         if (skipSelectedItems && itemList.at(i)->isSelected()) continue;
3895         if (itemList.at(i)->type() == AVWIDGET) {
3896             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
3897             GenTime start = item->startPos();
3898             GenTime end = item->endPos();
3899             snaps.append(start);
3900             snaps.append(end);
3901             if (!offsetList.isEmpty()) {
3902                 for (int j = 0; j < offsetList.size(); j++) {
3903                     if (start > offsetList.at(j)) snaps.append(start - offsetList.at(j));
3904                     if (end > offsetList.at(j)) snaps.append(end - offsetList.at(j));
3905                 }
3906             }
3907             // Add clip markers
3908             QList < GenTime > markers = item->snapMarkers();
3909             for (int j = 0; j < markers.size(); ++j) {
3910                 GenTime t = markers.at(j);
3911                 snaps.append(t);
3912                 if (!offsetList.isEmpty()) {
3913                     for (int k = 0; k < offsetList.size(); k++) {
3914                         if (t > offsetList.at(k)) snaps.append(t - offsetList.at(k));
3915                     }
3916                 }
3917             }
3918         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
3919             Transition *transition = static_cast <Transition*>(itemList.at(i));
3920             GenTime start = transition->startPos();
3921             GenTime end = transition->endPos();
3922             snaps.append(start);
3923             snaps.append(end);
3924             if (!offsetList.isEmpty()) {
3925                 for (int j = 0; j < offsetList.size(); j++) {
3926                     if (start > offsetList.at(j)) snaps.append(start - offsetList.at(j));
3927                     if (end > offsetList.at(j)) snaps.append(end - offsetList.at(j));
3928                 }
3929             }
3930         }
3931     }
3932
3933     // add cursor position
3934     GenTime pos = GenTime(m_cursorPos, m_document->fps());
3935     snaps.append(pos);
3936     if (!offsetList.isEmpty()) {
3937         for (int j = 0; j < offsetList.size(); j++) {
3938             snaps.append(pos - offsetList.at(j));
3939         }
3940     }
3941
3942     // add guides
3943     for (int i = 0; i < m_guides.count(); i++) {
3944         snaps.append(m_guides.at(i)->position());
3945         if (!offsetList.isEmpty()) {
3946             for (int j = 0; j < offsetList.size(); j++) {
3947                 snaps.append(m_guides.at(i)->position() - offsetList.at(j));
3948             }
3949         }
3950     }
3951
3952     qSort(snaps);
3953     m_scene->setSnapList(snaps);
3954     //for (int i = 0; i < m_snapPoints.size(); ++i)
3955     //    kDebug() << "SNAP POINT: " << m_snapPoints.at(i).frames(25);
3956 }
3957
3958 void CustomTrackView::slotSeekToPreviousSnap()
3959 {
3960     updateSnapPoints(NULL);
3961     GenTime res = m_scene->previousSnapPoint(GenTime(m_cursorPos, m_document->fps()));
3962     setCursorPos((int) res.frames(m_document->fps()));
3963     checkScrolling();
3964 }
3965
3966 void CustomTrackView::slotSeekToNextSnap()
3967 {
3968     updateSnapPoints(NULL);
3969     GenTime res = m_scene->nextSnapPoint(GenTime(m_cursorPos, m_document->fps()));
3970     setCursorPos((int) res.frames(m_document->fps()));
3971     checkScrolling();
3972 }
3973
3974 void CustomTrackView::clipStart()
3975 {
3976     AbstractClipItem *item = getMainActiveClip();
3977     if (item != NULL) {
3978         setCursorPos((int) item->startPos().frames(m_document->fps()));
3979         checkScrolling();
3980     }
3981 }
3982
3983 void CustomTrackView::clipEnd()
3984 {
3985     AbstractClipItem *item = getMainActiveClip();
3986     if (item != NULL) {
3987         setCursorPos((int) item->endPos().frames(m_document->fps()) - 1);
3988         checkScrolling();
3989     }
3990 }
3991
3992 void CustomTrackView::slotAddClipMarker(const QString &id, GenTime t, QString c)
3993 {
3994     QString oldcomment = m_document->clipManager()->getClipById(id)->markerComment(t);
3995     AddMarkerCommand *command = new AddMarkerCommand(this, oldcomment, c, id, t);
3996     m_commandStack->push(command);
3997 }
3998
3999 void CustomTrackView::slotDeleteClipMarker(const QString &comment, const QString &id, const GenTime &position)
4000 {
4001     AddMarkerCommand *command = new AddMarkerCommand(this, comment, QString(), id, position);
4002     m_commandStack->push(command);
4003 }
4004
4005 void CustomTrackView::slotDeleteAllClipMarkers(const QString &id)
4006 {
4007     DocClipBase *base = m_document->clipManager()->getClipById(id);
4008     QList <CommentedTime> markers = base->commentedSnapMarkers();
4009
4010     if (markers.isEmpty()) {
4011         emit displayMessage(i18n("Clip has no markers"), ErrorMessage);
4012         return;
4013     }
4014     QUndoCommand *deleteMarkers = new QUndoCommand();
4015     deleteMarkers->setText("Delete clip markers");
4016
4017     for (int i = 0; i < markers.size(); i++) {
4018         new AddMarkerCommand(this, markers.at(i).comment(), QString(), id, markers.at(i).time(), deleteMarkers);
4019     }
4020     m_commandStack->push(deleteMarkers);
4021 }
4022
4023 void CustomTrackView::addMarker(const QString &id, const GenTime &pos, const QString comment)
4024 {
4025     DocClipBase *base = m_document->clipManager()->getClipById(id);
4026     if (!comment.isEmpty()) base->addSnapMarker(pos, comment);
4027     else base->deleteSnapMarker(pos);
4028     emit updateClipMarkers(base);
4029     setDocumentModified();
4030     viewport()->update();
4031 }
4032
4033 int CustomTrackView::hasGuide(int pos, int offset)
4034 {
4035     for (int i = 0; i < m_guides.count(); i++) {
4036         int guidePos = m_guides.at(i)->position().frames(m_document->fps());
4037         if (qAbs(guidePos - pos) <= offset) return guidePos;
4038         else if (guidePos > pos) return -1;
4039     }
4040     return -1;
4041 }
4042
4043 void CustomTrackView::editGuide(const GenTime oldPos, const GenTime pos, const QString &comment)
4044 {
4045     if (oldPos > GenTime() && pos > GenTime()) {
4046         // move guide
4047         for (int i = 0; i < m_guides.count(); i++) {
4048             if (m_guides.at(i)->position() == oldPos) {
4049                 Guide *item = m_guides.at(i);
4050                 item->updateGuide(pos, comment);
4051                 break;
4052             }
4053         }
4054     } else if (pos > GenTime()) addGuide(pos, comment);
4055     else {
4056         // remove guide
4057         bool found = false;
4058         for (int i = 0; i < m_guides.count(); i++) {
4059             if (m_guides.at(i)->position() == oldPos) {
4060                 delete m_guides.takeAt(i);
4061                 found = true;
4062                 break;
4063             }
4064         }
4065         if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
4066     }
4067     qSort(m_guides.begin(), m_guides.end(), sortGuidesList);
4068     m_document->syncGuides(m_guides);
4069 }
4070
4071 bool CustomTrackView::addGuide(const GenTime pos, const QString &comment)
4072 {
4073     for (int i = 0; i < m_guides.count(); i++) {
4074         if (m_guides.at(i)->position() == pos) {
4075             emit displayMessage(i18n("A guide already exists at position %1", m_document->timecode().getTimecodeFromFrames(pos.frames(m_document->fps()))), ErrorMessage);
4076             return false;
4077         }
4078     }
4079     Guide *g = new Guide(this, pos, comment, m_tracksHeight * m_document->tracksCount());
4080     scene()->addItem(g);
4081     m_guides.append(g);
4082     qSort(m_guides.begin(), m_guides.end(), sortGuidesList);
4083     m_document->syncGuides(m_guides);
4084     return true;
4085 }
4086
4087 void CustomTrackView::slotAddGuide()
4088 {
4089     CommentedTime marker(GenTime(m_cursorPos, m_document->fps()), i18n("Guide"));
4090     MarkerDialog d(NULL, marker, m_document->timecode(), i18n("Add Guide"), this);
4091     if (d.exec() != QDialog::Accepted) return;
4092     if (addGuide(d.newMarker().time(), d.newMarker().comment())) {
4093         EditGuideCommand *command = new EditGuideCommand(this, GenTime(), QString(), d.newMarker().time(), d.newMarker().comment(), false);
4094         m_commandStack->push(command);
4095     }
4096 }
4097
4098 void CustomTrackView::slotEditGuide(int guidePos)
4099 {
4100     GenTime pos;
4101     if (guidePos == -1) pos = GenTime(m_cursorPos, m_document->fps());
4102     else pos = GenTime(guidePos, m_document->fps());
4103     bool found = false;
4104     for (int i = 0; i < m_guides.count(); i++) {
4105         if (m_guides.at(i)->position() == pos) {
4106             slotEditGuide(m_guides.at(i)->info());
4107             found = true;
4108             break;
4109         }
4110     }
4111     if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
4112 }
4113
4114 void CustomTrackView::slotEditGuide(CommentedTime guide)
4115 {
4116     MarkerDialog d(NULL, guide, m_document->timecode(), i18n("Edit Guide"), this);
4117     if (d.exec() == QDialog::Accepted) {
4118         EditGuideCommand *command = new EditGuideCommand(this, guide.time(), guide.comment(), d.newMarker().time(), d.newMarker().comment(), true);
4119         m_commandStack->push(command);
4120     }
4121 }
4122
4123
4124 void CustomTrackView::slotEditTimeLineGuide()
4125 {
4126     if (m_dragGuide == NULL) return;
4127     CommentedTime guide = m_dragGuide->info();
4128     MarkerDialog d(NULL, guide, m_document->timecode(), i18n("Edit Guide"), this);
4129     if (d.exec() == QDialog::Accepted) {
4130         EditGuideCommand *command = new EditGuideCommand(this, guide.time(), guide.comment(), d.newMarker().time(), d.newMarker().comment(), true);
4131         m_commandStack->push(command);
4132     }
4133 }
4134
4135 void CustomTrackView::slotDeleteGuide(int guidePos)
4136 {
4137     GenTime pos;
4138     if (guidePos == -1) pos = GenTime(m_cursorPos, m_document->fps());
4139     else pos = GenTime(guidePos, m_document->fps());
4140     bool found = false;
4141     for (int i = 0; i < m_guides.count(); i++) {
4142         if (m_guides.at(i)->position() == pos) {
4143             EditGuideCommand *command = new EditGuideCommand(this, m_guides.at(i)->position(), m_guides.at(i)->label(), GenTime(), QString(), true);
4144             m_commandStack->push(command);
4145             found = true;
4146             break;
4147         }
4148     }
4149     if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
4150 }
4151
4152
4153 void CustomTrackView::slotDeleteTimeLineGuide()
4154 {
4155     if (m_dragGuide == NULL) return;
4156     EditGuideCommand *command = new EditGuideCommand(this, m_dragGuide->position(), m_dragGuide->label(), GenTime(), QString(), true);
4157     m_commandStack->push(command);
4158 }
4159
4160
4161 void CustomTrackView::slotDeleteAllGuides()
4162 {
4163     QUndoCommand *deleteAll = new QUndoCommand();
4164     deleteAll->setText("Delete all guides");
4165     for (int i = 0; i < m_guides.count(); i++) {
4166         new EditGuideCommand(this, m_guides.at(i)->position(), m_guides.at(i)->label(), GenTime(), QString(), true, deleteAll);
4167     }
4168     m_commandStack->push(deleteAll);
4169 }
4170
4171 void CustomTrackView::setTool(PROJECTTOOL tool)
4172 {
4173     m_tool = tool;
4174 }
4175
4176 void CustomTrackView::setScale(double scaleFactor, double verticalScale)
4177 {
4178     QMatrix matrix;
4179     matrix = matrix.scale(scaleFactor, verticalScale);
4180     m_scene->setScale(scaleFactor, verticalScale);
4181     if (m_visualTip) {
4182         scene()->removeItem(m_visualTip);
4183         m_animationTimer->stop();
4184         delete m_animation;
4185         m_animation = NULL;
4186         delete m_visualTip;
4187         m_visualTip = NULL;
4188     }
4189     double verticalPos = mapToScene(QPoint(0, viewport()->height() / 2)).y();
4190     setMatrix(matrix);
4191     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), m_tracksHeight * m_document->tracksCount() * verticalScale);
4192     int diff = sceneRect().width() - m_projectDuration;
4193     if (diff * matrix.m11() < 50) {
4194         if (matrix.m11() < 0.4) setSceneRect(0, 0, (m_projectDuration + 100 / matrix.m11()), sceneRect().height());
4195         else setSceneRect(0, 0, (m_projectDuration + 300), sceneRect().height());
4196     }
4197     centerOn(QPointF(cursorPos(), verticalPos));
4198 }
4199
4200 void CustomTrackView::slotRefreshGuides()
4201 {
4202     if (KdenliveSettings::showmarkers()) {
4203         for (int i = 0; i < m_guides.count(); i++) {
4204             m_guides.at(i)->update();
4205         }
4206     }
4207 }
4208
4209 void CustomTrackView::drawBackground(QPainter * painter, const QRectF &rect)
4210 {
4211     //kDebug() << "// DRAW BG: " << rect.width();
4212     painter->setClipRect(rect);
4213     KColorScheme scheme(palette().currentColorGroup(), KColorScheme::Window);
4214     QPen pen1 = painter->pen();
4215     pen1.setColor(scheme.shade(KColorScheme::DarkShade));
4216     painter->setPen(pen1);
4217     double min = rect.left();
4218     double max = rect.right();
4219     painter->drawLine(QPointF(min, 0), QPointF(max, 0));
4220     int maxTrack = m_document->tracksCount();
4221     QColor lockedColor = scheme.background(KColorScheme::NegativeBackground).color();
4222     QColor audioColor = palette().alternateBase().color();
4223     QColor base = scheme.background(KColorScheme::NormalBackground).color();
4224     for (int i = 0; i < maxTrack; i++) {
4225         TrackInfo info = m_document->trackInfoAt(maxTrack - i - 1);
4226         if (info.isLocked || info.type == AUDIOTRACK || i == m_selectedTrack) {
4227             const QRectF track(min, m_tracksHeight * i + 1, max - min, m_tracksHeight - 1);
4228             if (i == m_selectedTrack) painter->fillRect(track, scheme.background(KColorScheme::ActiveBackground).color());
4229             else painter->fillRect(track, info.isLocked ? lockedColor : audioColor);
4230         }
4231         painter->drawLine(QPointF(min, m_tracksHeight *(i + 1)), QPointF(max, m_tracksHeight *(i + 1)));
4232     }
4233     int lowerLimit = m_tracksHeight * maxTrack + 1;
4234     if (height() > lowerLimit) {
4235         const QRectF bg(min, lowerLimit, max - min, height() - lowerLimit);
4236         painter->fillRect(bg, base);
4237     }
4238 }
4239
4240 bool CustomTrackView::findString(const QString &text)
4241 {
4242     QString marker;
4243     for (int i = 0; i < m_searchPoints.size(); ++i) {
4244         marker = m_searchPoints.at(i).comment();
4245         if (marker.contains(text, Qt::CaseInsensitive)) {
4246             setCursorPos(m_searchPoints.at(i).time().frames(m_document->fps()), true);
4247             int vert = verticalScrollBar()->value();
4248             int hor = cursorPos();
4249             ensureVisible(hor, vert + 10, 2, 2, 50, 0);
4250             m_findIndex = i;
4251             return true;
4252         }
4253     }
4254     return false;
4255 }
4256
4257 bool CustomTrackView::findNextString(const QString &text)
4258 {
4259     QString marker;
4260     for (int i = m_findIndex + 1; i < m_searchPoints.size(); ++i) {
4261         marker = m_searchPoints.at(i).comment();
4262         if (marker.contains(text, Qt::CaseInsensitive)) {
4263             setCursorPos(m_searchPoints.at(i).time().frames(m_document->fps()), true);
4264             int vert = verticalScrollBar()->value();
4265             int hor = cursorPos();
4266             ensureVisible(hor, vert + 10, 2, 2, 50, 0);
4267             m_findIndex = i;
4268             return true;
4269         }
4270     }
4271     m_findIndex = -1;
4272     return false;
4273 }
4274
4275 void CustomTrackView::initSearchStrings()
4276 {
4277     m_searchPoints.clear();
4278     QList<QGraphicsItem *> itemList = items();
4279     for (int i = 0; i < itemList.count(); i++) {
4280         // parse all clip names
4281         if (itemList.at(i)->type() == AVWIDGET) {
4282             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
4283             GenTime start = item->startPos();
4284             CommentedTime t(start, item->clipName());
4285             m_searchPoints.append(t);
4286             // add all clip markers
4287             QList < CommentedTime > markers = item->commentedSnapMarkers();
4288             m_searchPoints += markers;
4289         }
4290     }
4291
4292     // add guides
4293     for (int i = 0; i < m_guides.count(); i++) {
4294         m_searchPoints.append(m_guides.at(i)->info());
4295     }
4296
4297     qSort(m_searchPoints);
4298 }
4299
4300 void CustomTrackView::clearSearchStrings()
4301 {
4302     m_searchPoints.clear();
4303     m_findIndex = 0;
4304 }
4305
4306 void CustomTrackView::copyClip()
4307 {
4308     qDeleteAll(m_copiedItems);
4309     m_copiedItems.clear();
4310     QList<QGraphicsItem *> itemList = scene()->selectedItems();
4311     if (itemList.count() == 0) {
4312         emit displayMessage(i18n("Select a clip before copying"), ErrorMessage);
4313         return;
4314     }
4315     for (int i = 0; i < itemList.count(); i++) {
4316         if (itemList.at(i)->type() == AVWIDGET) {
4317             ClipItem *clip = static_cast <ClipItem *>(itemList.at(i));
4318             ClipItem *clone = clip->clone(clip->info());
4319             m_copiedItems.append(clone);
4320         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
4321             Transition *dup = static_cast <Transition *>(itemList.at(i));
4322             m_copiedItems.append(dup->clone());
4323         }
4324     }
4325 }
4326
4327 bool CustomTrackView::canBePastedTo(ItemInfo info, int type) const
4328 {
4329     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));
4330     QList<QGraphicsItem *> collisions = scene()->items(rect, Qt::IntersectsItemBoundingRect);
4331     for (int i = 0; i < collisions.count(); i++) {
4332         if (collisions.at(i)->type() == type) return false;
4333     }
4334     return true;
4335 }
4336
4337 bool CustomTrackView::canBePastedTo(QList <ItemInfo> infoList, int type) const
4338 {
4339     QPainterPath path;
4340     for (int i = 0; i < infoList.count(); i++) {
4341         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));
4342         path.addRect(rect);
4343     }
4344     QList<QGraphicsItem *> collisions = scene()->items(path);
4345     for (int i = 0; i < collisions.count(); i++) {
4346         if (collisions.at(i)->type() == type) return false;
4347     }
4348     return true;
4349 }
4350
4351 bool CustomTrackView::canBePasted(QList<AbstractClipItem *> items, GenTime offset, int trackOffset) const
4352 {
4353     for (int i = 0; i < items.count(); i++) {
4354         ItemInfo info = items.at(i)->info();
4355         info.startPos += offset;
4356         info.endPos += offset;
4357         info.track += trackOffset;
4358         if (!canBePastedTo(info, items.at(i)->type())) return false;
4359     }
4360     return true;
4361 }
4362
4363 bool CustomTrackView::canBeMoved(QList<AbstractClipItem *> items, GenTime offset, int trackOffset) const
4364 {
4365     QPainterPath movePath;
4366     movePath.moveTo(0, 0);
4367
4368     for (int i = 0; i < items.count(); i++) {
4369         ItemInfo info = items.at(i)->info();
4370         info.startPos = info.startPos + offset;
4371         info.endPos = info.endPos + offset;
4372         info.track = info.track + trackOffset;
4373         if (info.startPos < GenTime()) {
4374             // No clip should go below 0
4375             return false;
4376         }
4377         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));
4378         movePath.addRect(rect);
4379     }
4380     QList<QGraphicsItem *> collisions = scene()->items(movePath, Qt::IntersectsItemBoundingRect);
4381     for (int i = 0; i < collisions.count(); i++) {
4382         if ((collisions.at(i)->type() == AVWIDGET || collisions.at(i)->type() == TRANSITIONWIDGET) && !items.contains(static_cast <AbstractClipItem *>(collisions.at(i)))) {
4383             kDebug() << "  ////////////   CLIP COLLISION, MOVE NOT ALLOWED";
4384             return false;
4385         }
4386     }
4387     return true;
4388 }
4389
4390 void CustomTrackView::pasteClip()
4391 {
4392     if (m_copiedItems.count() == 0) {
4393         emit displayMessage(i18n("No clip copied"), ErrorMessage);
4394         return;
4395     }
4396     QPoint position;
4397     if (m_menuPosition.isNull()) {
4398         position = mapFromGlobal(QCursor::pos());
4399         if (!underMouse() || position.y() > m_tracksHeight * m_document->tracksCount()) {
4400             emit displayMessage(i18n("Cannot paste selected clips"), ErrorMessage);
4401             return;
4402         }
4403     } else position = m_menuPosition;
4404     GenTime pos = GenTime((int)(mapToScene(position).x()), m_document->fps());
4405     int track = (int)(position.y() / m_tracksHeight);
4406     ItemInfo first = m_copiedItems.at(0)->info();
4407
4408     GenTime offset = pos - first.startPos;
4409     int trackOffset = track - first.track;
4410
4411     if (!canBePasted(m_copiedItems, offset, trackOffset)) {
4412         emit displayMessage(i18n("Cannot paste selected clips"), ErrorMessage);
4413         return;
4414     }
4415     QUndoCommand *pasteClips = new QUndoCommand();
4416     pasteClips->setText("Paste clips");
4417
4418     for (int i = 0; i < m_copiedItems.count(); i++) {
4419         // parse all clip names
4420         if (m_copiedItems.at(i) && m_copiedItems.at(i)->type() == AVWIDGET) {
4421             ClipItem *clip = static_cast <ClipItem *>(m_copiedItems.at(i));
4422             ItemInfo info = clip->info();
4423             info.startPos += offset;
4424             info.endPos += offset;
4425             info.track += trackOffset;
4426             if (canBePastedTo(info, AVWIDGET)) {
4427                 new AddTimelineClipCommand(this, clip->xml(), clip->clipProducer(), info, clip->effectList(), true, false, pasteClips);
4428             } else emit displayMessage(i18n("Cannot paste clip to selected place"), ErrorMessage);
4429         } else if (m_copiedItems.at(i) && m_copiedItems.at(i)->type() == TRANSITIONWIDGET) {
4430             Transition *tr = static_cast <Transition *>(m_copiedItems.at(i));
4431             ItemInfo info;
4432             info.startPos = tr->startPos() + offset;
4433             info.endPos = tr->endPos() + offset;
4434             info.track = tr->track() + trackOffset;
4435             int transitionEndTrack;
4436             if (!tr->forcedTrack()) transitionEndTrack = getPreviousVideoTrack(info.track);
4437             else transitionEndTrack = tr->transitionEndTrack();
4438             if (canBePastedTo(info, TRANSITIONWIDGET)) {
4439                 if (info.startPos >= info.endPos) {
4440                     emit displayMessage(i18n("Invalid transition"), ErrorMessage);
4441                 } else new AddTransitionCommand(this, info, transitionEndTrack, tr->toXML(), false, true, pasteClips);
4442             } else emit displayMessage(i18n("Cannot paste transition to selected place"), ErrorMessage);
4443         }
4444     }
4445     m_commandStack->push(pasteClips);
4446 }
4447
4448 void CustomTrackView::pasteClipEffects()
4449 {
4450     if (m_copiedItems.count() != 1 || m_copiedItems.at(0)->type() != AVWIDGET) {
4451         emit displayMessage(i18n("You must copy exactly one clip before pasting effects"), ErrorMessage);
4452         return;
4453     }
4454     ClipItem *clip = static_cast < ClipItem *>(m_copiedItems.at(0));
4455
4456     QUndoCommand *paste = new QUndoCommand();
4457     paste->setText("Paste effects");
4458
4459     QList<QGraphicsItem *> clips = scene()->selectedItems();
4460     for (int i = 0; i < clips.count(); ++i) {
4461         if (clips.at(i)->type() == AVWIDGET) {
4462             ClipItem *item = static_cast < ClipItem *>(clips.at(i));
4463             for (int j = 0; j < clip->effectsCount(); j++) {
4464                 QDomElement eff = clip->effectAt(j);
4465                 if (eff.attribute("unique", "0") == "0" || item->hasEffect(eff.attribute("tag"), eff.attribute("id")) == -1) {
4466                     new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), eff, true, paste);
4467                 }
4468             }
4469         }
4470     }
4471     m_commandStack->push(paste);
4472
4473     // adjust effects (fades, ...)
4474     for (int i = 0; i < clips.count(); ++i) {
4475         ClipItem *item = static_cast < ClipItem *>(clips.at(i));
4476         updatePositionEffects(item, item->info());
4477     }
4478 }
4479
4480
4481 ClipItem *CustomTrackView::getClipUnderCursor() const
4482 {
4483     QRectF rect((double) m_cursorPos, 0.0, 1.0, (double)(m_tracksHeight * m_document->tracksCount()));
4484     QList<QGraphicsItem *> collisions = scene()->items(rect, Qt::IntersectsItemBoundingRect);
4485     for (int i = 0; i < collisions.count(); i++) {
4486         if (collisions.at(i)->type() == AVWIDGET) {
4487             return static_cast < ClipItem *>(collisions.at(i));
4488         }
4489     }
4490     return NULL;
4491 }
4492
4493 AbstractClipItem *CustomTrackView::getMainActiveClip() const
4494 {
4495     QList<QGraphicsItem *> clips = scene()->selectedItems();
4496     if (clips.isEmpty()) {
4497         return getClipUnderCursor();
4498     } else {
4499         AbstractClipItem *item = NULL;
4500         for (int i = 0; i < clips.count(); ++i) {
4501             if (clips.count() == 1 || clips.at(i)->type() == AVWIDGET) {
4502                 item = static_cast < AbstractClipItem *>(clips.at(i));
4503                 if (clips.count() > 1 && item->startPos().frames(m_document->fps()) <= m_cursorPos && item->endPos().frames(m_document->fps()) >= m_cursorPos) break;
4504             }
4505         }
4506         if (item) return item;
4507     }
4508     return NULL;
4509 }
4510
4511 ClipItem *CustomTrackView::getActiveClipUnderCursor(bool allowOutsideCursor) const
4512 {
4513     QList<QGraphicsItem *> clips = scene()->selectedItems();
4514     if (clips.isEmpty()) {
4515         return getClipUnderCursor();
4516     } else {
4517         ClipItem *item;
4518         // remove all items in the list that are not clips
4519         for (int i = 0; i < clips.count();) {
4520             if (clips.at(i)->type() != AVWIDGET) clips.removeAt(i);
4521             else i++;
4522         }
4523         if (clips.count() == 1 && allowOutsideCursor) return static_cast < ClipItem *>(clips.at(0));
4524         for (int i = 0; i < clips.count(); ++i) {
4525             if (clips.at(i)->type() == AVWIDGET) {
4526                 item = static_cast < ClipItem *>(clips.at(i));
4527                 if (item->startPos().frames(m_document->fps()) <= m_cursorPos && item->endPos().frames(m_document->fps()) >= m_cursorPos)
4528                     return item;
4529             }
4530         }
4531     }
4532     return NULL;
4533 }
4534
4535 void CustomTrackView::setInPoint()
4536 {
4537     AbstractClipItem *clip = getActiveClipUnderCursor(true);
4538     if (clip == NULL) {
4539         if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET) {
4540             clip = m_dragItem;
4541         } else {
4542             emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
4543             return;
4544         }
4545     }
4546     ItemInfo startInfo = clip->info();
4547     ItemInfo endInfo = startInfo;
4548     endInfo.startPos = GenTime(m_cursorPos, m_document->fps());
4549     if (endInfo.startPos >= startInfo.endPos || endInfo.startPos < startInfo.startPos - startInfo.cropStart) {
4550         // Check for invalid resize
4551         emit displayMessage(i18n("Invalid action"), ErrorMessage);
4552         return;
4553     } else if (endInfo.startPos < startInfo.startPos) {
4554         int length = m_document->renderer()->mltGetSpaceLength(endInfo.startPos, m_document->tracksCount() - startInfo.track, false);
4555         if ((clip->type() == TRANSITIONWIDGET && itemCollision(clip, endInfo) == true) || (
4556                     (clip->type() == AVWIDGET) && length < (startInfo.startPos - endInfo.startPos).frames(m_document->fps()))) {
4557             emit displayMessage(i18n("Invalid action"), ErrorMessage);
4558             return;
4559         }
4560     }
4561     if (clip->type() == TRANSITIONWIDGET) {
4562         m_commandStack->push(new MoveTransitionCommand(this, startInfo, endInfo, true));
4563     } else m_commandStack->push(new ResizeClipCommand(this, startInfo, endInfo, true, false));
4564 }
4565
4566 void CustomTrackView::setOutPoint()
4567 {
4568     AbstractClipItem *clip = getActiveClipUnderCursor(true);
4569     if (clip == NULL) {
4570         if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET) {
4571             clip = m_dragItem;
4572         } else {
4573             emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
4574             return;
4575         }
4576     }
4577     ItemInfo startInfo = clip->info();
4578     ItemInfo endInfo = clip->info();
4579     endInfo.endPos = GenTime(m_cursorPos, m_document->fps());
4580     CLIPTYPE type = (CLIPTYPE) static_cast <ClipItem *>(clip)->clipType();
4581     if (endInfo.endPos <= startInfo.startPos || (type != IMAGE && type != COLOR && type != TEXT && endInfo.endPos > startInfo.startPos + clip->maxDuration() - startInfo.cropStart)) {
4582         // Check for invalid resize
4583         emit displayMessage(i18n("Invalid action"), ErrorMessage);
4584         return;
4585     } else if (endInfo.endPos > startInfo.endPos) {
4586         int length = m_document->renderer()->mltGetSpaceLength(startInfo.endPos, m_document->tracksCount() - startInfo.track, false);
4587         if ((clip->type() == TRANSITIONWIDGET && itemCollision(clip, endInfo) == true) || (clip->type() == AVWIDGET && length != -1 && length < (endInfo.endPos - startInfo.endPos).frames(m_document->fps()))) {
4588             kDebug() << " RESIZE ERROR, BLNK: " << length << ", RESIZE: " << (endInfo.endPos - startInfo.endPos).frames(m_document->fps());
4589             emit displayMessage(i18n("Invalid action"), ErrorMessage);
4590             return;
4591         }
4592     }
4593
4594
4595     if (clip->type() == TRANSITIONWIDGET) {
4596         m_commandStack->push(new MoveTransitionCommand(this, startInfo, endInfo, true));
4597     } else m_commandStack->push(new ResizeClipCommand(this, startInfo, endInfo, true, false));
4598 }
4599
4600 void CustomTrackView::slotUpdateAllThumbs()
4601 {
4602     QList<QGraphicsItem *> itemList = items();
4603     //if (itemList.isEmpty()) return;
4604     ClipItem *item;
4605     const QString thumbBase = m_document->projectFolder().path() + "/thumbs/";
4606     for (int i = 0; i < itemList.count(); i++) {
4607         if (itemList.at(i)->type() == AVWIDGET) {
4608             item = static_cast <ClipItem *>(itemList.at(i));
4609             if (item->clipType() != COLOR && item->clipType() != AUDIO) {
4610                 // Check if we have a cached thumbnail
4611                 if (item->clipType() == IMAGE || item->clipType() == TEXT) {
4612                     QString thumb = thumbBase + item->baseClip()->getClipHash() + "_0.png";
4613                     if (QFile::exists(thumb)) {
4614                         QPixmap pix(thumb);
4615                         item->slotSetStartThumb(pix);
4616                     }
4617                 } else {
4618                     QString startThumb = thumbBase + item->baseClip()->getClipHash() + '_';
4619                     QString endThumb = startThumb;
4620                     startThumb.append(QString::number(item->speedIndependantCropStart().frames(m_document->fps())) + ".png");
4621                     endThumb.append(QString::number((item->speedIndependantCropStart() + item->speedIndependantCropDuration()).frames(m_document->fps()) - 1) + ".png");
4622                     if (QFile::exists(startThumb)) {
4623                         QPixmap pix(startThumb);
4624                         item->slotSetStartThumb(pix);
4625                     }
4626                     if (QFile::exists(endThumb)) {
4627                         QPixmap pix(endThumb);
4628                         item->slotSetEndThumb(pix);
4629                     }
4630                 }
4631             }
4632             item->refreshClip(false);
4633             qApp->processEvents();
4634         }
4635     }
4636     viewport()->update();
4637 }
4638
4639 void CustomTrackView::saveThumbnails()
4640 {
4641     QList<QGraphicsItem *> itemList = items();
4642     ClipItem *item;
4643     QString thumbBase = m_document->projectFolder().path() + "/thumbs/";
4644     for (int i = 0; i < itemList.count(); i++) {
4645         if (itemList.at(i)->type() == AVWIDGET) {
4646             item = static_cast <ClipItem *>(itemList.at(i));
4647             if (item->clipType() != COLOR) {
4648                 // Check if we have a cached thumbnail
4649                 if (item->clipType() == IMAGE || item->clipType() == TEXT || item->clipType() == AUDIO) {
4650                     QString thumb = thumbBase + item->baseClip()->getClipHash() + "_0.png";
4651                     if (!QFile::exists(thumb)) {
4652                         QPixmap pix(item->startThumb());
4653                         pix.save(thumb);
4654                     }
4655                 } else {
4656                     QString startThumb = thumbBase + item->baseClip()->getClipHash() + '_';
4657                     QString endThumb = startThumb;
4658                     startThumb.append(QString::number(item->speedIndependantCropStart().frames(m_document->fps())) + ".png");
4659                     endThumb.append(QString::number((item->speedIndependantCropStart() + item->speedIndependantCropDuration()).frames(m_document->fps()) - 1) + ".png");
4660                     if (!QFile::exists(startThumb)) {
4661                         QPixmap pix(item->startThumb());
4662                         pix.save(startThumb);
4663                     }
4664                     if (!QFile::exists(endThumb)) {
4665                         QPixmap pix(item->endThumb());
4666                         pix.save(endThumb);
4667                     }
4668                 }
4669             }
4670         }
4671     }
4672 }
4673
4674
4675 void CustomTrackView::slotInsertTrack(int ix)
4676 {
4677     TrackDialog d(m_document, parentWidget());
4678     d.label->setText(i18n("Insert track"));
4679     d.track_nb->setMaximum(m_document->tracksCount() - 1);
4680     d.track_nb->setValue(ix);
4681     d.setWindowTitle(i18n("Insert New Track"));
4682     d.slotUpdateName(ix);
4683
4684     if (d.exec() == QDialog::Accepted) {
4685         ix = d.track_nb->value();
4686         if (d.before_select->currentIndex() == 1) {
4687             ix++;
4688         }
4689         TrackInfo info;
4690         if (d.video_track->isChecked()) {
4691             info.type = VIDEOTRACK;
4692             info.isMute = false;
4693             info.isBlind = false;
4694             info.isLocked = false;
4695         } else {
4696             info.type = AUDIOTRACK;
4697             info.isMute = false;
4698             info.isBlind = true;
4699             info.isLocked = false;
4700         }
4701         AddTrackCommand *addTrack = new AddTrackCommand(this, ix, info, true);
4702         m_commandStack->push(addTrack);
4703         setDocumentModified();
4704     }
4705 }
4706
4707 void CustomTrackView::slotDeleteTrack(int ix)
4708 {
4709     TrackDialog d(m_document, parentWidget());
4710     d.label->setText(i18n("Delete track"));
4711     d.before_select->setHidden(true);
4712     d.track_nb->setMaximum(m_document->tracksCount() - 1);
4713     d.track_nb->setValue(ix);
4714     d.slotUpdateName(ix);
4715     d.setWindowTitle(i18n("Delete Track"));
4716     d.video_track->setHidden(true);
4717     d.audio_track->setHidden(true);
4718     if (d.exec() == QDialog::Accepted) {
4719         ix = d.track_nb->value();
4720         TrackInfo info = m_document->trackInfoAt(m_document->tracksCount() - ix - 1);
4721         deleteTimelineTrack(ix, info);
4722         setDocumentModified();
4723         /*AddTrackCommand* command = new AddTrackCommand(this, ix, info, false);
4724         m_commandStack->push(command);*/
4725     }
4726 }
4727
4728 void CustomTrackView::slotChangeTrack(int ix)
4729 {
4730     TrackDialog d(m_document, parentWidget());
4731     d.label->setText(i18n("Change track"));
4732     d.before_select->setHidden(true);
4733     d.track_nb->setMaximum(m_document->tracksCount() - 1);
4734     d.track_nb->setValue(ix);
4735     d.slotUpdateName(ix);
4736     d.setWindowTitle(i18n("Change Track Type"));
4737
4738     if (m_document->trackInfoAt(m_document->tracksCount() - ix - 1).type == VIDEOTRACK)
4739         d.video_track->setChecked(true);
4740     else
4741         d.audio_track->setChecked(true);
4742
4743     if (d.exec() == QDialog::Accepted) {
4744         TrackInfo info;
4745         info.isLocked = false;
4746         info.isMute = false;
4747         ix = d.track_nb->value();
4748
4749         if (d.video_track->isChecked()) {
4750             info.type = VIDEOTRACK;
4751             info.isBlind = false;
4752         } else {
4753             info.type = AUDIOTRACK;
4754             info.isBlind = true;
4755         }
4756         changeTimelineTrack(ix, info);
4757         setDocumentModified();
4758     }
4759 }
4760
4761
4762 void CustomTrackView::deleteTimelineTrack(int ix, TrackInfo trackinfo)
4763 {
4764     double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
4765     QRectF r(0, startY, sceneRect().width(), m_tracksHeight / 2 - 1);
4766     QList<QGraphicsItem *> selection = m_scene->items(r);
4767     QUndoCommand *deleteTrack = new QUndoCommand();
4768     deleteTrack->setText("Delete track");
4769
4770     // Delete all clips in selected track
4771     for (int i = 0; i < selection.count(); i++) {
4772         if (selection.at(i)->type() == AVWIDGET) {
4773             ClipItem *item =  static_cast <ClipItem *>(selection.at(i));
4774             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), false, true, deleteTrack);
4775             m_scene->removeItem(item);
4776             delete item;
4777             item = NULL;
4778         } else if (selection.at(i)->type() == TRANSITIONWIDGET) {
4779             Transition *item =  static_cast <Transition *>(selection.at(i));
4780             new AddTransitionCommand(this, item->info(), item->transitionEndTrack(), item->toXML(), true, false, deleteTrack);
4781             m_scene->removeItem(item);
4782             delete item;
4783             item = NULL;
4784         }
4785     }
4786
4787     new AddTrackCommand(this, ix, trackinfo, false, deleteTrack);
4788     m_commandStack->push(deleteTrack);
4789 }
4790
4791 void CustomTrackView::changeTimelineTrack(int ix, TrackInfo trackinfo)
4792 {
4793     TrackInfo oldinfo = m_document->trackInfoAt(m_document->tracksCount() - ix - 1);
4794     ChangeTrackCommand *changeTrack = new ChangeTrackCommand(this, ix, oldinfo, trackinfo);
4795     m_commandStack->push(changeTrack);
4796 }
4797
4798 void CustomTrackView::autoTransition()
4799 {
4800     QList<QGraphicsItem *> itemList = scene()->selectedItems();
4801     if (itemList.count() != 1 || itemList.at(0)->type() != TRANSITIONWIDGET) {
4802         emit displayMessage(i18n("You must select one transition for this action"), ErrorMessage);
4803         return;
4804     }
4805     Transition *tr = static_cast <Transition*>(itemList.at(0));
4806     tr->setAutomatic(!tr->isAutomatic());
4807     QDomElement transition = tr->toXML();
4808     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);
4809     setDocumentModified();
4810 }
4811
4812
4813 QStringList CustomTrackView::getLadspaParams(QDomElement effect) const
4814 {
4815     QStringList result;
4816     QDomNodeList params = effect.elementsByTagName("parameter");
4817     for (int i = 0; i < params.count(); i++) {
4818         QDomElement e = params.item(i).toElement();
4819         if (!e.isNull() && e.attribute("type") == "constant") {
4820             if (e.hasAttribute("factor")) {
4821                 double factor = e.attribute("factor").toDouble();
4822                 double value = e.attribute("value").toDouble();
4823                 value = value / factor;
4824                 result.append(QString::number(value));
4825             } else result.append(e.attribute("value"));
4826         }
4827     }
4828     return result;
4829 }
4830
4831 void CustomTrackView::clipNameChanged(const QString id, const QString name)
4832 {
4833     QList<QGraphicsItem *> list = scene()->items();
4834     ClipItem *clip = NULL;
4835     for (int i = 0; i < list.size(); ++i) {
4836         if (list.at(i)->type() == AVWIDGET) {
4837             clip = static_cast <ClipItem *>(list.at(i));
4838             if (clip->clipProducer() == id) {
4839                 clip->setClipName(name);
4840             }
4841         }
4842     }
4843     viewport()->update();
4844 }
4845
4846 void CustomTrackView::getClipAvailableSpace(AbstractClipItem *item, GenTime &minimum, GenTime &maximum)
4847 {
4848     minimum = GenTime();
4849     maximum = GenTime();
4850     QList<QGraphicsItem *> selection;
4851     selection = m_scene->items(0, item->track() * m_tracksHeight + m_tracksHeight / 2, sceneRect().width(), 2);
4852     selection.removeAll(item);
4853     for (int i = 0; i < selection.count(); i++) {
4854         AbstractClipItem *clip = static_cast <AbstractClipItem *>(selection.at(i));
4855         if (clip && clip->type() == AVWIDGET) {
4856             if (clip->endPos() <= item->startPos() && clip->endPos() > minimum) minimum = clip->endPos();
4857             if (clip->startPos() > item->startPos() && (clip->startPos() < maximum || maximum == GenTime())) maximum = clip->startPos();
4858         }
4859     }
4860 }
4861
4862 void CustomTrackView::getTransitionAvailableSpace(AbstractClipItem *item, GenTime &minimum, GenTime &maximum)
4863 {
4864     minimum = GenTime();
4865     maximum = GenTime();
4866     QList<QGraphicsItem *> selection;
4867     selection = m_scene->items(0, (item->track() + 1) * m_tracksHeight, sceneRect().width(), 2);
4868     selection.removeAll(item);
4869     for (int i = 0; i < selection.count(); i++) {
4870         AbstractClipItem *clip = static_cast <AbstractClipItem *>(selection.at(i));
4871         if (clip && clip->type() == TRANSITIONWIDGET) {
4872             if (clip->endPos() <= item->startPos() && clip->endPos() > minimum) minimum = clip->endPos();
4873             if (clip->startPos() > item->startPos() && (clip->startPos() < maximum || maximum == GenTime())) maximum = clip->startPos();
4874         }
4875     }
4876 }
4877
4878
4879 void CustomTrackView::loadGroups(const QDomNodeList groups)
4880 {
4881     for (int i = 0; i < groups.count(); i++) {
4882         QDomNodeList children = groups.at(i).childNodes();
4883         scene()->clearSelection();
4884         for (int nodeindex = 0; nodeindex < children.count(); nodeindex++) {
4885             QDomNode n = children.item(nodeindex);
4886             QDomElement elem = n.toElement();
4887             int pos = elem.attribute("position").toInt();
4888             int track = elem.attribute("track").toInt();
4889             if (elem.tagName() == "clipitem") {
4890                 ClipItem *clip = getClipItemAt(pos, track); //m_document->tracksCount() - transitiontrack);
4891                 if (clip) clip->setSelected(true);
4892             } else {
4893                 Transition *clip = getTransitionItemAt(pos, track); //m_document->tracksCount() - transitiontrack);
4894                 if (clip) clip->setSelected(true);
4895             }
4896         }
4897         groupSelectedItems(false, true);
4898     }
4899 }
4900
4901 void CustomTrackView::splitAudio()
4902 {
4903     resetSelectionGroup();
4904     QList<QGraphicsItem *> selection = scene()->selectedItems();
4905     if (selection.isEmpty()) {
4906         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
4907         return;
4908     }
4909     QUndoCommand *splitCommand = new QUndoCommand();
4910     splitCommand->setText(i18n("Split audio"));
4911     for (int i = 0; i < selection.count(); i++) {
4912         if (selection.at(i)->type() == AVWIDGET) {
4913             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
4914             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
4915                 if (clip->parentItem()) {
4916                     emit displayMessage(i18n("Cannot split audio of grouped clips"), ErrorMessage);
4917                 } else {
4918                     new SplitAudioCommand(this, clip->track(), clip->startPos(), splitCommand);
4919                 }
4920             }
4921         }
4922     }
4923     m_commandStack->push(splitCommand);
4924 }
4925
4926 void CustomTrackView::doSplitAudio(const GenTime &pos, int track, bool split)
4927 {
4928     ClipItem *clip = getClipItemAt(pos, track);
4929     if (clip == NULL) {
4930         kDebug() << "// Cannot find clip to split!!!";
4931         return;
4932     }
4933     if (split) {
4934         int start = pos.frames(m_document->fps());
4935         int freetrack = m_document->tracksCount() - track - 1;
4936         for (; freetrack > 0; freetrack--) {
4937             kDebug() << "// CHK DOC TRK:" << freetrack << ", DUR:" << m_document->renderer()->mltTrackDuration(freetrack);
4938             if (m_document->trackInfoAt(freetrack - 1).type == AUDIOTRACK) {
4939                 kDebug() << "// CHK DOC TRK:" << freetrack << ", DUR:" << m_document->renderer()->mltTrackDuration(freetrack);
4940                 if (m_document->renderer()->mltTrackDuration(freetrack) < start || m_document->renderer()->mltGetSpaceLength(pos, freetrack, false) >= clip->cropDuration().frames(m_document->fps())) {
4941                     kDebug() << "FOUND SPACE ON TRK: " << freetrack;
4942                     break;
4943                 }
4944             }
4945         }
4946         kDebug() << "GOT TRK: " << track;
4947         if (freetrack == 0) {
4948             emit displayMessage(i18n("No empty space to put clip audio"), ErrorMessage);
4949         } else {
4950             ItemInfo info = clip->info();
4951             info.track = m_document->tracksCount() - freetrack;
4952             addClip(clip->xml(), clip->clipProducer(), info, clip->effectList());
4953             scene()->clearSelection();
4954             clip->setSelected(true);
4955             ClipItem *audioClip = getClipItemAt(start, info.track);
4956             if (audioClip) {
4957                 clip->setVideoOnly(true);
4958                 if (m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - track, start, clip->baseClip()->videoProducer()) == false) {
4959                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, track), ErrorMessage);
4960                 }
4961                 if (m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - info.track, start, clip->baseClip()->audioProducer(info.track)) == false) {
4962                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, info.track), ErrorMessage);
4963                 }
4964                 audioClip->setSelected(true);
4965                 audioClip->setAudioOnly(true);
4966                 groupSelectedItems(false, true);
4967             }
4968         }
4969     } else {
4970         // unsplit clip: remove audio part and change video part to normal clip
4971         if (clip->parentItem() == NULL || clip->parentItem()->type() != GROUPWIDGET) {
4972             kDebug() << "//CANNOT FIND CLP GRP";
4973             return;
4974         }
4975         AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(clip->parentItem());
4976         QList<QGraphicsItem *> children = grp->childItems();
4977         if (children.count() != 2) {
4978             kDebug() << "//SOMETHING IS WRONG WITH CLP GRP";
4979             return;
4980         }
4981         for (int i = 0; i < children.count(); i++) {
4982             if (children.at(i) != clip) {
4983                 ClipItem *clp = static_cast <ClipItem *>(children.at(i));
4984                 ItemInfo info = clip->info();
4985                 deleteClip(clp->info());
4986                 clip->setVideoOnly(false);
4987                 if (!m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - info.track, info.startPos.frames(m_document->fps()), clip->baseClip()->producer(info.track))) {
4988                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", info.startPos.frames(m_document->fps()), info.track), ErrorMessage);
4989                 }
4990                 break;
4991             }
4992         }
4993         clip->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
4994         m_document->clipManager()->removeGroup(grp);
4995         scene()->destroyItemGroup(grp);
4996     }
4997 }
4998
4999 void CustomTrackView::setVideoOnly()
5000 {
5001     resetSelectionGroup();
5002     QList<QGraphicsItem *> selection = scene()->selectedItems();
5003     if (selection.isEmpty()) {
5004         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
5005         return;
5006     }
5007     QUndoCommand *videoCommand = new QUndoCommand();
5008     videoCommand->setText(i18n("Video only"));
5009     for (int i = 0; i < selection.count(); i++) {
5010         if (selection.at(i)->type() == AVWIDGET) {
5011             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
5012             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
5013                 if (clip->parentItem()) {
5014                     emit displayMessage(i18n("Cannot change grouped clips"), ErrorMessage);
5015                 } else {
5016                     new ChangeClipTypeCommand(this, clip->track(), clip->startPos(), true, false, clip->isVideoOnly(), clip->isAudioOnly(), videoCommand);
5017                 }
5018             }
5019         }
5020     }
5021     m_commandStack->push(videoCommand);
5022 }
5023
5024 void CustomTrackView::setAudioOnly()
5025 {
5026     resetSelectionGroup();
5027     QList<QGraphicsItem *> selection = scene()->selectedItems();
5028     if (selection.isEmpty()) {
5029         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
5030         return;
5031     }
5032     QUndoCommand *videoCommand = new QUndoCommand();
5033     videoCommand->setText(i18n("Audio only"));
5034     for (int i = 0; i < selection.count(); i++) {
5035         if (selection.at(i)->type() == AVWIDGET) {
5036             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
5037             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
5038                 if (clip->parentItem()) {
5039                     emit displayMessage(i18n("Cannot change grouped clips"), ErrorMessage);
5040                 } else {
5041                     new ChangeClipTypeCommand(this, clip->track(), clip->startPos(), false, true, clip->isVideoOnly(), clip->isAudioOnly(), videoCommand);
5042                 }
5043             }
5044         }
5045     }
5046     m_commandStack->push(videoCommand);
5047 }
5048
5049 void CustomTrackView::setAudioAndVideo()
5050 {
5051     resetSelectionGroup();
5052     QList<QGraphicsItem *> selection = scene()->selectedItems();
5053     if (selection.isEmpty()) {
5054         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
5055         return;
5056     }
5057     QUndoCommand *videoCommand = new QUndoCommand();
5058     videoCommand->setText(i18n("Audio and Video"));
5059     for (int i = 0; i < selection.count(); i++) {
5060         if (selection.at(i)->type() == AVWIDGET) {
5061             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
5062             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
5063                 if (clip->parentItem()) {
5064                     emit displayMessage(i18n("Cannot change grouped clips"), ErrorMessage);
5065                 } else {
5066                     new ChangeClipTypeCommand(this, clip->track(), clip->startPos(), false, false, clip->isVideoOnly(), clip->isAudioOnly(), videoCommand);
5067                 }
5068             }
5069         }
5070     }
5071     m_commandStack->push(videoCommand);
5072 }
5073
5074 void CustomTrackView::doChangeClipType(const GenTime &pos, int track, bool videoOnly, bool audioOnly)
5075 {
5076     ClipItem *clip = getClipItemAt(pos, track);
5077     if (clip == NULL) {
5078         kDebug() << "// Cannot find clip to split!!!";
5079         return;
5080     }
5081     if (videoOnly) {
5082         int start = pos.frames(m_document->fps());
5083         clip->setVideoOnly(true);
5084         clip->setAudioOnly(false);
5085         if (m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - track, start, clip->baseClip()->videoProducer()) == false) {
5086             emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, track), ErrorMessage);
5087         }
5088     } else if (audioOnly) {
5089         int start = pos.frames(m_document->fps());
5090         clip->setAudioOnly(true);
5091         clip->setVideoOnly(false);
5092         if (m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - track, start, clip->baseClip()->audioProducer(track)) == false) {
5093             emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, track), ErrorMessage);
5094         }
5095     } else {
5096         int start = pos.frames(m_document->fps());
5097         clip->setAudioOnly(false);
5098         clip->setVideoOnly(false);
5099         if (m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - track, start, clip->baseClip()->producer(track)) == false) {
5100             emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", start, track), ErrorMessage);
5101         }
5102     }
5103     clip->update();
5104     setDocumentModified();
5105 }
5106
5107 void CustomTrackView::updateClipTypeActions(ClipItem *clip)
5108 {
5109     if (clip == NULL || (clip->clipType() != AV && clip->clipType() != PLAYLIST)) {
5110         m_clipTypeGroup->setEnabled(false);
5111     } else {
5112         m_clipTypeGroup->setEnabled(true);
5113         QList <QAction *> actions = m_clipTypeGroup->actions();
5114         QString lookup;
5115         if (clip->isAudioOnly()) lookup = "clip_audio_only";
5116         else if (clip->isVideoOnly()) lookup = "clip_video_only";
5117         else  lookup = "clip_audio_and_video";
5118         for (int i = 0; i < actions.count(); i++) {
5119             if (actions.at(i)->data().toString() == lookup) {
5120                 actions.at(i)->setChecked(true);
5121                 break;
5122             }
5123         }
5124     }
5125 }
5126
5127 void CustomTrackView::reloadTransitionLumas()
5128 {
5129     QString lumaNames;
5130     QString lumaFiles;
5131     QDomElement lumaTransition = MainWindow::transitions.getEffectByTag("luma", "luma");
5132     QDomNodeList params = lumaTransition.elementsByTagName("parameter");
5133     for (int i = 0; i < params.count(); i++) {
5134         QDomElement e = params.item(i).toElement();
5135         if (e.attribute("tag") == "resource") {
5136             lumaNames = e.attribute("paramlistdisplay");
5137             lumaFiles = e.attribute("paramlist");
5138             break;
5139         }
5140     }
5141
5142     QList<QGraphicsItem *> itemList = items();
5143     Transition *transitionitem;
5144     QDomElement transitionXml;
5145     for (int i = 0; i < itemList.count(); i++) {
5146         if (itemList.at(i)->type() == TRANSITIONWIDGET) {
5147             transitionitem = static_cast <Transition*>(itemList.at(i));
5148             transitionXml = transitionitem->toXML();
5149             if (transitionXml.attribute("id") == "luma" && transitionXml.attribute("tag") == "luma") {
5150                 QDomNodeList params = transitionXml.elementsByTagName("parameter");
5151                 for (int i = 0; i < params.count(); i++) {
5152                     QDomElement e = params.item(i).toElement();
5153                     if (e.attribute("tag") == "resource") {
5154                         e.setAttribute("paramlistdisplay", lumaNames);
5155                         e.setAttribute("paramlist", lumaFiles);
5156                         break;
5157                     }
5158                 }
5159             }
5160             if (transitionXml.attribute("id") == "composite" && transitionXml.attribute("tag") == "composite") {
5161                 QDomNodeList params = transitionXml.elementsByTagName("parameter");
5162                 for (int i = 0; i < params.count(); i++) {
5163                     QDomElement e = params.item(i).toElement();
5164                     if (e.attribute("tag") == "luma") {
5165                         e.setAttribute("paramlistdisplay", lumaNames);
5166                         e.setAttribute("paramlist", lumaFiles);
5167                         break;
5168                     }
5169                 }
5170             }
5171         }
5172     }
5173     emit transitionItemSelected(NULL);
5174 }
5175
5176 double CustomTrackView::fps() const
5177 {
5178     return m_document->fps();
5179 }
5180
5181 void CustomTrackView::updateProjectFps()
5182 {
5183     // update all clips to the new fps
5184     resetSelectionGroup();
5185     scene()->clearSelection();
5186     m_dragItem = NULL;
5187     QList<QGraphicsItem *> itemList = items();
5188     for (int i = 0; i < itemList.count(); i++) {
5189         // remove all items and re-add them one by one
5190         if (itemList.at(i) != m_cursorLine && itemList.at(i)->parentItem() == NULL) m_scene->removeItem(itemList.at(i));
5191     }
5192     for (int i = 0; i < itemList.count(); i++) {
5193         if (itemList.at(i)->parentItem() == 0 && (itemList.at(i)->type() == AVWIDGET || itemList.at(i)->type() == TRANSITIONWIDGET)) {
5194             AbstractClipItem *clip = static_cast <AbstractClipItem *>(itemList.at(i));
5195             clip->updateFps(m_document->fps());
5196             m_scene->addItem(clip);
5197         } else if (itemList.at(i)->type() == GROUPWIDGET) {
5198             AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(itemList.at(i));
5199             QList<QGraphicsItem *> children = grp->childItems();
5200             for (int j = 0; j < children.count(); j++) {
5201                 if (children.at(j)->type() == AVWIDGET || children.at(j)->type() == TRANSITIONWIDGET) {
5202                     AbstractClipItem *clip = static_cast <AbstractClipItem *>(children.at(j));
5203                     clip->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
5204                     clip->updateFps(m_document->fps());
5205                 }
5206             }
5207             m_document->clipManager()->removeGroup(grp);
5208             m_scene->addItem(grp);
5209             scene()->destroyItemGroup(grp);
5210             for (int j = 0; j < children.count(); j++) {
5211                 if (children.at(j)->type() == AVWIDGET || children.at(j)->type() == TRANSITIONWIDGET) {
5212                     //children.at(j)->setParentItem(0);
5213                     children.at(j)->setSelected(true);
5214                 }
5215             }
5216             groupSelectedItems(true, true);
5217         } else if (itemList.at(i)->type() == GUIDEITEM) {
5218             Guide *g = static_cast<Guide *>(itemList.at(i));
5219             g->updatePos();
5220             m_scene->addItem(g);
5221         }
5222     }
5223     viewport()->update();
5224 }
5225
5226 void CustomTrackView::slotTrackDown()
5227 {
5228     if (m_selectedTrack > m_document->tracksCount() - 2) m_selectedTrack = 0;
5229     else m_selectedTrack++;
5230     emit updateTrackHeaders();
5231     QRectF rect(mapToScene(QPoint()).x(), m_selectedTrack * m_tracksHeight, 10, m_tracksHeight);
5232     ensureVisible(rect);
5233     viewport()->update();
5234 }
5235
5236 void CustomTrackView::slotTrackUp()
5237 {
5238     if (m_selectedTrack > 0) m_selectedTrack--;
5239     else m_selectedTrack = m_document->tracksCount() - 1;
5240     emit updateTrackHeaders();
5241     QRectF rect(mapToScene(QPoint()).x(), m_selectedTrack * m_tracksHeight, 10, m_tracksHeight);
5242     ensureVisible(rect);
5243     viewport()->update();
5244 }
5245
5246 int CustomTrackView::selectedTrack() const
5247 {
5248     return m_selectedTrack;
5249 }
5250
5251 void CustomTrackView::slotSelectTrack(int ix)
5252 {
5253     m_selectedTrack = qMax(0, ix);
5254     m_selectedTrack = qMin(ix, m_document->tracksCount() - 1);
5255     emit updateTrackHeaders();
5256     QRectF rect(mapToScene(QPoint()).x(), m_selectedTrack * m_tracksHeight, 10, m_tracksHeight);
5257     ensureVisible(rect);
5258     viewport()->update();
5259 }
5260
5261 void CustomTrackView::selectClip(bool add, bool group)
5262 {
5263     QRectF rect(m_cursorPos, m_selectedTrack * m_tracksHeight + m_tracksHeight / 2, 1, 1);
5264     QList<QGraphicsItem *> selection = m_scene->items(rect);
5265     resetSelectionGroup(group);
5266     if (!group) m_scene->clearSelection();
5267     for (int i = 0; i < selection.count(); i++) {
5268         if (selection.at(i)->type() == AVWIDGET) {
5269             selection.at(i)->setSelected(add);
5270             break;
5271         }
5272     }
5273     if (group) groupSelectedItems();
5274 }
5275
5276 void CustomTrackView::selectTransition(bool add, bool group)
5277 {
5278     QRectF rect(m_cursorPos, m_selectedTrack * m_tracksHeight + m_tracksHeight, 1, 1);
5279     QList<QGraphicsItem *> selection = m_scene->items(rect);
5280     resetSelectionGroup(group);
5281     if (!group) m_scene->clearSelection();
5282     for (int i = 0; i < selection.count(); i++) {
5283         if (selection.at(i)->type() == TRANSITIONWIDGET) {
5284             selection.at(i)->setSelected(add);
5285             break;
5286         }
5287     }
5288     if (group) groupSelectedItems();
5289 }