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