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