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