]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/epg/EPGView.cpp
qt: epgview: const correctness
[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() const
108 {
109     return !epgitemsByChannel.isEmpty();
110 }
111
112 static void cleanOverlapped( EPGEventByTimeQMap *epgItemByTime, EPGItem *epgItem, QGraphicsScene *scene )
113 {
114     QDateTime epgItemTime = epgItem->start();
115     QDateTime epgItemTimeEnd = epgItem->end();
116     /* Clean overlapped programs */
117     foreach(const QDateTime existingTimes, epgItemByTime->keys())
118     {
119         if ( existingTimes > epgItemTimeEnd ) break; /* Can't overlap later items */
120         if ( existingTimes != epgItemTime )
121         {
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 ) ) )
126             {
127                 epgItemByTime->remove( otherEPGItem->start() );
128                 scene->removeItem( otherEPGItem );
129                 delete otherEPGItem;
130             }
131         }
132     }
133 }
134
135 bool EPGView::addEPGEvent( vlc_epg_event_t *data, QString channelName, bool b_current )
136 {
137     /* Init our nested map if required */
138     EPGEventByTimeQMap *epgItemByTime;
139     EPGItem *epgItem;
140     bool b_refresh_channels = false;
141
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 )
146     {
147         m_startTime = eventStart;
148         emit startTimeChanged( m_startTime );
149     }
150
151     mutex.lock();
152     if ( !epgitemsByChannel.contains( channelName ) )
153     {
154         epgItemByTime = new EPGEventByTimeQMap();
155         epgitemsByChannel.insert( channelName, epgItemByTime );
156         emit channelAdded( channelName );
157         b_refresh_channels = true;
158     } else {
159         epgItemByTime = epgitemsByChannel.value( channelName );
160     }
161
162     if ( epgItemByTime->contains( eventStart ) )
163     {
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() );
169         mutex.unlock();
170         return false;
171     } else {
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 ) );
181     }
182     mutex.unlock();
183
184     /* Update rows on each item */
185     if ( b_refresh_channels ) updateChannels();
186
187     return true;
188 }
189
190 void EPGView::removeEPGEvent( vlc_epg_event_t *data, QString channelName )
191 {
192     EPGEventByTimeQMap *epgItemByTime;
193     QDateTime eventStart = QDateTime::fromTime_t( data->i_start );
194     EPGItem *epgItem;
195     bool b_update_channels = false;
196
197     mutex.lock();
198     if ( epgitemsByChannel.contains( channelName ) )
199     {
200         epgItemByTime = epgitemsByChannel.value( channelName );
201
202         if ( epgItemByTime->contains( eventStart ) )
203         { /* delete our EPGItem */
204             epgItem = epgItemByTime->value( eventStart );
205             epgItemByTime->remove( eventStart );
206             scene()->removeItem( epgItem );
207             delete epgItem;
208         }
209
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;
216         }
217     }
218     mutex.unlock();
219
220     if ( b_update_channels ) updateChannels();
221 }
222
223 void EPGView::reset()
224 {
225     /* clean our items storage and remove them from the scene */
226     EPGEventByTimeQMap *epgItemByTime;
227     EPGItem *epgItem;
228     mutex.lock();
229     foreach( const QString &channelName, epgitemsByChannel.keys() )
230     {
231         epgItemByTime = epgitemsByChannel[ channelName ];
232         foreach( const QDateTime &key, epgItemByTime->keys() )
233         {
234             epgItem = epgItemByTime->value( key );
235             scene()->removeItem( epgItem );
236             epgItemByTime->remove( key );
237             delete epgItem;
238         }
239         epgitemsByChannel.remove( channelName );
240         delete epgItemByTime;
241         emit channelRemoved( channelName ); /* notify others */
242     }
243     mutex.unlock();
244 }
245
246 void EPGView::cleanup()
247 {
248     /* remove expired items and clear their current flag */
249     EPGEventByTimeQMap *epgItemByTime;
250     EPGItem *epgItem;
251     m_baseTime = QDateTime::currentDateTime();
252     QDateTime lowestTime = m_baseTime;
253     bool b_timechanged = false;
254     bool b_update_channels = false;
255
256     mutex.lock();
257     foreach( const QString &channelName, epgitemsByChannel.keys() )
258     {
259         epgItemByTime = epgitemsByChannel[ channelName ];
260         foreach( const QDateTime &key, epgItemByTime->keys() )
261         {
262             epgItem = epgItemByTime->value( key );
263             if ( epgItem->endsBefore( baseTime() ) ) /* Expired item ? */
264             {
265                 scene()->removeItem( epgItem );
266                 epgItemByTime->remove( key );
267                 delete epgItem;
268             } else {
269                 epgItem->setCurrent( false ); /* if stream doesn't update */
270                 if ( lowestTime > epgItem->start() )
271                 {
272                     lowestTime = epgItem->start(); /* update our reference */
273                     b_timechanged = true;
274                 }
275             }
276         }
277
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;
284         }
285     }
286     mutex.unlock();
287
288     if ( b_timechanged )
289     {
290         m_startTime = lowestTime;
291         emit startTimeChanged( m_startTime );
292     }
293
294     if ( b_update_channels ) updateChannels();
295 }
296
297 EPGView::~EPGView()
298 {
299     reset();
300 }
301
302 void EPGView::updateDuration()
303 {
304     QDateTime maxItemTime;
305     mutex.lock();
306     foreach( EPGEventByTimeQMap *epgItemByTime, epgitemsByChannel.values() )
307         foreach( EPGItem *epgItem, epgItemByTime->values() )
308             if ( epgItem->end() > maxItemTime ) maxItemTime = epgItem->end();
309     mutex.unlock();
310     m_duration = m_startTime.secsTo( maxItemTime );
311     emit durationChanged( m_duration );
312 }
313
314 void EPGView::focusItem( EPGItem *epgItem )
315 {
316     emit itemFocused( epgItem );
317 }