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