]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/epg/EPGView.cpp
4e7e3dd239f43264eedc6db7fef5b18e2c1c39ab
[vlc] / modules / gui / qt4 / components / epg / EPGView.cpp
1 /*****************************************************************************
2  * EPGView.cpp: EPGView
3  ****************************************************************************
4  * Copyright © 2009-2010 VideoLAN
5  * $Id$
6  *
7  * Authors: Ludovic Fauvet <etix@l0cal.com>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 #include "EPGView.hpp"
25 #include "EPGItem.hpp"
26
27 #include <QDateTime>
28 #include <QMatrix>
29 #include <QPaintEvent>
30 #include <QScrollBar>
31 #include <QtDebug>
32 #include <QGraphicsTextItem>
33
34 EPGGraphicsScene::EPGGraphicsScene( QObject *parent ) : QGraphicsScene( parent )
35 {}
36
37 void EPGGraphicsScene::drawBackground( QPainter *painter, const QRectF &rect)
38 {
39     EPGView *epgView;
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() ) );
47 }
48
49 EPGView::EPGView( QWidget *parent ) : QGraphicsView( parent )
50 {
51     setContentsMargins( 0, 0, 0, 0 );
52     setFrameStyle( QFrame::Box );
53     setAlignment( Qt::AlignLeft | Qt::AlignTop );
54
55     m_startTime = QDateTime::currentDateTime();
56
57     EPGGraphicsScene *EPGscene = new EPGGraphicsScene( this );
58
59     setScene( EPGscene );
60 }
61
62 void EPGView::setScale( double scaleFactor )
63 {
64     m_scaleFactor = scaleFactor;
65     QMatrix matrix;
66     matrix.scale( scaleFactor, 1 );
67     setMatrix( matrix );
68 }
69
70 void EPGView::updateStartTime()
71 {
72     mutex.lock();
73     foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
74     {
75         foreach( EPGItem *epgItem, epgItemByTime->values() )
76         {
77             epgItem->updatePos();
78         }
79     }
80     mutex.unlock();
81 }
82
83 void EPGView::updateChannels()
84 {
85     /* Make sure our items goes to the correct row */
86     unsigned int channelIndex = 0;
87     mutex.lock();
88     foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
89     {
90         foreach( EPGItem *epgItem, epgItemByTime->values() )
91             epgItem->setRow( channelIndex );
92         channelIndex++;
93     }
94     mutex.unlock();
95 }
96
97 const QDateTime& EPGView::startTime()
98 {
99     return m_startTime;
100 }
101
102 const QDateTime& EPGView::baseTime()
103 {
104     return m_baseTime;
105 }
106
107 bool EPGView::hasValidData()
108 {
109     return !epgitemsByChannel.empty();
110 }
111
112 static void cleanOverlapped( EPGEventByTimeQMap *epgItemByTime, EPGItem *epgItem, QGraphicsScene *scene )
113 {
114     /* Clean overlapped programs */
115     foreach(const QDateTime existingTimes, epgItemByTime->keys())
116     {
117         if ( existingTimes != epgItem->start() )
118         {
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 ) ) )
123             {
124                 epgItemByTime->remove( otherEPGItem->start() );
125                 scene->removeItem( otherEPGItem );
126                 delete otherEPGItem;
127             }
128         }
129     }
130 }
131
132 bool EPGView::addEPGEvent( vlc_epg_event_t *data, QString channelName, bool b_current )
133 {
134     /* Init our nested map if required */
135     EPGEventByTimeQMap *epgItemByTime;
136     EPGItem *epgItem;
137     bool b_refresh_channels = false;
138
139     QDateTime eventStart = QDateTime::fromTime_t( data->i_start );
140     if ( eventStart < m_startTime )
141     {
142         m_startTime = eventStart;
143         emit startTimeChanged( m_startTime );
144     }
145
146     mutex.lock();
147     if ( !epgitemsByChannel.contains( channelName ) )
148     {
149         epgItemByTime = new EPGEventByTimeQMap();
150         epgitemsByChannel.insert( channelName, epgItemByTime );
151         emit channelAdded( channelName );
152         b_refresh_channels = true;
153     } else {
154         epgItemByTime = epgitemsByChannel.value( channelName );
155     }
156
157     if ( epgItemByTime->contains( eventStart ) )
158     {
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() );
164         mutex.unlock();
165         return false;
166     } else {
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 ) );
176     }
177     mutex.unlock();
178
179     /* Update rows on each item */
180     if ( b_refresh_channels ) updateChannels();
181
182     return true;
183 }
184
185 void EPGView::removeEPGEvent( vlc_epg_event_t *data, QString channelName )
186 {
187     EPGEventByTimeQMap *epgItemByTime;
188     QDateTime eventStart = QDateTime::fromTime_t( data->i_start );
189     EPGItem *epgItem;
190     bool b_update_channels = false;
191
192     mutex.lock();
193     if ( epgitemsByChannel.contains( channelName ) )
194     {
195         epgItemByTime = epgitemsByChannel.value( channelName );
196
197         if ( epgItemByTime->contains( eventStart ) )
198         { /* delete our EPGItem */
199             epgItem = epgItemByTime->value( eventStart );
200             epgItemByTime->remove( eventStart );
201             scene()->removeItem( epgItem );
202             delete epgItem;
203         }
204
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;
211         }
212     }
213     mutex.unlock();
214
215     if ( b_update_channels ) updateChannels();
216 }
217
218 void EPGView::reset()
219 {
220     /* clean our items storage and remove them from the scene */
221     EPGEventByTimeQMap *epgItemByTime;
222     EPGItem *epgItem;
223     mutex.lock();
224     foreach( const QString &channelName, epgitemsByChannel.keys() )
225     {
226         epgItemByTime = epgitemsByChannel[ channelName ];
227         foreach( const QDateTime &key, epgItemByTime->keys() )
228         {
229             epgItem = epgItemByTime->value( key );
230             scene()->removeItem( epgItem );
231             epgItemByTime->remove( key );
232             delete epgItem;
233         }
234         epgitemsByChannel.remove( channelName );
235         delete epgItemByTime;
236         emit channelRemoved( channelName ); /* notify others */
237     }
238     mutex.unlock();
239 }
240
241 void EPGView::cleanup()
242 {
243     /* remove expired items and clear their current flag */
244     EPGEventByTimeQMap *epgItemByTime;
245     EPGItem *epgItem;
246     m_baseTime = QDateTime::currentDateTime();
247     QDateTime lowestTime = m_baseTime;
248     bool b_timechanged = false;
249     bool b_update_channels = false;
250
251     mutex.lock();
252     foreach( const QString &channelName, epgitemsByChannel.keys() )
253     {
254         epgItemByTime = epgitemsByChannel[ channelName ];
255         foreach( const QDateTime &key, epgItemByTime->keys() )
256         {
257             epgItem = epgItemByTime->value( key );
258             if ( epgItem->endsBefore( baseTime() ) ) /* Expired item ? */
259             {
260                 scene()->removeItem( epgItem );
261                 epgItemByTime->remove( key );
262                 delete epgItem;
263             } else {
264                 epgItem->setCurrent( false ); /* if stream doesn't update */
265                 if ( lowestTime > epgItem->start() )
266                 {
267                     lowestTime = epgItem->start(); /* update our reference */
268                     b_timechanged = true;
269                 }
270             }
271         }
272
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;
279         }
280     }
281     mutex.unlock();
282
283     if ( b_timechanged )
284     {
285         m_startTime = lowestTime;
286         emit startTimeChanged( m_startTime );
287     }
288
289     if ( b_update_channels ) updateChannels();
290 }
291
292 EPGView::~EPGView()
293 {
294     reset();
295 }
296
297 void EPGView::updateDuration()
298 {
299     QDateTime maxItemTime;
300     mutex.lock();
301     foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
302         foreach( EPGItem *epgItem, epgItemByTime->values() )
303             if ( epgItem->end() > maxItemTime ) maxItemTime = epgItem->end();
304     mutex.unlock();
305     m_duration = m_startTime.secsTo( maxItemTime );
306     emit durationChanged( m_duration );
307 }
308
309 void EPGView::focusItem( EPGItem *epgItem )
310 {
311     emit itemFocused( epgItem );
312 }