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