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