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

0001 /****************************************************************************************
0002  * Copyright (c) 2009 Joffrey Clavel <jclavel@clabert.info>                             *
0003  * Copyright (c) 2009 Oleksandr Khayrullin <saniokh@gmail.com>                          *
0004  * Copyright (c) 2009-2010 Ludovic Deveaux <deveaux.ludovic31@gmail.com>                *
0005  * Copyright (c) 2010 Hormiere Guillaume <hormiere.guillaume@gmail.com>                 *
0006  *                                                                                      *
0007  * This program is free software; you can redistribute it and/or modify it under        *
0008  * the terms of the GNU General Public License as published by the Free Software        *
0009  * Foundation; either version 2 of the License, or (at your option) any later           *
0010  * version.                                                                             *
0011  *                                                                                      *
0012  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0013  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0014  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0015  *                                                                                      *
0016  * You should have received a copy of the GNU General Public License along with         *
0017  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0018  ****************************************************************************************/
0019 
0020 #define DEBUG_PREFIX "UpcomingEventsApplet"
0021 
0022 #include "UpcomingEventsApplet.h"
0023 
0024 #include "amarokurls/AmarokUrl.h"
0025 #include "context/applets/upcomingevents/LastFmEvent.h"
0026 #include "context/widgets/AppletHeader.h"
0027 #include "core/support/Amarok.h"
0028 #include "core/support/Debug.h"
0029 #include "SvgHandler.h"
0030 #include "LastFmEventXmlParser.h"
0031 #include "UpcomingEventsMapWidget.h"
0032 #include "UpcomingEventsCalendarWidget.h"
0033 #include "UpcomingEventsStack.h"
0034 #include "UpcomingEventsStackItem.h"
0035 
0036 #include <KConfigDialog>
0037 #include <KGlobalSettings>
0038 #include <Plasma/IconWidget>
0039 #include <Plasma/Theme>
0040 #include <Plasma/Svg>
0041 
0042 #include <QDesktopServices>
0043 #include <QGraphicsLinearLayout>
0044 #include <QXmlStreamReader>
0045 #include <KConfigGroup>
0046 
0047 UpcomingEventsApplet::UpcomingEventsApplet( QObject* parent, const QVariantList& args )
0048     : Context::Applet( parent, args )
0049     , m_groupVenues( false )
0050     , m_stack( 0 )
0051 {
0052     setHasConfigurationInterface( true );
0053     setBackgroundHints( Plasma::Applet::NoBackground );
0054 }
0055 
0056 UpcomingEventsApplet::~UpcomingEventsApplet()
0057 {
0058 }
0059 
0060 void
0061 UpcomingEventsApplet::init()
0062 {
0063     DEBUG_BLOCK
0064 
0065     Context::Applet::init();
0066 
0067     enableHeader( true );
0068     setHeaderText( i18n( "Upcoming Events" ) );
0069 
0070     m_stack = new UpcomingEventsStack( this );
0071     m_stack->setContentsMargins( 0, 0, 0, 0 );
0072 
0073     connect( m_stack, SIGNAL(collapseStateChanged()), SLOT(collapseStateChanged()) );
0074     connect( this, SIGNAL(listWidgetRemoved(UpcomingEventsListWidget*)),
0075              m_stack, SLOT(cleanupListWidgets()) );
0076 
0077     QAction *calendarAction = new QAction( this );
0078     calendarAction->setIcon( QIcon::fromTheme( "view-calendar" ) );
0079     calendarAction->setToolTip( i18n( "View Events Calendar" ) );
0080     Plasma::IconWidget *calendarIcon = addLeftHeaderAction( calendarAction );
0081     connect( calendarIcon, SIGNAL(clicked()), this, SLOT(viewCalendar()) );
0082 
0083     QAction* settingsAction = new QAction( this );
0084     settingsAction->setIcon( QIcon::fromTheme( "preferences-system" ) );
0085     settingsAction->setToolTip( i18n( "Settings" ) );
0086     settingsAction->setEnabled( true );
0087     Plasma::IconWidget *settingsIcon = addRightHeaderAction( settingsAction );
0088     connect( settingsIcon, SIGNAL(clicked()), this, SLOT(configure()) );
0089 
0090     m_artistStackItem = m_stack->create( QLatin1String("currentartistevents") );
0091     m_artistEventsList = new UpcomingEventsListWidget( m_artistStackItem );
0092     m_artistStackItem->setTitle( i18nc( "@title:group", "No track is currently playing" ) );
0093     m_artistStackItem->setWidget( m_artistEventsList );
0094     m_artistStackItem->setCollapsed( true );
0095     m_artistStackItem->setIcon( QIcon::fromTheme("filename-artist-amarok") );
0096     connect( m_artistEventsList, SIGNAL(mapRequested(QObject*)), SLOT(handleMapRequest(QObject*)) );
0097 
0098     QGraphicsLinearLayout *layout = new QGraphicsLinearLayout( Qt::Vertical );
0099     layout->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
0100     layout->addItem( m_header );
0101     layout->addItem( m_stack );
0102     setLayout( layout );
0103 
0104     // Read config and inform the engine.
0105     enableVenueGrouping( Amarok::config("UpcomingEvents Applet").readEntry( "groupVenues", false ) );
0106     QStringList venueData = Amarok::config("UpcomingEvents Applet").readEntry( "favVenues", QStringList() );
0107     m_favoriteVenues = venueStringToDataList( venueData );
0108 
0109     Plasma::DataEngine *engine = dataEngine( "amarok-upcomingEvents" );
0110     connect( engine, SIGNAL(sourceAdded(QString)), SLOT(engineSourceAdded(QString)) );
0111     engine->query( "artistevents" );
0112     engine->query( "venueevents" );
0113 
0114     updateConstraints();
0115 }
0116 
0117 void
0118 UpcomingEventsApplet::engineSourceAdded( const QString &source )
0119 {
0120     if( source == "artistevents" || source == "venueevents" )
0121         dataEngine( "amarok-upcomingEvents" )->connectSource( source, this );
0122 }
0123 
0124 void
0125 UpcomingEventsApplet::constraintsEvent( Plasma::Constraints constraints )
0126 {
0127     Context::Applet::constraintsEvent( constraints );
0128     prepareGeometryChange();
0129     setHeaderText( i18n( "Upcoming Events" ) );
0130     update();
0131 }
0132 
0133 void
0134 UpcomingEventsApplet::dataUpdated( const QString &source, const Plasma::DataEngine::Data &data )
0135 {
0136     const LastFmEvent::List &events = data[ "events" ].value< LastFmEvent::List >();
0137     if( source == "artistevents" )
0138     {
0139         QString artistName = data[ "artist" ].toString();
0140         m_artistEventsList->clear();
0141         m_artistEventsList->setName( artistName );
0142         addToStackItem( m_artistStackItem, events, artistName );
0143         if( !m_artistStackItem->action( "showinmediasources" ) )
0144         {
0145             QAction *act = new QAction( QIcon::fromTheme("edit-find"), QString(), m_artistStackItem );
0146             act->setToolTip( i18n( "Show in Media Sources" ) );
0147             connect( act, SIGNAL(triggered()), this, SLOT(navigateToArtist()) );
0148             m_artistStackItem->addAction( "showinmediasources", act );
0149         }
0150         m_artistStackItem->setCollapsed( events.isEmpty() );
0151     }
0152     else if( source == "venueevents" )
0153     {
0154         if( !events.isEmpty() )
0155         {
0156             LastFmVenuePtr venue = data[ "venue" ].value<LastFmVenuePtr>();
0157             if( m_groupVenues && m_stack->hasItem("favoritevenuesgroup") )
0158             {
0159                 QString title = i18n( "Favorite Venues" );
0160                 addToStackItem( m_stack->item("favoritevenuesgroup"), events, title );
0161             }
0162             else
0163             {
0164                 UpcomingEventsStackItem *stackItem( 0 );
0165                 UpcomingEventsListWidget *listWidget( 0 );
0166                 LastFmEvent::List newEvents;
0167                 if( !m_stack->hasItem( venue->name ) )
0168                 {
0169                     stackItem = m_stack->create( venue->name );
0170                     listWidget = new UpcomingEventsListWidget( stackItem );
0171                     listWidget->setName( venue->name );
0172                     stackItem->setWidget( listWidget );
0173                     stackItem->setCollapsed( true );
0174                     stackItem->setIcon( QIcon::fromTheme("favorites") );
0175                     stackItem->showCloseButton();
0176                     connect( listWidget, SIGNAL(mapRequested(QObject*)), SLOT(handleMapRequest(QObject*)) );
0177                     connect( listWidget, SIGNAL(destroyed(QObject*)), SLOT(listWidgetDestroyed(QObject*)) );
0178                     emit listWidgetAdded( listWidget );
0179                     newEvents = events;
0180                 }
0181                 else
0182                 {
0183                     stackItem = m_stack->item( venue->name );
0184                     typedef UpcomingEventsListWidget UELW;
0185                     UELW *widget = static_cast<UELW*>( stackItem->widget() );
0186                     newEvents = events.toSet().subtract( widget->events().toSet() ).toList();
0187                 }
0188                 addToStackItem( stackItem, newEvents, venue->name );
0189             }
0190             update();
0191         }
0192         else if( m_groupVenues && m_stack->hasItem( QLatin1String("favoritevenuesgroup") ) )
0193         {
0194             m_stack->remove( QLatin1String("favoritevenuesgroup" ) );
0195         }
0196         else
0197         {
0198             // remove all venue lists
0199             const QRegExp pattern( QLatin1String("^(?!(currentartistevents|venuemapview|calendar)).*$") );
0200             QList<UpcomingEventsStackItem*> eventItems = m_stack->items( pattern );
0201             qDeleteAll( eventItems );
0202         }
0203     }
0204 }
0205 
0206 void
0207 UpcomingEventsApplet::clearVenueItems()
0208 {
0209     m_stack->remove( QLatin1String("favoritevenuesgroup" ) );
0210     m_stack->remove( QLatin1String("venuemapview" ) );
0211 }
0212 
0213 void
0214 UpcomingEventsApplet::addToStackItem( UpcomingEventsStackItem *item,
0215                                       const LastFmEvent::List &events,
0216                                       const QString &name )
0217 {
0218     UpcomingEventsListWidget *listWidget = static_cast<UpcomingEventsListWidget*>( item->widget() );
0219     listWidget->addEvents( events );
0220 
0221     QString title;
0222     int added = listWidget->count();
0223     if( added == 0 )
0224     {
0225         title = name.isEmpty() ? i18n( "No upcoming events" ) : i18n( "%1: No upcoming events", name );
0226     }
0227     else
0228     {
0229         title = name.isEmpty()
0230             ? i18ncp( "@title:group Number of upcoming events", "1 event", "%1 events", added )
0231             : i18ncp( "@title:group Number of upcoming events", "%1: 1 event", "%1: %2 events", name, added );
0232     }
0233     item->setTitle( title );
0234     item->layout()->invalidate();
0235 }
0236 
0237 void
0238 UpcomingEventsApplet::paintInterface( QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &contentsRect )
0239 {
0240     Q_UNUSED( option )
0241     Q_UNUSED( contentsRect )
0242     addGradientToAppletBackground( p );
0243 }
0244 
0245 void
0246 UpcomingEventsApplet::configure()
0247 {
0248     DEBUG_BLOCK
0249     showConfigurationInterface();
0250 }
0251 
0252 void
0253 UpcomingEventsApplet::createConfigurationInterface( KConfigDialog *parent )
0254 {
0255     QWidget *generalSettings = new QWidget;
0256     QWidget *venueSettings = new QWidget;
0257     ui_GeneralSettings.setupUi( generalSettings );
0258     ui_VenueSettings.setupUi( venueSettings );
0259 
0260     // TODO bad, it's done manually ...
0261     QString timeSpan = Amarok::config("UpcomingEvents Applet").readEntry( "timeSpan", "AllEvents" );
0262     if( timeSpan == "AllEvents" )
0263         ui_GeneralSettings.filterComboBox->setCurrentIndex( 0 );
0264     else if( timeSpan == "ThisWeek" )
0265         ui_GeneralSettings.filterComboBox->setCurrentIndex( 1 );
0266     else if( timeSpan == "ThisMonth" )
0267         ui_GeneralSettings.filterComboBox->setCurrentIndex( 2 );
0268     else if( timeSpan == "ThisYear" )
0269         ui_GeneralSettings.filterComboBox->setCurrentIndex( 3 );
0270 
0271     connect( ui_VenueSettings.searchLineEdit, SIGNAL(returnPressed(QString)), SLOT(searchVenue(QString)) );
0272     connect( ui_VenueSettings.searchResultsList, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(showVenueInfo(QListWidgetItem*)) );
0273     connect( ui_VenueSettings.selectedVenuesList, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(showVenueInfo(QListWidgetItem*)) );
0274     connect( ui_VenueSettings.searchResultsList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(venueResultDoubleClicked(QListWidgetItem*)) );
0275     connect( ui_VenueSettings.selectedVenuesList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(selectedVenueDoubleClicked(QListWidgetItem*)) );
0276     connect( ui_VenueSettings.urlValue, SIGNAL(leftClickedUrl(QString)), SLOT(openUrl(QString)) );
0277     connect( ui_VenueSettings.urlValue, SIGNAL(rightClickedUrl(QString)), SLOT(openUrl(QString)) );
0278     connect( ui_VenueSettings.websiteValue, SIGNAL(leftClickedUrl(QString)), SLOT(openUrl(QString)) );
0279     connect( ui_VenueSettings.websiteValue, SIGNAL(rightClickedUrl(QString)), SLOT(openUrl(QString)) );
0280     connect( parent, SIGNAL(clicked()), SLOT(saveSettings()) );
0281 
0282     ui_VenueSettings.photoLabel->hide();
0283     ui_VenueSettings.infoGroupBox->setFont( KGlobalSettings::smallestReadableFont() );
0284     ui_GeneralSettings.groupVenueCheckBox->setCheckState( m_groupVenues ? Qt::Checked : Qt::Unchecked );
0285 
0286     ui_VenueSettings.countryCombo->insertSeparator( 1 );
0287     const QStringList &countryCodes = KGlobal::locale()->allCountriesList();
0288     foreach( const QString &code, countryCodes )
0289         ui_VenueSettings.countryCombo->addItem( KGlobal::locale()->countryCodeToName(code), code );
0290 
0291     foreach( const VenueData &data, m_favoriteVenues )
0292     {
0293         QListWidgetItem *item = new QListWidgetItem;
0294         item->setData( VenueIdRole, data.id );
0295         item->setData( VenueCityRole, data.city );
0296         item->setData( VenueNameRole, data.name );
0297         item->setText( QString( "%1, %2" )
0298                        .arg( item->data( VenueNameRole ).toString() )
0299                        .arg( item->data( VenueCityRole ).toString() ) );
0300         ui_VenueSettings.selectedVenuesList->addItem( item );
0301     }
0302 
0303     parent->addPage( generalSettings, i18n( "Upcoming Events Settings" ), "preferences-system");
0304     parent->addPage( venueSettings, i18n( "Favorite Venues" ), "favorites" );
0305 }
0306 
0307 void
0308 UpcomingEventsApplet::venueResultDoubleClicked( QListWidgetItem *item )
0309 {
0310     if( !item )
0311         return;
0312 
0313     int row = ui_VenueSettings.searchResultsList->row( item );
0314     QListWidgetItem *moveItem = ui_VenueSettings.searchResultsList->takeItem( row );
0315     ui_VenueSettings.searchResultsList->clearSelection();
0316     ui_VenueSettings.selectedVenuesList->addItem( moveItem );
0317     ui_VenueSettings.selectedVenuesList->setCurrentItem( moveItem );
0318 }
0319 
0320 void
0321 UpcomingEventsApplet::selectedVenueDoubleClicked( QListWidgetItem *item )
0322 {
0323     if( !item )
0324         return;
0325 
0326     int row = ui_VenueSettings.selectedVenuesList->row( item );
0327     QListWidgetItem *moveItem = ui_VenueSettings.selectedVenuesList->takeItem( row );
0328     ui_VenueSettings.selectedVenuesList->clearSelection();
0329     ui_VenueSettings.searchResultsList->addItem( moveItem );
0330     ui_VenueSettings.searchResultsList->setCurrentItem( moveItem );
0331 }
0332 
0333 void
0334 UpcomingEventsApplet::showVenueInfo( QListWidgetItem *item )
0335 {
0336     if( !item )
0337         return;
0338 
0339     const QString &name    = item->data( VenueNameRole ).toString();
0340     const QString &city    = item->data( VenueCityRole ).toString();
0341     const QString &country = item->data( VenueCountryRole ).toString();
0342     const QString &street  = item->data( VenueStreetRole ).toString();
0343     const QUrl &url        = item->data( VenueUrlRole ).value<QUrl>();
0344     const QUrl &website    = item->data( VenueWebsiteRole ).value<QUrl>();
0345     const QUrl &photoUrl   = item->data( VenuePhotoUrlRole ).value<QUrl>();
0346 
0347     ui_VenueSettings.nameValue->setText( name );
0348     ui_VenueSettings.cityValue->setText( city );
0349     ui_VenueSettings.countryValue->setText( country );
0350     ui_VenueSettings.streetValue->setText( street );
0351 
0352     if( url.isValid() )
0353     {
0354         ui_VenueSettings.urlValue->setText( i18nc("@label:textbox Url label", "link") );
0355         ui_VenueSettings.urlValue->setTipText( url.url() );
0356         ui_VenueSettings.urlValue->setUrl( url.url() );
0357     }
0358     else
0359         ui_VenueSettings.urlValue->clear();
0360 
0361     if( website.isValid() )
0362     {
0363         ui_VenueSettings.websiteValue->setText( i18nc("@label:textbox Url label", "link") );
0364         ui_VenueSettings.websiteValue->setTipText( website.url() );
0365         ui_VenueSettings.websiteValue->setUrl( website.url() );
0366     }
0367     else
0368         ui_VenueSettings.websiteValue->clear();
0369 
0370     if( photoUrl.isValid() )
0371     {
0372         The::networkAccessManager()->getData( photoUrl, this,
0373              SLOT(venuePhotoResult(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) );
0374     }
0375     else
0376     {
0377         ui_VenueSettings.photoLabel->hide();
0378         ui_VenueSettings.photoLabel->clear();
0379     }
0380 }
0381 
0382 void
0383 UpcomingEventsApplet::searchVenue( const QString &text )
0384 {
0385     QUrl url;
0386     url.setScheme( "http" );
0387     url.setHost( "ws.audioscrobbler.com" );
0388     url.setPath( "/2.0/" );
0389     url.addQueryItem( "method", "venue.search" );
0390     url.addQueryItem( "api_key", Amarok::lastfmApiKey() );
0391     url.addQueryItem( "venue", text );
0392     int currentCountryIndex = ui_VenueSettings.countryCombo->currentIndex();
0393     const QString &countryCode = ui_VenueSettings.countryCombo->itemData( currentCountryIndex ).toString();
0394     if( !countryCode.isEmpty() )
0395         url.addQueryItem( "country", countryCode );
0396     The::networkAccessManager()->getData( url, this,
0397          SLOT(venueResults(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) );
0398 }
0399 
0400 void
0401 UpcomingEventsApplet::venueResults( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e )
0402 {
0403     Q_UNUSED( url )
0404     if( e.code != QNetworkReply::NoError )
0405     {
0406         debug() << "Failed to get venue results:" << e.description;
0407         return;
0408     }
0409 
0410     ui_VenueSettings.searchResultsList->clear();
0411     QXmlStreamReader xml( data );
0412     while( !xml.atEnd() )
0413     {
0414         xml.readNext();
0415         if( xml.isStartElement() && xml.name() == "venue" )
0416         {
0417             LastFmVenueXmlParser venueParser( xml );
0418             if( venueParser.read() )
0419             {
0420                 QListWidgetItem *item = new QListWidgetItem;
0421 
0422                 LastFmVenuePtr venue = venueParser.venue();
0423                 item->setData( VenueIdRole, venue->id );
0424                 item->setData( VenueNameRole, venue->name );
0425                 item->setData( VenuePhotoUrlRole, venue->imageUrls[LastFmEvent::Large] );
0426                 item->setData( VenueUrlRole, venue->url );
0427                 item->setData( VenueWebsiteRole, venue->website );
0428 
0429                 LastFmLocationPtr location = venue->location;
0430                 item->setData( VenueCityRole, location->city );
0431                 item->setData( VenueCountryRole, location->country );
0432                 item->setData( VenueStreetRole, location->street );
0433 
0434                 item->setText( QString( "%1, %2" )
0435                                .arg( item->data( VenueNameRole ).toString() )
0436                                .arg( item->data( VenueCityRole ).toString() ) );
0437                 ui_VenueSettings.searchResultsList->addItem( item );
0438             }
0439         }
0440     }
0441 }
0442 
0443 void
0444 UpcomingEventsApplet::venuePhotoResult( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e )
0445 {
0446     Q_UNUSED( url )
0447     if( e.code != QNetworkReply::NoError )
0448     {
0449         debug() << "Failed to get venue photo:" << e.description;
0450         return;
0451     }
0452 
0453     QPixmap photo;
0454     if( photo.loadFromData( data ) )
0455     {
0456         photo = photo.scaled( 140, 140, Qt::KeepAspectRatio, Qt::SmoothTransformation );
0457         photo = The::svgHandler()->addBordersToPixmap( photo, 5, QString(), true );
0458         ui_VenueSettings.photoLabel->setPixmap( photo );
0459         ui_VenueSettings.photoLabel->show();
0460     }
0461 }
0462 
0463 QList<UpcomingEventsApplet::VenueData>
0464 UpcomingEventsApplet::venueStringToDataList( const QStringList &list )
0465 {
0466     // config qstringlist is stored as format: QString(id;name;city), QString(id;name;city), ...
0467     QList<VenueData> dataList;
0468     foreach( const QString &item, list )
0469     {
0470         const QStringList &frag = item.split( QChar(';') );
0471         VenueData data = { frag.at( 0 ).toInt(), frag.at( 1 ), frag.at( 2 ) };
0472         dataList << data;
0473     }
0474     return dataList;
0475 }
0476 
0477 void
0478 UpcomingEventsApplet::openUrl( const QString &url )
0479 {
0480     QDesktopServices::openUrl( QUrl(url) );
0481 }
0482 
0483 UpcomingEventsMapWidget *
0484 UpcomingEventsApplet::mapView()
0485 {
0486     if( m_stack->hasItem("venuemapview") )
0487     {
0488         UpcomingEventsStackItem *item = m_stack->item( "venuemapview" );
0489         return static_cast<UpcomingEventsMapWidget*>( item->widget() );
0490     }
0491 
0492     UpcomingEventsStackItem *stackItem = m_stack->create( QLatin1String("venuemapview") );
0493     UpcomingEventsMapWidget *view = new UpcomingEventsMapWidget( stackItem );
0494     stackItem->setIcon( QIcon::fromTheme( "edit-find" ) );
0495     stackItem->setTitle( i18n( "Map View" ) );
0496     stackItem->setWidget( view );
0497     stackItem->setMinimumWidth( 50 );
0498     stackItem->showCloseButton();
0499     m_stack->setMinimumWidth( 50 );
0500     const QRegExp pattern( QLatin1String("^(?!(venuemapview|calendar)).*$") );
0501     QList<UpcomingEventsStackItem*> eventItems = m_stack->items( pattern );
0502     foreach( UpcomingEventsStackItem *item, eventItems )
0503     {
0504         typedef UpcomingEventsListWidget LW;
0505         if( item )
0506             view->addEventsListWidget( qgraphicsitem_cast<LW*>( item->widget() ) );
0507     }
0508     connect( this, SIGNAL(listWidgetAdded(UpcomingEventsListWidget*)),
0509              view, SLOT(addEventsListWidget(UpcomingEventsListWidget*)) );
0510     connect( this, SIGNAL(listWidgetRemoved(UpcomingEventsListWidget*)),
0511              view, SLOT(removeEventsListWidget(UpcomingEventsListWidget*)) );
0512     return view;
0513 }
0514 
0515 void
0516 UpcomingEventsApplet::collapseStateChanged()
0517 {
0518     emit sizeHintChanged( Qt::PreferredSize );
0519 }
0520 
0521 void
0522 UpcomingEventsApplet::viewCalendar()
0523 {
0524     if( m_stack->hasItem("calendar") )
0525     {
0526         m_stack->item("calendar")->setCollapsed( false );
0527         return;
0528     }
0529 
0530     UpcomingEventsStackItem *stackItem = m_stack->create( QLatin1String("calendar") );
0531     UpcomingEventsCalendarWidget *calendar = new UpcomingEventsCalendarWidget( stackItem );
0532     stackItem->setIcon( QIcon::fromTheme( "view-calendar" ) );
0533     stackItem->setTitle( i18n( "Events Calendar" ) );
0534     stackItem->setWidget( calendar );
0535     stackItem->setMinimumWidth( 50 );
0536     stackItem->showCloseButton();
0537     stackItem->addAction( "jumptotoday", calendar->todayAction() );
0538     const QRegExp pattern( QLatin1String("^(?!(venuemapview|calendar)).*$") );
0539     QList<UpcomingEventsStackItem*> eventItems = m_stack->items( pattern );
0540     foreach( UpcomingEventsStackItem *item, eventItems )
0541     {
0542         typedef UpcomingEventsListWidget LW;
0543         if( item )
0544             calendar->addEvents( qgraphicsitem_cast<LW*>( item->widget() )->events() );
0545     }
0546 }
0547 
0548 QString
0549 UpcomingEventsApplet::currentTimeSpan()
0550 {
0551     QString span = ui_GeneralSettings.filterComboBox->currentText();
0552     if( span == i18n("This week") )
0553         return "ThisWeek";
0554     else if( span == i18n("This month") )
0555         return "ThisMonth";
0556     else if( span == i18n("This year") )
0557         return "ThisYear";
0558     else
0559         return "AllEvents";
0560 }
0561 
0562 void
0563 UpcomingEventsApplet::navigateToArtist()
0564 {
0565     if( m_artistEventsList->name().isEmpty() )
0566         return;
0567 
0568     AmarokUrl url;
0569     url.setCommand( "navigate" );
0570     url.setPath( "collections" );
0571     url.setArg( "filter", "artist:\"" + m_artistEventsList->name() + "\"" );
0572     url.run();
0573 }
0574 
0575 void
0576 UpcomingEventsApplet::handleMapRequest( QObject *widget )
0577 {
0578     if( !mapView()->isLoaded() )
0579     {
0580         UpcomingEventsWidget *eventWidget = static_cast<UpcomingEventsWidget*>( widget );
0581         LastFmVenuePtr venue = eventWidget->eventPtr()->venue();
0582         mapView()->centerAt( venue );
0583         m_stack->maximizeItem( QLatin1String("venuemapview") );
0584     }
0585 }
0586 
0587 void
0588 UpcomingEventsApplet::listWidgetDestroyed( QObject *obj )
0589 {
0590     UpcomingEventsListWidget *widget = static_cast<UpcomingEventsListWidget*>( obj );
0591     emit listWidgetRemoved( widget );
0592 }
0593 
0594 void
0595 UpcomingEventsApplet::saveTimeSpan()
0596 {
0597     DEBUG_BLOCK
0598     Amarok::config("UpcomingEvents Applet").writeEntry( "timeSpan", currentTimeSpan() );
0599     dataEngine( "amarok-upcomingEvents" )->query( QString( "timespan:update" ) );
0600 }
0601 
0602 void
0603 UpcomingEventsApplet::saveSettings()
0604 {
0605     clearVenueItems();
0606     saveTimeSpan();
0607 
0608     // save venue settings
0609     QStringList venueConfig;
0610     m_favoriteVenues.clear();
0611     for( int i = 0, count = ui_VenueSettings.selectedVenuesList->count() ; i < count; ++i )
0612     {
0613         int itemId = ui_VenueSettings.selectedVenuesList->item( i )->data( VenueIdRole ).toString().toInt();
0614         QString itemCity = ui_VenueSettings.selectedVenuesList->item( i )->data( VenueCityRole ).toString();
0615         QString itemName = ui_VenueSettings.selectedVenuesList->item( i )->data( VenueNameRole ).toString();
0616         VenueData data = { itemId, itemName, itemCity };
0617         m_favoriteVenues << data;
0618         venueConfig << (QStringList() << QString::number(itemId) << itemName << itemCity).join( QChar(';') );
0619     }
0620     Amarok::config("UpcomingEvents Applet").writeEntry( "favVenues", venueConfig );
0621 
0622     enableVenueGrouping( ui_GeneralSettings.groupVenueCheckBox->checkState() == Qt::Checked );
0623     Amarok::config("UpcomingEvents Applet").writeEntry( "groupVenues", m_groupVenues );
0624 
0625     if( !m_favoriteVenues.isEmpty() )
0626         dataEngine( "amarok-upcomingEvents" )->query( "venueevents:update" );
0627 }
0628 
0629 void
0630 UpcomingEventsApplet::enableVenueGrouping( bool enable )
0631 {
0632     m_groupVenues = enable;
0633     if( enable )
0634     {
0635         if( !m_stack->hasItem("favoritevenuesgroup") )
0636         {
0637             UpcomingEventsStackItem *item = m_stack->create( QLatin1String("favoritevenuesgroup") );
0638             UpcomingEventsListWidget *listWidget = new UpcomingEventsListWidget( item );
0639             listWidget->setName( i18nc( "@title:group", "Favorite Venues" ) );
0640             QString title = i18ncp("@title:group Number of upcoming events",
0641                                    "%1: 1 event", "%1: %2 events",
0642                                    listWidget->name(), listWidget->count());
0643             item->setTitle( title );
0644             item->setIcon( "favorites" );
0645             item->setWidget( listWidget );
0646             connect( listWidget, SIGNAL(mapRequested(QObject*)), SLOT(handleMapRequest(QObject*)) );
0647             connect( listWidget, SIGNAL(destroyed(QObject*)), SLOT(listWidgetDestroyed(QObject*)) );
0648             emit listWidgetAdded( listWidget );
0649         }
0650     }
0651     else
0652     {
0653         m_stack->remove( QLatin1String("favoritevenuesgroup" ) );
0654     }
0655     updateConstraints();
0656 }
0657