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() const
109 return !epgitemsByChannel.isEmpty();
112 static void cleanOverlapped( EPGEventByTimeQMap *epgItemByTime, EPGItem *epgItem, QGraphicsScene *scene )
114 QDateTime epgItemTime = epgItem->start();
115 QDateTime epgItemTimeEnd = epgItem->end();
116 /* Clean overlapped programs */
117 foreach(const QDateTime existingTimes, epgItemByTime->keys())
119 if ( existingTimes > epgItemTimeEnd ) break; /* Can't overlap later items */
120 if ( existingTimes != epgItemTime )
122 EPGItem *otherEPGItem = epgItemByTime->value( existingTimes );
123 if ( otherEPGItem->playsAt( epgItemTime.addSecs( 1 ) )
124 || /* add/minus one sec because next one can start at prev end min */
125 otherEPGItem->playsAt( epgItemTimeEnd.addSecs( -1 ) ) )
127 epgItemByTime->remove( otherEPGItem->start() );
128 scene->removeItem( otherEPGItem );
135 bool EPGView::addEPGEvent( vlc_epg_event_t *data, QString channelName, bool b_current )
137 /* Init our nested map if required */
138 EPGEventByTimeQMap *epgItemByTime;
140 bool b_refresh_channels = false;
142 QDateTime eventStart = QDateTime::fromTime_t( data->i_start );
143 if ( eventStart.addSecs( data->i_duration ) < m_baseTime )
144 return false; /* EPG feed sent expired item */
145 if ( eventStart < m_startTime )
147 m_startTime = eventStart;
148 emit startTimeChanged( m_startTime );
152 if ( !epgitemsByChannel.contains( channelName ) )
154 epgItemByTime = new EPGEventByTimeQMap();
155 epgitemsByChannel.insert( channelName, epgItemByTime );
156 emit channelAdded( channelName );
157 b_refresh_channels = true;
159 epgItemByTime = epgitemsByChannel.value( channelName );
162 if ( epgItemByTime->contains( eventStart ) )
164 /* Update our existing programs */
165 epgItem = epgItemByTime->value( eventStart );
166 epgItem->setCurrent( b_current );
167 if ( epgItem->setData( data ) ) /* updates our entry */
168 cleanOverlapped( epgItemByTime, epgItem, scene() );
172 /* Insert a new program entry */
173 epgItem = new EPGItem( data, this );
174 cleanOverlapped( epgItemByTime, epgItem, scene() );
175 /* Effectively insert our new program */
176 epgItem->setCurrent( b_current );
177 epgItemByTime->insert( eventStart, epgItem );
178 scene()->addItem( epgItem );
179 /* update only our row (without calling the updatechannels()) */
180 epgItem->setRow( epgitemsByChannel.keys().indexOf( channelName ) );
184 /* Update rows on each item */
185 if ( b_refresh_channels ) updateChannels();
190 void EPGView::removeEPGEvent( vlc_epg_event_t *data, QString channelName )
192 EPGEventByTimeQMap *epgItemByTime;
193 QDateTime eventStart = QDateTime::fromTime_t( data->i_start );
195 bool b_update_channels = false;
198 if ( epgitemsByChannel.contains( channelName ) )
200 epgItemByTime = epgitemsByChannel.value( channelName );
202 if ( epgItemByTime->contains( eventStart ) )
203 { /* delete our EPGItem */
204 epgItem = epgItemByTime->value( eventStart );
205 epgItemByTime->remove( eventStart );
206 scene()->removeItem( epgItem );
210 if ( epgItemByTime->keys().isEmpty() )
211 { /* Now unused channel */
212 epgitemsByChannel.remove( channelName );
213 delete epgItemByTime;
214 emit channelRemoved( channelName );
215 b_update_channels = true;
220 if ( b_update_channels ) updateChannels();
223 void EPGView::reset()
225 /* clean our items storage and remove them from the scene */
226 EPGEventByTimeQMap *epgItemByTime;
229 foreach( const QString &channelName, epgitemsByChannel.keys() )
231 epgItemByTime = epgitemsByChannel[ channelName ];
232 foreach( const QDateTime &key, epgItemByTime->keys() )
234 epgItem = epgItemByTime->value( key );
235 scene()->removeItem( epgItem );
236 epgItemByTime->remove( key );
239 epgitemsByChannel.remove( channelName );
240 delete epgItemByTime;
241 emit channelRemoved( channelName ); /* notify others */
246 void EPGView::cleanup()
248 /* remove expired items and clear their current flag */
249 EPGEventByTimeQMap *epgItemByTime;
251 m_baseTime = QDateTime::currentDateTime();
252 QDateTime lowestTime = m_baseTime;
253 bool b_timechanged = false;
254 bool b_update_channels = false;
257 foreach( const QString &channelName, epgitemsByChannel.keys() )
259 epgItemByTime = epgitemsByChannel[ channelName ];
260 foreach( const QDateTime &key, epgItemByTime->keys() )
262 epgItem = epgItemByTime->value( key );
263 if ( epgItem->endsBefore( baseTime() ) ) /* Expired item ? */
265 scene()->removeItem( epgItem );
266 epgItemByTime->remove( key );
269 epgItem->setCurrent( false ); /* if stream doesn't update */
270 if ( lowestTime > epgItem->start() )
272 lowestTime = epgItem->start(); /* update our reference */
273 b_timechanged = true;
278 if ( epgItemByTime->keys().isEmpty() )
279 { /* Now unused channel */
280 epgitemsByChannel.remove( channelName );
281 delete epgItemByTime;
282 emit channelRemoved( channelName );
283 b_update_channels = true;
290 m_startTime = lowestTime;
291 emit startTimeChanged( m_startTime );
294 if ( b_update_channels ) updateChannels();
302 void EPGView::updateDuration()
304 QDateTime maxItemTime;
306 foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
307 foreach( EPGItem *epgItem, epgItemByTime->values() )
308 if ( epgItem->end() > maxItemTime ) maxItemTime = epgItem->end();
310 m_duration = m_startTime.secsTo( maxItemTime );
311 emit durationChanged( m_duration );
314 void EPGView::focusItem( EPGItem *epgItem )
316 emit itemFocused( epgItem );