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