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