]> git.sesse.net Git - kdenlive/blob - src/customtrackview.cpp
Rewrote correct implementation of vertical timeline zoom
[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();
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(), mapToScene(event->pos()).x() / m_scale - (clip->startPos() - clip->cropStart()).frames(m_document->fps()));
403             }
404             event->accept();
405             return;
406         }
407         opMode = clip->operationMode(mapToScene(event->pos()));
408         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, rect.height() / 2 - size * 2);
430                 polygon << QPoint(size * 2, (int)(rect.height() / 2));
431                 polygon << QPoint(0, (int)(rect.height() / 2 + size * 2));
432                 polygon << QPoint(0, (int)(rect.height() / 2 - 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());
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                 double scale = 2.0;
444                 m_animation->setScaleAt(.5, scale, 1);
445                 //m_animation->setPosAt(.5, QPointF(rect.x() - rect.x() * scale, 0));
446                 scale = 1.0;
447                 m_animation->setScaleAt(1, scale, 1);
448                 //m_animation->setPosAt(1, QPointF(rect.x() - rect.x() * scale, 0));
449                 scene()->addItem(m_visualTip);
450                 m_animationTimer->start();
451             }
452         } else if (opMode == RESIZEEND) {
453             setCursor(KCursor("right_side", Qt::SizeHorCursor));
454             if (m_visualTip == NULL) {
455                 QRectF rect = clip->sceneBoundingRect();
456                 QPolygon polygon;
457                 polygon << QPoint(0, (int)(rect.height() / 2 - size * 2));
458                 polygon << QPoint(- size * 2, (int)(rect.height() / 2));
459                 polygon << QPoint(0, (int)(rect.height() / 2 + size * 2));
460                 polygon << QPoint(0, (int)(rect.height() / 2 - size * 2));
461
462                 m_visualTip = new QGraphicsPolygonItem(polygon);
463                 ((QGraphicsPolygonItem*) m_visualTip)->setBrush(m_tipColor);
464                 ((QGraphicsPolygonItem*) m_visualTip)->setPen(m_tipPen);
465                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
466                 m_visualTip->setPos(rect.right(), rect.y());
467                 m_visualTip->setZValue(100);
468                 m_animation = new QGraphicsItemAnimation;
469                 m_animation->setItem(m_visualTip);
470                 m_animation->setTimeLine(m_animationTimer);
471                 double scale = 2.0;
472                 m_animation->setScaleAt(.5, scale, 1);
473                 scale = 1.0;
474                 m_animation->setScaleAt(1, scale, 1);
475                 scene()->addItem(m_visualTip);
476                 m_animationTimer->start();
477             }
478         } else if (opMode == FADEIN) {
479             if (m_visualTip == NULL) {
480                 ClipItem *item = (ClipItem *) clip;
481                 QRectF rect = clip->sceneBoundingRect();
482                 m_visualTip = new QGraphicsEllipseItem(-size, -size, size * 2, size * 2);
483                 ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
484                 ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
485                 m_visualTip->setPos(rect.x() + item->fadeIn(), rect.y());
486                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
487                 m_visualTip->setZValue(100);
488                 m_animation = new QGraphicsItemAnimation;
489                 m_animation->setItem(m_visualTip);
490                 m_animation->setTimeLine(m_animationTimer);
491                 double scale = 2.0;
492                 m_animation->setScaleAt(.5, scale, scale);
493                 scale = 1.0;
494                 m_animation->setScaleAt(1, scale, scale);
495                 scene()->addItem(m_visualTip);
496                 m_animationTimer->start();
497             }
498             setCursor(Qt::PointingHandCursor);
499         } else if (opMode == FADEOUT) {
500             if (m_visualTip == NULL) {
501                 ClipItem *item = (ClipItem *) clip;
502                 QRectF rect = clip->sceneBoundingRect();
503                 m_visualTip = new QGraphicsEllipseItem(-size, -size, size * 2, size * 2);
504                 ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
505                 ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
506                 m_visualTip->setPos(rect.right() - item->fadeOut(), rect.y());
507                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
508                 m_visualTip->setZValue(100);
509                 m_animation = new QGraphicsItemAnimation;
510                 m_animation->setItem(m_visualTip);
511                 m_animation->setTimeLine(m_animationTimer);
512                 double scale = 2.0;
513                 m_animation->setScaleAt(.5, scale, scale);
514                 scale = 1.0;
515                 m_animation->setScaleAt(1, scale, scale);
516                 scene()->addItem(m_visualTip);
517                 m_animationTimer->start();
518             }
519             setCursor(Qt::PointingHandCursor);
520         } else if (opMode == TRANSITIONSTART) {
521             /*if (m_visualTip == NULL) {
522                 QRectF rect = clip->sceneBoundingRect();
523                 m_visualTip = new QGraphicsEllipseItem(-5, -5 , 10, 10);
524                 ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
525                 ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
526                 m_visualTip->setZValue(100);
527                 m_animation = new QGraphicsItemAnimation;
528                 m_animation->setItem(m_visualTip);
529                 m_animation->setTimeLine(m_animationTimer);
530                 m_visualTip->setPos(rect.x() + 10, rect.y() + rect.height() / 2 + 12);
531                 double scale = 2.0;
532                 m_animation->setScaleAt(.5, scale, scale);
533                 scale = 1.0;
534                 m_animation->setScaleAt(1, scale, scale);
535                 scene()->addItem(m_visualTip);
536                 m_animationTimer->start();
537             }*/
538             setCursor(Qt::PointingHandCursor);
539         } else if (opMode == TRANSITIONEND) {
540             /*if (m_visualTip == NULL) {
541                 QRectF rect = clip->sceneBoundingRect();
542                 m_visualTip = new QGraphicsEllipseItem(-5, -5 , 10, 10);
543                 ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
544                 ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
545                 m_visualTip->setZValue(100);
546                 m_animation = new QGraphicsItemAnimation;
547                 m_animation->setItem(m_visualTip);
548                 m_animation->setTimeLine(m_animationTimer);
549                 m_visualTip->setPos(rect.x() + rect.width() - 10 , rect.y() + rect.height() / 2 + 12);
550                 double scale = 2.0;
551                 m_animation->setScaleAt(.5, scale, scale);
552                 scale = 1.0;
553                 m_animation->setScaleAt(1, scale, scale);
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         m_document->setModified(true);
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             }
1021
1022         } else  {
1023             // add keyframe
1024             GenTime keyFramePos = GenTime((int)(mapToScene(event->pos()).x()), m_document->fps()) - m_dragItem->startPos() + m_dragItem->cropStart();
1025             m_dragItem->addKeyFrame(keyFramePos, mapToScene(event->pos()).toPoint().y());
1026             ClipItem * item = (ClipItem *) m_dragItem;
1027             QString previous = item->keyframes(item->selectedEffectIndex());
1028             item->updateKeyframeEffect();
1029             QString next = item->keyframes(item->selectedEffectIndex());
1030             EditKeyFrameCommand *command = new EditKeyFrameCommand(this, m_dragItem->track(), m_dragItem->startPos(), item->selectedEffectIndex(), previous, next, false);
1031             m_commandStack->push(command);
1032             updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
1033         }
1034     } else if (m_dragItem && !m_dragItem->isItemLocked()) {
1035         ClipDurationDialog d(m_dragItem, m_document->timecode(), this);
1036         GenTime minimum;
1037         GenTime maximum;
1038         if (m_dragItem->type() == TRANSITIONWIDGET) {
1039             getTransitionAvailableSpace(m_dragItem, minimum, maximum);
1040         } else {
1041             getClipAvailableSpace(m_dragItem, minimum, maximum);
1042         }
1043         //kDebug()<<"// GOT MOVE POS: "<<minimum.frames(25)<<" - "<<maximum.frames(25);
1044         d.setMargins(minimum, maximum);
1045         if (d.exec() == QDialog::Accepted) {
1046             if (m_dragItem->type() == TRANSITIONWIDGET) {
1047                 // move & resize transition
1048                 ItemInfo startInfo;
1049                 startInfo.startPos = m_dragItem->startPos();
1050                 startInfo.endPos = m_dragItem->endPos();
1051                 startInfo.track = m_dragItem->track();
1052                 ItemInfo endInfo;
1053                 endInfo.startPos = d.startPos();
1054                 endInfo.endPos = endInfo.startPos + d.duration();
1055                 endInfo.track = m_dragItem->track();
1056                 MoveTransitionCommand *command = new MoveTransitionCommand(this, startInfo, endInfo, true);
1057                 m_commandStack->push(command);
1058             } else {
1059                 // move and resize clip
1060                 QUndoCommand *moveCommand = new QUndoCommand();
1061                 moveCommand->setText(i18n("Edit clip"));
1062                 ItemInfo clipInfo = m_dragItem->info();
1063                 if (d.duration() < m_dragItem->duration() || d.cropStart() != clipInfo.cropStart) {
1064                     // duration was reduced, so process it first
1065                     ItemInfo startInfo = clipInfo;
1066                     clipInfo.endPos = clipInfo.startPos + d.duration();
1067                     clipInfo.cropStart = d.cropStart();
1068                     new ResizeClipCommand(this, startInfo, clipInfo, true, moveCommand);
1069                 }
1070                 if (d.startPos() != clipInfo.startPos) {
1071                     ItemInfo startInfo = clipInfo;
1072                     clipInfo.startPos = d.startPos();
1073                     clipInfo.endPos = m_dragItem->endPos() + (clipInfo.startPos - startInfo.startPos);
1074                     new MoveClipCommand(this, startInfo, clipInfo, true, moveCommand);
1075                 }
1076                 if (d.duration() > m_dragItem->duration()) {
1077                     // duration was increased, so process it after move
1078                     ItemInfo startInfo = clipInfo;
1079                     clipInfo.endPos = clipInfo.startPos + d.duration();
1080                     clipInfo.cropStart = d.cropStart();
1081                     new ResizeClipCommand(this, startInfo, clipInfo, true, moveCommand);
1082                 }
1083                 m_commandStack->push(moveCommand);
1084             }
1085         }
1086     } else {
1087         QList<QGraphicsItem *> collisionList = items(event->pos());
1088         if (collisionList.count() == 1 && collisionList.at(0)->type() == GUIDEITEM) {
1089             Guide *editGuide = (Guide *) collisionList.at(0);
1090             if (editGuide) slotEditGuide(editGuide->info());
1091         }
1092     }
1093 }
1094
1095
1096 void CustomTrackView::editKeyFrame(const GenTime pos, const int track, const int index, const QString keyframes)
1097 {
1098     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), track);
1099     if (clip) {
1100         clip->setKeyframes(index, keyframes);
1101         updateEffect(m_document->tracksCount() - clip->track(), clip->startPos(), clip->effectAt(index), index);
1102     } else emit displayMessage(i18n("Cannot find clip with keyframe"), ErrorMessage);
1103 }
1104
1105
1106 void CustomTrackView::displayContextMenu(QPoint pos, AbstractClipItem *clip, AbstractGroupItem *group)
1107 {
1108     m_deleteGuide->setEnabled(m_dragGuide != NULL);
1109     m_editGuide->setEnabled(m_dragGuide != NULL);
1110     if (clip == NULL) m_timelineContextMenu->popup(pos);
1111     else if (group != NULL) {
1112         m_changeSpeedAction->setEnabled(false);
1113         m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
1114         m_ungroupAction->setEnabled(true);
1115         updateClipTypeActions(NULL);
1116         m_timelineContextClipMenu->popup(pos);
1117     } else {
1118         m_ungroupAction->setEnabled(false);
1119         if (clip->type() == AVWIDGET) {
1120             ClipItem *item = static_cast <ClipItem*>(clip);
1121             updateClipTypeActions(item);
1122             m_changeSpeedAction->setEnabled(item->clipType() == AV || item->clipType() == VIDEO);
1123             m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
1124             m_timelineContextClipMenu->popup(pos);
1125         } else if (clip->type() == TRANSITIONWIDGET) m_timelineContextTransitionMenu->popup(pos);
1126     }
1127 }
1128
1129 void CustomTrackView::activateMonitor()
1130 {
1131     emit activateDocumentMonitor();
1132 }
1133
1134 void CustomTrackView::dragEnterEvent(QDragEnterEvent * event)
1135 {
1136     if (event->mimeData()->hasFormat("kdenlive/clip")) {
1137         m_clipDrag = true;
1138         resetSelectionGroup();
1139         QStringList list = QString(event->mimeData()->data("kdenlive/clip")).split(';');
1140         m_selectionGroup = new AbstractGroupItem(m_document->fps());
1141         QPoint pos;
1142         DocClipBase *clip = m_document->getBaseClip(list.at(0));
1143         if (clip == NULL) kDebug() << " WARNING))))))))) CLIP NOT FOUND : " << list.at(0);
1144         ItemInfo info;
1145         info.startPos = GenTime();
1146         info.cropStart = GenTime(list.at(1).toInt(), m_document->fps());
1147         info.endPos = GenTime(list.at(2).toInt() - list.at(1).toInt(), m_document->fps());
1148         info.track = (int)(1 / m_tracksHeight);
1149         ClipItem *item = new ClipItem(clip, info, m_document->fps(), 1.0);
1150         m_selectionGroup->addToGroup(item);
1151         item->setFlags(QGraphicsItem::ItemIsSelectable);
1152         //TODO: check if we do not overlap another clip when first dropping in timeline
1153         // if (insertPossible(m_selectionGroup, event->pos()))
1154         QList <GenTime> offsetList;
1155         offsetList.append(info.endPos);
1156         updateSnapPoints(NULL, offsetList);
1157         scene()->addItem(m_selectionGroup);
1158         event->acceptProposedAction();
1159     } else if (event->mimeData()->hasFormat("kdenlive/producerslist")) {
1160         m_clipDrag = true;
1161         QStringList ids = QString(event->mimeData()->data("kdenlive/producerslist")).split(';');
1162         m_scene->clearSelection();
1163         resetSelectionGroup(false);
1164
1165         m_selectionGroup = new AbstractGroupItem(m_document->fps());
1166         QPoint pos;
1167         GenTime start;
1168         QList <GenTime> offsetList;
1169         for (int i = 0; i < ids.size(); ++i) {
1170             DocClipBase *clip = m_document->getBaseClip(ids.at(i));
1171             if (clip == NULL) kDebug() << " WARNING))))))))) CLIP NOT FOUND : " << ids.at(i);
1172             ItemInfo info;
1173             info.startPos = start;
1174             info.endPos = info.startPos + clip->duration();
1175             info.track = (int)(1 / m_tracksHeight);
1176             ClipItem *item = new ClipItem(clip, info, m_document->fps(), 1.0, false);
1177             start += clip->duration();
1178             offsetList.append(start);
1179             m_selectionGroup->addToGroup(item);
1180             item->setFlags(QGraphicsItem::ItemIsSelectable);
1181             m_waitingThumbs.append(item);
1182         }
1183         //TODO: check if we do not overlap another clip when first dropping in timeline
1184         //if (insertPossible(m_selectionGroup, event->pos()))
1185         updateSnapPoints(NULL, offsetList);
1186         scene()->addItem(m_selectionGroup);
1187         m_thumbsTimer.start();
1188         event->acceptProposedAction();
1189
1190     } else {
1191         // the drag is not a clip (may be effect, ...)
1192         m_clipDrag = false;
1193         QGraphicsView::dragEnterEvent(event);
1194     }
1195 }
1196
1197
1198 bool CustomTrackView::insertPossible(AbstractGroupItem *group, const QPoint &pos) const
1199 {
1200     QPolygonF path;
1201     QList<QGraphicsItem *> children = group->childItems();
1202     for (int i = 0; i < children.count(); i++) {
1203         if (children.at(i)->type() == AVWIDGET) {
1204             ClipItem *clip = static_cast <ClipItem *>(children.at(i));
1205             ItemInfo info = clip->info();
1206             kDebug() << " / / INSERT : " << pos.x();
1207             QRectF shape = QRectF(clip->startPos().frames(m_document->fps()), clip->track() * m_tracksHeight + 1, clip->duration().frames(m_document->fps()) - 0.02, m_tracksHeight - 1);
1208             kDebug() << " / / INSERT RECT: " << shape;
1209             path = path.united(QPolygonF(shape));
1210         }
1211     }
1212
1213     QList<QGraphicsItem*> collindingItems = scene()->items(path, Qt::IntersectsItemShape);
1214     if (collindingItems.isEmpty()) return true;
1215     else {
1216         for (int i = 0; i < collindingItems.count(); i++) {
1217             QGraphicsItem *collision = collindingItems.at(i);
1218             if (collision->type() == AVWIDGET) {
1219                 // Collision
1220                 kDebug() << "// COLLISIION DETECTED";
1221                 return false;
1222             }
1223         }
1224         return true;
1225     }
1226
1227 }
1228
1229 void CustomTrackView::slotRefreshEffects(ClipItem *clip)
1230 {
1231     int track = m_document->tracksCount() - clip->track();
1232     GenTime pos = clip->startPos();
1233     if (!m_document->renderer()->mltRemoveEffect(track, pos, "-1", false, false)) {
1234         emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
1235         return;
1236     }
1237     bool success = true;
1238     for (int i = 0; i < clip->effectsCount(); i++) {
1239         if (!m_document->renderer()->mltAddEffect(track, pos, clip->getEffectArgs(clip->effectAt(i)), false)) success = false;
1240     }
1241     if (!success) emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
1242     m_document->renderer()->doRefresh();
1243 }
1244
1245 void CustomTrackView::addEffect(int track, GenTime pos, QDomElement effect)
1246 {
1247     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()) + 1, m_document->tracksCount() - track);
1248     if (clip) {
1249         if (!m_document->renderer()->mltAddEffect(track, pos, clip->addEffect(effect)))
1250             emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
1251         emit clipItemSelected(clip);
1252     } else emit displayMessage(i18n("Cannot find clip to add effect"), ErrorMessage);
1253 }
1254
1255 void CustomTrackView::deleteEffect(int track, GenTime pos, QDomElement effect)
1256 {
1257     QString index = effect.attribute("kdenlive_ix");
1258     if (!m_document->renderer()->mltRemoveEffect(track, pos, index, true) && effect.attribute("disabled") != "1") {
1259         kDebug() << "// ERROR REMOV EFFECT: " << index << ", DISABLE: " << effect.attribute("disabled");
1260         emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
1261         return;
1262     }
1263     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()) + 1, m_document->tracksCount() - track);
1264     if (clip) {
1265         clip->deleteEffect(index);
1266         emit clipItemSelected(clip);
1267     }
1268 }
1269
1270 void CustomTrackView::slotAddGroupEffect(QDomElement effect, AbstractGroupItem *group)
1271 {
1272     QList<QGraphicsItem *> itemList = group->childItems();
1273     QUndoCommand *effectCommand = new QUndoCommand();
1274     QString effectName;
1275     QDomNode namenode = effect.elementsByTagName("name").item(0);
1276     if (!namenode.isNull()) effectName = i18n(namenode.toElement().text().toUtf8().data());
1277     else effectName = i18n("effect");
1278     effectCommand->setText(i18n("Add %1", effectName));
1279     int count = 0;
1280     for (int i = 0; i < itemList.count(); i++) {
1281         if (itemList.at(i)->type() == AVWIDGET) {
1282             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
1283             if (effect.attribute("type") == "audio") {
1284                 // Don't add audio effects on video clips
1285                 if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) continue;
1286             } else if (effect.hasAttribute("type") == false) {
1287                 // Don't add video effect on audio clips
1288                 if (item->isAudioOnly() || item->clipType() == AUDIO) continue;
1289             }
1290
1291             if (item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1 && effect.attribute("unique", "0") != "0") {
1292                 emit displayMessage(i18n("Effect already present in clip"), ErrorMessage);
1293                 continue;
1294             }
1295             if (item->isItemLocked()) {
1296                 continue;
1297             }
1298             item->initEffect(effect);
1299             if (effect.attribute("tag") == "ladspa") {
1300                 QString ladpsaFile = m_document->getLadspaFile();
1301                 initEffects::ladspaEffectFile(ladpsaFile, effect.attribute("ladspaid").toInt(), getLadspaParams(effect));
1302                 effect.setAttribute("src", ladpsaFile);
1303             }
1304             new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand);
1305             count++;
1306         }
1307     }
1308     if (count > 0) {
1309         m_commandStack->push(effectCommand);
1310         m_document->setModified(true);
1311     } else delete effectCommand;
1312 }
1313
1314 void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track)
1315 {
1316     QList<QGraphicsItem *> itemList;
1317     QUndoCommand *effectCommand = new QUndoCommand();
1318     QString effectName;
1319     QDomNode namenode = effect.elementsByTagName("name").item(0);
1320     if (!namenode.isNull()) effectName = i18n(namenode.toElement().text().toUtf8().data());
1321     else effectName = i18n("effect");
1322     effectCommand->setText(i18n("Add %1", effectName));
1323     int count = 0;
1324     if (track == -1) itemList = scene()->selectedItems();
1325     if (itemList.isEmpty()) {
1326         ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()) + 1, track);
1327         if (clip) itemList.append(clip);
1328         else emit displayMessage(i18n("Select a clip if you want to apply an effect"), ErrorMessage);
1329     }
1330     kDebug() << "// REQUESTING EFFECT ON CLIP: " << pos.frames(25) << ", TRK: " << track << "SELECTED ITEMS: " << itemList.count();
1331     for (int i = 0; i < itemList.count(); i++) {
1332         if (itemList.at(i)->type() == AVWIDGET) {
1333             ClipItem *item = (ClipItem *)itemList.at(i);
1334             if (effect.attribute("type") == "audio") {
1335                 // Don't add audio effects on video clips
1336                 if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) {
1337                     emit displayMessage(i18n("Cannot add an audio effect to this clip"), ErrorMessage);
1338                     continue;
1339                 }
1340             } else if (effect.hasAttribute("type") == false) {
1341                 // Don't add video effect on audio clips
1342                 if (item->isAudioOnly() || item->clipType() == AUDIO) {
1343                     emit displayMessage(i18n("Cannot add a video effect to this clip"), ErrorMessage);
1344                     continue;
1345                 }
1346             }
1347             if (item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1 && effect.attribute("unique", "0") != "0") {
1348                 emit displayMessage(i18n("Effect already present in clip"), ErrorMessage);
1349                 continue;
1350             }
1351             if (item->isItemLocked()) {
1352                 continue;
1353             }
1354             item->initEffect(effect);
1355             if (effect.attribute("tag") == "ladspa") {
1356                 QString ladpsaFile = m_document->getLadspaFile();
1357                 initEffects::ladspaEffectFile(ladpsaFile, effect.attribute("ladspaid").toInt(), getLadspaParams(effect));
1358                 effect.setAttribute("src", ladpsaFile);
1359             }
1360             new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand);
1361             count++;
1362         }
1363     }
1364     if (count > 0) {
1365         m_commandStack->push(effectCommand);
1366         m_document->setModified(true);
1367     } else delete effectCommand;
1368 }
1369
1370 void CustomTrackView::slotDeleteEffect(ClipItem *clip, QDomElement effect)
1371 {
1372     AddEffectCommand *command = new AddEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effect, false);
1373     m_commandStack->push(command);
1374     m_document->setModified(true);
1375 }
1376
1377 void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement effect, int ix, bool triggeredByUser)
1378 {
1379     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()) + 1, m_document->tracksCount() - track);
1380     if (clip) {
1381         EffectsParameterList effectParams = clip->getEffectArgs(effect);
1382         if (effect.attribute("tag") == "ladspa") {
1383             // Update the ladspa affect file
1384             initEffects::ladspaEffectFile(effect.attribute("src"), effect.attribute("ladspaid").toInt(), getLadspaParams(effect));
1385         }
1386         // check if we are trying to reset a keyframe effect
1387         if (effectParams.hasParam("keyframes") && effectParams.paramValue("keyframes").isEmpty()) {
1388             clip->initEffect(effect);
1389             clip->setEffectAt(ix, effect);
1390             effectParams = clip->getEffectArgs(effect);
1391         }
1392         if (effectParams.paramValue("disabled") == "1") {
1393             if (m_document->renderer()->mltRemoveEffect(track, pos, effectParams.paramValue("kdenlive_ix"), false)) {
1394                 kDebug() << "//////  DISABLING EFFECT: " << ix << ", CURRENTLA: " << clip->selectedEffectIndex();
1395             } else emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
1396         } else if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - clip->track(), clip->startPos(), effectParams))
1397             emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
1398
1399         clip->setEffectAt(ix, effect);
1400         if (ix == clip->selectedEffectIndex()) {
1401             clip->setSelectedEffect(ix);
1402             if (!triggeredByUser) emit clipItemSelected(clip, ix);
1403         }
1404         if (effect.attribute("tag") == "volume" || effect.attribute("tag") == "brightness") {
1405             // A fade effect was modified, update the clip
1406             if (effect.attribute("id") == "fadein" || effect.attribute("id") == "fade_from_black") {
1407                 int pos = effectParams.paramValue("out").toInt() - effectParams.paramValue("in").toInt();
1408                 clip->setFadeIn(pos);
1409             }
1410             if (effect.attribute("id") == "fadeout" || effect.attribute("id") == "fade_to_black") {
1411                 int pos = effectParams.paramValue("out").toInt() - effectParams.paramValue("in").toInt();
1412                 clip->setFadeOut(pos);
1413             }
1414         }
1415     }
1416     m_document->setModified(true);
1417 }
1418
1419 void CustomTrackView::moveEffect(int track, GenTime pos, int oldPos, int newPos)
1420 {
1421     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()) + 1, m_document->tracksCount() - track);
1422     if (clip) {
1423         m_document->renderer()->mltMoveEffect(track, pos, oldPos, newPos);
1424         QDomElement act = clip->effectAt(newPos - 1).cloneNode().toElement();
1425         QDomElement before = clip->effectAt(oldPos - 1).cloneNode().toElement();
1426         clip->setEffectAt(oldPos - 1, act);
1427         clip->setEffectAt(newPos - 1, before);
1428         emit clipItemSelected(clip, newPos - 1);
1429     }
1430     m_document->setModified(true);
1431 }
1432
1433 void CustomTrackView::slotChangeEffectState(ClipItem *clip, int effectPos, bool disable)
1434 {
1435     QDomElement effect = clip->effectAt(effectPos);
1436     QDomElement oldEffect = effect.cloneNode().toElement();
1437     effect.setAttribute("disabled", disable);
1438     EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldEffect, effect, effectPos, true);
1439     m_commandStack->push(command);
1440     m_document->setModified(true);
1441 }
1442
1443 void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int currentPos, int newPos)
1444 {
1445     MoveEffectCommand *command = new MoveEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), currentPos, newPos);
1446     m_commandStack->push(command);
1447     m_document->setModified(true);
1448 }
1449
1450 void CustomTrackView::slotUpdateClipEffect(ClipItem *clip, QDomElement oldeffect, QDomElement effect, int ix)
1451 {
1452     EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, true);
1453     m_commandStack->push(command);
1454 }
1455
1456 void CustomTrackView::cutClip(ItemInfo info, GenTime cutTime, bool cut)
1457 {
1458     if (cut) {
1459         // cut clip
1460         ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()) + 1, info.track);
1461         if (!item || cutTime >= item->endPos() || cutTime <= item->startPos()) {
1462             emit displayMessage(i18n("Cannot find clip to cut"), ErrorMessage);
1463             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);
1464             m_blockRefresh = false;
1465             return;
1466         }
1467         if (item->parentItem()) {
1468             // Item is part of a group, reset group
1469             resetSelectionGroup();
1470         }
1471         kDebug() << "/////////  CUTTING CLIP : (" << item->startPos().frames(25) << "-" << item->endPos().frames(25) << "), INFO: (" << info.startPos.frames(25) << "-" << info.endPos.frames(25) << ")" << ", CUT: " << cutTime.frames(25);
1472
1473         m_document->renderer()->mltCutClip(m_document->tracksCount() - info.track, cutTime);
1474         int cutPos = (int) cutTime.frames(m_document->fps());
1475         ItemInfo newPos;
1476         double speed = item->speed();
1477         newPos.startPos = cutTime;
1478         newPos.endPos = info.endPos;
1479         if (speed == 1) newPos.cropStart = item->info().cropStart + (cutTime - info.startPos);
1480         else newPos.cropStart = item->info().cropStart + (cutTime - info.startPos) * speed;
1481         newPos.track = info.track;
1482         ClipItem *dup = item->clone(newPos);
1483         // remove unwanted effects (fade in) from 2nd part of cutted clip
1484         int ix = dup->hasEffect(QString(), "fadein");
1485         if (ix != -1) {
1486             QDomElement oldeffect = item->effectAt(ix);
1487             dup->deleteEffect(oldeffect.attribute("kdenlive_ix"));
1488         }
1489         ix = dup->hasEffect(QString(), "fade_from_black");
1490         if (ix != -1) {
1491             QDomElement oldeffect = item->effectAt(ix);
1492             dup->deleteEffect(oldeffect.attribute("kdenlive_ix"));
1493         }
1494         item->resizeEnd(cutPos, false);
1495         scene()->addItem(dup);
1496         if (item->checkKeyFrames()) slotRefreshEffects(item);
1497         if (dup->checkKeyFrames()) slotRefreshEffects(dup);
1498         item->baseClip()->addReference();
1499         m_document->updateClip(item->baseClip()->getId());
1500         m_document->setModified(true);
1501         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);
1502         kDebug() << "//  CUTTING CLIP dONE";
1503     } else {
1504         // uncut clip
1505
1506         ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()), info.track);
1507         ClipItem *dup = getClipItemAt((int) cutTime.frames(m_document->fps()) + 1, info.track);
1508         if (!item || !dup || item == dup) {
1509             emit displayMessage(i18n("Cannot find clip to uncut"), ErrorMessage);
1510             m_blockRefresh = false;
1511             return;
1512         }
1513         if (m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, cutTime) == false) {
1514             emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(cutTime.frames(m_document->fps())), info.track), ErrorMessage);
1515             return;
1516         }
1517
1518         kDebug() << "// UNCUTTING CLIPS: ITEM 1 (" << item->startPos().frames(25) << "x" << item->endPos().frames(25) << ")";
1519         kDebug() << "// UNCUTTING CLIPS: ITEM 2 (" << dup->startPos().frames(25) << "x" << dup->endPos().frames(25) << ")";
1520         kDebug() << "// UNCUTTING CLIPS, INFO (" << info.startPos.frames(25) << "x" << info.endPos.frames(25) << ") , CUT: " << cutTime.frames(25);;
1521         //deleteClip(dup->info());
1522
1523
1524         if (dup->isSelected()) emit clipItemSelected(NULL);
1525         dup->baseClip()->removeReference();
1526         m_document->updateClip(dup->baseClip()->getId());
1527         scene()->removeItem(dup);
1528         delete dup;
1529
1530         ItemInfo clipinfo = item->info();
1531         clipinfo.track = m_document->tracksCount() - clipinfo.track;
1532         bool success = m_document->renderer()->mltResizeClipEnd(clipinfo, info.endPos - info.startPos);
1533         if (success) {
1534             item->resizeEnd((int) info.endPos.frames(m_document->fps()));
1535             m_document->setModified(true);
1536         } else
1537             emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
1538
1539     }
1540     QTimer::singleShot(3000, this, SLOT(slotEnableRefresh()));
1541 }
1542
1543 void CustomTrackView::slotEnableRefresh()
1544 {
1545     m_blockRefresh = false;
1546 }
1547
1548 void CustomTrackView::slotAddTransitionToSelectedClips(QDomElement transition)
1549 {
1550     QList<QGraphicsItem *> itemList = scene()->selectedItems();
1551     if (itemList.count() == 1) {
1552         if (itemList.at(0)->type() == AVWIDGET) {
1553             ClipItem *item = (ClipItem *) itemList.at(0);
1554             ItemInfo info;
1555             info.track = item->track();
1556             ClipItem *transitionClip = NULL;
1557             const int transitiontrack = getPreviousVideoTrack(info.track);
1558             GenTime pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
1559             if (pos < item->startPos() + item->duration() / 2) {
1560                 // add transition to clip start
1561                 info.startPos = item->startPos();
1562                 if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.startPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
1563                 if (transitionClip && transitionClip->endPos() < item->endPos()) {
1564                     info.endPos = transitionClip->endPos();
1565                 } else info.endPos = info.startPos + GenTime(65, m_document->fps());
1566                 // Check there is no other transition at that place
1567                 double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
1568                 QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
1569                 QList<QGraphicsItem *> selection = m_scene->items(r);
1570                 bool transitionAccepted = true;
1571                 for (int i = 0; i < selection.count(); i++) {
1572                     if (selection.at(i)->type() == TRANSITIONWIDGET) {
1573                         Transition *tr = static_cast <Transition *>(selection.at(i));
1574                         if (tr->startPos() - info.startPos > GenTime(5, m_document->fps())) {
1575                             if (tr->startPos() < info.endPos) info.endPos = tr->startPos();
1576                         } else transitionAccepted = false;
1577                     }
1578                 }
1579                 if (transitionAccepted) slotAddTransition(item, info, transitiontrack, transition);
1580                 else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1581
1582             } else {
1583                 // add transition to clip  end
1584                 info.endPos = item->endPos();
1585                 if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.endPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
1586                 if (transitionClip && transitionClip->startPos() > item->startPos()) {
1587                     info.startPos = transitionClip->startPos();
1588                 } else info.startPos = info.endPos - GenTime(65, m_document->fps());
1589                 if (transition.attribute("tag") == "luma") EffectsList::setParameter(transition, "reverse", "1");
1590                 else if (transition.attribute("id") == "slide") EffectsList::setParameter(transition, "invert", "1");
1591
1592                 // Check there is no other transition at that place
1593                 double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
1594                 QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
1595                 QList<QGraphicsItem *> selection = m_scene->items(r);
1596                 bool transitionAccepted = true;
1597                 for (int i = 0; i < selection.count(); i++) {
1598                     if (selection.at(i)->type() == TRANSITIONWIDGET) {
1599                         Transition *tr = static_cast <Transition *>(selection.at(i));
1600                         if (info.endPos - tr->endPos() > GenTime(5, m_document->fps())) {
1601                             if (tr->endPos() > info.startPos) info.startPos = tr->endPos();
1602                         } else transitionAccepted = false;
1603                     }
1604                 }
1605                 if (transitionAccepted) slotAddTransition(item, info, transitiontrack, transition);
1606                 else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1607             }
1608         }
1609     } else for (int i = 0; i < itemList.count(); i++) {
1610             if (itemList.at(i)->type() == AVWIDGET) {
1611                 ClipItem *item = (ClipItem *) itemList.at(i);
1612                 ItemInfo info;
1613                 info.startPos = item->startPos();
1614                 info.endPos = info.startPos + GenTime(65, m_document->fps());
1615                 info.track = item->track();
1616
1617                 // Check there is no other transition at that place
1618                 double startY = info.track * m_tracksHeight + 1 + m_tracksHeight / 2;
1619                 QRectF r(info.startPos.frames(m_document->fps()), startY, (info.endPos - info.startPos).frames(m_document->fps()), m_tracksHeight / 2);
1620                 QList<QGraphicsItem *> selection = m_scene->items(r);
1621                 bool transitionAccepted = true;
1622                 for (int i = 0; i < selection.count(); i++) {
1623                     if (selection.at(i)->type() == TRANSITIONWIDGET) {
1624                         Transition *tr = static_cast <Transition *>(selection.at(i));
1625                         if (tr->startPos() - info.startPos > GenTime(5, m_document->fps())) {
1626                             if (tr->startPos() < info.endPos) info.endPos = tr->startPos();
1627                         } else transitionAccepted = false;
1628                     }
1629                 }
1630                 int transitiontrack = getPreviousVideoTrack(info.track);
1631                 if (transitionAccepted) slotAddTransition(item, info, transitiontrack, transition);
1632                 else emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1633             }
1634         }
1635 }
1636
1637 void CustomTrackView::slotAddTransition(ClipItem* /*clip*/, ItemInfo transitionInfo, int endTrack, QDomElement transition)
1638 {
1639     if (transitionInfo.startPos >= transitionInfo.endPos) {
1640         emit displayMessage(i18n("Invalid transition"), ErrorMessage);
1641         return;
1642     }
1643     AddTransitionCommand* command = new AddTransitionCommand(this, transitionInfo, endTrack, transition, false, true);
1644     m_commandStack->push(command);
1645     m_document->setModified(true);
1646 }
1647
1648 void CustomTrackView::addTransition(ItemInfo transitionInfo, int endTrack, QDomElement params)
1649 {
1650     Transition *tr = new Transition(transitionInfo, endTrack, m_document->fps(), params, true);
1651     //kDebug() << "---- ADDING transition " << params.attribute("value");
1652     if (m_document->renderer()->mltAddTransition(tr->transitionTag(), endTrack, m_document->tracksCount() - transitionInfo.track, transitionInfo.startPos, transitionInfo.endPos, tr->toXML())) {
1653         scene()->addItem(tr);
1654         m_document->setModified(true);
1655     } else {
1656         emit displayMessage(i18n("Cannot add transition"), ErrorMessage);
1657         delete tr;
1658     }
1659 }
1660
1661 void CustomTrackView::deleteTransition(ItemInfo transitionInfo, int endTrack, QDomElement /*params*/)
1662 {
1663     Transition *item = getTransitionItemAt(transitionInfo.startPos, transitionInfo.track);
1664     if (!item) {
1665         emit displayMessage(i18n("Select clip to delete"), ErrorMessage);
1666         return;
1667     }
1668     m_document->renderer()->mltDeleteTransition(item->transitionTag(), endTrack, m_document->tracksCount() - transitionInfo.track, transitionInfo.startPos, transitionInfo.endPos, item->toXML());
1669     if (m_dragItem == item) m_dragItem = NULL;
1670     delete item;
1671     emit transitionItemSelected(NULL);
1672     m_document->setModified(true);
1673 }
1674
1675 void CustomTrackView::slotTransitionUpdated(Transition *tr, QDomElement old)
1676 {
1677     kDebug() << "TRANS UPDATE, TRACKS: " << old.attribute("transition_btrack") << ", NEW: " << tr->toXML().attribute("transition_btrack");
1678     EditTransitionCommand *command = new EditTransitionCommand(this, tr->track(), tr->startPos(), old, tr->toXML(), false);
1679     m_commandStack->push(command);
1680     m_document->setModified(true);
1681 }
1682
1683 void CustomTrackView::updateTransition(int track, GenTime pos, QDomElement oldTransition, QDomElement transition, bool updateTransitionWidget)
1684 {
1685     Transition *item = getTransitionItemAt(pos, track);
1686     if (!item) {
1687         kWarning() << "Unable to find transition at pos :" << pos.frames(m_document->fps()) << ", ON track: " << track;
1688         return;
1689     }
1690     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);
1691     //kDebug() << "ORIGINAL TRACK: "<< oldTransition.attribute("transition_btrack") << ", NEW TRACK: "<<transition.attribute("transition_btrack");
1692     item->setTransitionParameters(transition);
1693     if (updateTransitionWidget) {
1694         ItemInfo info = item->info();
1695         QPoint p;
1696         ClipItem *transitionClip = getClipItemAt(info.startPos, info.track);
1697         if (transitionClip && transitionClip->baseClip()) {
1698             QString size = transitionClip->baseClip()->getProperty("frame_size");
1699             p.setX(size.section('x', 0, 0).toInt());
1700             p.setY(size.section('x', 1, 1).toInt());
1701         }
1702         emit transitionItemSelected(item, getPreviousVideoTrack(info.track), p, true);
1703     }
1704     m_document->setModified(true);
1705 }
1706
1707 void CustomTrackView::dragMoveEvent(QDragMoveEvent * event)
1708 {
1709     event->setDropAction(Qt::IgnoreAction);
1710     const QPointF pos = mapToScene(event->pos());
1711     if (m_selectionGroup && m_clipDrag) {
1712         m_selectionGroup->setPos(pos.x(), pos.y());
1713         emit mousePosition((int)(m_selectionGroup->scenePos().x() + 0.5));
1714         event->setDropAction(Qt::MoveAction);
1715         event->acceptProposedAction();
1716     } else {
1717         QGraphicsView::dragMoveEvent(event);
1718     }
1719 }
1720
1721 void CustomTrackView::dragLeaveEvent(QDragLeaveEvent * event)
1722 {
1723     if (m_selectionGroup && m_clipDrag) {
1724         m_thumbsTimer.stop();
1725         m_waitingThumbs.clear();
1726         QList<QGraphicsItem *> items = m_selectionGroup->childItems();
1727         qDeleteAll(items);
1728         scene()->destroyItemGroup(m_selectionGroup);
1729         m_selectionGroup = NULL;
1730     } else QGraphicsView::dragLeaveEvent(event);
1731 }
1732
1733 void CustomTrackView::dropEvent(QDropEvent * event)
1734 {
1735     if (m_selectionGroup && m_clipDrag) {
1736         QList<QGraphicsItem *> items = m_selectionGroup->childItems();
1737         resetSelectionGroup();
1738         m_scene->clearSelection();
1739         bool hasVideoClip = false;
1740         QUndoCommand *addCommand = new QUndoCommand();
1741         addCommand->setText(i18n("Add timeline clip"));
1742
1743         for (int i = 0; i < items.count(); i++) {
1744             ClipItem *item = static_cast <ClipItem *>(items.at(i));
1745             if (!hasVideoClip && (item->clipType() == AV || item->clipType() == VIDEO)) hasVideoClip = true;
1746             if (items.count() == 1) {
1747                 updateClipTypeActions(item);
1748             } else {
1749                 updateClipTypeActions(NULL);
1750             }
1751
1752             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), false, false, addCommand);
1753             item->baseClip()->addReference();
1754             m_document->updateClip(item->baseClip()->getId());
1755             ItemInfo info = item->info();
1756
1757             int tracknumber = m_document->tracksCount() - info.track - 1;
1758             bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
1759             if (isLocked) item->setItemLocked(true);
1760
1761             if (item->baseClip()->isTransparent() && getTransitionItemAtStart(info.startPos, info.track) == NULL) {
1762                 // add transparency transition
1763                 new AddTransitionCommand(this, info, getPreviousVideoTrack(info.track), MainWindow::transitions.getEffectByTag("composite", "composite"), false, true, addCommand);
1764             }
1765             info.track = m_document->tracksCount() - item->track();
1766             m_document->renderer()->mltInsertClip(info, item->xml(), item->baseClip()->producer(item->track()));
1767             item->setSelected(true);
1768         }
1769         m_commandStack->push(addCommand);
1770         m_document->setModified(true);
1771         m_changeSpeedAction->setEnabled(hasVideoClip);
1772         m_pasteEffectsAction->setEnabled(m_copiedItems.count() == 1);
1773         if (items.count() > 1) groupSelectedItems(true);
1774     } else QGraphicsView::dropEvent(event);
1775     setFocus();
1776 }
1777
1778
1779 QStringList CustomTrackView::mimeTypes() const
1780 {
1781     QStringList qstrList;
1782     // list of accepted mime types for drop
1783     qstrList.append("text/plain");
1784     qstrList.append("kdenlive/producerslist");
1785     qstrList.append("kdenlive/clip");
1786     return qstrList;
1787 }
1788
1789 Qt::DropActions CustomTrackView::supportedDropActions() const
1790 {
1791     // returns what actions are supported when dropping
1792     return Qt::MoveAction;
1793 }
1794
1795 void CustomTrackView::setDuration(int duration)
1796 {
1797     int diff = qAbs(duration - sceneRect().width());
1798     if (diff * matrix().m11() > -50) {
1799         if (matrix().m11() < 0.4) setSceneRect(0, 0, (duration + 100 / matrix().m11()), sceneRect().height());
1800         else setSceneRect(0, 0, (duration + 300), sceneRect().height());
1801     }
1802     m_projectDuration = duration;
1803 }
1804
1805 int CustomTrackView::duration() const
1806 {
1807     return m_projectDuration;
1808 }
1809
1810 void CustomTrackView::addTrack(TrackInfo type, int ix)
1811 {
1812     if (ix == -1 || ix == m_document->tracksCount()) {
1813         m_document->insertTrack(ix, type);
1814         m_document->renderer()->mltInsertTrack(1, type.type == VIDEOTRACK);
1815     } else {
1816         m_document->insertTrack(m_document->tracksCount() - ix, type);
1817         // insert track in MLT playlist
1818         m_document->renderer()->mltInsertTrack(m_document->tracksCount() - ix, type.type == VIDEOTRACK);
1819
1820         double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
1821         QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY);
1822         QList<QGraphicsItem *> selection = m_scene->items(r);
1823         resetSelectionGroup();
1824
1825         m_selectionGroup = new AbstractGroupItem(m_document->fps());
1826         scene()->addItem(m_selectionGroup);
1827         for (int i = 0; i < selection.count(); i++) {
1828             if (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET)
1829                 m_selectionGroup->addToGroup(selection.at(i));
1830             selection.at(i)->setFlags(QGraphicsItem::ItemIsSelectable);
1831         }
1832         // Move graphic items
1833         m_selectionGroup->translate(0, m_tracksHeight);
1834
1835         // adjust track number
1836         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
1837         for (int i = 0; i < children.count(); i++) {
1838             AbstractClipItem *item = static_cast <AbstractClipItem *>(children.at(i));
1839             item->updateItem();
1840             ItemInfo clipinfo = item->info();
1841             if (item->type() == AVWIDGET) {
1842                 ClipItem *clip = static_cast <ClipItem *>(item);
1843                 // We add a move clip command so that we get the correct producer for new track number
1844                 if (clip->clipType() == AV || clip->clipType() == AUDIO) {
1845                     Mlt::Producer *prod;
1846                     if (clip->isAudioOnly()) prod = clip->baseClip()->audioProducer(clipinfo.track);
1847                     else if (clip->isVideoOnly()) prod = clip->baseClip()->videoProducer();
1848                     else prod = clip->baseClip()->producer(clipinfo.track);
1849                     m_document->renderer()->mltUpdateClipProducer((int)(m_document->tracksCount() - clipinfo.track), clipinfo.startPos.frames(m_document->fps()), prod);
1850                     kDebug() << "// UPDATING CLIP TO TRACK PROD: " << clipinfo.track;
1851                 }
1852             } else if (item->type() == TRANSITIONWIDGET) {
1853                 Transition *tr = static_cast <Transition *>(item);
1854                 int track = tr->transitionEndTrack();
1855                 if (track >= ix) {
1856                     tr->updateTransitionEndTrack(getPreviousVideoTrack(clipinfo.track));
1857                 }
1858             }
1859         }
1860         resetSelectionGroup(false);
1861
1862     }
1863     int maxHeight = m_tracksHeight * m_document->tracksCount();
1864     for (int i = 0; i < m_guides.count(); i++) {
1865         QLineF l = m_guides.at(i)->line();
1866         l.setP2(QPointF(l.x2(), maxHeight));
1867         m_guides.at(i)->setLine(l);
1868     }
1869     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), maxHeight);
1870     setSceneRect(0, 0, sceneRect().width(), maxHeight);
1871     QTimer::singleShot(300, this, SIGNAL(trackHeightChanged(false)));
1872     viewport()->update();
1873     //setFixedHeight(50 * m_tracksCount);
1874 }
1875
1876 void CustomTrackView::removeTrack(int ix)
1877 {
1878     // Delete track in MLT playlist
1879     m_document->renderer()->mltDeleteTrack(m_document->tracksCount() - ix);
1880     m_document->deleteTrack(m_document->tracksCount() - ix - 1);
1881
1882     double startY = ix * (m_tracksHeight + 1) + m_tracksHeight / 2;
1883     QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY);
1884     QList<QGraphicsItem *> selection = m_scene->items(r);
1885
1886     resetSelectionGroup();
1887
1888     m_selectionGroup = new AbstractGroupItem(m_document->fps());
1889     scene()->addItem(m_selectionGroup);
1890     for (int i = 0; i < selection.count(); i++) {
1891         if (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET) {
1892             m_selectionGroup->addToGroup(selection.at(i));
1893             selection.at(i)->setFlags(QGraphicsItem::ItemIsSelectable);
1894         }
1895     }
1896     // Move graphic items
1897     qreal ydiff = 0 - (int) m_tracksHeight;
1898     m_selectionGroup->translate(0, ydiff);
1899
1900     // adjust track number
1901     QList<QGraphicsItem *> children = m_selectionGroup->childItems();
1902     //kDebug() << "// FOUND CLIPS TO MOVE: " << children.count();
1903     for (int i = 0; i < children.count(); i++) {
1904         if (children.at(i)->type() == AVWIDGET) {
1905             ClipItem *clip = static_cast <ClipItem *>(children.at(i));
1906             clip->updateItem();
1907             ItemInfo clipinfo = clip->info();
1908             // We add a move clip command so that we get the correct producer for new track number
1909             if (clip->clipType() == AV || clip->clipType() == AUDIO) {
1910                 Mlt::Producer *prod;
1911                 if (clip->isAudioOnly()) prod = clip->baseClip()->audioProducer(clipinfo.track);
1912                 else if (clip->isVideoOnly()) prod = clip->baseClip()->videoProducer();
1913                 else prod = clip->baseClip()->producer(clipinfo.track);
1914                 m_document->renderer()->mltUpdateClipProducer((int)(m_document->tracksCount() - clipinfo.track), clipinfo.startPos.frames(m_document->fps()), prod);
1915             }
1916         } else if (children.at(i)->type() == TRANSITIONWIDGET) {
1917             Transition *tr = static_cast <Transition *>(children.at(i));
1918             tr->updateItem();
1919             int track = tr->transitionEndTrack();
1920             if (track >= ix) {
1921                 ItemInfo clipinfo = tr->info();
1922                 tr->updateTransitionEndTrack(getPreviousVideoTrack(clipinfo.track));
1923             }
1924         }
1925     }
1926     resetSelectionGroup(false);
1927
1928     int maxHeight = m_tracksHeight * m_document->tracksCount();
1929     for (int i = 0; i < m_guides.count(); i++) {
1930         QLineF l = m_guides.at(i)->line();
1931         l.setP2(QPointF(l.x2(), maxHeight));
1932         m_guides.at(i)->setLine(l);
1933     }
1934     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), maxHeight);
1935     setSceneRect(0, 0, sceneRect().width(), maxHeight);
1936     QTimer::singleShot(300, this, SIGNAL(trackHeightChanged(false)));
1937     viewport()->update();
1938 }
1939
1940 void CustomTrackView::changeTrack(int ix, TrackInfo type)
1941 {
1942     int tracknumber = m_document->tracksCount() - ix;
1943     m_document->setTrackType(tracknumber - 1, type);
1944     m_document->renderer()->mltChangeTrackState(tracknumber, m_document->trackInfoAt(tracknumber - 1).isMute, m_document->trackInfoAt(tracknumber - 1).isBlind);
1945     QTimer::singleShot(300, this, SIGNAL(trackHeightChanged(false)));
1946     viewport()->update();
1947 }
1948
1949
1950 void CustomTrackView::slotSwitchTrackAudio(int ix)
1951 {
1952     /*for (int i = 0; i < m_document->tracksCount(); i++)
1953         kDebug() << "TRK " << i << " STATE: " << m_document->trackInfoAt(i).isMute << m_document->trackInfoAt(i).isBlind;*/
1954     int tracknumber = m_document->tracksCount() - ix - 1;
1955     m_document->switchTrackAudio(tracknumber, !m_document->trackInfoAt(tracknumber).isMute);
1956     kDebug() << "NEXT TRK STATE: " << m_document->trackInfoAt(tracknumber).isMute << m_document->trackInfoAt(tracknumber).isBlind;
1957     m_document->renderer()->mltChangeTrackState(tracknumber + 1, m_document->trackInfoAt(tracknumber).isMute, m_document->trackInfoAt(tracknumber).isBlind);
1958     m_document->setModified(true);
1959 }
1960
1961 void CustomTrackView::slotSwitchTrackLock(int ix)
1962 {
1963     int tracknumber = m_document->tracksCount() - ix - 1;
1964     LockTrackCommand *command = new LockTrackCommand(this, ix, !m_document->trackInfoAt(tracknumber).isLocked);
1965     m_commandStack->push(command);
1966 }
1967
1968
1969 void CustomTrackView::lockTrack(int ix, bool lock)
1970 {
1971     int tracknumber = m_document->tracksCount() - ix - 1;
1972     m_document->switchTrackLock(tracknumber, lock);
1973     emit doTrackLock(ix, lock);
1974     QList<QGraphicsItem *> selection = m_scene->items(0, ix * m_tracksHeight + m_tracksHeight / 2, sceneRect().width(), m_tracksHeight / 2 - 2);
1975
1976     for (int i = 0; i < selection.count(); i++) {
1977         if (selection.at(i)->type() != AVWIDGET && selection.at(i)->type() != TRANSITIONWIDGET) continue;
1978         if (selection.at(i)->isSelected()) {
1979             if (selection.at(i)->type() == AVWIDGET) emit clipItemSelected(NULL);
1980             else emit transitionItemSelected(NULL);
1981         }
1982         static_cast <AbstractClipItem *>(selection.at(i))->setItemLocked(lock);
1983     }
1984     kDebug() << "NEXT TRK STATE: " << m_document->trackInfoAt(tracknumber).isLocked;
1985     viewport()->update();
1986     m_document->setModified(true);
1987 }
1988
1989 void CustomTrackView::slotSwitchTrackVideo(int ix)
1990 {
1991     int tracknumber = m_document->tracksCount() - ix;
1992     m_document->switchTrackVideo(tracknumber - 1, !m_document->trackInfoAt(tracknumber - 1).isBlind);
1993     m_document->renderer()->mltChangeTrackState(tracknumber, m_document->trackInfoAt(tracknumber - 1).isMute, m_document->trackInfoAt(tracknumber - 1).isBlind);
1994     m_document->setModified(true);
1995 }
1996
1997 void CustomTrackView::slotRemoveSpace()
1998 {
1999     GenTime pos;
2000     int track = 0;
2001     if (m_menuPosition.isNull()) {
2002         pos = GenTime(cursorPos(), m_document->fps());
2003         bool ok;
2004         track = QInputDialog::getInteger(this, i18n("Remove Space"), i18n("Track"), 0, 0, m_document->tracksCount() - 1, 1, &ok);
2005         if (!ok) return;
2006     } else {
2007         pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
2008         track = (int)(mapToScene(m_menuPosition).y() / m_tracksHeight);
2009     }
2010     ClipItem *item = getClipItemAt(pos, track);
2011     if (item) {
2012         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);
2013         return;
2014     }
2015     int length = m_document->renderer()->mltGetSpaceLength(pos, m_document->tracksCount() - track, true);
2016     //kDebug() << "// GOT LENGT; " << length;
2017     if (length <= 0) {
2018         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);
2019         return;
2020     }
2021
2022     QRectF r(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - pos.frames(m_document->fps()), m_tracksHeight / 2 - 1);
2023     QList<QGraphicsItem *> items = m_scene->items(r);
2024
2025     QList<ItemInfo> clipsToMove;
2026     QList<ItemInfo> transitionsToMove;
2027
2028     for (int i = 0; i < items.count(); i++) {
2029         if (items.at(i)->type() == AVWIDGET || items.at(i)->type() == TRANSITIONWIDGET) {
2030             AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2031             ItemInfo info = item->info();
2032             if (item->type() == AVWIDGET) {
2033                 clipsToMove.append(info);
2034             } else if (item->type() == TRANSITIONWIDGET) {
2035                 transitionsToMove.append(info);
2036             }
2037         }
2038     }
2039
2040     InsertSpaceCommand *command = new InsertSpaceCommand(this, clipsToMove, transitionsToMove, track, GenTime(-length, m_document->fps()), true);
2041     m_commandStack->push(command);
2042 }
2043
2044 void CustomTrackView::slotInsertSpace()
2045 {
2046     GenTime pos;
2047     int track = 0;
2048     if (m_menuPosition.isNull()) {
2049         pos = GenTime(cursorPos(), m_document->fps());
2050     } else {
2051         pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
2052         track = (int)(mapToScene(m_menuPosition).y() / m_tracksHeight) + 1;
2053     }
2054     SpacerDialog d(GenTime(65, m_document->fps()), m_document->timecode(), track, m_document->tracksCount(), this);
2055     if (d.exec() != QDialog::Accepted) return;
2056     GenTime spaceDuration = d.selectedDuration();
2057     track = d.selectedTrack();
2058     ClipItem *item = getClipItemAt(pos, track);
2059     if (item) pos = item->startPos();
2060
2061     int minh = 0;
2062     int maxh = sceneRect().height();
2063     if (track != -1) {
2064         minh = track * m_tracksHeight + m_tracksHeight / 2;
2065         maxh = m_tracksHeight / 2 - 1;
2066     }
2067
2068     QRectF r(pos.frames(m_document->fps()), minh, sceneRect().width() - pos.frames(m_document->fps()), maxh);
2069     QList<QGraphicsItem *> items = m_scene->items(r);
2070
2071     QList<ItemInfo> clipsToMove;
2072     QList<ItemInfo> transitionsToMove;
2073
2074     for (int i = 0; i < items.count(); i++) {
2075         if (items.at(i)->type() == AVWIDGET || items.at(i)->type() == TRANSITIONWIDGET) {
2076             AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2077             ItemInfo info = item->info();
2078             if (item->type() == AVWIDGET) {
2079                 clipsToMove.append(info);
2080             } else if (item->type() == TRANSITIONWIDGET) {
2081                 transitionsToMove.append(info);
2082             }
2083         }
2084     }
2085
2086     InsertSpaceCommand *command = new InsertSpaceCommand(this, clipsToMove, transitionsToMove, track, spaceDuration, true);
2087     m_commandStack->push(command);
2088 }
2089
2090 void CustomTrackView::insertSpace(QList<ItemInfo> clipsToMove, QList<ItemInfo> transToMove, int track, const GenTime duration, const GenTime offset)
2091 {
2092     int diff = duration.frames(m_document->fps());
2093     resetSelectionGroup();
2094     m_selectionGroup = new AbstractGroupItem(m_document->fps());
2095     scene()->addItem(m_selectionGroup);
2096     ClipItem *clip;
2097     Transition *transition;
2098
2099     // Create lists with start pos for each track
2100     QMap <int, int> trackClipStartList;
2101     QMap <int, int> trackTransitionStartList;
2102
2103     for (int i = 1; i < m_document->tracksCount() + 1; i++) {
2104         trackClipStartList[i] = -1;
2105         trackTransitionStartList[i] = -1;
2106     }
2107
2108     if (!clipsToMove.isEmpty()) for (int i = 0; i < clipsToMove.count(); i++) {
2109             clip = getClipItemAtStart(clipsToMove.at(i).startPos + offset, clipsToMove.at(i).track);
2110             if (clip) {
2111                 if (clip->parentItem()) {
2112                     m_selectionGroup->addToGroup(clip->parentItem());
2113                     clip->parentItem()->setFlags(QGraphicsItem::ItemIsSelectable);
2114                 } else {
2115                     m_selectionGroup->addToGroup(clip);
2116                     clip->setFlags(QGraphicsItem::ItemIsSelectable);
2117                 }
2118                 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))
2119                     trackClipStartList[m_document->tracksCount() - clipsToMove.at(i).track] = clipsToMove.at(i).startPos.frames(m_document->fps());
2120             } 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);
2121         }
2122     if (!transToMove.isEmpty()) for (int i = 0; i < transToMove.count(); i++) {
2123             transition = getTransitionItemAtStart(transToMove.at(i).startPos + offset, transToMove.at(i).track);
2124             if (transition) {
2125                 if (transition->parentItem()) m_selectionGroup->addToGroup(transition->parentItem());
2126                 m_selectionGroup->addToGroup(transition);
2127                 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))
2128                     trackTransitionStartList[m_document->tracksCount() - transToMove.at(i).track] = transToMove.at(i).startPos.frames(m_document->fps());
2129                 transition->setFlags(QGraphicsItem::ItemIsSelectable);
2130             } 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);
2131         }
2132     m_selectionGroup->translate(diff, 0);
2133
2134     // update items coordinates
2135     QList<QGraphicsItem *> itemList = scene()->selectedItems();
2136     for (int i = 0; i < itemList.count(); i++) {
2137         if (itemList.at(i)->type() == AVWIDGET || itemList.at(i)->type() == TRANSITIONWIDGET) {
2138             static_cast < AbstractClipItem *>(itemList.at(i))->updateItem();
2139         } else if (itemList.at(i)->type() == GROUPWIDGET) {
2140             QList<QGraphicsItem *> children = itemList.at(i)->childItems();
2141             for (int j = 0; j < children.count(); j++) {
2142                 static_cast < AbstractClipItem *>(children.at(j))->updateItem();
2143             }
2144         }
2145     }
2146     resetSelectionGroup(false);
2147     if (track != -1) track = m_document->tracksCount() - track;
2148     m_document->renderer()->mltInsertSpace(trackClipStartList, trackTransitionStartList, track, duration, offset);
2149 }
2150
2151 void CustomTrackView::deleteClip(const QString &clipId)
2152 {
2153     resetSelectionGroup();
2154     QList<QGraphicsItem *> itemList = items();
2155     QUndoCommand *deleteCommand = new QUndoCommand();
2156     deleteCommand->setText(i18n("Delete timeline clips"));
2157     int count = 0;
2158     for (int i = 0; i < itemList.count(); i++) {
2159         if (itemList.at(i)->type() == AVWIDGET) {
2160             ClipItem *item = (ClipItem *)itemList.at(i);
2161             if (item->clipProducer() == clipId) {
2162                 count++;
2163                 if (item->parentItem()) {
2164                     // Clip is in a group, destroy the group
2165                     new GroupClipsCommand(this, QList<ItemInfo>() << item->info(), QList<ItemInfo>(), false, deleteCommand);
2166                 }
2167                 new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), true, true, deleteCommand);
2168             }
2169         }
2170     }
2171     if (count == 0) delete deleteCommand;
2172     else m_commandStack->push(deleteCommand);
2173 }
2174
2175 void CustomTrackView::setCursorPos(int pos, bool seek)
2176 {
2177     if (pos == m_cursorPos) return;
2178     emit cursorMoved((int)(m_cursorPos), (int)(pos));
2179     m_cursorPos = pos;
2180     if (seek) m_document->renderer()->seek(GenTime(m_cursorPos, m_document->fps()));
2181     else if (m_autoScroll) checkScrolling();
2182     m_cursorLine->setPos(m_cursorPos, 0);
2183 }
2184
2185 void CustomTrackView::updateCursorPos()
2186 {
2187     m_cursorLine->setPos(m_cursorPos, 0);
2188 }
2189
2190 int CustomTrackView::cursorPos()
2191 {
2192     return (int)(m_cursorPos);
2193 }
2194
2195 void CustomTrackView::moveCursorPos(int delta)
2196 {
2197     if (m_cursorPos + delta < 0) delta = 0 - m_cursorPos;
2198     emit cursorMoved((int)(m_cursorPos), (int)((m_cursorPos + delta)));
2199     m_cursorPos += delta;
2200     m_cursorLine->setPos(m_cursorPos, 0);
2201     m_document->renderer()->seek(GenTime(m_cursorPos, m_document->fps()));
2202 }
2203
2204 void CustomTrackView::initCursorPos(int pos)
2205 {
2206     emit cursorMoved((int)(m_cursorPos), (int)(pos));
2207     m_cursorPos = pos;
2208     m_cursorLine->setPos(pos, 0);
2209     checkScrolling();
2210 }
2211
2212 void CustomTrackView::checkScrolling()
2213 {
2214     ensureVisible(m_cursorPos, verticalScrollBar()->value() + 10, 2, 2, 50, 0);
2215 }
2216
2217 void CustomTrackView::mouseReleaseEvent(QMouseEvent * event)
2218 {
2219     if (m_moveOpMode == SEEK) m_moveOpMode = NONE;
2220     QGraphicsView::mouseReleaseEvent(event);
2221     if (m_scrollTimer.isActive()) m_scrollTimer.stop();
2222     if (event->button() == Qt::MidButton) {
2223         return;
2224     }
2225     setDragMode(QGraphicsView::NoDrag);
2226     if (m_operationMode == MOVEGUIDE) {
2227         setCursor(Qt::ArrowCursor);
2228         m_operationMode = NONE;
2229         m_dragGuide->setFlag(QGraphicsItem::ItemIsMovable, false);
2230         GenTime newPos = GenTime(m_dragGuide->pos().x(), m_document->fps());
2231         if (newPos != m_dragGuide->position()) {
2232             EditGuideCommand *command = new EditGuideCommand(this, m_dragGuide->position(), m_dragGuide->label(), newPos, m_dragGuide->label(), false);
2233             m_commandStack->push(command);
2234             m_dragGuide->updateGuide(GenTime(m_dragGuide->pos().x(), m_document->fps()));
2235             m_document->syncGuides(m_guides);
2236         }
2237         m_dragGuide = NULL;
2238         m_dragItem = NULL;
2239         return;
2240     } else if (m_operationMode == SPACER && m_selectionGroup) {
2241         int track;
2242         if (event->modifiers() != Qt::ControlModifier) {
2243             // We are moving all tracks
2244             track = -1;
2245         } else track = (int)(mapToScene(m_clickEvent).y() / m_tracksHeight);
2246         GenTime timeOffset = GenTime(m_selectionGroup->scenePos().x(), m_document->fps()) - m_selectionGroupInfo.startPos;
2247         if (timeOffset != GenTime()) {
2248             QList<QGraphicsItem *> items = m_selectionGroup->childItems();
2249
2250             QList<ItemInfo> clipsToMove;
2251             QList<ItemInfo> transitionsToMove;
2252
2253             // Create lists with start pos for each track
2254             QMap <int, int> trackClipStartList;
2255             QMap <int, int> trackTransitionStartList;
2256
2257             for (int i = 1; i < m_document->tracksCount() + 1; i++) {
2258                 trackClipStartList[i] = -1;
2259                 trackTransitionStartList[i] = -1;
2260             }
2261
2262             int max = items.count();
2263             for (int i = 0; i < max; i++) {
2264                 if (items.at(i)->type() == GROUPWIDGET)
2265                     items += static_cast <QGraphicsItemGroup *>(items.at(i))->childItems();
2266             }
2267
2268             for (int i = 0; i < items.count(); i++) {
2269                 if (items.at(i)->type() == AVWIDGET) {
2270                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2271                     ItemInfo info = item->info();
2272                     clipsToMove.append(info);
2273                     item->updateItem();
2274                     if (trackClipStartList.value(m_document->tracksCount() - info.track) == -1 || info.startPos.frames(m_document->fps()) < trackClipStartList.value(m_document->tracksCount() - info.track))
2275                         trackClipStartList[m_document->tracksCount() - info.track] = info.startPos.frames(m_document->fps());
2276                 } else if (items.at(i)->type() == TRANSITIONWIDGET) {
2277                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2278                     ItemInfo info = item->info();
2279                     transitionsToMove.append(info);
2280                     item->updateItem();
2281                     if (trackTransitionStartList.value(m_document->tracksCount() - info.track) == -1 || info.startPos.frames(m_document->fps()) < trackTransitionStartList.value(m_document->tracksCount() - info.track))
2282                         trackTransitionStartList[m_document->tracksCount() - info.track] = info.startPos.frames(m_document->fps());
2283                 }
2284             }
2285
2286             InsertSpaceCommand *command = new InsertSpaceCommand(this, clipsToMove, transitionsToMove, track, timeOffset, false);
2287             m_commandStack->push(command);
2288             if (track != -1) track = m_document->tracksCount() - track;
2289             kDebug() << "SPACER TRACK:" << track;
2290             m_document->renderer()->mltInsertSpace(trackClipStartList, trackTransitionStartList, track, timeOffset, GenTime());
2291         }
2292         resetSelectionGroup(false);
2293         m_operationMode = NONE;
2294     } else if (m_operationMode == RUBBERSELECTION) {
2295         //kDebug() << "// END RUBBER SELECT";
2296         resetSelectionGroup();
2297         groupSelectedItems();
2298         m_operationMode = NONE;
2299     }
2300
2301     if (m_dragItem == NULL && m_selectionGroup == NULL) {
2302         emit transitionItemSelected(NULL);
2303         return;
2304     }
2305     ItemInfo info;
2306     if (m_dragItem) info = m_dragItem->info();
2307
2308     if (m_operationMode == MOVE) {
2309         setCursor(Qt::OpenHandCursor);
2310
2311         if (m_dragItem->parentItem() == 0) {
2312             // we are moving one clip, easy
2313             if (m_dragItem->type() == AVWIDGET && (m_dragItemInfo.startPos != info.startPos || m_dragItemInfo.track != info.track)) {
2314                 ClipItem *item = static_cast <ClipItem *>(m_dragItem);
2315                 Mlt::Producer *prod;
2316                 if (item->isAudioOnly()) prod = item->baseClip()->audioProducer(m_dragItemInfo.track);
2317                 else if (item->isVideoOnly()) prod = item->baseClip()->videoProducer();
2318                 else prod = item->baseClip()->producer(m_dragItemInfo.track);
2319                 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);
2320                 if (success) {
2321                     kDebug() << "// get trans info";
2322                     int tracknumber = m_document->tracksCount() - item->track() - 1;
2323                     bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
2324                     if (isLocked) item->setItemLocked(true);
2325
2326                     QUndoCommand *moveCommand = new QUndoCommand();
2327                     moveCommand->setText(i18n("Move clip"));
2328                     new MoveClipCommand(this, m_dragItemInfo, info, false, moveCommand);
2329                     // Also move automatic transitions (on lower track)
2330                     Transition *tr = getTransitionItemAtStart(m_dragItemInfo.startPos, m_dragItemInfo.track);
2331                     kDebug() << "// get trans info2";
2332                     if (tr && tr->isAutomatic()) {
2333                         ItemInfo trInfo = tr->info();
2334                         ItemInfo newTrInfo = trInfo;
2335                         newTrInfo.track = info.track;
2336                         newTrInfo.startPos = m_dragItem->startPos();
2337                         if (m_dragItemInfo.track == info.track && !item->baseClip()->isTransparent() && getClipItemAtEnd(newTrInfo.endPos, m_document->tracksCount() - tr->transitionEndTrack())) {
2338                             // transition end should stay the same
2339                         } else {
2340                             // transition end should be adjusted to clip
2341                             newTrInfo.endPos = newTrInfo.endPos + (newTrInfo.startPos - trInfo.startPos);
2342                         }
2343                         if (newTrInfo.startPos < newTrInfo.endPos) new MoveTransitionCommand(this, trInfo, newTrInfo, true, moveCommand);
2344                     }
2345                     if (tr == NULL || tr->endPos() < m_dragItemInfo.endPos) {
2346                         // Check if there is a transition at clip end
2347                         tr = getTransitionItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track);
2348                         if (tr && tr->isAutomatic()) {
2349                             ItemInfo trInfo = tr->info();
2350                             ItemInfo newTrInfo = trInfo;
2351                             newTrInfo.track = info.track;
2352                             newTrInfo.endPos = m_dragItem->endPos();
2353                             if (m_dragItemInfo.track == info.track && !item->baseClip()->isTransparent() && getClipItemAtStart(trInfo.startPos, m_document->tracksCount() - tr->transitionEndTrack())) {
2354                                 // transition start should stay the same
2355                             } else {
2356                                 // transition start should be moved
2357                                 newTrInfo.startPos = newTrInfo.startPos + (newTrInfo.endPos - trInfo.endPos);
2358                             }
2359                             if (newTrInfo.startPos < newTrInfo.endPos)
2360                                 new MoveTransitionCommand(this, trInfo, newTrInfo, true, moveCommand);
2361                         }
2362                     }
2363                     // Also move automatic transitions (on upper track)
2364                     tr = getTransitionItemAtStart(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
2365                     if (m_dragItemInfo.track == info.track && tr && tr->isAutomatic() && (m_document->tracksCount() - tr->transitionEndTrack()) == m_dragItemInfo.track) {
2366                         ItemInfo trInfo = tr->info();
2367                         ItemInfo newTrInfo = trInfo;
2368                         newTrInfo.startPos = m_dragItem->startPos();
2369                         ClipItem * upperClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
2370                         if (!upperClip || !upperClip->baseClip()->isTransparent()) {
2371                             if (!getClipItemAtEnd(newTrInfo.endPos, tr->track())) {
2372                                 // transition end should be adjusted to clip on upper track
2373                                 newTrInfo.endPos = newTrInfo.endPos + (newTrInfo.startPos - trInfo.startPos);
2374                             }
2375                             if (newTrInfo.startPos < newTrInfo.endPos) new MoveTransitionCommand(this, trInfo, newTrInfo, true, moveCommand);
2376                         }
2377                     }
2378                     if (m_dragItemInfo.track == info.track && (tr == NULL || tr->endPos() < m_dragItemInfo.endPos)) {
2379                         // Check if there is a transition at clip end
2380                         tr = getTransitionItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track - 1);
2381                         if (tr && tr->isAutomatic() && (m_document->tracksCount() - tr->transitionEndTrack()) == m_dragItemInfo.track) {
2382                             ItemInfo trInfo = tr->info();
2383                             ItemInfo newTrInfo = trInfo;
2384                             newTrInfo.endPos = m_dragItem->endPos();
2385                             ClipItem * upperClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
2386                             if (!upperClip || !upperClip->baseClip()->isTransparent()) {
2387                                 if (!getClipItemAtStart(trInfo.startPos, tr->track())) {
2388                                     // transition start should be moved
2389                                     newTrInfo.startPos = newTrInfo.startPos + (newTrInfo.endPos - trInfo.endPos);
2390                                 }
2391                                 if (newTrInfo.startPos < newTrInfo.endPos) new MoveTransitionCommand(this, trInfo, newTrInfo, true, moveCommand);
2392                             }
2393                         }
2394                     }
2395                     m_commandStack->push(moveCommand);
2396                 } else {
2397                     // undo last move and emit error message
2398                     bool snap = KdenliveSettings::snaptopoints();
2399                     KdenliveSettings::setSnaptopoints(false);
2400                     item->setPos((int) m_dragItemInfo.startPos.frames(m_document->fps()), (int)(m_dragItemInfo.track * m_tracksHeight + 1));
2401                     KdenliveSettings::setSnaptopoints(snap);
2402                     emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(m_dragItemInfo.startPos.frames(m_document->fps()))), ErrorMessage);
2403                 }
2404                 m_document->setModified(true);
2405             }
2406             if (m_dragItem->type() == TRANSITIONWIDGET && (m_dragItemInfo.startPos != info.startPos || m_dragItemInfo.track != info.track)) {
2407                 Transition *transition = static_cast <Transition *>(m_dragItem);
2408                 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)) {
2409                     // Moving transition failed, revert to previous position
2410                     emit displayMessage(i18n("Cannot move transition"), ErrorMessage);
2411                     transition->setPos((int) m_dragItemInfo.startPos.frames(m_document->fps()), (m_dragItemInfo.track) * m_tracksHeight + 1);
2412                 } else {
2413                     MoveTransitionCommand *command = new MoveTransitionCommand(this, m_dragItemInfo, info, false);
2414                     m_commandStack->push(command);
2415                     transition->updateTransitionEndTrack(getPreviousVideoTrack(m_dragItem->track()));
2416                 }
2417             }
2418         } else {
2419             // Moving several clips. We need to delete them and readd them to new position,
2420             // or they might overlap each other during the move
2421             QGraphicsItemGroup *group = static_cast <QGraphicsItemGroup *>(m_dragItem->parentItem());
2422             QList<QGraphicsItem *> items = group->childItems();
2423
2424             QList<ItemInfo> clipsToMove;
2425             QList<ItemInfo> transitionsToMove;
2426
2427             GenTime timeOffset = GenTime(m_dragItem->scenePos().x(), m_document->fps()) - m_dragItemInfo.startPos;
2428             const int trackOffset = (int)(m_dragItem->scenePos().y() / m_tracksHeight) - m_dragItemInfo.track;
2429             //kDebug() << "// MOVED SEVERAL CLIPS" << timeOffset.frames(25);
2430             if (timeOffset != GenTime() || trackOffset != 0) {
2431                 // remove items in MLT playlist
2432
2433                 // Expand groups
2434                 int max = items.count();
2435                 for (int i = 0; i < max; i++) {
2436                     if (items.at(i)->type() == GROUPWIDGET) {
2437                         items += items.at(i)->childItems();
2438                     }
2439                 }
2440
2441                 for (int i = 0; i < items.count(); i++) {
2442                     if (items.at(i)->type() != AVWIDGET && items.at(i)->type() != TRANSITIONWIDGET) continue;
2443                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2444                     ItemInfo info = item->info();
2445                     if (item->type() == AVWIDGET) {
2446                         if (m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, info.startPos) == false) {
2447                             // error, clip cannot be removed from playlist
2448                             emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(info.startPos.frames(m_document->fps())), info.track), ErrorMessage);
2449                         } else {
2450                             clipsToMove.append(info);
2451                         }
2452                     } else {
2453                         transitionsToMove.append(info);
2454                         Transition *tr = static_cast <Transition*>(item);
2455                         m_document->renderer()->mltDeleteTransition(tr->transitionTag(), tr->transitionEndTrack(), m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
2456                     }
2457                 }
2458
2459                 for (int i = 0; i < items.count(); i++) {
2460                     // re-add items in correct place
2461                     if (items.at(i)->type() != AVWIDGET && items.at(i)->type() != TRANSITIONWIDGET) continue;
2462                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
2463                     item->updateItem();
2464                     ItemInfo info = item->info();
2465                     int tracknumber = m_document->tracksCount() - info.track - 1;
2466                     bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
2467                     if (isLocked) {
2468                         group->removeFromGroup(item);
2469                         item->setItemLocked(true);
2470                     }
2471
2472                     if (item->type() == AVWIDGET) {
2473                         ClipItem *clip = static_cast <ClipItem*>(item);
2474                         info.track = m_document->tracksCount() - info.track;
2475                         Mlt::Producer *prod;
2476                         if (clip->isAudioOnly()) prod = clip->baseClip()->audioProducer(info.track);
2477                         else if (clip->isVideoOnly()) prod = clip->baseClip()->videoProducer();
2478                         else prod = clip->baseClip()->producer(info.track);
2479                         m_document->renderer()->mltInsertClip(info, clip->xml(), prod);
2480                         for (int i = 0; i < clip->effectsCount(); i++) {
2481                             m_document->renderer()->mltAddEffect(info.track, info.startPos, clip->getEffectArgs(clip->effectAt(i)), false);
2482                         }
2483                     } else {
2484                         Transition *tr = static_cast <Transition*>(item);
2485                         int newTrack = tr->transitionEndTrack();
2486                         if (!tr->forcedTrack()) {
2487                             newTrack = getPreviousVideoTrack(info.track);
2488                         }
2489                         tr->updateTransitionEndTrack(newTrack);
2490                         m_document->renderer()->mltAddTransition(tr->transitionTag(), newTrack, m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
2491                     }
2492                 }
2493
2494                 MoveGroupCommand *move = new MoveGroupCommand(this, clipsToMove, transitionsToMove, timeOffset, trackOffset, false);
2495                 m_commandStack->push(move);
2496
2497                 //QPointF top = group->sceneBoundingRect().topLeft();
2498                 //QPointF oldpos = m_selectionGroup->scenePos();
2499                 //kDebug()<<"SELECTION GRP POS: "<<m_selectionGroup->scenePos()<<", TOP: "<<top;
2500                 //group->setPos(top);
2501                 //TODO: get rid of the 3 lines below
2502                 if (m_selectionGroup) {
2503                     m_selectionGroupInfo.startPos = GenTime(m_selectionGroup->scenePos().x(), m_document->fps());
2504                     m_selectionGroupInfo.track = m_selectionGroup->track();
2505                 }
2506                 m_document->setModified(true);
2507             }
2508         }
2509         m_document->renderer()->doRefresh();
2510     } else if (m_operationMode == RESIZESTART && m_dragItem->startPos() != m_dragItemInfo.startPos) {
2511         // resize start
2512         if (m_dragItem->type() == AVWIDGET) {
2513             ItemInfo resizeinfo = m_dragItemInfo;
2514             resizeinfo.track = m_document->tracksCount() - resizeinfo.track;
2515             bool success = m_document->renderer()->mltResizeClipStart(resizeinfo, m_dragItem->startPos() - m_dragItemInfo.startPos);
2516             if (success) {
2517                 QUndoCommand *resizeCommand = new QUndoCommand();
2518                 resizeCommand->setText(i18n("Resize clip"));
2519
2520                 // Check if there is an automatic transition on that clip (lower track)
2521                 Transition *transition = getTransitionItemAtStart(m_dragItemInfo.startPos, m_dragItemInfo.track);
2522                 if (transition && transition->isAutomatic()) {
2523                     ItemInfo trInfo = transition->info();
2524                     ItemInfo newTrInfo = trInfo;
2525                     newTrInfo.startPos = m_dragItem->startPos();
2526                     if (newTrInfo.startPos < newTrInfo.endPos)
2527                         new MoveTransitionCommand(this, trInfo, newTrInfo, true, resizeCommand);
2528                 }
2529                 // Check if there is an automatic transition on that clip (upper track)
2530                 transition = getTransitionItemAtStart(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
2531                 if (transition && transition->isAutomatic() && (m_document->tracksCount() - transition->transitionEndTrack()) == m_dragItemInfo.track) {
2532                     ItemInfo trInfo = transition->info();
2533                     ItemInfo newTrInfo = trInfo;
2534                     newTrInfo.startPos = m_dragItem->startPos();
2535                     ClipItem * upperClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track - 1);
2536                     if ((!upperClip || !upperClip->baseClip()->isTransparent()) && newTrInfo.startPos < newTrInfo.endPos) {
2537                         new MoveTransitionCommand(this, trInfo, newTrInfo, true, resizeCommand);
2538                     }
2539                 }
2540                 updateClipFade(static_cast <ClipItem *>(m_dragItem));
2541                 new ResizeClipCommand(this, m_dragItemInfo, info, false, resizeCommand);
2542                 m_commandStack->push(resizeCommand);
2543             } else {
2544                 m_dragItem->resizeStart((int) m_dragItemInfo.startPos.frames(m_document->fps()));
2545                 emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
2546             }
2547         } else if (m_dragItem->type() == TRANSITIONWIDGET) {
2548             Transition *transition = static_cast <Transition *>(m_dragItem);
2549             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)) {
2550                 // Cannot resize transition
2551                 transition->resizeStart((int) m_dragItemInfo.startPos.frames(m_document->fps()));
2552                 emit displayMessage(i18n("Cannot resize transition"), ErrorMessage);
2553             } else {
2554                 MoveTransitionCommand *command = new MoveTransitionCommand(this, m_dragItemInfo, info, false);
2555                 m_commandStack->push(command);
2556             }
2557
2558         }
2559         if (m_dragItem->parentItem() && m_dragItem->parentItem() != m_selectionGroup) {
2560             // Item was resized, rebuild group;
2561             AbstractGroupItem *group = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
2562             QList <QGraphicsItem *> children = group->childItems();
2563             m_document->clipManager()->removeGroup(group);
2564             scene()->destroyItemGroup(group);
2565             for (int i = 0; i < children.count(); i++) {
2566                 children.at(i)->setSelected(true);
2567             }
2568             groupSelectedItems(false, true);
2569         }
2570         //m_document->renderer()->doRefresh();
2571     } else if (m_operationMode == RESIZEEND && m_dragItem->endPos() != m_dragItemInfo.endPos) {
2572         // resize end
2573         if (m_dragItem->type() == AVWIDGET) {
2574             ItemInfo resizeinfo = info;
2575             resizeinfo.track = m_document->tracksCount() - resizeinfo.track;
2576             bool success = m_document->renderer()->mltResizeClipEnd(resizeinfo, resizeinfo.endPos - resizeinfo.startPos);
2577             if (success) {
2578                 QUndoCommand *resizeCommand = new QUndoCommand();
2579                 resizeCommand->setText(i18n("Resize clip"));
2580
2581                 // Check if there is an automatic transition on that clip (lower track)
2582                 Transition *tr = getTransitionItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track);
2583                 if (tr && tr->isAutomatic()) {
2584                     ItemInfo trInfo = tr->info();
2585                     ItemInfo newTrInfo = trInfo;
2586                     newTrInfo.endPos = m_dragItem->endPos();
2587                     if (newTrInfo.endPos > newTrInfo.startPos) new MoveTransitionCommand(this, trInfo, newTrInfo, true, resizeCommand);
2588                 }
2589
2590                 // Check if there is an automatic transition on that clip (upper track)
2591                 tr = getTransitionItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track - 1);
2592                 if (tr && tr->isAutomatic() && (m_document->tracksCount() - tr->transitionEndTrack()) == m_dragItemInfo.track) {
2593                     ItemInfo trInfo = tr->info();
2594                     ItemInfo newTrInfo = trInfo;
2595                     newTrInfo.endPos = m_dragItem->endPos();
2596                     ClipItem * upperClip = getClipItemAtEnd(m_dragItemInfo.endPos, m_dragItemInfo.track - 1);
2597                     if ((!upperClip || !upperClip->baseClip()->isTransparent()) && newTrInfo.endPos > newTrInfo.startPos) {
2598                         new MoveTransitionCommand(this, trInfo, newTrInfo, true, resizeCommand);
2599                     }
2600                 }
2601
2602                 new ResizeClipCommand(this, m_dragItemInfo, info, false, resizeCommand);
2603                 m_commandStack->push(resizeCommand);
2604                 updateClipFade(static_cast <ClipItem *>(m_dragItem));
2605             } else {
2606                 m_dragItem->resizeEnd((int) m_dragItemInfo.endPos.frames(m_document->fps()));
2607                 emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
2608             }
2609         } else if (m_dragItem->type() == TRANSITIONWIDGET) {
2610             Transition *transition = static_cast <Transition *>(m_dragItem);
2611             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)) {
2612                 // Cannot resize transition
2613                 transition->resizeEnd((int) m_dragItemInfo.endPos.frames(m_document->fps()));
2614                 emit displayMessage(i18n("Cannot resize transition"), ErrorMessage);
2615             } else {
2616                 MoveTransitionCommand *command = new MoveTransitionCommand(this, m_dragItemInfo, info, false);
2617                 m_commandStack->push(command);
2618             }
2619         }
2620         if (m_dragItem->parentItem() && m_dragItem->parentItem() != m_selectionGroup) {
2621             // Item was resized, rebuild group;
2622             AbstractGroupItem *group = static_cast <AbstractGroupItem *>(m_dragItem->parentItem());
2623             QList <QGraphicsItem *> children = group->childItems();
2624             m_document->clipManager()->removeGroup(group);
2625             scene()->destroyItemGroup(group);
2626             for (int i = 0; i < children.count(); i++) {
2627                 children.at(i)->setSelected(true);
2628             }
2629             groupSelectedItems(false, true);
2630         }
2631         //m_document->renderer()->doRefresh();
2632     } else if (m_operationMode == FADEIN) {
2633         // resize fade in effect
2634         ClipItem * item = (ClipItem *) m_dragItem;
2635         int ix = item->hasEffect("volume", "fadein");
2636         if (ix != -1) {
2637             QDomElement oldeffect = item->effectAt(ix);
2638             int start = item->cropStart().frames(m_document->fps());
2639             int end = item->fadeIn();
2640             if (end == 0) {
2641                 slotDeleteEffect(item, oldeffect);
2642             } else {
2643                 end += start;
2644                 QDomElement effect = oldeffect.cloneNode().toElement();
2645                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
2646                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
2647                 slotUpdateClipEffect(item, effect, oldeffect, ix);
2648                 emit clipItemSelected(item, ix);
2649             }
2650         } else if (item->fadeIn() != 0 && item->hasEffect("", "fade_from_black") == -1) {
2651             QDomElement effect = MainWindow::audioEffects.getEffectByTag("volume", "fadein").cloneNode().toElement();
2652             EffectsList::setParameter(effect, "out", QString::number(item->fadeIn()));
2653             slotAddEffect(effect, m_dragItem->startPos(), m_dragItem->track());
2654         }
2655         ix = item->hasEffect("volume", "fade_from_black");
2656         if (ix != -1) {
2657             QDomElement oldeffect = item->effectAt(ix);
2658             int start = item->cropStart().frames(m_document->fps());
2659             int end = item->fadeIn();
2660             if (end == 0) {
2661                 slotDeleteEffect(item, oldeffect);
2662             } else {
2663                 end += start;
2664                 QDomElement effect = oldeffect.cloneNode().toElement();
2665                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
2666                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
2667                 slotUpdateClipEffect(item, effect, oldeffect, ix);
2668                 emit clipItemSelected(item, ix);
2669             }
2670         }
2671     } else if (m_operationMode == FADEOUT) {
2672         // resize fade in effect
2673         ClipItem * item = (ClipItem *) m_dragItem;
2674         int ix = item->hasEffect("volume", "fadeout");
2675         if (ix != -1) {
2676             QDomElement oldeffect = item->effectAt(ix);
2677             int end = (item->duration() + item->cropStart()).frames(m_document->fps());
2678             int start = item->fadeOut();
2679             if (start == 0) {
2680                 slotDeleteEffect(item, oldeffect);
2681             } else {
2682                 start = end - start;
2683                 QDomElement effect = oldeffect.cloneNode().toElement();
2684                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
2685                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
2686                 // kDebug()<<"EDIT FADE OUT : "<<start<<"x"<<end;
2687                 slotUpdateClipEffect(item, effect, oldeffect, ix);
2688                 emit clipItemSelected(item, ix);
2689             }
2690         } else if (item->fadeOut() != 0 && item->hasEffect("", "fade_to_black") == -1) {
2691             QDomElement effect = MainWindow::audioEffects.getEffectByTag("volume", "fadeout").cloneNode().toElement();
2692             EffectsList::setParameter(effect, "in", QString::number(item->fadeOut()));
2693             EffectsList::setParameter(effect, "out", QString::number(0));
2694             slotAddEffect(effect, m_dragItem->startPos(), m_dragItem->track());
2695         }
2696         ix = item->hasEffect("brightness", "fade_to_black");
2697         if (ix != -1) {
2698             QDomElement oldeffect = item->effectAt(ix);
2699             int end = (item->duration() + item->cropStart()).frames(m_document->fps());
2700             int start = item->fadeOut();
2701             if (start == 0) {
2702                 slotDeleteEffect(item, oldeffect);
2703             } else {
2704                 start = end - start;
2705                 QDomElement effect = oldeffect.cloneNode().toElement();
2706                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
2707                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
2708                 // kDebug()<<"EDIT FADE OUT : "<<start<<"x"<<end;
2709                 slotUpdateClipEffect(item, effect, oldeffect, ix);
2710                 emit clipItemSelected(item, ix);
2711             }
2712         }
2713     } else if (m_operationMode == KEYFRAME) {
2714         // update the MLT effect
2715         ClipItem * item = (ClipItem *) m_dragItem;
2716         QString previous = item->keyframes(item->selectedEffectIndex());
2717         item->updateKeyframeEffect();
2718         QString next = item->keyframes(item->selectedEffectIndex());
2719         EditKeyFrameCommand *command = new EditKeyFrameCommand(this, item->track(), item->startPos(), item->selectedEffectIndex(), previous, next, false);
2720         m_commandStack->push(command);
2721         updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
2722     }
2723     if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET && m_dragItem->isSelected()) {
2724         // A transition is selected
2725         QPoint p;
2726         ClipItem *transitionClip = getClipItemAt(m_dragItemInfo.startPos, m_dragItemInfo.track);
2727         if (transitionClip && transitionClip->baseClip()) {
2728             QString size = transitionClip->baseClip()->getProperty("frame_size");
2729             p.setX(size.section('x', 0, 0).toInt());
2730             p.setY(size.section('x', 1, 1).toInt());
2731         }
2732         emit transitionItemSelected(static_cast <Transition *>(m_dragItem), getPreviousVideoTrack(m_dragItem->track()), p);
2733     } else emit transitionItemSelected(NULL);
2734     if (m_operationMode != NONE && m_operationMode != MOVE) m_document->setModified(true);
2735     m_operationMode = NONE;
2736 }
2737
2738 void CustomTrackView::deleteClip(ItemInfo info)
2739 {
2740     ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()) + 1, info.track);
2741
2742     if (!item || m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, info.startPos) == false) {
2743         emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(info.startPos.frames(m_document->fps())), info.track), ErrorMessage);
2744         return;
2745     }
2746     if (item->isSelected()) emit clipItemSelected(NULL);
2747     item->baseClip()->removeReference();
2748     m_document->updateClip(item->baseClip()->getId());
2749
2750     /*if (item->baseClip()->isTransparent()) {
2751         // also remove automatic transition
2752         Transition *tr = getTransitionItemAt(info.startPos, info.track);
2753         if (tr && tr->isAutomatic()) {
2754             m_document->renderer()->mltDeleteTransition(tr->transitionTag(), tr->transitionEndTrack(), m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
2755             scene()->removeItem(tr);
2756             delete tr;
2757         }
2758     }*/
2759     scene()->removeItem(item);
2760     m_waitingThumbs.removeAll(item);
2761     if (m_dragItem == item) m_dragItem = NULL;
2762     delete item;
2763     item = NULL;
2764     m_document->setModified(true);
2765     m_document->renderer()->doRefresh();
2766 }
2767
2768 void CustomTrackView::deleteSelectedClips()
2769 {
2770     resetSelectionGroup();
2771     QList<QGraphicsItem *> itemList = scene()->selectedItems();
2772     if (itemList.count() == 0) {
2773         emit displayMessage(i18n("Select clip to delete"), ErrorMessage);
2774         return;
2775     }
2776     scene()->clearSelection();
2777     QUndoCommand *deleteSelected = new QUndoCommand();
2778     deleteSelected->setText(i18n("Delete selected items"));
2779     bool resetGroup = false;
2780
2781     // expand & destroy groups
2782     for (int i = 0; i < itemList.count(); i++) {
2783         if (itemList.at(i)->type() == GROUPWIDGET) {
2784             QList<QGraphicsItem *> children = itemList.at(i)->childItems();
2785             itemList += children;
2786             QList <ItemInfo> clipInfos;
2787             QList <ItemInfo> transitionInfos;
2788             GenTime currentPos = GenTime(m_cursorPos, m_document->fps());
2789             for (int j = 0; j < children.count(); j++) {
2790                 if (children.at(j)->type() == AVWIDGET) {
2791                     AbstractClipItem *clip = static_cast <AbstractClipItem *>(children.at(j));
2792                     if (!clip->isItemLocked()) clipInfos.append(clip->info());
2793                 } else if (children.at(j)->type() == TRANSITIONWIDGET) {
2794                     AbstractClipItem *clip = static_cast <AbstractClipItem *>(children.at(j));
2795                     if (!clip->isItemLocked()) transitionInfos.append(clip->info());
2796                 }
2797             }
2798             if (clipInfos.count() > 0) {
2799                 new GroupClipsCommand(this, clipInfos, transitionInfos, false, deleteSelected);
2800             }
2801         }
2802     }
2803
2804     for (int i = 0; i < itemList.count(); i++) {
2805         if (itemList.at(i)->type() == AVWIDGET) {
2806             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
2807             if (item->parentItem()) resetGroup = true;
2808             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), true, true, deleteSelected);
2809             emit clipItemSelected(NULL);
2810         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
2811             Transition *item = static_cast <Transition *>(itemList.at(i));
2812             if (item->parentItem()) resetGroup = true;
2813             new AddTransitionCommand(this, item->info(), item->transitionEndTrack(), item->toXML(), true, true, deleteSelected);
2814             emit transitionItemSelected(NULL);
2815         }
2816     }
2817
2818     m_commandStack->push(deleteSelected);
2819 }
2820
2821 void CustomTrackView::changeClipSpeed()
2822 {
2823     QList<QGraphicsItem *> itemList = scene()->selectedItems();
2824     if (itemList.count() == 0) {
2825         emit displayMessage(i18n("Select clip to change speed"), ErrorMessage);
2826         return;
2827     }
2828     QUndoCommand *changeSelected = new QUndoCommand();
2829     changeSelected->setText("Edit clip speed");
2830     int count = 0;
2831     int percent = -1;
2832     bool ok;
2833     for (int i = 0; i < itemList.count(); i++) {
2834         if (itemList.at(i)->type() == AVWIDGET) {
2835             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
2836             ItemInfo info = item->info();
2837             if (percent == -1) percent = QInputDialog::getInteger(this, i18n("Edit Clip Speed"), i18n("New speed (percents)"), item->speed() * 100, 1, 10000, 1, &ok);
2838             if (!ok) break;
2839             double speed = (double) percent / 100.0;
2840             if (item->speed() != speed && (item->clipType() == VIDEO || item->clipType() == AV)) {
2841                 count++;
2842                 new ChangeSpeedCommand(this, info, item->speed(), speed, item->clipProducer(), changeSelected);
2843             }
2844         }
2845     }
2846     if (count > 0) m_commandStack->push(changeSelected);
2847     else delete changeSelected;
2848 }
2849
2850 void CustomTrackView::doChangeClipSpeed(ItemInfo info, const double speed, const double oldspeed, const QString &id)
2851 {
2852     DocClipBase *baseclip = m_document->clipManager()->getClipById(id);
2853     ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()) + 1, info.track);
2854     if (!item) {
2855         kDebug() << "ERROR: Cannot find clip for speed change";
2856         emit displayMessage(i18n("Cannot find clip for speed change"), ErrorMessage);
2857         return;
2858     }
2859     info.track = m_document->tracksCount() - item->track();
2860     int endPos = m_document->renderer()->mltChangeClipSpeed(info, speed, oldspeed, baseclip->producer());
2861     if (endPos >= 0) {
2862         item->setSpeed(speed);
2863         item->updateRectGeometry();
2864         if (item->cropDuration().frames(m_document->fps()) > endPos)
2865             item->AbstractClipItem::resizeEnd(info.startPos.frames(m_document->fps()) + endPos, speed);
2866         m_document->setModified(true);
2867     } else emit displayMessage(i18n("Invalid clip"), ErrorMessage);
2868 }
2869
2870 void CustomTrackView::cutSelectedClips()
2871 {
2872     QList<QGraphicsItem *> itemList = scene()->selectedItems();
2873     GenTime currentPos = GenTime(m_cursorPos, m_document->fps());
2874     for (int i = 0; i < itemList.count(); i++) {
2875         if (itemList.at(i)->type() == AVWIDGET) {
2876             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
2877             if (item->parentItem() && item->parentItem() != m_selectionGroup) {
2878                 emit displayMessage(i18n("Cannot cut a clip in a group"), ErrorMessage);
2879             } else if (currentPos > item->startPos() && currentPos <  item->endPos()) {
2880                 RazorClipCommand *command = new RazorClipCommand(this, item->info(), currentPos);
2881                 m_commandStack->push(command);
2882             }
2883         }
2884     }
2885 }
2886
2887 void CustomTrackView::groupClips(bool group)
2888 {
2889     QList<QGraphicsItem *> itemList = scene()->selectedItems();
2890     QList <ItemInfo> clipInfos;
2891     QList <ItemInfo> transitionInfos;
2892     GenTime currentPos = GenTime(m_cursorPos, m_document->fps());
2893
2894     // Expand groups
2895     int max = itemList.count();
2896     for (int i = 0; i < max; i++) {
2897         if (itemList.at(i)->type() == GROUPWIDGET) {
2898             itemList += itemList.at(i)->childItems();
2899         }
2900     }
2901
2902     for (int i = 0; i < itemList.count(); i++) {
2903         if (itemList.at(i)->type() == AVWIDGET) {
2904             AbstractClipItem *clip = static_cast <AbstractClipItem *>(itemList.at(i));
2905             if (!clip->isItemLocked()) clipInfos.append(clip->info());
2906         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
2907             AbstractClipItem *clip = static_cast <AbstractClipItem *>(itemList.at(i));
2908             if (!clip->isItemLocked()) transitionInfos.append(clip->info());
2909         }
2910     }
2911     if (clipInfos.count() > 0) {
2912         GroupClipsCommand *command = new GroupClipsCommand(this, clipInfos, transitionInfos, group);
2913         m_commandStack->push(command);
2914     }
2915 }
2916
2917 void CustomTrackView::doGroupClips(QList <ItemInfo> clipInfos, QList <ItemInfo> transitionInfos, bool group)
2918 {
2919     resetSelectionGroup();
2920     m_scene->clearSelection();
2921     if (!group) {
2922         for (int i = 0; i < clipInfos.count(); i++) {
2923             ClipItem *clip = getClipItemAt(clipInfos.at(i).startPos, clipInfos.at(i).track);
2924             if (clip == NULL) continue;
2925             if (clip->parentItem() && clip->parentItem()->type() == GROUPWIDGET) {
2926                 AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(clip->parentItem());
2927                 m_document->clipManager()->removeGroup(grp);
2928                 scene()->destroyItemGroup(grp);
2929             }
2930             clip->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
2931         }
2932         for (int i = 0; i < transitionInfos.count(); i++) {
2933             Transition *tr = getTransitionItemAt(transitionInfos.at(i).startPos, transitionInfos.at(i).track);
2934             if (tr == NULL) continue;
2935             if (tr->parentItem() && tr->parentItem()->type() == GROUPWIDGET) {
2936                 AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(tr->parentItem());
2937                 m_document->clipManager()->removeGroup(grp);
2938                 scene()->destroyItemGroup(grp);
2939             }
2940             tr->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
2941         }
2942         m_document->setModified(true);
2943         return;
2944     }
2945
2946     QList <QGraphicsItemGroup *> groups;
2947     for (int i = 0; i < clipInfos.count(); i++) {
2948         ClipItem *clip = getClipItemAt(clipInfos.at(i).startPos, clipInfos.at(i).track);
2949         if (clip) {
2950             clip->setSelected(true);
2951         }
2952     }
2953     for (int i = 0; i < transitionInfos.count(); i++) {
2954         Transition *clip = getTransitionItemAt(transitionInfos.at(i).startPos, transitionInfos.at(i).track);
2955         if (clip) {
2956             clip->setSelected(true);
2957         }
2958     }
2959
2960     groupSelectedItems(false, true);
2961     m_document->setModified(true);
2962 }
2963
2964 void CustomTrackView::addClip(QDomElement xml, const QString &clipId, ItemInfo info, EffectsList effects)
2965 {
2966     DocClipBase *baseclip = m_document->clipManager()->getClipById(clipId);
2967     if (baseclip == NULL) {
2968         emit displayMessage(i18n("No clip copied"), ErrorMessage);
2969         return;
2970     }
2971     ClipItem *item = new ClipItem(baseclip, info, m_document->fps(), xml.attribute("speed", "1").toDouble());
2972     item->setEffectList(effects);
2973     if (xml.hasAttribute("audio_only")) item->setAudioOnly(true);
2974     else if (xml.hasAttribute("video_only")) item->setVideoOnly(true);
2975     scene()->addItem(item);
2976
2977     int tracknumber = m_document->tracksCount() - info.track - 1;
2978     bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
2979     if (isLocked) item->setItemLocked(true);
2980
2981     baseclip->addReference();
2982     m_document->updateClip(baseclip->getId());
2983     info.track = m_document->tracksCount() - info.track;
2984     Mlt::Producer *prod;
2985     if (item->isAudioOnly()) prod = baseclip->audioProducer(info.track);
2986     else if (item->isVideoOnly()) prod = baseclip->videoProducer();
2987     else prod = baseclip->producer(info.track);
2988     m_document->renderer()->mltInsertClip(info, xml, prod);
2989     for (int i = 0; i < item->effectsCount(); i++) {
2990         m_document->renderer()->mltAddEffect(info.track, info.startPos, item->getEffectArgs(item->effectAt(i)), false);
2991     }
2992     m_document->setModified(true);
2993     m_document->renderer()->doRefresh();
2994 }
2995
2996 void CustomTrackView::slotUpdateClip(const QString &clipId)
2997 {
2998     QList<QGraphicsItem *> list = scene()->items();
2999     ClipItem *clip = NULL;
3000     for (int i = 0; i < list.size(); ++i) {
3001         if (list.at(i)->type() == AVWIDGET) {
3002             clip = static_cast <ClipItem *>(list.at(i));
3003             if (clip->clipProducer() == clipId) {
3004                 ItemInfo info = clip->info();
3005                 info.track = m_document->tracksCount() - clip->track();
3006                 m_document->renderer()->mltUpdateClip(info, clip->xml(), clip->baseClip()->producer());
3007                 clip->refreshClip(true);
3008                 clip->update();
3009             }
3010         }
3011     }
3012 }
3013
3014 ClipItem *CustomTrackView::getClipItemAtEnd(GenTime pos, int track)
3015 {
3016     int framepos = (int)(pos.frames(m_document->fps()));
3017     QList<QGraphicsItem *> list = scene()->items(QPointF(framepos - 1, track * m_tracksHeight + m_tracksHeight / 2));
3018     ClipItem *clip = NULL;
3019     for (int i = 0; i < list.size(); i++) {
3020         if (list.at(i)->type() == AVWIDGET) {
3021             ClipItem *test = static_cast <ClipItem *>(list.at(i));
3022             if (test->endPos() == pos) clip = test;
3023             break;
3024         }
3025     }
3026     return clip;
3027 }
3028
3029 ClipItem *CustomTrackView::getClipItemAtStart(GenTime pos, int track)
3030 {
3031     QList<QGraphicsItem *> list = scene()->items(QPointF(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2));
3032     ClipItem *clip = NULL;
3033     for (int i = 0; i < list.size(); i++) {
3034         if (list.at(i)->type() == AVWIDGET) {
3035             ClipItem *test = static_cast <ClipItem *>(list.at(i));
3036             if (test->startPos() == pos) clip = test;
3037             break;
3038         }
3039     }
3040     return clip;
3041 }
3042
3043 ClipItem *CustomTrackView::getClipItemAt(int pos, int track)
3044 {
3045     QList<QGraphicsItem *> list = scene()->items(QPointF(pos, track * m_tracksHeight + m_tracksHeight / 2));
3046     ClipItem *clip = NULL;
3047     for (int i = 0; i < list.size(); i++) {
3048         if (list.at(i)->type() == AVWIDGET) {
3049             clip = static_cast <ClipItem *>(list.at(i));
3050             break;
3051         }
3052     }
3053     return clip;
3054 }
3055
3056 ClipItem *CustomTrackView::getClipItemAt(GenTime pos, int track)
3057 {
3058     int framepos = (int)(pos.frames(m_document->fps()));
3059     return getClipItemAt(framepos, track);
3060 }
3061
3062 Transition *CustomTrackView::getTransitionItemAt(int pos, int track)
3063 {
3064     QList<QGraphicsItem *> list = scene()->items(QPointF(pos, (track + 1) * m_tracksHeight));
3065     Transition *clip = NULL;
3066     for (int i = 0; i < list.size(); i++) {
3067         if (list.at(i)->type() == TRANSITIONWIDGET) {
3068             clip = static_cast <Transition *>(list.at(i));
3069             break;
3070         }
3071     }
3072     return clip;
3073 }
3074
3075 Transition *CustomTrackView::getTransitionItemAt(GenTime pos, int track)
3076 {
3077     return getTransitionItemAt(pos.frames(m_document->fps()), track);
3078 }
3079
3080 Transition *CustomTrackView::getTransitionItemAtEnd(GenTime pos, int track)
3081 {
3082     int framepos = (int)(pos.frames(m_document->fps()));
3083     QList<QGraphicsItem *> list = scene()->items(QPointF(framepos - 1, (track + 1) * m_tracksHeight));
3084     Transition *clip = NULL;
3085     for (int i = 0; i < list.size(); i++) {
3086         if (list.at(i)->type() == TRANSITIONWIDGET) {
3087             Transition *test = static_cast <Transition *>(list.at(i));
3088             if (test->endPos() == pos) clip = test;
3089             break;
3090         }
3091     }
3092     return clip;
3093 }
3094
3095 Transition *CustomTrackView::getTransitionItemAtStart(GenTime pos, int track)
3096 {
3097     QList<QGraphicsItem *> list = scene()->items(QPointF(pos.frames(m_document->fps()), (track + 1) * m_tracksHeight));
3098     Transition *clip = NULL;
3099     for (int i = 0; i < list.size(); ++i) {
3100         if (list.at(i)->type() == TRANSITIONWIDGET) {
3101             Transition *test = static_cast <Transition *>(list.at(i));
3102             if (test->startPos() == pos) clip = test;
3103             break;
3104         }
3105     }
3106     return clip;
3107 }
3108
3109 void CustomTrackView::moveClip(const ItemInfo start, const ItemInfo end)
3110 {
3111     if (m_selectionGroup) resetSelectionGroup(false);
3112     ClipItem *item = getClipItemAt((int) start.startPos.frames(m_document->fps()) + 1, start.track);
3113     if (!item) {
3114         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);
3115         kDebug() << "----------------  ERROR, CANNOT find clip to move at.. ";
3116         return;
3117     }
3118     Mlt::Producer *prod;
3119     if (item->isAudioOnly()) prod = item->baseClip()->audioProducer(end.track);
3120     else if (item->isVideoOnly()) prod = item->baseClip()->videoProducer();
3121     else prod = item->baseClip()->producer(end.track);
3122
3123     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);
3124     if (success) {
3125         bool snap = KdenliveSettings::snaptopoints();
3126         KdenliveSettings::setSnaptopoints(false);
3127         item->setPos((int) end.startPos.frames(m_document->fps()), (int)(end.track * m_tracksHeight + 1));
3128
3129         int tracknumber = m_document->tracksCount() - end.track - 1;
3130         bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
3131         m_scene->clearSelection();
3132         if (isLocked) item->setItemLocked(true);
3133         else {
3134             if (item->isItemLocked()) item->setItemLocked(false);
3135             item->setSelected(true);
3136         }
3137         if (item->baseClip()->isTransparent()) {
3138             // Also move automatic transition
3139             Transition *tr = getTransitionItemAt(start.startPos, start.track);
3140             if (tr && tr->isAutomatic()) {
3141                 tr->updateTransitionEndTrack(getPreviousVideoTrack(end.track));
3142                 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);
3143                 tr->setPos((int) end.startPos.frames(m_document->fps()), (int)(end.track * m_tracksHeight + 1));
3144             }
3145         }
3146         KdenliveSettings::setSnaptopoints(snap);
3147         m_document->setModified(true);
3148     } else {
3149         // undo last move and emit error message
3150         emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(end.startPos.frames(m_document->fps()))), ErrorMessage);
3151     }
3152     m_document->renderer()->doRefresh();
3153     //kDebug() << " // MOVED CLIP TO: " << end.startPos.frames(25) << ", ITEM START: " << item->startPos().frames(25);
3154 }
3155
3156 void CustomTrackView::moveGroup(QList <ItemInfo> startClip, QList <ItemInfo> startTransition, const GenTime offset, const int trackOffset, bool reverseMove)
3157 {
3158     // Group Items
3159     /*kDebug() << "//GRP MOVE, REVERS:" << reverseMove;
3160     kDebug() << "// GROUP MOV; OFFSET: " << offset.frames(25) << ", TK OFF: " << trackOffset;*/
3161     resetSelectionGroup();
3162     m_scene->clearSelection();
3163
3164     for (int i = 0; i < startClip.count(); i++) {
3165         if (reverseMove) {
3166             startClip[i].startPos = startClip.at(i).startPos - offset;
3167             startClip[i].track = startClip.at(i).track - trackOffset;
3168         }
3169         //kDebug()<<"//LKING FR CLIP AT:"<<startClip.at(i).startPos.frames(25)<<", TK:"<<startClip.at(i).track;
3170         ClipItem *clip = getClipItemAt(startClip.at(i).startPos, startClip.at(i).track);
3171         if (clip) {
3172             clip->setItemLocked(false);
3173             if (clip->parentItem()) clip->parentItem()->setSelected(true);
3174             else clip->setSelected(true);
3175             m_document->renderer()->mltRemoveClip(m_document->tracksCount() - startClip.at(i).track, startClip.at(i).startPos);
3176         } else kDebug() << "//MISSING CLIP AT: " << startClip.at(i).startPos.frames(25);
3177     }
3178     for (int i = 0; i < startTransition.count(); i++) {
3179         if (reverseMove) {
3180             startTransition[i].startPos = startTransition.at(i).startPos - offset;
3181             startTransition[i].track = startTransition.at(i).track - trackOffset;
3182         }
3183         Transition *tr = getTransitionItemAt(startTransition.at(i).startPos, startTransition.at(i).track);
3184         if (tr) {
3185             tr->setItemLocked(false);
3186             if (tr->parentItem()) tr->parentItem()->setSelected(true);
3187             else tr->setSelected(true);
3188             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());
3189         } else kDebug() << "//MISSING TRANSITION AT: " << startTransition.at(i).startPos.frames(25);
3190     }
3191     groupSelectedItems(true);
3192     if (m_selectionGroup) {
3193         bool snap = KdenliveSettings::snaptopoints();
3194         KdenliveSettings::setSnaptopoints(false);
3195
3196         m_selectionGroup->moveBy(offset.frames(m_document->fps()), trackOffset * (qreal) m_tracksHeight);
3197
3198         kDebug() << "%% GRP NEW POS: " << m_selectionGroup->scenePos().x();
3199
3200         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
3201         // Expand groups
3202         int max = children.count();
3203         for (int i = 0; i < max; i++) {
3204             if (children.at(i)->type() == GROUPWIDGET) {
3205                 children += children.at(i)->childItems();
3206             }
3207         }
3208         kDebug() << "// GRP MOVE; FOUND CHILDREN:" << children.count();
3209
3210         for (int i = 0; i < children.count(); i++) {
3211             // re-add items in correct place
3212             if (children.at(i)->type() != AVWIDGET && children.at(i)->type() != TRANSITIONWIDGET) continue;
3213             AbstractClipItem *item = static_cast <AbstractClipItem *>(children.at(i));
3214             item->updateItem();
3215             ItemInfo info = item->info();
3216             int tracknumber = m_document->tracksCount() - info.track - 1;
3217             bool isLocked = m_document->trackInfoAt(tracknumber).isLocked;
3218             if (isLocked) item->setItemLocked(true);
3219             else if (item->isItemLocked()) item->setItemLocked(false);
3220
3221             if (item->type() == AVWIDGET) {
3222                 ClipItem *clip = static_cast <ClipItem*>(item);
3223                 info.track = m_document->tracksCount() - info.track;
3224                 Mlt::Producer *prod;
3225                 if (clip->isAudioOnly()) prod = clip->baseClip()->audioProducer(info.track);
3226                 else if (clip->isVideoOnly()) prod = clip->baseClip()->videoProducer();
3227                 else prod = clip->baseClip()->producer(info.track);
3228                 m_document->renderer()->mltInsertClip(info, clip->xml(), prod);
3229                 kDebug() << "// inserting new clp: " << info.startPos.frames(25);
3230             } else if (item->type() == TRANSITIONWIDGET) {
3231                 Transition *tr = static_cast <Transition*>(item);
3232                 int newTrack = tr->transitionEndTrack();
3233                 kDebug() << "/// TRANSITION CURR TRK: " << newTrack;
3234                 if (!tr->forcedTrack()) {
3235                     newTrack += trackOffset;
3236                     if (newTrack < 0 || newTrack > m_document->tracksCount()) newTrack = getPreviousVideoTrack(info.track);
3237                 }
3238                 tr->updateTransitionEndTrack(newTrack);
3239                 kDebug() << "/// TRANSITION UPDATED TRK: " << newTrack;
3240                 m_document->renderer()->mltAddTransition(tr->transitionTag(), newTrack, m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
3241             }
3242         }
3243         KdenliveSettings::setSnaptopoints(snap);
3244         m_document->renderer()->doRefresh();
3245     } else kDebug() << "///////// WARNING; NO GROUP TO MOVE";
3246 }
3247
3248 void CustomTrackView::moveTransition(const ItemInfo start, const ItemInfo end)
3249 {
3250     Transition *item = getTransitionItemAt(start.startPos, start.track);
3251     if (!item) {
3252         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);
3253         kDebug() << "----------------  ERROR, CANNOT find transition to move... ";// << startPos.x() * m_scale * FRAME_SIZE + 1 << ", " << startPos.y() * m_tracksHeight + m_tracksHeight / 2;
3254         return;
3255     }
3256     //kDebug() << "----------------  Move TRANSITION FROM: " << startPos.x() << ", END:" << endPos.x() << ",TRACKS: " << oldtrack << " TO " << newtrack;
3257     bool snap = KdenliveSettings::snaptopoints();
3258     KdenliveSettings::setSnaptopoints(false);
3259     //kDebug()<<"///  RESIZE TRANS START: ("<< startPos.x()<<"x"<< startPos.y()<<") / ("<<endPos.x()<<"x"<< endPos.y()<<")";
3260     if (end.endPos - end.startPos == start.endPos - start.startPos) {
3261         // Transition was moved
3262         item->setPos((int) end.startPos.frames(m_document->fps()), (end.track) * m_tracksHeight + 1);
3263     } else if (end.endPos == start.endPos) {
3264         // Transition start resize
3265         item->resizeStart((int) end.startPos.frames(m_document->fps()));
3266     } else if (end.startPos == start.startPos) {
3267         // Transition end resize;
3268         item->resizeEnd((int) end.endPos.frames(m_document->fps()));
3269     } else {
3270         // Move & resize
3271         item->setPos((int) end.startPos.frames(m_document->fps()), (end.track) * m_tracksHeight + 1);
3272         item->resizeStart((int) end.startPos.frames(m_document->fps()));
3273         item->resizeEnd((int) end.endPos.frames(m_document->fps()));
3274     }
3275     //item->moveTransition(GenTime((int) (endPos.x() - startPos.x()), m_document->fps()));
3276     KdenliveSettings::setSnaptopoints(snap);
3277     item->updateTransitionEndTrack(getPreviousVideoTrack(end.track));
3278     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);
3279     if (m_dragItem && m_dragItem == item) {
3280         QPoint p;
3281         ClipItem *transitionClip = getClipItemAt(item->startPos(), item->track());
3282         if (transitionClip && transitionClip->baseClip()) {
3283             QString size = transitionClip->baseClip()->getProperty("frame_size");
3284             p.setX(size.section('x', 0, 0).toInt());
3285             p.setY(size.section('x', 1, 1).toInt());
3286         }
3287         emit transitionItemSelected(item, getPreviousVideoTrack(item->track()), p);
3288     }
3289     m_document->renderer()->doRefresh();
3290 }
3291
3292 void CustomTrackView::resizeClip(const ItemInfo start, const ItemInfo end)
3293 {
3294     bool resizeClipStart = true;
3295     if (start.startPos == end.startPos) resizeClipStart = false;
3296     /*if (resizeClipStart) offset = 1;
3297     else offset = -1;*/
3298     ClipItem *item = getClipItemAt((int)(start.startPos.frames(m_document->fps())), start.track);
3299     if (!item) {
3300         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);
3301         kDebug() << "----------------  ERROR, CANNOT find clip to resize at... "; // << startPos;
3302         return;
3303     }
3304     if (item->parentItem()) {
3305         // Item is part of a group, reset group
3306         resetSelectionGroup();
3307     }
3308     bool snap = KdenliveSettings::snaptopoints();
3309     KdenliveSettings::setSnaptopoints(false);
3310     if (resizeClipStart && start.startPos != end.startPos) {
3311         ItemInfo clipinfo = item->info();
3312         clipinfo.track = m_document->tracksCount() - clipinfo.track;
3313         bool success = m_document->renderer()->mltResizeClipStart(clipinfo, end.startPos - item->startPos());
3314         if (success) {
3315             kDebug() << "RESIZE CLP STRAT TO:" << end.startPos.frames(m_document->fps()) << ", OLD ST: " << start.startPos.frames(25);
3316             item->resizeStart((int) end.startPos.frames(m_document->fps()));
3317             updateClipFade(item);
3318         } else emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
3319     } else if (!resizeClipStart) {
3320         ItemInfo clipinfo = item->info();
3321         clipinfo.track = m_document->tracksCount() - clipinfo.track;
3322         bool success = m_document->renderer()->mltResizeClipEnd(clipinfo, end.endPos - clipinfo.startPos);
3323         if (success) {
3324             item->resizeEnd((int) end.endPos.frames(m_document->fps()));
3325             updateClipFade(item);
3326         } else emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
3327     }
3328     if (end.cropStart != start.cropStart) {
3329         kDebug() << "// RESIZE CROP, DIFF: " << (end.cropStart - start.cropStart).frames(25);
3330         ItemInfo clipinfo = end;
3331         clipinfo.track = m_document->tracksCount() - end.track;
3332         bool success = m_document->renderer()->mltResizeClipCrop(clipinfo, end.cropStart - start.cropStart);
3333         if (success) {
3334             item->setCropStart(end.cropStart);
3335             item->resetThumbs();
3336         }
3337     }
3338     m_document->renderer()->doRefresh();
3339     KdenliveSettings::setSnaptopoints(snap);
3340 }
3341
3342 void CustomTrackView::updateClipFade(ClipItem * item)
3343 {
3344     int end = item->fadeIn();
3345     if (end != 0) {
3346         // there is a fade in effect
3347         int effectPos = item->hasEffect("volume", "fadein");
3348         if (effectPos != -1) {
3349             QDomElement oldeffect = item->effectAt(effectPos);
3350             int start = item->cropStart().frames(m_document->fps());
3351             int max = item->cropDuration().frames(m_document->fps());
3352             if (end > max) {
3353                 // Make sure the fade effect is not longer than the clip
3354                 item->setFadeIn(max);
3355                 end = item->fadeIn();
3356             }
3357             end += start;
3358             EffectsList::setParameter(oldeffect, "in", QString::number(start));
3359             EffectsList::setParameter(oldeffect, "out", QString::number(end));
3360             if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), item->getEffectArgs(oldeffect)))
3361                 emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
3362             // if fade effect is displayed, update the effect edit widget with new clip duration
3363             if (item->isSelected() && effectPos == item->selectedEffectIndex()) emit clipItemSelected(item, effectPos);
3364         }
3365         effectPos = item->hasEffect("brightness", "fade_from_black");
3366         if (effectPos != -1) {
3367             QDomElement oldeffect = item->effectAt(effectPos);
3368             int start = item->cropStart().frames(m_document->fps());
3369             int max = item->cropDuration().frames(m_document->fps());
3370             if (end > max) {
3371                 // Make sure the fade effect is not longer than the clip
3372                 item->setFadeIn(max);
3373                 end = item->fadeIn();
3374             }
3375             end += start;
3376             EffectsList::setParameter(oldeffect, "in", QString::number(start));
3377             EffectsList::setParameter(oldeffect, "out", QString::number(end));
3378             if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), item->getEffectArgs(oldeffect)))
3379                 emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
3380             // if fade effect is displayed, update the effect edit widget with new clip duration
3381             if (item->isSelected() && effectPos == item->selectedEffectIndex()) emit clipItemSelected(item, effectPos);
3382         }
3383     }
3384     int start = item->fadeOut();
3385     if (start != 0) {
3386         // there is a fade out effect
3387         int effectPos = item->hasEffect("volume", "fadeout");
3388         if (effectPos != -1) {
3389             QDomElement oldeffect = item->effectAt(effectPos);
3390             int max = item->cropDuration().frames(m_document->fps());
3391             int end = max + item->cropStart().frames(m_document->fps());
3392             if (start > max) {
3393                 // Make sure the fade effect is not longer than the clip
3394                 item->setFadeOut(max);
3395                 start = item->fadeOut();
3396             }
3397             start = end - start;
3398             EffectsList::setParameter(oldeffect, "in", QString::number(start));
3399             EffectsList::setParameter(oldeffect, "out", QString::number(end));
3400             if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), item->getEffectArgs(oldeffect)))
3401                 emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
3402             // if fade effect is displayed, update the effect edit widget with new clip duration
3403             if (item->isSelected() && effectPos == item->selectedEffectIndex()) emit clipItemSelected(item, effectPos);
3404         }
3405         effectPos = item->hasEffect("brightness", "fade_to_black");
3406         if (effectPos != -1) {
3407             QDomElement oldeffect = item->effectAt(effectPos);
3408             int max = item->cropDuration().frames(m_document->fps());
3409             int end = max + item->cropStart().frames(m_document->fps());
3410             if (start > max) {
3411                 // Make sure the fade effect is not longer than the clip
3412                 item->setFadeOut(max);
3413                 start = item->fadeOut();
3414             }
3415             start = end - start;
3416             EffectsList::setParameter(oldeffect, "in", QString::number(start));
3417             EffectsList::setParameter(oldeffect, "out", QString::number(end));
3418             if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), item->getEffectArgs(oldeffect)))
3419                 emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
3420             // if fade effect is displayed, update the effect edit widget with new clip duration
3421             if (item->isSelected() && effectPos == item->selectedEffectIndex()) emit clipItemSelected(item, effectPos);
3422         }
3423     }
3424 }
3425
3426 double CustomTrackView::getSnapPointForPos(double pos)
3427 {
3428     return m_scene->getSnapPointForPos(pos, KdenliveSettings::snaptopoints());
3429 }
3430
3431 void CustomTrackView::updateSnapPoints(AbstractClipItem *selected, QList <GenTime> offsetList, bool skipSelectedItems)
3432 {
3433     QList <GenTime> snaps;
3434     if (selected && offsetList.isEmpty()) offsetList.append(selected->duration());
3435     QList<QGraphicsItem *> itemList = items();
3436     for (int i = 0; i < itemList.count(); i++) {
3437         if (itemList.at(i) == selected) continue;
3438         if (skipSelectedItems && itemList.at(i)->isSelected()) continue;
3439         if (itemList.at(i)->type() == AVWIDGET) {
3440             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
3441             GenTime start = item->startPos();
3442             GenTime end = item->endPos();
3443             snaps.append(start);
3444             snaps.append(end);
3445             if (!offsetList.isEmpty()) {
3446                 for (int j = 0; j < offsetList.size(); j++) {
3447                     if (start > offsetList.at(j)) snaps.append(start - offsetList.at(j));
3448                     if (end > offsetList.at(j)) snaps.append(end - offsetList.at(j));
3449                 }
3450             }
3451             // Add clip markers
3452             QList < GenTime > markers = item->snapMarkers();
3453             for (int j = 0; j < markers.size(); ++j) {
3454                 GenTime t = markers.at(j);
3455                 snaps.append(t);
3456                 if (!offsetList.isEmpty()) {
3457                     for (int k = 0; k < offsetList.size(); k++) {
3458                         if (t > offsetList.at(k)) snaps.append(t - offsetList.at(k));
3459                     }
3460                 }
3461             }
3462         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
3463             Transition *transition = static_cast <Transition*>(itemList.at(i));
3464             GenTime start = transition->startPos();
3465             GenTime end = transition->endPos();
3466             snaps.append(start);
3467             snaps.append(end);
3468             if (!offsetList.isEmpty()) {
3469                 for (int j = 0; j < offsetList.size(); j++) {
3470                     if (start > offsetList.at(j)) snaps.append(start - offsetList.at(j));
3471                     if (end > offsetList.at(j)) snaps.append(end - offsetList.at(j));
3472                 }
3473             }
3474         }
3475     }
3476
3477     // add cursor position
3478     GenTime pos = GenTime(m_cursorPos, m_document->fps());
3479     snaps.append(pos);
3480     if (!offsetList.isEmpty()) {
3481         for (int j = 0; j < offsetList.size(); j++) {
3482             snaps.append(pos - offsetList.at(j));
3483         }
3484     }
3485
3486     // add guides
3487     for (int i = 0; i < m_guides.count(); i++) {
3488         snaps.append(m_guides.at(i)->position());
3489         if (!offsetList.isEmpty()) {
3490             for (int j = 0; j < offsetList.size(); j++) {
3491                 snaps.append(m_guides.at(i)->position() - offsetList.at(j));
3492             }
3493         }
3494     }
3495
3496     qSort(snaps);
3497     m_scene->setSnapList(snaps);
3498     //for (int i = 0; i < m_snapPoints.size(); ++i)
3499     //    kDebug() << "SNAP POINT: " << m_snapPoints.at(i).frames(25);
3500 }
3501
3502 void CustomTrackView::slotSeekToPreviousSnap()
3503 {
3504     updateSnapPoints(NULL);
3505     GenTime res = m_scene->previousSnapPoint(GenTime(m_cursorPos, m_document->fps()));
3506     setCursorPos((int) res.frames(m_document->fps()));
3507     checkScrolling();
3508 }
3509
3510 void CustomTrackView::slotSeekToNextSnap()
3511 {
3512     updateSnapPoints(NULL);
3513     GenTime res = m_scene->nextSnapPoint(GenTime(m_cursorPos, m_document->fps()));
3514     setCursorPos((int) res.frames(m_document->fps()));
3515     checkScrolling();
3516 }
3517
3518 void CustomTrackView::clipStart()
3519 {
3520     ClipItem *item = getMainActiveClip();
3521     if (item != NULL) {
3522         setCursorPos((int) item->startPos().frames(m_document->fps()));
3523         checkScrolling();
3524     }
3525 }
3526
3527 void CustomTrackView::clipEnd()
3528 {
3529     ClipItem *item = getMainActiveClip();
3530     if (item != NULL) {
3531         setCursorPos((int) item->endPos().frames(m_document->fps()) - 1);
3532         checkScrolling();
3533     }
3534 }
3535
3536 void CustomTrackView::slotAddClipMarker(const QString &id, GenTime t, QString c)
3537 {
3538     QString oldcomment = m_document->clipManager()->getClipById(id)->markerComment(t);
3539     AddMarkerCommand *command = new AddMarkerCommand(this, oldcomment, c, id, t);
3540     m_commandStack->push(command);
3541 }
3542
3543 void CustomTrackView::slotDeleteClipMarker(const QString &comment, const QString &id, const GenTime &position)
3544 {
3545     AddMarkerCommand *command = new AddMarkerCommand(this, comment, QString(), id, position);
3546     m_commandStack->push(command);
3547 }
3548
3549 void CustomTrackView::slotDeleteAllClipMarkers(const QString &id)
3550 {
3551     DocClipBase *base = m_document->clipManager()->getClipById(id);
3552     QList <CommentedTime> markers = base->commentedSnapMarkers();
3553
3554     if (markers.isEmpty()) {
3555         emit displayMessage(i18n("Clip has no markers"), ErrorMessage);
3556         return;
3557     }
3558     QUndoCommand *deleteMarkers = new QUndoCommand();
3559     deleteMarkers->setText("Delete clip markers");
3560
3561     for (int i = 0; i < markers.size(); i++) {
3562         new AddMarkerCommand(this, markers.at(i).comment(), QString(), id, markers.at(i).time(), deleteMarkers);
3563     }
3564     m_commandStack->push(deleteMarkers);
3565 }
3566
3567 void CustomTrackView::addMarker(const QString &id, const GenTime &pos, const QString comment)
3568 {
3569     DocClipBase *base = m_document->clipManager()->getClipById(id);
3570     if (!comment.isEmpty()) base->addSnapMarker(pos, comment);
3571     else base->deleteSnapMarker(pos);
3572     m_document->setModified(true);
3573     viewport()->update();
3574 }
3575
3576 bool sortGuidesList(const Guide *g1 , const Guide *g2)
3577 {
3578     return (*g1).position() < (*g2).position();
3579 }
3580
3581 int CustomTrackView::hasGuide(int pos, int offset)
3582 {
3583     for (int i = 0; i < m_guides.count(); i++) {
3584         int guidePos = m_guides.at(i)->position().frames(m_document->fps());
3585         if (qAbs(guidePos - pos) <= offset) return guidePos;
3586         else if (guidePos > pos) return -1;
3587     }
3588     return -1;
3589 }
3590
3591 void CustomTrackView::editGuide(const GenTime oldPos, const GenTime pos, const QString &comment)
3592 {
3593     if (oldPos > GenTime() && pos > GenTime()) {
3594         // move guide
3595         for (int i = 0; i < m_guides.count(); i++) {
3596             if (m_guides.at(i)->position() == oldPos) {
3597                 Guide *item = m_guides.at(i);
3598                 item->updateGuide(pos, comment);
3599                 break;
3600             }
3601         }
3602     } else if (pos > GenTime()) addGuide(pos, comment);
3603     else {
3604         // remove guide
3605         bool found = false;
3606         for (int i = 0; i < m_guides.count(); i++) {
3607             if (m_guides.at(i)->position() == oldPos) {
3608                 delete m_guides.takeAt(i);
3609                 found = true;
3610                 break;
3611             }
3612         }
3613         if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
3614     }
3615     qSort(m_guides.begin(), m_guides.end(), sortGuidesList);
3616     m_document->syncGuides(m_guides);
3617 }
3618
3619 bool CustomTrackView::addGuide(const GenTime pos, const QString &comment)
3620 {
3621     for (int i = 0; i < m_guides.count(); i++) {
3622         if (m_guides.at(i)->position() == pos) {
3623             emit displayMessage(i18n("A guide already exists at position %1", m_document->timecode().getTimecodeFromFrames(pos.frames(m_document->fps()))), ErrorMessage);
3624             return false;
3625         }
3626     }
3627     Guide *g = new Guide(this, pos, comment, m_document->fps(), m_tracksHeight * m_document->tracksCount());
3628     scene()->addItem(g);
3629     m_guides.append(g);
3630     qSort(m_guides.begin(), m_guides.end(), sortGuidesList);
3631     m_document->syncGuides(m_guides);
3632     return true;
3633 }
3634
3635 void CustomTrackView::slotAddGuide()
3636 {
3637     CommentedTime marker(GenTime(m_cursorPos, m_document->fps()), i18n("Guide"));
3638     MarkerDialog d(NULL, marker, m_document->timecode(), i18n("Add Guide"), this);
3639     if (d.exec() != QDialog::Accepted) return;
3640     if (addGuide(d.newMarker().time(), d.newMarker().comment())) {
3641         EditGuideCommand *command = new EditGuideCommand(this, GenTime(), QString(), d.newMarker().time(), d.newMarker().comment(), false);
3642         m_commandStack->push(command);
3643     }
3644 }
3645
3646 void CustomTrackView::slotEditGuide(int guidePos)
3647 {
3648     GenTime pos;
3649     if (guidePos == -1) pos = GenTime(m_cursorPos, m_document->fps());
3650     else pos = GenTime(guidePos, m_document->fps());
3651     bool found = false;
3652     for (int i = 0; i < m_guides.count(); i++) {
3653         if (m_guides.at(i)->position() == pos) {
3654             slotEditGuide(m_guides.at(i)->info());
3655             found = true;
3656             break;
3657         }
3658     }
3659     if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
3660 }
3661
3662 void CustomTrackView::slotEditGuide(CommentedTime guide)
3663 {
3664     MarkerDialog d(NULL, guide, m_document->timecode(), i18n("Edit Guide"), this);
3665     if (d.exec() == QDialog::Accepted) {
3666         EditGuideCommand *command = new EditGuideCommand(this, guide.time(), guide.comment(), d.newMarker().time(), d.newMarker().comment(), true);
3667         m_commandStack->push(command);
3668     }
3669 }
3670
3671
3672 void CustomTrackView::slotEditTimeLineGuide()
3673 {
3674     if (m_dragGuide == NULL) return;
3675     CommentedTime guide = m_dragGuide->info();
3676     MarkerDialog d(NULL, guide, m_document->timecode(), i18n("Edit Guide"), this);
3677     if (d.exec() == QDialog::Accepted) {
3678         EditGuideCommand *command = new EditGuideCommand(this, guide.time(), guide.comment(), d.newMarker().time(), d.newMarker().comment(), true);
3679         m_commandStack->push(command);
3680     }
3681 }
3682
3683 void CustomTrackView::slotDeleteGuide(int guidePos)
3684 {
3685     GenTime pos;
3686     if (guidePos == -1) pos = GenTime(m_cursorPos, m_document->fps());
3687     else pos = GenTime(guidePos, m_document->fps());
3688     bool found = false;
3689     for (int i = 0; i < m_guides.count(); i++) {
3690         if (m_guides.at(i)->position() == pos) {
3691             EditGuideCommand *command = new EditGuideCommand(this, m_guides.at(i)->position(), m_guides.at(i)->label(), GenTime(), QString(), true);
3692             m_commandStack->push(command);
3693             found = true;
3694             break;
3695         }
3696     }
3697     if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
3698 }
3699
3700
3701 void CustomTrackView::slotDeleteTimeLineGuide()
3702 {
3703     if (m_dragGuide == NULL) return;
3704     EditGuideCommand *command = new EditGuideCommand(this, m_dragGuide->position(), m_dragGuide->label(), GenTime(), QString(), true);
3705     m_commandStack->push(command);
3706 }
3707
3708
3709 void CustomTrackView::slotDeleteAllGuides()
3710 {
3711     QUndoCommand *deleteAll = new QUndoCommand();
3712     deleteAll->setText("Delete all guides");
3713     for (int i = 0; i < m_guides.count(); i++) {
3714         new EditGuideCommand(this, m_guides.at(i)->position(), m_guides.at(i)->label(), GenTime(), QString(), true, deleteAll);
3715     }
3716     m_commandStack->push(deleteAll);
3717 }
3718
3719 void CustomTrackView::setTool(PROJECTTOOL tool)
3720 {
3721     m_tool = tool;
3722 }
3723
3724 void CustomTrackView::setScale(double scaleFactor, double verticalScale)
3725 {
3726     QMatrix matrix;
3727     matrix = matrix.scale(scaleFactor, verticalScale);
3728     m_scene->setScale(scaleFactor, verticalScale);
3729     kDebug() << "// Set SCALE: " << scaleFactor << "," << verticalScale;
3730     //scale(scaleFactor, 1);
3731     m_animationTimer->stop();
3732     delete m_visualTip;
3733     m_visualTip = NULL;
3734     delete m_animation;
3735     m_animation = NULL;
3736
3737     //setSceneRect(0, 0, m_projectDuration + 100 * scaleFactor, sceneRect().height());
3738     setMatrix(matrix);
3739     int diff = sceneRect().width() - m_projectDuration;
3740     if (diff * matrix.m11() < 50) {
3741         if (matrix.m11() < 0.4) setSceneRect(0, 0, (m_projectDuration + 100 / matrix.m11()), sceneRect().height());
3742         else setSceneRect(0, 0, (m_projectDuration + 300), sceneRect().height());
3743     }
3744
3745     centerOn(QPointF(cursorPos(), m_tracksHeight));
3746     //verticalScrollBar()->setValue(vert);*/
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) {
4112                 // Check if we have a cached thumbnail
4113                 if (item->clipType() == IMAGE || item->clipType() == TEXT || item->clipType() == AUDIO) {
4114                     QString thumb = thumbBase + item->baseClip()->getClipHash() + "_0.png";
4115                     if (QFile::exists(thumb)) {
4116                         QPixmap pix(thumb);
4117                         item->slotSetStartThumb(pix);
4118                         item->slotSetEndThumb(pix);
4119                     }
4120                 } else {
4121                     QString startThumb = thumbBase + item->baseClip()->getClipHash() + '_';
4122                     QString endThumb = startThumb;
4123                     startThumb.append(QString::number(item->cropStart().frames(m_document->fps())) + ".png");
4124                     endThumb.append(QString::number((item->cropStart() + item->cropDuration()).frames(m_document->fps()) - 1) + ".png");
4125                     if (QFile::exists(startThumb)) {
4126                         QPixmap pix(startThumb);
4127                         item->slotSetStartThumb(pix);
4128                     }
4129                     if (QFile::exists(endThumb)) {
4130                         QPixmap pix(endThumb);
4131                         item->slotSetEndThumb(pix);
4132                     }
4133                 }
4134             }
4135             item->refreshClip(false);
4136             qApp->processEvents();
4137         }
4138     }
4139     viewport()->update();
4140 }
4141
4142 void CustomTrackView::saveThumbnails()
4143 {
4144     QList<QGraphicsItem *> itemList = items();
4145     ClipItem *item;
4146     QString thumbBase = m_document->projectFolder().path() + "/thumbs/";
4147     for (int i = 0; i < itemList.count(); i++) {
4148         if (itemList.at(i)->type() == AVWIDGET) {
4149             item = static_cast <ClipItem *>(itemList.at(i));
4150             if (item->clipType() != COLOR) {
4151                 // Check if we have a cached thumbnail
4152                 if (item->clipType() == IMAGE || item->clipType() == TEXT || item->clipType() == AUDIO) {
4153                     QString thumb = thumbBase + item->baseClip()->getClipHash() + "_0.png";
4154                     if (!QFile::exists(thumb)) {
4155                         QPixmap pix(item->startThumb());
4156                         pix.save(thumb);
4157                     }
4158                 } else {
4159                     QString startThumb = thumbBase + item->baseClip()->getClipHash() + '_';
4160                     QString endThumb = startThumb;
4161                     startThumb.append(QString::number(item->cropStart().frames(m_document->fps())) + ".png");
4162                     endThumb.append(QString::number((item->cropStart() + item->cropDuration()).frames(m_document->fps()) - 1) + ".png");
4163                     if (!QFile::exists(startThumb)) {
4164                         QPixmap pix(item->startThumb());
4165                         pix.save(startThumb);
4166                     }
4167                     if (!QFile::exists(endThumb)) {
4168                         QPixmap pix(item->endThumb());
4169                         pix.save(endThumb);
4170                     }
4171                 }
4172             }
4173         }
4174     }
4175 }
4176
4177
4178 void CustomTrackView::slotInsertTrack(int ix)
4179 {
4180     kDebug() << "// INSERTING TRK: " << ix;
4181     QDialog d(parentWidget());
4182     Ui::AddTrack_UI view;
4183     view.setupUi(&d);
4184     view.track_nb->setMaximum(m_document->tracksCount() - 1);
4185     view.track_nb->setValue(ix);
4186     d.setWindowTitle(i18n("Insert Track"));
4187
4188     if (d.exec() == QDialog::Accepted) {
4189         ix = view.track_nb->value();
4190         if (view.before_select->currentIndex() == 1) {
4191             ix++;
4192         }
4193         TrackInfo info;
4194         if (view.video_track->isChecked()) {
4195             info.type = VIDEOTRACK;
4196             info.isMute = false;
4197             info.isBlind = false;
4198             info.isLocked = false;
4199         } else {
4200             info.type = AUDIOTRACK;
4201             info.isMute = false;
4202             info.isBlind = true;
4203             info.isLocked = false;
4204         }
4205         AddTrackCommand *addTrack = new AddTrackCommand(this, ix, info, true);
4206         m_commandStack->push(addTrack);
4207         m_document->setModified(true);
4208     }
4209 }
4210
4211 void CustomTrackView::slotDeleteTrack(int ix)
4212 {
4213     bool ok;
4214     ix = QInputDialog::getInteger(this, i18n("Remove Track"), i18n("Track"), ix, 0, m_document->tracksCount() - 1, 1, &ok);
4215     if (ok) {
4216         TrackInfo info = m_document->trackInfoAt(m_document->tracksCount() - ix - 1);
4217         deleteTimelineTrack(ix, info);
4218         m_document->setModified(true);
4219         /*AddTrackCommand* command = new AddTrackCommand(this, ix, info, false);
4220         m_commandStack->push(command);*/
4221     }
4222 }
4223
4224 void CustomTrackView::slotChangeTrack(int ix)
4225 {
4226     QDialog d(parentWidget());
4227     Ui::AddTrack_UI view;
4228     view.setupUi(&d);
4229     view.label->setText(i18n("Change track"));
4230     view.before_select->setHidden(true);
4231     view.track_nb->setMaximum(m_document->tracksCount() - 1);
4232     view.track_nb->setValue(ix);
4233     d.setWindowTitle(i18n("Change Track Type"));
4234
4235     if (m_document->trackInfoAt(m_document->tracksCount() - ix - 1).type == VIDEOTRACK)
4236         view.video_track->setChecked(true);
4237     else
4238         view.audio_track->setChecked(true);
4239
4240     if (d.exec() == QDialog::Accepted) {
4241         TrackInfo info;
4242         info.isLocked = false;
4243         info.isMute = false;
4244         ix = view.track_nb->value();
4245
4246         if (view.video_track->isChecked()) {
4247             info.type = VIDEOTRACK;
4248             info.isBlind = false;
4249         } else {
4250             info.type = AUDIOTRACK;
4251             info.isBlind = true;
4252         }
4253         changeTimelineTrack(ix, info);
4254         m_document->setModified(true);
4255     }
4256 }
4257
4258
4259 void CustomTrackView::deleteTimelineTrack(int ix, TrackInfo trackinfo)
4260 {
4261     double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
4262     QRectF r(0, startY, sceneRect().width(), m_tracksHeight / 2 - 1);
4263     QList<QGraphicsItem *> selection = m_scene->items(r);
4264     QUndoCommand *deleteTrack = new QUndoCommand();
4265     deleteTrack->setText("Delete track");
4266
4267     // Delete all clips in selected track
4268     for (int i = 0; i < selection.count(); i++) {
4269         if (selection.at(i)->type() == AVWIDGET) {
4270             ClipItem *item =  static_cast <ClipItem *>(selection.at(i));
4271             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), false, true, deleteTrack);
4272             m_scene->removeItem(item);
4273             delete item;
4274             item = NULL;
4275         } else if (selection.at(i)->type() == TRANSITIONWIDGET) {
4276             Transition *item =  static_cast <Transition *>(selection.at(i));
4277             new AddTransitionCommand(this, item->info(), item->transitionEndTrack(), item->toXML(), true, false, deleteTrack);
4278             m_scene->removeItem(item);
4279             delete item;
4280             item = NULL;
4281         }
4282     }
4283
4284     selection = m_scene->items();
4285     new AddTrackCommand(this, ix, trackinfo, false, deleteTrack);
4286     m_commandStack->push(deleteTrack);
4287 }
4288
4289 void CustomTrackView::changeTimelineTrack(int ix, TrackInfo trackinfo)
4290 {
4291     TrackInfo oldinfo = m_document->trackInfoAt(m_document->tracksCount() - ix - 1);
4292     ChangeTrackCommand *changeTrack = new ChangeTrackCommand(this, ix, oldinfo, trackinfo);
4293     m_commandStack->push(changeTrack);
4294 }
4295
4296 void CustomTrackView::autoTransition()
4297 {
4298     QList<QGraphicsItem *> itemList = scene()->selectedItems();
4299     if (itemList.count() != 1 || itemList.at(0)->type() != TRANSITIONWIDGET) {
4300         emit displayMessage(i18n("You must select one transition for this action"), ErrorMessage);
4301         return;
4302     }
4303     Transition *tr = static_cast <Transition*>(itemList.at(0));
4304     tr->setAutomatic(!tr->isAutomatic());
4305     QDomElement transition = tr->toXML();
4306     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);
4307 }
4308
4309
4310 QStringList CustomTrackView::getLadspaParams(QDomElement effect) const
4311 {
4312     QStringList result;
4313     QDomNodeList params = effect.elementsByTagName("parameter");
4314     for (int i = 0; i < params.count(); i++) {
4315         QDomElement e = params.item(i).toElement();
4316         if (!e.isNull() && e.attribute("type") == "constant") {
4317             if (e.hasAttribute("factor")) {
4318                 double factor = e.attribute("factor").toDouble();
4319                 double value = e.attribute("value").toDouble();
4320                 value = value / factor;
4321                 result.append(QString::number(value));
4322             } else result.append(e.attribute("value"));
4323         }
4324     }
4325     return result;
4326 }
4327
4328 void CustomTrackView::clipNameChanged(const QString id, const QString name)
4329 {
4330     QList<QGraphicsItem *> list = scene()->items();
4331     ClipItem *clip = NULL;
4332     for (int i = 0; i < list.size(); ++i) {
4333         if (list.at(i)->type() == AVWIDGET) {
4334             clip = static_cast <ClipItem *>(list.at(i));
4335             if (clip->clipProducer() == id) {
4336                 clip->setClipName(name);
4337             }
4338         }
4339     }
4340     viewport()->update();
4341 }
4342
4343 void CustomTrackView::getClipAvailableSpace(AbstractClipItem *item, GenTime &minimum, GenTime &maximum)
4344 {
4345     minimum = GenTime();
4346     maximum = GenTime();
4347     QList<QGraphicsItem *> selection;
4348     selection = m_scene->items(0, item->track() * m_tracksHeight + m_tracksHeight / 2, sceneRect().width(), 2);
4349     selection.removeAll(item);
4350     for (int i = 0; i < selection.count(); i++) {
4351         AbstractClipItem *clip = static_cast <AbstractClipItem *>(selection.at(i));
4352         if (clip && clip->type() == AVWIDGET) {
4353             if (clip->endPos() <= item->startPos() && clip->endPos() > minimum) minimum = clip->endPos();
4354             if (clip->startPos() > item->startPos() && (clip->startPos() < maximum || maximum == GenTime())) maximum = clip->startPos();
4355         }
4356     }
4357 }
4358
4359 void CustomTrackView::getTransitionAvailableSpace(AbstractClipItem *item, GenTime &minimum, GenTime &maximum)
4360 {
4361     minimum = GenTime();
4362     maximum = GenTime();
4363     QList<QGraphicsItem *> selection;
4364     selection = m_scene->items(0, (item->track() + 1) * m_tracksHeight, sceneRect().width(), 2);
4365     selection.removeAll(item);
4366     for (int i = 0; i < selection.count(); i++) {
4367         AbstractClipItem *clip = static_cast <AbstractClipItem *>(selection.at(i));
4368         if (clip && clip->type() == TRANSITIONWIDGET) {
4369             if (clip->endPos() <= item->startPos() && clip->endPos() > minimum) minimum = clip->endPos();
4370             if (clip->startPos() > item->startPos() && (clip->startPos() < maximum || maximum == GenTime())) maximum = clip->startPos();
4371         }
4372     }
4373 }
4374
4375
4376 void CustomTrackView::loadGroups(const QDomNodeList groups)
4377 {
4378     for (int i = 0; i < groups.count(); i++) {
4379         QDomNodeList children = groups.at(i).childNodes();
4380         scene()->clearSelection();
4381         for (int nodeindex = 0; nodeindex < children.count(); nodeindex++) {
4382             QDomNode n = children.item(nodeindex);
4383             QDomElement elem = n.toElement();
4384             int pos = elem.attribute("position").toInt();
4385             int track = elem.attribute("track").toInt();
4386             if (elem.tagName() == "clipitem") {
4387                 ClipItem *clip = getClipItemAt(pos, track); //m_document->tracksCount() - transitiontrack);
4388                 if (clip) clip->setSelected(true);
4389             } else {
4390                 Transition *clip = getTransitionItemAt(pos, track); //m_document->tracksCount() - transitiontrack);
4391                 if (clip) clip->setSelected(true);
4392             }
4393         }
4394         groupSelectedItems(false, true);
4395     }
4396 }
4397
4398 void CustomTrackView::splitAudio()
4399 {
4400     resetSelectionGroup();
4401     QList<QGraphicsItem *> selection = scene()->selectedItems();
4402     if (selection.isEmpty()) {
4403         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
4404         return;
4405     }
4406     QUndoCommand *splitCommand = new QUndoCommand();
4407     splitCommand->setText(i18n("Split audio"));
4408     for (int i = 0; i < selection.count(); i++) {
4409         if (selection.at(i)->type() == AVWIDGET) {
4410             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
4411             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
4412                 if (clip->parentItem()) {
4413                     emit displayMessage(i18n("Cannot split audio of grouped clips"), ErrorMessage);
4414                 } else {
4415                     new SplitAudioCommand(this, clip->track(), clip->startPos(), splitCommand);
4416                 }
4417             }
4418         }
4419     }
4420     m_commandStack->push(splitCommand);
4421 }
4422
4423 void CustomTrackView::doSplitAudio(const GenTime &pos, int track, bool split)
4424 {
4425     ClipItem *clip = getClipItemAt(pos, track);
4426     if (clip == NULL) {
4427         kDebug() << "// Cannot find clip to split!!!";
4428         return;
4429     }
4430     if (split) {
4431         int start = pos.frames(m_document->fps());
4432         int freetrack = m_document->tracksCount() - track - 1;
4433         for (; freetrack > 0; freetrack--) {
4434             kDebug() << "// CHK DOC TRK:" << freetrack << ", DUR:" << m_document->renderer()->mltTrackDuration(freetrack);
4435             if (m_document->trackInfoAt(freetrack - 1).type == AUDIOTRACK) {
4436                 kDebug() << "// CHK DOC TRK:" << freetrack << ", DUR:" << m_document->renderer()->mltTrackDuration(freetrack);
4437                 if (m_document->renderer()->mltTrackDuration(freetrack) < start || m_document->renderer()->mltGetSpaceLength(pos, freetrack, false) >= clip->duration().frames(m_document->fps())) {
4438                     kDebug() << "FOUND SPACE ON TRK: " << freetrack;
4439                     break;
4440                 }
4441             }
4442         }
4443         kDebug() << "GOT TRK: " << track;
4444         if (freetrack == 0) {
4445             emit displayMessage(i18n("No empty space to put clip audio"), ErrorMessage);
4446         } else {
4447             ItemInfo info;
4448             info.startPos = clip->startPos();
4449             info.endPos = clip->endPos();
4450             info.cropStart = clip->cropStart();
4451             info.track = m_document->tracksCount() - freetrack;
4452             addClip(clip->xml(), clip->clipProducer(), info, clip->effectList());
4453             scene()->clearSelection();
4454             clip->setSelected(true);
4455             ClipItem *audioClip = getClipItemAt(start, info.track);
4456             if (audioClip) {
4457                 clip->setVideoOnly(true);
4458                 m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - track, start, clip->baseClip()->videoProducer());
4459                 m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - info.track, start, clip->baseClip()->audioProducer(info.track));
4460                 audioClip->setSelected(true);
4461                 audioClip->setAudioOnly(true);
4462                 groupSelectedItems(false, true);
4463             }
4464         }
4465     } else {
4466         // unsplit clip: remove audio part and change video part to normal clip
4467         if (clip->parentItem() == NULL || clip->parentItem()->type() != GROUPWIDGET) {
4468             kDebug() << "//CANNOT FIND CLP GRP";
4469             return;
4470         }
4471         AbstractGroupItem *grp = static_cast <AbstractGroupItem *>(clip->parentItem());
4472         QList<QGraphicsItem *> children = grp->childItems();
4473         if (children.count() != 2) {
4474             kDebug() << "//SOMETHING IS WRONG WITH CLP GRP";
4475             return;
4476         }
4477         for (int i = 0; i < children.count(); i++) {
4478             if (children.at(i) != clip) {
4479                 ClipItem *clp = static_cast <ClipItem *>(children.at(i));
4480                 ItemInfo info = clip->info();
4481                 deleteClip(clp->info());
4482                 clip->setVideoOnly(false);
4483                 m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - info.track, info.startPos.frames(m_document->fps()), clip->baseClip()->producer(info.track));
4484                 break;
4485             }
4486         }
4487         clip->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
4488         m_document->clipManager()->removeGroup(grp);
4489         scene()->destroyItemGroup(grp);
4490     }
4491 }
4492
4493 void CustomTrackView::setVideoOnly()
4494 {
4495     resetSelectionGroup();
4496     QList<QGraphicsItem *> selection = scene()->selectedItems();
4497     if (selection.isEmpty()) {
4498         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
4499         return;
4500     }
4501     QUndoCommand *videoCommand = new QUndoCommand();
4502     videoCommand->setText(i18n("Video only"));
4503     for (int i = 0; i < selection.count(); i++) {
4504         if (selection.at(i)->type() == AVWIDGET) {
4505             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
4506             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
4507                 if (clip->parentItem()) {
4508                     emit displayMessage(i18n("Cannot change grouped clips"), ErrorMessage);
4509                 } else {
4510                     new ChangeClipTypeCommand(this, clip->track(), clip->startPos(), true, false, clip->isVideoOnly(), clip->isAudioOnly(), videoCommand);
4511                 }
4512             }
4513         }
4514     }
4515     m_commandStack->push(videoCommand);
4516 }
4517
4518 void CustomTrackView::setAudioOnly()
4519 {
4520     resetSelectionGroup();
4521     QList<QGraphicsItem *> selection = scene()->selectedItems();
4522     if (selection.isEmpty()) {
4523         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
4524         return;
4525     }
4526     QUndoCommand *videoCommand = new QUndoCommand();
4527     videoCommand->setText(i18n("Audio only"));
4528     for (int i = 0; i < selection.count(); i++) {
4529         if (selection.at(i)->type() == AVWIDGET) {
4530             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
4531             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
4532                 if (clip->parentItem()) {
4533                     emit displayMessage(i18n("Cannot change grouped clips"), ErrorMessage);
4534                 } else {
4535                     new ChangeClipTypeCommand(this, clip->track(), clip->startPos(), false, true, clip->isVideoOnly(), clip->isAudioOnly(), videoCommand);
4536                 }
4537             }
4538         }
4539     }
4540     m_commandStack->push(videoCommand);
4541 }
4542
4543 void CustomTrackView::setAudioAndVideo()
4544 {
4545     resetSelectionGroup();
4546     QList<QGraphicsItem *> selection = scene()->selectedItems();
4547     if (selection.isEmpty()) {
4548         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
4549         return;
4550     }
4551     QUndoCommand *videoCommand = new QUndoCommand();
4552     videoCommand->setText(i18n("Audio and Video"));
4553     for (int i = 0; i < selection.count(); i++) {
4554         if (selection.at(i)->type() == AVWIDGET) {
4555             ClipItem *clip = static_cast <ClipItem *>(selection.at(i));
4556             if (clip->clipType() == AV || clip->clipType() == PLAYLIST) {
4557                 if (clip->parentItem()) {
4558                     emit displayMessage(i18n("Cannot change grouped clips"), ErrorMessage);
4559                 } else {
4560                     new ChangeClipTypeCommand(this, clip->track(), clip->startPos(), false, false, clip->isVideoOnly(), clip->isAudioOnly(), videoCommand);
4561                 }
4562             }
4563         }
4564     }
4565     m_commandStack->push(videoCommand);
4566 }
4567
4568 void CustomTrackView::doChangeClipType(const GenTime &pos, int track, bool videoOnly, bool audioOnly)
4569 {
4570     ClipItem *clip = getClipItemAt(pos, track);
4571     if (clip == NULL) {
4572         kDebug() << "// Cannot find clip to split!!!";
4573         return;
4574     }
4575     if (videoOnly) {
4576         int start = pos.frames(m_document->fps());
4577         clip->setVideoOnly(true);
4578         clip->setAudioOnly(false);
4579         m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - track, start, clip->baseClip()->videoProducer());
4580     } else if (audioOnly) {
4581         int start = pos.frames(m_document->fps());
4582         clip->setAudioOnly(true);
4583         clip->setVideoOnly(false);
4584         m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - track, start, clip->baseClip()->audioProducer(track));
4585     } else {
4586         int start = pos.frames(m_document->fps());
4587         clip->setAudioOnly(false);
4588         clip->setVideoOnly(false);
4589         m_document->renderer()->mltUpdateClipProducer(m_document->tracksCount() - track, start, clip->baseClip()->producer(track));
4590     }
4591     clip->update();
4592     m_document->setModified(true);
4593 }
4594
4595 void CustomTrackView::updateClipTypeActions(ClipItem *clip)
4596 {
4597     if (clip == NULL || (clip->clipType() != AV && clip->clipType() != PLAYLIST)) {
4598         m_clipTypeGroup->setEnabled(false);
4599     } else {
4600         m_clipTypeGroup->setEnabled(true);
4601         QList <QAction *> actions = m_clipTypeGroup->actions();
4602         QString lookup;
4603         if (clip->isAudioOnly()) lookup = "clip_audio_only";
4604         else if (clip->isVideoOnly()) lookup = "clip_video_only";
4605         else  lookup = "clip_audio_and_video";
4606         for (int i = 0; i < actions.count(); i++) {
4607             if (actions.at(i)->data().toString() == lookup) {
4608                 actions.at(i)->setChecked(true);
4609                 break;
4610             }
4611         }
4612     }
4613 }
4614
4615 void CustomTrackView::reloadTransitionLumas()
4616 {
4617     QString lumaNames;
4618     QString lumaFiles;
4619     QDomElement lumaTransition = MainWindow::transitions.getEffectByTag("luma", "luma");
4620     QDomNodeList params = lumaTransition.elementsByTagName("parameter");
4621     for (int i = 0; i < params.count(); i++) {
4622         QDomElement e = params.item(i).toElement();
4623         if (e.attribute("tag") == "resource") {
4624             lumaNames = e.attribute("paramlistdisplay");
4625             lumaFiles = e.attribute("paramlist");
4626             break;
4627         }
4628     }
4629
4630     QList<QGraphicsItem *> itemList = items();
4631     Transition *transitionitem;
4632     QDomElement transitionXml;
4633     for (int i = 0; i < itemList.count(); i++) {
4634         if (itemList.at(i)->type() == TRANSITIONWIDGET) {
4635             transitionitem = static_cast <Transition*>(itemList.at(i));
4636             transitionXml = transitionitem->toXML();
4637             if (transitionXml.attribute("id") == "luma" && transitionXml.attribute("tag") == "luma") {
4638                 QDomNodeList params = transitionXml.elementsByTagName("parameter");
4639                 for (int i = 0; i < params.count(); i++) {
4640                     QDomElement e = params.item(i).toElement();
4641                     if (e.attribute("tag") == "resource") {
4642                         e.setAttribute("paramlistdisplay", lumaNames);
4643                         e.setAttribute("paramlist", lumaFiles);
4644                         break;
4645                     }
4646                 }
4647             }
4648             if (transitionXml.attribute("id") == "composite" && transitionXml.attribute("tag") == "composite") {
4649                 QDomNodeList params = transitionXml.elementsByTagName("parameter");
4650                 for (int i = 0; i < params.count(); i++) {
4651                     QDomElement e = params.item(i).toElement();
4652                     if (e.attribute("tag") == "luma") {
4653                         e.setAttribute("paramlistdisplay", lumaNames);
4654                         e.setAttribute("paramlist", lumaFiles);
4655                         break;
4656                     }
4657                 }
4658             }
4659         }
4660     }
4661     emit transitionItemSelected(NULL);
4662 }
4663
4664 #include "customtrackview.moc"