File indexing completed on 2024-05-05 04:47:20
0001 /**************************************************************************************** 0002 * Copyright (c) 2009 Téo Mrnjavac <teo@kde.org> * 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 "OcsPersonItem.h" 0018 0019 #include "core/support/Debug.h" 0020 0021 #include <QAction> 0022 #include <KRun> 0023 #include <QStandardPaths> 0024 #include <Attica/Provider> 0025 #include <Attica/ItemJob> 0026 #include <KIO/OpenUrlJob> 0027 #include <KIO/StoredTransferJob> 0028 0029 #include <QHBoxLayout> 0030 #include <QVBoxLayout> 0031 #include <QPainter> 0032 #include <QStyleOption> 0033 0034 OcsPersonItem::OcsPersonItem( const KAboutPerson &person, const QString &ocsUsername, PersonStatus status, QWidget *parent ) 0035 : QWidget( parent ) 0036 , m_status( status ) 0037 , m_state( Offline ) 0038 { 0039 m_person = &person; 0040 m_ocsUsername = ocsUsername; 0041 0042 setupUi( this ); 0043 init(); 0044 0045 m_avatar->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); 0046 0047 //TODO: Add favorite artists! 0048 0049 } 0050 0051 void 0052 OcsPersonItem::init() 0053 { 0054 m_textLabel->setTextInteractionFlags( Qt::TextBrowserInteraction ); 0055 m_textLabel->setOpenExternalLinks( true ); 0056 m_textLabel->setContentsMargins( 5, 0, 0, 2 ); 0057 m_verticalLayout->setSpacing( 0 ); 0058 0059 m_vertLine->hide(); 0060 m_initialSpacer->changeSize( 0, 40, QSizePolicy::Fixed, QSizePolicy::Fixed ); 0061 layout()->invalidate(); 0062 0063 m_aboutText.append( "<b>" + m_person->name() + "</b>" ); 0064 if( !m_person->task().isEmpty() ) 0065 m_aboutText.append( "<br/>" + m_person->task() ); 0066 0067 m_iconsBar = new KToolBar( this, false, false ); 0068 m_snBar = new KToolBar( this, false, false ); 0069 0070 m_iconsBar->setIconSize( QSize( 22, 22 ) ); 0071 m_iconsBar->setContentsMargins( 0, 0, 0, 0 ); 0072 m_iconsBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); 0073 0074 if( m_status == Author ) 0075 { 0076 QHBoxLayout *iconsLayout = new QHBoxLayout( this ); 0077 iconsLayout->setMargin( 0 ); 0078 iconsLayout->setSpacing( 0 ); 0079 m_verticalLayout->insertLayout( m_verticalLayout->count() - 1, iconsLayout ); 0080 iconsLayout->addWidget( m_iconsBar ); 0081 iconsLayout->addWidget( m_snBar ); 0082 iconsLayout->addStretch( 0 ); 0083 0084 m_snBar->setIconSize( QSize( 16, 16 ) ); 0085 m_snBar->setContentsMargins( 0, 0, 0, 0 ); 0086 m_snBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); 0087 } 0088 else 0089 { 0090 layout()->addWidget( m_iconsBar ); 0091 m_snBar->hide(); 0092 } 0093 0094 if( !m_person->emailAddress().isEmpty() ) 0095 { 0096 QAction *email = new QAction( QIcon::fromTheme( QStringLiteral("mail-send") ), i18n("Email contributor"), this ); 0097 email->setToolTip( m_person->emailAddress() ); 0098 email->setData( QString( "mailto:" + m_person->emailAddress() ) ); 0099 m_iconsBar->addAction( email ); 0100 } 0101 0102 if( !m_person->webAddress().isEmpty() ) 0103 { 0104 QAction *homepage = new QAction( QIcon::fromTheme( QStringLiteral("internet-services") ), i18n("Visit contributor's homepage"), this ); 0105 homepage->setToolTip( m_person->webAddress() ); 0106 homepage->setData( m_person->webAddress() ); 0107 m_iconsBar->addAction( homepage ); 0108 } 0109 0110 connect( m_iconsBar, &KToolBar::actionTriggered, this, &OcsPersonItem::launchUrl ); 0111 connect( m_snBar, &KToolBar::actionTriggered, this, &OcsPersonItem::launchUrl ); 0112 m_textLabel->setText( m_aboutText ); 0113 } 0114 0115 OcsPersonItem::~OcsPersonItem() 0116 {} 0117 0118 QString 0119 OcsPersonItem::name() const 0120 { 0121 return m_person->name(); 0122 } 0123 0124 void 0125 OcsPersonItem::launchUrl( QAction *action ) //SLOT 0126 { 0127 QUrl url = QUrl( action->data().toString() ); 0128 KIO::OpenUrlJob *openUrlJob = new KIO::OpenUrlJob(url, QStringLiteral("text/html"), this ); 0129 openUrlJob->setRunExecutables( true ); 0130 openUrlJob->start(); 0131 } 0132 0133 void 0134 OcsPersonItem::switchToOcs( Attica::Provider &provider ) 0135 { 0136 if( m_state == Online ) 0137 return; 0138 m_avatar->setFixedWidth( 56 ); 0139 m_vertLine->show(); 0140 m_initialSpacer->changeSize( 5, 40, QSizePolicy::Fixed, QSizePolicy::Fixed ); 0141 layout()->invalidate(); 0142 0143 if( !m_ocsUsername.isEmpty() ) 0144 { 0145 Attica::ItemJob< Attica::Person > *personJob; 0146 if( m_ocsUsername == QStringLiteral( "%%category%%" ) ) //TODO: handle grouping 0147 return; 0148 0149 personJob = provider.requestPerson( m_ocsUsername ); 0150 connect( personJob, &Attica::BaseJob::finished, this, &OcsPersonItem::onJobFinished ); 0151 Q_EMIT ocsFetchStarted(); 0152 m_state = Online; 0153 personJob->start(); 0154 } 0155 } 0156 0157 void 0158 OcsPersonItem::onJobFinished( Attica::BaseJob *job ) 0159 { 0160 Attica::ItemJob< Attica::Person > *personJob = static_cast< Attica::ItemJob< Attica::Person > * >( job ); 0161 Attica::Metadata metadata = personJob->metadata(); 0162 if( metadata.error() == Attica::Metadata::NoError ) 0163 { 0164 fillOcsData( personJob->result() ); 0165 } 0166 Q_EMIT ocsFetchResult( metadata.error() ); 0167 } 0168 0169 void 0170 OcsPersonItem::fillOcsData( const Attica::Person &ocsPerson ) 0171 { 0172 if( ocsPerson.avatarUrl().isValid() ) 0173 { 0174 auto job = KIO::storedGet( ocsPerson.avatarUrl(), KIO::NoReload, KIO::HideProgressInfo ); 0175 connect( job, &KIO::StoredTransferJob::result, this, &OcsPersonItem::onAvatarLoadingJobFinished ); 0176 } 0177 0178 if( !ocsPerson.country().isEmpty() ) 0179 { 0180 m_aboutText.append( "<br/>" ); 0181 if( !ocsPerson.city().isEmpty() ) 0182 m_aboutText.append( i18nc( "A person's location: City, Country", "%1, %2", ocsPerson.city(), ocsPerson.country() ) ); 0183 else 0184 m_aboutText.append( ocsPerson.country() ); 0185 } 0186 0187 if( m_status == Author ) 0188 { 0189 if( !ocsPerson.extendedAttribute( QStringLiteral("ircchannels") ).isEmpty() ) 0190 { 0191 QString channelsString = ocsPerson.extendedAttribute( QStringLiteral("ircchannels") ); 0192 //We extract the channel names from the string provided by OCS: 0193 QRegExp channelrx = QRegExp( "#+[\\w\\.\\-\\/!()+]+([\\w\\-\\/!()+]?)", Qt::CaseInsensitive ); 0194 QStringList channels; 0195 int pos = 0; 0196 while( ( pos = channelrx.indexIn( channelsString, pos ) ) != -1 ) 0197 { 0198 channels << channelrx.cap( 0 ); 0199 pos += channelrx.matchedLength(); 0200 } 0201 0202 m_aboutText.append( "<br/>" + i18n("IRC channels: ") ); 0203 QString link; 0204 foreach( const QString &channel, channels ) 0205 { 0206 const QString channelName = QString( channel ).remove( '#' ); 0207 link = QStringLiteral( "irc://irc.freenode.org/%1" ).arg( channelName ); 0208 m_aboutText.append( QStringLiteral( "<a href=\"%1\">%2</a>" ).arg( link, channel ) + " " ); 0209 } 0210 } 0211 if( !ocsPerson.extendedAttribute( QStringLiteral("favouritemusic") ).isEmpty() ) 0212 { 0213 QStringList artists = ocsPerson.extendedAttribute( QStringLiteral("favouritemusic") ).split( QStringLiteral(", ") ); 0214 //TODO: make them clickable 0215 m_aboutText.append( "<br/>" + i18n( "Favorite music: " ) + artists.join( QStringLiteral(", ") ) ); 0216 } 0217 } 0218 0219 QAction *visitProfile = new QAction( QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, 0220 "amarok/images/opendesktop-22.png" ) ) ), i18n( "Visit %1's openDesktop.org profile", ocsPerson.firstName() ), this ); 0221 0222 visitProfile->setToolTip( i18n( "Visit %1's profile on openDesktop.org", ocsPerson.firstName() ) ); 0223 0224 visitProfile->setData( ocsPerson.extendedAttribute( QStringLiteral("profilepage") ) ); 0225 m_iconsBar->addAction( visitProfile ); 0226 0227 if( m_status == Author ) 0228 { 0229 QVector< QPair< QString, QString > > ocsHomepages; 0230 ocsHomepages.append( QPair< QString, QString >( ocsPerson.extendedAttribute( QStringLiteral("homepagetype") ), ocsPerson.homepage() ) ); 0231 0232 debug() << "USER HOMEPAGE DATA STARTS HERE"; 0233 debug() << ocsHomepages.last().first << " :: " << ocsHomepages.last().second; 0234 0235 for( int i = 2; i <= 10; i++ ) //OCS supports 10 total homepages as of 2/oct/2009 0236 { 0237 QString type = ocsPerson.extendedAttribute( QStringLiteral( "homepagetype%1" ).arg( i ) ); 0238 ocsHomepages.append( QPair< QString, QString >( ( type == QLatin1String(" ") ) ? QLatin1String("") : type, 0239 ocsPerson.extendedAttribute( QStringLiteral( "homepage%1" ).arg( i ) ) ) ); 0240 debug() << ocsHomepages.last().first << " :: " << ocsHomepages.last().second; 0241 } 0242 0243 bool fillHomepageFromOcs = m_person->webAddress().isEmpty(); //We check if the person already has a homepage in KAboutPerson. 0244 0245 for( QVector< QPair< QString, QString > >::const_iterator entry = ocsHomepages.constBegin(); 0246 entry != ocsHomepages.constEnd(); ++entry ) 0247 { 0248 QString type = (*entry).first; 0249 QString url = (*entry).second; 0250 QIcon icon; 0251 QString text; 0252 0253 if( type == QLatin1String("Blog") ) 0254 { 0255 icon = QIcon::fromTheme( QStringLiteral("kblogger") ); 0256 text = i18n( "Visit contributor's blog" ); 0257 } 0258 else if( type == QLatin1String("delicious") ) 0259 { 0260 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-delicious.png" ) ) ); 0261 text = i18n( "Visit contributor's del.icio.us profile" ); 0262 } 0263 else if( type == QLatin1String("Digg") ) 0264 { 0265 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-digg.png" ) ) ); 0266 text = i18n( "Visit contributor's Digg profile" ); 0267 } 0268 else if( type == QLatin1String("Facebook") ) 0269 { 0270 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-facebook.png" ) ) ); 0271 text = i18n( "Visit contributor's Facebook profile" ); 0272 } 0273 else if( type == QLatin1String("Homepage") || type == QLatin1String("other") || ( type.isEmpty() && !url.isEmpty() ) ) 0274 { 0275 if( fillHomepageFromOcs ) 0276 { 0277 QAction *homepage = new QAction( QIcon::fromTheme( QStringLiteral("internet-services") ), i18n("Visit contributor's homepage"), this ); 0278 homepage->setToolTip( url ); 0279 homepage->setData( url ); 0280 m_iconsBar->addAction( homepage ); 0281 fillHomepageFromOcs = false; 0282 continue; 0283 } 0284 if( type == QLatin1String("other") && url.contains( QLatin1String("last.fm/") ) ) //HACK: assign a last.fm icon if the URL contains last.fm 0285 { 0286 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-lastfm.png" ) ) ); 0287 text = i18n( "Visit contributor's Last.fm profile" ); 0288 } 0289 else 0290 continue; 0291 } 0292 else if( type == QLatin1String("LinkedIn") ) 0293 { 0294 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-linkedin.png" ) ) ); 0295 text = i18n( "Visit contributor's LinkedIn profile" ); 0296 } 0297 else if( type == QLatin1String("MySpace") ) 0298 { 0299 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-myspace.png" ) ) ); 0300 text = i18n( "Visit contributor's MySpace homepage" ); 0301 } 0302 else if( type == QLatin1String("Reddit") ) 0303 { 0304 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-reddit.png" ) ) ); 0305 text = i18n( "Visit contributor's Reddit profile" ); 0306 } 0307 else if( type == QLatin1String("YouTube") ) 0308 { 0309 icon = QIcon( "dragonplayer" ); //FIXME: icon 0310 text = i18n( "Visit contributor's YouTube profile" ); 0311 } 0312 else if( type == QLatin1String("Twitter") ) 0313 { 0314 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-twitter.png" ) ) ); 0315 text = i18n( "Visit contributor's Twitter feed" ); 0316 } 0317 else if( type == QLatin1String("Wikipedia") ) 0318 { 0319 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-wikipedia.png" ) ) ); 0320 text = i18n( "Visit contributor's Wikipedia profile" ); 0321 } 0322 else if( type == QLatin1String("Xing") ) 0323 { 0324 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-xing.png" ) ) ); 0325 text = i18n( "Visit contributor's Xing profile" ); 0326 } 0327 else if( type == QLatin1String("identi.ca") ) 0328 { 0329 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-identica.png" ) ) ); 0330 text = i18n( "Visit contributor's identi.ca feed" ); 0331 } 0332 else if( type == QLatin1String("libre.fm") ) 0333 { 0334 icon = QIcon( "juk" ); //FIXME: icon 0335 text = i18n( "Visit contributor's libre.fm profile" ); 0336 } 0337 else if( type == QLatin1String("StackOverflow") ) 0338 { 0339 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-stackoverflow.png" ) ) ); 0340 text = i18n( "Visit contributor's StackOverflow profile" ); 0341 } 0342 else 0343 break; 0344 QAction *action = new QAction( icon, text, this ); 0345 action->setToolTip( url ); 0346 action->setData( url ); 0347 m_snBar->addAction( action ); 0348 } 0349 0350 debug() << "END USER HOMEPAGE DATA"; 0351 } 0352 0353 m_textLabel->setText( m_aboutText ); 0354 } 0355 0356 void 0357 OcsPersonItem::onAvatarLoadingJobFinished( KJob *job ) 0358 { 0359 auto storedJob = qobject_cast< KIO::StoredTransferJob * >( job ); 0360 if( storedJob->error() ) 0361 { 0362 debug() << "failed to download the avatar of" << m_ocsUsername << "error:" << storedJob->errorString(); 0363 return; 0364 } 0365 0366 QPixmap pic; 0367 if ( !pic.loadFromData( storedJob->data() ) ) 0368 { 0369 debug() << "failed to load the avatar of" << m_ocsUsername; 0370 return; 0371 } 0372 0373 m_avatar->setFixedSize( 56, 56 ); 0374 m_avatar->setFrameShape( QFrame::StyledPanel ); //this is a FramedLabel, otherwise oxygen wouldn't paint the frame 0375 m_avatar->setPixmap( pic ); 0376 m_avatar->setAlignment( Qt::AlignCenter ); 0377 } 0378