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