]> git.sesse.net Git - vlc/blobdiff - modules/gui/qt4/components/epg/EPGView.cpp
Qt: epg: don't hide EPG when there's still data.
[vlc] / modules / gui / qt4 / components / epg / EPGView.cpp
index 5af6f93382f12fa5a989d71a07cfe0f52c5af450..4e7e3dd239f43264eedc6db7fef5b18e2c1c39ab 100644 (file)
 #include <QtDebug>
 #include <QGraphicsTextItem>
 
+EPGGraphicsScene::EPGGraphicsScene( QObject *parent ) : QGraphicsScene( parent )
+{}
+
+void EPGGraphicsScene::drawBackground( QPainter *painter, const QRectF &rect)
+{
+    EPGView *epgView;
+    painter->setPen( QPen( QColor( 224, 224, 224 ) ) );
+    for( int y = rect.top() + TRACKS_HEIGHT ; y < rect.bottom() ; y += TRACKS_HEIGHT )
+       painter->drawLine( QLineF( rect.left(), y, rect.right(), y ) );
+    epgView = qobject_cast<EPGView *>(parent());
+    int x = epgView->startTime().secsTo( epgView->baseTime() );
+    painter->setPen( QPen( QColor( 255, 192, 192 ) ) );
+        painter->drawLine( QLineF( x, rect.top(), x, rect.bottom() ) );
+}
+
 EPGView::EPGView( QWidget *parent ) : QGraphicsView( parent )
 {
     setContentsMargins( 0, 0, 0, 0 );
-    setFrameStyle( QFrame::NoFrame );
+    setFrameStyle( QFrame::Box );
     setAlignment( Qt::AlignLeft | Qt::AlignTop );
-    setViewportUpdateMode( QGraphicsView::FullViewportUpdate );
 
     m_startTime = QDateTime::currentDateTime();
 
-    QGraphicsScene *EPGscene = new QGraphicsScene( this );
+    EPGGraphicsScene *EPGscene = new EPGGraphicsScene( this );
 
     setScene( EPGscene );
-
-    connect( horizontalScrollBar(), SIGNAL( valueChanged(int) ),
-             this, SLOT( updateOverlayPosition(int) ) );
-
-    m_overlay = EPGscene->addRect( 0, 0, 100, 1, QPen(), QBrush( QColor( 40, 86, 255, 220 ) ) );
-    m_overlay->setFlag( QGraphicsItem::ItemIgnoresTransformations );
-    m_overlay->setZValue( 100 );
-
-    sceneRectChanged( scene()->sceneRect() );
-
-    connect( scene(), SIGNAL( sceneRectChanged(QRectF) ),
-             this, SLOT( sceneRectChanged(QRectF) ) );
-}
-
-void EPGView::updateOverlayPosition( int value )
-{
-    int pos = value * matrix().inverted().m11();
-    m_overlay->setPos( pos, 0 );
 }
 
 void EPGView::setScale( double scaleFactor )
@@ -71,23 +67,31 @@ void EPGView::setScale( double scaleFactor )
     setMatrix( matrix );
 }
 
-void EPGView::setStartTime( const QDateTime& startTime )
+void EPGView::updateStartTime()
 {
-    QList<QGraphicsItem*> itemList = items();
-
-    int diff = startTime.secsTo( m_startTime );
-
-    for ( int i = 0; i < itemList.count(); ++i )
+    mutex.lock();
+    foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
     {
-        EPGItem* item = dynamic_cast<EPGItem*>( itemList.at( i ) );
-        if ( !item ) continue;
-        item->setStart( item->start().addSecs( diff ) );
+        foreach( EPGItem *epgItem, epgItemByTime->values() )
+        {
+            epgItem->updatePos();
+        }
     }
+    mutex.unlock();
+}
 
-    m_startTime = startTime;
-
-    // Our start time has changed
-    emit startTimeChanged( startTime );
+void EPGView::updateChannels()
+{
+    /* Make sure our items goes to the correct row */
+    unsigned int channelIndex = 0;
+    mutex.lock();
+    foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
+    {
+        foreach( EPGItem *epgItem, epgItemByTime->values() )
+            epgItem->setRow( channelIndex );
+        channelIndex++;
+    }
+    mutex.unlock();
 }
 
 const QDateTime& EPGView::startTime()
@@ -95,80 +99,214 @@ const QDateTime& EPGView::startTime()
     return m_startTime;
 }
 
