TracksView.cpp 17.3 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 30
#include <QtDebug>
#include <cmath>
31
#include "TracksView.h"
32
#include "Media.h"
33 34
#include "Library.h"
#include "GraphicsMovieItem.h"
35
#include "GraphicsCursorItem.h"
36
#include "Commands.hpp"
37

Ludovic Fauvet's avatar
Ludovic Fauvet committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
TracksView* TracksView::m_instance = NULL;

TracksView* TracksView::instance( QGraphicsScene* scene, MainWorkflow* mainWorkflow, QWidget* parent )
{
    if ( m_instance ) return m_instance;
    m_instance = new TracksView( scene, mainWorkflow, parent );
    return m_instance;
}

TracksView* TracksView::instance()
{
    Q_ASSERT( m_instance );
    return m_instance;
}

53 54
TracksView::TracksView( QGraphicsScene* scene, MainWorkflow* mainWorkflow, QWidget* parent )
        : QGraphicsView( scene, parent ), m_scene( scene ), m_mainWorkflow( mainWorkflow )
55
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
56 57
    connect( qApp, SIGNAL( aboutToQuit() ), this, SLOT( deleteLater() ) );

58
    //TODO should be defined by the settings
59
    m_tracksHeight = 25;
60

61
    m_tracksCount = mainWorkflow->getTrackCount();
62
    m_fps = FPS;
63

Ludovic Fauvet's avatar
Ludovic Fauvet committed
64 65
    m_numAudioTrack = 0;
    m_numVideoTrack = 0;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
66
    m_dragItem = NULL;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
67 68 69
    m_actionMove = false;
    m_actionRelativeX = -1;
    m_actionItem = NULL;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
70

71 72 73
    setMouseTracking( true );
    setAcceptDrops( true );
    setContentsMargins( 0, 0, 0, 0 );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
74
    setFrameStyle( QFrame::NoFrame );
75

Ludovic Fauvet's avatar
Ludovic Fauvet committed
76 77
    //TODO Remove the GraphicsCursorItem parameter height (not useful anymore)
    m_cursorLine = new GraphicsCursorItem( 1, QPen( QColor( 220, 30, 30 ) ) );
78

79
    m_scene->addItem( m_cursorLine );
80

Ludovic Fauvet's avatar
Ludovic Fauvet committed
81 82
    createLayout();

83 84
    connect( m_cursorLine, SIGNAL( cursorPositionChanged(int) ),
             this, SLOT( ensureCursorVisible() ) );
85 86
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
87 88 89
void TracksView::createLayout()
{
    m_layout = new QGraphicsLinearLayout( Qt::Vertical );
90
    m_layout->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
91 92
    m_layout->setContentsMargins( 0, 0, 0, 0 );
    m_layout->setSpacing( 0 );
93
    m_layout->setPreferredWidth( 0 );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
94 95

    QGraphicsWidget* container = new QGraphicsWidget();
96
    container->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
97 98 99
    container->setContentsMargins( 0, 0, 0, 0 );
    container->setLayout( m_layout );

100 101 102 103
    // Create the initial layout
    // - 1 video track
    // - a separator
    // - 1 audio track
Ludovic Fauvet's avatar
Ludovic Fauvet committed
104
    addVideoTrack();
105 106

    m_separator = new QGraphicsWidget();
107
    m_separator->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
108 109 110
    m_separator->setPreferredHeight( 20 );
    m_layout->insertItem( 1, m_separator );

Ludovic Fauvet's avatar
Ludovic Fauvet committed
111 112 113 114 115 116 117
    addAudioTrack();

    m_scene->addItem( container );
}

