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