-void EPGView::addEvent( EPGEvent* event )
+const QDateTime& EPGView::baseTime()
 {
-    if ( !m_channels.contains( event->channelName ) )
-    {
-        m_channels.append( event->channelName );
-        QGraphicsTextItem* channelTitle = new QGraphicsTextItem( event->channelName, m_overlay );
-        channelTitle->setZValue( 101 );
-        channelTitle->setPos( 0, m_channels.indexOf( event->channelName ) * TRACKS_HEIGHT );
-        channelTitle->setTextWidth( 100 );
-    }
-
-    EPGItem* item = new EPGItem( this );
-    item->setChannel( m_channels.indexOf( event->channelName ) );
-    item->setStart( event->start );
-    item->setDuration( event->duration );
-    item->setName( event->name );
-    item->setDescription( event->description );
-    item->setShortDescription( event->shortDescription );
-    item->setCurrent( event->current );
+    return m_baseTime;
+}
 
-    scene()->addItem( item );
+bool EPGView::hasValidData()
+{
+    return !epgitemsByChannel.empty();
 }
 
-void EPGView::updateEvent( EPGEvent* event )
+static void cleanOverlapped( EPGEventByTimeQMap *epgItemByTime, EPGItem *epgItem, QGraphicsScene *scene )
 {
-    //qDebug() << "Update event: " << event->name;
+    /* Clean overlapped programs */
+    foreach(const QDateTime existingTimes, epgItemByTime->keys())
+    {
+        if ( existingTimes != epgItem->start() )
+        {
+            EPGItem *otherEPGItem = epgItemByTime->value( existingTimes );
+            if ( otherEPGItem->playsAt( epgItem->start().addSecs( 1 ) )
+                || /* add/minus one sec because next one can start at prev end min */
+                 otherEPGItem->playsAt( epgItem->end().addSecs( -1 ) ) )
+            {
+                epgItemByTime->remove( otherEPGItem->start() );
+                scene->removeItem( otherEPGItem );
+                delete otherEPGItem;
+            }
+        }
+    }
 }
 
-void EPGView::delEvent( EPGEvent* event )
+bool EPGView::addEPGEvent( vlc_epg_event_t *data, QString channelName, bool b_current )
 {
-    //qDebug() << "Del event: " << event->name;
+    /* Init our nested map if required */
+    EPGEventByTimeQMap *epgItemByTime;
+    EPGItem *epgItem;
+    bool b_refresh_channels = false;
+
+    QDateTime eventStart = QDateTime::fromTime_t( data->i_start );
+    if ( eventStart < m_startTime )
+    {
+        m_startTime = eventStart;
+        emit startTimeChanged( m_startTime );
+    }
+
+    mutex.lock();
+    if ( !epgitemsByChannel.contains( channelName ) )
+    {
+        epgItemByTime = new EPGEventByTimeQMap();
+        epgitemsByChannel.insert( channelName, epgItemByTime );
+        emit channelAdded( channelName );
+        b_refresh_channels = true;
+    } else {
+        epgItemByTime = epgitemsByChannel.value( channelName );
+    }
+
+    if ( epgItemByTime->contains( eventStart ) )
+    {
+        /* Update our existing programs */
+        epgItem = epgItemByTime->value( eventStart );
+        epgItem->setData( data ); /* updates our entry */
+        epgItem->setCurrent( b_current );
+        cleanOverlapped( epgItemByTime, epgItem, scene() );
+        mutex.unlock();
+        return false;
+    } else {
+        /* Insert a new program entry */
+        epgItem = new EPGItem( data, this );
+        cleanOverlapped( epgItemByTime, epgItem, scene() );
+        /* Effectively insert our new program */
+        epgItem->setCurrent( b_current );
+        epgItemByTime->insert( eventStart, epgItem );
+        scene()->addItem( epgItem );
+        /* update only our row (without calling the updatechannels()) */
+        epgItem->setRow( epgitemsByChannel.keys().indexOf( channelName ) );
+    }
+    mutex.unlock();
+
+    /* Update rows on each item */
+    if ( b_refresh_channels ) updateChannels();
+
+    return true;
 }
 