void TracksView::addVideoTrack()
{
118
    GraphicsTrack* track = new GraphicsTrack( GraphicsTrack::Video, m_numVideoTrack );
119
    track->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
120 121 122
    track->setPreferredHeight( m_tracksHeight );
    track->setContentsMargins( 0, 0, 0, 0 );
    m_layout->insertItem( 0, track );
123
    QApplication::processEvents(); //FIXME This is a bit hackish
Ludovic Fauvet's avatar
Ludovic Fauvet committed
124
    m_numVideoTrack++;
125
    m_scene->invalidate();
126 127
    //FIXME this should maybe go elsewhere
    setSceneRect( m_layout->contentsRect().adjusted( 0, 0, 100, 100 ) );
128
    m_cursorLine->setHeight( m_layout->contentsRect().height() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
129 130 131 132
}

void TracksView::addAudioTrack()
{
133
    GraphicsTrack* track = new GraphicsTrack( GraphicsTrack::Audio, 0 );
134
    track->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
135 136 137
    track->setPreferredHeight( m_tracksHeight );
    track->setContentsMargins( 0, 0, 0, 0 );
    m_layout->insertItem( 1000, track );
138
    QApplication::processEvents(); //FIXME This is a bit hackish
Ludovic Fauvet's avatar
Ludovic Fauvet committed
139
    m_numAudioTrack++;
140
    m_scene->invalidate();
141 142
    //FIXME this should maybe go elsewhere
    setSceneRect( m_layout->contentsRect().adjusted( 0, 0, 100, 100 ) );
143
    m_cursorLine->setHeight( m_layout->contentsRect().height() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
144 145
}

146 147 148 149
void TracksView::dragEnterEvent( QDragEnterEvent* event )
{
    if ( event->mimeData()->hasFormat( "vlmc/uuid" ) )
        event->acceptProposedAction();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
150

Ludovic Fauvet's avatar
Ludovic Fauvet committed
151
    QUuid uuid = QUuid( QString( event->mimeData()->data( "vlmc/uuid" ) ) );
152 153
    Clip* clip = Library::getInstance()->getClip( uuid );
    if ( !clip ) return;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
154 155 156 157

    qreal mappedXPos = ( mapToScene( event->pos() ).x() + 0.5 );

    if ( m_dragItem ) delete m_dragItem;
158
    m_dragItem = new GraphicsMovieItem( clip );
159
    m_dragItem->setWidth( ( int ) ( clip->getLength() ) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
160
    m_dragItem->setHeight( tracksHeight() );
161 162 163
    m_dragItem->setPos( mappedXPos, 0 );
    m_dragItem->setParentItem( m_layout->itemAt( 0 )->graphicsItem() );
    moveMediaItem( m_dragItem, event->pos() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
164 165 166 167 168
}

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

172
void TracksView::moveMediaItem( const QUuid& uuid, unsigned int track, qint64 time )
173 174 175 176 177 178 179 180 181 182 183 184
{
    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 );
    }
}

185 186
void TracksView::moveMediaItem( AbstractGraphicsMediaItem* item, QPoint position )
{
187
    static GraphicsTrack* lastKnownTrack = NULL;
188
    GraphicsTrack* track = NULL;
189

190
    QList<QGraphicsItem*> list = items( 0, position.y() );
191 192 193 194 195 196
    for ( int i = 0; i < list.size(); ++i )
    {
        track = qgraphicsitem_cast<GraphicsTrack*>( list.at(i) );
        if (track) break;
    }

197 198 199 200 201 202 203 204 205 206
    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;
207

208
    qreal time = ( mapToScene( position ).x() + 0.5 );
209
    moveMediaItem( item, track->trackNumber(), (int)time);
210 211 212 213
}

void TracksView::moveMediaItem( AbstractGraphicsMediaItem* item, int track, int time )
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
214 215 216
    if ( track < 0 )
        track = 0;
    else if ( track > m_numVideoTrack - 1)
Ludovic Fauvet's avatar
Ludovic Fauvet committed
217 218
        track = m_numVideoTrack - 1;

219 220
    QPointF oldPos = item->pos();
    QGraphicsItem* oldParent = item->parentItem();
221
    // Check for vertical collisions
222
    item->setParentItem( m_layout->itemAt( m_numVideoTrack - track - 1 )->graphicsItem() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
223 224
    bool continueSearch = true;
    while ( continueSearch )
225
    {
226
        QList<QGraphicsItem*> colliding = item->collidingItems( Qt::IntersectsItemShape );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
227 228
        bool itemCollision = false;
        for ( int i = 0; i < colliding.size(); ++i )
229
        {
230 231
            AbstractGraphicsMediaItem* currentItem = dynamic_cast<AbstractGraphicsMediaItem*>( colliding.at( i ) );
            if ( currentItem )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
232 233 234
            {
                // Collision with an item of the same type
                itemCollision = true;
235
                if ( currentItem->trackNumber() >= track )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
236
                {
237
                    if ( track < 1 )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
238
                    {
239
                        item->setParentItem( oldParent );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
240 241 242 243
                        continueSearch = false;
                        break;
                    }
                    track -= 1;
244
                    Q_ASSERT( m_layout->itemAt( track )->graphicsItem() != NULL );
245
                    item->setParentItem( m_layout->itemAt( track )->graphicsItem() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
246
                }
247
                else if ( currentItem->trackNumber() < track )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
248 249 250
                {
                    if ( track >= m_numVideoTrack - 1 )
                    {
251
                        item->setParentItem( oldParent );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
252 253 254 255
                        continueSearch = false;
                        break;
                    }
                    track += 1;
256
                    Q_ASSERT( m_layout->itemAt( track )->graphicsItem() != NULL );
257
                    item->setParentItem( m_layout->itemAt( track )->graphicsItem() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
258 259
                }
            }
260
        }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
261 262
        if ( !itemCollision )
            continueSearch = false;
263 264
    }
    // Check for horizontal collisions
265
    int mappedXPos = qMax( time, 0 );
266 267
    item->setPos( mappedXPos, 0 );
    QList<QGraphicsItem*> colliding = item->collidingItems( Qt::IntersectsItemShape );
268 269
    for ( int i = 0; i < colliding.size(); ++i )
    {
270 271
        AbstractGraphicsMediaItem* currentItem = dynamic_cast<AbstractGraphicsMediaItem*>( colliding.at( i ) );
        if ( currentItem )
272 273 274
        {
            // Collision with an item of the same type
            // Restoring original position (horizontal)
275 276
            if ( oldPos.isNull() == false )
                item->setPos( oldPos );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
277
            break;
278 279
        }
    }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
280 281 282 283
}

void TracksView::dragLeaveEvent( QDragLeaveEvent* event )
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
284
    Q_UNUSED( event )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
285 286 287 288
    if ( m_dragItem )
    {
        delete m_dragItem;
        m_dragItem = NULL;
289
        updateDuration();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
290
    }
291 292 293 294
}

void TracksView::dropEvent( QDropEvent* event )
{
295
    if ( m_dragItem )
296
    {
297
        updateDuration();
298 299 300
        if ( m_layout->itemAt( 0 )->graphicsItem()->childItems().count() > 0 )
            addVideoTrack();
        event->acceptProposedAction();
301 302

        qreal mappedXPos = ( mapToScene( event->pos() ).x() + 0.5 );
303
        m_dragItem->oldTrackNumber = m_dragItem->trackNumber();
304 305 306 307
        Commands::trigger( new Commands::MainWorkflow::AddClip( m_mainWorkflow,
                                                                m_dragItem->clip(),
                                                                m_dragItem->trackNumber(),
                                                                (qint64)mappedXPos ) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
308
        m_dragItem = NULL;
309
    }
310 311
}

312 313
void TracksView::setDuration( int duration )
{
314
    int diff = ( int ) qAbs( ( qreal )duration - sceneRect().width() );
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
    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;
}

void TracksView::resizeEvent( QResizeEvent* event )
{
    QGraphicsView::resizeEvent( event );
}

void TracksView::drawBackground( QPainter* painter, const QRectF& rect )
{
332 333 334
    QRectF r = rect;
    r.setWidth( r.width() + 1 );

335
    painter->setBrush( QBrush( palette().dark().color(), Qt::Dense3Pattern ) );
336
    painter->setPen( Qt::transparent );
337 338 339
    painter->drawRect( ( int ) r.left(), ( int ) m_separator->y(),
                       ( int ) r.right(),
                       ( int ) m_separator->boundingRect().height() );
340

341
    /*QColor base = palette().button().color();
342 343 344 345 346 347 348 349 350 351 352 353
    QRectF r = rect;
    r.setWidth( r.width() + 1 );

    painter->setClipRect( r );
    painter->drawLine( r.left(), 0, r.right(), 0 );

    uint tracks = m_tracksCount;
    for ( uint i = 0; i < tracks; ++i )
        painter->drawLine( r.left(), m_tracksHeight * ( i + 1 ), r.right(), m_tracksHeight * ( i + 1 ) );

    int lowerLimit = m_tracksHeight * m_tracksCount + 1;
    if ( height() > lowerLimit )
354 355
        painter->fillRect( QRectF ( r.left(), lowerLimit, r.width(),
        height() - lowerLimit ), QBrush( base ) );*/
356
}
357 358 359

void TracksView::mouseMoveEvent( QMouseEvent* event )
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
360 361 362 363
    if ( event->modifiers() == Qt::NoModifier &&
         event->buttons() == Qt::LeftButton &&
         m_actionMove == true )
    {
364 365 366
        // The move action is obviously executed
        m_actionMoveExecuted = true;

Ludovic Fauvet's avatar
Ludovic Fauvet committed
367 368 369 370 371
        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() ) );
    }

372 373 374 375 376
    QGraphicsView::mouseMoveEvent( event );
}

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

379
    if ( event->modifiers() == Qt::ControlModifier && mediaCollisionList.count() == 0 )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
380 381 382 383 384 385
    {
        setDragMode( QGraphicsView::ScrollHandDrag );
        QGraphicsView::mousePressEvent( event );
        return;
    }

Ludovic Fauvet's avatar
Ludovic Fauvet committed
386 387 388 389
    if ( event->modifiers() == Qt::NoModifier &&
         event->button() == Qt::LeftButton &&
         mediaCollisionList.count() == 1 )
    {
390 391 392 393
        AbstractGraphicsMediaItem* item = mediaCollisionList.at( 0 );
        if ( item->moveable() )
        {
            m_actionMove = true;
394
            m_actionMoveExecuted = false;
395 396
            m_actionItem = mediaCollisionList.at( 0 );
        }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
397 398 399
        return;
    }

400
    /*if ( event->modifiers() & Qt::ShiftModifier && collisionList.count() == 0 )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
401 402 403 404 405 406
    {
        setDragMode( QGraphicsView::RubberBandDrag );
        if ( !event->modifiers() & Qt::ControlModifier )
            scene()->clearSelection();
        QGraphicsView::mousePressEvent( event );
        return;
407
    }*/
Ludovic Fauvet's avatar
Ludovic Fauvet committed
408

409
    QGraphicsView::mousePressEvent( event );
410 411
}

