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