TracksView.cpp 17.1 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

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

44
    m_tracksCount = mainWorkflow->getTrackCount();
45
    m_fps = FPS;
46

Ludovic Fauvet's avatar
Ludovic Fauvet committed
47 48
    m_numAudioTrack = 0;
    m_numVideoTrack = 0;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
49
    m_dragItem = NULL;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
50 51 52
    m_actionMove = false;
    m_actionRelativeX = -1;
    m_actionItem = NULL;
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 );
58

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

62
    m_scene->addItem( m_cursorLine );
63

Ludovic Fauvet's avatar
Ludovic Fauvet committed
64 65
    createLayout();

66 67
    connect( m_cursorLine, SIGNAL( cursorPositionChanged(int) ),
             this, SLOT( ensureCursorVisible() ) );
68 69
}

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

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

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

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

Ludovic Fauvet's avatar
Ludovic Fauvet committed
94 95 96 97 98 99 100
    addAudioTrack();

    m_scene->addItem( container );
}

void TracksView::addVideoTrack()
{
101
    GraphicsTrack* track = new GraphicsTrack( GraphicsTrack::Video, m_numVideoTrack );
102
    track->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
103 104 105
    track->setPreferredHeight( m_tracksHeight );
    track->setContentsMargins( 0, 0, 0, 0 );
    m_layout->insertItem( 0, track );
106
    QApplication::processEvents(); //FIXME This is a bit hackish
Ludovic Fauvet's avatar
Ludovic Fauvet committed
107
    m_numVideoTrack++;
108
    m_scene->invalidate();
109 110
    //FIXME this should maybe go elsewhere
    setSceneRect( m_layout->contentsRect().adjusted( 0, 0, 100, 100 ) );
111
    m_cursorLine->setHeight( m_layout->contentsRect().height() );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
112 113 114 115
}

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

129 130 131 132
void TracksView::dragEnterEvent( QDragEnterEvent* event )
{
    if ( event->mimeData()->hasFormat( "vlmc/uuid" ) )
        event->acceptProposedAction();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
133

Ludovic Fauvet's avatar
Ludovic Fauvet committed
134
    QUuid uuid = QUuid( QString( event->mimeData()->data( "vlmc/uuid" ) ) );
135 136
    Clip* clip = Library::getInstance()->getClip( uuid );
    if ( !clip ) return;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
137 138 139 140

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

    if ( m_dragItem ) delete m_dragItem;
141
    m_dragItem = new GraphicsMovieItem( clip );
142
    m_dragItem->setWidth( ( int ) ( clip->getLength() ) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
143
    m_dragItem->setHeight( tracksHeight() );
144 145 146
    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
147 148 149 150 151
}

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

155
void TracksView::moveMediaItem( const QUuid& uuid, unsigned int track, qint64 time )
156 157 158 159 160 161 162 163 164 165 166 167
{
    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 );
    }
}

168 169
void TracksView::moveMediaItem( AbstractGraphicsMediaItem* item, QPoint position )
{
170
    static GraphicsTrack* lastKnownTrack = NULL;
171
    GraphicsTrack* track = NULL;
172

173
    QList<QGraphicsItem*> list = items( 0, position.y() );
174 175 176 177 178 179
    for ( int i = 0; i < list.size(); ++i )
    {
        track = qgraphicsitem_cast<GraphicsTrack*>( list.at(i) );
        if (track) break;
    }

180 181 182 183 184 185 186 187 188 189
    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;
190

191
    qreal time = ( mapToScene( position ).x() + 0.5 );
192
    moveMediaItem( item, track->trackNumber(), (int)time);
193 194 195 196
}

void TracksView::moveMediaItem( AbstractGraphicsMediaItem* item, int track, int time )
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
197 198 199
    if ( track < 0 )
        track = 0;
    else if ( track > m_numVideoTrack - 1)
Ludovic Fauvet's avatar
Ludovic Fauvet committed
200 201
        track = m_numVideoTrack - 1;

202
    //qDebug() << ">>>>>> Move track number" << track;
203

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

void TracksView::dragLeaveEvent( QDragLeaveEvent* event )
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
269
    Q_UNUSED( event )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
270 271 272 273
    if ( m_dragItem )
    {
        delete m_dragItem;
        m_dragItem = NULL;
274
        updateDuration();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
275
    }
276 277 278 279
}

