File indexing completed on 2025-01-05 04:25:45

0001 /****************************************************************************************
0002  * Copyright (c) 2010 Rick W. Chen <stuffcorpse@archlinux.us>                           *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "UpcomingEventsMapWidget.h"
0018 #include "UpcomingEventsWidget.h"
0019 #include "network/NetworkAccessManagerProxy.h"
0020 
0021 #include <KLocale>
0022 #include <KStandardDirs>
0023 
0024 #include <QDesktopServices>
0025 #include <QFile>
0026 #include <QFileInfo>
0027 #include <QTimer>
0028 #include <QWebView>
0029 #include <QWebFrame>
0030 
0031 class UpcomingEventsMapWidgetPrivate
0032 {
0033 public:
0034     UpcomingEventsMapWidgetPrivate( UpcomingEventsMapWidget *parent );
0035     ~UpcomingEventsMapWidgetPrivate();
0036 
0037     void addEvent( const LastFmEventPtr &event );
0038     void addMarker( const LastFmEventPtr &event );
0039     QString createInfoString( const LastFmEventPtr &event ) const;
0040     QUrl eventForMapIcon( const LastFmEventPtr &event ) const;
0041     void removeEvent( const LastFmEventPtr &event );
0042     void removeMarker( const LastFmEventPtr &event );
0043 
0044     void _init();
0045     void _linkClicked( const QUrl &url );
0046     void _loadFinished( bool success );
0047     void _centerAt( QObject *obj );
0048 
0049     LastFmEvent::List events;
0050     LastFmEvent::List eventQueue;
0051     QSet<UpcomingEventsListWidget*> listWidgets;
0052     QPointF centerWhenLoaded;
0053     bool isLoaded;
0054 
0055 private:
0056     UpcomingEventsMapWidget *const q_ptr;
0057     Q_DECLARE_PUBLIC( UpcomingEventsMapWidget )
0058 };
0059 
0060 UpcomingEventsMapWidgetPrivate::UpcomingEventsMapWidgetPrivate( UpcomingEventsMapWidget *parent )
0061     : isLoaded( false )
0062     , q_ptr( parent )
0063 {
0064 }
0065 
0066 UpcomingEventsMapWidgetPrivate::~UpcomingEventsMapWidgetPrivate()
0067 {
0068 }
0069 
0070 void
0071 UpcomingEventsMapWidgetPrivate::addEvent( const LastFmEventPtr &event )
0072 {
0073     if( !isLoaded )
0074     {
0075         eventQueue << event;
0076         return;
0077     }
0078     events << event;
0079     addMarker( event );
0080 }
0081 
0082 void
0083 UpcomingEventsMapWidgetPrivate::addMarker( const LastFmEventPtr &event )
0084 {
0085     Q_Q( UpcomingEventsMapWidget );
0086     LastFmLocationPtr loc = event->venue()->location;
0087     QString js = QString( "javascript:addMarker(%1,%2,'%3','%4')" )
0088         .arg( QString::number( loc->latitude ) )
0089         .arg( QString::number( loc->longitude ) )
0090         .arg( eventForMapIcon(event).url() )
0091         .arg( createInfoString(event) );
0092     q->page()->mainFrame()->evaluateJavaScript( js );
0093 }
0094 
0095 void
0096 UpcomingEventsMapWidgetPrivate::removeEvent( const LastFmEventPtr &event )
0097 {
0098     eventQueue.removeAll( event );
0099     if( isLoaded )
0100     {
0101         events.removeAll( event );
0102         removeMarker( event );
0103     }
0104 }
0105 
0106 void
0107 UpcomingEventsMapWidgetPrivate::removeMarker( const LastFmEventPtr &event )
0108 {
0109     Q_Q( UpcomingEventsMapWidget );
0110     LastFmLocationPtr loc = event->venue()->location;
0111     QString js = QString( "javascript:removeMarker(%1,%2)" )
0112         .arg( QString::number( loc->latitude ) )
0113         .arg( QString::number( loc->longitude ) );
0114     q->page()->mainFrame()->evaluateJavaScript( js );
0115 }
0116 
0117 QString
0118 UpcomingEventsMapWidgetPrivate::createInfoString( const LastFmEventPtr &event ) const
0119 {
0120     QString name = event->name();
0121     if( event->isCancelled() )
0122         name = i18nc( "@label:textbox Title for a canceled upcoming event", "<s>%1</s> (Canceled)", name );
0123 
0124     QStringList artists = event->artists();
0125     artists.removeDuplicates();
0126 
0127     QString desc = event->description();
0128     KDateTime dt = event->date();
0129     QStringList tags = event->tags();
0130     LastFmVenuePtr venue = event->venue();
0131     QString venueWebsite = venue->website.url();
0132     QString venueLastFmUrl = venue->url.url();
0133     QString location = venue->location->city;
0134     if( !venue->location->street.isEmpty() )
0135         location.prepend( venue->location->street + ", " );
0136 
0137     QString html = QString(
0138         "<div><img src=\"%1\" alt=\"\" style=\"float:right;margin:5px;clear:right\"/></div>" \
0139         "<div><img src=\"%2\" alt=\"\" style=\"float:right;margin:5px;clear:right\"/></div>" \
0140         "<div id=\"bodyContent\">" \
0141         "<small>" \
0142         "<b>Event:</b> %3<br/>" \
0143         "<b>Artists:</b> %4<br/>" \
0144         "<b>Time:</b> %5<br/>" \
0145         "<b>Date:</b> %6<br/>" \
0146         "<b>Venue:</b> %7<br/>" \
0147         "<b>Location:</b> %8<br/>" \
0148         "<b>Description:</b> %9<br/>" \
0149         "<b>Tags:</b> %10<br/>" \
0150         "<b>Event Website:</b> <a href=\"%11\">Last.fm</a><br/>" \
0151         "<b>Venue Website:</b> <a href=\"%12\">URL</a>, <a href=\"%13\">Last.fm</a><br/>" \
0152         "</small>" \
0153         "</div>")
0154         .arg( event->imageUrl(LastFmEvent::Medium).url() )
0155         .arg( venue->imageUrls[LastFmEvent::Medium].url() )
0156         .arg( name )
0157         .arg( artists.join(", ") )
0158         .arg( KGlobal::locale()->formatTime( dt.time() ) )
0159         .arg( KGlobal::locale()->formatDate( dt.date(), KLocale::FancyShortDate ) )
0160         .arg( venue->name )
0161         .arg( location )
0162         .arg( desc.isEmpty() ? i18n("none") : desc )
0163         .arg( tags.isEmpty() ? i18n("none") : tags.join(", ") )
0164         .arg( event->url().url() )
0165         .arg( venueWebsite.isEmpty() ? i18n("none") : venueWebsite )
0166         .arg( venueLastFmUrl.isEmpty() ? i18n("none") : venueLastFmUrl );
0167     return html;
0168 }
0169 
0170 QUrl
0171 UpcomingEventsMapWidgetPrivate::eventForMapIcon( const LastFmEventPtr &event ) const
0172 {
0173     // Thanks a whole bunch to Nicolas Mollet, Matthias Stasiak at google-maps-icons
0174     // pack (http://code.google.com/p/google-maps-icons/wiki/CultureIcons)
0175     const QStringList &tags = event->tags();
0176     QString name;
0177     if( tags.contains( "festival", Qt::CaseInsensitive ) )
0178         name = "festival.png";
0179     else if( !tags.filter( QRegExp("rock|metal") ).isEmpty() )
0180         name = "music-rock.png";
0181     else if( !tags.filter( QRegExp("hip.?hop|rap") ).isEmpty() )
0182         name = "music-hiphop.png";
0183     else if( !tags.filter( QRegExp("orchest.*|classical|symphon.*") ).isEmpty() )
0184         name = "music-classical.png";
0185     else if( !tags.filter( QRegExp("choir|chorus|choral") ).isEmpty() )
0186         name = "choral.png";
0187     else if( !tags.filter( QRegExp("danc(e|ing)|disco|electronic") ).isEmpty() )
0188         name = "dancinghall.png";
0189     else
0190         name = "music-live.png";
0191     return QUrl( "http://google-maps-icons.googlecode.com/files/" + name );
0192 }
0193 
0194 void
0195 UpcomingEventsMapWidgetPrivate::_centerAt( QObject *obj )
0196 {
0197     Q_Q( UpcomingEventsMapWidget );
0198     UpcomingEventsWidget *widget = static_cast<UpcomingEventsWidget*>( obj );
0199     LastFmVenuePtr venue = widget->eventPtr()->venue();
0200     q->centerAt( venue );
0201 }
0202 
0203 void
0204 UpcomingEventsMapWidgetPrivate::_init()
0205 {
0206     Q_Q( UpcomingEventsMapWidget );
0207     q->connect( q, SIGNAL(loadFinished(bool)), q, SLOT(_loadFinished(bool)) );
0208     QFile mapHtml( KStandardDirs::locate( "data", "amarok/data/upcoming-events-map.html" ) );
0209     if( mapHtml.open( QIODevice::ReadOnly | QIODevice::Text ) )
0210         q->setHtml( mapHtml.readAll() );
0211 }
0212 
0213 void
0214 UpcomingEventsMapWidgetPrivate::_linkClicked( const QUrl &url )
0215 {
0216     QDesktopServices::openUrl( url );
0217 }
0218 
0219 void
0220 UpcomingEventsMapWidgetPrivate::_loadFinished( bool success )
0221 {
0222     if( !success )
0223         return;
0224 
0225     Q_Q( UpcomingEventsMapWidget );
0226     isLoaded = true;
0227     LastFmEvent::List queue = eventQueue;
0228     eventQueue.clear();
0229 
0230     foreach( const LastFmEventPtr &event, queue )
0231         addEvent( event );
0232 
0233     if( !centerWhenLoaded.isNull() )
0234     {
0235         q->centerAt( centerWhenLoaded.y(), centerWhenLoaded.x() );
0236         centerWhenLoaded *= 0.0;
0237     }
0238 }
0239 
0240 UpcomingEventsMapWidget::UpcomingEventsMapWidget( QGraphicsItem *parent )
0241     : KGraphicsWebView( parent )
0242     , d_ptr( new UpcomingEventsMapWidgetPrivate( this ) )
0243 {
0244     page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );
0245     page()->setNetworkAccessManager( The::networkAccessManager() );
0246     connect( page(), SIGNAL(linkClicked(QUrl)), this, SLOT(_linkClicked(QUrl)) );
0247     QTimer::singleShot( 0, this, SLOT(_init()) );
0248 }
0249 
0250 UpcomingEventsMapWidget::~UpcomingEventsMapWidget()
0251 {
0252     delete d_ptr;
0253 }
0254 
0255 void
0256 UpcomingEventsMapWidget::addEvent( const LastFmEventPtr &event )
0257 {
0258     Q_D( UpcomingEventsMapWidget );
0259     d->addEvent( event );
0260 }
0261 
0262 void
0263 UpcomingEventsMapWidget::addEvents( const LastFmEvent::List &events )
0264 {
0265     foreach( const LastFmEventPtr &event, events )
0266         addEvent( event );
0267 }
0268 
0269 void
0270 UpcomingEventsMapWidget::addEventsListWidget( UpcomingEventsListWidget *widget )
0271 {
0272     Q_D( UpcomingEventsMapWidget );
0273     if( widget )
0274     {
0275         d->listWidgets << widget;
0276         addEvents( widget->events() );
0277         connect( widget, SIGNAL(eventAdded(LastFmEventPtr)), this, SLOT(addEvent(LastFmEventPtr)) );
0278         connect( widget, SIGNAL(eventRemoved(LastFmEventPtr)), this, SLOT(removeEvent(LastFmEventPtr)) );
0279         connect( widget, SIGNAL(mapRequested(QObject*)), this, SLOT(_centerAt(QObject*)) );
0280     }
0281 }
0282 
0283 void
0284 UpcomingEventsMapWidget::removeEventsListWidget( UpcomingEventsListWidget *widget )
0285 {
0286     Q_D( UpcomingEventsMapWidget );
0287     if( d->listWidgets.contains( widget ) )
0288     {
0289         foreach( const LastFmEventPtr &event, widget->events() )
0290             removeEvent( event );
0291         d->listWidgets.remove( widget );
0292         widget->disconnect( this );
0293     }
0294 }
0295 
0296 void
0297 UpcomingEventsMapWidget::removeEvent( const LastFmEventPtr &event )
0298 {
0299     Q_D( UpcomingEventsMapWidget );
0300     d->removeEvent( event );
0301 }
0302 
0303 bool
0304 UpcomingEventsMapWidget::isLoaded() const
0305 {
0306     Q_D( const UpcomingEventsMapWidget );
0307     return d->isLoaded;
0308 }
0309 
0310 int
0311 UpcomingEventsMapWidget::eventCount() const
0312 {
0313     Q_D( const UpcomingEventsMapWidget );
0314     return d->events.count();
0315 }
0316 
0317 LastFmEvent::List
0318 UpcomingEventsMapWidget::events() const
0319 {
0320     Q_D( const UpcomingEventsMapWidget );
0321     return d->events;
0322 }
0323 
0324 void
0325 UpcomingEventsMapWidget::centerAt( double latitude, double longitude )
0326 {
0327     Q_D( UpcomingEventsMapWidget );
0328     if( !d->isLoaded )
0329     {
0330         QPointF geo( longitude, latitude );
0331         d->centerWhenLoaded = geo;
0332         return;
0333     }
0334 
0335     QString lat( QString::number( latitude ) );
0336     QString lng( QString::number( longitude ) );
0337     QString js = QString( "javascript:centerAt(%1,%2)" ).arg( lat ).arg( lng );
0338     page()->mainFrame()->evaluateJavaScript( js );
0339 }
0340 
0341 void
0342 UpcomingEventsMapWidget::centerAt( const LastFmVenuePtr &venue )
0343 {
0344     LastFmLocationPtr loc = venue->location;
0345     centerAt( loc->latitude, loc->longitude );
0346 }
0347 
0348 void
0349 UpcomingEventsMapWidget::clear()
0350 {
0351     Q_D( UpcomingEventsMapWidget );
0352     d->events.clear();
0353     page()->mainFrame()->evaluateJavaScript( "javascript:clearMarkers()" );
0354 }
0355