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