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