void TracksView::dropEvent( QDropEvent* event )
{
280
    if ( m_dragItem )
281
    {
282
        updateDuration();
283 284 285
        if ( m_layout->itemAt( 0 )->graphicsItem()->childItems().count() > 0 )
            addVideoTrack();
        event->acceptProposedAction();
286 287

        qreal mappedXPos = ( mapToScene( event->pos() ).x() + 0.5 );
288
        m_dragItem->oldTrackNumber = m_dragItem->trackNumber();
289 290 291 292
        Commands::trigger( new Commands::MainWorkflow::AddClip( m_mainWorkflow,
                                                                m_dragItem->clip(),
                                                                m_dragItem->trackNumber(),
                                                                (qint64)mappedXPos ) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
293
        m_dragItem = NULL;
294
    }
295 296
}

297 298
void TracksView::setDuration( int duration )
{
299
    int diff = ( int ) qAbs( ( qreal )duration - sceneRect().width() );
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    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 )
{
317 318 319
    QRectF r = rect;
    r.setWidth( r.width() + 1 );

320
    painter->setBrush( QBrush( palette().dark().color(), Qt::Dense3Pattern ) );
321
    painter->setPen( Qt::transparent );
322 323 324
    painter->drawRect( ( int ) r.left(), ( int ) m_separator->y(),
                       ( int ) r.right(),
                       ( int ) m_separator->boundingRect().height() );
325

326
    /*QColor base = palette().button().color();
327 328 329 330 331 332 333 334 335 336 337 338
    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 )
339 340
        painter->fillRect( QRectF ( r.left(), lowerLimit, r.width(),
        height() - lowerLimit ), QBrush( base ) );*/
341
}
342 343 344

void TracksView::mouseMoveEvent( QMouseEvent* event )
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
345 346 347 348
    if ( event->modifiers() == Qt::NoModifier &&
         event->buttons() == Qt::LeftButton &&
         m_actionMove == true )
    {
349 350 351
        // The move action is obviously executed
        m_actionMoveExecuted = true;

Ludovic Fauvet's avatar
Ludovic Fauvet committed
352 353 354 355 356
        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() ) );
    }

357 358 359 360 361
    QGraphicsView::mouseMoveEvent( event );
}

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

364
    if ( event->modifiers() == Qt::ControlModifier && mediaCollisionList.count() == 0 )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
365 366 367 368 369 370
    {
        setDragMode( QGraphicsView::ScrollHandDrag );
        QGraphicsView::mousePressEvent( event );
        return;
    }

Ludovic Fauvet's avatar
Ludovic Fauvet committed
371 372 373 374
    if ( event->modifiers() == Qt::NoModifier &&
         event->button() == Qt::LeftButton &&
         mediaCollisionList.count() == 1 )
    {
375 376 377 378
        AbstractGraphicsMediaItem* item = mediaCollisionList.at( 0 );
        if ( item->moveable() )
        {
            m_actionMove = true;
379
            m_actionMoveExecuted = false;
380 381
            m_actionItem = mediaCollisionList.at( 0 );
        }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
382 383 384
        return;
    }

385
    /*if ( event->modifiers() & Qt::ShiftModifier && collisionList.count() == 0 )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
386 387 388 389 390 391
    {
        setDragMode( QGraphicsView::RubberBandDrag );
        if ( !event->modifiers() & Qt::ControlModifier )
            scene()->clearSelection();
        QGraphicsView::mousePressEvent( event );
        return;
392
    }*/
Ludovic Fauvet's avatar
Ludovic Fauvet committed
393

394
    QGraphicsView::mousePressEvent( event );
395 396
}

397
void TracksView::mouseReleaseEvent( QMouseEvent* event )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
398
{
399
    if ( m_actionMove && m_actionMoveExecuted )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
400
    {
401 402 403 404 405 406
        GraphicsMovieItem* movieItem = qgraphicsitem_cast<GraphicsMovieItem*>( m_actionItem );
        if ( movieItem )
        {
            updateDuration();
            if ( m_layout->itemAt( 0 )->graphicsItem()->childItems().count() > 0 )
                addVideoTrack();
407
            qDebug() << "Trigerring move command. track" << movieItem->oldTrackNumber << "->" << movieItem->trackNumber();
408 409 410
            Commands::trigger( new Commands::MainWorkflow::MoveClip( m_mainWorkflow, movieItem->clip()->getUuid(),
                                                                     movieItem->oldTrackNumber, movieItem->trackNumber(),
                                                                     (qint64)movieItem->pos().x() ) );
411
            movieItem->oldTrackNumber = movieItem->trackNumber();
412 413 414 415
            m_actionMove = false;
            m_actionRelativeX = -1;
            m_actionItem = NULL;
        }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
416 417
    }

418 419
    setDragMode( QGraphicsView::NoDrag );
    QGraphicsView::mouseReleaseEvent( event );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
420 421
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
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 );
    }
}

441 442 443 444 445 446 447 448 449 450 451 452 453 454
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
455
void TracksView::setCursorPos( int pos )
456
{
457 458
    if ( pos < 0 ) pos = 0;
    m_cursorLine->setCursorPos( pos );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
459 460 461 462
}

int TracksView::cursorPos()
{
463
    return m_cursorLine->cursorPos();
464
}
465 466 467 468 469 470 471 472

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

473
    int diff = ( int ) ( sceneRect().width() - ( qreal ) m_projectDuration );
474 475 476 477 478 479 480
    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() );
    }
481
    centerOn( m_cursorLine );
482
}
483 484 485 486

void TracksView::ensureCursorVisible()
{
    if ( horizontalScrollBar()->isVisible() )
487 488 489 490 491 492
    {
        QRectF r( m_cursorLine->boundingRect().width() / 2,
                  m_cursorLine->boundingRect().height() / 2,
                  1, 1 );
        m_cursorLine->ensureVisible( r, 150, 50 );
    }
493
}
494 495 496

void TracksView::updateDuration()
{
497
    //TODO this should use a variant of mediaItems( const QPoint& )
498 499 500 501 502 503 504 505 506
    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 )
507
            projectDuration = ( int ) ( item->pos().x() + item->boundingRect().width() );
508 509 510 511 512 513 514 515 516 517
    }

    m_projectDuration = projectDuration;

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

    emit durationChanged( m_projectDuration );
}