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