1 /*****************************************************************************
3 ****************************************************************************
4 * Copyright © 2009-2010 VideoLAN
7 * Authors: Ludovic Fauvet <etix@l0cal.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #include "EPGView.hpp"
25 #include "EPGItem.hpp"
29 #include <QPaintEvent>
32 #include <QGraphicsTextItem>
34 EPGGraphicsScene::EPGGraphicsScene( QObject *parent ) : QGraphicsScene( parent )
37 void EPGGraphicsScene::drawBackground( QPainter *painter, const QRectF &rect)
40 painter->setPen( QPen( QColor( 224, 224, 224 ) ) );
41 for( int y = rect.top() + TRACKS_HEIGHT ; y < rect.bottom() ; y += TRACKS_HEIGHT )
42 painter->drawLine( QLineF( rect.left(), y, rect.right(), y ) );
43 epgView = qobject_cast<EPGView *>(parent());
44 int x = epgView->startTime().secsTo( epgView->baseTime() );
45 painter->setPen( QPen( QColor( 255, 192, 192 ) ) );
46 painter->drawLine( QLineF( x, rect.top(), x, rect.bottom() ) );
49 EPGView::EPGView( QWidget *parent ) : QGraphicsView( parent )
51 setContentsMargins( 0, 0, 0, 0 );
52 setFrameStyle( QFrame::Box );
53 setAlignment( Qt::AlignLeft | Qt::AlignTop );
55 m_startTime = QDateTime::currentDateTime();
57 EPGGraphicsScene *EPGscene = new EPGGraphicsScene( this );
62 void EPGView::setScale( double scaleFactor )
64 m_scaleFactor = scaleFactor;
66 matrix.scale( scaleFactor, 1 );
70 void EPGView::updateStartTime()
73 foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
75 foreach( EPGItem *epgItem, epgItemByTime->values() )
83 void EPGView::updateChannels()
85 /* Make sure our items goes to the correct row */
86 unsigned int channelIndex = 0;
88 foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
90 foreach( EPGItem *epgItem, epgItemByTime->values() )
91 epgItem->setRow( channelIndex );
97 const QDateTime& EPGView::startTime()
102 const QDateTime& EPGView::baseTime()
107 bool EPGView::hasValidData()
109 return !epgitemsByChannel.empty();
112 static void cleanOverlapped( EPGEventByTimeQMap *epgItemByTime, EPGItem *epgItem, QGraphicsScene *scene )
114 /* Clean overlapped programs */
115 foreach(const QDateTime existingTimes, epgItemByTime->keys())
117 if ( existingTimes != epgItem->start() )
119 EPGItem *otherEPGItem = epgItemByTime->value( existingTimes );
120 if ( otherEPGItem->playsAt( epgItem->start().addSecs( 1 ) )
121 || /* add/minus one sec because next one can start at prev end min */
122 otherEPGItem->playsAt( epgItem->end().addSecs( -1 ) ) )
124 epgItemByTime->remove( otherEPGItem->start() );
125 scene->removeItem( otherEPGItem );
132 bool EPGView::addEPGEvent( vlc_epg_event_t *data, QString channelName, bool b_current )
134 /* Init our nested map if required */
135 EPGEventByTimeQMap *epgItemByTime;
137 bool b_refresh_channels = false;
139 QDateTime eventStart = QDateTime::fromTime_t( data->i_start );
140 if ( eventStart < m_startTime )
142 m_startTime = eventStart;
143 emit startTimeChanged( m_startTime );
147 if ( !epgitemsByChannel.contains( channelName ) )
149 epgItemByTime = new EPGEventByTimeQMap();
150 epgitemsByChannel.insert( channelName, epgItemByTime );
151 emit channelAdded( channelName );
152 b_refresh_channels = true;
154 epgItemByTime = epgitemsByChannel.value( channelName );
157 if ( epgItemByTime->contains( eventStart ) )
159 /* Update our existing programs */
160 epgItem = epgItemByTime->value( eventStart );
161 epgItem->setData( data ); /* updates our entry */
162 epgItem->setCurrent( b_current );
163 cleanOverlapped( epgItemByTime, epgItem, scene() );
167 /* Insert a new program entry */
168 epgItem = new EPGItem( data, this );
169 cleanOverlapped( epgItemByTime, epgItem, scene() );
170 /* Effectively insert our new program */
171 epgItem->setCurrent( b_current );
172 epgItemByTime->insert( eventStart, epgItem );
173 scene()->addItem( epgItem );
174 /* update only our row (without calling the updatechannels()) */
175 epgItem->setRow( epgitemsByChannel.keys().indexOf( channelName ) );
179 /* Update rows on each item */
180 if ( b_refresh_channels ) updateChannels();
185 void EPGView::removeEPGEvent( vlc_epg_event_t *data, QString channelName )
187 EPGEventByTimeQMap *epgItemByTime;
188 QDateTime eventStart = QDateTime::fromTime_t( data->i_start );
190 bool b_update_channels = false;
193 if ( epgitemsByChannel.contains( channelName ) )
195 epgItemByTime = epgitemsByChannel.value( channelName );
197 if ( epgItemByTime->contains( eventStart ) )
198 { /* delete our EPGItem */
199 epgItem = epgItemByTime->value( eventStart );
200 epgItemByTime->remove( eventStart );
201 scene()->removeItem( epgItem );
205 if ( epgItemByTime->keys().empty() )
206 { /* Now unused channel */
207 epgitemsByChannel.remove( channelName );
208 delete epgItemByTime;
209 emit channelRemoved( channelName );
210 b_update_channels = true;
215 if ( b_update_channels ) updateChannels();
218 void EPGView::reset()
220 /* clean our items storage and remove them from the scene */
221 EPGEventByTimeQMap *epgItemByTime;
224 foreach( const QString &channelName, epgitemsByChannel.keys() )
226 epgItemByTime = epgitemsByChannel[ channelName ];
227 foreach( const QDateTime &key, epgItemByTime->keys() )
229 epgItem = epgItemByTime->value( key );
230 scene()->removeItem( epgItem );
231 epgItemByTime->remove( key );
234 epgitemsByChannel.remove( channelName );
235 delete epgItemByTime;
236 emit channelRemoved( channelName ); /* notify others */
241 void EPGView::cleanup()
243 /* remove expired items and clear their current flag */
244 EPGEventByTimeQMap *epgItemByTime;
246 m_baseTime = QDateTime::currentDateTime();
247 QDateTime lowestTime = m_baseTime;
248 bool b_timechanged = false;
249 bool b_update_channels = false;
252 foreach( const QString &channelName, epgitemsByChannel.keys() )
254 epgItemByTime = epgitemsByChannel[ channelName ];
255 foreach( const QDateTime &key, epgItemByTime->keys() )
257 epgItem = epgItemByTime->value( key );
258 if ( epgItem->endsBefore( baseTime() ) ) /* Expired item ? */
260 scene()->removeItem( epgItem );
261 epgItemByTime->remove( key );
264 epgItem->setCurrent( false ); /* if stream doesn't update */
265 if ( lowestTime > epgItem->start() )
267 lowestTime = epgItem->start(); /* update our reference */
268 b_timechanged = true;
273 if ( epgItemByTime->keys().empty() )
274 { /* Now unused channel */
275 epgitemsByChannel.remove( channelName );
276 delete epgItemByTime;
277 emit channelRemoved( channelName );
278 b_update_channels = true;
285 m_startTime = lowestTime;
286 emit startTimeChanged( m_startTime );
289 if ( b_update_channels ) updateChannels();
297 void EPGView::updateDuration()
299 QDateTime maxItemTime;
301 foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
302 foreach( EPGItem *epgItem, epgItemByTime->values() )
303 if ( epgItem->end() > maxItemTime ) maxItemTime = epgItem->end();
305 m_duration = m_startTime.secsTo( maxItemTime );
306 emit durationChanged( m_duration );
309 void EPGView::focusItem( EPGItem *epgItem )
311 emit itemFocused( epgItem );