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