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