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