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