-void EPGView::drawBackground( QPainter *painter, const QRectF &rect )
+void EPGView::removeEPGEvent( vlc_epg_event_t *data, QString channelName )
 {
-    painter->setPen( QPen( QColor( 72, 72, 72 ) ) );
+    EPGEventByTimeQMap *epgItemByTime;
+    QDateTime eventStart = QDateTime::fromTime_t( data->i_start );
+    EPGItem *epgItem;
+    bool b_update_channels = false;
+
+    mutex.lock();
+    if ( epgitemsByChannel.contains( channelName ) )
+    {
+        epgItemByTime = epgitemsByChannel.value( channelName );
 
-    QPointF p = mapToScene( width(), 0 );
+        if ( epgItemByTime->contains( eventStart ) )
+        { /* delete our EPGItem */
+            epgItem = epgItemByTime->value( eventStart );
+            epgItemByTime->remove( eventStart );
+            scene()->removeItem( epgItem );
+            delete epgItem;
+        }
 
-    int y = 0;
-    for ( int i = 0; i < m_channels.count() + 1; ++i )
+        if ( epgItemByTime->keys().empty() )
+        { /* Now unused channel */
+            epgitemsByChannel.remove( channelName );
+            delete epgItemByTime;
+            emit channelRemoved( channelName );
+            b_update_channels = true;
+        }
+    }
+    mutex.unlock();
+
+    if ( b_update_channels ) updateChannels();
+}
+
+void EPGView::reset()
+{
+    /* clean our items storage and remove them from the scene */
+    EPGEventByTimeQMap *epgItemByTime;
+    EPGItem *epgItem;
+    mutex.lock();
+    foreach( const QString &channelName, epgitemsByChannel.keys() )
     {
-        painter->drawLine( 0,
-                           y * TRACKS_HEIGHT,
-                           p.x(),
-                           y * TRACKS_HEIGHT );
-        ++y;
+        epgItemByTime = epgitemsByChannel[ channelName ];
+        foreach( const QDateTime &key, epgItemByTime->keys() )
+        {
+            epgItem = epgItemByTime->value( key );
+            scene()->removeItem( epgItem );
+            epgItemByTime->remove( key );
+            delete epgItem;
+        }
+        epgitemsByChannel.remove( channelName );
+        delete epgItemByTime;
+        emit channelRemoved( channelName ); /* notify others */
     }
+    mutex.unlock();
 }
 
-void EPGView::updateDuration()
+void EPGView::cleanup()
 {
-    QDateTime lastItem;
-    QList<QGraphicsItem*> list = items();
+    /* remove expired items and clear their current flag */
+    EPGEventByTimeQMap *epgItemByTime;
+    EPGItem *epgItem;
+    m_baseTime = QDateTime::currentDateTime();
+    QDateTime lowestTime = m_baseTime;
+    bool b_timechanged = false;
+    bool b_update_channels = false;
 
-    for ( int i = 0; i < list.count(); ++i )
+    mutex.lock();
+    foreach( const QString &channelName, epgitemsByChannel.keys() )
     {
-        EPGItem* item = dynamic_cast<EPGItem*>( list.at( i ) );
-        if ( !item ) continue;
-        QDateTime itemEnd = item->start().addSecs( item->duration() );
+        epgItemByTime = epgitemsByChannel[ channelName ];
+        foreach( const QDateTime &key, epgItemByTime->keys() )
+        {
+            epgItem = epgItemByTime->value( key );
+            if ( epgItem->endsBefore( baseTime() ) ) /* Expired item ? */
+            {
+                scene()->removeItem( epgItem );
+                epgItemByTime->remove( key );
+                delete epgItem;
+            } else {
+                epgItem->setCurrent( false ); /* if stream doesn't update */
+                if ( lowestTime > epgItem->start() )
+                {
+                    lowestTime = epgItem->start(); /* update our reference */
+                    b_timechanged = true;
+                }
+            }
+        }
 
-        if ( itemEnd > lastItem )
-            lastItem = itemEnd;
+        if ( epgItemByTime->keys().empty() )
+        { /* Now unused channel */
+            epgitemsByChannel.remove( channelName );
+            delete epgItemByTime;
+            emit channelRemoved( channelName );
+            b_update_channels = true;
+        }
     }
-    m_duration = m_startTime.secsTo( lastItem );
-    emit durationChanged( m_duration );
+    mutex.unlock();
+
+    if ( b_timechanged )
+    {
+        m_startTime = lowestTime;
+        emit startTimeChanged( m_startTime );
+    }
+
+    if ( b_update_channels ) updateChannels();
+}
+
+EPGView::~EPGView()
+{
+    reset();
 }
 
-void EPGView::eventFocused( EPGEvent *ev )
+void EPGView::updateDuration()
 {
-    emit eventFocusedChanged( ev );
+    QDateTime maxItemTime;
+    mutex.lock();
+    foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
+        foreach( EPGItem *epgItem, epgItemByTime->values() )
+            if ( epgItem->end() > maxItemTime ) maxItemTime = epgItem->end();
+    mutex.unlock();
+    m_duration = m_startTime.secsTo( maxItemTime );
+    emit durationChanged( m_duration );
 }
 
-void EPGView::sceneRectChanged( const QRectF& rect )
+void EPGView::focusItem( EPGItem *epgItem )
 {
-    m_overlay->setRect( 0, 0, m_overlay->rect().width(), rect.height() );
+    emit itemFocused( epgItem );
 }