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