TracksView.cpp 23.6 KB
Newer Older
1
/*****************************************************************************
2
 * TracksView.cpp: QGraphicsView that contains the TracksScene
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *****************************************************************************
 * Copyright (C) 2008-2009 the VLMC team
 *
 * Authors: Ludovic Fauvet <etix@l0cal.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

23
#include <QScrollBar>
24
#include <QMouseEvent>
Ludovic Fauvet's avatar
Ludovic Fauvet committed
25
#include <QWheelEvent>
Ludovic Fauvet's avatar
Ludovic Fauvet committed
26 27
#include <QGraphicsLinearLayout>
#include <QGraphicsWidget>
Ludovic Fauvet's avatar
Ludovic Fauvet committed
28
#include <QGraphicsSceneDragDropEvent>
29
#include <QtDebug>
30
#include "TracksView.h"
31 32
#include "Library.h"
#include "GraphicsMovieItem.h"
33
#include "GraphicsCursorItem.h"
34
#include "Commands.h"
35
#include "GraphicsTrack.hpp"
36

37 38 39
TracksView::TracksView( QGraphicsScene* scene, MainWorkflow* mainWorkflow, WorkflowRenderer* renderer, QWidget* parent )
        : QGraphicsView( scene, parent ), m_scene( scene ), m_mainWorkflow( mainWorkflow ),
        m_renderer( renderer )
40
{
41
    //TODO should be defined by the settings
42
    m_tracksHeight = 25;
43

44
    m_tracksCount = mainWorkflow->getTrackCount( MainWorkflow::VideoTrack );
45

Ludovic Fauvet's avatar
Ludovic Fauvet committed
46 47
    m_numAudioTrack = 0;
    m_numVideoTrack = 0;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
48
    m_dragItem = NULL;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
49 50 51
    m_actionMove = false;
    m_actionRelativeX = -1;
    m_actionItem = NULL;
52
    m_tool = TOOL_DEFAULT;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
53

54 55 56
    setMouseTracking( true );
    setAcceptDrops( true );
    setContentsMargins( 0, 0, 0, 0 );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
57
    setFrameStyle( QFrame::NoFrame );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
58
    setAlignment( Qt::AlignLeft | Qt::AlignTop );
59

Ludovic Fauvet's avatar
Ludovic Fauvet committed
60
    m_cursorLine = new GraphicsCursorItem( QPen( QColor( 220, 30, 30 ) ) );
61

62
    m_scene->addItem( m_cursorLine );
63

64
    connect( m_cursorLine, SIGNAL( cursorPositionChanged(qint64) ),
65
             this, SLOT( ensureCursorVisible() ) );
66 67
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
68 69 70
void TracksView::createLayout()
{
    m_layout = new QGraphicsLinearLayout( Qt::Vertical );
71
    m_layout->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
72 73
    m_layout->setContentsMargins( 0, 0, 0, 0 );
    m_layout->setSpacing( 0 );
74
    m_layout->setPreferredWidth( 0 );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
75 76

    QGraphicsWidget* container = new QGraphicsWidget();
77
    container->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
78 79 80
    container->setContentsMargins( 0, 0, 0, 0 );
    container->setLayout( m_layout );

81 82 83 84
    // Create the initial layout
    // - 1 video track
    // - a separator
    // - 1 audio track
Ludovic Fauvet's avatar
Ludovic Fauvet committed
85
    addVideoTrack();
86 87

    m_separator = new QGraphicsWidget();
88
    m_separator->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
89 90 91
    m_separator->setPreferredHeight( 20 );
    m_layout->insertItem( 1, m_separator );

Ludovic Fauvet's avatar
Ludovic Fauvet committed
92 93 94
    addAudioTrack();

    m_scene->addItem( container );
95 96

    setSceneRect( m_layout->contentsRect() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
97 98 99 100
}

void TracksView::addVideoTrack()
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
101
    GraphicsTrack* track = new GraphicsTrack( MainWorkflow::VideoTrack, m_numVideoTrack );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
102
    track->setHeight( m_tracksHeight );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
103
    m_layout->insertItem( 0, track );
104
    m_layout->activate();
105
    m_cursorLine->setHeight( m_layout->contentsRect().height() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
106 107
    m_scene->invalidate(); // Redraw the background
    m_numVideoTrack++;
108
    emit videoTrackAdded( track );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
109 110 111 112
}

void TracksView::addAudioTrack()
{
113
    GraphicsTrack* track = new GraphicsTrack( MainWorkflow::AudioTrack, m_numAudioTrack );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
114
    track->setHeight( m_tracksHeight );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
115
    m_layout->insertItem( 1000, track );
116
    m_layout->activate();
117
    m_cursorLine->setHeight( m_layout->contentsRect().height() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
118 119
    m_scene->invalidate(); // Redraw the background
    m_numAudioTrack++;
120
    emit audioTrackAdded( track );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
121 122
}

123 124
void TracksView::clear()
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
125
    m_layout->removeItem( m_separator );
126

Ludovic Fauvet's avatar
Ludovic Fauvet committed
127 128 129 130
    while ( m_layout->count() > 0 )
        delete m_layout->itemAt( 0 );

    m_layout->addItem( m_separator );
131 132 133 134 135 136 137 138 139 140

    m_numAudioTrack = 0;
    m_numVideoTrack = 0;

    addVideoTrack();
    addAudioTrack();

    updateDuration();
}

141 142 143 144
void TracksView::addMediaItem( Clip* clip, unsigned int track, qint64 start )
{
    Q_ASSERT( clip );

145 146
    // If there is not enough tracks to insert
    // the clip do it now.
Ludovic Fauvet's avatar
Ludovic Fauvet committed
147
    if ( track > (quint32)m_numVideoTrack )
148 149 150 151 152
    {
        unsigned int nbTrackToAdd = track - m_numVideoTrack;
        for ( unsigned int i = 0; i < nbTrackToAdd; ++i )
            addVideoTrack();
    }
153 154 155
    // Add the empty upper track
    if ( track + 1 == m_numVideoTrack )
        addVideoTrack();
156

157 158 159 160 161 162 163 164 165 166 167 168 169 170
    // Is the clip already existing in the timeline ?
    //TODO: please optimize me!
    QList<QGraphicsItem*> sceneItems = m_scene->items();
    for ( int i = 0; i < sceneItems.size(); ++i )
    {
        AbstractGraphicsMediaItem* item =
                dynamic_cast<AbstractGraphicsMediaItem*>( sceneItems.at( i ) );
        if ( !item || item->uuid() != clip->getUuid() ) continue;
        // Item already exist: goodbye!
        return;
    }

    GraphicsMovieItem* item = new GraphicsMovieItem( clip );
    item->setHeight( tracksHeight() );
171
    item->setParentItem( getTrack( MainWorkflow::VideoTrack, track ) );
172
    item->setStartPos( start );
173
    item->oldTrackNumber = track;
174
    item->oldPosition = start;
175 176
    connect( item, SIGNAL( split(GraphicsMovieItem*,qint64) ),
             this, SLOT( split(GraphicsMovieItem*,qint64) ) );
177
    moveMediaItem( item, track, start );
178 179

    updateDuration();
180 181
}

182 183 184 185
void TracksView::dragEnterEvent( QDragEnterEvent* event )
{
    if ( event->mimeData()->hasFormat( "vlmc/uuid" ) )
        event->acceptProposedAction();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
186

Ludovic Fauvet's avatar
Ludovic Fauvet committed
187
    QUuid uuid = QUuid( QString( event->mimeData()->data( "vlmc/uuid" ) ) );
188 189
    Clip* clip = Library::getInstance()->getClip( uuid );
    if ( !clip ) return;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
190

191 192 193
    //FIXME: this leaks, but at least we have independant clips.
    clip = new Clip( clip );

Ludovic Fauvet's avatar
Ludovic Fauvet committed
194
    if ( m_dragItem ) delete m_dragItem;
195
    m_dragItem = new GraphicsMovieItem( clip );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
196
    m_dragItem->setHeight( tracksHeight() );
197
    m_dragItem->setParentItem( m_layout->itemAt( 0 )->graphicsItem() );
198 199
    connect( m_dragItem, SIGNAL( split(GraphicsMovieItem*,qint64) ),
             this, SLOT( split(GraphicsMovieItem*,qint64) ) );
200
    moveMediaItem( m_dragItem, event->pos() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
201 202 203 204 205
}

void TracksView::dragMoveEvent( QDragMoveEvent* event )
{
    if ( !m_dragItem ) return;
206 207
    moveMediaItem( m_dragItem, event->pos() );
}
Ludovic Fauvet's avatar
Ludovic Fauvet committed
208

209
void TracksView::moveMediaItem( const QUuid& uuid, unsigned int track, qint64 time )
210 211 212 213 214 215 216 217 218 219 220 221
{
    QList<QGraphicsItem*> sceneItems = m_scene->items();

    for ( int i = 0; i < sceneItems.size(); ++i )
    {
        AbstractGraphicsMediaItem* item =
                dynamic_cast<AbstractGraphicsMediaItem*>( sceneItems.at( i ) );
        if ( !item || item->uuid() != uuid ) continue;
        moveMediaItem( item, track, time );
    }
}

222 223
void TracksView::moveMediaItem( AbstractGraphicsMediaItem* item, QPoint position )
{
224
    static GraphicsTrack* lastKnownTrack = NULL;
225
    GraphicsTrack* track = NULL;
226

227
    QList<QGraphicsItem*> list = items( 0, position.y() );
228 229 230 231 232 233
    for ( int i = 0; i < list.size(); ++i )
    {
        track = qgraphicsitem_cast<GraphicsTrack*>( list.at(i) );
        if (track) break;
    }

234 235 236 237 238 239 240 241 242 243
    if ( !track )
    {
        // When the mouse pointer is not on a track,
        // use the last known track.
        // This avoids "breaks" when moving a rush
        if ( !lastKnownTrack ) return;
        track = lastKnownTrack;
    }

    lastKnownTrack = track;
244

245
    qreal time = ( mapToScene( position ).x() + 0.5 );
246
    moveMediaItem( item, track->trackNumber(), (qint64)time);
247 248
}

249
void TracksView::moveMediaItem( AbstractGraphicsMediaItem* item, quint32 track, qint64 time )
250
{
251
    if ( track > m_numVideoTrack - 1)
Ludovic Fauvet's avatar
Ludovic Fauvet committed
252 253
        track = m_numVideoTrack - 1;

254
    qint64 oldPos = item->startPos();
255
    QGraphicsItem* oldParent = item->parentItem();
256
    // Check for vertical collisions
257
    item->setParentItem( getTrack( MainWorkflow::VideoTrack, track ) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
258 259
    bool continueSearch = true;
    while ( continueSearch )
260
    {
261
        QList<QGraphicsItem*> colliding = item->collidingItems( Qt::IntersectsItemShape );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
262 263
        bool itemCollision = false;
        for ( int i = 0; i < colliding.size(); ++i )
264
        {
265 266
            AbstractGraphicsMediaItem* currentItem = dynamic_cast<AbstractGraphicsMediaItem*>( colliding.at( i ) );
            if ( currentItem )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
267 268 269
            {
                // Collision with an item of the same type
                itemCollision = true;
270
                if ( currentItem->trackNumber() >= track )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
271
                {
272
                    if ( track < 1 )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
273
                    {
274
                        item->setParentItem( oldParent );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
275 276 277 278
                        continueSearch = false;
                        break;
                    }
                    track -= 1;
279
                    Q_ASSERT( m_layout->itemAt( track )->graphicsItem() != NULL );
280
                    item->setParentItem( getTrack( MainWorkflow::VideoTrack, track ) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
281
                }
282
                else if ( currentItem->trackNumber() < track )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
283 284 285
                {
                    if ( track >= m_numVideoTrack - 1 )
                    {
286
                        item->setParentItem( oldParent );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
287 288 289 290
                        continueSearch = false;
                        break;
                    }
                    track += 1;
291
                    Q_ASSERT( m_layout->itemAt( track )->graphicsItem() != NULL );
292
                    item->setParentItem( getTrack( MainWorkflow::VideoTrack, track ) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
293 294
                }
            }
295
        }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
296 297
        if ( !itemCollision )
            continueSearch = false;
298 299
    }
    // Check for horizontal collisions
300
    qint64 mappedXPos = qMax( time, (qint64)0 );
301
    item->setStartPos( mappedXPos );
302

303 304 305
    AbstractGraphicsMediaItem* hItem = NULL;
    QList<QGraphicsItem*> collide = item->collidingItems();
    for ( int i = 0; i < collide.count(); ++i )
306
    {
307 308 309
        hItem = dynamic_cast<AbstractGraphicsMediaItem*>( collide.at( i ) );
        if ( hItem ) break;
    }
310

311 312 313 314 315 316 317 318
    if ( hItem )
    {
        qreal newpos;
        // Evaluate a possible solution
        if ( item->pos().x() > hItem->pos().x() )
            newpos = hItem->pos().x() + hItem->boundingRect().width();
        else
            newpos = hItem->pos().x() - item->boundingRect().width();
319

320
        if ( newpos < 0 || newpos == hItem->pos().x() )
321
            item->setStartPos( oldPos ); // Fail
322
        else
323 324
        {
            // A solution may be found
325
            item->setStartPos( qRound64( newpos ) );
326 327 328 329 330 331 332
            QList<QGraphicsItem*> collideAgain = item->collidingItems();
            for ( int i = 0; i < collideAgain.count(); ++i )
            {
                AbstractGraphicsMediaItem* currentItem =
                        dynamic_cast<AbstractGraphicsMediaItem*>( collideAgain.at( i ) );
                if ( currentItem )
                {
333
                    item->setStartPos( oldPos ); // Fail
334 335 336 337
                    break;
                }
            }
        }
338
    }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
339 340
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
341
void TracksView::removeMediaItem( const QUuid& uuid, unsigned int track )
342
{
343
    QList<QGraphicsItem*> trackItems = getTrack( MainWorkflow::VideoTrack, track )->childItems();;
344

345
    for ( int i = 0; i < trackItems.size(); ++i )
346 347
    {
        AbstractGraphicsMediaItem* item =
348
                dynamic_cast<AbstractGraphicsMediaItem*>( trackItems.at( i ) );
349
        if ( !item || item->uuid() != uuid ) continue;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
350
        removeMediaItem( item );
351 352 353
    }
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
354
void TracksView::removeMediaItem( AbstractGraphicsMediaItem* item )
355
{
356 357
    QList<AbstractGraphicsMediaItem*> items;
    items.append( item );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
358
    removeMediaItem( items );
359 360
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
361
void TracksView::removeMediaItem( const QList<AbstractGraphicsMediaItem*>& items )
362
{
363
    bool needUpdate = false;
364
    for ( int i = 0; i < items.size(); ++i )
365
    {
366 367 368 369
        GraphicsMovieItem* movieItem = qgraphicsitem_cast<GraphicsMovieItem*>( items.at( i ) );
        if ( !movieItem )
        {
            //TODO add support for audio tracks
Ludovic Fauvet's avatar
Ludovic Fauvet committed
370
            qWarning() << tr( "Action not supported." );
371 372 373 374
            continue;
        }

        delete movieItem;
375
        needUpdate = true;
376
    }
377 378

    if ( needUpdate ) updateDuration();
379 380
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
381 382
void TracksView::dragLeaveEvent( QDragLeaveEvent* event )
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
383
    Q_UNUSED( event )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
384 385 386 387
    if ( m_dragItem )
    {
        delete m_dragItem;
        m_dragItem = NULL;
388
        updateDuration();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
389
    }
390 391 392 393
}

void TracksView::dropEvent( QDropEvent* event )
{
394
    if ( m_dragItem )
395
    {
396
        updateDuration();
397 398 399
        if ( m_layout->itemAt( 0 )->graphicsItem()->childItems().count() > 0 )
            addVideoTrack();
        event->acceptProposedAction();
400 401

        qreal mappedXPos = ( mapToScene( event->pos() ).x() + 0.5 );
402
        m_dragItem->oldTrackNumber = m_dragItem->trackNumber();
403
        m_dragItem->oldPosition = (qint64)mappedXPos;
404

405
        Commands::trigger( new Commands::MainWorkflow::AddClip( m_renderer,
406
                                                                m_dragItem->clip(),
407
                                                                m_dragItem->trackNumber(),
408
                                                                (qint64)mappedXPos,
409
                                                                MainWorkflow::VideoTrack ) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
410
        m_dragItem = NULL;
411
    }
412 413
}

414 415
void TracksView::setDuration( int duration )
{
416
    int diff = ( int ) qAbs( ( qreal )duration - sceneRect().width() );
417 418 419 420 421 422 423 424 425 426
    if ( diff * matrix().m11() > -50 )
    {
        if ( matrix().m11() < 0.4 )
            setSceneRect( 0, 0, ( duration + 100 / matrix().m11() ), sceneRect().height() );
        else
            setSceneRect( 0, 0, ( duration + 300 ), sceneRect().height() );
    }
    m_projectDuration = duration;
}

427 428 429
void TracksView::setTool( ToolButtons button )
{
    m_tool = button;
430 431
    if ( m_tool == TOOL_CUT )
        scene()->clearSelection();
432 433
}

434 435 436 437 438 439 440
void TracksView::resizeEvent( QResizeEvent* event )
{
    QGraphicsView::resizeEvent( event );
}

void TracksView::drawBackground( QPainter* painter, const QRectF& rect )
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
441 442
    // Draw the tracks separators
    painter->setPen( QPen( QColor( 72, 72, 72 ) ) );
443
    for ( int i = 0; i < m_layout->count(); ++i )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
444
    {
445 446 447
        QGraphicsItem* gi = m_layout->itemAt( i )->graphicsItem();
        if ( !gi ) continue;
        GraphicsTrack* track = qgraphicsitem_cast<GraphicsTrack*>( gi );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
448 449 450
        if ( !track ) continue;

        QRectF trackRect = track->mapRectToScene( track->boundingRect() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
451
        if ( track->mediaType() == MainWorkflow::VideoTrack )
452 453 454
            painter->drawLine( trackRect.left(), trackRect.top(), rect.right(), trackRect.top() );
        else
            painter->drawLine( trackRect.left(), trackRect.bottom(), rect.right(), trackRect.bottom() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
455 456 457
    }

    // Audio/Video separator
458 459 460
    QRectF r = rect;
    r.setWidth( r.width() + 1 );

Ludovic Fauvet's avatar
Ludovic Fauvet committed
461
    painter->setWorldMatrixEnabled( false );
462 463 464 465 466 467 468 469 470 471

    QLinearGradient g( 0, m_separator->y(), 0, m_separator->y() + m_separator->boundingRect().height() );
    QColor base = palette().window().color();
    QColor end = palette().dark().color();
    g.setColorAt( 0, end );
    g.setColorAt( 0.1, base );
    g.setColorAt( 0.9, base );
    g.setColorAt( 1.0, end );

    painter->setBrush( QBrush( g ) );
472
    painter->setPen( Qt::transparent );
473 474 475 476
    painter->drawRect( 0,
                       (int) m_separator->y(),
                       (int) r.right(),
                       (int) m_separator->boundingRect().height() );
477
}
478 479 480

void TracksView::mouseMoveEvent( QMouseEvent* event )
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
481 482 483 484
    if ( event->modifiers() == Qt::NoModifier &&
         event->buttons() == Qt::LeftButton &&
         m_actionMove == true )
    {
485 486 487
        // The move action is obviously executed
        m_actionMoveExecuted = true;

488
        m_actionItem->setOpacity( 0.6 );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
489 490 491 492 493
        if ( m_actionRelativeX < 0 )
            m_actionRelativeX = event->pos().x() - mapFromScene( m_actionItem->pos() ).x();
        moveMediaItem( m_actionItem, QPoint( event->pos().x() - m_actionRelativeX, event->pos().y() ) );
    }

494 495 496 497 498
    QGraphicsView::mouseMoveEvent( event );
}

void TracksView::mousePressEvent( QMouseEvent* event )
{
499
    QList<AbstractGraphicsMediaItem*> mediaCollisionList = mediaItems( event->pos() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
500

501
    if ( event->modifiers() == Qt::ControlModifier && mediaCollisionList.count() == 0 )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
502 503
    {
        setDragMode( QGraphicsView::ScrollHandDrag );
504
        event->accept();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
505
    }
506
    else if ( event->modifiers() == Qt::NoModifier &&
Ludovic Fauvet's avatar
Ludovic Fauvet committed
507
         event->button() == Qt::LeftButton &&
508
         tool() == TOOL_DEFAULT &&
Ludovic Fauvet's avatar
Ludovic Fauvet committed
509 510
         mediaCollisionList.count() == 1 )
    {
511 512 513 514
        AbstractGraphicsMediaItem* item = mediaCollisionList.at( 0 );
        if ( item->moveable() )
        {
            m_actionMove = true;
515
            m_actionMoveExecuted = false;
516 517
            m_actionItem = mediaCollisionList.at( 0 );
        }
518 519
        scene()->clearSelection();
        item->setSelected( true );
520
        event->accept();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
521
    }
522 523
    else if ( event->modifiers() == Qt::ControlModifier &&
              event->button() == Qt::LeftButton &&
524
              tool() == TOOL_DEFAULT &&
525 526 527 528
              mediaCollisionList.count() == 1 )
    {
        AbstractGraphicsMediaItem* item = mediaCollisionList.at( 0 );
        item->setSelected( !item->isSelected() );
529
        event->accept();
530 531
    }
    else if ( event->modifiers() & Qt::ShiftModifier && mediaCollisionList.count() == 0 )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
532 533 534 535
    {
        setDragMode( QGraphicsView::RubberBandDrag );
        if ( !event->modifiers() & Qt::ControlModifier )
            scene()->clearSelection();
536
        event->accept();
537
    }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
538

539
    QGraphicsView::mousePressEvent( event );
540 541
}

542
void TracksView::mouseReleaseEvent( QMouseEvent* event )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
543
{
544
    if ( m_actionMove && m_actionMoveExecuted )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
545
    {
546 547
        m_actionItem->setOpacity( 1.0 );

548 549 550 551 552 553
        GraphicsMovieItem* movieItem = qgraphicsitem_cast<GraphicsMovieItem*>( m_actionItem );
        if ( movieItem )
        {
            updateDuration();
            if ( m_layout->itemAt( 0 )->graphicsItem()->childItems().count() > 0 )
                addVideoTrack();
554
            Commands::trigger( new Commands::MainWorkflow::MoveClip( m_mainWorkflow,
555
                                                                     movieItem->clip()->getUuid(),
556 557 558
                                                                     movieItem->oldTrackNumber,
                                                                     movieItem->oldPosition,
                                                                     movieItem->trackNumber(),
559
                                                                     movieItem->startPos(),
560
                                                                     MainWorkflow::VideoTrack ) );
561
            movieItem->oldTrackNumber = movieItem->trackNumber();
562
            movieItem->oldPosition = movieItem->startPos();
563 564 565
            m_actionRelativeX = -1;
            m_actionItem = NULL;
        }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
566 567
    }

568 569
    m_actionMove = false;

570 571
    setDragMode( QGraphicsView::NoDrag );
    QGraphicsView::mouseReleaseEvent( event );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
572 573
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
void TracksView::wheelEvent( QWheelEvent* event )
{
    if ( event->modifiers() == Qt::ControlModifier )
    {
        // CTRL + WHEEL = Zoom
        if ( event->delta() > 0 )
            emit zoomIn();
        else
            emit zoomOut();
        event->accept();
    }
    else
    {
        //TODO should scroll the timeline
        event->ignore();
        QGraphicsView::wheelEvent( event );
    }
}

593 594 595 596 597 598 599 600 601 602 603 604 605 606
QList<AbstractGraphicsMediaItem*> TracksView::mediaItems( const QPoint& pos )
{
    QList<QGraphicsItem*> collisionList = items( pos );
    QList<AbstractGraphicsMediaItem*> mediaCollisionList;
    for ( int i = 0; i < collisionList.size(); ++i )
    {
        AbstractGraphicsMediaItem* item =
                dynamic_cast<AbstractGraphicsMediaItem*>( collisionList.at( i ) );
        if ( item )
            mediaCollisionList.append( item );
    }
    return mediaCollisionList;
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
607
void TracksView::setCursorPos( qint64 pos )
608
{
609
    if ( pos < 0 ) pos = 0;
610
    m_cursorLine->frameChanged( pos, MainWorkflow::TimelineCursor );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
611 612
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
613
qint64 TracksView::cursorPos()
Ludovic Fauvet's avatar
Ludovic Fauvet committed
614
{
615
    return m_cursorLine->cursorPos();
616
}
617 618 619 620 621 622 623 624

void TracksView::setScale( double scaleFactor )
{
    QMatrix matrix;
    matrix.scale( scaleFactor, 1 );
    //TODO update the scene scale ?
    setMatrix( matrix );

625
    int diff = ( int ) ( sceneRect().width() - ( qreal ) m_projectDuration );
626 627 628 629 630 631 632
    if ( diff * matrix.m11() < 50 )
    {
        if ( matrix.m11() < 0.4 )
            setSceneRect( 0, 0, ( m_projectDuration + 100 / matrix.m11() ), sceneRect().height() );
        else
            setSceneRect( 0, 0, ( m_projectDuration + 300 ), sceneRect().height() );
    }
633
    centerOn( m_cursorLine );
634
}
635 636 637 638

void TracksView::ensureCursorVisible()
{
    if ( horizontalScrollBar()->isVisible() )
639 640 641 642 643 644
    {
        QRectF r( m_cursorLine->boundingRect().width() / 2,
                  m_cursorLine->boundingRect().height() / 2,
                  1, 1 );
        m_cursorLine->ensureVisible( r, 150, 50 );
    }
645
}
646 647 648

void TracksView::updateDuration()
{
649
    //TODO this should use a variant of mediaItems( const QPoint& )
650 651 652 653 654 655 656 657
    QList<QGraphicsItem*> sceneItems = m_scene->items();

    int projectDuration = 0;
    for ( int i = 0; i < sceneItems.size(); ++i )
    {
        AbstractGraphicsMediaItem* item =
                dynamic_cast<AbstractGraphicsMediaItem*>( sceneItems.at( i ) );
        if ( !item ) continue;
658 659
        if ( ( item->startPos() + item->boundingRect().width() ) > projectDuration )
            projectDuration = ( int ) ( item->startPos() + item->boundingRect().width() );
660 661 662 663
    }

    m_projectDuration = projectDuration;

Ludovic Fauvet's avatar
Ludovic Fauvet committed
664 665
    // Make sure that the width is not below zero
    int minimumWidth = qMax( m_projectDuration, 0 );
666

667
    // PreferredWidth not working ?
Ludovic Fauvet's avatar
Ludovic Fauvet committed
668 669
    m_layout->setMinimumWidth( minimumWidth );
    m_layout->setMaximumWidth( minimumWidth );
670

Ludovic Fauvet's avatar
Ludovic Fauvet committed
671 672
    setSceneRect( m_layout->contentsRect() );

673 674
    emit durationChanged( m_projectDuration );
}
675

676
GraphicsTrack* TracksView::getTrack( MainWorkflow::TrackType type, unsigned int number )
677 678 679 680 681 682
{
    for (int i = 0; i < m_layout->count(); ++i )
    {
        QGraphicsItem* gi = m_layout->itemAt( i )->graphicsItem();
        GraphicsTrack* track = qgraphicsitem_cast<GraphicsTrack*>( gi );
        if ( !track ) continue;
683
        if ( track->mediaType() != type ) continue;
684 685 686 687 688
        if ( track->trackNumber() == number )
            return track;
    }
    return NULL;
}
689 690 691

void TracksView::split( GraphicsMovieItem* item, qint64 frame )
{
692 693 694 695
//    Q_ASSERT( item );
//    Clip* newclip = item->clip()->split( frame );
//    Q_ASSERT( newclip );
//
696
//    addMediaItem( newclip, item->trackNumber(), item->startPos() + frame );
697 698 699
//    Commands::trigger( new Commands::MainWorkflow::AddClip( m_renderer,
//                                                            newclip,
//                                                            item->trackNumber(),
700
//                                                            item->startPos() + frame,
701 702 703 704 705
//                                                            MainWorkflow::VideoTrack ) );

    //frame is the number of frame from the beginning of the clip
    //item->pos().x() is the position of the splitted clip (in frame)
    //therefore, the position of the newly created clip is
706 707
    //the splitted clip pos + the splitting point (ie startPos() + frame)
//    m_renderer->split( item->clip(), item->trackNumber(), item->startPos() + frame, frame, MainWorkflow::VideoTrack );
708
    Commands::trigger( new Commands::MainWorkflow::SplitClip( m_renderer, item->clip(), item->trackNumber(),
709
                                                              item->startPos() + frame, frame, MainWorkflow::VideoTrack ) );
710
}