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