]> git.sesse.net Git - kdenlive/blob - src/customtrackview.cpp
New: Change track type (audio / video)
[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 #include <QMouseEvent>
21 #include <QStylePainter>
22 #include <QGraphicsItem>
23 #include <QDomDocument>
24 #include <QScrollBar>
25 #include <QApplication>
26 #include <QInputDialog>
27
28 #include <KDebug>
29 #include <KLocale>
30 #include <KUrl>
31 #include <KIcon>
32 #include <KCursor>
33
34 #include "customtrackview.h"
35 #include "customtrackscene.h"
36 #include "docclipbase.h"
37 #include "clipitem.h"
38 #include "definitions.h"
39 #include "moveclipcommand.h"
40 #include "movetransitioncommand.h"
41 #include "resizeclipcommand.h"
42 #include "editguidecommand.h"
43 #include "addtimelineclipcommand.h"
44 #include "addeffectcommand.h"
45 #include "editeffectcommand.h"
46 #include "moveeffectcommand.h"
47 #include "addtransitioncommand.h"
48 #include "edittransitioncommand.h"
49 #include "editkeyframecommand.h"
50 #include "changespeedcommand.h"
51 #include "addmarkercommand.h"
52 #include "razorclipcommand.h"
53 #include "kdenlivesettings.h"
54 #include "transition.h"
55 #include "clipitem.h"
56 #include "customtrackview.h"
57 #include "clipmanager.h"
58 #include "renderer.h"
59 #include "markerdialog.h"
60 #include "mainwindow.h"
61 #include "ui_keyframedialog_ui.h"
62 #include "clipdurationdialog.h"
63 #include "abstractgroupitem.h"
64 #include "insertspacecommand.h"
65 #include "spacerdialog.h"
66 #include "addtrackcommand.h"
67 #include "changetrackcommand.h"
68 #include "ui_addtrack_ui.h"
69
70 //TODO:
71 // disable animation if user asked it in KDE's global settings
72 // http://lists.kde.org/?l=kde-commits&m=120398724717624&w=2
73 // needs something like below (taken from dolphin)
74 // #include <kglobalsettings.h>
75 // const bool animate = KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects;
76 // const int duration = animate ? 1500 : 1;
77
78 CustomTrackView::CustomTrackView(KdenliveDoc *doc, CustomTrackScene* projectscene, QWidget *parent)
79         : QGraphicsView(projectscene, parent), m_scene(projectscene), m_cursorPos(0), m_cursorLine(NULL), m_operationMode(NONE), m_dragItem(NULL), m_visualTip(NULL), m_moveOpMode(NONE), m_animation(NULL), m_projectDuration(0), m_clickPoint(QPoint()), m_document(doc), m_autoScroll(KdenliveSettings::autoscroll()), m_tracksHeight(KdenliveSettings::trackheight()), m_tool(SELECTTOOL), m_dragGuide(NULL), m_findIndex(0), m_menuPosition(QPoint()), m_blockRefresh(false), m_selectionGroup(NULL), m_selectedTrack(0), m_copiedItems(QList<AbstractClipItem *> ()), m_scrollOffset(0) {
80     if (doc) m_commandStack = doc->commandStack();
81     else m_commandStack == NULL;
82     setMouseTracking(true);
83     setAcceptDrops(true);
84     m_animationTimer = new QTimeLine(800);
85     m_animationTimer->setFrameRange(0, 5);
86     m_animationTimer->setUpdateInterval(100);
87     m_animationTimer->setLoopCount(0);
88     m_tipColor = QColor(0, 192, 0, 200);
89     QColor border = QColor(255, 255, 255, 100);
90     m_tipPen.setColor(border);
91     m_tipPen.setWidth(3);
92     setContentsMargins(0, 0, 0, 0);
93     const int maxWidth = m_tracksHeight * m_document->tracksCount();
94     setSceneRect(0, 0, sceneRect().width(), maxWidth);
95     verticalScrollBar()->setMaximum(maxWidth);
96     m_cursorLine = projectscene->addLine(0, 0, 0, maxWidth);
97     m_cursorLine->setZValue(1000);
98
99     KIcon razorIcon("edit-cut");
100     m_razorCursor = QCursor(razorIcon.pixmap(22, 22));
101
102     KIcon spacerIcon("kdenlive-spacer-tool");
103     m_spacerCursor = QCursor(spacerIcon.pixmap(22, 22));
104     verticalScrollBar()->setTracking(true);
105     connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotRefreshGuides()));
106     connect(&m_scrollTimer, SIGNAL(timeout()), this, SLOT(slotCheckMouseScrolling()));
107     m_scrollTimer.setInterval(100);
108     m_scrollTimer.setSingleShot(true);
109 }
110
111 CustomTrackView::~CustomTrackView() {
112     qDeleteAll(m_guides);
113 }
114
115 void CustomTrackView::setDocumentModified() {
116     m_document->setModified(true);
117 }
118
119 void CustomTrackView::setContextMenu(QMenu *timeline, QMenu *clip, QMenu *transition) {
120     m_timelineContextMenu = timeline;
121     m_timelineContextClipMenu = clip;
122     m_timelineContextTransitionMenu = transition;
123 }
124
125 void CustomTrackView::checkAutoScroll() {
126     m_autoScroll = KdenliveSettings::autoscroll();
127 }
128
129 /*sQList <TrackInfo> CustomTrackView::tracksList() const {
130     return m_scene->m_tracksList;
131 }*/
132
133 void CustomTrackView::checkTrackHeight() {
134     if (m_tracksHeight == KdenliveSettings::trackheight()) return;
135     m_tracksHeight = KdenliveSettings::trackheight();
136     emit trackHeightChanged();
137     QList<QGraphicsItem *> itemList = items();
138     ClipItem *item;
139     Transition *transitionitem;
140     for (int i = 0; i < itemList.count(); i++) {
141         if (itemList.at(i)->type() == AVWIDGET) {
142             item = (ClipItem*) itemList.at(i);
143             item->setRect(0, 0, item->rect().width(), m_tracksHeight - 1);
144             item->setPos((qreal) item->startPos().frames(m_document->fps()), (qreal) item->track() * m_tracksHeight + 1);
145             item->resetThumbs();
146         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
147             transitionitem = (Transition*) itemList.at(i);
148             transitionitem->setRect(0, 0, transitionitem->rect().width(), m_tracksHeight / 3 * 2 - 1);
149             transitionitem->setPos((qreal) transitionitem->startPos().frames(m_document->fps()), (qreal) transitionitem->track() * m_tracksHeight + m_tracksHeight / 3 * 2);
150         }
151     }
152     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), m_tracksHeight * m_document->tracksCount());
153
154     for (int i = 0; i < m_guides.count(); i++) {
155         QLineF l = m_guides.at(i)->line();
156         l.setP2(QPointF(l.x2(), m_tracksHeight * m_document->tracksCount()));
157         m_guides.at(i)->setLine(l);
158     }
159
160     setSceneRect(0, 0, sceneRect().width(), m_tracksHeight * m_document->tracksCount());
161     verticalScrollBar()->setMaximum(m_tracksHeight * m_document->tracksCount());
162     update();
163 }
164
165 // virtual
166 void CustomTrackView::resizeEvent(QResizeEvent * event) {
167     QGraphicsView::resizeEvent(event);
168 }
169
170 // virtual
171 /** Zoom or move viewport on mousewheel
172  *
173  * If mousewheel+Ctrl, zooms in/out on the timeline.
174  *
175  * With Ctrl, moves viewport towards end of timeline if down/back,
176  * opposite on up/forward.
177  *
178  * See also http://www.kdenlive.org/mantis/view.php?id=265 */
179 void CustomTrackView::wheelEvent(QWheelEvent * e) {
180     if (e->modifiers() == Qt::ControlModifier) {
181         if (e->delta() > 0) emit zoomIn();
182         else emit zoomOut();
183     } else {
184         if (e->delta() <= 0) horizontalScrollBar()->setValue(horizontalScrollBar()->value() + horizontalScrollBar()->singleStep());
185         else  horizontalScrollBar()->setValue(horizontalScrollBar()->value() - horizontalScrollBar()->singleStep());
186     }
187 }
188
189 int CustomTrackView::getPreviousVideoTrack(int track) {
190     track = m_document->tracksCount() - track - 1;
191     track --;
192     for (int i = track; i > -1; i--) {
193         if (m_document->trackInfoAt(i).type == VIDEOTRACK) return i + 1;
194     }
195     return 0;
196 }
197
198
199 void CustomTrackView::slotCheckMouseScrolling() {
200     if (m_scrollOffset == 0) {
201         m_scrollTimer.stop();
202         return;
203     }
204     horizontalScrollBar()->setValue(horizontalScrollBar()->value() + m_scrollOffset);
205     m_scrollTimer.start();
206 }
207
208 void CustomTrackView::slotCheckPositionScrolling() {
209     // If mouse is at a border of the view, scroll
210     if (m_moveOpMode != SEEK) return;
211     int pos = cursorPos();
212     if (mapFromScene(pos, 0).x() < 7) {
213         horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 2);
214         setCursorPos(mapToScene(QPoint()).x() - 1);
215         QTimer::singleShot(200, this, SLOT(slotCheckPositionScrolling()));
216
217     } else if (viewport()->width() - 5 < mapFromScene(pos + 1, 0).x()) {
218         horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 2);
219         setCursorPos(mapToScene(viewport()->width(), 0).x() + 1);
220         QTimer::singleShot(200, this, SLOT(slotCheckPositionScrolling()));
221     }
222 }
223
224
225 // virtual
226
227 void CustomTrackView::mouseMoveEvent(QMouseEvent * event) {
228     int pos = event->x();
229     int mappedXPos = (int)(mapToScene(event->pos()).x() + 0.5);
230     emit mousePosition(mappedXPos);
231     if (event->buttons() & Qt::MidButton) return;
232     if (event->modifiers() == Qt::ControlModifier || event->modifiers() == Qt::ShiftModifier) {
233         QGraphicsView::mouseMoveEvent(event);
234         m_moveOpMode = NONE;
235         return;
236     }
237
238     if (event->buttons() != Qt::NoButton) {
239         bool move = (event->pos() - m_clickEvent).manhattanLength() >= QApplication::startDragDistance();
240         if (m_dragItem && m_tool == SELECTTOOL) {
241             if (m_operationMode == MOVE && move) {
242                 QGraphicsView::mouseMoveEvent(event);
243                 // If mouse is at a border of the view, scroll
244                 if (pos < 5) {
245                     m_scrollOffset = -30;
246                     m_scrollTimer.start();
247                 } else if (viewport()->width() - pos < 10) {
248                     m_scrollOffset = 30;
249                     m_scrollTimer.start();
250                 } else if (m_scrollTimer.isActive()) m_scrollTimer.stop();
251
252             } else if (m_operationMode == RESIZESTART && move) {
253                 double snappedPos = getSnapPointForPos(mappedXPos);
254                 m_dragItem->resizeStart((int)(snappedPos));
255             } else if (m_operationMode == RESIZEEND && move) {
256                 double snappedPos = getSnapPointForPos(mappedXPos);
257                 m_dragItem->resizeEnd((int)(snappedPos));
258             } else if (m_operationMode == FADEIN && move) {
259                 ((ClipItem*) m_dragItem)->setFadeIn((int)(mappedXPos - m_dragItem->startPos().frames(m_document->fps())));
260             } else if (m_operationMode == FADEOUT && move) {
261                 ((ClipItem*) m_dragItem)->setFadeOut((int)(m_dragItem->endPos().frames(m_document->fps()) - mappedXPos));
262             } else if (m_operationMode == KEYFRAME && move) {
263                 GenTime keyFramePos = GenTime(mappedXPos, m_document->fps()) - m_dragItem->startPos() + m_dragItem->cropStart();
264                 double pos = mapToScene(event->pos()).toPoint().y();
265                 QRectF br = m_dragItem->sceneBoundingRect();
266                 double maxh = 100.0 / br.height();
267                 pos = (br.bottom() - pos) * maxh;
268                 m_dragItem->updateKeyFramePos(keyFramePos, pos);
269             }
270
271             if (m_animation) delete m_animation;
272             m_animation = NULL;
273             if (m_visualTip) delete m_visualTip;
274             m_visualTip = NULL;
275             return;
276         } else if (m_operationMode == MOVEGUIDE) {
277             if (m_animation) delete m_animation;
278             m_animation = NULL;
279             if (m_visualTip) delete m_visualTip;
280             m_visualTip = NULL;
281             QGraphicsView::mouseMoveEvent(event);
282             return;
283         } else if (m_operationMode == SPACER && move) {
284             // spacer tool
285             int mappedClick = (int)(mapToScene(m_clickEvent).x() + 0.5);
286             m_selectionGroup->setPos(mappedXPos + (m_spacerStart - mappedClick) , m_selectionGroup->pos().y());
287         }
288     }
289
290     if (m_tool == RAZORTOOL) {
291         setCursor(m_razorCursor);
292         //QGraphicsView::mouseMoveEvent(event);
293         //return;
294     } else if (m_tool == SPACERTOOL) {
295         setCursor(m_spacerCursor);
296         return;
297     }
298
299     QList<QGraphicsItem *> itemList = items(event->pos());
300     QGraphicsRectItem *item = NULL;
301     OPERATIONTYPE opMode = NONE;
302
303     if (itemList.count() == 1 && itemList.at(0)->type() == GUIDEITEM) {
304         opMode = MOVEGUIDE;
305     } else for (int i = 0; i < itemList.count(); i++) {
306             if (itemList.at(i)->type() == AVWIDGET || itemList.at(i)->type() == TRANSITIONWIDGET) {
307                 item = (QGraphicsRectItem*) itemList.at(i);
308                 break;
309             }
310         }
311
312     if (item && event->buttons() == Qt::NoButton) {
313         AbstractClipItem *clip = static_cast <AbstractClipItem*>(item);
314         if (m_tool == RAZORTOOL) {
315             // razor tool over a clip, display current frame in monitor
316             if (!m_blockRefresh && item->type() == AVWIDGET) {
317                 //TODO: solve crash when showing frame when moving razor over clip
318                 //emit showClipFrame(((ClipItem *) item)->baseClip(), mapToScene(event->pos()).x() / m_scale - (clip->startPos() - clip->cropStart()).frames(m_document->fps()));
319             }
320             event->accept();
321             return;
322         }
323         opMode = clip->operationMode(mapToScene(event->pos()));
324         double size = 5;
325         if (opMode == m_moveOpMode) {
326             QGraphicsView::mouseMoveEvent(event);
327             return;
328         } else {
329             if (m_visualTip) {
330                 if (m_animation) delete m_animation;
331                 m_animation = NULL;
332                 m_animationTimer->stop();
333                 delete m_visualTip;
334                 m_visualTip = NULL;
335             }
336         }
337         m_moveOpMode = opMode;
338         if (opMode == MOVE) {
339             setCursor(Qt::OpenHandCursor);
340         } else if (opMode == RESIZESTART) {
341             setCursor(KCursor("left_side", Qt::SizeHorCursor));
342             if (m_visualTip == NULL) {
343                 QRectF rect = clip->sceneBoundingRect();
344                 QPolygon polygon;
345                 polygon << QPoint(0, rect.height() / 2 - size * 2);
346                 polygon << QPoint(size * 2, (int)(rect.height() / 2));
347                 polygon << QPoint(0, (int)(rect.height() / 2 + size * 2));
348                 polygon << QPoint(0, (int)(rect.height() / 2 - size * 2));
349
350                 m_visualTip = new QGraphicsPolygonItem(polygon);
351                 ((QGraphicsPolygonItem*) m_visualTip)->setBrush(m_tipColor);
352                 ((QGraphicsPolygonItem*) m_visualTip)->setPen(m_tipPen);
353                 m_visualTip->setPos(rect.x(), rect.y());
354                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
355                 m_visualTip->setZValue(100);
356                 m_animation = new QGraphicsItemAnimation;
357                 m_animation->setItem(m_visualTip);
358                 m_animation->setTimeLine(m_animationTimer);
359                 double scale = 2.0;
360                 m_animation->setScaleAt(.5, scale, 1);
361                 //m_animation->setPosAt(.5, QPointF(rect.x() - rect.x() * scale, 0));
362                 scale = 1.0;
363                 m_animation->setScaleAt(1, scale, 1);
364                 //m_animation->setPosAt(1, QPointF(rect.x() - rect.x() * scale, 0));
365                 scene()->addItem(m_visualTip);
366                 m_animationTimer->start();
367             }
368         } else if (opMode == RESIZEEND) {
369             setCursor(KCursor("right_side", Qt::SizeHorCursor));
370             if (m_visualTip == NULL) {
371                 QRectF rect = clip->sceneBoundingRect();
372                 QPolygon polygon;
373                 polygon << QPoint(0, (int)(rect.height() / 2 - size * 2));
374                 polygon << QPoint(- size * 2, (int)(rect.height() / 2));
375                 polygon << QPoint(0, (int)(rect.height() / 2 + size * 2));
376                 polygon << QPoint(0, (int)(rect.height() / 2 - size * 2));
377
378                 m_visualTip = new QGraphicsPolygonItem(polygon);
379                 ((QGraphicsPolygonItem*) m_visualTip)->setBrush(m_tipColor);
380                 ((QGraphicsPolygonItem*) m_visualTip)->setPen(m_tipPen);
381                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
382                 m_visualTip->setPos(rect.right(), rect.y());
383                 m_visualTip->setZValue(100);
384                 m_animation = new QGraphicsItemAnimation;
385                 m_animation->setItem(m_visualTip);
386                 m_animation->setTimeLine(m_animationTimer);
387                 double scale = 2.0;
388                 m_animation->setScaleAt(.5, scale, 1);
389                 scale = 1.0;
390                 m_animation->setScaleAt(1, scale, 1);
391                 scene()->addItem(m_visualTip);
392                 m_animationTimer->start();
393             }
394         } else if (opMode == FADEIN) {
395             if (m_visualTip == NULL) {
396                 ClipItem *item = (ClipItem *) clip;
397                 QRectF rect = clip->sceneBoundingRect();
398                 m_visualTip = new QGraphicsEllipseItem(-size, -size, size * 2, size * 2);
399                 ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
400                 ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
401                 m_visualTip->setPos(rect.x() + item->fadeIn(), rect.y());
402                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
403                 m_visualTip->setZValue(100);
404                 m_animation = new QGraphicsItemAnimation;
405                 m_animation->setItem(m_visualTip);
406                 m_animation->setTimeLine(m_animationTimer);
407                 double scale = 2.0;
408                 m_animation->setScaleAt(.5, scale, scale);
409                 scale = 1.0;
410                 m_animation->setScaleAt(1, scale, scale);
411                 scene()->addItem(m_visualTip);
412                 m_animationTimer->start();
413             }
414             setCursor(Qt::PointingHandCursor);
415         } else if (opMode == FADEOUT) {
416             if (m_visualTip == NULL) {
417                 ClipItem *item = (ClipItem *) clip;
418                 QRectF rect = clip->sceneBoundingRect();
419                 m_visualTip = new QGraphicsEllipseItem(-size, -size, size * 2, size * 2);
420                 ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
421                 ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
422                 m_visualTip->setPos(rect.right() - item->fadeOut(), rect.y());
423                 m_visualTip->setFlags(QGraphicsItem::ItemIgnoresTransformations);
424                 m_visualTip->setZValue(100);
425                 m_animation = new QGraphicsItemAnimation;
426                 m_animation->setItem(m_visualTip);
427                 m_animation->setTimeLine(m_animationTimer);
428                 double scale = 2.0;
429                 m_animation->setScaleAt(.5, scale, scale);
430                 scale = 1.0;
431                 m_animation->setScaleAt(1, scale, scale);
432                 scene()->addItem(m_visualTip);
433                 m_animationTimer->start();
434             }
435             setCursor(Qt::PointingHandCursor);
436         } else if (opMode == TRANSITIONSTART) {
437             /*if (m_visualTip == NULL) {
438                 QRectF rect = clip->sceneBoundingRect();
439                 m_visualTip = new QGraphicsEllipseItem(-5, -5 , 10, 10);
440                 ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
441                 ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
442                 m_visualTip->setZValue(100);
443                 m_animation = new QGraphicsItemAnimation;
444                 m_animation->setItem(m_visualTip);
445                 m_animation->setTimeLine(m_animationTimer);
446                 m_visualTip->setPos(rect.x() + 10, rect.y() + rect.height() / 2 + 12);
447                 double scale = 2.0;
448                 m_animation->setScaleAt(.5, scale, scale);
449                 scale = 1.0;
450                 m_animation->setScaleAt(1, scale, scale);
451                 scene()->addItem(m_visualTip);
452                 m_animationTimer->start();
453             }*/
454             setCursor(Qt::PointingHandCursor);
455         } else if (opMode == TRANSITIONEND) {
456             /*if (m_visualTip == NULL) {
457                 QRectF rect = clip->sceneBoundingRect();
458                 m_visualTip = new QGraphicsEllipseItem(-5, -5 , 10, 10);
459                 ((QGraphicsEllipseItem*) m_visualTip)->setBrush(m_tipColor);
460                 ((QGraphicsEllipseItem*) m_visualTip)->setPen(m_tipPen);
461                 m_visualTip->setZValue(100);
462                 m_animation = new QGraphicsItemAnimation;
463                 m_animation->setItem(m_visualTip);
464                 m_animation->setTimeLine(m_animationTimer);
465                 m_visualTip->setPos(rect.x() + rect.width() - 10 , rect.y() + rect.height() / 2 + 12);
466                 double scale = 2.0;
467                 m_animation->setScaleAt(.5, scale, scale);
468                 scale = 1.0;
469                 m_animation->setScaleAt(1, scale, scale);
470                 scene()->addItem(m_visualTip);
471                 m_animationTimer->start();
472             }*/
473             setCursor(Qt::PointingHandCursor);
474         } else if (opMode == KEYFRAME) {
475             setCursor(Qt::PointingHandCursor);
476         }
477     } // no clip under mouse
478     else if (m_tool == RAZORTOOL) {
479         event->accept();
480         return;
481     } else if (opMode == MOVEGUIDE) {
482         m_moveOpMode = opMode;
483         setCursor(Qt::SplitHCursor);
484     } else {
485         if (event->buttons() != Qt::NoButton && event->modifiers() == Qt::NoModifier) {
486             m_moveOpMode = SEEK;
487             setCursorPos(mappedXPos);
488             slotCheckPositionScrolling();
489         } else m_moveOpMode = NONE;
490         if (m_visualTip) {
491             if (m_animation) delete m_animation;
492             m_animationTimer->stop();
493             m_animation = NULL;
494             delete m_visualTip;
495             m_visualTip = NULL;
496
497         }
498         setCursor(Qt::ArrowCursor);
499     }
500     QGraphicsView::mouseMoveEvent(event);
501 }
502
503 // virtual
504 void CustomTrackView::mousePressEvent(QMouseEvent * event) {
505     m_menuPosition = QPoint();
506     m_blockRefresh = true;
507     bool collision = false;
508
509     if (m_tool != RAZORTOOL) activateMonitor();
510     else if (m_document->renderer()->playSpeed() != 0.0) {
511         m_document->renderer()->pause();
512         return;
513     }
514     m_clickEvent = event->pos();
515
516     // special cases (middle click button or ctrl / shift click
517     if (event->button() == Qt::MidButton) {
518         m_document->renderer()->switchPlay();
519         m_blockRefresh = false;
520         return;
521     }
522
523     // check item under mouse
524     QList<QGraphicsItem *> collisionList = items(event->pos());
525
526     if (event->modifiers() == Qt::ControlModifier && collisionList.count() == 0) {
527         setDragMode(QGraphicsView::ScrollHandDrag);
528         QGraphicsView::mousePressEvent(event);
529         m_blockRefresh = false;
530         return;
531     }
532
533     if (event->modifiers() == Qt::ShiftModifier && collisionList.count() == 0) {
534         setDragMode(QGraphicsView::RubberBandDrag);
535         QGraphicsView::mousePressEvent(event);
536         m_blockRefresh = false;
537         return;
538     }
539
540     if (collisionList.count() == 1 && collisionList.at(0)->type() == GUIDEITEM) {
541         // a guide item was pressed
542         collisionList.at(0)->setFlag(QGraphicsItem::ItemIsMovable, true);
543         m_dragItem = NULL;
544         m_dragGuide = (Guide *) collisionList.at(0);
545         collision = true;
546         m_operationMode = MOVEGUIDE;
547         // deselect all clips so that only the guide will move
548         m_scene->clearSelection();
549         resetSelectionGroup();
550         updateSnapPoints(NULL);
551         QGraphicsView::mousePressEvent(event);
552         return;
553     }
554
555     // Find first clip or transition under mouse
556     int i = 0;
557     m_dragItem = NULL;
558     while (i < collisionList.count()) {
559         if (collisionList.at(i)->type() == AVWIDGET || collisionList.at(i)->type() == TRANSITIONWIDGET) {
560             m_dragItem = static_cast <AbstractClipItem *>(collisionList.at(i));
561             m_dragItemInfo = m_dragItem->info();
562             break;
563         }
564         i++;
565     }
566
567     // context menu requested
568     if (event->button() == Qt::RightButton) {
569         if (m_dragItem) {
570             if (!m_dragItem->isSelected()) {
571                 m_scene->clearSelection();
572                 resetSelectionGroup();
573                 m_dragItem->setSelected(true);
574             }
575         }
576         m_operationMode = NONE;
577         displayContextMenu(event->globalPos(), m_dragItem);
578         m_menuPosition = event->pos();
579         m_dragItem = NULL;
580         event->accept();
581         return;
582     }
583
584     // No item under click
585     if (m_dragItem == NULL || m_tool == SPACERTOOL) {
586         resetSelectionGroup();
587         setCursor(Qt::ArrowCursor);
588         m_scene->clearSelection();
589         event->accept();
590         emit clipItemSelected(NULL);
591         if (m_tool == SPACERTOOL) {
592             // Select all items on track after click position
593             int track = (int)(mapToScene(m_clickEvent).y() / m_tracksHeight);
594             QList<QGraphicsItem *> selection = items(event->pos().x(), track * m_tracksHeight + 1, sceneRect().width() - event->pos().x(), m_tracksHeight - 2);
595             m_selectionGroup = new AbstractGroupItem(m_document->fps());
596             scene()->addItem(m_selectionGroup);
597             m_spacerStart = -1;
598             int itemStart;
599             for (int i = 0; i < selection.count(); i++) {
600                 if (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET) {
601                     m_selectionGroup->addToGroup(selection.at(i));
602                     AbstractClipItem *item = static_cast <AbstractClipItem *>(selection.at(i));
603                     itemStart = item->startPos().frames(m_document->fps());
604                     if (m_spacerStart == -1 || itemStart < m_spacerStart)
605                         m_spacerStart = itemStart;
606                 }
607             }
608             QPointF top = m_selectionGroup->boundingRect().topLeft();
609             const int width = m_selectionGroup->boundingRect().width();
610             const int height = m_selectionGroup->boundingRect().height();
611             m_selectionGroup->setPos(top);
612             m_selectionGroup->translate(-top.x(), -top.y() + 1);
613             //kDebug()<<"// SPACER START GRP: "<<m_spacerStart;
614             m_operationMode = SPACER;
615         } else setCursorPos((int)(mapToScene(event->x(), 0).x()));
616         return;
617     }
618
619     // Razor tool
620     if (m_tool == RAZORTOOL) {
621         if (m_dragItem->type() == TRANSITIONWIDGET) {
622             emit displayMessage(i18n("Cannot cut a transition"), ErrorMessage);
623             event->accept();
624             m_dragItem = NULL;
625             return;
626         }
627         AbstractClipItem *clip = static_cast <AbstractClipItem *>(m_dragItem);
628         RazorClipCommand* command = new RazorClipCommand(this, clip->info(), GenTime((int)(mapToScene(event->pos()).x()), m_document->fps()), true);
629         m_commandStack->push(command);
630         m_document->setModified(true);
631         m_dragItem = NULL;
632         event->accept();
633         return;
634     }
635     updateSnapPoints(m_dragItem);
636     if (m_dragItem->type() == AVWIDGET) emit clipItemSelected((ClipItem*) m_dragItem);
637     else emit clipItemSelected(NULL);
638
639     if (event->modifiers() != Qt::ControlModifier && (m_dragItem->group() || m_dragItem->isSelected())) {
640         // If clicked item is selected, allow move
641         event->accept();
642         if (m_selectionGroup) m_selectionGroup->setSelected(true);
643         if (m_operationMode == NONE) QGraphicsView::mousePressEvent(event);
644     } else {
645         resetSelectionGroup();
646         if (event->modifiers() != Qt::ControlModifier) m_scene->clearSelection();
647         m_dragItem->setSelected(!m_dragItem->isSelected());
648         QList<QGraphicsItem *> selection = m_scene->selectedItems();
649         if (selection.count() > 1) {
650             m_selectionGroup = new AbstractGroupItem(m_document->fps());
651             scene()->addItem(m_selectionGroup);
652             for (int i = 0; i < selection.count(); i++) {
653                 if (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET) {
654                     m_selectionGroup->addToGroup(selection.at(i));
655                     selection.at(i)->setFlags(QGraphicsItem::ItemIsSelectable);
656                 }
657             }
658         }
659         if (m_selectionGroup) {
660             QPointF top = m_selectionGroup->boundingRect().topLeft();
661             const int width = m_selectionGroup->boundingRect().width();
662             const int height = m_selectionGroup->boundingRect().height();
663             m_selectionGroup->setPos(top);
664             m_selectionGroup->translate(-top.x(), -top.y() + 1);
665             m_selectionGroupInfo.startPos = GenTime(m_selectionGroup->scenePos().x(), m_document->fps());
666             m_selectionGroupInfo.track = m_selectionGroup->track();
667         }
668     }
669
670     m_clickPoint = QPoint((int)(mapToScene(event->pos()).x() - m_dragItem->startPos().frames(m_document->fps())), (int)(event->pos().y() - m_dragItem->pos().y()));
671     /*
672                         if (!item->isSelected()) {
673
674                             if (event->modifiers() != Qt::ControlModifier) {
675                                 QList<QGraphicsItem *> itemList = items();
676                                 for (int i = 0; i < itemList.count(); i++) {
677                                     itemList.at(i)->setSelected(false);
678                                     itemList.at(i)->update();
679                                 }
680                             }
681                             item->setSelected(true);
682                             item->update();
683                         }
684
685
686
687                         m_clickPoint = QPoint((int)(mapToScene(event->pos()).x() - m_dragItem->startPos().frames(m_document->fps()) * m_scale), (int)(event->pos().y() - m_dragItem->pos().y()));
688                         m_dragItemInfo.startPos = m_dragItem->startPos();
689                         m_dragItemInfo.endPos = m_dragItem->endPos();
690                         m_dragItemInfo.track = m_dragItem->track();
691
692                         m_selectedClipList.clear();
693                         QList<QGraphicsItem *> selected = scene()->selectedItems();
694                         for (int i = 0; i < selected.count(); i++) {
695                             if (selected.at(i)->type() == AVWIDGET || selected.at(i)->type() == TRANSITIONWIDGET)
696                                 m_selectedClipList.append(static_cast <AbstractClipItem *>(selected.at(i)));
697                         }
698           */
699     m_operationMode = m_dragItem->operationMode(mapToScene(event->pos()));
700
701     if (m_operationMode == KEYFRAME) {
702         m_dragItem->updateSelectedKeyFrame();
703         m_blockRefresh = false;
704         return;
705     } else if (m_operationMode == MOVE) {
706         setCursor(Qt::ClosedHandCursor);
707     } else if (m_operationMode == TRANSITIONSTART) {
708         ItemInfo info;
709         info.startPos = m_dragItem->startPos();
710         info.track = m_dragItem->track();
711         int transitiontrack = getPreviousVideoTrack(info.track);
712         ClipItem *transitionClip = NULL;
713         if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.startPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
714         if (transitionClip && transitionClip->endPos() < m_dragItem->endPos()) {
715             info.endPos = transitionClip->endPos();
716         } else info.endPos = info.startPos + GenTime(65, m_document->fps());
717         if (info.endPos == info.startPos) info.endPos = info.startPos + GenTime(65, m_document->fps());
718         slotAddTransition((ClipItem *) m_dragItem, info, transitiontrack);
719     } else if (m_operationMode == TRANSITIONEND) {
720         ItemInfo info;
721         info.endPos = GenTime(m_dragItem->endPos().frames(m_document->fps()), m_document->fps());
722         info.track = m_dragItem->track();
723         int transitiontrack = getPreviousVideoTrack(info.track);
724         ClipItem *transitionClip = NULL;
725         if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.endPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
726         if (transitionClip && transitionClip->startPos() > m_dragItem->startPos()) {
727             info.startPos = transitionClip->startPos();
728         } else info.startPos = info.endPos - GenTime(65, m_document->fps());
729         if (info.endPos == info.startPos) info.startPos = info.endPos - GenTime(65, m_document->fps());
730         QDomElement transition = MainWindow::transitions.getEffectByName("Luma").cloneNode().toElement();
731         EffectsList::setParameter(transition, "reverse", "1");
732         slotAddTransition((ClipItem *) m_dragItem, info, transitiontrack, transition);
733     }
734
735     m_blockRefresh = false;
736     //kDebug()<<pos;
737     //QGraphicsView::mousePressEvent(event);
738 }
739
740 void CustomTrackView::resetSelectionGroup() {
741     if (m_selectionGroup) {
742         // delete selection group
743         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
744         for (int i = 0; i < children.count(); i++) {
745             children.at(i)->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
746             children.at(i)->setSelected(true);
747         }
748         scene()->destroyItemGroup(m_selectionGroup);
749         m_selectionGroup = NULL;
750     }
751 }
752
753 void CustomTrackView::mouseDoubleClickEvent(QMouseEvent *event) {
754     kDebug() << "++++++++++++ DBL CLK";
755     if (m_dragItem && m_dragItem->hasKeyFrames()) {
756         if (m_moveOpMode == KEYFRAME) {
757             // user double clicked on a keyframe, open edit dialog
758             QDialog d(parentWidget());
759             Ui::KeyFrameDialog_UI view;
760             view.setupUi(&d);
761             view.kfr_position->setText(m_document->timecode().getTimecode(GenTime(m_dragItem->selectedKeyFramePos(), m_document->fps()) - m_dragItem->cropStart(), m_document->fps()));
762             view.kfr_value->setValue(m_dragItem->selectedKeyFrameValue());
763             view.kfr_value->setFocus();
764             if (d.exec() == QDialog::Accepted) {
765                 int pos = m_document->timecode().getFrameCount(view.kfr_position->text(), m_document->fps());
766                 m_dragItem->updateKeyFramePos(GenTime(pos, m_document->fps()) + m_dragItem->cropStart(), (double) view.kfr_value->value() * m_dragItem->keyFrameFactor());
767                 ClipItem *item = (ClipItem *)m_dragItem;
768                 QString previous = item->keyframes(item->selectedEffectIndex());
769                 item->updateKeyframeEffect();
770                 QString next = item->keyframes(item->selectedEffectIndex());
771                 EditKeyFrameCommand *command = new EditKeyFrameCommand(this, item->track(), item->startPos(), item->selectedEffectIndex(), previous, next, false);
772                 m_commandStack->push(command);
773                 updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
774             }
775
776         } else  {
777             // add keyframe
778             GenTime keyFramePos = GenTime((int)(mapToScene(event->pos()).x()), m_document->fps()) - m_dragItem->startPos() + m_dragItem->cropStart();
779             m_dragItem->addKeyFrame(keyFramePos, mapToScene(event->pos()).toPoint().y());
780             ClipItem * item = (ClipItem *) m_dragItem;
781             QString previous = item->keyframes(item->selectedEffectIndex());
782             item->updateKeyframeEffect();
783             QString next = item->keyframes(item->selectedEffectIndex());
784             EditKeyFrameCommand *command = new EditKeyFrameCommand(this, m_dragItem->track(), m_dragItem->startPos(), item->selectedEffectIndex(), previous, next, false);
785             m_commandStack->push(command);
786             updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
787         }
788     } else if (m_dragItem) {
789         ClipDurationDialog d(m_dragItem, m_document->timecode(), this);
790         if (d.exec() == QDialog::Accepted) {
791             if (d.startPos() != m_dragItem->startPos()) {
792                 if (m_dragItem->type() == AVWIDGET) {
793                     ItemInfo startInfo;
794                     startInfo.startPos = m_dragItem->startPos();
795                     startInfo.endPos = m_dragItem->endPos();
796                     startInfo.track = m_dragItem->track();
797                     ItemInfo endInfo;
798                     endInfo.startPos = d.startPos();
799                     endInfo.endPos = m_dragItem->endPos() + (endInfo.startPos - startInfo.startPos);
800                     endInfo.track = m_dragItem->track();
801                     MoveClipCommand *command = new MoveClipCommand(this, startInfo, endInfo, true);
802                     m_commandStack->push(command);
803                 } else {
804                     //TODO: move transition
805                 }
806             }
807             if (d.duration() != m_dragItem->duration()) {
808                 if (m_dragItem->type() == AVWIDGET) {
809                     ItemInfo startInfo;
810                     startInfo.startPos = m_dragItem->startPos();
811                     startInfo.endPos = m_dragItem->endPos();
812                     startInfo.track = m_dragItem->track();
813                     ItemInfo endInfo;
814                     endInfo.startPos = startInfo.startPos;
815                     endInfo.endPos = endInfo.startPos + d.duration();
816                     endInfo.track = m_dragItem->track();
817                     ResizeClipCommand *command = new ResizeClipCommand(this, startInfo, endInfo, true);
818                     m_commandStack->push(command);
819                 } else {
820                     //TODO: resize transition
821                 }
822             }
823         }
824     } else {
825         QList<QGraphicsItem *> collisionList = items(event->pos());
826         if (collisionList.count() == 1 && collisionList.at(0)->type() == GUIDEITEM) {
827             Guide *editGuide = (Guide *) collisionList.at(0);
828             if (editGuide) slotEditGuide(editGuide->info());
829         }
830     }
831 }
832
833
834 void CustomTrackView::editKeyFrame(const GenTime pos, const int track, const int index, const QString keyframes) {
835     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), track);
836     if (clip) {
837         clip->setKeyframes(index, keyframes);
838         updateEffect(m_document->tracksCount() - clip->track(), clip->startPos(), clip->effectAt(index), index);
839     } else emit displayMessage(i18n("Cannot find clip with keyframe"), ErrorMessage);
840 }
841
842
843 void CustomTrackView::displayContextMenu(QPoint pos, AbstractClipItem *clip) {
844     if (clip == NULL) m_timelineContextMenu->popup(pos);
845     else if (clip->type() == AVWIDGET) m_timelineContextClipMenu->popup(pos);
846     else if (clip->type() == TRANSITIONWIDGET) m_timelineContextTransitionMenu->popup(pos);
847 }
848
849 void CustomTrackView::activateMonitor() {
850     emit activateDocumentMonitor();
851 }
852
853 void CustomTrackView::dragEnterEvent(QDragEnterEvent * event) {
854     if (event->mimeData()->hasFormat("kdenlive/clip")) {
855         resetSelectionGroup();
856
857         QStringList list = QString(event->mimeData()->data("kdenlive/clip")).split(";");
858         m_selectionGroup = new AbstractGroupItem(m_document->fps());
859         QPoint pos = QPoint();
860         DocClipBase *clip = m_document->getBaseClip(list.at(0));
861         if (clip == NULL) kDebug() << " WARNING))))))))) CLIP NOT FOUND : " << list.at(0);
862         ItemInfo info;
863         info.startPos = GenTime(pos.x(), m_document->fps());
864         info.cropStart = GenTime(list.at(1).toInt(), m_document->fps());
865         info.endPos = info.startPos + GenTime(list.at(2).toInt() - list.at(1).toInt(), m_document->fps());
866         info.track = (int)(pos.y() / m_tracksHeight);
867         ClipItem *item = new ClipItem(clip, info, m_document->fps());
868         m_selectionGroup->addToGroup(item);
869         //TODO: check if we do not overlap another clip when first dropping in timeline
870         // if (insertPossible(m_selectionGroup, event->pos()))
871         scene()->addItem(m_selectionGroup);
872         event->acceptProposedAction();
873     } else if (event->mimeData()->hasFormat("kdenlive/producerslist")) {
874         QStringList ids = QString(event->mimeData()->data("kdenlive/producerslist")).split(";");
875         m_scene->clearSelection();
876         resetSelectionGroup();
877
878         m_selectionGroup = new AbstractGroupItem(m_document->fps());
879         QPoint pos = QPoint();
880         for (int i = 0; i < ids.size(); ++i) {
881             DocClipBase *clip = m_document->getBaseClip(ids.at(i));
882             if (clip == NULL) kDebug() << " WARNING))))))))) CLIP NOT FOUND : " << ids.at(i);
883             ItemInfo info;
884             info.startPos = GenTime(pos.x(), m_document->fps());
885             info.endPos = info.startPos + clip->duration();
886             info.track = (int)(pos.y() / m_tracksHeight);
887             ClipItem *item = new ClipItem(clip, info, m_document->fps());
888             pos.setX(pos.x() + clip->duration().frames(m_document->fps()));
889             m_selectionGroup->addToGroup(item);
890         }
891         //TODO: check if we do not overlap another clip when first dropping in timeline
892         //if (insertPossible(m_selectionGroup, event->pos()))
893         scene()->addItem(m_selectionGroup);
894         event->acceptProposedAction();
895     } else QGraphicsView::dragEnterEvent(event);
896 }
897
898
899 bool CustomTrackView::insertPossible(AbstractGroupItem *group, const QPoint &pos) const {
900     QPolygonF path;
901     QList<QGraphicsItem *> children = group->childItems();
902     for (int i = 0; i < children.count(); i++) {
903         if (children.at(i)->type() == AVWIDGET) {
904             ClipItem *clip = static_cast <ClipItem *>(children.at(i));
905             ItemInfo info = clip->info();
906             kDebug() << " / / INSERT : " << pos.x();
907             QRectF shape = QRectF(clip->startPos().frames(m_document->fps()), clip->track() * m_tracksHeight + 1, clip->duration().frames(m_document->fps()) - 0.02, m_tracksHeight - 1);
908             kDebug() << " / / INSERT RECT: " << shape;
909             path = path.united(QPolygonF(shape));
910         }
911     }
912
913     QList<QGraphicsItem*> collindingItems = scene()->items(path, Qt::IntersectsItemShape);
914     if (collindingItems.isEmpty()) return true;
915     else {
916         for (int i = 0; i < collindingItems.count(); i++) {
917             QGraphicsItem *collision = collindingItems.at(i);
918             if (collision->type() == AVWIDGET) {
919                 // Collision
920                 kDebug() << "// COLLISIION DETECTED";
921                 return false;
922             }
923         }
924         return true;
925     }
926
927 }
928
929 void CustomTrackView::slotRefreshEffects(ClipItem *clip) {
930     int track = m_document->tracksCount() - clip->track();
931     GenTime pos = clip->startPos();
932     if (!m_document->renderer()->mltRemoveEffect(track, pos, "-1", false)) {
933         emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
934         return;
935     }
936     bool success = true;
937     for (int i = 0; i < clip->effectsCount(); i++) {
938         if (!m_document->renderer()->mltAddEffect(track, pos, clip->getEffectArgs(clip->effectAt(i)), false)) success = false;
939     }
940     if (!success) emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
941     m_document->renderer()->doRefresh();
942 }
943
944 void CustomTrackView::addEffect(int track, GenTime pos, QDomElement effect) {
945     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()) + 1, m_document->tracksCount() - track);
946     if (clip) {
947         QHash <QString, QString> effectParams = clip->addEffect(effect);
948         if (!m_document->renderer()->mltAddEffect(track, pos, effectParams))
949             emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
950         emit clipItemSelected(clip);
951     } else emit displayMessage(i18n("Cannot find clip to add effect"), ErrorMessage);
952 }
953
954 void CustomTrackView::deleteEffect(int track, GenTime pos, QDomElement effect) {
955     QString index = effect.attribute("kdenlive_ix");
956     if (effect.attribute("disabled") != "1" && !m_document->renderer()->mltRemoveEffect(track, pos, index)) {
957         emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
958         return;
959     }
960     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()) + 1, m_document->tracksCount() - track);
961     if (clip) {
962         clip->deleteEffect(index);
963         emit clipItemSelected(clip);
964     }
965 }
966
967 void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track) {
968     QList<QGraphicsItem *> itemList;
969     if (track == -1) itemList = scene()->selectedItems();
970     if (itemList.isEmpty()) {
971         ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()) + 1, track);
972         if (clip) itemList.append(clip);
973         else emit displayMessage(i18n("Select a clip if you want to apply an effect"), ErrorMessage);
974     }
975     kDebug() << "// REQUESTING EFFECT ON CLIP: " << pos.frames(25) << ", TRK: " << track << "SELECTED ITEMS: " << itemList.count();
976     for (int i = 0; i < itemList.count(); i++) {
977         if (itemList.at(i)->type() == AVWIDGET) {
978             ClipItem *item = (ClipItem *)itemList.at(i);
979             item->initEffect(effect);
980             AddEffectCommand *command = new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true);
981             m_commandStack->push(command);
982         }
983     }
984     m_document->setModified(true);
985 }
986
987 void CustomTrackView::slotDeleteEffect(ClipItem *clip, QDomElement effect) {
988     AddEffectCommand *command = new AddEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effect, false);
989     m_commandStack->push(command);
990     m_document->setModified(true);
991 }
992
993 void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement effect, int ix, bool triggeredByUser) {
994     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()) + 1, m_document->tracksCount() - track);
995     if (clip) {
996         QHash <QString, QString> effectParams = clip->getEffectArgs(effect);
997         // check if we are trying to reset a keyframe effect
998         if (effectParams.contains("keyframes") && effectParams.value("keyframes").isEmpty()) {
999             clip->initEffect(effect);
1000             clip->setEffectAt(ix, effect);
1001             effectParams = clip->getEffectArgs(effect);
1002         }
1003         if (effectParams.value("disabled") == "1") {
1004             if (m_document->renderer()->mltRemoveEffect(track, pos, effectParams.value("kdenlive_ix"))) {
1005                 kDebug() << "//////  DISABLING EFFECT: " << index << ", CURRENTLA: " << clip->selectedEffectIndex();
1006             } else emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
1007         } else if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - clip->track(), clip->startPos(), effectParams))
1008             emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
1009
1010         clip->setEffectAt(ix, effect);
1011         if (ix == clip->selectedEffectIndex()) {
1012             clip->setSelectedEffect(ix);
1013             if (!triggeredByUser) emit clipItemSelected(clip, ix);
1014         }
1015         if (effect.attribute("tag") == "volume") {
1016             // A fade effect was modified, update the clip
1017             if (effect.attribute("id") == "fadein") {
1018                 int pos = effectParams.value("out").toInt() - effectParams.value("in").toInt();
1019                 clip->setFadeIn(pos);
1020             }
1021             if (effect.attribute("id") == "fadeout") {
1022                 int pos = effectParams.value("out").toInt() - effectParams.value("in").toInt();
1023                 clip->setFadeOut(pos);
1024             }
1025
1026         }
1027     }
1028     m_document->setModified(true);
1029 }
1030
1031 void CustomTrackView::moveEffect(int track, GenTime pos, int oldPos, int newPos) {
1032     ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()) + 1, m_document->tracksCount() - track);
1033     if (clip) {
1034         m_document->renderer()->mltMoveEffect(track, pos, oldPos, newPos);
1035         QDomElement act = clip->effectAt(newPos - 1).cloneNode().toElement();
1036         QDomElement before = clip->effectAt(oldPos - 1).cloneNode().toElement();
1037         clip->setEffectAt(oldPos - 1, act);
1038         clip->setEffectAt(newPos - 1, before);
1039         emit clipItemSelected(clip, newPos - 1);
1040     }
1041     m_document->setModified(true);
1042 }
1043
1044 void CustomTrackView::slotChangeEffectState(ClipItem *clip, int effectPos, bool disable) {
1045     QDomElement effect = clip->effectAt(effectPos);
1046     QDomElement oldEffect = effect.cloneNode().toElement();
1047     effect.setAttribute("disabled", disable);
1048     EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldEffect, effect, effectPos, true);
1049     m_commandStack->push(command);
1050     m_document->setModified(true);
1051 }
1052
1053 void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int currentPos, int newPos) {
1054     MoveEffectCommand *command = new MoveEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), currentPos, newPos, true);
1055     m_commandStack->push(command);
1056     m_document->setModified(true);
1057 }
1058
1059 void CustomTrackView::slotUpdateClipEffect(ClipItem *clip, QDomElement oldeffect, QDomElement effect, int ix) {
1060     EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, true);
1061     m_commandStack->push(command);
1062 }
1063
1064 void CustomTrackView::cutClip(ItemInfo info, GenTime cutTime, bool cut) {
1065     if (cut) {
1066         // cut clip
1067         ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()) + 1, info.track);
1068         if (!item || cutTime >= item->endPos() || cutTime <= item->startPos()) {
1069             emit displayMessage(i18n("Cannot find clip to cut"), ErrorMessage);
1070             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);
1071             m_blockRefresh = false;
1072             return;
1073         }
1074         kDebug() << "/////////  CUTTING CLIP : (" << item->startPos().frames(25) << "-" << item->endPos().frames(25) << "), INFO: (" << info.startPos.frames(25) << "-" << info.endPos.frames(25) << ")" << ", CUT: " << cutTime.frames(25);
1075
1076         m_document->renderer()->mltCutClip(m_document->tracksCount() - info.track, cutTime);
1077         int cutPos = (int) cutTime.frames(m_document->fps());
1078         ItemInfo newPos;
1079         newPos.startPos = cutTime;
1080         newPos.endPos = info.endPos;
1081         newPos.cropStart = item->cropStart() + (cutTime - info.startPos);
1082         newPos.track = info.track;
1083         ClipItem *dup = item->clone(newPos);
1084         kDebug() << "// REsizing item to: " << cutPos;
1085         item->resizeEnd(cutPos, false);
1086         scene()->addItem(dup);
1087         if (item->checkKeyFrames()) slotRefreshEffects(item);
1088         if (dup->checkKeyFrames()) slotRefreshEffects(dup);
1089         item->baseClip()->addReference();
1090         m_document->updateClip(item->baseClip()->getId());
1091         kDebug() << "/////////  CUTTING CLIP RESULT: (" << item->startPos().frames(25) << "-" << item->endPos().frames(25) << "), DUP: (" << dup->startPos().frames(25) << "-" << dup->endPos().frames(25) << ")" << ", CUT: " << cutTime.frames(25);
1092         kDebug() << "//  CUTTING CLIP dONE";
1093     } else {
1094         // uncut clip
1095
1096         ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()), info.track);
1097         ClipItem *dup = getClipItemAt((int) cutTime.frames(m_document->fps()) + 1, info.track);
1098         if (!item || !dup || item == dup) {
1099             emit displayMessage(i18n("Cannot find clip to uncut"), ErrorMessage);
1100             m_blockRefresh = false;
1101             return;
1102         }
1103         if (m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, cutTime) == false) {
1104             emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(cutTime.frames(m_document->fps())), info.track), ErrorMessage);
1105             return;
1106         }
1107
1108         kDebug() << "// UNCUTTING CLIPS: ITEM 1 (" << item->startPos().frames(25) << "x" << item->endPos().frames(25) << ")";
1109         kDebug() << "// UNCUTTING CLIPS: ITEM 2 (" << dup->startPos().frames(25) << "x" << dup->endPos().frames(25) << ")";
1110         kDebug() << "// UNCUTTING CLIPS, INFO (" << info.startPos.frames(25) << "x" << info.endPos.frames(25) << ") , CUT: " << cutTime.frames(25);;
1111         //deleteClip(dup->info());
1112
1113
1114         if (dup->isSelected()) emit clipItemSelected(NULL);
1115         dup->baseClip()->removeReference();
1116         m_document->updateClip(dup->baseClip()->getId());
1117         scene()->removeItem(dup);
1118         delete dup;
1119
1120         ItemInfo clipinfo = item->info();
1121         clipinfo.track = m_document->tracksCount() - clipinfo.track;
1122         bool success = m_document->renderer()->mltResizeClipEnd(clipinfo, info.endPos - info.startPos);
1123         if (success) {
1124             item->resizeEnd((int) info.endPos.frames(m_document->fps()));
1125         } else
1126             emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
1127
1128     }
1129     QTimer::singleShot(3000, this, SLOT(slotEnableRefresh()));
1130 }
1131
1132 void CustomTrackView::slotEnableRefresh() {
1133     m_blockRefresh = false;
1134 }
1135
1136 void CustomTrackView::slotAddTransitionToSelectedClips(QDomElement transition) {
1137     QList<QGraphicsItem *> itemList = scene()->selectedItems();
1138     if (itemList.count() == 1) {
1139         if (itemList.at(0)->type() == AVWIDGET) {
1140             ClipItem *item = (ClipItem *) itemList.at(0);
1141             ItemInfo info;
1142             info.track = item->track();
1143             ClipItem *transitionClip = NULL;
1144             const int transitiontrack = getPreviousVideoTrack(info.track);
1145             GenTime pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
1146             if (pos < item->startPos() + item->duration() / 2) {
1147                 // add transition to clip start
1148                 info.startPos = item->startPos();
1149                 if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.startPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
1150                 if (transitionClip && transitionClip->endPos() < item->endPos()) {
1151                     info.endPos = transitionClip->endPos();
1152                 } else info.endPos = info.startPos + GenTime(65, m_document->fps());
1153             } else {
1154                 // add transition to clip  end
1155                 info.endPos = item->endPos();
1156                 if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.endPos.frames(m_document->fps()), m_document->tracksCount() - transitiontrack);
1157                 if (transitionClip && transitionClip->startPos() > item->startPos()) {
1158                     info.startPos = transitionClip->startPos();
1159                 } else info.startPos = info.endPos - GenTime(65, m_document->fps());
1160                 if (transition.attribute("tag") == "luma") EffectsList::setParameter(transition, "reverse", "1");
1161             }
1162             slotAddTransition(item, info, transitiontrack, transition);
1163         }
1164     } else for (int i = 0; i < itemList.count(); i++) {
1165             if (itemList.at(i)->type() == AVWIDGET) {
1166                 ClipItem *item = (ClipItem *) itemList.at(i);
1167                 ItemInfo info;
1168                 info.startPos = item->startPos();
1169                 info.endPos = info.startPos + GenTime(65, m_document->fps());
1170                 info.track = item->track();
1171                 int transitiontrack = getPreviousVideoTrack(info.track);
1172                 slotAddTransition(item, info, transitiontrack, transition);
1173             }
1174         }
1175 }
1176
1177 void CustomTrackView::slotAddTransition(ClipItem* clip, ItemInfo transitionInfo, int endTrack, QDomElement transition) {
1178     AddTransitionCommand* command = new AddTransitionCommand(this, transitionInfo, endTrack, transition, false, true);
1179     m_commandStack->push(command);
1180     m_document->setModified(true);
1181 }
1182
1183 void CustomTrackView::addTransition(ItemInfo transitionInfo, int endTrack, QDomElement params) {
1184     Transition *tr = new Transition(transitionInfo, endTrack, m_document->fps(), params);
1185     scene()->addItem(tr);
1186
1187     //kDebug() << "---- ADDING transition " << params.attribute("value");
1188     m_document->renderer()->mltAddTransition(tr->transitionTag(), endTrack, m_document->tracksCount() - transitionInfo.track, transitionInfo.startPos, transitionInfo.endPos, tr->toXML());
1189     m_document->setModified(true);
1190 }
1191
1192 void CustomTrackView::deleteTransition(ItemInfo transitionInfo, int endTrack, QDomElement params) {
1193     Transition *item = getTransitionItemAt((int)transitionInfo.startPos.frames(m_document->fps()), transitionInfo.track);
1194     if (!item) {
1195         emit displayMessage(i18n("Select clip to delete"), ErrorMessage);
1196         return;
1197     }
1198     m_document->renderer()->mltDeleteTransition(item->transitionTag(), endTrack, m_document->tracksCount() - transitionInfo.track, transitionInfo.startPos, transitionInfo.endPos, item->toXML());
1199     if (m_dragItem == item) m_dragItem = NULL;
1200     delete item;
1201     emit transitionItemSelected(NULL);
1202     m_document->setModified(true);
1203 }
1204
1205 void CustomTrackView::slotTransitionUpdated(Transition *tr, QDomElement old) {
1206     EditTransitionCommand *command = new EditTransitionCommand(this, tr->track(), tr->startPos(), old, tr->toXML(), true);
1207     m_commandStack->push(command);
1208     m_document->setModified(true);
1209 }
1210
1211 void CustomTrackView::slotTransitionTrackUpdated(Transition *tr, int track) {
1212     QDomElement old = tr->toXML().cloneNode().toElement();
1213     if (track == 0) {
1214         track = getPreviousVideoTrack(tr->track());
1215         tr->setForcedTrack(false, track);
1216     } else {
1217         tr->setForcedTrack(true, m_document->tracksCount() + 1 - track);
1218     }
1219     EditTransitionCommand *command = new EditTransitionCommand(this, tr->track(), tr->startPos(), old, tr->toXML(), true);
1220     m_commandStack->push(command);
1221     m_document->setModified(true);
1222 }
1223
1224 void CustomTrackView::updateTransition(int track, GenTime pos, QDomElement oldTransition, QDomElement transition, bool updateTransitionWidget) {
1225     Transition *item = getTransitionItemAt((int)pos.frames(m_document->fps()), track);
1226     if (!item) {
1227         kWarning() << "Unable to find transition at pos :" << pos.frames(m_document->fps()) << ", ON track: " << track;
1228         return;
1229     }
1230     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);
1231     item->setTransitionParameters(transition);
1232     if (updateTransitionWidget) emit transitionItemSelected(item, true);
1233     m_document->setModified(true);
1234 }
1235
1236 void CustomTrackView::dragMoveEvent(QDragMoveEvent * event) {
1237     event->setDropAction(Qt::IgnoreAction);
1238     const int track = (int)(mapToScene(event->pos()).y() / m_tracksHeight);
1239     const int pos = mapToScene(event->pos()).x();
1240     //kDebug() << "// DRAG MOVE TO TRACK: " << track;
1241     if (m_selectionGroup) {
1242         m_selectionGroup->setPos(pos, event->pos().y());
1243         event->setDropAction(Qt::MoveAction);
1244         if (event->mimeData()->hasFormat("kdenlive/producerslist") || event->mimeData()->hasFormat("kdenlive/clip")) {
1245             event->acceptProposedAction();
1246         }
1247     } else {
1248         QGraphicsView::dragMoveEvent(event);
1249     }
1250 }
1251
1252 void CustomTrackView::dragLeaveEvent(QDragLeaveEvent * event) {
1253     if (m_selectionGroup) {
1254         QList<QGraphicsItem *> items = m_selectionGroup->childItems();
1255         qDeleteAll(items);
1256         scene()->destroyItemGroup(m_selectionGroup);
1257         m_selectionGroup = NULL;
1258     } else QGraphicsView::dragLeaveEvent(event);
1259 }
1260
1261 void CustomTrackView::dropEvent(QDropEvent * event) {
1262     if (m_selectionGroup) {
1263         QList<QGraphicsItem *> items = m_selectionGroup->childItems();
1264         m_scene->clearSelection();
1265         resetSelectionGroup();
1266         for (int i = 0; i < items.count(); i++) {
1267             ClipItem *item = static_cast <ClipItem *>(items.at(i));
1268             AddTimelineClipCommand *command = new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), false, false);
1269             m_commandStack->push(command);
1270             item->baseClip()->addReference();
1271             m_document->updateClip(item->baseClip()->getId());
1272             ItemInfo info;
1273             info = item->info();
1274             if (item->baseClip()->isTransparent()) {
1275                 // add transparency transition
1276                 int endTrack = getPreviousVideoTrack(info.track);
1277                 Transition *tr = new Transition(info, endTrack, m_document->fps(), MainWindow::transitions.getEffectByTag("composite", "alphatransparency"), true);
1278                 scene()->addItem(tr);
1279                 m_document->renderer()->mltAddTransition(tr->transitionTag(), endTrack, m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
1280             }
1281             info.track = m_document->tracksCount() - item->track();
1282             m_document->renderer()->mltInsertClip(info, item->xml(), item->baseClip()->producer(item->track()));
1283             item->setSelected(true);
1284         }
1285         m_document->setModified(true);
1286     } else QGraphicsView::dropEvent(event);
1287     setFocus();
1288 }
1289
1290
1291 QStringList CustomTrackView::mimeTypes() const {
1292     QStringList qstrList;
1293     // list of accepted mime types for drop
1294     qstrList.append("text/plain");
1295     qstrList.append("kdenlive/producerslist");
1296     qstrList.append("kdenlive/clip");
1297     return qstrList;
1298 }
1299
1300 Qt::DropActions CustomTrackView::supportedDropActions() const {
1301     // returns what actions are supported when dropping
1302     return Qt::MoveAction;
1303 }
1304
1305 void CustomTrackView::setDuration(int duration) {
1306     if (duration > sceneRect().width())
1307         setSceneRect(0, 0, (duration + 100), sceneRect().height());
1308     m_projectDuration = duration;
1309 }
1310
1311 int CustomTrackView::duration() const {
1312     return m_projectDuration;
1313 }
1314
1315 void CustomTrackView::addTrack(TrackInfo type, int ix) {
1316     if (ix == -1) m_document->insertTrack(ix, type);
1317     else {
1318         m_document->insertTrack(m_document->tracksCount() - ix, type);
1319         // insert track in MLT playlist
1320         m_document->renderer()->mltInsertTrack(m_document->tracksCount() - ix, type.type == VIDEOTRACK);
1321
1322         double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
1323         QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY);
1324         QList<QGraphicsItem *> selection = m_scene->items(r);
1325         resetSelectionGroup();
1326
1327         m_selectionGroup = new AbstractGroupItem(m_document->fps());
1328         scene()->addItem(m_selectionGroup);
1329         for (int i = 0; i < selection.count(); i++) {
1330             if (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET)
1331                 m_selectionGroup->addToGroup(selection.at(i));
1332         }
1333         QPointF top = m_selectionGroup->boundingRect().topLeft();
1334         const int width = m_selectionGroup->boundingRect().width();
1335         const int height = m_selectionGroup->boundingRect().height();
1336         m_selectionGroup->setPos(top);
1337         m_selectionGroup->translate(-top.x(), -top.y() + 1);
1338
1339         // Move graphic items
1340         m_selectionGroup->setPos(m_selectionGroup->pos().x(), m_selectionGroup->pos().y() + m_tracksHeight);
1341
1342         // adjust track number
1343         QList<QGraphicsItem *> children = m_selectionGroup->childItems();
1344         for (int i = 0; i < children.count(); i++) {
1345             AbstractClipItem *item = static_cast <AbstractClipItem *>(children.at(i));
1346             item->updateItem();
1347             ItemInfo clipinfo = item->info();
1348             if (item->type() == AVWIDGET) {
1349                 ClipItem *clip = static_cast <ClipItem *>(item);
1350                 // We add a move clip command so that we get the correct producer for new track number
1351                 if (clip->clipType() == AV || clip->clipType() == AUDIO) {
1352                     m_document->renderer()->mltUpdateClipProducer((int)(m_document->tracksCount() - clipinfo.track), clipinfo.startPos.frames(m_document->fps()), clip->baseClip()->producer(clipinfo.track));
1353                     kDebug() << "// UPDATING CLIP TO TRACK PROD: " << clipinfo.track;
1354                 }
1355             } else if (item->type() == TRANSITIONWIDGET) {
1356                 Transition *tr = static_cast <Transition *>(item);
1357                 int track = tr->transitionEndTrack();
1358                 if (track >= ix) {
1359                     tr->updateTransitionEndTrack(getPreviousVideoTrack(clipinfo.track));
1360                 }
1361             }
1362         }
1363         resetSelectionGroup();
1364
1365     }
1366     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), m_tracksHeight * m_document->tracksCount());
1367     setSceneRect(0, 0, sceneRect().width(), m_tracksHeight * m_document->tracksCount());
1368     verticalScrollBar()->setMaximum(m_tracksHeight * m_document->tracksCount());
1369     QTimer::singleShot(300, this, SIGNAL(trackHeightChanged()));
1370     //setFixedHeight(50 * m_tracksCount);
1371 }
1372
1373 void CustomTrackView::removeTrack(int ix) {
1374     // Delete track in MLT playlist
1375     m_document->renderer()->mltDeleteTrack(m_document->tracksCount() - ix);
1376     m_document->deleteTrack(m_document->tracksCount() - ix - 1);
1377
1378     double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
1379     QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY);
1380     QList<QGraphicsItem *> selection = m_scene->items(r);
1381
1382     resetSelectionGroup();
1383
1384     m_selectionGroup = new AbstractGroupItem(m_document->fps());
1385     scene()->addItem(m_selectionGroup);
1386     for (int i = 0; i < selection.count(); i++) {
1387         if (selection.at(i)->type() == AVWIDGET || selection.at(i)->type() == TRANSITIONWIDGET)
1388             m_selectionGroup->addToGroup(selection.at(i));
1389     }
1390
1391     QPointF top = m_selectionGroup->boundingRect().topLeft();
1392     const int width = m_selectionGroup->boundingRect().width();
1393     const int height = m_selectionGroup->boundingRect().height();
1394     m_selectionGroup->setPos(top);
1395     m_selectionGroup->translate(-top.x(), -top.y() + 1);
1396
1397     // Move graphic items
1398     m_selectionGroup->setPos(m_selectionGroup->pos().x(), m_selectionGroup->pos().y() - m_tracksHeight);
1399
1400     // adjust track number
1401     QList<QGraphicsItem *> children = m_selectionGroup->childItems();
1402     kDebug() << "// FOUND CLIPS TO MOVE: " << children.count();
1403     for (int i = 0; i < children.count(); i++) {
1404         if (children.at(i)->type() == AVWIDGET) {
1405             ClipItem *clip = static_cast <ClipItem *>(children.at(i));
1406             clip->updateItem();
1407             ItemInfo clipinfo = clip->info();
1408             kDebug() << "// CLIP TRK IS: " << clipinfo.track;
1409             // We add a move clip command so that we get the correct producer for new track number
1410             if (clip->clipType() == AV || clip->clipType() == AUDIO)
1411                 m_document->renderer()->mltUpdateClipProducer((int)(m_document->tracksCount() - clipinfo.track), clipinfo.startPos.frames(m_document->fps()), clip->baseClip()->producer(clipinfo.track));
1412         } else if (children.at(i)->type() == TRANSITIONWIDGET) {
1413             Transition *tr = static_cast <Transition *>(children.at(i));
1414             tr->updateItem();
1415             int track = tr->transitionEndTrack();
1416             if (track >= ix) {
1417                 ItemInfo clipinfo = tr->info();
1418                 tr->updateTransitionEndTrack(getPreviousVideoTrack(clipinfo.track));
1419             }
1420         }
1421     }
1422     resetSelectionGroup();
1423
1424     m_cursorLine->setLine(m_cursorLine->line().x1(), 0, m_cursorLine->line().x1(), m_tracksHeight * m_document->tracksCount());
1425     setSceneRect(0, 0, sceneRect().width(), m_tracksHeight * m_document->tracksCount());
1426     verticalScrollBar()->setMaximum(m_tracksHeight * m_document->tracksCount());
1427     QTimer::singleShot(300, this, SIGNAL(trackHeightChanged()));
1428 }
1429
1430 void CustomTrackView::changeTrack(int ix, TrackInfo type) {
1431     int tracknumber = m_document->tracksCount() - ix;
1432     m_document->setTrackType(tracknumber - 1, type);
1433     m_document->renderer()->mltChangeTrackState(tracknumber, m_document->trackInfoAt(tracknumber - 1).isMute, m_document->trackInfoAt(tracknumber - 1).isBlind);
1434     QTimer::singleShot(300, this, SIGNAL(trackHeightChanged()));
1435     viewport()->update();
1436 }
1437
1438
1439 void CustomTrackView::slotSwitchTrackAudio(int ix) {
1440     /*for (int i = 0; i < m_document->tracksCount(); i++)
1441         kDebug() << "TRK " << i << " STATE: " << m_document->trackInfoAt(i).isMute << m_document->trackInfoAt(i).isBlind;*/
1442
1443     int tracknumber = m_document->tracksCount() - ix;
1444
1445     m_document->switchTrackAudio(tracknumber - 1, !m_document->trackInfoAt(tracknumber - 1).isMute);
1446     kDebug() << "NEXT TRK STATE: " << m_document->trackInfoAt(tracknumber - 1).isMute << m_document->trackInfoAt(tracknumber - 1).isBlind;
1447     m_document->renderer()->mltChangeTrackState(tracknumber, m_document->trackInfoAt(tracknumber - 1).isMute, m_document->trackInfoAt(tracknumber - 1).isBlind);
1448     m_document->setModified(true);
1449 }
1450
1451 void CustomTrackView::slotSwitchTrackVideo(int ix) {
1452     int tracknumber = m_document->tracksCount() - ix;
1453     m_document->switchTrackVideo(tracknumber - 1, !m_document->trackInfoAt(tracknumber - 1).isBlind);
1454     m_document->renderer()->mltChangeTrackState(tracknumber, m_document->trackInfoAt(tracknumber - 1).isMute, m_document->trackInfoAt(tracknumber - 1).isBlind);
1455     m_document->setModified(true);
1456 }
1457
1458 void CustomTrackView::slotRemoveSpace() {
1459     GenTime pos;
1460     int track = 0;
1461     if (m_menuPosition.isNull()) {
1462         pos = GenTime(cursorPos(), m_document->fps());
1463         bool ok;
1464         track = QInputDialog::getInteger(this, i18n("Remove Space"), i18n("Track"), 0, 0, m_document->tracksCount() - 1, 1, &ok);
1465         if (!ok) return;
1466     } else {
1467         pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
1468         track = (int)(mapToScene(m_menuPosition).y() / m_tracksHeight);
1469     }
1470     ClipItem *item = getClipItemAt(pos, track);
1471     if (item) {
1472         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);
1473         return;
1474     }
1475     int length = m_document->renderer()->mltGetSpaceLength(pos, m_document->tracksCount() - track);
1476     //kDebug() << "// GOT LENGT; " << length;
1477     if (length <= 0) {
1478         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);
1479         return;
1480     }
1481     InsertSpaceCommand *command = new InsertSpaceCommand(this, pos, track, GenTime(-length, m_document->fps()), true);
1482     m_commandStack->push(command);
1483 }
1484
1485 void CustomTrackView::slotInsertSpace() {
1486     GenTime pos;
1487     int track = 0;
1488     if (m_menuPosition.isNull()) {
1489         pos = GenTime(cursorPos(), m_document->fps());
1490     } else {
1491         pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
1492         track = (int)(mapToScene(m_menuPosition).y() / m_tracksHeight) + 1;
1493     }
1494     SpacerDialog d(GenTime(65, m_document->fps()), m_document->timecode(), track, m_document->tracksCount(), this);
1495     if (d.exec() != QDialog::Accepted) return;
1496     GenTime spaceDuration = d.selectedDuration();
1497     track = d.selectedTrack();
1498     ClipItem *item = getClipItemAt(pos, track);
1499     if (item) pos = item->startPos();
1500
1501     InsertSpaceCommand *command = new InsertSpaceCommand(this, pos, track, spaceDuration, true);
1502     m_commandStack->push(command);
1503 }
1504
1505 void CustomTrackView::insertSpace(const GenTime &pos, int track, const GenTime duration, bool add) {
1506     int diff = duration.frames(m_document->fps());
1507     if (!add) diff = -diff;
1508     QList<QGraphicsItem *> itemList;
1509     if (track == -1) itemList = scene()->items(pos.frames(m_document->fps()) , 1, sceneRect().width() - pos.frames(m_document->fps()), sceneRect().height());
1510     else itemList = scene()->items(pos.frames(m_document->fps()) , track * m_tracksHeight + 1, sceneRect().width() - pos.frames(m_document->fps()), m_tracksHeight - 2);
1511     resetSelectionGroup();
1512     m_selectionGroup = new AbstractGroupItem(m_document->fps());
1513     scene()->addItem(m_selectionGroup);
1514     for (int i = 0; i < itemList.count(); i++) {
1515         if (itemList.at(i)->type() == AVWIDGET || itemList.at(i)->type() == TRANSITIONWIDGET) {
1516             /*AbstractClipItem *item = static_cast <AbstractClipItem *> (itemList.at(i));
1517             if (item->endPos() > pos)*/
1518             m_selectionGroup->addToGroup(itemList.at(i));
1519             //item->moveBy(diff, 0);
1520         }
1521     }
1522     QPointF top = m_selectionGroup->boundingRect().topLeft();
1523     const int width = m_selectionGroup->boundingRect().width();
1524     const int height = m_selectionGroup->boundingRect().height();
1525     m_selectionGroup->setPos(top);
1526     m_selectionGroup->translate(-top.x(), -top.y() + 1);
1527     m_selectionGroup->moveBy(diff, 0);
1528     resetSelectionGroup();
1529     if (track != -1) track = m_document->tracksCount() - track;
1530     if (!add) m_document->renderer()->mltInsertSpace(pos, track, GenTime() - duration);
1531     else m_document->renderer()->mltInsertSpace(pos, track, duration);
1532 }
1533
1534 void CustomTrackView::deleteClip(const QString &clipId) {
1535     QList<QGraphicsItem *> itemList = items();
1536     for (int i = 0; i < itemList.count(); i++) {
1537         if (itemList.at(i)->type() == AVWIDGET) {
1538             ClipItem *item = (ClipItem *)itemList.at(i);
1539             if (item->clipProducer() == clipId) {
1540                 AddTimelineClipCommand *command = new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), true, true);
1541                 m_commandStack->push(command);
1542                 //delete item;
1543             }
1544         }
1545     }
1546 }
1547
1548 void CustomTrackView::setCursorPos(int pos, bool seek) {
1549     emit cursorMoved((int)(m_cursorPos), (int)(pos));
1550     m_cursorPos = pos;
1551     m_cursorLine->setPos(pos, 0);
1552     if (seek) m_document->renderer()->seek(GenTime(pos, m_document->fps()));
1553     else if (m_autoScroll) checkScrolling();
1554 }
1555
1556 void CustomTrackView::updateCursorPos() {
1557     m_cursorLine->setPos(m_cursorPos, 0);
1558 }
1559
1560 int CustomTrackView::cursorPos() {
1561     return (int)(m_cursorPos);
1562 }
1563
1564 void CustomTrackView::moveCursorPos(int delta) {
1565     if (m_cursorPos + delta < 0) delta = 0 - m_cursorPos;
1566     emit cursorMoved((int)(m_cursorPos), (int)((m_cursorPos + delta)));
1567     m_cursorPos += delta;
1568     m_cursorLine->setPos(m_cursorPos, 0);
1569     m_document->renderer()->seek(GenTime(m_cursorPos, m_document->fps()));
1570     //if (m_autoScroll && m_scale < 50) checkScrolling();
1571 }
1572
1573 void CustomTrackView::checkScrolling() {
1574     int vert = verticalScrollBar()->value();
1575     int hor = cursorPos();
1576     ensureVisible(hor, vert + 10, 2, 2, 50, 0);
1577     //centerOn(QPointF(cursorPos(), m_tracksHeight));
1578     /*QRect rectInView = viewport()->rect();
1579     int delta = rectInView.width() / 3;
1580     int max = rectInView.right() + horizontalScrollBar()->value() - delta;
1581     //kDebug() << "CURSOR POS: "<<m_cursorPos<< "Scale: "<<m_scale;
1582     if (m_cursorPos * m_scale >= max) horizontalScrollBar()->setValue((int)(horizontalScrollBar()->value() + 1 + m_scale));*/
1583 }
1584
1585 void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) {
1586     if (m_moveOpMode == SEEK) m_moveOpMode = NONE;
1587     QGraphicsView::mouseReleaseEvent(event);
1588     if (m_scrollTimer.isActive()) m_scrollTimer.stop();
1589     if (event->button() == Qt::MidButton) {
1590         return;
1591     }
1592     setDragMode(QGraphicsView::NoDrag);
1593     if (m_operationMode == MOVEGUIDE) {
1594         setCursor(Qt::ArrowCursor);
1595         m_operationMode = NONE;
1596         m_dragGuide->setFlag(QGraphicsItem::ItemIsMovable, false);
1597         EditGuideCommand *command = new EditGuideCommand(this, m_dragGuide->position(), m_dragGuide->label(), GenTime(m_dragGuide->pos().x(), m_document->fps()), m_dragGuide->label(), false);
1598         m_commandStack->push(command);
1599         m_dragGuide->updateGuide(GenTime(m_dragGuide->pos().x(), m_document->fps()));
1600         m_dragGuide = NULL;
1601         m_dragItem = NULL;
1602         return;
1603     } else if (m_operationMode == SPACER) {
1604         int endClick = (int)(mapToScene(event->pos()).x() + 0.5);
1605         int mappedClick = (int)(mapToScene(m_clickEvent).x() + 0.5);
1606         int track = (int)(mapToScene(m_clickEvent).y() / m_tracksHeight);
1607         ClipItem *item = getClipItemAt(mappedClick, track);
1608         if (item) mappedClick = item->startPos().frames(m_document->fps());
1609         int diff = m_selectionGroup->pos().x() - m_spacerStart;//endClick - mappedClick;
1610         kDebug() << "// MOVING SPACER DIFF:" << diff;
1611         if (diff < 0) mappedClick += diff;
1612         InsertSpaceCommand *command = new InsertSpaceCommand(this, GenTime(mappedClick, m_document->fps()), track, GenTime(diff, m_document->fps()), false);
1613         m_commandStack->push(command);
1614         track = m_document->tracksCount() - track;
1615         m_document->renderer()->mltInsertSpace(GenTime(mappedClick, m_document->fps()), track, GenTime(diff, m_document->fps()));
1616         resetSelectionGroup();
1617         m_operationMode = NONE;
1618     }
1619
1620     if (m_dragItem == NULL && m_selectionGroup == NULL) {
1621         emit transitionItemSelected(NULL);
1622         return;
1623     }
1624     ItemInfo info;
1625     if (m_dragItem) info = m_dragItem->info();
1626
1627     if (m_operationMode == MOVE) {
1628         setCursor(Qt::OpenHandCursor);
1629
1630         if (m_selectionGroup == NULL) {
1631             // we are moving one clip, easy
1632             if (m_dragItem->type() == AVWIDGET && (m_dragItemInfo.startPos != info.startPos || m_dragItemInfo.track != info.track)) {
1633                 ClipItem *item = static_cast <ClipItem *>(m_dragItem);
1634                 bool success = m_document->renderer()->mltMoveClip((int)(m_document->tracksCount() - m_dragItemInfo.track), (int)(m_document->tracksCount() - m_dragItem->track()), (int) m_dragItemInfo.startPos.frames(m_document->fps()), (int)(m_dragItem->startPos().frames(m_document->fps())), item->baseClip()->producer(info.track));
1635                 if (success) {
1636                     MoveClipCommand *command = new MoveClipCommand(this, m_dragItemInfo, info, false, false);
1637                     m_commandStack->push(command);
1638                     if (item->baseClip()->isTransparent()) {
1639                         // Also move automatic transition
1640                         Transition *tr = getTransitionItemAt((int) m_dragItemInfo.startPos.frames(m_document->fps()), m_dragItemInfo.track);
1641                         if (tr && tr->isAutomatic()) {
1642                             tr->updateTransitionEndTrack(getPreviousVideoTrack(info.track));
1643                             m_document->renderer()->mltMoveTransition(tr->transitionTag(), m_document->tracksCount() - m_dragItemInfo.track, m_document->tracksCount() - info.track, tr->transitionEndTrack(), m_dragItemInfo.startPos, m_dragItemInfo.endPos, info.startPos, info.endPos);
1644                             tr->setPos((int) info.startPos.frames(m_document->fps()), (int)(info.track * m_tracksHeight + 1));
1645                         }
1646                     }
1647                 } else {
1648                     // undo last move and emit error message
1649                     MoveClipCommand *command = new MoveClipCommand(this, info, m_dragItemInfo, true);
1650                     m_commandStack->push(command);
1651                     emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(m_dragItemInfo.startPos.frames(m_document->fps()))), ErrorMessage);
1652                 }
1653             }
1654             if (m_dragItem->type() == TRANSITIONWIDGET && (m_dragItemInfo.startPos != info.startPos || m_dragItemInfo.track != info.track)) {
1655                 MoveTransitionCommand *command = new MoveTransitionCommand(this, m_dragItemInfo, info, false);
1656                 m_commandStack->push(command);
1657                 Transition *transition = (Transition *) m_dragItem;
1658                 transition->updateTransitionEndTrack(getPreviousVideoTrack(m_dragItem->track()));
1659                 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);
1660             }
1661         } else {
1662             // Moving several clips. We need to delete them and readd them to new position,
1663             // or they might overlap each other during the move
1664
1665             QList<QGraphicsItem *> items = m_selectionGroup->childItems();
1666
1667             GenTime timeOffset = GenTime(m_selectionGroup->scenePos().x(), m_document->fps()) - m_selectionGroupInfo.startPos;
1668             const int trackOffset = m_selectionGroup->track() - m_selectionGroupInfo.track;
1669             // kDebug() << "&DROPPED GRPOUP:" << timeOffset.frames(25) << "TRK OFF: " << trackOffset;
1670             if (timeOffset != GenTime() || trackOffset != 0) {
1671                 QUndoCommand *moveClips = new QUndoCommand();
1672                 moveClips->setText("Move clips");
1673                 // remove items in MLT playlist
1674                 for (int i = 0; i < items.count(); i++) {
1675                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
1676                     ItemInfo info = item->info();
1677                     if (item->type() == AVWIDGET) {
1678                         if (m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, info.startPos) == false) {
1679                             // error, clip cannot be removed from playlist
1680                             emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(info.startPos.frames(m_document->fps())), info.track), ErrorMessage);
1681                         } else {
1682                             // clip removed from playlist, create command
1683                             ClipItem *clip = static_cast <ClipItem*>(item);
1684                             new AddTimelineClipCommand(this, clip->xml(), clip->clipProducer(), info, clip->effectList(), false, true, moveClips);
1685                         }
1686                     } else {
1687                         Transition *tr = static_cast <Transition*>(item);
1688                         new AddTransitionCommand(this, info, tr->transitionEndTrack(), tr->toXML(), true, false, moveClips);
1689                         m_document->renderer()->mltDeleteTransition(tr->transitionTag(), tr->transitionEndTrack(), m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
1690                     }
1691                 }
1692
1693                 for (int i = 0; i < items.count(); i++) {
1694                     // re-add items in correct place
1695                     AbstractClipItem *item = static_cast <AbstractClipItem *>(items.at(i));
1696                     item->updateItem();
1697                     ItemInfo info = item->info();
1698                     /*info.startPos = info.startPos + timeOffset;
1699                     info.endPos = info.endPos + timeOffset;
1700                     info.track = info.track + trackOffset;*/
1701                     if (item->type() == AVWIDGET) {
1702                         ClipItem *clip = static_cast <ClipItem*>(item);
1703                         new AddTimelineClipCommand(this, clip->xml(), clip->clipProducer(), info, clip->effectList(), false, false, moveClips);
1704                         info.track = m_document->tracksCount() - info.track;
1705                         m_document->renderer()->mltInsertClip(info, clip->xml(), clip->baseClip()->producer(info.track));
1706                     } else {
1707                         Transition *tr = static_cast <Transition*>(item);
1708                         int newTrack = tr->transitionEndTrack();
1709                         if (!tr->forcedTrack()) {
1710                             newTrack += trackOffset;
1711                             if (newTrack < 0 || newTrack > m_document->tracksCount()) newTrack = getPreviousVideoTrack(info.track);
1712                         }
1713                         new AddTransitionCommand(this, info, newTrack, tr->toXML(), false, false, moveClips);
1714
1715                         m_document->renderer()->mltAddTransition(tr->transitionTag(), newTrack, m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
1716                     }
1717                 }
1718                 m_commandStack->push(moveClips);
1719             }
1720         }
1721
1722     } else if (m_operationMode == RESIZESTART && m_dragItem->startPos() != m_dragItemInfo.startPos) {
1723         // resize start
1724         if (m_dragItem->type() == AVWIDGET) {
1725             ItemInfo resizeinfo = m_dragItemInfo;
1726             resizeinfo.track = m_document->tracksCount() - resizeinfo.track;
1727             bool success = m_document->renderer()->mltResizeClipStart(resizeinfo, m_dragItem->startPos() - m_dragItemInfo.startPos);
1728             if (success) {
1729                 updateClipFade(static_cast <ClipItem *>(m_dragItem));
1730                 ResizeClipCommand *command = new ResizeClipCommand(this, m_dragItemInfo, info, false);
1731                 m_commandStack->push(command);
1732             } else {
1733                 m_dragItem->resizeStart((int) m_dragItemInfo.startPos.frames(m_document->fps()));
1734                 emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
1735             }
1736         } else if (m_dragItem->type() == TRANSITIONWIDGET) {
1737             MoveTransitionCommand *command = new MoveTransitionCommand(this, m_dragItemInfo, info, false);
1738             m_commandStack->push(command);
1739             Transition *transition = static_cast <Transition *>(m_dragItem);
1740             m_document->renderer()->mltMoveTransition(transition->transitionTag(), (int)(m_document->tracksCount() - m_dragItemInfo.track), (int)(m_document->tracksCount() - m_dragItemInfo.track), 0, m_dragItemInfo.startPos, m_dragItemInfo.endPos, info.startPos, info.endPos);
1741         }
1742
1743         //m_document->renderer()->doRefresh();
1744     } else if (m_operationMode == RESIZEEND && m_dragItem->endPos() != m_dragItemInfo.endPos) {
1745         // resize end
1746         if (m_dragItem->type() == AVWIDGET) {
1747             ItemInfo resizeinfo = info;
1748             resizeinfo.track = m_document->tracksCount() - resizeinfo.track;
1749             bool success = m_document->renderer()->mltResizeClipEnd(resizeinfo, resizeinfo.endPos - resizeinfo.startPos);
1750             if (success) {
1751                 ResizeClipCommand *command = new ResizeClipCommand(this, m_dragItemInfo, info, false);
1752                 m_commandStack->push(command);
1753                 updateClipFade(static_cast <ClipItem *>(m_dragItem), true);
1754             } else {
1755                 m_dragItem->resizeEnd((int) m_dragItemInfo.endPos.frames(m_document->fps()));
1756                 emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
1757             }
1758         } else if (m_dragItem->type() == TRANSITIONWIDGET) {
1759             MoveTransitionCommand *command = new MoveTransitionCommand(this, m_dragItemInfo, info, false);
1760             m_commandStack->push(command);
1761             Transition *transition = static_cast <Transition *>(m_dragItem);
1762             m_document->renderer()->mltMoveTransition(transition->transitionTag(), (int)(m_document->tracksCount() - m_dragItemInfo.track), (int)(m_document->tracksCount() - m_dragItemInfo.track), 0, m_dragItemInfo.startPos, m_dragItemInfo.endPos, info.startPos, info.endPos);
1763         }
1764         //m_document->renderer()->doRefresh();
1765     } else if (m_operationMode == FADEIN) {
1766         // resize fade in effect
1767         ClipItem * item = (ClipItem *) m_dragItem;
1768         int ix = item->hasEffect("volume", "fadein");
1769         if (ix != -1) {
1770             QDomElement oldeffect = item->effectAt(ix);
1771             int start = item->cropStart().frames(m_document->fps());
1772             int end = item->fadeIn();
1773             if (end == 0) {
1774                 slotDeleteEffect(item, oldeffect);
1775             } else {
1776                 end += start;
1777                 QDomElement effect = oldeffect.cloneNode().toElement();
1778                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
1779                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
1780                 slotUpdateClipEffect(item, effect, oldeffect, ix);
1781                 emit clipItemSelected(item, ix);
1782             }
1783         } else if (item->fadeIn() != 0) {
1784             QDomElement effect = MainWindow::audioEffects.getEffectByTag("volume", "fadein").cloneNode().toElement();
1785             EffectsList::setParameter(effect, "out", QString::number(item->fadeIn()));
1786             slotAddEffect(effect, m_dragItem->startPos(), m_dragItem->track());
1787         }
1788     } else if (m_operationMode == FADEOUT) {
1789         // resize fade in effect
1790         ClipItem * item = (ClipItem *) m_dragItem;
1791         int ix = item->hasEffect("volume", "fadeout");
1792         if (ix != -1) {
1793             QDomElement oldeffect = item->effectAt(ix);
1794             int end = (item->duration() + item->cropStart()).frames(m_document->fps());
1795             int start = item->fadeOut();
1796             if (start == 0) {
1797                 slotDeleteEffect(item, oldeffect);
1798             } else {
1799                 start = end - start;
1800                 QDomElement effect = oldeffect.cloneNode().toElement();
1801                 EffectsList::setParameter(oldeffect, "in", QString::number(start));
1802                 EffectsList::setParameter(oldeffect, "out", QString::number(end));
1803                 slotUpdateClipEffect(item, effect, oldeffect, ix);
1804                 emit clipItemSelected(item, ix);
1805             }
1806         } else if (item->fadeOut() != 0) {
1807             QDomElement effect = MainWindow::audioEffects.getEffectByTag("volume", "fadeout").cloneNode().toElement();
1808             EffectsList::setParameter(effect, "out", QString::number(item->fadeOut()));
1809             slotAddEffect(effect, m_dragItem->startPos(), m_dragItem->track());
1810         }
1811     } else if (m_operationMode == KEYFRAME) {
1812         // update the MLT effect
1813         ClipItem * item = (ClipItem *) m_dragItem;
1814         QString previous = item->keyframes(item->selectedEffectIndex());
1815         item->updateKeyframeEffect();
1816         QString next = item->keyframes(item->selectedEffectIndex());
1817         EditKeyFrameCommand *command = new EditKeyFrameCommand(this, item->track(), item->startPos(), item->selectedEffectIndex(), previous, next, false);
1818         m_commandStack->push(command);
1819         updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
1820     }
1821
1822     emit transitionItemSelected((m_dragItem && m_dragItem->type() == TRANSITIONWIDGET) ? static_cast <Transition *>(m_dragItem) : NULL);
1823     m_document->setModified(true);
1824     m_operationMode = NONE;
1825 }
1826
1827 void CustomTrackView::deleteClip(ItemInfo info) {
1828     ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()) + 1, info.track);
1829
1830     if (!item || m_document->renderer()->mltRemoveClip(m_document->tracksCount() - info.track, info.startPos) == false) {
1831         emit displayMessage(i18n("Error removing clip at %1 on track %2", m_document->timecode().getTimecodeFromFrames(info.startPos.frames(m_document->fps())), info.track), ErrorMessage);
1832         return;
1833     }
1834     if (item->isSelected()) emit clipItemSelected(NULL);
1835     item->baseClip()->removeReference();
1836     m_document->updateClip(item->baseClip()->getId());
1837
1838     if (item->baseClip()->isTransparent()) {
1839         // also remove automatic transition
1840         Transition *tr = getTransitionItemAt((int) info.startPos.frames(m_document->fps()), info.track);
1841         if (tr && tr->isAutomatic()) {
1842             m_document->renderer()->mltDeleteTransition(tr->transitionTag(), tr->transitionEndTrack(), m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
1843             scene()->removeItem(tr);
1844             delete tr;
1845         }
1846     }
1847     scene()->removeItem(item);
1848     if (m_dragItem == item) m_dragItem = NULL;
1849     delete item;
1850     m_document->renderer()->doRefresh();
1851 }
1852
1853 void CustomTrackView::deleteSelectedClips() {
1854     QList<QGraphicsItem *> itemList = scene()->selectedItems();
1855     if (itemList.count() == 0) {
1856         emit displayMessage(i18n("Select clip to delete"), ErrorMessage);
1857         return;
1858     }
1859     QUndoCommand *deleteSelected = new QUndoCommand();
1860     deleteSelected->setText(i18n("Delete selected items"));
1861     for (int i = 0; i < itemList.count(); i++) {
1862         if (itemList.at(i)->type() == AVWIDGET) {
1863             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
1864             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), true, true, deleteSelected);
1865         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
1866             Transition *item = static_cast <Transition *>(itemList.at(i));
1867             ItemInfo info;
1868             info.startPos = item->startPos();
1869             info.endPos = item->endPos();
1870             info.track = item->track();
1871             new AddTransitionCommand(this, info, item->transitionEndTrack(), item->toXML(), true, true, deleteSelected);
1872         }
1873     }
1874     m_commandStack->push(deleteSelected);
1875 }
1876
1877 void CustomTrackView::changeClipSpeed() {
1878     QList<QGraphicsItem *> itemList = scene()->selectedItems();
1879     if (itemList.count() == 0) {
1880         emit displayMessage(i18n("Select clip to change speed"), ErrorMessage);
1881         return;
1882     }
1883     QUndoCommand *changeSelected = new QUndoCommand();
1884     changeSelected->setText("Edit clip speed");
1885     for (int i = 0; i < itemList.count(); i++) {
1886         if (itemList.at(i)->type() == AVWIDGET) {
1887             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
1888             ItemInfo info = item->info();
1889             int percent = QInputDialog::getInteger(this, i18n("Edit Clip Speed"), i18n("New speed (percents)"), item->speed() * 100, 1, 300);
1890             double speed = (double) percent / 100.0;
1891             if (item->speed() != speed)
1892                 new ChangeSpeedCommand(this, info, item->speed(), speed, item->clipProducer(), true, changeSelected);
1893         }
1894     }
1895     m_commandStack->push(changeSelected);
1896 }
1897
1898 void CustomTrackView::doChangeClipSpeed(ItemInfo info, const double speed, const double oldspeed, const QString &id) {
1899     DocClipBase *baseclip = m_document->clipManager()->getClipById(id);
1900     ClipItem *item = getClipItemAt((int) info.startPos.frames(m_document->fps()) + 1, info.track);
1901     info.track = m_document->tracksCount() - item->track();
1902     int endPos = m_document->renderer()->mltChangeClipSpeed(info, speed, oldspeed, baseclip->producer());
1903     //kDebug() << "//CH CLIP SPEED: " << speed << "x" << oldspeed << ", END POS: " << endPos;
1904     item->setSpeed(speed);
1905     item->updateRectGeometry();
1906     if (item->cropDuration().frames(m_document->fps()) > endPos)
1907         item->AbstractClipItem::resizeEnd(info.startPos.frames(m_document->fps()) + endPos, speed);
1908     m_document->setModified(true);
1909 }
1910
1911 void CustomTrackView::cutSelectedClips() {
1912     QList<QGraphicsItem *> itemList = scene()->selectedItems();
1913     GenTime currentPos = GenTime(m_cursorPos, m_document->fps());
1914     for (int i = 0; i < itemList.count(); i++) {
1915         if (itemList.at(i)->type() == AVWIDGET) {
1916             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
1917             if (currentPos > item->startPos() && currentPos <  item->endPos()) {
1918                 RazorClipCommand *command = new RazorClipCommand(this, item->info(), currentPos, true);
1919                 m_commandStack->push(command);
1920             }
1921         }
1922     }
1923 }
1924
1925 void CustomTrackView::addClip(QDomElement xml, const QString &clipId, ItemInfo info, EffectsList effects) {
1926     DocClipBase *baseclip = m_document->clipManager()->getClipById(clipId);
1927     if (baseclip == NULL) {
1928         emit displayMessage(i18n("No clip copied"), ErrorMessage);
1929         return;
1930     }
1931     ClipItem *item = new ClipItem(baseclip, info, m_document->fps());
1932     item->setEffectList(effects);
1933     scene()->addItem(item);
1934     if (item->baseClip()->isTransparent()) {
1935         // add transparency transition
1936         int endTrack = getPreviousVideoTrack(info.track);
1937         Transition *tr = new Transition(info, endTrack, m_document->fps(), MainWindow::transitions.getEffectByTag("composite", "alphatransparency"), true);
1938         scene()->addItem(tr);
1939         m_document->renderer()->mltAddTransition(tr->transitionTag(), endTrack, m_document->tracksCount() - info.track, info.startPos, info.endPos, tr->toXML());
1940     }
1941
1942     baseclip->addReference();
1943     m_document->updateClip(baseclip->getId());
1944     info.track = m_document->tracksCount() - info.track;
1945     m_document->renderer()->mltInsertClip(info, xml, baseclip->producer(info.track));
1946     for (int i = 0; i < item->effectsCount(); i++) {
1947         m_document->renderer()->mltAddEffect(info.track, info.startPos, item->getEffectArgs(item->effectAt(i)), false);
1948     }
1949     m_document->renderer()->doRefresh();
1950 }
1951
1952 void CustomTrackView::slotUpdateClip(const QString &clipId) {
1953     QList<QGraphicsItem *> list = scene()->items();
1954     ClipItem *clip = NULL;
1955     for (int i = 0; i < list.size(); ++i) {
1956         if (list.at(i)->type() == AVWIDGET) {
1957             clip = static_cast <ClipItem *>(list.at(i));
1958             if (clip->clipProducer() == clipId) {
1959                 clip->refreshClip();
1960                 ItemInfo info = clip->info();
1961                 info.track = m_document->tracksCount() - clip->track();
1962                 m_document->renderer()->mltUpdateClip(info, clip->xml(), clip->baseClip()->producer());
1963             }
1964         }
1965     }
1966 }
1967
1968 ClipItem *CustomTrackView::getClipItemAt(int pos, int track) {
1969     QList<QGraphicsItem *> list = scene()->items(QPointF(pos , track * m_tracksHeight + m_tracksHeight / 2));
1970     ClipItem *clip = NULL;
1971     for (int i = 0; i < list.size(); ++i) {
1972         if (list.at(i)->type() == AVWIDGET) {
1973             clip = static_cast <ClipItem *>(list.at(i));
1974             break;
1975         }
1976     }
1977     return clip;
1978 }
1979
1980 ClipItem *CustomTrackView::getClipItemAt(GenTime pos, int track) {
1981     int framepos = (int)(pos.frames(m_document->fps()));
1982     return getClipItemAt(framepos, track);
1983 }
1984
1985 Transition *CustomTrackView::getTransitionItemAt(int pos, int track) {
1986     QList<QGraphicsItem *> list = scene()->items(QPointF(pos, (track + 1) * m_tracksHeight));
1987     Transition *clip = NULL;
1988     for (int i = 0; i < list.size(); ++i) {
1989         if (list.at(i)->type() == TRANSITIONWIDGET) {
1990             clip = static_cast <Transition *>(list.at(i));
1991             break;
1992         }
1993     }
1994     return clip;
1995 }
1996
1997 Transition *CustomTrackView::getTransitionItemAt(GenTime pos, int track) {
1998     int framepos = (int)(pos.frames(m_document->fps()));
1999     return getTransitionItemAt(framepos, track);
2000 }
2001
2002 void CustomTrackView::moveClip(const ItemInfo start, const ItemInfo end) {
2003     ClipItem *item = getClipItemAt((int) start.startPos.frames(m_document->fps()) + 1, start.track);
2004     if (!item) {
2005         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);
2006         kDebug() << "----------------  ERROR, CANNOT find clip to move at.. ";// << startPos.x() * m_scale * FRAME_SIZE + 1 << ", " << startPos.y() * m_tracksHeight + m_tracksHeight / 2;
2007         return;
2008     }
2009     //kDebug() << "----------------  Move CLIP FROM: " << startPos.x() << ", END:" << endPos.x() << ",TRACKS: " << startPos.y() << " TO " << endPos.y();
2010
2011     bool success = m_document->renderer()->mltMoveClip((int)(m_document->tracksCount() - start.track), (int)(m_document->tracksCount() - end.track), (int) start.startPos.frames(m_document->fps()), (int)end.startPos.frames(m_document->fps()), item->baseClip()->producer(end.track));
2012     if (success) {
2013         item->setPos((int) end.startPos.frames(m_document->fps()), (int)(end.track * m_tracksHeight + 1));
2014         if (item->baseClip()->isTransparent()) {
2015             // Also move automatic transition
2016             Transition *tr = getTransitionItemAt((int) start.startPos.frames(m_document->fps()), start.track);
2017             if (tr && tr->isAutomatic()) {
2018                 tr->updateTransitionEndTrack(getPreviousVideoTrack(end.track));
2019                 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);
2020                 tr->setPos((int) end.startPos.frames(m_document->fps()), (int)(end.track * m_tracksHeight + 1));
2021             }
2022         }
2023     } else {
2024         // undo last move and emit error message
2025         emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(end.startPos.frames(m_document->fps()))), ErrorMessage);
2026     }
2027 }
2028
2029 void CustomTrackView::moveTransition(const ItemInfo start, const ItemInfo end) {
2030     Transition *item = getTransitionItemAt((int)start.startPos.frames(m_document->fps()), start.track);
2031     if (!item) {
2032         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);
2033         kDebug() << "----------------  ERROR, CANNOT find transition to move... ";// << startPos.x() * m_scale * FRAME_SIZE + 1 << ", " << startPos.y() * m_tracksHeight + m_tracksHeight / 2;
2034         return;
2035     }
2036     //kDebug() << "----------------  Move TRANSITION FROM: " << startPos.x() << ", END:" << endPos.x() << ",TRACKS: " << oldtrack << " TO " << newtrack;
2037
2038     //kDebug()<<"///  RESIZE TRANS START: ("<< startPos.x()<<"x"<< startPos.y()<<") / ("<<endPos.x()<<"x"<< endPos.y()<<")";
2039     if (end.endPos - end.startPos == start.endPos - start.startPos) {
2040         // Transition was moved
2041         item->setPos((int) end.startPos.frames(m_document->fps()), (end.track) * m_tracksHeight + 1);
2042     } else if (end.endPos == start.endPos) {
2043         // Transition start resize
2044         item->resizeStart((int) end.startPos.frames(m_document->fps()));
2045     } else {
2046         // Transition end resize;
2047         item->resizeEnd((int) end.endPos.frames(m_document->fps()));
2048     }
2049     //item->moveTransition(GenTime((int) (endPos.x() - startPos.x()), m_document->fps()));
2050     item->updateTransitionEndTrack(getPreviousVideoTrack(end.track));
2051     m_document->renderer()->mltMoveTransition(item->transitionTag(), m_document->tracksCount() - start.track, m_document->tracksCount() - end.track, item->transitionEndTrack(), start.startPos, start.endPos, end.startPos, end.endPos);
2052 }
2053
2054 void CustomTrackView::resizeClip(const ItemInfo start, const ItemInfo end) {
2055     int offset = 0;
2056     bool resizeClipStart = true;
2057     if (start.startPos == end.startPos) resizeClipStart = false;
2058     /*if (resizeClipStart) offset = 1;
2059     else offset = -1;*/
2060     ClipItem *item = getClipItemAt((int)(start.startPos.frames(m_document->fps()) + offset), start.track);
2061     if (!item) {
2062         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);
2063         kDebug() << "----------------  ERROR, CANNOT find clip to resize at... "; // << startPos;
2064         return;
2065     }
2066     if (resizeClipStart) {
2067         ItemInfo clipinfo = item->info();
2068         clipinfo.track = m_document->tracksCount() - clipinfo.track;
2069         bool success = m_document->renderer()->mltResizeClipStart(clipinfo, end.startPos - item->startPos());
2070         if (success) {
2071             item->resizeStart((int) end.startPos.frames(m_document->fps()));
2072             updateClipFade(item);
2073         } else emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
2074     } else {
2075         ItemInfo clipinfo = item->info();
2076         clipinfo.track = m_document->tracksCount() - clipinfo.track;
2077         bool success = m_document->renderer()->mltResizeClipEnd(clipinfo, end.endPos - clipinfo.startPos);
2078         if (success) {
2079             item->resizeEnd((int) end.endPos.frames(m_document->fps()));
2080             updateClipFade(item, true);
2081         } else emit displayMessage(i18n("Error when resizing clip"), ErrorMessage);
2082     }
2083     m_document->renderer()->doRefresh();
2084 }
2085
2086 void CustomTrackView::updateClipFade(ClipItem * item, bool updateFadeOut) {
2087     if (!updateFadeOut) {
2088         int end = item->fadeIn();
2089         if (end != 0) {
2090             // there is a fade in effect
2091             int effectPos = item->hasEffect("volume", "fadein");
2092             if (effectPos == -1) return;
2093             QDomElement oldeffect = item->effectAt(effectPos);
2094             int start = item->cropStart().frames(m_document->fps());
2095             int max = item->cropDuration().frames(m_document->fps());
2096             if (end > max) {
2097                 item->setFadeIn(max);
2098                 end = item->fadeIn();
2099             }
2100             end += start;
2101             EffectsList::setParameter(oldeffect, "in", QString::number(start));
2102             EffectsList::setParameter(oldeffect, "out", QString::number(end));
2103             QHash <QString, QString> effectParams = item->getEffectArgs(oldeffect);
2104             if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), effectParams))
2105                 emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
2106             // if fade effect is displayed, update the effect edit widget with new clip duration
2107             if (item->isSelected() && effectPos == item->selectedEffectIndex()) emit clipItemSelected(item, effectPos);
2108         }
2109     } else {
2110         int start = item->fadeOut();
2111         if (start != 0) {
2112             // there is a fade in effect
2113             int effectPos = item->hasEffect("volume", "fadeout");
2114             if (effectPos == -1) return;
2115             QDomElement oldeffect = item->effectAt(effectPos);
2116             int end = (item->duration() - item->cropStart()).frames(m_document->fps());
2117             int max = item->cropDuration().frames(m_document->fps());
2118             if (end > max) {
2119                 item->setFadeOut(max);
2120                 start = item->fadeOut();
2121             }
2122             start = end - start;
2123             EffectsList::setParameter(oldeffect, "in", QString::number(start));
2124             EffectsList::setParameter(oldeffect, "out", QString::number(end));
2125             QHash <QString, QString> effectParams = item->getEffectArgs(oldeffect);
2126             if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - item->track(), item->startPos(), effectParams))
2127                 emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
2128             // if fade effect is displayed, update the effect edit widget with new clip duration
2129             if (item->isSelected() && effectPos == item->selectedEffectIndex()) emit clipItemSelected(item, effectPos);
2130         }
2131     }
2132 }
2133
2134 double CustomTrackView::getSnapPointForPos(double pos) {
2135     return m_scene->getSnapPointForPos(pos, KdenliveSettings::snaptopoints());
2136 }
2137
2138 void CustomTrackView::updateSnapPoints(AbstractClipItem *selected) {
2139     QList <GenTime> snaps;
2140     GenTime offset;
2141     if (selected) offset = selected->duration();
2142     QList<QGraphicsItem *> itemList = items();
2143     for (int i = 0; i < itemList.count(); i++) {
2144         if (itemList.at(i)->type() == AVWIDGET && itemList.at(i) != selected) {
2145             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
2146             GenTime start = item->startPos();
2147             GenTime end = item->endPos();
2148             snaps.append(start);
2149             snaps.append(end);
2150             QList < GenTime > markers = item->snapMarkers();
2151             for (int i = 0; i < markers.size(); ++i) {
2152                 GenTime t = markers.at(i);
2153                 snaps.append(t);
2154                 if (t > offset) snaps.append(t - offset);
2155             }
2156             if (offset != GenTime()) {
2157                 if (start > offset) snaps.append(start - offset);
2158                 if (end > offset) snaps.append(end - offset);
2159             }
2160         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
2161             Transition *transition = static_cast <Transition*>(itemList.at(i));
2162             GenTime start = transition->startPos();
2163             GenTime end = transition->endPos();
2164             snaps.append(start);
2165             snaps.append(end);
2166             if (offset != GenTime()) {
2167                 if (start > offset) snaps.append(start - offset);
2168                 if (end > offset) snaps.append(end - offset);
2169             }
2170         }
2171     }
2172
2173     // add cursor position
2174     GenTime pos = GenTime(m_cursorPos, m_document->fps());
2175     snaps.append(pos);
2176     if (offset != GenTime()) snaps.append(pos - offset);
2177
2178     // add guides
2179     for (int i = 0; i < m_guides.count(); i++) {
2180         snaps.append(m_guides.at(i)->position());
2181         if (offset != GenTime()) snaps.append(m_guides.at(i)->position() - offset);
2182     }
2183
2184     qSort(snaps);
2185     m_scene->setSnapList(snaps);
2186     //for (int i = 0; i < m_snapPoints.size(); ++i)
2187     //    kDebug() << "SNAP POINT: " << m_snapPoints.at(i).frames(25);
2188 }
2189
2190 void CustomTrackView::slotSeekToPreviousSnap() {
2191     updateSnapPoints(NULL);
2192     GenTime res = m_scene->previousSnapPoint(GenTime(m_cursorPos, m_document->fps()));
2193     setCursorPos((int) res.frames(m_document->fps()));
2194     checkScrolling();
2195 }
2196
2197 void CustomTrackView::slotSeekToNextSnap() {
2198     updateSnapPoints(NULL);
2199     GenTime res = m_scene->nextSnapPoint(GenTime(m_cursorPos, m_document->fps()));
2200     setCursorPos((int) res.frames(m_document->fps()));
2201     checkScrolling();
2202 }
2203
2204 void CustomTrackView::clipStart() {
2205     ClipItem *item = getMainActiveClip();
2206     if (item != NULL) {
2207         setCursorPos((int) item->startPos().frames(m_document->fps()));
2208         checkScrolling();
2209     }
2210 }
2211
2212 void CustomTrackView::clipEnd() {
2213     ClipItem *item = getMainActiveClip();
2214     if (item != NULL) {
2215         setCursorPos((int) item->endPos().frames(m_document->fps()));
2216         checkScrolling();
2217     }
2218 }
2219
2220 void CustomTrackView::slotAddClipMarker(const QString &id, GenTime t, QString c) {
2221     QString oldcomment = m_document->clipManager()->getClipById(id)->markerComment(t);
2222     AddMarkerCommand *command = new AddMarkerCommand(this, oldcomment, c, id, t, true);
2223     m_commandStack->push(command);
2224 }
2225
2226 void CustomTrackView::slotDeleteClipMarker(const QString &comment, const QString &id, const GenTime &position) {
2227     AddMarkerCommand *command = new AddMarkerCommand(this, comment, QString(), id, position, true);
2228     m_commandStack->push(command);
2229 }
2230
2231 void CustomTrackView::slotDeleteAllClipMarkers(const QString &id) {
2232     DocClipBase *base = m_document->clipManager()->getClipById(id);
2233     QList <CommentedTime> markers = base->commentedSnapMarkers();
2234
2235     if (markers.isEmpty()) {
2236         emit displayMessage(i18n("Clip has no markers"), ErrorMessage);
2237         return;
2238     }
2239     QUndoCommand *deleteMarkers = new QUndoCommand();
2240     deleteMarkers->setText("Delete clip markers");
2241
2242     for (int i = 0; i < markers.size(); i++) {
2243         new AddMarkerCommand(this, markers.at(i).comment(), QString(), id, markers.at(i).time(), true, deleteMarkers);
2244     }
2245     m_commandStack->push(deleteMarkers);
2246 }
2247
2248 void CustomTrackView::addMarker(const QString &id, const GenTime &pos, const QString comment) {
2249     DocClipBase *base = m_document->clipManager()->getClipById(id);
2250     if (!comment.isEmpty()) base->addSnapMarker(pos, comment);
2251     else base->deleteSnapMarker(pos);
2252     m_document->setModified(true);
2253     viewport()->update();
2254 }
2255
2256 bool sortGuidesList(const Guide *g1 , const Guide *g2) {
2257     return (*g1).position() < (*g2).position();
2258 }
2259
2260 void CustomTrackView::editGuide(const GenTime oldPos, const GenTime pos, const QString &comment) {
2261     if (oldPos > GenTime() && pos > GenTime()) {
2262         // move guide
2263         for (int i = 0; i < m_guides.count(); i++) {
2264             if (m_guides.at(i)->position() == oldPos) {
2265                 Guide *item = m_guides.at(i);
2266                 item->updateGuide(pos, comment);
2267                 break;
2268             }
2269         }
2270     } else if (pos > GenTime()) addGuide(pos, comment);
2271     else {
2272         // remove guide
2273         bool found = false;
2274         for (int i = 0; i < m_guides.count(); i++) {
2275             if (m_guides.at(i)->position() == oldPos) {
2276                 Guide *item = m_guides.takeAt(i);
2277                 delete item;
2278                 found = true;
2279                 break;
2280             }
2281         }
2282         if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
2283     }
2284     qSort(m_guides.begin(), m_guides.end(), sortGuidesList);
2285     m_document->syncGuides(m_guides);
2286 }
2287
2288 bool CustomTrackView::addGuide(const GenTime pos, const QString &comment) {
2289     for (int i = 0; i < m_guides.count(); i++) {
2290         if (m_guides.at(i)->position() == pos) {
2291             emit displayMessage(i18n("A guide already exists at position %1", m_document->timecode().getTimecodeFromFrames(pos.frames(m_document->fps()))), ErrorMessage);
2292             return false;
2293         }
2294     }
2295     Guide *g = new Guide(this, pos, comment, m_document->fps(), m_tracksHeight * m_document->tracksCount());
2296     scene()->addItem(g);
2297     m_guides.append(g);
2298     qSort(m_guides.begin(), m_guides.end(), sortGuidesList);
2299     m_document->syncGuides(m_guides);
2300     return true;
2301 }
2302
2303 void CustomTrackView::slotAddGuide() {
2304     CommentedTime marker(GenTime(m_cursorPos, m_document->fps()), i18n("Guide"));
2305     MarkerDialog d(NULL, marker, m_document->timecode(), i18n("Add Guide"), this);
2306     if (d.exec() != QDialog::Accepted) return;
2307     if (addGuide(d.newMarker().time(), d.newMarker().comment())) {
2308         EditGuideCommand *command = new EditGuideCommand(this, GenTime(), QString(), d.newMarker().time(), d.newMarker().comment(), false);
2309         m_commandStack->push(command);
2310     }
2311 }
2312
2313 void CustomTrackView::slotEditGuide() {
2314     GenTime pos = GenTime(m_cursorPos, m_document->fps());
2315     bool found = false;
2316     for (int i = 0; i < m_guides.count(); i++) {
2317         if (m_guides.at(i)->position() == pos) {
2318             slotEditGuide(m_guides.at(i)->info());
2319             found = true;
2320             break;
2321         }
2322     }
2323     if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
2324 }
2325
2326 void CustomTrackView::slotEditGuide(CommentedTime guide) {
2327     MarkerDialog d(NULL, guide, m_document->timecode(), i18n("Edit Guide"), this);
2328     if (d.exec() == QDialog::Accepted) {
2329         EditGuideCommand *command = new EditGuideCommand(this, guide.time(), guide.comment(), d.newMarker().time(), d.newMarker().comment(), true);
2330         m_commandStack->push(command);
2331     }
2332 }
2333
2334
2335 void CustomTrackView::slotDeleteGuide() {
2336     GenTime pos = GenTime(m_cursorPos, m_document->fps());
2337     bool found = false;
2338     for (int i = 0; i < m_guides.count(); i++) {
2339         if (m_guides.at(i)->position() == pos) {
2340             EditGuideCommand *command = new EditGuideCommand(this, m_guides.at(i)->position(), m_guides.at(i)->label(), GenTime(), QString(), true);
2341             m_commandStack->push(command);
2342             found = true;
2343             break;
2344         }
2345     }
2346     if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
2347 }
2348
2349 void CustomTrackView::slotDeleteAllGuides() {
2350     QUndoCommand *deleteAll = new QUndoCommand();
2351     deleteAll->setText("Delete all guides");
2352     for (int i = 0; i < m_guides.count(); i++) {
2353         EditGuideCommand *command = new EditGuideCommand(this, m_guides.at(i)->position(), m_guides.at(i)->label(), GenTime(), QString(), true, deleteAll);
2354     }
2355     m_commandStack->push(deleteAll);
2356 }
2357
2358 void CustomTrackView::setTool(PROJECTTOOL tool) {
2359     m_tool = tool;
2360 }
2361
2362 void CustomTrackView::setScale(double scaleFactor) {
2363     QMatrix matrix;
2364     matrix = matrix.scale(scaleFactor, 1);
2365     m_scene->setScale(scaleFactor);
2366     //scale(scaleFactor, 1);
2367     m_animationTimer->stop();
2368     if (m_visualTip) {
2369         delete m_visualTip;
2370         m_visualTip = NULL;
2371     }
2372     if (m_animation) {
2373         delete m_animation;
2374         m_animation = NULL;
2375     }
2376     /*double pos = cursorPos() / m_scale;
2377     m_scale = scaleFactor;
2378     m_scene->setScale(m_scale);
2379     int vert = verticalScrollBar()->value();
2380     kDebug() << " HHHHHHHH  SCALING: " << m_scale;
2381     QList<QGraphicsItem *> itemList = items();
2382     for (int i = 0; i < itemList.count(); i++) {
2383         if (itemList.at(i)->type() == AVWIDGET || itemList.at(i)->type() == TRANSITIONWIDGET) {
2384             AbstractClipItem *clip = (AbstractClipItem *)itemList.at(i);
2385             clip->setRect(0, 0, (qreal) clip->duration().frames(m_document->fps()) * m_scale - .5, clip->rect().height());
2386             clip->setPos((qreal) clip->startPos().frames(m_document->fps()) * m_scale, clip->pos().y());
2387         }
2388     }
2389
2390     for (int i = 0; i < m_guides.count(); i++) {
2391         m_guides.at(i)->updatePosition(m_scale);
2392     }
2393
2394     setSceneRect(0, 0, (m_projectDuration + 100) * m_scale, sceneRect().height());
2395     updateCursorPos();*/
2396     setMatrix(matrix);
2397     centerOn(QPointF(cursorPos(), m_tracksHeight));
2398     //verticalScrollBar()->setValue(vert);*/
2399 }
2400
2401 void CustomTrackView::slotRefreshGuides() {
2402     if (KdenliveSettings::showmarkers()) {
2403         kDebug() << "// refresh GUIDES";
2404         for (int i = 0; i < m_guides.count(); i++) {
2405             m_guides.at(i)->update();
2406         }
2407     }
2408 }
2409
2410 void CustomTrackView::drawBackground(QPainter * painter, const QRectF & rect) {
2411     QColor base = palette().button().color();
2412     QRectF r = rect;
2413     r.setWidth(r.width() + 1);
2414     painter->setClipRect(r);
2415     painter->drawLine(r.left(), 0, r.right(), 0);
2416     uint max = m_document->tracksCount();
2417     for (uint i = 0; i < max;i++) {
2418         /*if (max - i - 1 == m_selectedTrack) painter->fillRect(r.left(), m_tracksHeight * i + 1, r.right() - r.left() + 1, m_tracksHeight - 1, QBrush(QColor(211, 205, 147)));
2419                else*/
2420         if (m_document->trackInfoAt(max - i - 1).type == AUDIOTRACK) painter->fillRect(r.left(), m_tracksHeight * i + 1, r.right() - r.left() + 1, m_tracksHeight - 1, QBrush(QColor(240, 240, 255)));
2421         painter->drawLine(r.left(), m_tracksHeight * (i + 1), r.right(), m_tracksHeight * (i + 1));
2422     }
2423     int lowerLimit = m_tracksHeight * m_document->tracksCount() + 1;
2424     if (height() > lowerLimit)
2425         painter->fillRect(QRectF(r.left(), lowerLimit, r.width(), height() - lowerLimit), QBrush(base));
2426 }
2427
2428 bool CustomTrackView::findString(const QString &text) {
2429     QString marker;
2430     for (int i = 0; i < m_searchPoints.size(); ++i) {
2431         marker = m_searchPoints.at(i).comment();
2432         if (marker.contains(text, Qt::CaseInsensitive)) {
2433             setCursorPos(m_searchPoints.at(i).time().frames(m_document->fps()), true);
2434             int vert = verticalScrollBar()->value();
2435             int hor = cursorPos();
2436             ensureVisible(hor, vert + 10, 2, 2, 50, 0);
2437             m_findIndex = i;
2438             return true;
2439         }
2440     }
2441     return false;
2442 }
2443
2444 bool CustomTrackView::findNextString(const QString &text) {
2445     QString marker;
2446     for (int i = m_findIndex + 1; i < m_searchPoints.size(); ++i) {
2447         marker = m_searchPoints.at(i).comment();
2448         if (marker.contains(text, Qt::CaseInsensitive)) {
2449             setCursorPos(m_searchPoints.at(i).time().frames(m_document->fps()), true);
2450             int vert = verticalScrollBar()->value();
2451             int hor = cursorPos();
2452             ensureVisible(hor, vert + 10, 2, 2, 50, 0);
2453             m_findIndex = i;
2454             return true;
2455         }
2456     }
2457     m_findIndex = -1;
2458     return false;
2459 }
2460
2461 void CustomTrackView::initSearchStrings() {
2462     m_searchPoints.clear();
2463     QList<QGraphicsItem *> itemList = items();
2464     for (int i = 0; i < itemList.count(); i++) {
2465         // parse all clip names
2466         if (itemList.at(i)->type() == AVWIDGET) {
2467             ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
2468             GenTime start = item->startPos();
2469             CommentedTime t(start, item->clipName());
2470             m_searchPoints.append(t);
2471             // add all clip markers
2472             QList < CommentedTime > markers = item->commentedSnapMarkers();
2473             m_searchPoints += markers;
2474         }
2475     }
2476
2477     // add guides
2478     for (int i = 0; i < m_guides.count(); i++) {
2479         m_searchPoints.append(m_guides.at(i)->info());
2480     }
2481
2482     qSort(m_searchPoints);
2483 }
2484
2485 void CustomTrackView::clearSearchStrings() {
2486     m_searchPoints.clear();
2487     m_findIndex = 0;
2488 }
2489
2490 void CustomTrackView::copyClip() {
2491     while (m_copiedItems.count() > 0) {
2492         delete m_copiedItems.takeFirst();
2493     }
2494     QList<QGraphicsItem *> itemList = scene()->selectedItems();
2495     if (itemList.count() == 0) {
2496         emit displayMessage(i18n("Select a clip before copying"), ErrorMessage);
2497         return;
2498     }
2499     for (int i = 0; i < itemList.count(); i++) {
2500         if (itemList.at(i)->type() == AVWIDGET) {
2501             ClipItem *dup = static_cast <ClipItem *>(itemList.at(i));
2502             m_copiedItems.append(dup->clone(dup->info()));
2503         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
2504             Transition *dup = static_cast <Transition *>(itemList.at(i));
2505             m_copiedItems.append(dup->clone());
2506         }
2507     }
2508 }
2509
2510 bool CustomTrackView::canBePastedTo(ItemInfo info, int type) const {
2511     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));
2512     QList<QGraphicsItem *> collisions = scene()->items(rect, Qt::IntersectsItemBoundingRect);
2513     for (int i = 0; i < collisions.count(); i++) {
2514         if (collisions.at(i)->type() == type) return false;
2515     }
2516     return true;
2517 }
2518
2519 bool CustomTrackView::canBePasted(QList<AbstractClipItem *> items, GenTime offset, int trackOffset) const {
2520     for (int i = 0; i < items.count(); i++) {
2521         ItemInfo info = items.at(i)->info();
2522         info.startPos += offset;
2523         info.endPos += offset;
2524         info.track += trackOffset;
2525         if (!canBePastedTo(info, items.at(i)->type())) return false;
2526     }
2527     return true;
2528 }
2529
2530 bool CustomTrackView::canBeMoved(QList<AbstractClipItem *> items, GenTime offset, int trackOffset) const {
2531     QPainterPath movePath;
2532     movePath.moveTo(0, 0);
2533
2534     for (int i = 0; i < items.count(); i++) {
2535         ItemInfo info = items.at(i)->info();
2536         info.startPos = info.startPos + offset;
2537         info.endPos = info.endPos + offset;
2538         info.track = info.track + trackOffset;
2539         if (info.startPos < GenTime()) {
2540             // No clip should go below 0
2541             return false;
2542         }
2543         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));
2544         movePath.addRect(rect);
2545     }
2546     QList<QGraphicsItem *> collisions = scene()->items(movePath, Qt::IntersectsItemBoundingRect);
2547     for (int i = 0; i < collisions.count(); i++) {
2548         if ((collisions.at(i)->type() == AVWIDGET || collisions.at(i)->type() == TRANSITIONWIDGET) && !items.contains(static_cast <AbstractClipItem *>(collisions.at(i)))) {
2549             kDebug() << "  ////////////   CLIP COLLISION, MOVE NOT ALLOWED";
2550             return false;
2551         }
2552     }
2553     return true;
2554 }
2555
2556 void CustomTrackView::pasteClip() {
2557     if (m_copiedItems.count() == 0) {
2558         emit displayMessage(i18n("No clip copied"), ErrorMessage);
2559         return;
2560     }
2561     QPoint position;
2562     if (m_menuPosition.isNull()) position = mapFromGlobal(QCursor::pos());
2563     else position = m_menuPosition;
2564     GenTime pos = GenTime((int)(mapToScene(position).x()), m_document->fps());
2565     int track = (int)(position.y() / m_tracksHeight);
2566     ItemInfo first = m_copiedItems.at(0)->info();
2567
2568     GenTime offset = pos - first.startPos;
2569     int trackOffset = track - first.track;
2570
2571     if (!canBePasted(m_copiedItems, offset, trackOffset)) {
2572         emit displayMessage(i18n("Cannot paste selected clips"), ErrorMessage);
2573         return;
2574     }
2575     QUndoCommand *pasteClips = new QUndoCommand();
2576     pasteClips->setText("Paste clips");
2577
2578     for (int i = 0; i < m_copiedItems.count(); i++) {
2579         // parse all clip names
2580         if (m_copiedItems.at(i) && m_copiedItems.at(i)->type() == AVWIDGET) {
2581             ClipItem *clip = static_cast <ClipItem *>(m_copiedItems.at(i));
2582             ItemInfo info;
2583             info.startPos = clip->startPos() + offset;
2584             info.endPos = clip->endPos() + offset;
2585             info.cropStart = clip->cropStart();
2586             info.track = clip->track() + trackOffset;
2587             if (canBePastedTo(info, AVWIDGET)) {
2588                 new AddTimelineClipCommand(this, clip->xml(), clip->clipProducer(), info, clip->effectList(), true, false, pasteClips);
2589             } else emit displayMessage(i18n("Cannot paste clip to selected place"), ErrorMessage);
2590         } else if (m_copiedItems.at(i) && m_copiedItems.at(i)->type() == TRANSITIONWIDGET) {
2591             Transition *tr = static_cast <Transition *>(m_copiedItems.at(i));
2592             ItemInfo info;
2593             info.startPos = tr->startPos() + offset;
2594             info.endPos = tr->endPos() + offset;
2595             info.track = tr->track() + trackOffset;
2596             if (canBePastedTo(info, TRANSITIONWIDGET)) {
2597                 new AddTransitionCommand(this, info, tr->transitionEndTrack() + trackOffset, tr->toXML(), false, true, pasteClips);
2598             } else emit displayMessage(i18n("Cannot paste transition to selected place"), ErrorMessage);
2599         }
2600     }
2601     m_commandStack->push(pasteClips);
2602 }
2603
2604 void CustomTrackView::pasteClipEffects() {
2605     if (m_copiedItems.count() != 1 || m_copiedItems.at(0)->type() != AVWIDGET) {
2606         emit displayMessage(i18n("You must copy exactly one clip before pasting effects"), ErrorMessage);
2607         return;
2608     }
2609     ClipItem *clip = static_cast < ClipItem *>(m_copiedItems.at(0));
2610     EffectsList effects = clip->effectList();
2611
2612     QUndoCommand *paste = new QUndoCommand();
2613     paste->setText("Paste effects");
2614
2615     QList<QGraphicsItem *> clips = scene()->selectedItems();
2616     for (int i = 0; i < clips.count(); ++i) {
2617         if (clips.at(i)->type() == AVWIDGET) {
2618             ClipItem *item = static_cast < ClipItem *>(clips.at(i));
2619             for (int i = 0; i < clip->effectsCount(); i++) {
2620                 new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), clip->effectAt(i), true, paste);
2621             }
2622         }
2623     }
2624     m_commandStack->push(paste);
2625 }
2626
2627
2628 ClipItem *CustomTrackView::getClipUnderCursor() const {
2629     QRectF rect((double) m_cursorPos, 0.0, 1.0, (double)(m_tracksHeight * m_document->tracksCount()));
2630     QList<QGraphicsItem *> collisions = scene()->items(rect, Qt::IntersectsItemBoundingRect);
2631     for (int i = 0; i < collisions.count(); i++) {
2632         if (collisions.at(i)->type() == AVWIDGET) {
2633             return static_cast < ClipItem *>(collisions.at(i));
2634         }
2635     }
2636     return NULL;
2637 }
2638
2639 ClipItem *CustomTrackView::getMainActiveClip() const {
2640     QList<QGraphicsItem *> clips = scene()->selectedItems();
2641     if (clips.isEmpty()) {
2642         return getClipUnderCursor();
2643     } else {
2644         ClipItem *item = NULL;
2645         for (int i = 0; i < clips.count(); ++i) {
2646             if (clips.at(i)->type() == AVWIDGET)
2647                 item = static_cast < ClipItem *>(clips.at(i));
2648             if (item->startPos().frames(m_document->fps()) <= m_cursorPos && item->endPos().frames(m_document->fps()) >= m_cursorPos) break;
2649         }
2650         if (item) return item;
2651     }
2652     return NULL;
2653 }
2654
2655 ClipItem *CustomTrackView::getActiveClipUnderCursor(bool allowOutsideCursor) const {
2656     QList<QGraphicsItem *> clips = scene()->selectedItems();
2657     if (clips.isEmpty()) {
2658         return getClipUnderCursor();
2659     } else {
2660         ClipItem *item;
2661         // remove all items in the list that are not clips
2662         for (int i = 0; i < clips.count();) {
2663             if (clips.at(i)->type() != AVWIDGET) clips.removeAt(i);
2664             else i++;
2665         }
2666         if (clips.count() == 1 && allowOutsideCursor) return static_cast < ClipItem *>(clips.at(0));
2667         for (int i = 0; i < clips.count(); ++i) {
2668             if (clips.at(i)->type() == AVWIDGET)
2669                 item = static_cast < ClipItem *>(clips.at(i));
2670             if (item->startPos().frames(m_document->fps()) <= m_cursorPos && item->endPos().frames(m_document->fps()) >= m_cursorPos) return item;
2671         }
2672     }
2673     return NULL;
2674 }
2675
2676 void CustomTrackView::setInPoint() {
2677     ClipItem *clip = getActiveClipUnderCursor(true);
2678     if (clip == NULL) {
2679         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
2680         return;
2681     }
2682     ItemInfo startInfo = clip->info();
2683     ItemInfo endInfo = clip->info();
2684     endInfo.startPos = GenTime(m_cursorPos, m_document->fps());
2685     ResizeClipCommand *command = new ResizeClipCommand(this, startInfo, endInfo, true);
2686     m_commandStack->push(command);
2687 }
2688
2689 void CustomTrackView::setOutPoint() {
2690     ClipItem *clip = getActiveClipUnderCursor(true);
2691     if (clip == NULL) {
2692         emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
2693         return;
2694     }
2695     ItemInfo startInfo = clip->info();
2696     ItemInfo endInfo = clip->info();
2697     endInfo.endPos = GenTime(m_cursorPos, m_document->fps());
2698     ResizeClipCommand *command = new ResizeClipCommand(this, startInfo, endInfo, true);
2699     m_commandStack->push(command);
2700 }
2701
2702 void CustomTrackView::slotUpdateAllThumbs() {
2703     QList<QGraphicsItem *> itemList = items();
2704     ClipItem *item;
2705     Transition *transitionitem;
2706     for (int i = 0; i < itemList.count(); i++) {
2707         if (itemList.at(i)->type() == AVWIDGET) {
2708             item = static_cast <ClipItem *>(itemList.at(i));
2709             item->refreshClip();
2710             qApp->processEvents();
2711         }
2712     }
2713 }
2714
2715 void CustomTrackView::slotInsertTrack(int ix) {
2716     kDebug() << "// INSERTING TRK: " << ix;
2717     QDialog d(parentWidget());
2718     Ui::AddTrack_UI view;
2719     view.setupUi(&d);
2720     view.track_nb->setMaximum(m_document->tracksCount() - 1);
2721     view.track_nb->setValue(ix);
2722     d.setWindowTitle(i18n("Insert Track"));
2723
2724     if (d.exec() == QDialog::Accepted) {
2725         if (view.before_select->currentIndex() == 1) {
2726             ix++;
2727         }
2728         TrackInfo info;
2729         if (view.video_track->isChecked()) {
2730             info.type = VIDEOTRACK;
2731             info.isMute = false;
2732             info.isBlind = false;
2733         } else {
2734             info.type = AUDIOTRACK;
2735             info.isMute = false;
2736             info.isBlind = true;
2737         }
2738         addTimelineTrack(ix, info);
2739         m_document->setModified(true);
2740         /*AddTrackCommand* command = new AddTrackCommand(this, ix, info, true, true);
2741         m_commandStack->push(command);*/
2742     }
2743 }
2744
2745 void CustomTrackView::slotDeleteTrack(int ix) {
2746     bool ok;
2747     ix = QInputDialog::getInteger(this, i18n("Remove Track"), i18n("Track"), ix, 0, m_document->tracksCount() - 1, 1, &ok);
2748     if (ok) {
2749         TrackInfo info = m_document->trackInfoAt(m_document->tracksCount() - ix - 1);
2750         deleteTimelineTrack(ix, info);
2751         m_document->setModified(true);
2752         /*AddTrackCommand* command = new AddTrackCommand(this, ix, info, false, true);
2753         m_commandStack->push(command);*/
2754     }
2755 }
2756
2757 void CustomTrackView::slotChangeTrack(int ix) {
2758     QDialog d(parentWidget());
2759     Ui::AddTrack_UI view;
2760     view.setupUi(&d);
2761     view.label->setText(i18n("Change track"));
2762     view.before_select->setHidden(true);
2763     view.track_nb->setMaximum(m_document->tracksCount() - 1);
2764     view.track_nb->setValue(ix);
2765     d.setWindowTitle(i18n("Change Track Type"));
2766
2767     if (d.exec() == QDialog::Accepted) {
2768         TrackInfo info;
2769         if (view.video_track->isChecked()) {
2770             info.type = VIDEOTRACK;
2771             info.isMute = false;
2772             info.isBlind = false;
2773         } else {
2774             info.type = AUDIOTRACK;
2775             info.isMute = false;
2776             info.isBlind = true;
2777         }
2778         changeTimelineTrack(ix, info);
2779         m_document->setModified(true);
2780     }
2781 }
2782
2783 void CustomTrackView::addTimelineTrack(int ix, TrackInfo trackinfo) {
2784     double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
2785     QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY);
2786     QList<QGraphicsItem *> selection = m_scene->items(r);
2787     kDebug() << "// TRK RECT: " << r << ", ITEMS: " << selection.count();
2788     AddTrackCommand *addTrack = new AddTrackCommand(this, ix, trackinfo, true, true, addTrack);
2789     m_commandStack->push(addTrack);
2790 }
2791
2792 void CustomTrackView::deleteTimelineTrack(int ix, TrackInfo trackinfo) {
2793     double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
2794     QRectF r(0, startY, sceneRect().width(), m_tracksHeight / 2 - 1);
2795     QList<QGraphicsItem *> selection = m_scene->items(r);
2796     QUndoCommand *deleteTrack = new QUndoCommand();
2797     deleteTrack->setText("Delete track");
2798
2799     // Delete all clips in selected track
2800     for (int i = 0; i < selection.count(); i++) {
2801         if (selection.at(i)->type() == AVWIDGET) {
2802             ClipItem *item =  static_cast <ClipItem *>(selection.at(i));
2803             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), false, true, deleteTrack);
2804             m_scene->removeItem(item);
2805             delete item;
2806             item = NULL;
2807         } else if (selection.at(i)->type() == TRANSITIONWIDGET) {
2808             Transition *item =  static_cast <Transition *>(selection.at(i));
2809             new AddTransitionCommand(this, item->info(), item->transitionEndTrack(), item->toXML(), true, false, deleteTrack);
2810             m_scene->removeItem(item);
2811             delete item;
2812             item = NULL;
2813         }
2814     }
2815
2816     new AddTrackCommand(this, ix, trackinfo, false, true, deleteTrack);
2817     m_commandStack->push(deleteTrack);
2818 }
2819
2820 void CustomTrackView::changeTimelineTrack(int ix, TrackInfo trackinfo) {
2821     TrackInfo oldinfo = m_document->trackInfoAt(m_document->tracksCount() - ix);
2822     ChangeTrackCommand *changeTrack = new ChangeTrackCommand(this, ix, oldinfo, trackinfo, true);
2823     m_commandStack->push(changeTrack);
2824 }
2825
2826 #include "customtrackview.moc"