412
void TracksView::mouseReleaseEvent( QMouseEvent* event )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
413
{
414
    if ( m_actionMove && m_actionMoveExecuted )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
415
    {
416 417 418 419 420 421
        GraphicsMovieItem* movieItem = qgraphicsitem_cast<GraphicsMovieItem*>( m_actionItem );
        if ( movieItem )
        {
            updateDuration();
            if ( m_layout->itemAt( 0 )->graphicsItem()->childItems().count() > 0 )
                addVideoTrack();
422 423 424
            Commands::trigger( new Commands::MainWorkflow::MoveClip( m_mainWorkflow, movieItem->clip()->getUuid(),
                                                                     movieItem->oldTrackNumber, movieItem->trackNumber(),
                                                                     (qint64)movieItem->pos().x() ) );
425
            movieItem->oldTrackNumber = movieItem->trackNumber();
426 427 428 429
            m_actionMove = false;
            m_actionRelativeX = -1;
            m_actionItem = NULL;
        }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
430 431
    }

432 433
    setDragMode( QGraphicsView::NoDrag );
    QGraphicsView::mouseReleaseEvent( event );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
434 435
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
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 );
    }
}

455 456 457 458 459 460 461 462 463 464 465 466 467 468
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
469
void TracksView::setCursorPos( int pos )
470
{
471 472
    if ( pos < 0 ) pos = 0;
    m_cursorLine->setCursorPos( pos );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
473 474 475 476
}

