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