int TracksView::cursorPos()
{
477
    return m_cursorLine->cursorPos();
478
}
479 480 481 482 483 484 485 486

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

487
    int diff = ( int ) ( sceneRect().width() - ( qreal ) m_projectDuration );
488 489 490 491 492 493 494
    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() );
    }
495
    centerOn( m_cursorLine );
496
}
497 498 499 500

void TracksView::ensureCursorVisible()
{
    if ( horizontalScrollBar()->isVisible() )
501 502 503 504 505 506
    {
        QRectF r( m_cursorLine->boundingRect().width() / 2,
                  m_cursorLine->boundingRect().height() / 2,
                  1, 1 );
        m_cursorLine->ensureVisible( r, 150, 50 );
    }
507
}
508 509 510

void TracksView::updateDuration()
{
511
    //TODO this should use a variant of mediaItems( const QPoint& )
512 513 514 515 516 517 518 519 520
    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;
        if ( item->pos().x() + item->boundingRect().width() > projectDuration )
521
            projectDuration = ( int ) ( item->pos().x() + item->boundingRect().width() );
522 523 524 525 526 527 528 529 530 531
    }

    m_projectDuration = projectDuration;

    // PreferredWidth not working ?
    m_layout->setMinimumWidth( m_projectDuration );
    m_layout->setMaximumWidth( m_projectDuration );

    emit durationChanged( m_projectDuration );
}