diff --git a/CMakeLists.txt b/CMakeLists.txt index d7f1e6b99..4e2a5c5b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,10 +111,10 @@ macro_optional_find_package(QCA2) macro_log_feature(QCA2_FOUND "QCA2" "Provides encryption and signing functions required for Grooveshark resolver" "http://delta.affinix.com/qca/" FALSE "" "") macro_optional_find_package(LibAttica) -macro_log_feature(LIBATTICA_FOUND "libattica" "Provides support for automatic fetching and managing of resolvers from the tomahawk website" "https://projects.kde.org/projects/kdesupport/attica" FALSE "" "") +macro_log_feature(LIBATTICA_FOUND "libattica" "Provides support for automatic fetching and managing of resolvers from the tomahawk website" "https://projects.kde.org/projects/kdesupport/attica" TRUE "" "") macro_optional_find_package(QuaZip) -macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloaded resolvers automatically." "http://quazip.sourceforge.net/" FALSE "" "") +macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloaded resolvers automatically." "http://quazip.sourceforge.net/" TRUE "" "") macro_optional_find_package(Jreen) macro_log_feature(LIBJREEN_FOUND "Jreen" "Qt XMPP Library" "https://github.com/euroelessar/jreen" FALSE "" "Jreen is needed for the Jabber SIP plugin.\n") diff --git a/CMakeModules/NSIS.template.in b/CMakeModules/NSIS.template.in index 15e252d20..03122c6fa 100644 --- a/CMakeModules/NSIS.template.in +++ b/CMakeModules/NSIS.template.in @@ -280,7 +280,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${INSTALL_PATH}\bin\libtomahawk_portfwd.dll" File "${INSTALL_PATH}\bin\libtomahawk_lastfm2.dll" File "${INSTALL_PATH}\bin\libtomahawklib.dll" - File "${INSTALL_PATH}\lib\libtomahawk_sip*.dll" + File "${INSTALL_PATH}\lib\libtomahawk_account_*.dll" !endif !ifndef INSTALL_PATH ;Main executable. @@ -293,7 +293,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${BUILD_PATH}\libqxtweb-standalone.dll" File "${BUILD_PATH}\libtomahawk_portfwd.dll" File "${BUILD_PATH}\libtomahawk_lastfm2.dll" - File "${BUILD_PATH}\libtomahawk_sip*.dll" + File "${BUILD_PATH}\libtomahawk_account_*.dll" !endif ;License & release notes. diff --git a/data/images/lastfm-icon.png b/data/images/lastfm-icon.png new file mode 100644 index 000000000..ff605dae4 Binary files /dev/null and b/data/images/lastfm-icon.png differ diff --git a/resources.qrc b/resources.qrc index bd779ef74..ad8118fc5 100644 --- a/resources.qrc +++ b/resources.qrc @@ -134,6 +134,7 @@ data/images/no-album-no-case.png data/images/rdio.png data/images/grooveshark.png + data/images/lastfm-icon.png data/sql/dbmigrate-27_to_28.sql data/images/process-stop.png diff --git a/src/AccountDelegate.cpp b/src/AccountDelegate.cpp new file mode 100644 index 000000000..4af815b76 --- /dev/null +++ b/src/AccountDelegate.cpp @@ -0,0 +1,650 @@ +/* + Copyright (C) 2011 Leo Franchi + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "AccountDelegate.h" + +#include +#include +#include + +#include "accounts/AccountModel.h" +#include "accounts/Account.h" + +#include "utils/tomahawkutils.h" +#include "utils/logger.h" + +#define CHILD_ACCOUNT_HEIGHT 24 + +#define PADDING 4 +#define PADDING_BETWEEN_STARS 2 +#define STAR_SIZE 12 + +#ifdef Q_WS_MAC +#define TOPLEVEL_ACCOUNT_HEIGHT 72 +#else +#define TOPLEVEL_ACCOUNT_HEIGHT 68 +#endif + +#define ICONSIZE 40 +#define WRENCH_SIZE 24 +#define SMALL_WRENCH_SIZE 16 +#define STATUS_ICON_SIZE 13 +#define CHECK_LEFT_EDGE 8 +#define REMOVE_ICON_SIZE 12 + +using namespace Tomahawk; +using namespace Accounts; + +AccountDelegate::AccountDelegate( QObject* parent ) + : QStyledItemDelegate ( parent ) +{ + + m_defaultCover.load( RESPATH "images/sipplugin-online.png" ); + m_ratingStarPositive.load( RESPATH "images/starred.png" ); + m_ratingStarNegative.load( RESPATH "images/star-unstarred.png" ); + m_onHoverStar.load( RESPATH "images/star-hover.png" ); + m_onlineIcon.load( RESPATH "images/sipplugin-online.png" ); + m_offlineIcon.load( RESPATH "images/sipplugin-offline.png" ); + m_removeIcon.load( RESPATH "images/list-remove.png" ); + + m_ratingStarPositive = m_ratingStarPositive.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + m_ratingStarNegative = m_ratingStarNegative.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + m_onlineIcon = m_onlineIcon.scaled( STATUS_ICON_SIZE, STATUS_ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + m_offlineIcon = m_offlineIcon.scaled( STATUS_ICON_SIZE, STATUS_ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + m_onHoverStar = m_onHoverStar.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + m_removeIcon = m_removeIcon.scaled( REMOVE_ICON_SIZE, REMOVE_ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + + m_defaultCover = m_defaultCover.scaled( ICONSIZE, ICONSIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + +} + + +QSize +AccountDelegate::sizeHint( const QStyleOptionViewItem&, const QModelIndex& index ) const +{ + AccountModel::RowType rowType = static_cast< AccountModel::RowType >( index.data( AccountModel::RowTypeRole ).toInt() ); + if ( rowType == AccountModel::TopLevelAccount || rowType == AccountModel::UniqueFactory || rowType == AccountModel::CustomAccount ) + return QSize( 200, TOPLEVEL_ACCOUNT_HEIGHT ); + else if ( rowType == AccountModel::TopLevelFactory ) + { + // Make more space for each account we have to show. + AccountFactory* fac = qobject_cast< AccountFactory* >( index.data( AccountModel::AccountData ).value< QObject* >() ); + if ( fac->isUnique() ) + return QSize( 200, TOPLEVEL_ACCOUNT_HEIGHT ); + + const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >(); + const QSize s = QSize( 200, TOPLEVEL_ACCOUNT_HEIGHT + 12 * accts.size()-1 ); + + if ( s != m_sizeHints[ index ] ) + const_cast< AccountDelegate* >( this )->sizeHintChanged( index ); // FU KTHBBQ + + m_sizeHints[ index ] = s; + return s; + } + + return QSize(); +} + + +void +AccountDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, index ); + + // draw the background + const QWidget* w = opt.widget; + QStyle* style = w ? w->style() : QApplication::style(); + style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, w ); + + painter->setRenderHint( QPainter::Antialiasing ); + + QFont titleFont = opt.font; + titleFont.setBold( true ); + titleFont.setPointSize( titleFont.pointSize() + 2 ); + const QFontMetrics titleMetrics( titleFont ); + + QFont authorFont = opt.font; + authorFont.setItalic( true ); + authorFont.setPointSize( authorFont.pointSize() - 1 ); + #ifdef Q_OS_MAC + authorFont.setPointSize( authorFont.pointSize() - 1 ); + #endif + const QFontMetrics authorMetrics( authorFont ); + + QFont descFont = authorFont; + descFont.setItalic( false ); + const QFontMetrics descMetrics( descFont ); + + QFont installFont = opt.font; + installFont.setPointSize( installFont.pointSize() - 1 ); + const QFontMetrics installMetrics( descFont ); + + const int height = opt.rect.height(); + const int center = height / 2 + opt.rect.top(); + + // Left account enable/disable checkbox + const AccountModel::RowType rowType = static_cast< AccountModel::RowType >( index.data( AccountModel::RowTypeRole ).toInt() ); + int leftEdge = PADDING; + // draw checkbox first + const int checkboxYPos = ( center ) - ( WRENCH_SIZE / 2 ); + QRect checkRect = QRect( leftEdge, checkboxYPos, WRENCH_SIZE, WRENCH_SIZE ); + QStyleOptionViewItemV4 opt2 = opt; + opt2.rect = checkRect; + drawCheckBox( opt2, painter, opt.widget ); + leftEdge += WRENCH_SIZE + PADDING / 2; + + // Pixmap + QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >(); + QRect pixmapRect( leftEdge + PADDING, center - ICONSIZE/2, ICONSIZE, ICONSIZE ); + if ( p.isNull() ) // default image... TODO + p = m_defaultCover; + else + p = p.scaled( pixmapRect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation ); + + painter->drawPixmap( pixmapRect, p ); + + // Draw config wrench if there is one + const bool hasConfigWrench = index.data( AccountModel::HasConfig ).toBool(); + int rightEdge = opt.rect.right(); + m_cachedConfigRects[ index ] = QRect(); + if ( hasConfigWrench ) + { + const QRect confRect = QRect( rightEdge - 2*PADDING - WRENCH_SIZE, center - WRENCH_SIZE / 2, WRENCH_SIZE, WRENCH_SIZE ); + QStyleOptionToolButton topt; + topt.rect = confRect; + topt.pos = confRect.topLeft(); + + drawConfigWrench( painter, opt, topt ); + m_cachedConfigRects[ index ] = confRect; + rightEdge = confRect.left(); + + } + + // Draw individual accounts and add account button for factories + m_cachedButtonRects[ index ] = QRect(); + + bool canDelete = index.data( AccountModel::CanDeleteRole ) .toBool(); + if ( rowType == Tomahawk::Accounts::AccountModel::TopLevelFactory ) + { + const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >(); + + QRect btnRect; + const QString btnText = tr( "Add Account" ); + const int btnWidth = installMetrics.width( btnText ) + 2*PADDING; + + if ( accts.isEmpty() ) + { + Q_ASSERT( !hasConfigWrench ); + + // Draw button in center of row + btnRect= QRect( opt.rect.right() - PADDING - btnWidth, center - ( installMetrics.height() + 4 ) / 2, btnWidth, installMetrics.height() + 2*PADDING ); + rightEdge = btnRect.left(); + } + else + { + painter->save(); + painter->setFont( installFont ); + int oldRightEdge = rightEdge; + rightEdge = drawAccountList( painter, opt, accts, rightEdge ); + painter->restore(); + + btnRect = QRect( opt.rect.right() - PADDING - btnWidth, opt.rect.bottom() - installMetrics.height() - 3*PADDING, btnWidth, installMetrics.height() + 2*PADDING ); +#ifdef Q_WS_MAC + btnRect.adjust( -4, 0, 4, 0 ); +#endif + } + + leftEdge = btnRect.left(); + m_cachedButtonRects[ index ] = btnRect; + + painter->save(); + painter->setPen( opt.palette.color( QPalette::Active, QPalette::AlternateBase ) ); + + drawRoundedButton( painter, btnRect ); + + painter->setFont( installFont ); + painter->drawText( btnRect, Qt::AlignCenter, btnText ); + painter->restore(); + } + else if ( rowType == AccountModel::UniqueFactory ) + { + // Display as usual, except if it has an account, show the status. + const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >(); + if ( !accts.isEmpty() ) + { + Q_ASSERT( accts.size() == 1 ); + + rightEdge = drawStatus( painter, QPointF( rightEdge, center - painter->fontMetrics().height()/2 ), accts.first(), true ); + } + + } + else if ( canDelete ) + { + const QString btnText = tr( "Remove Account" ); + const int btnWidth = installMetrics.width( btnText ) + 2*PADDING; + QRect btnRect; + + if ( hasConfigWrench ) + btnRect = QRect( opt.rect.right() - PADDING - btnWidth, opt.rect.bottom() - installMetrics.height() - 3*PADDING, btnWidth, installMetrics.height() + 2*PADDING ); + else + btnRect = QRect( opt.rect.right() - PADDING - btnWidth, center - ( installMetrics.height() + 4 ) / 2, btnWidth, installMetrics.height() + 2*PADDING ); + +#ifdef Q_WS_MAC + btnRect.adjust( -4, 2, 4, -2 ); +#endif + leftEdge = btnRect.left(); + m_cachedButtonRects[ index ] = btnRect; + + painter->save(); + painter->setPen( opt.palette.color( QPalette::Active, QPalette::AlternateBase ) ); + + drawRoundedButton( painter, btnRect, true ); + + painter->setFont( installFont ); + painter->drawText( btnRect, Qt::AlignCenter, btnText ); + painter->restore(); + } + + // Draw the title and description + // title + QString title = index.data( Qt::DisplayRole ).toString(); + const int rightTitleEdge = rightEdge - PADDING; + const int leftTitleEdge = pixmapRect.right() + PADDING; + painter->setFont( titleFont ); + QRect textRect; + const bool canRate = index.data( AccountModel::CanRateRole ).toBool(); + if ( canRate ) + { + textRect = QRect( leftTitleEdge, opt.rect.top() + PADDING, rightTitleEdge - leftTitleEdge, painter->fontMetrics().height() ); + } + else + { + textRect = QRect( leftTitleEdge, opt.rect.top() + PADDING, rightTitleEdge - leftTitleEdge, center - opt.rect.top() - PADDING ); + } + painter->drawText( textRect, Qt::AlignVCenter | Qt::AlignLeft, title ); + + // author + QString author = index.data( AccountModel::AuthorRole ).toString(); + int runningBottom = textRect.bottom(); + if ( !author.isEmpty() && canRate ) + { + painter->save(); + painter->setFont( authorFont ); + painter->setPen( QColor( Qt::gray ).darker( 150 ) ); + const int authorWidth = authorMetrics.width( author ); + const QRect authorRect( textRect.left(), textRect.bottom() + PADDING/2, authorWidth + 6, authorMetrics.height() ); + painter->drawText( authorRect, Qt::AlignLeft | Qt::AlignVCenter, author ); + painter->restore(); + + runningBottom = authorRect.bottom(); + } + + // description + QString desc = index.data( AccountModel::DescriptionRole ).toString(); + const int descWidth = rightEdge - leftTitleEdge - PADDING; + painter->setFont( descFont ); + const QRect descRect( leftTitleEdge, runningBottom + PADDING, descWidth, painter->fontMetrics().height() ); + desc = painter->fontMetrics().elidedText( desc, Qt::ElideRight, descWidth ); + painter->drawText( descRect, Qt::AlignLeft | Qt::TextWordWrap | Qt::AlignTop, desc ); + runningBottom = descRect.bottom(); + + if ( index.data( AccountModel::CanRateRole ).toBool() ) + { + // rating stars + const int rating = index.data( AccountModel::RatingRole ).toInt(); + +// int runningEdge = opt.rect.right() - 2*PADDING - ratingWidth; + int runningEdge = textRect.left(); +// int starsTop = opt.rect.bottom() - 3*PADDING - m_ratingStarNegative.height(); + int starsTop = runningBottom + PADDING; + for ( int i = 1; i < 6; i++ ) + { + QRect r( runningEdge, starsTop, m_ratingStarPositive.width(), m_ratingStarPositive.height() ); +// QRect r( runningEdge, opt.rect.top() + PADDING, m_ratingStarPositive.width(), m_ratingStarPositive.height() ); + if ( i == 1 ) + m_cachedStarRects[ index ] = r; + + const bool userHasRated = index.data( AccountModel::UserHasRatedRole ).toBool(); + if ( !userHasRated && // Show on-hover animation if the user hasn't rated it yet, and is hovering over it + m_hoveringOver > -1 && + m_hoveringItem == index ) + { + if ( i <= m_hoveringOver ) // positive star + painter->drawPixmap( r, m_onHoverStar ); + else + painter->drawPixmap( r, m_ratingStarNegative ); + } + else + { + if ( i <= rating ) // positive or rated star + { + if ( userHasRated ) + painter->drawPixmap( r, m_onHoverStar ); + else + painter->drawPixmap( r, m_ratingStarPositive ); + } + else + painter->drawPixmap( r, m_ratingStarNegative ); + } + runningEdge += m_ratingStarPositive.width() + PADDING_BETWEEN_STARS; + } + + // downloaded num times + QString count = tr( "%1 downloads" ).arg( index.data( AccountModel::DownloadCounterRole ).toInt() ); + painter->setFont( descFont ); + const int countW = painter->fontMetrics().width( count ); + const QRect countRect( runningEdge + 50, starsTop, countW, painter->fontMetrics().height() ); + count = painter->fontMetrics().elidedText( count, Qt::ElideRight, rightEdge - PADDING - countRect.left() ); + painter->drawText( countRect, Qt::AlignLeft | Qt::TextWordWrap, count ); + // runningEdge = authorRect.x(); + } + + // Title and description! + return; +} + + +int +AccountDelegate::drawAccountList( QPainter* painter, QStyleOptionViewItemV4& opt, const QList< Account* > accts, int rightEdge ) const +{ + // list each account name, and show the online, offline icon + const int textHeight = painter->fontMetrics().height() + 1; + const int mid = opt.rect.bottom() - opt.rect.height() / 2; + int runningRightEdge = rightEdge; + int current = 0; + + int leftOfAccounts = rightEdge; + + if ( accts.size() % 2 == 1 ) + { + // If there's an odd number, the center one is centered + current = mid - ((textHeight + PADDING/2) * (accts.size()/2) ) - textHeight / 2; + } + else + { + // Even number, center between the middle ones + current = mid - ((textHeight + PADDING/2) * (accts.size()/2) ); + } + + for ( int i = 0; i < accts.size(); i++ ) + { + // draw lightbulb and text + runningRightEdge = drawStatus( painter, QPointF( rightEdge - PADDING, current), accts.at( i ) ); + + const QString label = accts.at( i )->accountFriendlyName(); + const QPoint textTopLeft( runningRightEdge - PADDING - painter->fontMetrics().width( label ), current); + painter->drawText( QRect( textTopLeft, QSize( painter->fontMetrics().width( label ) + 1, textHeight ) ), label ); + + current += textHeight + PADDING/2; + + leftOfAccounts = qMin( leftOfAccounts, textTopLeft.x() ); + } + + return leftOfAccounts; +} + + +bool +AccountDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) +{ + if ( event->type() != QEvent::MouseButtonPress && + event->type() != QEvent::MouseButtonRelease && + event->type() != QEvent::MouseButtonDblClick && + event->type() != QEvent::MouseMove ) + return false; + + if ( event->type() == QEvent::MouseButtonPress ) + { + // Show the config wrench as depressed on click + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + if ( me->button() == Qt::LeftButton && m_cachedConfigRects.contains( index ) && m_cachedConfigRects[ index ].contains( me->pos() ) ) + { + m_configPressed = index; + + const AccountModel::RowType rowType = static_cast< AccountModel::RowType >( index.data( AccountModel::RowTypeRole ).toInt() ); + if ( rowType == AccountModel::TopLevelAccount || + rowType == AccountModel::CustomAccount ) + { + Account* acct = qobject_cast< Account* >( index.data( AccountModel::AccountData ).value< QObject* >() ); + Q_ASSERT( acct ); // Should not be showing a config wrench if there is no account! + + emit openConfig( acct ); + } + else if ( rowType == AccountModel::TopLevelFactory ) + { + AccountFactory* fac = qobject_cast< AccountFactory* >( index.data( AccountModel::AccountData ).value< QObject* >() ); + Q_ASSERT( fac ); // Should not be showing a config wrench if there is no account! + emit openConfig( fac ); + } + else if ( rowType == AccountModel::UniqueFactory ) + { + const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >(); + + Q_ASSERT( !accts.isEmpty() ); // If there's no account, why is there a config widget for this factory? + Q_ASSERT( accts.size() == 1 ); + emit openConfig( accts.first() ); + } + return true; + } + } else if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick ) + { + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + if ( m_configPressed.isValid() ) + emit update( m_configPressed ); + + m_configPressed = QModelIndex(); + + if ( checkRectForIndex( option, index ).contains( me->pos() ) ) + { + // Check box for this row + + // eat the double click events inside the check rect + if( event->type() == QEvent::MouseButtonDblClick ) { + return true; + } + + Qt::CheckState curState = static_cast< Qt::CheckState >( index.data( Qt::CheckStateRole ).toInt() ); + Qt::CheckState newState = curState == Qt::Checked ? Qt::Unchecked : Qt::Checked; + return model->setData( index, newState, AccountModel::CheckboxClickedRole ); + } + else if ( m_cachedButtonRects.contains( index ) && m_cachedButtonRects[ index ].contains( me->pos() ) ) + { + // Install/create/etc button for this row + model->setData( index, true, AccountModel::CustomButtonRole ); + } + } + + if ( m_cachedStarRects.contains( index ) ) + { + QRect fullStars = m_cachedStarRects[ index ]; + const int starsWidth = 5 * ( m_ratingStarPositive.width() + PADDING_BETWEEN_STARS ); + fullStars.setWidth( starsWidth ); + + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + + if ( fullStars.contains( me->pos() ) ) + { + const int eachStar = starsWidth / 5; + const int clickOffset = me->pos().x() - fullStars.x(); + const int whichStar = (clickOffset / eachStar) + 1; + + if ( event->type() == QEvent::MouseButtonRelease ) + { + model->setData( index, whichStar, AccountModel::RatingRole ); + } + else if ( event->type() == QEvent::MouseMove ) + { + // 0-indexed + m_hoveringOver = whichStar; + m_hoveringItem = index; + } + + return true; + } + } + + if ( m_hoveringOver > -1 ) + { + emit update( m_hoveringItem ); + m_hoveringOver = -1; + m_hoveringItem = QPersistentModelIndex(); + } + return false; +} + + +void +AccountDelegate::drawRoundedButton( QPainter* painter, const QRect& btnRect, bool red ) const +{ + QPainterPath btnPath; + const int radius = 3; + // draw top half gradient + const int btnCenter = btnRect.bottom() - ( btnRect.height() / 2 ); + btnPath.moveTo( btnRect.left(), btnCenter ); + btnPath.lineTo( btnRect.left(), btnRect.top() + radius ); + btnPath.quadTo( QPoint( btnRect.topLeft() ), QPoint( btnRect.left() + radius, btnRect.top() ) ); + btnPath.lineTo( btnRect.right() - radius, btnRect.top() ); + btnPath.quadTo( QPoint( btnRect.topRight() ), QPoint( btnRect.right(), btnRect.top() + radius ) ); + btnPath.lineTo( btnRect.right(),btnCenter ); + btnPath.lineTo( btnRect.left(), btnCenter ); + + QLinearGradient g; + if ( !red ) + { + g.setColorAt( 0, QColor(54, 127, 211) ); + g.setColorAt( 0.5, QColor(43, 104, 182) ); + } + else + { + g.setColorAt( 0, QColor(206, 63, 63) ); + g.setColorAt( 0.5, QColor(170, 52, 52) ); + } + //painter->setPen( bg.darker() ); + painter->fillPath( btnPath, g ); + //painter->drawPath( btnPath ); + + btnPath = QPainterPath(); + btnPath.moveTo( btnRect.left(), btnCenter ); + btnPath.lineTo( btnRect.left(), btnRect.bottom() - radius ); + btnPath.quadTo( QPoint( btnRect.bottomLeft() ), QPoint( btnRect.left() + radius, btnRect.bottom() ) ); + btnPath.lineTo( btnRect.right() - radius, btnRect.bottom() ); + btnPath.quadTo( QPoint( btnRect.bottomRight() ), QPoint( btnRect.right(), btnRect.bottom() - radius ) ); + btnPath.lineTo( btnRect.right(), btnCenter ); + btnPath.lineTo( btnRect.left(), btnCenter ); + + if ( !red ) + { + g.setColorAt( 0, QColor(34, 85, 159) ); + g.setColorAt( 0.5, QColor(35, 79, 147) ); + } + else + { + g.setColorAt( 0, QColor(150, 50, 50) ); + g.setColorAt( 0.5, QColor(130, 40, 40) ); + } + painter->fillPath( btnPath, g ); +} + + +int +AccountDelegate::drawStatus( QPainter* painter, const QPointF& rightTopEdge, Account* acct, bool drawText ) const +{ + QPixmap p; + QString statusText; + Account::ConnectionState state = acct->connectionState(); + if ( state == Account::Connected ) + { + p = m_onlineIcon; + statusText = tr( "Online" ); + } + else if ( state == Account::Connecting ) + { + p = m_offlineIcon; + statusText = tr( "Connecting..." ); + } + else + { + p = m_offlineIcon; + statusText = tr( "Offline" ); + } + + const int yPos = rightTopEdge.y(); + const QRect connectIconRect( rightTopEdge.x() - STATUS_ICON_SIZE, yPos, STATUS_ICON_SIZE, STATUS_ICON_SIZE ); + painter->drawPixmap( connectIconRect, p ); + + int leftEdge = connectIconRect.x(); + if ( drawText ) + { + int width = painter->fontMetrics().width( statusText ); + int statusTextX = connectIconRect.x() - PADDING - width; + painter->drawText( QRect( statusTextX, yPos, width, painter->fontMetrics().height() ), statusText ); + + leftEdge = statusTextX; + } + + return leftEdge; +} + + +void +AccountDelegate::drawCheckBox( QStyleOptionViewItemV4& opt, QPainter* p, const QWidget* w ) const +{ + QStyle* style = w ? w->style() : QApplication::style(); + opt.checkState == Qt::Checked ? opt.state |= QStyle::State_On : opt.state |= QStyle::State_Off; + style->drawPrimitive( QStyle::PE_IndicatorViewItemCheck, &opt, p, w ); +} + + +void +AccountDelegate::drawConfigWrench ( QPainter* painter, QStyleOptionViewItemV4& opt, QStyleOptionToolButton& topt ) const +{ + const QWidget* w = opt.widget; + QStyle* style = w ? w->style() : QApplication::style(); + + // draw it the same size as the check belox + topt.font = opt.font; + topt.icon = QIcon( RESPATH "images/configure.png" ); + topt.iconSize = QSize( 14, 14 ); + topt.subControls = QStyle::SC_ToolButton; + topt.activeSubControls = QStyle::SC_None; + topt.features = QStyleOptionToolButton::None; + bool pressed = ( m_configPressed == opt.index ); + topt.state = pressed ? QStyle::State_On : QStyle::State_Raised; + if( opt.state & QStyle::State_MouseOver || pressed ) + topt.state |= QStyle::State_HasFocus; + style->drawComplexControl( QStyle::CC_ToolButton, &topt, painter, w ); +} + + + +QRect +AccountDelegate::checkRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, idx ); + + // Top level item, return the corresponding rect + const int ypos = ( opt.rect.top() + opt.rect.height() / 2 ) - ( WRENCH_SIZE / 2 ); + const QRect checkRect = QRect( PADDING, ypos, WRENCH_SIZE, WRENCH_SIZE ); + + return checkRect; + +} + + diff --git a/src/AccountDelegate.h b/src/AccountDelegate.h new file mode 100644 index 000000000..6787489b4 --- /dev/null +++ b/src/AccountDelegate.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2011 Leo Franchi + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifndef ACCOUNTDELEGATE_H +#define ACCOUNTDELEGATE_H + +#include +#include "accounts/AccountModel.h" + +namespace Tomahawk +{ +namespace Accounts +{ + +class Account; + +class AccountDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + AccountDelegate( QObject* parent = 0); + + virtual void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + virtual QSize sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + +protected: + virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); + +signals: + void update( const QModelIndex& idx ); + void openConfig( Tomahawk::Accounts::Account* ); + void openConfig( Tomahawk::Accounts::AccountFactory* ); + +private: + void drawRoundedButton( QPainter* painter, const QRect& buttonRect, bool red = false ) const; + // Returns new left edge + int drawStatus( QPainter* painter, const QPointF& rightTopEdge, Account* acct, bool drawText = false ) const; + void drawCheckBox( QStyleOptionViewItemV4& opt, QPainter* p, const QWidget* w ) const; + void drawConfigWrench( QPainter* painter, QStyleOptionViewItemV4& option, QStyleOptionToolButton& topt ) const; + // returns new left edge + int drawAccountList( QPainter* painter, QStyleOptionViewItemV4& option, const QList< Account* > accounts, int rightEdge ) const; + + QRect checkRectForIndex( const QStyleOptionViewItem &option, const QModelIndex &idx ) const; + + QPixmap m_offlineIcon, m_onlineIcon, m_defaultCover, m_onHoverStar, m_ratingStarPositive, m_ratingStarNegative, m_removeIcon; + int m_hoveringOver; + QPersistentModelIndex m_hoveringItem, m_configPressed; + mutable QHash< QPersistentModelIndex, QRect > m_cachedButtonRects; + mutable QHash< QPersistentModelIndex, QRect > m_cachedStarRects; + mutable QHash< QPersistentModelIndex, QRect > m_cachedConfigRects; + mutable QHash< QPersistentModelIndex, QSize > m_sizeHints; +}; + +} +} + +#endif // ACCOUNTDELEGATE_H diff --git a/src/AccountFactoryWrapper.cpp b/src/AccountFactoryWrapper.cpp new file mode 100644 index 000000000..f5f270b06 --- /dev/null +++ b/src/AccountFactoryWrapper.cpp @@ -0,0 +1,149 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "AccountFactoryWrapper.h" + +#include "accounts/Account.h" +#include +#include "AccountFactoryWrapperDelegate.h" +#include "delegateconfigwrapper.h" +#include "ui_AccountFactoryWrapper.h" + +using namespace Tomahawk::Accounts; +AccountFactoryWrapper::AccountFactoryWrapper( AccountFactory* factory, QWidget* parent ) + : QDialog( parent, Qt::Sheet ) + , m_factory( factory ) + , m_ui( new Ui_AccountFactoryWrapper ) + , m_createAccount( false ) +{ + m_ui->setupUi( this ); + + setWindowTitle( factory->prettyName() ); + + m_ui->factoryIcon->setPixmap( factory->icon() ); + m_ui->factoryDescription->setText( factory->description() ); + + m_addButton = m_ui->buttonBox->addButton( tr( "Add Account" ), QDialogButtonBox::ActionRole ); + + AccountFactoryWrapperDelegate* del = new AccountFactoryWrapperDelegate( m_ui->accountsList ); + m_ui->accountsList->setItemDelegate( del ); + + connect( del, SIGNAL( openConfig( Tomahawk::Accounts::Account* ) ), this, SLOT( openAccountConfig( Tomahawk::Accounts::Account* ) ) ); + connect( del, SIGNAL( removeAccount( Tomahawk::Accounts::Account* ) ), this, SLOT( removeAccount( Tomahawk::Accounts::Account* ) ) ); + + load(); + + connect( m_ui->buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) ); + connect( m_ui->buttonBox, SIGNAL( accepted() ), this, SLOT( accept() ) ); + connect( m_ui->buttonBox, SIGNAL( clicked( QAbstractButton*) ), this, SLOT( buttonClicked( QAbstractButton* ) ) ); + + + connect ( AccountManager::instance(), SIGNAL( added( Tomahawk::Accounts::Account* ) ), this, SLOT( load() ) ); + connect ( AccountManager::instance(), SIGNAL( removed( Tomahawk::Accounts::Account* ) ), this, SLOT( load() ) ); + +#ifdef Q_OS_MAC + setContentsMargins( 0, 0, 0, 0 ); + m_ui->verticalLayout->setSpacing( 6 ); +#endif +} + +void +AccountFactoryWrapper::load() +{ + m_ui->accountsList->clear(); + foreach ( Account* acc, AccountManager::instance()->accounts() ) + { + if ( AccountManager::instance()->factoryForAccount( acc ) == m_factory ) + { + QTreeWidgetItem* item = new QTreeWidgetItem( m_ui->accountsList ); + item->setData( 0, AccountRole, QVariant::fromValue< QObject *>( acc ) ); + } + } + + if ( m_ui->accountsList->model()->rowCount() == 0 ) + accept(); + +#ifndef Q_OS_MAC + const int padding = 7; +#else + const int padding = 8; +#endif + const int height = m_ui->accountsList->model()->rowCount( QModelIndex() ) * ACCOUNT_ROW_HEIGHT + padding; + + m_ui->accountsList->setFixedHeight( height ); +} + + +void +AccountFactoryWrapper::openAccountConfig( Account* account ) +{ + if( account->configurationWidget() ) + { +#ifndef Q_WS_MAC + DelegateConfigWrapper dialog( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), this ); + QWeakPointer< DelegateConfigWrapper > watcher( &dialog ); + int ret = dialog.exec(); + if( !watcher.isNull() && ret == QDialog::Accepted ) + { + // send changed config to resolver + account->saveConfig(); + } +#else + // on osx a sheet needs to be non-modal + DelegateConfigWrapper* dialog = new DelegateConfigWrapper( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), this, Qt::Sheet ); + dialog->setProperty( "accountplugin", QVariant::fromValue< QObject* >( account ) ); + connect( dialog, SIGNAL( finished( int ) ), this, SLOT( accountConfigClosed( int ) ) ); + + dialog->show(); +#endif + } +} + + +void +AccountFactoryWrapper::accountConfigClosed( int value ) +{ + if( value == QDialog::Accepted ) + { + DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() ); + Account* account = qobject_cast< Account* >( dialog->property( "accountplugin" ).value< QObject* >() ); + account->saveConfig(); + } +} + +void +AccountFactoryWrapper::removeAccount( Tomahawk::Accounts::Account* acct ) +{ + AccountManager::instance()->removeAccount( acct ); + + load(); +} + +void +AccountFactoryWrapper::buttonClicked( QAbstractButton* button ) +{ + if ( button == m_addButton ) + { + m_createAccount = true; + emit createAccount( m_factory ); + return; + } + else + reject(); +} + diff --git a/src/AccountFactoryWrapper.h b/src/AccountFactoryWrapper.h new file mode 100644 index 000000000..02c8ec335 --- /dev/null +++ b/src/AccountFactoryWrapper.h @@ -0,0 +1,66 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef ACCOUNTFACTORYWRAPPER_H +#define ACCOUNTFACTORYWRAPPER_H + +#include + +class QAbstractButton; +namespace Tomahawk { +namespace Accounts { + class AccountFactory; +class Account; +} +} + +class Ui_AccountFactoryWrapper; + +// class AccountFactoryWrapper_ +class AccountFactoryWrapper : public QDialog +{ + Q_OBJECT +public: + enum ExtraRoles { + AccountRole = Qt::UserRole + 140 + }; + + explicit AccountFactoryWrapper( Tomahawk::Accounts::AccountFactory* factory, QWidget* parent = 0 ); + + bool doCreateAccount() const { return m_createAccount; } + +signals: + void createAccount( Tomahawk::Accounts::AccountFactory* factory ); + +public slots: + void openAccountConfig( Tomahawk::Accounts::Account* ); + void accountConfigClosed( int value ); + void removeAccount( Tomahawk::Accounts::Account* ); + +private slots: + void buttonClicked( QAbstractButton* ); + void load(); + +private: + Tomahawk::Accounts::AccountFactory* m_factory; + Ui_AccountFactoryWrapper* m_ui; + QPushButton* m_addButton; + bool m_createAccount; +}; + +#endif // ACCOUNTFACTORYWRAPPER_H diff --git a/src/AccountFactoryWrapper.ui b/src/AccountFactoryWrapper.ui new file mode 100644 index 000000000..b7ba54df3 --- /dev/null +++ b/src/AccountFactoryWrapper.ui @@ -0,0 +1,110 @@ + + + AccountFactoryWrapper + + + + 0 + 0 + 507 + 150 + + + + Dialog + + + + + + + + + Qt::AlignCenter + + + + + + + Description goes here + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + false + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerItem + + + false + + + true + + + false + + + + 1 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + diff --git a/src/AccountFactoryWrapperDelegate.cpp b/src/AccountFactoryWrapperDelegate.cpp new file mode 100644 index 000000000..fcd89701b --- /dev/null +++ b/src/AccountFactoryWrapperDelegate.cpp @@ -0,0 +1,166 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "AccountFactoryWrapperDelegate.h" +#include "accounts/Account.h" +#include "AccountFactoryWrapper.h" +#include "utils/tomahawkutils.h" + +#include +#include +#include + +using namespace Tomahawk::Accounts; + +#define ICON_SIZE 15 +#define CONFIG_WRENCH_SIZE 20 +#define PADDING 4 + +AccountFactoryWrapperDelegate::AccountFactoryWrapperDelegate( QObject* parent ) + : QStyledItemDelegate( parent ) +{ + m_removePixmap.load( RESPATH "images/list-remove.png" ); + m_onlineIcon.load( RESPATH "images/sipplugin-online.png" ); + m_offlineIcon.load( RESPATH "images/sipplugin-offline.png" ); + + m_removePixmap = m_removePixmap.scaled( ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + m_onlineIcon = m_onlineIcon.scaled( ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + m_offlineIcon = m_offlineIcon.scaled( ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + + m_configIcon.addFile( RESPATH "images/configure.png", QSize( CONFIG_WRENCH_SIZE - 8, CONFIG_WRENCH_SIZE - 8 ) ); +} + +void +AccountFactoryWrapperDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, index ); + + const int center = opt.rect.height() / 2 + opt.rect.top(); + const int topIcon = center - ICON_SIZE/2; + + // draw the background + const QWidget* w = opt.widget; + QStyle* style = w ? w->style() : QApplication::style(); + style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, w ); + + Account* acc = qobject_cast< Account* >( index.data( AccountFactoryWrapper::AccountRole ).value< QObject* >() ); + Q_ASSERT( acc ); + + // name on left + painter->drawText( opt.rect.adjusted( PADDING, PADDING, -PADDING, -PADDING ), Qt::AlignLeft | Qt::AlignVCenter, acc->accountFriendlyName() ); + + // remove, config, status on right + const QRect pmRect( opt.rect.right() - PADDING - m_removePixmap.width(), topIcon, ICON_SIZE, ICON_SIZE ); + painter->drawPixmap( pmRect, m_removePixmap ); + m_cachedButtonRects[ index ] = pmRect; + + const QRect confRect( pmRect.left() - PADDING - CONFIG_WRENCH_SIZE, center - CONFIG_WRENCH_SIZE/2, CONFIG_WRENCH_SIZE, CONFIG_WRENCH_SIZE ); + + QStyleOptionToolButton topt; + topt.rect = confRect; + topt.pos = confRect.topLeft(); + topt.font = opt.font; + topt.icon = m_configIcon; + topt.iconSize = QSize( CONFIG_WRENCH_SIZE - 8, CONFIG_WRENCH_SIZE - 8 ); + topt.subControls = QStyle::SC_ToolButton; + topt.activeSubControls = QStyle::SC_None; + topt.features = QStyleOptionToolButton::None; + bool pressed = ( m_configPressed == opt.index ); + topt.state = pressed ? QStyle::State_On : QStyle::State_Raised; + if( opt.state & QStyle::State_MouseOver || pressed ) + topt.state |= QStyle::State_HasFocus; + style->drawComplexControl( QStyle::CC_ToolButton, &topt, painter, w ); + m_cachedConfigRects[ index ] = confRect; + + QPixmap p; + QString statusText; + Account::ConnectionState state = acc->connectionState(); + if ( state == Account::Connected ) + { + p = m_onlineIcon; + statusText = tr( "Online" ); + } + else if ( state == Account::Connecting ) + { + p = m_offlineIcon; + statusText = tr( "Connecting..." ); + } + else + { + p = m_offlineIcon; + statusText = tr( "Offline" ); + } + + const QRect connectIconRect( confRect.left() - PADDING - ICON_SIZE, topIcon, ICON_SIZE, ICON_SIZE ); + painter->drawPixmap( connectIconRect, p ); + + int width = painter->fontMetrics().width( statusText ); + painter->drawText( QRect( connectIconRect.left() - PADDING - width, center - painter->fontMetrics().height()/2, width, painter->fontMetrics().height() ), statusText ); + +} + +QSize +AccountFactoryWrapperDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const +{ + return QSize( 200, ACCOUNT_ROW_HEIGHT ); +} + + +bool +AccountFactoryWrapperDelegate::editorEvent( QEvent* event, QAbstractItemModel*, const QStyleOptionViewItem&, const QModelIndex& index ) +{ + if ( event->type() != QEvent::MouseButtonPress && + event->type() != QEvent::MouseButtonRelease && + event->type() != QEvent::MouseButtonDblClick && + event->type() != QEvent::MouseMove ) + return false; + + if ( event->type() == QEvent::MouseButtonPress ) + { + // Show the config wrench as depressed on click + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + if ( me->button() == Qt::LeftButton && m_cachedConfigRects.contains( index ) && m_cachedConfigRects[ index ].contains( me->pos() ) ) + { + m_configPressed = index; + Account* acct = qobject_cast< Account* >( index.data( AccountFactoryWrapper::AccountRole ).value< QObject* >() ); + Q_ASSERT( acct ); // Should not be showing a config wrench if there is no account! + + emit openConfig( acct ); + + return true; + } + } else if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick ) + { + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + if ( m_configPressed.isValid() ) + emit update( m_configPressed ); + + m_configPressed = QModelIndex(); + + if ( m_cachedButtonRects.contains( index ) && m_cachedButtonRects[ index ].contains( me->pos() ) ) + { + Account* acct = qobject_cast< Account* >( index.data( AccountFactoryWrapper::AccountRole ).value< QObject* >() ); + emit removeAccount( acct ); + + return true; + } + } + return false; +} + diff --git a/src/GetNewStuffDelegate.h b/src/AccountFactoryWrapperDelegate.h similarity index 52% rename from src/GetNewStuffDelegate.h rename to src/AccountFactoryWrapperDelegate.h index 570210226..699ed22f8 100644 --- a/src/GetNewStuffDelegate.h +++ b/src/AccountFactoryWrapperDelegate.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Leo Franchi + * Copyright 2010-2012, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,35 +16,43 @@ * along with Tomahawk. If not, see . */ -#ifndef GETNEWSTUFFDELEGATE_H -#define GETNEWSTUFFDELEGATE_H +#ifndef ACCOUNTFACTORYWRAPPERDELEGATE_H +#define ACCOUNTFACTORYWRAPPERDELEGATE_H #include +#define ACCOUNT_ROW_HEIGHT 20 -class -GetNewStuffDelegate : public QStyledItemDelegate +namespace Tomahawk { +namespace Accounts { +class Account; +} +} + +class AccountFactoryWrapperDelegate : public QStyledItemDelegate { Q_OBJECT public: - explicit GetNewStuffDelegate( QObject* parent = 0 ); + explicit AccountFactoryWrapperDelegate( QObject* parent = 0 ); + virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + virtual bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index); signals: - void update( const QModelIndex& idx ); + void update( const QModelIndex& ); -protected: - virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); + void openConfig( Tomahawk::Accounts::Account* ); + void removeAccount( Tomahawk::Accounts::Account* ); private: - QPixmap m_defaultCover, m_onHoverStar, m_ratingStarPositive, m_ratingStarNegative; + QPixmap m_removePixmap, m_offlineIcon, m_onlineIcon; + QIcon m_configIcon; + QModelIndex m_configPressed; - int m_widestTextWidth; - int m_hoveringOver; - QPersistentModelIndex m_hoveringItem; - mutable QHash< QPair, QRect > m_cachedButtonRects; - mutable QHash< QPair, QRect > m_cachedStarRects; + mutable QHash< QPersistentModelIndex, QRect > m_cachedButtonRects; + mutable QHash< QPersistentModelIndex, QRect > m_cachedConfigRects; }; -#endif // GETNEWSTUFFDELEGATE_H +#endif // ACCOUNTFACTORYWRAPPERDELEGATE_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee9d3fbe6..ee8ac9b0b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,13 +74,12 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui} audiocontrols.cpp settingsdialog.cpp diagnosticsdialog.cpp - configdelegatebase.cpp - sipconfigdelegate.cpp - resolverconfigdelegate.cpp + AccountDelegate.cpp settingslistdelegate.cpp - resolversmodel.cpp tomahawkwindow.cpp LoadXSPFDialog.cpp + AccountFactoryWrapper.cpp + AccountFactoryWrapperDelegate.cpp ) SET( tomahawkHeaders ${tomahawkHeaders} @@ -100,13 +99,6 @@ IF(LIBLASTFM_FOUND) ) ENDIF(LIBLASTFM_FOUND) -IF(LIBATTICA_FOUND) - SET( tomahawkSourcesGui ${tomahawkSourcesGui} GetNewStuffDialog.cpp GetNewStuffDelegate.cpp GetNewStuffModel.cpp ) - SET( tomahawkHeadersGui ${tomahawkHeadersGui} GetNewStuffDialog.h GetNewStuffDelegate.h GetNewStuffModel.h ) - INCLUDE_DIRECTORIES( ${LIBATTICA_INCLUDE_DIR} ) -ENDIF(LIBATTICA_FOUND) - - SET( tomahawkHeadersGui ${tomahawkHeadersGui} sourcetree/sourcesmodel.h sourcetree/sourcesproxymodel.h @@ -126,14 +118,13 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui} audiocontrols.h settingsdialog.h diagnosticsdialog.h - configdelegatebase.h - resolverconfigdelegate.h - sipconfigdelegate.h + AccountDelegate.h settingslistdelegate.h - resolversmodel.h delegateconfigwrapper.h tomahawkwindow.h LoadXSPFDialog.h + AccountFactoryWrapper.h + AccountFactoryWrapperDelegate.h ) SET( tomahawkUI ${tomahawkUI} @@ -144,8 +135,9 @@ SET( tomahawkUI ${tomahawkUI} audiocontrols.ui - GetNewStuffDialog.ui LoadXSPFDialog.ui + + AccountFactoryWrapper.ui ) INCLUDE_DIRECTORIES( @@ -171,6 +163,7 @@ INCLUDE_DIRECTORIES( ${TAGLIB_INCLUDES} ${PHONON_INCLUDES} ${QJSON_INCLUDE_DIR} + ${LIBATTICA_INCLUDE_DIR} ${LIBECHONEST_INCLUDE_DIR} ${LIBECHONEST_INCLUDE_DIR}/.. ) @@ -200,7 +193,8 @@ IF(GLOOX_FOUND) SET( tomahawkHeaders ${tomahawkHeaders} xmppbot/xmppbot.h ) SET( tomahawkSources ${tomahawkSources} xmppbot/xmppbot.cpp ) ENDIF(GLOOX_FOUND) -ADD_SUBDIRECTORY( sip ) + +ADD_SUBDIRECTORY( accounts ) IF(QCA2_FOUND) INCLUDE_DIRECTORIES( ${QCA2_INCLUDE_DIR} ) diff --git a/src/GetNewStuffDelegate.cpp b/src/GetNewStuffDelegate.cpp deleted file mode 100644 index 1e1c5d381..000000000 --- a/src/GetNewStuffDelegate.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Leo Franchi - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "GetNewStuffDelegate.h" - -#include "GetNewStuffModel.h" -#include "utils/tomahawkutils.h" -#include "utils/logger.h" - -#include -#include -#include -#include "AtticaManager.h" - -#define PADDING 4 -#define PADDING_BETWEEN_STARS 2 -#define STAR_SIZE 12 - -#ifdef Q_WS_MAC -#define SIZEHINT_HEIGHT 70 -#else -#define SIZEHINT_HEIGHT 60 -#endif - -GetNewStuffDelegate::GetNewStuffDelegate( QObject* parent ) - : QStyledItemDelegate ( parent ) - , m_widestTextWidth( 0 ) - , m_hoveringOver( -1 ) -{ - m_defaultCover.load( RESPATH "images/sipplugin-online.png" ); - m_ratingStarPositive.load( RESPATH "images/starred.png" ); - m_ratingStarNegative.load( RESPATH "images/star-unstarred.png" ); - m_onHoverStar.load( RESPATH "images/star-hover.png" ); - - m_ratingStarPositive = m_ratingStarPositive.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); - m_ratingStarNegative = m_ratingStarNegative.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); - m_onHoverStar = m_onHoverStar.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation ); - - const int w = SIZEHINT_HEIGHT - 2*PADDING; - m_defaultCover = m_defaultCover.scaled( w, w, Qt::KeepAspectRatio, Qt::SmoothTransformation ); - - // save the widest wifth - QFont f( QApplication::font() ); - f.setPointSize( f.pointSize() - 1 ); - QFontMetrics fm( f ); - QStringList l = QStringList() << tr( "Installed" ) << tr( "Installing" ) << tr( "Failed" ) << tr( "Uninstalling" ); - foreach ( const QString& str, l ) - { - if ( fm.width( str ) > m_widestTextWidth ) - m_widestTextWidth = fm.width( str ); - } -} - -void -GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const -{ - QStyleOptionViewItemV4 opt = option; - initStyleOption( &opt, index ); - - QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget ); - - painter->setRenderHint( QPainter::Antialiasing ); - - QFont titleFont = opt.font; - titleFont.setBold( true ); - titleFont.setPointSize( titleFont.pointSize() + 2 ); - QFontMetrics titleMetrics( titleFont ); - - QFont authorFont = opt.font; - authorFont.setItalic( true ); - authorFont.setPointSize( authorFont.pointSize() - 1 ); - QFontMetrics authorMetrics( authorFont ); - - QFont descFont = authorFont; - descFont.setItalic( false ); - QFontMetrics descMetrics( descFont ); - - QFont installFont = opt.font; - installFont.setPointSize( installFont.pointSize() - 1 ); - QFontMetrics installMetrics( descFont ); - - const int height = opt.rect.height(); - const int center = height / 2 + opt.rect.top(); - - // Pixmap - QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >(); - const int pixmapWidth = height - 2*PADDING; - QRect pixmapRect( PADDING, PADDING + opt.rect.top(), pixmapWidth, pixmapWidth ); - if ( p.isNull() ) // default image... TODO - p = m_defaultCover; - else - p = p.scaled( pixmapRect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation ); - - painter->drawPixmap( pixmapRect, p ); - - // Go from right edge now, stars, install button, and downloaded info - - // install / status button - AtticaManager::ResolverState state = static_cast< AtticaManager::ResolverState >( index.data( GetNewStuffModel::StateRole ).toInt() ); - QString actionText; - switch( state ) - { - case AtticaManager::Uninstalled: - actionText = tr( "Install" ); - break; - case AtticaManager::Installing: - actionText = tr( "Installing" ); - break; - case AtticaManager::Upgrading: - actionText = tr( "Upgrading" ); - break; - case AtticaManager::Failed: - actionText = tr( "Failed" ); - break; - case AtticaManager::Installed: - actionText = tr( "Uninstall" ); - break; - case AtticaManager::NeedsUpgrade: - actionText = tr( "Upgrade" ); - break; - } - - const int btnWidth = m_widestTextWidth + 7; - const int leftEdge = opt.rect.width() - PADDING - btnWidth - 3; - const QRect btnRect( leftEdge, center - ( installMetrics.height() + 4 ) / 2, btnWidth, installMetrics.height() + 4 ); - m_cachedButtonRects[ QPair(index.row(), index.column()) ] = btnRect; - - QPen saved = painter->pen(); - painter->setPen( opt.palette.color( QPalette::Active, QPalette::AlternateBase ) ); - - QPainterPath btnPath; - const int radius = 3; - //btnPath.addRoundedRect( btnRect, 3, 3 ); - // draw top half gradient - const int btnCenter = btnRect.bottom() - ( btnRect.height() / 2 ); - btnPath.moveTo( btnRect.left(), btnCenter ); - btnPath.lineTo( btnRect.left(), btnRect.top() + radius ); - btnPath.quadTo( QPoint( btnRect.topLeft() ), QPoint( btnRect.left() + radius, btnRect.top() ) ); - btnPath.lineTo( btnRect.right() - radius, btnRect.top() ); - btnPath.quadTo( QPoint( btnRect.topRight() ), QPoint( btnRect.right(), btnRect.top() + radius ) ); - btnPath.lineTo( btnRect.right(),btnCenter ); - btnPath.lineTo( btnRect.left(), btnCenter ); - - QLinearGradient g; - g.setColorAt( 0, QColor(54, 127, 211) ); - g.setColorAt( 0.5, QColor(43, 104, 182) ); - //painter->setPen( bg.darker() ); - painter->fillPath( btnPath, g ); - //painter->drawPath( btnPath ); - - btnPath = QPainterPath(); - btnPath.moveTo( btnRect.left(), btnCenter ); - btnPath.lineTo( btnRect.left(), btnRect.bottom() - radius ); - btnPath.quadTo( QPoint( btnRect.bottomLeft() ), QPoint( btnRect.left() + radius, btnRect.bottom() ) ); - btnPath.lineTo( btnRect.right() - radius, btnRect.bottom() ); - btnPath.quadTo( QPoint( btnRect.bottomRight() ), QPoint( btnRect.right(), btnRect.bottom() - radius ) ); - btnPath.lineTo( btnRect.right(), btnCenter ); - btnPath.lineTo( btnRect.left(), btnCenter ); - - g.setColorAt( 0, QColor(34, 85, 159) ); - g.setColorAt( 0.5, QColor(35, 79, 147) ); - painter->fillPath( btnPath, g ); - - painter->setFont( installFont ); - painter->drawText( btnRect, Qt::AlignCenter, actionText ); - - painter->setPen( saved ); - - // rating stars - int rating = index.data( GetNewStuffModel::RatingRole ).toInt(); - const int ratingWidth = 5 * ( m_ratingStarPositive.width() + PADDING_BETWEEN_STARS ); - int runningEdge = ( btnRect.right() - btnRect.width() / 2 ) - ratingWidth / 2; - for ( int i = 1; i < 6; i++ ) - { - QRect r( runningEdge, btnRect.top() - m_ratingStarPositive.height() - PADDING, m_ratingStarPositive.width(), m_ratingStarPositive.height() ); - if ( i == 1 ) - m_cachedStarRects[ QPair(index.row(), index.column()) ] = r; - - const bool userHasRated = index.data( GetNewStuffModel::UserHasRatedRole ).toBool(); - if ( !userHasRated && // Show on-hover animation if the user hasn't rated it yet, and is hovering over it - m_hoveringOver > -1 && - m_hoveringItem == index ) - { - if ( i <= m_hoveringOver ) // positive star - painter->drawPixmap( r, m_onHoverStar ); - else - painter->drawPixmap( r, m_ratingStarNegative ); - } - else - { - if ( i <= rating ) // positive or rated star - { - if ( userHasRated ) - painter->drawPixmap( r, m_onHoverStar ); - else - painter->drawPixmap( r, m_ratingStarPositive ); - } - else - painter->drawPixmap( r, m_ratingStarNegative ); - } - runningEdge += m_ratingStarPositive.width() + PADDING_BETWEEN_STARS; - } - - // downloaded num times, underneath button - QString count = tr( "%1 downloads" ).arg( index.data( GetNewStuffModel::DownloadCounterRole ).toInt() ); - const QRect countRect( btnRect.left(), btnRect.bottom() + PADDING, btnRect.width(), opt.rect.bottom() - PADDING - btnRect.bottom() ); - QFont countFont = descFont; - countFont.setPointSize( countFont.pointSize() - 2 ); - countFont.setBold( true ); - painter->setFont( countFont ); - painter->drawText( countRect, Qt::AlignCenter | Qt::TextWordWrap, count ); - - // author and version - QString author = index.data( GetNewStuffModel::AuthorRole ).toString(); - const int authorWidth = authorMetrics.width( author ); - const int topTextLine = opt.rect.top() + PADDING; - const QRect authorRect( btnRect.x() - 3*PADDING - authorWidth, topTextLine, authorWidth + 6, authorMetrics.height() ); - painter->setFont( authorFont ); - painter->drawText( authorRect, Qt::AlignCenter, author ); - - const QRect versionRect = authorRect.translated( 0, authorRect.height() ); - QString version = index.data( GetNewStuffModel::VersionRole ).toString(); - painter->drawText( versionRect, Qt::AlignCenter, version ); - - // title - QString title = index.data( Qt::DisplayRole ).toString(); - const int rightTitleEdge = authorRect.left() - PADDING; - const int leftTitleEdge = pixmapRect.right() + PADDING; - const QRect textRect( leftTitleEdge, topTextLine, rightTitleEdge - leftTitleEdge, versionRect.bottom() - opt.rect.top() - PADDING ); - painter->setFont( titleFont ); - painter->drawText( textRect, Qt::AlignVCenter | Qt::AlignLeft, title ); - - // description - QString desc = index.data( GetNewStuffModel::DescriptionRole ).toString(); - const int descWidth = btnRect.left() - leftTitleEdge - PADDING; - const QRect descRect( leftTitleEdge, versionRect.bottom(), descWidth, opt.rect.bottom() - versionRect.bottom() + PADDING ); - painter->setFont( descFont ); - painter->drawText( descRect, Qt::AlignLeft | Qt::TextWordWrap, desc ); -} - - -QSize -GetNewStuffDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const -{ - Q_UNUSED( option ); - Q_UNUSED( index ); - return QSize( 200, SIZEHINT_HEIGHT ); -} - -bool -GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) -{ - Q_UNUSED( option ); - - if ( event->type() != QEvent::MouseButtonRelease && - event->type() != QEvent::MouseMove ) - return false; - - if ( event->type() == QEvent::MouseButtonRelease && m_cachedButtonRects.contains( QPair( index.row(), index.column() ) ) ) - { - QRect rect = m_cachedButtonRects[ QPair( index.row(), index.column() ) ]; - QMouseEvent* me = static_cast< QMouseEvent* >( event ); - - if ( rect.contains( me->pos() ) ) - { - model->setData( index, true ); - - return true; - } - } - - if ( m_cachedStarRects.contains( QPair( index.row(), index.column() ) ) ) - { - QRect fullStars = m_cachedStarRects[ QPair( index.row(), index.column() ) ]; - const int starsWidth = 5 * ( m_ratingStarPositive.width() + PADDING_BETWEEN_STARS ); - fullStars.setWidth( starsWidth ); - - QMouseEvent* me = static_cast< QMouseEvent* >( event ); - - if ( fullStars.contains( me->pos() ) ) - { - const int eachStar = starsWidth / 5; - const int clickOffset = me->pos().x() - fullStars.x(); - const int whichStar = (clickOffset / eachStar) + 1; - - if ( event->type() == QEvent::MouseButtonRelease ) - { - model->setData( index, whichStar, GetNewStuffModel::RatingRole ); - } - else if ( event->type() == QEvent::MouseMove ) - { - // 0-indexed - m_hoveringOver = whichStar; - m_hoveringItem = index; - } - - return true; - } - } - - if ( m_hoveringOver > -1 ) - { - emit update( m_hoveringItem ); - m_hoveringOver = -1; - m_hoveringItem = QPersistentModelIndex(); - } - return false; -} diff --git a/src/GetNewStuffDialog.cpp b/src/GetNewStuffDialog.cpp deleted file mode 100644 index da3f0891f..000000000 --- a/src/GetNewStuffDialog.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2011, Leo Franchi - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "GetNewStuffDialog.h" - -#include "ui_GetNewStuffDialog.h" -#include "GetNewStuffDelegate.h" -#include "GetNewStuffModel.h" - -GetNewStuffDialog::GetNewStuffDialog( QWidget* parent, Qt::WindowFlags f ) - : QDialog( parent, f ) - , ui( new Ui::GetNewStuffDialog ) - , m_model( new GetNewStuffModel( this ) ) -{ - ui->setupUi( this ); - - ui->listView->setModel( m_model ); - GetNewStuffDelegate* del = new GetNewStuffDelegate( ui->listView ); - connect( del, SIGNAL( update( QModelIndex ) ), ui->listView, SLOT( update( QModelIndex ) ) ); - ui->listView->setItemDelegate( del ); - ui->listView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); - - ui->listView->setMouseTracking( true ); - - setMinimumSize( 560, 350 ); - -#ifdef Q_WS_MAC - setMaximumSize( 560, 350 ); - setSizeGripEnabled( false ); - - ui->listView->setAttribute( Qt::WA_MacShowFocusRect, false ); -#endif -} - - -GetNewStuffDialog::~GetNewStuffDialog() -{ - delete ui; -} diff --git a/src/GetNewStuffDialog.ui b/src/GetNewStuffDialog.ui deleted file mode 100644 index 1686a8200..000000000 --- a/src/GetNewStuffDialog.ui +++ /dev/null @@ -1,67 +0,0 @@ - - - GetNewStuffDialog - - - - 0 - 0 - 449 - 282 - - - - Download New Resolvers - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - GetNewStuffDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - GetNewStuffDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/GetNewStuffModel.cpp b/src/GetNewStuffModel.cpp deleted file mode 100644 index 405ca968b..000000000 --- a/src/GetNewStuffModel.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Leo Franchi - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "GetNewStuffModel.h" - -#include "utils/tomahawkutils.h" -#include "utils/logger.h" - -#include -#include -#include "AtticaManager.h" - -GetNewStuffModel::GetNewStuffModel( QObject* parent ) - : QAbstractListModel ( parent ) -{ - - if ( AtticaManager::instance()->resolversLoaded() ) - m_contentList = AtticaManager::instance()->resolvers(); - - connect( AtticaManager::instance(), SIGNAL( resolversReloaded( Attica::Content::List ) ), this, SLOT( resolversReloaded( Attica::Content::List ) ) ); - connect( AtticaManager::instance(), SIGNAL( resolverStateChanged( QString ) ), this, SLOT( resolverStateChanged( QString ) ) ); - -} - -GetNewStuffModel::~GetNewStuffModel() -{ -} - -void -GetNewStuffModel::resolversReloaded( const Attica::Content::List& resolvers ) -{ - beginResetModel(); - m_contentList = resolvers; - endResetModel(); -} - -void -GetNewStuffModel::resolverStateChanged( const QString& resolverId ) -{ - for ( int i = 0; i < m_contentList.count(); i++ ) - { - const Attica::Content resolver = m_contentList[ i ]; - if ( resolver.id() == resolverId ) - { - QModelIndex idx = index( i, 0, QModelIndex() ); - emit dataChanged( idx, idx ); - } - } -} - - -QVariant -GetNewStuffModel::data( const QModelIndex& index, int role ) const -{ - if ( !index.isValid() || !hasIndex( index.row(), index.column(), index.parent() ) ) - return QVariant(); - - Attica::Content resolver = m_contentList[ index.row() ]; - switch ( role ) - { - case Qt::DisplayRole: - return resolver.name(); - case Qt::DecorationRole: - return QVariant::fromValue< QPixmap >( AtticaManager::instance()->iconForResolver( resolver ) ); - case DownloadUrlRole: - // TODO - return QUrl(); - case RatingRole: - return resolver.rating() / 20; // rating is out of 100 - case DownloadCounterRole: - return resolver.downloads(); - case VersionRole: - return resolver.version(); - case DescriptionRole: - return resolver.description(); - case TypeRole: - return ResolverType; - case AuthorRole: - return resolver.author(); - case StateRole: - return (int)AtticaManager::instance()->resolverState( resolver ); - case UserHasRatedRole: - return AtticaManager::instance()->userHasRated( resolver ); - } - return QVariant(); -} - -int -GetNewStuffModel::rowCount( const QModelIndex& parent ) const -{ - Q_UNUSED( parent ); - return m_contentList.count(); -} - -bool -GetNewStuffModel::setData( const QModelIndex &index, const QVariant &value, int role ) -{ - Q_UNUSED( value ); - if ( !hasIndex( index.row(), index.column(), index.parent() ) ) - return false; - - - Attica::Content resolver = m_contentList[ index.row() ]; - AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( resolver ); - if ( role == Qt::EditRole ) - { - switch( state ) - { - case AtticaManager::Uninstalled: - // install - AtticaManager::instance()->installResolver( resolver ); - break; - case AtticaManager::Installing: - case AtticaManager::Upgrading: - // Do nothing, busy - break; - case AtticaManager::Installed: - // Uninstall - AtticaManager::instance()->uninstallResolver( resolver ); - break; - case AtticaManager::NeedsUpgrade: - AtticaManager::instance()->upgradeResolver( resolver ); - break; - default: - //FIXME -- this handles e.g. Failed - break; - }; - } else if ( role == RatingRole ) - { - // For now only allow rating if a resolver is installed! - if ( state != AtticaManager::Installed && state != AtticaManager::NeedsUpgrade ) - return false; - if ( AtticaManager::instance()->userHasRated( resolver ) ) - return false; - m_contentList[ index.row() ].setRating( value.toInt() * 20 ); - AtticaManager::instance()->uploadRating( m_contentList[ index.row() ] ); - } - emit dataChanged( index, index ); - - return true; -} diff --git a/src/GetNewStuffModel.h b/src/GetNewStuffModel.h deleted file mode 100644 index 378057e1e..000000000 --- a/src/GetNewStuffModel.h +++ /dev/null @@ -1,64 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Leo Franchi - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef GETNEWSTUFFMODEL_H -#define GETNEWSTUFFMODEL_H - -#include - -#include -#include - -class GetNewStuffModel: public QAbstractListModel -{ - Q_OBJECT -public: - enum NewStuffRoles { - // DisplayRole is title - // DecorationRole is qicon for item - DownloadUrlRole = Qt::UserRole + 1, - RatingRole = Qt::UserRole + 2, - DownloadCounterRole = Qt::UserRole + 3, - VersionRole = Qt::UserRole + 4, - DescriptionRole = Qt::UserRole + 5, - TypeRole = Qt::UserRole + 6, // Category in attica-speak. What sort of item this is (resolver, etc). - AuthorRole = Qt::UserRole + 7, - StateRole = Qt::UserRole + 8, - UserHasRatedRole = Qt::UserRole + 9 - }; - - enum Types { - ResolverType = 0, - }; - - explicit GetNewStuffModel( QObject* parent = 0 ); - virtual ~GetNewStuffModel(); - - virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; - virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; - virtual bool setData( const QModelIndex &index, const QVariant &value, int role ); - -private slots: - void resolversReloaded( const Attica::Content::List& ); - void resolverStateChanged( const QString& resolverId ); - -private: - Attica::Content::List m_contentList; -}; - -#endif // GETNEWSTUFFMODEL_H diff --git a/src/sip/CMakeLists.txt b/src/accounts/CMakeLists.txt similarity index 54% rename from src/sip/CMakeLists.txt rename to src/accounts/CMakeLists.txt index 6857d099c..c7d709337 100644 --- a/src/sip/CMakeLists.txt +++ b/src/accounts/CMakeLists.txt @@ -1,9 +1,10 @@ + IF( LIBJREEN_FOUND ) - ADD_SUBDIRECTORY( jabber ) -ENDIF( LIBJREEN_FOUND ) + add_subdirectory( xmpp ) +ENDIF() IF( QTWEETLIB_FOUND AND BUILD_GUI ) ADD_SUBDIRECTORY( twitter ) ENDIF() -ADD_SUBDIRECTORY( zeroconf ) +ADD_SUBDIRECTORY( zeroconf ) \ No newline at end of file diff --git a/src/GetNewStuffDialog.h b/src/accounts/accountdllmacro.h similarity index 61% rename from src/GetNewStuffDialog.h rename to src/accounts/accountdllmacro.h index a0e195a8d..bca08fc02 100644 --- a/src/GetNewStuffDialog.h +++ b/src/accounts/accountdllmacro.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2011, Leo Franchi + * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,26 +16,17 @@ * along with Tomahawk. If not, see . */ -#ifndef GETNEWSTUFFDIALOG_H -#define GETNEWSTUFFDIALOG_H +#ifndef ACCOUNTDLLMACRO_H +#define ACCOUNTDLLMACRO_H -#include +#ifdef Q_WS_WIN + #ifdef ACCOUNTDLLEXPORT_PRO + #define ACCOUNTDLLEXPORT __declspec(dllexport) + #else + #define ACCOUNTDLLEXPORT __declspec(dllimport) + #endif +#else + #define ACCOUNTDLLEXPORT +#endif -class GetNewStuffModel; -namespace Ui { - class GetNewStuffDialog; -} - -class GetNewStuffDialog : public QDialog -{ - Q_OBJECT -public: - explicit GetNewStuffDialog( QWidget *parent = 0, Qt::WindowFlags f = 0 ); - ~GetNewStuffDialog(); - -private: - Ui::GetNewStuffDialog *ui; - GetNewStuffModel* m_model; -}; - -#endif // GETNEWSTUFFDIALOG_H +#endif diff --git a/src/sip/twitter/CMakeLists.txt b/src/accounts/twitter/CMakeLists.txt similarity index 58% rename from src/sip/twitter/CMakeLists.txt rename to src/accounts/twitter/CMakeLists.txt index fc6037fb2..3ad17a72a 100644 --- a/src/sip/twitter/CMakeLists.txt +++ b/src/accounts/twitter/CMakeLists.txt @@ -4,21 +4,23 @@ include( ${QT_USE_FILE} ) add_definitions( ${QT_DEFINITIONS} ) add_definitions( -DQT_PLUGIN ) add_definitions( -DQT_SHARED ) -add_definitions( -DSIPDLLEXPORT_PRO ) +add_definitions( -DACCOUNTDLLEXPORT_PRO ) -set( twitterSources - twitter.cpp +set( twitterAccountSources + twitteraccount.cpp twitterconfigwidget.cpp tomahawkoauthtwitter.cpp + sip/twittersip.cpp ) -set( twitterHeaders - twitter.h +set( twitterAccountHeaders + twitteraccount.h twitterconfigwidget.h tomahawkoauthtwitter.h + sip/twittersip.h ) -set( twitterUI +set( twitterAccountUI twitterconfigwidget.ui ) @@ -27,10 +29,10 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. ${QTWEETLIB_INCLUDE_DIR} ) +qt4_wrap_cpp( twitterAccountMoc ${twitterAccountHeaders} ) +qt4_wrap_ui( twitterAccountUI_H ${twitterAccountUI} ) qt4_add_resources( RC_SRCS "resources.qrc" ) -qt4_wrap_cpp( twitterMoc ${twitterHeaders} ) -qt4_wrap_ui( twitterUI_H ${twitterUI} ) -add_library( tomahawk_siptwitter SHARED ${twitterUI_H} ${twitterSources} ${twitterMoc} ${RC_SRCS} ) +add_library( tomahawk_account_twitter MODULE ${twitterAccountUI_H} ${twitterAccountSources} ${twitterAccountMoc} ${RC_SRCS} ) IF( WIN32 ) SET( OS_SPECIFIC_LINK_LIBRARIES @@ -40,7 +42,7 @@ SET( OS_SPECIFIC_LINK_LIBRARIES ) ENDIF( WIN32 ) -target_link_libraries( tomahawk_siptwitter +target_link_libraries( tomahawk_account_twitter ${TOMAHAWK_LIBRARIES} ${QTWEETLIB_LIBRARIES} ${QT_LIBRARIES} @@ -51,4 +53,4 @@ IF( APPLE ) # SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) ENDIF( APPLE ) -install( TARGETS tomahawk_siptwitter DESTINATION ${CMAKE_INSTALL_LIBDIR} ) +install( TARGETS tomahawk_account_twitter DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/src/sip/twitter/resources.qrc b/src/accounts/twitter/resources.qrc similarity index 100% rename from src/sip/twitter/resources.qrc rename to src/accounts/twitter/resources.qrc diff --git a/src/accounts/twitter/sip/twittersip.cpp b/src/accounts/twitter/sip/twittersip.cpp new file mode 100644 index 000000000..ee65f50ff --- /dev/null +++ b/src/accounts/twitter/sip/twittersip.cpp @@ -0,0 +1,714 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "twittersip.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "utils/logger.h" +#include "accounts/twitter/tomahawkoauthtwitter.h" +#include + +static QString s_gotTomahawkRegex = QString( "^(@[a-zA-Z0-9]+ )?(Got Tomahawk\\?) (\\{[a-fA-F0-9\\-]+\\}) (.*)$" ); + +TwitterSipPlugin::TwitterSipPlugin( Tomahawk::Accounts::Account* account ) + : SipPlugin( account ) + , m_checkTimer( this ) + , m_connectTimer( this ) + , m_dmPollTimer( this ) + , m_cachedFriendsSinceId( 0 ) + , m_cachedMentionsSinceId( 0 ) + , m_cachedDirectMessagesSinceId( 0 ) + , m_cachedPeers() + , m_keyCache() + , m_state( Tomahawk::Accounts::Account::Disconnected ) +{ + qDebug() << Q_FUNC_INFO; + + connect( account, SIGNAL( nowAuthenticated( const QWeakPointer< TomahawkOAuthTwitter > &, const QTweetUser & ) ), SLOT( accountAuthenticated( const QWeakPointer< TomahawkOAuthTwitter > &, const QTweetUser & ) ) ); + + m_configuration = account->configuration(); + qDebug() << "SIP configuration:" << m_configuration << m_configuration[ "cachedpeers" ]; + if ( Database::instance()->dbid() != m_account->configuration()[ "saveddbid" ].toString() ) + { + m_configuration[ "cachedpeers" ] = QVariantHash(); + m_configuration[ "saveddbid" ] = Database::instance()->dbid(); + syncConfig(); + } + + m_checkTimer.setInterval( 180000 ); + m_checkTimer.setSingleShot( false ); + connect( &m_checkTimer, SIGNAL( timeout() ), SLOT( checkTimerFired() ) ); + + m_dmPollTimer.setInterval( 60000 ); + m_dmPollTimer.setSingleShot( false ); + connect( &m_dmPollTimer, SIGNAL( timeout() ), SLOT( pollDirectMessages() ) ); + + m_connectTimer.setInterval( 180000 ); + m_connectTimer.setSingleShot( false ); + connect( &m_connectTimer, SIGNAL( timeout() ), SLOT( connectTimerFired() ) ); +} + + +bool +TwitterSipPlugin::isValid() const +{ + return m_account->enabled() && m_account->isAuthenticated() && !m_cachedTwitterAuth.isNull(); +} + + +Tomahawk::Accounts::Account::ConnectionState +TwitterSipPlugin::connectionState() const +{ + return m_state; +} + + +void +TwitterSipPlugin::checkSettings() +{ + configurationChanged(); +} + + +void +TwitterSipPlugin::connectPlugin() +{ + tDebug() << Q_FUNC_INFO; + if ( !m_account->enabled() ) + { + tDebug() << Q_FUNC_INFO << "account isn't enabled"; + return; + } + + m_cachedPeers = m_configuration[ "cachedpeers" ].toHash(); + QStringList peerList = m_cachedPeers.keys(); + qStableSort( peerList.begin(), peerList.end() ); + + if ( !m_account->isAuthenticated() ) + { + tDebug() << Q_FUNC_INFO << "account isn't authenticated, attempting"; + m_account->authenticate(); + } + + m_state = Tomahawk::Accounts::Account::Connecting; + emit stateChanged( m_state ); +} + + +void +TwitterSipPlugin::disconnectPlugin() +{ + tDebug() << Q_FUNC_INFO; + m_checkTimer.stop(); + m_connectTimer.stop(); + m_dmPollTimer.stop(); + if( !m_friendsTimeline.isNull() ) + delete m_friendsTimeline.data(); + if( !m_mentions.isNull() ) + delete m_mentions.data(); + if( !m_directMessages.isNull() ) + delete m_directMessages.data(); + if( !m_directMessageNew.isNull() ) + delete m_directMessageNew.data(); + if( !m_directMessageDestroy.isNull() ) + delete m_directMessageDestroy.data(); + + m_cachedTwitterAuth.clear(); + + m_configuration[ "cachedpeers" ] = m_cachedPeers; + syncConfig(); + m_cachedPeers.empty(); + m_state = Tomahawk::Accounts::Account::Disconnected; + emit stateChanged( m_state ); +} + +void +TwitterSipPlugin::accountAuthenticated( const QWeakPointer< TomahawkOAuthTwitter > &twitterAuth, const QTweetUser &user ) +{ + Q_UNUSED( user ); + + if ( !m_account->enabled() || !m_account->isAuthenticated() ) + return; + + m_cachedTwitterAuth = twitterAuth; + + m_friendsTimeline = QWeakPointer( new QTweetFriendsTimeline( m_cachedTwitterAuth.data(), this ) ); + m_mentions = QWeakPointer( new QTweetMentions( m_cachedTwitterAuth.data(), this ) ); + m_directMessages = QWeakPointer( new QTweetDirectMessages( m_cachedTwitterAuth.data(), this ) ); + m_directMessageNew = QWeakPointer( new QTweetDirectMessageNew( m_cachedTwitterAuth.data(), this ) ); + m_directMessageDestroy = QWeakPointer( new QTweetDirectMessageDestroy( m_cachedTwitterAuth.data(), this ) ); + connect( m_friendsTimeline.data(), SIGNAL( parsedStatuses(const QList< QTweetStatus > &) ), SLOT( friendsTimelineStatuses(const QList &) ) ); + connect( m_mentions.data(), SIGNAL( parsedStatuses(const QList< QTweetStatus > &) ), SLOT( mentionsStatuses(const QList &) ) ); + connect( m_directMessages.data(), SIGNAL( parsedDirectMessages(const QList &)), SLOT( directMessages(const QList &) ) ); + connect( m_directMessageNew.data(), SIGNAL( parsedDirectMessage(const QTweetDMStatus &)), SLOT( directMessagePosted(const QTweetDMStatus &) ) ); + connect( m_directMessageNew.data(), SIGNAL( error(QTweetNetBase::ErrorCode, const QString &) ), SLOT( directMessagePostError(QTweetNetBase::ErrorCode, const QString &) ) ); + connect( m_directMessageDestroy.data(), SIGNAL( parsedDirectMessage(const QTweetDMStatus &) ), SLOT( directMessageDestroyed(const QTweetDMStatus &) ) ); + m_state = Tomahawk::Accounts::Account::Connected; + emit stateChanged( m_state ); + QStringList peerList = m_cachedPeers.keys(); + qStableSort( peerList.begin(), peerList.end() ); + registerOffers( peerList ); + m_connectTimer.start(); + m_checkTimer.start(); + m_dmPollTimer.start(); + + QMetaObject::invokeMethod( this, "checkTimerFired", Qt::AutoConnection ); + QTimer::singleShot( 20000, this, SLOT( connectTimerFired() ) ); +} + + +void +TwitterSipPlugin::checkTimerFired() +{ + if ( !isValid() ) + return; + + if ( m_cachedFriendsSinceId == 0 ) + m_cachedFriendsSinceId = m_configuration[ "cachedfriendssinceid" ].toLongLong(); + + qDebug() << "TwitterSipPlugin looking at friends timeline since id " << m_cachedFriendsSinceId; + + if ( !m_friendsTimeline.isNull() ) + m_friendsTimeline.data()->fetch( m_cachedFriendsSinceId, 0, 800 ); + + if ( m_cachedMentionsSinceId == 0 ) + m_cachedMentionsSinceId = m_configuration[ "cachedmentionssinceid" ].toLongLong(); + + qDebug() << "TwitterSipPlugin looking at mentions timeline since id " << m_cachedMentionsSinceId; + + if ( !m_mentions.isNull() ) + m_mentions.data()->fetch( m_cachedMentionsSinceId, 0, 800 ); +} + + +void +TwitterSipPlugin::registerOffers( const QStringList &peerList ) +{ + if ( !isValid() ) + return; + + foreach( QString screenName, peerList ) + { + QVariantHash peerData = m_cachedPeers[screenName].toHash(); + + if ( peerData.contains( "onod" ) && peerData["onod"] != Database::instance()->dbid() ) + { + m_cachedPeers.remove( screenName ); + m_configuration[ "cachedpeers" ] = m_cachedPeers; + syncConfig(); + } + + if ( Servent::instance()->connectedToSession( peerData["node"].toString() ) ) + { + peerData["lastseen"] = QDateTime::currentMSecsSinceEpoch(); + m_cachedPeers[screenName] = peerData; + m_configuration[ "cachedpeers" ] = m_cachedPeers; + syncConfig(); + qDebug() << Q_FUNC_INFO << " already connected"; + continue; + } + else if ( QDateTime::currentMSecsSinceEpoch() - peerData["lastseen"].toLongLong() > 1209600000 ) // 2 weeks + { + qDebug() << Q_FUNC_INFO << " aging peer " << screenName << " out of cache"; + m_cachedPeers.remove( screenName ); + m_configuration[ "cachedpeers" ] = m_cachedPeers; + syncConfig(); + m_cachedAvatars.remove( screenName ); + continue; + } + + if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) ) + { + qDebug() << "TwitterSipPlugin does not have host, port and/or pkey values for " << screenName << " (this is usually *not* a bug or problem but a normal part of the process)"; + continue; + } + + QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), Q_ARG( QVariantHash, peerData ) ); + } +} + + +void +TwitterSipPlugin::connectTimerFired() +{ + tDebug() << Q_FUNC_INFO << " beginning"; + if ( !isValid() || m_cachedPeers.isEmpty() ) + { + if ( !isValid() ) + tDebug() << Q_FUNC_INFO << " is not valid"; + if ( m_cachedPeers.isEmpty() ) + tDebug() << Q_FUNC_INFO << " has empty cached peers"; + return; + } + + tDebug() << Q_FUNC_INFO << " continuing"; + QString myScreenName = m_configuration[ "screenname" ].toString(); + QStringList peerList = m_cachedPeers.keys(); + qStableSort( peerList.begin(), peerList.end() ); + registerOffers( peerList ); +} + +void +TwitterSipPlugin::parseGotTomahawk( const QRegExp ®ex, const QString &screenName, const QString &text ) +{ + QString myScreenName = m_configuration[ "screenname" ].toString(); + qDebug() << "TwitterSipPlugin found an exact matching Got Tomahawk? mention or direct message from user " << screenName << ", now parsing"; + regex.exactMatch( text ); + if ( text.startsWith( '@' ) && regex.captureCount() >= 2 && regex.cap( 1 ) != QString( '@' + myScreenName ) ) + { + qDebug() << "TwitterSipPlugin skipping mention because it's directed @someone that isn't us"; + return; + } + + QString node; + for ( int i = 0; i < regex.captureCount(); ++i ) + { + if ( regex.cap( i ) == QString( "Got Tomahawk?" ) ) + { + QString nodeCap = regex.cap( i + 1 ); + nodeCap.chop( 1 ); + node = nodeCap.mid( 1 ); + } + } + if ( node.isEmpty() ) + { + qDebug() << "TwitterSipPlugin could not parse node out of the tweet"; + return; + } + else + qDebug() << "TwitterSipPlugin parsed node " << node << " out of the tweet"; + + if ( node == Database::instance()->dbid() ) + { + qDebug() << "My dbid found; ignoring"; + return; + } + + QVariantHash peerData; + if( m_cachedPeers.contains( screenName ) ) + { + peerData = m_cachedPeers[screenName].toHash(); + //force a re-send of info but no need to re-register + peerData["resend"] = QVariant::fromValue< bool >( true ); + if ( peerData["node"].toString() != node ) + peerData["rekey"] = QVariant::fromValue< bool >( true ); + } + peerData["node"] = QVariant::fromValue< QString >( node ); + QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), Q_ARG( QVariantHash, peerData ) ); +} + +void +TwitterSipPlugin::friendsTimelineStatuses( const QList< QTweetStatus > &statuses ) +{ + tDebug() << Q_FUNC_INFO; + QRegExp regex( s_gotTomahawkRegex, Qt::CaseSensitive, QRegExp::RegExp2 ); + + QHash< QString, QTweetStatus > latestHash; + foreach ( QTweetStatus status, statuses ) + { + if ( !regex.exactMatch( status.text() ) ) + continue; + + if ( !latestHash.contains( status.user().screenName() ) ) + latestHash[status.user().screenName()] = status; + else + { + if ( status.id() > latestHash[status.user().screenName()].id() ) + latestHash[status.user().screenName()] = status; + } + } + + foreach( QTweetStatus status, latestHash.values() ) + { + if ( status.id() > m_cachedFriendsSinceId ) + m_cachedFriendsSinceId = status.id(); + + tDebug() << "TwitterSipPlugin checking mention from " << status.user().screenName() << " with content " << status.text(); + parseGotTomahawk( regex, status.user().screenName(), status.text() ); + } + + m_configuration[ "cachedfriendssinceid" ] = m_cachedFriendsSinceId; + syncConfig(); +} + +void +TwitterSipPlugin::mentionsStatuses( const QList< QTweetStatus > &statuses ) +{ + tDebug() << Q_FUNC_INFO; + QRegExp regex( s_gotTomahawkRegex, Qt::CaseSensitive, QRegExp::RegExp2 ); + + QHash< QString, QTweetStatus > latestHash; + foreach ( QTweetStatus status, statuses ) + { + if ( !regex.exactMatch( status.text() ) ) + continue; + + if ( !latestHash.contains( status.user().screenName() ) ) + latestHash[status.user().screenName()] = status; + else + { + if ( status.id() > latestHash[status.user().screenName()].id() ) + latestHash[status.user().screenName()] = status; + } + } + + foreach( QTweetStatus status, latestHash.values() ) + { + if ( status.id() > m_cachedMentionsSinceId ) + m_cachedMentionsSinceId = status.id(); + + tDebug() << "TwitterSipPlugin checking mention from " << status.user().screenName() << " with content " << status.text(); + parseGotTomahawk( regex, status.user().screenName(), status.text() ); + } + + m_configuration[ "cachedmentionssinceid" ] = m_cachedMentionsSinceId; + syncConfig(); +} + +void +TwitterSipPlugin::pollDirectMessages() +{ + if ( !isValid() ) + return; + + if ( m_cachedDirectMessagesSinceId == 0 ) + m_cachedDirectMessagesSinceId = m_configuration[ "cacheddirectmessagessinceid" ].toLongLong(); + + tDebug() << "TwitterSipPlugin looking for direct messages since id " << m_cachedDirectMessagesSinceId; + + if ( !m_directMessages.isNull() ) + m_directMessages.data()->fetch( m_cachedDirectMessagesSinceId, 0, 800 ); +} + +void +TwitterSipPlugin::directMessages( const QList< QTweetDMStatus > &messages ) +{ + tDebug() << Q_FUNC_INFO; + + QRegExp regex( s_gotTomahawkRegex, Qt::CaseSensitive, QRegExp::RegExp2 ); + QString myScreenName = m_configuration[ "screenname" ].toString(); + + QHash< QString, QTweetDMStatus > latestHash; + foreach ( QTweetDMStatus status, messages ) + { + if ( !regex.exactMatch( status.text() ) ) + { + QStringList splitList = status.text().split(':'); + if ( splitList.length() != 5 ) + continue; + if ( splitList[0] != "TOMAHAWKPEER" ) + continue; + if ( !splitList[1].startsWith( "Host=" ) || !splitList[2].startsWith( "Port=" ) || !splitList[3].startsWith( "Node=" ) || !splitList[4].startsWith( "PKey=" ) ) + continue; + int port = splitList[2].mid( 5 ).toInt(); + if ( port == 0 ) + continue; + } + + if ( !latestHash.contains( status.senderScreenName() ) ) + latestHash[status.senderScreenName()] = status; + else + { + if ( status.id() > latestHash[status.senderScreenName()].id() ) + latestHash[status.senderScreenName()] = status; + } + } + + foreach( QTweetDMStatus status, latestHash.values() ) + { + qDebug() << "TwitterSipPlugin checking direct message from " << status.senderScreenName() << " with content " << status.text(); + if ( status.id() > m_cachedDirectMessagesSinceId ) + m_cachedDirectMessagesSinceId = status.id(); + + if ( regex.exactMatch( status.text() ) ) + parseGotTomahawk( regex, status.sender().screenName(), status.text() ); + else + { + QStringList splitList = status.text().split(':'); + qDebug() << "TwitterSipPlugin found " << splitList.length() << " parts to the message; the parts are:"; + foreach( QString part, splitList ) + qDebug() << part; + //validity is checked above + int port = splitList[2].mid( 5 ).toInt(); + QString host = splitList[1].mid( 5 ); + QString node = splitList[3].mid( 5 ); + QString pkey = splitList[4].mid( 5 ); + QStringList splitNode = node.split('*'); + if ( splitNode.length() != 2 ) + { + qDebug() << "Old-style node info found, ignoring"; + continue; + } + qDebug() << "TwitterSipPlugin found a peerstart message from " << status.senderScreenName() << " with host " << host << " and port " << port << " and pkey " << pkey << " and node " << splitNode[0] << " destined for node " << splitNode[1]; + + + QVariantHash peerData = ( m_cachedPeers.contains( status.senderScreenName() ) ) ? + m_cachedPeers[status.senderScreenName()].toHash() : + QVariantHash(); + + peerData["host"] = QVariant::fromValue< QString >( host ); + peerData["port"] = QVariant::fromValue< int >( port ); + peerData["pkey"] = QVariant::fromValue< QString >( pkey ); + peerData["node"] = QVariant::fromValue< QString >( splitNode[0] ); + peerData["dirty"] = QVariant::fromValue< bool >( true ); + + QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, status.senderScreenName() ), Q_ARG( QVariantHash, peerData ) ); + + if ( Database::instance()->dbid().startsWith( splitNode[1] ) ) + { + qDebug() << "TwitterSipPlugin found message destined for this node; destroying it"; + if ( !m_directMessageDestroy.isNull() ) + m_directMessageDestroy.data()->destroyMessage( status.id() ); + } + } + } + + m_configuration[ "cacheddirectmessagessinceid" ] = m_cachedDirectMessagesSinceId; + syncConfig(); +} + +void +TwitterSipPlugin::registerOffer( const QString &screenName, const QVariantHash &peerData ) +{ + qDebug() << Q_FUNC_INFO; + + bool peersChanged = false; + bool needToSend = false; + bool needToAddToCache = false; + + QString friendlyName = QString( '@' + screenName ); + + if ( !m_cachedAvatars.contains( screenName ) ) + QMetaObject::invokeMethod( this, "fetchAvatar", Q_ARG( QString, screenName ) ); + + QVariantHash _peerData( peerData ); + + if ( _peerData.contains( "dirty" ) ) + { + peersChanged = true; + _peerData.remove( "dirty" ); + } + + if ( _peerData.contains( "resend" ) ) + { + needToSend = true; + peersChanged = true; + _peerData.remove( "resend" ); + } + + if ( !_peerData.contains( "okey" ) || + !_peerData.contains( "onod" ) || + ( _peerData.contains( "onod" ) && _peerData["onod"] != Database::instance()->dbid() ) ) + { + QString okey = QUuid::createUuid().toString().split( '-' ).last(); + okey.chop( 1 ); + _peerData["okey"] = QVariant::fromValue< QString >( okey ); + _peerData["onod"] = QVariant::fromValue< QString >( Database::instance()->dbid() ); + peersChanged = true; + needToAddToCache = true; + needToSend = true; + } + + if ( _peerData.contains( "rekey" ) || !m_keyCache.contains( _peerData["okey"].toString() ) ) + { + _peerData.remove( "rekey" ); + needToAddToCache = true; + } + + if ( !_peerData.contains( "ohst" ) || !_peerData.contains( "oprt" ) || + _peerData["ohst"].toString() != Servent::instance()->externalAddress() || + _peerData["oprt"].toInt() != Servent::instance()->externalPort() + ) + needToSend = true; + + if( needToAddToCache && _peerData.contains( "node" ) ) + { + qDebug() << "TwitterSipPlugin registering offer to " << friendlyName << " with node " << _peerData["node"].toString() << " and offeredkey " << _peerData["okey"].toString(); + m_keyCache << Servent::instance()->createConnectionKey( friendlyName, _peerData["node"].toString(), _peerData["okey"].toString(), false ); + } + + if( needToSend && _peerData.contains( "node") ) + { + qDebug() << "TwitterSipPlugin needs to send and has node"; + _peerData["ohst"] = QVariant::fromValue< QString >( Servent::instance()->externalAddress() ); + _peerData["oprt"] = QVariant::fromValue< int >( Servent::instance()->externalPort() ); + peersChanged = true; + if( !Servent::instance()->externalAddress().isEmpty() && !Servent::instance()->externalPort() == 0 ) + QMetaObject::invokeMethod( this, "sendOffer", Q_ARG( QString, screenName ), Q_ARG( QVariantHash, _peerData ) ); + else + qDebug() << "TwitterSipPlugin did not send offer because external address is " << Servent::instance()->externalAddress() << " and external port is " << Servent::instance()->externalPort(); + } + + if ( peersChanged ) + { + _peerData["lastseen"] = QString::number( QDateTime::currentMSecsSinceEpoch() ); + m_cachedPeers[screenName] = QVariant::fromValue< QVariantHash >( _peerData ); + m_configuration[ "cachedpeers" ] = m_cachedPeers; + syncConfig(); + } + + if ( m_state == Tomahawk::Accounts::Account::Connected && _peerData.contains( "host" ) && _peerData.contains( "port" ) && _peerData.contains( "pkey" ) ) + QMetaObject::invokeMethod( this, "makeConnection", Q_ARG( QString, screenName ), Q_ARG( QVariantHash, _peerData ) ); + +} + +void +TwitterSipPlugin::sendOffer( const QString &screenName, const QVariantHash &peerData ) +{ + qDebug() << Q_FUNC_INFO; + QString offerString = QString( "TOMAHAWKPEER:Host=%1:Port=%2:Node=%3*%4:PKey=%5" ).arg( peerData["ohst"].toString() ) + .arg( peerData["oprt"].toString() ) + .arg( Database::instance()->dbid() ) + .arg( peerData["node"].toString().left( 8 ) ) + .arg( peerData["okey"].toString() ); + qDebug() << "TwitterSipPlugin sending message to " << screenName << ": " << offerString; + if( !m_directMessageNew.isNull() ) + m_directMessageNew.data()->post( screenName, offerString ); +} + +void +TwitterSipPlugin::makeConnection( const QString &screenName, const QVariantHash &peerData ) +{ + qDebug() << Q_FUNC_INFO; + if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) || !peerData.contains( "node" ) || + peerData["host"].toString().isEmpty() || peerData["port"].toString().isEmpty() || peerData["pkey"].toString().isEmpty() || peerData["node"].toString().isEmpty() ) + { + qDebug() << "TwitterSipPlugin could not find host and/or port and/or pkey and/or node for peer " << screenName; + return; + } + + if ( peerData["host"].toString() == Servent::instance()->externalAddress() && + peerData["port"].toInt() == Servent::instance()->externalPort() ) + { + qDebug() << "TwitterSipPlugin asked to make connection to our own host and port, ignoring " << screenName; + return; + } + + QString friendlyName = QString( '@' + screenName ); + if ( !Servent::instance()->connectedToSession( peerData["node"].toString() ) ) + Servent::instance()->connectToPeer( peerData["host"].toString(), + peerData["port"].toString().toInt(), + peerData["pkey"].toString(), + friendlyName, + peerData["node"].toString() ); +} + +void +TwitterSipPlugin::directMessagePosted( const QTweetDMStatus& message ) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "TwitterSipPlugin sent message to " << message.recipientScreenName() << " containing: " << message.text(); + +} + +void +TwitterSipPlugin::directMessagePostError( QTweetNetBase::ErrorCode errorCode, const QString &message ) +{ + Q_UNUSED( errorCode ); + Q_UNUSED( message ); + qDebug() << Q_FUNC_INFO; + qDebug() << "TwitterSipPlugin received an error posting direct message: " << m_directMessageNew.data()->lastErrorMessage(); +} + +void +TwitterSipPlugin::directMessageDestroyed( const QTweetDMStatus& message ) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "TwitterSipPlugin destroyed message " << message.text(); +} + +void +TwitterSipPlugin::fetchAvatar( const QString& screenName ) +{ + qDebug() << Q_FUNC_INFO; + if ( !isValid() ) + return; + + QTweetUserShow *userShowFetch = new QTweetUserShow( m_cachedTwitterAuth.data(), this ); + connect( userShowFetch, SIGNAL( parsedUserInfo( QTweetUser ) ), SLOT( avatarUserDataSlot( QTweetUser ) ) ); + userShowFetch->fetch( screenName ); +} + +void +TwitterSipPlugin::avatarUserDataSlot( const QTweetUser &user ) +{ + tDebug() << Q_FUNC_INFO; + if ( !isValid() || user.profileImageUrl().isEmpty()) + return; + + QNetworkRequest request( user.profileImageUrl() ); + QNetworkReply *reply = m_cachedTwitterAuth.data()->networkAccessManager()->get( request ); + reply->setProperty( "screenname", user.screenName() ); + connect( reply, SIGNAL( finished() ), this, SLOT( profilePicReply() ) ); +} + + +void +TwitterSipPlugin::profilePicReply() +{ + tDebug() << Q_FUNC_INFO; + QNetworkReply *reply = qobject_cast< QNetworkReply* >( sender() ); + if ( !reply || reply->error() != QNetworkReply::NoError || !reply->property( "screenname" ).isValid() ) + { + tDebug() << Q_FUNC_INFO << " reply not valid or came back with error"; + return; + } + QString screenName = reply->property( "screenname" ).toString(); + QString friendlyName = '@' + screenName; + QByteArray rawData = reply->readAll(); + QImage image; + image.loadFromData( rawData, "PNG" ); + QPixmap pixmap = QPixmap::fromImage( image ); + m_cachedAvatars[screenName] = pixmap; + emit avatarReceived( friendlyName, QPixmap::fromImage( image ) ); +} + +void +TwitterSipPlugin::configurationChanged() +{ + tDebug() << Q_FUNC_INFO; + if ( m_state != Tomahawk::Accounts::Account::Disconnected ) + m_account->deauthenticate(); + connectPlugin(); +} + + +void +TwitterSipPlugin::syncConfig() +{ + m_account->setConfiguration( m_configuration ); + m_account->sync(); +} diff --git a/src/sip/twitter/twitter.h b/src/accounts/twitter/sip/twittersip.h similarity index 59% rename from src/sip/twitter/twitter.h rename to src/accounts/twitter/sip/twittersip.h index 3ca0688ca..09c18ac91 100644 --- a/src/sip/twitter/twitter.h +++ b/src/accounts/twitter/sip/twittersip.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,8 +20,6 @@ #ifndef TWITTER_H #define TWITTER_H -#include "twitterconfigwidget.h" - #include #include #include @@ -34,50 +33,30 @@ #include #include -#include "../sipdllmacro.h" +#include "accounts/accountdllmacro.h" #include "sip/SipPlugin.h" -#include "tomahawkoauthtwitter.h" +#include "accounts/Account.h" +#include "accounts/twitter/tomahawkoauthtwitter.h" -#define MYNAME "SIPTWITTER" - -class SIPDLLEXPORT TwitterFactory : public SipPluginFactory -{ - Q_OBJECT - Q_INTERFACES( SipPluginFactory ) - -public: - TwitterFactory() {} - virtual ~TwitterFactory() {} - - virtual QString prettyName() const { return "Twitter"; } - virtual QString factoryId() const { return "siptwitter"; } - virtual QIcon icon() const; - virtual SipPlugin* createPlugin( const QString& pluginId = QString() ); -}; - -class SIPDLLEXPORT TwitterPlugin : public SipPlugin +class ACCOUNTDLLEXPORT TwitterSipPlugin : public SipPlugin { Q_OBJECT public: - TwitterPlugin( const QString& pluginId ); + TwitterSipPlugin( Tomahawk::Accounts::Account *account ); - virtual ~TwitterPlugin() {} + virtual ~TwitterSipPlugin() {} virtual bool isValid() const; - virtual const QString name() const; - virtual const QString accountName() const; - virtual const QString friendlyName() const; - virtual ConnectionState connectionState() const; - virtual QIcon icon() const; - virtual QWidget* configWidget(); + virtual Tomahawk::Accounts::Account::ConnectionState connectionState() const; + +signals: + void stateChanged( Tomahawk::Accounts::Account::ConnectionState ); public slots: - virtual bool connectPlugin( bool startup ); + virtual void connectPlugin(); void disconnectPlugin(); - void checkSettings(); - void refreshProxy(); - void deletePlugin(); + void configurationChanged(); void sendMsg( const QString& to, const QString& msg ) { @@ -96,9 +75,10 @@ public slots: Q_UNUSED( msg ); } + void checkSettings(); + private slots: - void configDialogAuthedSignalSlot( bool authed ); - void connectAuthVerifyReply( const QTweetUser &user ); + void accountAuthenticated( const QWeakPointer< TomahawkOAuthTwitter > &twitterAuth, const QTweetUser &user ); void checkTimerFired(); void connectTimerFired(); void friendsTimelineStatuses( const QList< QTweetStatus > &statuses ); @@ -117,35 +97,20 @@ private slots: void profilePicReply(); private: - inline void syncConfig() { setTwitterCachedPeers( m_cachedPeers ); } + void syncConfig(); bool refreshTwitterAuth(); void parseGotTomahawk( const QRegExp ®ex, const QString &screenName, const QString &text ); - // handle per-plugin config - QString twitterSavedDbid() const; - void setTwitterSavedDbid( const QString& dbid ); - QString twitterScreenName() const; - void setTwitterScreenName( const QString& screenName ); - QString twitterOAuthToken() const; - void setTwitterOAuthToken( const QString& oauthtoken ); - QString twitterOAuthTokenSecret() const; - void setTwitterOAuthTokenSecret( const QString& oauthtokensecret ); - qint64 twitterCachedFriendsSinceId() const; - void setTwitterCachedFriendsSinceId( qint64 sinceid ); - qint64 twitterCachedMentionsSinceId() const; - void setTwitterCachedMentionsSinceId( qint64 sinceid ); - qint64 twitterCachedDirectMessagesSinceId() const; - void setTwitterCachedDirectMessagesSinceId( qint64 sinceid ); - QVariantHash twitterCachedPeers() const; - void setTwitterCachedPeers( const QVariantHash &cachedPeers ); - QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth; + QWeakPointer< TomahawkOAuthTwitter > m_cachedTwitterAuth; + QWeakPointer< QTweetFriendsTimeline > m_friendsTimeline; QWeakPointer< QTweetMentions > m_mentions; QWeakPointer< QTweetDirectMessages > m_directMessages; QWeakPointer< QTweetDirectMessageNew > m_directMessageNew; QWeakPointer< QTweetDirectMessageDestroy > m_directMessageDestroy; - bool m_isAuthed; + QVariantHash m_configuration; + QTimer m_checkTimer; QTimer m_connectTimer; QTimer m_dmPollTimer; @@ -155,12 +120,7 @@ private: QVariantHash m_cachedPeers; QHash< QString, QPixmap > m_cachedAvatars; QSet m_keyCache; - ConnectionState m_state; - - QWeakPointer m_configWidget; - - // for settings access - friend class TwitterConfigWidget; + Tomahawk::Accounts::Account::ConnectionState m_state; }; #endif diff --git a/src/sip/twitter/tomahawkoauthtwitter.cpp b/src/accounts/twitter/tomahawkoauthtwitter.cpp similarity index 96% rename from src/sip/twitter/tomahawkoauthtwitter.cpp rename to src/accounts/twitter/tomahawkoauthtwitter.cpp index 35cd98a9e..b9b7e653d 100644 --- a/src/sip/twitter/tomahawkoauthtwitter.cpp +++ b/src/accounts/twitter/tomahawkoauthtwitter.cpp @@ -1,6 +1,6 @@ #include "tomahawkoauthtwitter.h" -#include +#include #include "utils/logger.h" diff --git a/src/accounts/twitter/tomahawkoauthtwitter.h b/src/accounts/twitter/tomahawkoauthtwitter.h new file mode 100644 index 000000000..7cd243efe --- /dev/null +++ b/src/accounts/twitter/tomahawkoauthtwitter.h @@ -0,0 +1,26 @@ +#ifndef TOMAHAWKOAUTHTWITTERACCOUNT +#define TOMAHAWKOAUTHTWITTERACCOUNT + +#include "accounts/accountdllmacro.h" +#include + +#include +#include + +class ACCOUNTDLLEXPORT TomahawkOAuthTwitter : public OAuthTwitter +{ + Q_OBJECT + +public: + TomahawkOAuthTwitter( QNetworkAccessManager *nam = TomahawkUtils::nam() , QObject *parent = 0 ); + + ~TomahawkOAuthTwitter() {} + +protected: + virtual int authorizationWidget(); + +private slots: + void error(); +}; + +#endif diff --git a/src/sip/twitter/twitter-icon.png b/src/accounts/twitter/twitter-icon.png similarity index 100% rename from src/sip/twitter/twitter-icon.png rename to src/accounts/twitter/twitter-icon.png diff --git a/src/accounts/twitter/twitteraccount.cpp b/src/accounts/twitter/twitteraccount.cpp new file mode 100644 index 000000000..67bad5148 --- /dev/null +++ b/src/accounts/twitter/twitteraccount.cpp @@ -0,0 +1,186 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "twitteraccount.h" +#include "twitterconfigwidget.h" +#include "accounts/twitter/tomahawkoauthtwitter.h" + +#include "sip/SipPlugin.h" + +#include +#include +#include +#include + +#include + +namespace Tomahawk +{ + +namespace Accounts +{ + +Account* +TwitterAccountFactory::createAccount( const QString& accountId ) +{ + return new TwitterAccount( accountId.isEmpty() ? Tomahawk::Accounts::generateId( factoryId() ) : accountId ); +} + + +TwitterAccount::TwitterAccount( const QString &accountId ) + : Account( accountId ) + , m_isAuthenticated( false ) +{ + setAccountServiceName( "Twitter" ); + setTypes( AccountTypes( InfoType | SipType ) ); + + qDebug() << "Got cached peers:" << configuration() << configuration()[ "cachedpeers" ]; + + m_configWidget = QWeakPointer< TwitterConfigWidget >( new TwitterConfigWidget( this, 0 ) ); + connect( m_configWidget.data(), SIGNAL( twitterAuthed( bool ) ), SLOT( configDialogAuthedSignalSlot( bool ) ) ); + + m_twitterAuth = QWeakPointer< TomahawkOAuthTwitter >( new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ) ); +} + + +TwitterAccount::~TwitterAccount() +{ + +} + + +void +TwitterAccount::configDialogAuthedSignalSlot( bool authed ) +{ + tDebug() << Q_FUNC_INFO; + m_isAuthenticated = authed; + if ( !credentials()[ "username" ].toString().isEmpty() ) + setAccountFriendlyName( QString( "@%1" ).arg( credentials()[ "username" ].toString() ) ); + syncConfig(); + emit configurationChanged(); +} + + +Account::ConnectionState +TwitterAccount::connectionState() const +{ + return m_twitterSipPlugin.data()->connectionState(); +} + +SipPlugin* +TwitterAccount::sipPlugin() +{ + if ( m_twitterSipPlugin.isNull() ) + { + qDebug() << "CHECKING:" << configuration() << configuration()[ "cachedpeers" ]; + m_twitterSipPlugin = QWeakPointer< TwitterSipPlugin >( new TwitterSipPlugin( this ) ); + + connect( m_twitterSipPlugin.data(), SIGNAL( stateChanged( Tomahawk::Accounts::Account::ConnectionState ) ), this, SIGNAL( connectionStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ) ); + return m_twitterSipPlugin.data(); + } + return m_twitterSipPlugin.data(); +} + + +void +TwitterAccount::authenticate() +{ + tDebug() << Q_FUNC_INFO << "credentials: " << credentials().keys(); + + if ( credentials()[ "oauthtoken" ].toString().isEmpty() || credentials()[ "oauthtokensecret" ].toString().isEmpty() ) + { + qDebug() << "TwitterSipPlugin has empty Twitter credentials; not connecting"; + return; + } + + if ( refreshTwitterAuth() ) + { + QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( m_twitterAuth.data(), this ); + connect( credVerifier, SIGNAL( parsedUser( const QTweetUser & ) ), SLOT( connectAuthVerifyReply( const QTweetUser & ) ) ); + credVerifier->verify(); + } +} + + +void +TwitterAccount::deauthenticate() +{ + if ( sipPlugin() ) + sipPlugin()->disconnectPlugin(); + + m_isAuthenticated = false; + emit nowDeauthenticated(); +} + + + +bool +TwitterAccount::refreshTwitterAuth() +{ + qDebug() << Q_FUNC_INFO << " begin"; + if( !m_twitterAuth.isNull() ) + delete m_twitterAuth.data(); + + Q_ASSERT( TomahawkUtils::nam() != 0 ); + qDebug() << Q_FUNC_INFO << " with nam " << TomahawkUtils::nam(); + m_twitterAuth = QWeakPointer< TomahawkOAuthTwitter >( new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ) ); + + if( m_twitterAuth.isNull() ) + return false; + + m_twitterAuth.data()->setOAuthToken( credentials()[ "oauthtoken" ].toString().toLatin1() ); + m_twitterAuth.data()->setOAuthTokenSecret( credentials()[ "oauthtokensecret" ].toString().toLatin1() ); + + return true; +} + + +void +TwitterAccount::connectAuthVerifyReply( const QTweetUser &user ) +{ + if ( user.id() == 0 ) + { + qDebug() << "TwitterAccount could not authenticate to Twitter"; + deauthenticate(); + } + else + { + tDebug() << "TwitterAccount successfully authenticated to Twitter as user " << user.screenName(); + QVariantHash config = configuration(); + config[ "screenname" ] = user.screenName(); + setConfiguration( config ); + sync(); + + sipPlugin()->connectPlugin(); + + m_isAuthenticated = true; + emit nowAuthenticated( m_twitterAuth, user ); + } +} +QPixmap +TwitterAccount::icon() const { + return QPixmap( ":/twitter-icon.png" ); +} + + +} + +} + +Q_EXPORT_PLUGIN2( Tomahawk::Accounts::AccountFactory, Tomahawk::Accounts::TwitterAccountFactory ) diff --git a/src/accounts/twitter/twitteraccount.h b/src/accounts/twitter/twitteraccount.h new file mode 100644 index 000000000..a1a1c2629 --- /dev/null +++ b/src/accounts/twitter/twitteraccount.h @@ -0,0 +1,103 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef TWITTERACCOUNT_H +#define TWITTERACCOUNT_H + +#include "twitterconfigwidget.h" +#include "tomahawkoauthtwitter.h" + +#include "sip/twittersip.h" +#include "accounts/accountdllmacro.h" +#include "accounts/Account.h" + +#define MYNAME "ACCOUNTTWITTER" + +namespace Tomahawk +{ + +namespace Accounts +{ + +class ACCOUNTDLLEXPORT TwitterAccountFactory : public AccountFactory +{ + Q_OBJECT + Q_INTERFACES( Tomahawk::Accounts::AccountFactory ) + +public: + TwitterAccountFactory() {} + virtual ~TwitterAccountFactory() {} + + QString prettyName() const { return "Twitter"; } + QString factoryId() const { return "twitteraccount"; } + QString description() const { return tr( "Connect to your Twitter followers." ); } + QPixmap icon() const { return QPixmap( ":/twitter-icon.png" ); } + AccountTypes types() const { return AccountTypes( SipType ); }; + Account* createAccount( const QString& pluginId = QString() ); +}; + +class ACCOUNTDLLEXPORT TwitterAccount : public Account +{ + Q_OBJECT + +public: + TwitterAccount( const QString &accountId ); + virtual ~TwitterAccount(); + + QPixmap icon() const; + + void authenticate(); + void deauthenticate(); + bool isAuthenticated() const { return m_isAuthenticated; } + + ConnectionState connectionState() const; + + Tomahawk::InfoSystem::InfoPlugin* infoPlugin() { return 0; } + SipPlugin* sipPlugin(); + + QWidget* configurationWidget() { return m_configWidget.data(); } + QWidget* aclWidget() { return 0; } + + bool refreshTwitterAuth(); + TomahawkOAuthTwitter* twitterAuth() const { return m_twitterAuth.data(); } + +signals: + void nowAuthenticated( const QWeakPointer< TomahawkOAuthTwitter >&, const QTweetUser &user ); + void nowDeauthenticated(); + +private slots: + void configDialogAuthedSignalSlot( bool authed ); + void connectAuthVerifyReply( const QTweetUser &user ); + +private: + QIcon m_icon; + bool m_isAuthenticated; + QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth; + QWeakPointer< TwitterConfigWidget > m_configWidget; + QWeakPointer< TwitterSipPlugin > m_twitterSipPlugin; + + // for settings access + friend class TwitterConfigWidget; +}; + +}; + +}; + +#endif diff --git a/src/sip/twitter/twitterconfigwidget.cpp b/src/accounts/twitter/twitterconfigwidget.cpp similarity index 61% rename from src/sip/twitter/twitterconfigwidget.cpp rename to src/accounts/twitter/twitterconfigwidget.cpp index 535dcc8d1..1603b4b82 100644 --- a/src/sip/twitter/twitterconfigwidget.cpp +++ b/src/accounts/twitter/twitterconfigwidget.cpp @@ -17,7 +17,7 @@ */ #include "twitterconfigwidget.h" -#include "twitter.h" +#include "twitteraccount.h" #include "ui_twitterconfigwidget.h" #include "tomahawksettings.h" @@ -33,38 +33,47 @@ #include "utils/logger.h" - -TwitterConfigWidget::TwitterConfigWidget( TwitterPlugin* plugin, QWidget *parent ) : - QWidget( parent ), - ui( new Ui::TwitterConfigWidget ), - m_plugin( plugin ) +namespace Tomahawk { - ui->setupUi( this ); - connect( ui->twitterAuthenticateButton, SIGNAL( pressed() ), +namespace Accounts +{ + +TwitterConfigWidget::TwitterConfigWidget( TwitterAccount* account, QWidget *parent ) : + QWidget( parent ), + m_ui( new Ui::TwitterConfigWidget ), + m_account( account ) +{ + m_ui->setupUi( this ); + + connect( m_ui->twitterAuthenticateButton, SIGNAL( pressed() ), this, SLOT( authDeauthTwitter() ) ); - connect( ui->twitterTweetGotTomahawkButton, SIGNAL( pressed() ), + connect( m_ui->twitterTweetGotTomahawkButton, SIGNAL( pressed() ), this, SLOT( startPostGotTomahawkStatus() ) ); - connect( ui->twitterTweetComboBox, SIGNAL( currentIndexChanged( int ) ), + connect( m_ui->twitterTweetComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( tweetComboBoxIndexChanged( int ) ) ); - ui->twitterTweetComboBox->setCurrentIndex( 0 ); - ui->twitterTweetGotTomahawkButton->setText( tr( "Tweet!" ) ); + m_ui->twitterTweetComboBox->setCurrentIndex( 0 ); + m_ui->twitterTweetGotTomahawkButton->setText( tr( "Tweet!" ) ); - if ( m_plugin->twitterOAuthToken().isEmpty() || m_plugin->twitterOAuthTokenSecret().isEmpty() || m_plugin->twitterScreenName().isEmpty() ) + QVariantHash credentials = m_account->credentials(); + + if ( credentials[ "oauthtoken" ].toString().isEmpty() || + credentials[ "oauthtokensecret" ].toString().isEmpty() || + credentials[ "username" ].toString().isEmpty() ) { - ui->twitterStatusLabel->setText( tr( "Status: No saved credentials" ) ); - ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); - ui->twitterSyncGroupBox->setVisible( false ); + m_ui->twitterStatusLabel->setText( tr( "Status: No saved credentials" ) ); + m_ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); + m_ui->twitterSyncGroupBox->setVisible( false ); emit twitterAuthed( false ); } else { - ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( m_plugin->twitterScreenName() ) ); - ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); - ui->twitterSyncGroupBox->setVisible( true ); - ui->twitterUserTweetLineEdit->setVisible( false ); + m_ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( m_account->credentials()[ "username" ].toString() ) ); + m_ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); + m_ui->twitterSyncGroupBox->setVisible( true ); + m_ui->twitterUserTweetLineEdit->setVisible( false ); emit twitterAuthed( true ); } @@ -73,13 +82,13 @@ TwitterConfigWidget::TwitterConfigWidget( TwitterPlugin* plugin, QWidget *parent TwitterConfigWidget::~TwitterConfigWidget() { - delete ui; + delete m_ui; } void TwitterConfigWidget::authDeauthTwitter() { - if ( ui->twitterAuthenticateButton->text() == tr( "Authenticate" ) ) //FIXME: don't rely on UI strings here! + if ( m_ui->twitterAuthenticateButton->text() == tr( "Authenticate" ) ) //FIXME: don't rely on UI strings here! authenticateTwitter(); else deauthenticateTwitter(); @@ -92,8 +101,10 @@ TwitterConfigWidget::authenticateTwitter() TomahawkOAuthTwitter *twitAuth = new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ); twitAuth->authorizePin(); - m_plugin->setTwitterOAuthToken( twitAuth->oauthToken() ); - m_plugin->setTwitterOAuthTokenSecret( twitAuth->oauthTokenSecret() ); + QVariantHash credentials = m_account->credentials(); + credentials[ "oauthtoken" ] = twitAuth->oauthToken(); + credentials[ "oauthtokensecret" ] = twitAuth->oauthTokenSecret(); + m_account->setCredentials( credentials ); QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( twitAuth, this ); connect( credVerifier, SIGNAL( parsedUser( const QTweetUser & ) ), SLOT( authenticateVerifyReply( const QTweetUser & ) ) ); @@ -112,18 +123,21 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) return; } - m_plugin->setTwitterScreenName( user.screenName() ); - m_plugin->setTwitterCachedFriendsSinceId( 0 ); - m_plugin->setTwitterCachedMentionsSinceId( 0 ); + QVariantHash credentials = m_account->credentials(); + credentials[ "username" ] = user.screenName(); + m_account->setCredentials( credentials ); - ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( m_plugin->twitterScreenName() ) ); - ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); - ui->twitterSyncGroupBox->setVisible( true ); - ui->twitterTweetComboBox->setCurrentIndex( 0 ); - ui->twitterUserTweetLineEdit->setVisible( false ); - ui->twitterTweetGotTomahawkButton->setText( tr( "Tweet!" ) ); + QVariantHash configuration = m_account->configuration(); + configuration[ "sipcachedfriendssinceid" ] = 0; + configuration[ "sipcachedmentionssinceid" ] = 0; + m_account->setConfiguration( configuration ); - m_plugin->connectPlugin( false ); + m_ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( user.screenName() ) ); + m_ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); + m_ui->twitterSyncGroupBox->setVisible( true ); + m_ui->twitterTweetComboBox->setCurrentIndex( 0 ); + m_ui->twitterUserTweetLineEdit->setVisible( false ); + m_ui->twitterTweetGotTomahawkButton->setText( tr( "Tweet!" ) ); emit twitterAuthed( true ); emit sizeHintChanged(); @@ -134,7 +148,7 @@ TwitterConfigWidget::authenticateVerifyError( QTweetNetBase::ErrorCode code, con { qDebug() << Q_FUNC_INFO; qDebug() << "Error validating credentials, error code is " << code << ", error message is " << errorMsg; - ui->twitterStatusLabel->setText(tr("Status: Error validating credentials")); + m_ui->twitterStatusLabel->setText(tr("Status: Error validating credentials")); emit twitterAuthed( false ); return; } @@ -143,13 +157,15 @@ void TwitterConfigWidget::deauthenticateTwitter() { qDebug() << Q_FUNC_INFO; - m_plugin->setTwitterOAuthToken( QString() ); - m_plugin->setTwitterOAuthTokenSecret( QString() ); - m_plugin->setTwitterScreenName( QString() ); + QVariantHash credentials = m_account->credentials(); + credentials[ "oauthtoken" ] = QString(); + credentials[ "oauthtokensecret" ] = QString(); + credentials[ "username" ] = QString(); + m_account->setCredentials( credentials ); - ui->twitterStatusLabel->setText(tr("Status: No saved credentials")); - ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); - ui->twitterSyncGroupBox->setVisible( false ); + m_ui->twitterStatusLabel->setText(tr("Status: No saved credentials")); + m_ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); + m_ui->twitterSyncGroupBox->setVisible( false ); emit twitterAuthed( false ); emit sizeHintChanged(); @@ -159,41 +175,45 @@ void TwitterConfigWidget::tweetComboBoxIndexChanged( int index ) { Q_UNUSED( index ); - if ( ui->twitterTweetComboBox->currentText() == tr( "Global Tweet" ) ) //FIXME: use data! - ui->twitterUserTweetLineEdit->setVisible( false ); + if ( m_ui->twitterTweetComboBox->currentText() == tr( "Global Tweet" ) ) //FIXME: use data! + m_ui->twitterUserTweetLineEdit->setVisible( false ); else - ui->twitterUserTweetLineEdit->setVisible( true ); + m_ui->twitterUserTweetLineEdit->setVisible( true ); - if ( ui->twitterTweetComboBox->currentText() == tr( "Direct Message" ) ) //FIXME: use data! - ui->twitterTweetGotTomahawkButton->setText( tr( "Send Message!" ) ); - else if ( ui->twitterTweetComboBox->currentText() == tr( "@Mention" ) ) - ui->twitterTweetGotTomahawkButton->setText( tr( "Send Mention!" ) ); + if ( m_ui->twitterTweetComboBox->currentText() == tr( "Direct Message" ) ) //FIXME: use data! + m_ui->twitterTweetGotTomahawkButton->setText( tr( "Send Message!" ) ); + else if ( m_ui->twitterTweetComboBox->currentText() == tr( "@Mention" ) ) + m_ui->twitterTweetGotTomahawkButton->setText( tr( "Send Mention!" ) ); else - ui->twitterTweetGotTomahawkButton->setText( tr( "Tweet!" ) ); + m_ui->twitterTweetGotTomahawkButton->setText( tr( "Tweet!" ) ); } void TwitterConfigWidget::startPostGotTomahawkStatus() { qDebug() << Q_FUNC_INFO; - m_postGTtype = ui->twitterTweetComboBox->currentText(); + m_postGTtype = m_ui->twitterTweetComboBox->currentText(); - if ( m_postGTtype != "Global Tweet" && ( ui->twitterUserTweetLineEdit->text().isEmpty() || ui->twitterUserTweetLineEdit->text() == "@" ) ) + if ( m_postGTtype != "Global Tweet" && ( m_ui->twitterUserTweetLineEdit->text().isEmpty() || m_ui->twitterUserTweetLineEdit->text() == "@" ) ) { QMessageBox::critical( this, tr("Tweetin' Error"), tr("You must enter a user name for this type of tweet.") ); return; } qDebug() << "Posting Got Tomahawk status"; - if ( m_plugin->twitterOAuthToken().isEmpty() || m_plugin->twitterOAuthTokenSecret().isEmpty() || m_plugin->twitterScreenName().isEmpty() ) + QVariantHash credentials = m_account->credentials(); + + if ( credentials[ "oauthtoken" ].toString().isEmpty() || + credentials[ "oauthtokensecret" ].toString().isEmpty() || + credentials[ "username" ].toString().isEmpty() ) { QMessageBox::critical( this, tr("Tweetin' Error"), tr("Your saved credentials could not be loaded.\nYou may wish to try re-authenticating.") ); emit twitterAuthed( false ); return; } TomahawkOAuthTwitter *twitAuth = new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ); - twitAuth->setOAuthToken( m_plugin->twitterOAuthToken().toLatin1() ); - twitAuth->setOAuthTokenSecret( m_plugin->twitterOAuthTokenSecret().toLatin1() ); + twitAuth->setOAuthToken( credentials[ "oauthtoken" ].toString().toLatin1() ); + twitAuth->setOAuthTokenSecret( credentials[ "oauthtokensecret" ].toString().toLatin1() ); QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( twitAuth, this ); connect( credVerifier, SIGNAL( parsedUser(const QTweetUser &) ), SLOT( postGotTomahawkStatusAuthVerifyReply(const QTweetUser &) ) ); credVerifier->verify(); @@ -209,10 +229,10 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use emit twitterAuthed( false ); return; } - m_plugin->setTwitterScreenName( user.screenName() ); TomahawkOAuthTwitter *twitAuth = new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ); - twitAuth->setOAuthToken( m_plugin->twitterOAuthToken().toLatin1() ); - twitAuth->setOAuthTokenSecret( m_plugin->twitterOAuthTokenSecret().toLatin1() ); + QVariantHash credentials = m_account->credentials(); + twitAuth->setOAuthToken( credentials[ "oauthtoken" ].toString().toLatin1() ); + twitAuth->setOAuthTokenSecret( credentials[ "oauthtokensecret" ].toString().toLatin1() ); if ( m_postGTtype != "Direct Message" ) { QTweetStatusUpdate *statUpdate = new QTweetStatusUpdate( twitAuth, this ); @@ -222,7 +242,7 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use QString message = QString( "Got Tomahawk? {" ) + Database::instance()->dbid() + QString( "} (" ) + uuid.mid( 1, 8 ) + QString( ")" ) + QString( " http://gettomahawk.com" ); if ( m_postGTtype == "@Mention" ) { - QString user = ui->twitterUserTweetLineEdit->text(); + QString user = m_ui->twitterUserTweetLineEdit->text(); if ( user.startsWith( "@" ) ) user.remove( 0, 1 ); message = QString( "@" ) + user + QString( " " ) + message; @@ -236,7 +256,7 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use connect( statUpdate, SIGNAL( error(QTweetNetBase::ErrorCode, const QString&) ), SLOT( postGotTomahawkStatusUpdateError(QTweetNetBase::ErrorCode, const QString &) ) ); QString uuid = QUuid::createUuid(); QString message = QString( "Got Tomahawk? {" ) + Database::instance()->dbid() + QString( "} (" ) + uuid.mid( 1, 8 ) + QString( ")" ) + QString( " http://gettomahawk.com" ); - QString user = ui->twitterUserTweetLineEdit->text(); + QString user = m_ui->twitterUserTweetLineEdit->text(); if ( user.startsWith( "@" ) ) user.remove( 0, 1 ); statUpdate->post( user, message ); @@ -268,3 +288,7 @@ TwitterConfigWidget::postGotTomahawkStatusUpdateError( QTweetNetBase::ErrorCode qDebug() << "Error posting Got Tomahawk message, error code is " << code << ", error message is " << errorMsg; QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); } + +} + +} \ No newline at end of file diff --git a/src/sip/twitter/twitterconfigwidget.h b/src/accounts/twitter/twitterconfigwidget.h similarity index 80% rename from src/sip/twitter/twitterconfigwidget.h rename to src/accounts/twitter/twitterconfigwidget.h index d06c08737..3b8048146 100644 --- a/src/sip/twitter/twitterconfigwidget.h +++ b/src/accounts/twitter/twitterconfigwidget.h @@ -16,10 +16,10 @@ * along with Tomahawk. If not, see . */ -#ifndef TWITTERCONFIGWIDGET_H -#define TWITTERCONFIGWIDGET_H +#ifndef TWITTERACCOUNTCONFIGWIDGET_H +#define TWITTERACCOUNTCONFIGWIDGET_H -#include "sip/SipPlugin.h" +#include "accounts/accountdllmacro.h" #include #include @@ -28,24 +28,33 @@ #include -class TwitterPlugin; - -namespace Ui { +namespace Ui +{ class TwitterConfigWidget; } -class TwitterConfigWidget : public QWidget +namespace Tomahawk +{ + +namespace Accounts +{ + +class TwitterAccount; + + +class ACCOUNTDLLEXPORT TwitterConfigWidget : public QWidget { Q_OBJECT public: - explicit TwitterConfigWidget( TwitterPlugin* plugin = 0, QWidget *parent = 0 ); - ~TwitterConfigWidget(); + explicit TwitterConfigWidget( TwitterAccount* account = 0, QWidget *parent = 0 ); + virtual ~TwitterConfigWidget(); signals: void twitterAuthed( bool authed ); void sizeHintChanged(); + private slots: void authDeauthTwitter(); void startPostGotTomahawkStatus(); @@ -61,9 +70,13 @@ private: void authenticateTwitter(); void deauthenticateTwitter(); - Ui::TwitterConfigWidget *ui; - TwitterPlugin *m_plugin; + Ui::TwitterConfigWidget *m_ui; + TwitterAccount *m_account; QString m_postGTtype; }; +} + +} + #endif // TWITTERCONFIGWIDGET_H diff --git a/src/sip/twitter/twitterconfigwidget.ui b/src/accounts/twitter/twitterconfigwidget.ui similarity index 100% rename from src/sip/twitter/twitterconfigwidget.ui rename to src/accounts/twitter/twitterconfigwidget.ui diff --git a/src/accounts/xmpp/CMakeLists.txt b/src/accounts/xmpp/CMakeLists.txt new file mode 100644 index 000000000..ff1f5da2a --- /dev/null +++ b/src/accounts/xmpp/CMakeLists.txt @@ -0,0 +1,64 @@ +project( tomahawk ) + +include( ${QT_USE_FILE} ) +add_definitions( ${QT_DEFINITIONS} ) +add_definitions( -DQT_PLUGIN ) +add_definitions( -DQT_SHARED ) +add_definitions( -DACCOUNTDLLEXPORT_PRO ) + +set( xmppAccountSources + xmppaccount.cpp + xmppconfigwidget.cpp + sip/xmppsip.cpp + sip/tomahawkxmppmessage.cpp + sip/tomahawkxmppmessagefactory.cpp + sip/avatarmanager.cpp + sip/xmlconsole.cpp +) + +set( xmppAccountHeaders + xmppaccount.h + xmppconfigwidget.h + sip/xmppsip.h + sip/avatarmanager.h + sip/xmlconsole.h +) + +set( xmppAccountUI + xmppconfigwidget.ui + sip/xmlconsole.ui +) + +include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. + ${QT_INCLUDE_DIR} + ${LIBJREEN_INCLUDE_DIR} +) + +qt4_wrap_cpp( xmppAccountMoc ${xmppAccountHeaders} ) +qt4_wrap_ui( xmppAccountUI_H ${xmppAccountUI} ) +qt4_add_resources( RC_SRCS "resources.qrc" ) +add_library( tomahawk_account_xmpp MODULE ${xmppAccountUI_H} ${xmppAccountSources} ${xmppAccountMoc} ${RC_SRCS} ) + +IF( WIN32 ) +SET( OS_SPECIFIC_LINK_LIBRARIES + ${OS_SPECIFIC_LINK_LIBRARIES} + "winmm.dll" + "iphlpapi.a" +) +ENDIF( WIN32 ) + +target_link_libraries( tomahawk_account_xmpp + ${TOMAHAWK_LIBRARIES} + ${LIBJREEN_LIBRARY} + ${QT_LIBRARIES} + ${OS_SPECIFIC_LINK_LIBRARIES} +) + +IF( APPLE ) +# SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) +ENDIF( APPLE ) + +install( TARGETS tomahawk_account_xmpp DESTINATION ${CMAKE_INSTALL_LIBDIR} ) + +add_subdirectory(googlewrapper) + diff --git a/src/accounts/xmpp/googlewrapper/CMakeLists.txt b/src/accounts/xmpp/googlewrapper/CMakeLists.txt new file mode 100644 index 000000000..a2ce9560b --- /dev/null +++ b/src/accounts/xmpp/googlewrapper/CMakeLists.txt @@ -0,0 +1,37 @@ + +# fake google plugin + +set( googleHeaders + ../xmppaccount.h + ../xmppconfigwidget.h + ../sip/xmppsip.h + ../sip/avatarmanager.h + ../sip/xmlconsole.h + googlewrapper.h ) + +set( googleSources + ../xmppaccount.cpp + ../xmppconfigwidget.cpp + ../sip/xmppsip.cpp + ../sip/tomahawkxmppmessage.cpp + ../sip/tomahawkxmppmessagefactory.cpp + ../sip/avatarmanager.cpp + ../sip/xmlconsole.cpp + + googlewrapper.cpp ) + +add_definitions(-DGOOGLE_WRAPPER) + +qt4_add_resources( RCX_SRCS "resources.qrc" ) + +qt4_wrap_cpp( googleMoc ${googleHeaders} ) +add_library( tomahawk_account_google MODULE ${googleSources} ${googleMoc} ${googleMoc} ${RCX_SRCS} ) + +target_link_libraries( tomahawk_account_google + ${QT_LIBRARIES} + ${LIBJREEN_LIBRARY} + ${OS_SPECIFIC_LINK_LIBRARIES} + tomahawklib +) + +install( TARGETS tomahawk_account_google DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/src/sip/jabber/googlewrapper/gmail-logo.png b/src/accounts/xmpp/googlewrapper/gmail-logo.png similarity index 100% rename from src/sip/jabber/googlewrapper/gmail-logo.png rename to src/accounts/xmpp/googlewrapper/gmail-logo.png diff --git a/src/accounts/xmpp/googlewrapper/googlewrapper.cpp b/src/accounts/xmpp/googlewrapper/googlewrapper.cpp new file mode 100644 index 000000000..858565aba --- /dev/null +++ b/src/accounts/xmpp/googlewrapper/googlewrapper.cpp @@ -0,0 +1,122 @@ +/* + + Copyright (C) 2011 Leo Franchi + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "googlewrapper.h" +#include "xmppconfigwidget.h" +#include "ui_xmppconfigwidget.h" + +#include "utils/tomahawkutilsgui.h" + +#include +#include + +using namespace Tomahawk; +using namespace Accounts; + +Account* +GoogleWrapperFactory::createAccount( const QString& pluginId ) +{ + return new GoogleWrapper( pluginId.isEmpty() ? generateId( factoryId() ) : pluginId ); +} + + +QPixmap +GoogleWrapperFactory::icon() const +{ + return QPixmap( ":/gmail-logo.png" ); +} + +GoogleWrapperSip::GoogleWrapperSip( Account* account ) + : XmppSipPlugin( account ) +{ + +} + +GoogleWrapperSip::~GoogleWrapperSip() +{ +} + + +void +GoogleWrapperSip::showAddFriendDialog() +{ + bool ok; + QString id = QInputDialog::getText( TomahawkUtils::tomahawkWindow(), tr( "Add Friend" ), + tr( "Enter Google Address:" ), QLineEdit::Normal, "", &ok ); + if ( !ok ) + return; + + qDebug() << "Attempting to add google contact to roster:" << id; + addContact( id ); +} + + +QString +GoogleWrapperSip::defaultSuffix() const +{ + return "@gmail.com"; +} + + +GoogleWrapper::GoogleWrapper ( const QString& pluginID ) + : XmppAccount ( pluginID ) +{ + XmppConfigWidget* config = static_cast< XmppConfigWidget* >( m_configWidget.data() ); + config->m_ui->headerLabel->setText( tr( "Configure this Google Account" ) ); + config->m_ui->emailLabel->setText( tr( "Google Address" ) ); + config->m_ui->xmppBlurb->setText( tr( "Enter your Google login to connect with your friends using Tomahawk!" ) ); + config->m_ui->xmppUsername->setPlaceholderText( tr( "username@gmail.com" ) ); + config->m_ui->logoLabel->setPixmap( QPixmap( ":/gmail-logo.png" ) ); + config->m_ui->xmppServer->setText( "talk.google.com" ); + config->m_ui->xmppPort->setValue( 5222 ); + config->m_ui->groupBoxXmppAdvanced->hide(); +} + +GoogleWrapper::~GoogleWrapper() +{ + delete m_sipPlugin.data(); +} + + +QPixmap +GoogleWrapper::icon() const +{ + return QPixmap( ":/gmail-logo.png" ); +} + + +SipPlugin* +GoogleWrapper::sipPlugin() +{ + if ( m_xmppSipPlugin.isNull() ) + { + m_xmppSipPlugin = QWeakPointer< XmppSipPlugin >( new GoogleWrapperSip( const_cast< GoogleWrapper* >( this ) ) ); + + connect( m_xmppSipPlugin.data(), SIGNAL( stateChanged( Tomahawk::Accounts::Account::ConnectionState ) ), this, SIGNAL( connectionStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ) ); + connect( m_xmppSipPlugin.data(), SIGNAL( error( int, QString ) ), this, SIGNAL( error( int, QString ) ) ); + + return m_xmppSipPlugin.data(); + } + return m_xmppSipPlugin.data(); +} + + +#ifdef GOOGLE_WRAPPER +Q_EXPORT_PLUGIN2( Tomahawk::Accounts::AccountFactory, Tomahawk::Accounts::GoogleWrapperFactory ) +#endif diff --git a/src/sip/jabber/googlewrapper/googlewrapper.h b/src/accounts/xmpp/googlewrapper/googlewrapper.h similarity index 50% rename from src/sip/jabber/googlewrapper/googlewrapper.h rename to src/accounts/xmpp/googlewrapper/googlewrapper.h index f34238c6a..833cf3be2 100644 --- a/src/sip/jabber/googlewrapper/googlewrapper.h +++ b/src/accounts/xmpp/googlewrapper/googlewrapper.h @@ -19,39 +19,63 @@ #ifndef GOOGLEWRAPPER_H #define GOOGLEWRAPPER_H -#include "sip/jabber/jabber.h" +#include "xmppaccount.h" -class SIPDLLEXPORT GoogleWrapperFactory : public SipPluginFactory +namespace Tomahawk +{ + +namespace Accounts +{ + +class ACCOUNTDLLEXPORT GoogleWrapperFactory : public XmppAccountFactory { Q_OBJECT - Q_INTERFACES( SipPluginFactory ) + Q_INTERFACES( Tomahawk::Accounts::AccountFactory ) public: GoogleWrapperFactory() {} virtual ~GoogleWrapperFactory() {} virtual QString prettyName() const { return "Google"; } - virtual QString factoryId() const { return "sipgoogle"; } - virtual QIcon icon() const; - virtual SipPlugin* createPlugin( const QString& pluginId ); + virtual QString factoryId() const { return "googleaccount"; } + QString description() const { return tr( "Connect to GChat to find your friends" ); } + virtual QPixmap icon() const; + virtual Account* createAccount( const QString& pluginId ); }; -class SIPDLLEXPORT GoogleWrapper : public JabberPlugin +class ACCOUNTDLLEXPORT GoogleWrapperSip : public XmppSipPlugin { Q_OBJECT public: - GoogleWrapper( const QString& pluginID ); - virtual ~GoogleWrapper() {} - - virtual const QString name() const { return QString( "Google" ); } - virtual const QString friendlyName() const { return "Google"; } - virtual QIcon icon() const; - -protected: - QString defaultSuffix() const; + GoogleWrapperSip( Tomahawk::Accounts::Account* account ); + virtual ~GoogleWrapperSip(); public slots: void showAddFriendDialog(); + +protected: + QString defaultSuffix() const; }; +class ACCOUNTDLLEXPORT GoogleWrapper : public XmppAccount +{ + Q_OBJECT +public: + GoogleWrapper( const QString& pluginID ); + virtual ~GoogleWrapper(); + + virtual const QString name() const { return QString( "Google" ); } + virtual const QString friendlyName() const { return "Google"; } + virtual QPixmap icon() const; + + virtual SipPlugin* sipPlugin(); + +private: + QWeakPointer< GoogleWrapperSip > m_sipPlugin; +}; + +} + +} + #endif // GOOGLEWRAPPER_H diff --git a/src/sip/jabber/googlewrapper/resources.qrc b/src/accounts/xmpp/googlewrapper/resources.qrc similarity index 100% rename from src/sip/jabber/googlewrapper/resources.qrc rename to src/accounts/xmpp/googlewrapper/resources.qrc diff --git a/src/sip/jabber/resources.qrc b/src/accounts/xmpp/resources.qrc similarity index 69% rename from src/sip/jabber/resources.qrc rename to src/accounts/xmpp/resources.qrc index 9700ac51f..7240db9d3 100644 --- a/src/sip/jabber/resources.qrc +++ b/src/accounts/xmpp/resources.qrc @@ -1,5 +1,5 @@ -jabber-icon.png +xmpp-icon.png diff --git a/src/sip/jabber/avatarmanager.cpp b/src/accounts/xmpp/sip/avatarmanager.cpp similarity index 100% rename from src/sip/jabber/avatarmanager.cpp rename to src/accounts/xmpp/sip/avatarmanager.cpp diff --git a/src/sip/jabber/avatarmanager.h b/src/accounts/xmpp/sip/avatarmanager.h similarity index 92% rename from src/sip/jabber/avatarmanager.h rename to src/accounts/xmpp/sip/avatarmanager.h index ea755d76e..c16711453 100644 --- a/src/sip/jabber/avatarmanager.h +++ b/src/accounts/xmpp/sip/avatarmanager.h @@ -21,12 +21,12 @@ #include -#include -#include +#include +#include -#include "../sipdllmacro.h" +#include "accounts/accountdllmacro.h" -class SIPDLLEXPORT AvatarManager : public QObject +class ACCOUNTDLLEXPORT AvatarManager : public QObject { Q_OBJECT diff --git a/src/sip/jabber/tomahawksipmessage.cpp b/src/accounts/xmpp/sip/tomahawkxmppmessage.cpp similarity index 66% rename from src/sip/jabber/tomahawksipmessage.cpp rename to src/accounts/xmpp/sip/tomahawkxmppmessage.cpp index 68ecfea79..1b0cf0804 100644 --- a/src/sip/jabber/tomahawksipmessage.cpp +++ b/src/accounts/xmpp/sip/tomahawkxmppmessage.cpp @@ -16,12 +16,12 @@ * along with Tomahawk. If not, see . */ -#include "tomahawksipmessage.h" +#include "tomahawkxmppmessage.h" #include "utils/logger.h" -class TomahawkSipMessagePrivate +class TomahawkXmppMessagePrivate { public: QString ip; @@ -31,9 +31,9 @@ public: bool visible; }; -TomahawkSipMessage::TomahawkSipMessage(const QString &ip, unsigned int port, const QString &uniqname, const QString &key) : d_ptr(new TomahawkSipMessagePrivate) +TomahawkXmppMessage::TomahawkXmppMessage(const QString &ip, unsigned int port, const QString &uniqname, const QString &key) : d_ptr(new TomahawkXmppMessagePrivate) { - Q_D(TomahawkSipMessage); + Q_D(TomahawkXmppMessage); d->ip = ip; d->port = port; d->uniqname = uniqname; @@ -41,39 +41,39 @@ TomahawkSipMessage::TomahawkSipMessage(const QString &ip, unsigned int port, con d->visible = true; } -TomahawkSipMessage::TomahawkSipMessage() : d_ptr(new TomahawkSipMessagePrivate) +TomahawkXmppMessage::TomahawkXmppMessage() : d_ptr(new TomahawkXmppMessagePrivate) { - Q_D(TomahawkSipMessage); + Q_D(TomahawkXmppMessage); d->visible = false; d->port = -1; } -TomahawkSipMessage::~TomahawkSipMessage() +TomahawkXmppMessage::~TomahawkXmppMessage() { } -const QString TomahawkSipMessage::ip() const +const QString TomahawkXmppMessage::ip() const { return d_func()->ip; } -unsigned int TomahawkSipMessage::port() const +unsigned int TomahawkXmppMessage::port() const { return d_func()->port; } -const QString TomahawkSipMessage::uniqname() const +const QString TomahawkXmppMessage::uniqname() const { return d_func()->uniqname; } -const QString TomahawkSipMessage::key() const +const QString TomahawkXmppMessage::key() const { return d_func()->key; } -bool TomahawkSipMessage::visible() const +bool TomahawkXmppMessage::visible() const { return d_func()->visible; } diff --git a/src/sip/jabber/tomahawksipmessage.h b/src/accounts/xmpp/sip/tomahawkxmppmessage.h similarity index 74% rename from src/sip/jabber/tomahawksipmessage.h rename to src/accounts/xmpp/sip/tomahawkxmppmessage.h index 0dbdb0ad0..8661c30bd 100644 --- a/src/sip/jabber/tomahawksipmessage.h +++ b/src/accounts/xmpp/sip/tomahawkxmppmessage.h @@ -23,20 +23,20 @@ #define TOMAHAWK_SIP_MESSAGE_NS QLatin1String("http://www.tomhawk-player.org/sip/transports") -#include "../sipdllmacro.h" +#include "accounts/accountdllmacro.h" -class TomahawkSipMessagePrivate; -class SIPDLLEXPORT TomahawkSipMessage : public Jreen::Payload +class TomahawkXmppMessagePrivate; +class ACCOUNTDLLEXPORT TomahawkXmppMessage : public Jreen::Payload { - J_PAYLOAD(TomahawkSipMessage) - Q_DECLARE_PRIVATE(TomahawkSipMessage) + J_PAYLOAD(TomahawkXmppMessage) + Q_DECLARE_PRIVATE(TomahawkXmppMessage) public: // sets visible to true - TomahawkSipMessage(const QString &ip, unsigned int port, const QString &uniqname, const QString &key); + TomahawkXmppMessage(const QString &ip, unsigned int port, const QString &uniqname, const QString &key); // sets visible to false as we dont have any extra information - TomahawkSipMessage(); - ~TomahawkSipMessage(); + TomahawkXmppMessage(); + ~TomahawkXmppMessage(); const QString ip() const; unsigned int port() const; @@ -44,7 +44,7 @@ class SIPDLLEXPORT TomahawkSipMessage : public Jreen::Payload const QString key() const; bool visible() const; private: - QScopedPointer d_ptr; + QScopedPointer d_ptr; }; #endif // ENTITYTIME_H diff --git a/src/sip/jabber/tomahawksipmessagefactory.cpp b/src/accounts/xmpp/sip/tomahawkxmppmessagefactory.cpp similarity index 80% rename from src/sip/jabber/tomahawksipmessagefactory.cpp rename to src/accounts/xmpp/sip/tomahawkxmppmessagefactory.cpp index a45cc27f2..b20d4756a 100644 --- a/src/sip/jabber/tomahawksipmessagefactory.cpp +++ b/src/accounts/xmpp/sip/tomahawkxmppmessagefactory.cpp @@ -16,7 +16,7 @@ * along with Tomahawk. If not, see . */ -#include "tomahawksipmessagefactory.h" +#include "tomahawkxmppmessagefactory.h" #include #include @@ -26,29 +26,29 @@ using namespace Jreen; -TomahawkSipMessageFactory::TomahawkSipMessageFactory() +TomahawkXmppMessageFactory::TomahawkXmppMessageFactory() { m_depth = 0; m_state = AtNowhere; } -TomahawkSipMessageFactory::~TomahawkSipMessageFactory() +TomahawkXmppMessageFactory::~TomahawkXmppMessageFactory() { } -QStringList TomahawkSipMessageFactory::features() const +QStringList TomahawkXmppMessageFactory::features() const { return QStringList(TOMAHAWK_SIP_MESSAGE_NS); } -bool TomahawkSipMessageFactory::canParse(const QStringRef &name, const QStringRef &uri, const QXmlStreamAttributes &attributes) +bool TomahawkXmppMessageFactory::canParse(const QStringRef &name, const QStringRef &uri, const QXmlStreamAttributes &attributes) { Q_UNUSED(uri); Q_UNUSED(attributes); return name == QLatin1String("tomahawk") && uri == TOMAHAWK_SIP_MESSAGE_NS; } -void TomahawkSipMessageFactory::handleStartElement(const QStringRef &name, const QStringRef &uri, +void TomahawkXmppMessageFactory::handleStartElement(const QStringRef &name, const QStringRef &uri, const QXmlStreamAttributes &attributes) { m_depth++; @@ -83,7 +83,7 @@ void TomahawkSipMessageFactory::handleStartElement(const QStringRef &name, const Q_UNUSED(attributes); } -void TomahawkSipMessageFactory::handleEndElement(const QStringRef &name, const QStringRef &uri) +void TomahawkXmppMessageFactory::handleEndElement(const QStringRef &name, const QStringRef &uri) { if (m_depth == 3) m_state = AtNowhere; @@ -92,7 +92,7 @@ void TomahawkSipMessageFactory::handleEndElement(const QStringRef &name, const Q m_depth--; } -void TomahawkSipMessageFactory::handleCharacterData(const QStringRef &text) +void TomahawkXmppMessageFactory::handleCharacterData(const QStringRef &text) { /*if (m_state == AtUtc) { //m_utc = Util::fromStamp(text.toString()); @@ -105,9 +105,9 @@ void TomahawkSipMessageFactory::handleCharacterData(const QStringRef &text) Q_UNUSED(text); } -void TomahawkSipMessageFactory::serialize(Payload *extension, QXmlStreamWriter *writer) +void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter *writer) { - TomahawkSipMessage *sipMessage = se_cast(extension); + TomahawkXmppMessage *sipMessage = se_cast(extension); writer->writeStartElement(QLatin1String("tomahawk")); writer->writeDefaultNamespace(TOMAHAWK_SIP_MESSAGE_NS); @@ -137,10 +137,10 @@ void TomahawkSipMessageFactory::serialize(Payload *extension, QXmlStreamWriter * writer->writeEndElement(); } -Payload::Ptr TomahawkSipMessageFactory::createPayload() +Payload::Ptr TomahawkXmppMessageFactory::createPayload() { if(m_visible) - return Payload::Ptr(new TomahawkSipMessage(m_ip, m_port, m_uniqname, m_key)); + return Payload::Ptr(new TomahawkXmppMessage(m_ip, m_port, m_uniqname, m_key)); else - return Payload::Ptr(new TomahawkSipMessage()); + return Payload::Ptr(new TomahawkXmppMessage()); } diff --git a/src/sip/jabber/tomahawksipmessagefactory.h b/src/accounts/xmpp/sip/tomahawkxmppmessagefactory.h similarity index 86% rename from src/sip/jabber/tomahawksipmessagefactory.h rename to src/accounts/xmpp/sip/tomahawkxmppmessagefactory.h index a9e7a0db0..ab713f30f 100644 --- a/src/sip/jabber/tomahawksipmessagefactory.h +++ b/src/accounts/xmpp/sip/tomahawkxmppmessagefactory.h @@ -19,17 +19,17 @@ #ifndef ENTITYTIMEFACTORY_P_H #define ENTITYTIMEFACTORY_P_H -#include "tomahawksipmessage.h" +#include "tomahawkxmppmessage.h" #include -#include "../sipdllmacro.h" +#include "accounts/accountdllmacro.h" -class SIPDLLEXPORT TomahawkSipMessageFactory : public Jreen::PayloadFactory +class ACCOUNTDLLEXPORT TomahawkXmppMessageFactory : public Jreen::PayloadFactory { public: - TomahawkSipMessageFactory(); - virtual ~TomahawkSipMessageFactory(); + TomahawkXmppMessageFactory(); + virtual ~TomahawkXmppMessageFactory(); QStringList features() const; bool canParse(const QStringRef &name, const QStringRef &uri, const QXmlStreamAttributes &attributes); void handleStartElement(const QStringRef &name, const QStringRef &uri, const QXmlStreamAttributes &attributes); diff --git a/src/sip/jabber/xmlconsole.cpp b/src/accounts/xmpp/sip/xmlconsole.cpp similarity index 100% rename from src/sip/jabber/xmlconsole.cpp rename to src/accounts/xmpp/sip/xmlconsole.cpp diff --git a/src/sip/jabber/xmlconsole.h b/src/accounts/xmpp/sip/xmlconsole.h similarity index 95% rename from src/sip/jabber/xmlconsole.h rename to src/accounts/xmpp/sip/xmlconsole.h index 2cb617b5d..2f8e27846 100644 --- a/src/sip/jabber/xmlconsole.h +++ b/src/accounts/xmpp/sip/xmlconsole.h @@ -23,18 +23,18 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include namespace Ui { class XmlConsole; } -#include "../sipdllmacro.h" +#include "accounts/accountdllmacro.h" -class SIPDLLEXPORT XmlConsole : public QWidget, public Jreen::XmlStreamHandler +class ACCOUNTDLLEXPORT XmlConsole : public QWidget, public Jreen::XmlStreamHandler { Q_OBJECT diff --git a/src/sip/jabber/xmlconsole.ui b/src/accounts/xmpp/sip/xmlconsole.ui similarity index 100% rename from src/sip/jabber/xmlconsole.ui rename to src/accounts/xmpp/sip/xmlconsole.ui diff --git a/src/sip/jabber/jabber.cpp b/src/accounts/xmpp/sip/xmppsip.cpp similarity index 77% rename from src/sip/jabber/jabber.cpp rename to src/accounts/xmpp/sip/xmppsip.cpp index fa3295a4f..d89111ccb 100644 --- a/src/sip/jabber/jabber.cpp +++ b/src/accounts/xmpp/sip/xmppsip.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Dominik Schmidt * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,14 +18,12 @@ * along with Tomahawk. If not, see . */ -#include "jabber.h" -#include "ui_configwidget.h" +#include "xmppsip.h" #include "config.h" -#include "tomahawksettings.h" -#include "tomahawksipmessage.h" -#include "tomahawksipmessagefactory.h" +#include "tomahawkxmppmessage.h" +#include "tomahawkxmppmessagefactory.h" #include #include @@ -43,6 +42,10 @@ #include #include +#include +#include +#include + #ifndef ENABLE_HEADLESS #include #include @@ -52,59 +55,33 @@ #include #include "utils/logger.h" -using namespace Jreen; +using namespace Tomahawk; +using namespace Accounts; -SipPlugin* -JabberFactory::createPlugin( const QString& pluginId ) -{ - return new JabberPlugin( pluginId.isEmpty() ? generateId() : pluginId ); -} - - -QIcon -JabberFactory::icon() const -{ - return QIcon( ":/jabber-icon.png" ); -} - - -JabberPlugin::JabberPlugin( const QString& pluginId ) - : SipPlugin( pluginId ) - , m_state( Disconnected ) +XmppSipPlugin::XmppSipPlugin( Account *account ) + : SipPlugin( account ) #ifndef ENABLE_HEADLESS , m_menu( 0 ) , m_xmlConsole( 0 ) #endif + , m_state( Account::Disconnected ) { qDebug() << Q_FUNC_INFO; - m_currentUsername = accountName(); + m_currentUsername = readUsername(); + m_currentServer = readServer(); m_currentPassword = readPassword(); m_currentPort = readPort(); -#ifndef ENABLE_HEADLESS - m_configWidget = QWeakPointer< QWidget >( new QWidget ); - m_ui = new Ui_JabberConfig; - m_ui->setupUi( m_configWidget.data() ); - m_configWidget.data()->setVisible( false ); - - m_ui->jabberUsername->setText( m_currentUsername ); - m_ui->jabberPassword->setText( m_currentPassword ); - m_ui->jabberServer->setText( m_currentServer ); - m_ui->jabberPort->setValue( m_currentPort ); - m_ui->jidExistsLabel->hide(); - connect( m_ui->jabberUsername, SIGNAL( textChanged( QString ) ), SLOT( onCheckJidExists( QString ) ) ); -#endif - // setup JID object - Jreen::JID jid = Jreen::JID( accountName() ); + Jreen::JID jid = Jreen::JID( readUsername() ); // general client setup m_client = new Jreen::Client( jid, m_currentPassword ); setupClientHelper(); - m_client->registerPayload(new TomahawkSipMessageFactory); + m_client->registerPayload( new TomahawkXmppMessageFactory ); m_currentResource = QString::fromAscii( "tomahawk%1" ).arg( QString::number( qrand() % 10000 ) ); m_client->setResource( m_currentResource ); @@ -158,8 +135,7 @@ JabberPlugin::JabberPlugin( const QString& pluginId ) #endif } - -JabberPlugin::~JabberPlugin() +XmppSipPlugin::~XmppSipPlugin() { delete m_avatarManager; delete m_roster; @@ -167,67 +143,29 @@ JabberPlugin::~JabberPlugin() delete m_xmlConsole; #endif delete m_client; - delete m_ui; -} - - -const QString -JabberPlugin::name() const -{ - return QString( MYNAME ); -} - - -const QString -JabberPlugin::friendlyName() const -{ - return QString( "Jabber" ); -} - - -const QString -JabberPlugin::accountName() const -{ - return TomahawkSettings::instance()->value( pluginId() + "/username" ).toString(); } #ifndef ENABLE_HEADLESS QMenu* -JabberPlugin::menu() +XmppSipPlugin::menu() { return m_menu; } - - -QWidget* -JabberPlugin::configWidget() -{ - return m_configWidget.data(); -} - - -QIcon -JabberPlugin::icon() const -{ - return QIcon( ":/jabber-icon.png" ); -} #endif - -bool -JabberPlugin::connectPlugin( bool startup ) +void +XmppSipPlugin::connectPlugin() { - Q_UNUSED( startup ); qDebug() << Q_FUNC_INFO; - if(m_client->isConnected()) + if( m_client->isConnected() ) { qDebug() << Q_FUNC_INFO << "Already connected to server, not connecting again..."; - return true; //FIXME: should i return false here?! + return; //FIXME: should i return false here?! } - qDebug() << "Connecting to the XMPP server..." << m_client->jid().full(); + qDebug() << "Connecting to the Xmpp server..." << m_client->jid().full(); //FIXME: we're badly workarounding some missing reconnection api here, to be fixed soon QTimer::singleShot( 1000, m_client, SLOT( connectToServer() ) ); @@ -235,21 +173,21 @@ JabberPlugin::connectPlugin( bool startup ) if ( m_client->connection() ) connect(m_client->connection(), SIGNAL(error(SocketError)), SLOT(onError(SocketError))); - m_state = Connecting; + m_state = Account::Connecting; emit stateChanged( m_state ); - return true; + return; } void -JabberPlugin::disconnectPlugin() +XmppSipPlugin::disconnectPlugin() { if (!m_client->isConnected()) { - if ( m_state != Disconnected ) // might be Connecting + if ( m_state != Account::Disconnected ) // might be Connecting { - m_state = Disconnected; - emit stateChanged( m_state ); + m_state = Account::Disconnected; + emit stateChanged( m_state ); } return; } @@ -262,13 +200,13 @@ JabberPlugin::disconnectPlugin() m_peers.clear(); m_client->disconnectFromServer( true ); - m_state = Disconnecting; + m_state = Account::Disconnecting; emit stateChanged( m_state ); } void -JabberPlugin::onConnect() +XmppSipPlugin::onConnect() { // qDebug() << Q_FUNC_INFO; @@ -280,7 +218,7 @@ JabberPlugin::onConnect() emit jidChanged( m_client->jid().full() ); } - qDebug() << "Connected to jabber as:" << m_client->jid().full(); + qDebug() << "Connected to xmpp as:" << m_client->jid().full(); // set presence to least valid value m_client->setPresence(Jreen::Presence::XA, "Got Tomahawk? http://gettomahawk.com", -127); @@ -303,7 +241,7 @@ JabberPlugin::onConnect() //connect( m_room, SIGNAL( messageReceived( Jreen::Message, bool ) ), this, SLOT( onNewMessage( Jreen::Message ) ) ); //connect( m_room, SIGNAL( presenceReceived( Jreen::Presence, const Jreen::MUCRoom::Participant* ) ), this, SLOT( onNewPresence( Jreen::Presence ) ) ); - m_state = Connected; + m_state = Account::Connected; emit stateChanged( m_state ); addMenuHelper(); @@ -311,7 +249,7 @@ JabberPlugin::onConnect() void -JabberPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) +XmppSipPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) { qDebug() << Q_FUNC_INFO; @@ -321,7 +259,7 @@ JabberPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) break; case Jreen::Client::AuthorizationError: - emit error( SipPlugin::AuthError, errorMessage( reason ) ); + emit error( Account::AuthError, errorMessage( reason ) ); break; case Jreen::Client::HostUnknown: @@ -332,7 +270,7 @@ JabberPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) case Jreen::Client::SystemShutdown: case Jreen::Client::Conflict: case Jreen::Client::Unknown: - emit error( SipPlugin::ConnectionError, errorMessage( reason ) ); + emit error( Account::ConnectionError, errorMessage( reason ) ); break; default: @@ -340,7 +278,7 @@ JabberPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) Q_ASSERT(false); break; } - m_state = Disconnected; + m_state = Account::Disconnected; emit stateChanged( m_state ); removeMenuHelper(); @@ -353,14 +291,14 @@ JabberPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) void -JabberPlugin::onError( const Jreen::Connection::SocketError& e ) +XmppSipPlugin::onError( const Jreen::Connection::SocketError& e ) { tDebug() << "JABBER error:" << e; } QString -JabberPlugin::errorMessage( Jreen::Client::DisconnectReason reason ) +XmppSipPlugin::errorMessage( Jreen::Client::DisconnectReason reason ) { switch( reason ) { @@ -402,7 +340,7 @@ JabberPlugin::errorMessage( Jreen::Client::DisconnectReason reason ) break; } - m_state = Disconnected; + m_state = Account::Disconnected; emit stateChanged( m_state ); return QString(); @@ -410,7 +348,7 @@ JabberPlugin::errorMessage( Jreen::Client::DisconnectReason reason ) void -JabberPlugin::sendMsg( const QString& to, const QString& msg ) +XmppSipPlugin::sendMsg( const QString& to, const QString& msg ) { qDebug() << Q_FUNC_INFO << to << msg; @@ -426,16 +364,16 @@ JabberPlugin::sendMsg( const QString& to, const QString& msg ) QVariant v = parser.parse( msg.toAscii(), &ok ); if ( !ok || v.type() != QVariant::Map ) { - qDebug() << "Invalid JSON in XMPP msg"; + qDebug() << "Invalid JSON in Xmpp msg"; return; } QVariantMap m = v.toMap(); /*******************************************************/ - TomahawkSipMessage *sipMessage; + TomahawkXmppMessage *sipMessage; if(m["visible"].toBool()) { - sipMessage = new TomahawkSipMessage( m["ip"].toString(), + sipMessage = new TomahawkXmppMessage( m["ip"].toString(), m["port"].toInt(), m["uniqname"].toString(), m["key"].toString() @@ -443,7 +381,7 @@ JabberPlugin::sendMsg( const QString& to, const QString& msg ) } else { - sipMessage = new TomahawkSipMessage(); + sipMessage = new TomahawkXmppMessage(); } qDebug() << "Send sip messsage to " << to; @@ -456,7 +394,7 @@ JabberPlugin::sendMsg( const QString& to, const QString& msg ) void -JabberPlugin::broadcastMsg( const QString& msg ) +XmppSipPlugin::broadcastMsg( const QString& msg ) { qDebug() << Q_FUNC_INFO; @@ -471,7 +409,7 @@ JabberPlugin::broadcastMsg( const QString& msg ) void -JabberPlugin::addContact( const QString& jid, const QString& msg ) +XmppSipPlugin::addContact( const QString& jid, const QString& msg ) { // Add contact to the Tomahawk group on the roster @@ -486,30 +424,31 @@ JabberPlugin::addContact( const QString& jid, const QString& msg ) void -JabberPlugin::showAddFriendDialog() +XmppSipPlugin::showAddFriendDialog() { #ifndef ENABLE_HEADLESS bool ok; QString id = QInputDialog::getText( TomahawkUtils::tomahawkWindow(), tr( "Add Friend" ), - tr( "Enter Jabber ID:" ), QLineEdit::Normal, "", &ok ).trimmed(); + tr( "Enter Xmpp ID:" ), QLineEdit::Normal, "", &ok ).trimmed(); + if ( !ok ) return; - qDebug() << "Attempting to add jabber contact to roster:" << id; + qDebug() << "Attempting to add xmpp contact to roster:" << id; addContact( id ); #endif } QString -JabberPlugin::defaultSuffix() const +XmppSipPlugin::defaultSuffix() const { - return "@jabber.org"; + return "@xmpp.org"; } void -JabberPlugin::showXmlConsole() +XmppSipPlugin::showXmlConsole() { #ifndef ENABLE_HEADLESS m_xmlConsole->show(); @@ -518,14 +457,21 @@ JabberPlugin::showXmlConsole() void -JabberPlugin::checkSettings() +XmppSipPlugin::checkSettings() +{ + configurationChanged(); +} + + +void +XmppSipPlugin::configurationChanged() { bool reconnect = false; QString username, password, server; int port; - username = accountName(); + username = readUsername(); password = readPassword(); server = readServer(); port = readPort(); @@ -554,7 +500,10 @@ JabberPlugin::checkSettings() if ( !m_currentUsername.contains( '@' ) ) { m_currentUsername += defaultSuffix(); - TomahawkSettings::instance()->setValue( pluginId() + "/username", m_currentUsername ); + QVariantHash credentials = m_account->credentials(); + credentials[ "username" ] = m_currentUsername; + m_account->setCredentials( credentials ); + m_account->sync(); } if ( reconnect ) @@ -565,12 +514,11 @@ JabberPlugin::checkSettings() setupClientHelper(); qDebug() << Q_FUNC_INFO << "Updated settings"; - connectPlugin( false ); + connectPlugin(); } } - -void JabberPlugin::setupClientHelper() +void XmppSipPlugin::setupClientHelper() { Jreen::JID jid = Jreen::JID( m_currentUsername ); m_client->setJID( jid ); @@ -590,13 +538,12 @@ void JabberPlugin::setupClientHelper() } } - -void JabberPlugin::addMenuHelper() +void XmppSipPlugin::addMenuHelper() { #ifndef ENABLE_HEADLESS if( !m_menu ) { - m_menu = new QMenu( QString( "%1 (" ).arg( friendlyName() ).append( accountName() ).append(")" ) ); + m_menu = new QMenu( QString( "%1 (" ).arg( friendlyName() ).append( readUsername() ).append(")" ) ); QAction* addFriendAction = m_menu->addAction( tr( "Add Friend..." ) ); connect( addFriendAction, SIGNAL( triggered() ), this, SLOT( showAddFriendDialog() ) ); @@ -612,8 +559,7 @@ void JabberPlugin::addMenuHelper() #endif } - -void JabberPlugin::removeMenuHelper() +void XmppSipPlugin::removeMenuHelper() { #ifndef ENABLE_HEADLESS if( m_menu ) @@ -627,9 +573,9 @@ void JabberPlugin::removeMenuHelper() } -void JabberPlugin::onNewMessage( const Jreen::Message& message ) +void XmppSipPlugin::onNewMessage( const Jreen::Message& message ) { - if ( m_state != Connected ) + if ( m_state != Account::Connected ) return; // qDebug() << Q_FUNC_INFO << "message type" << message.subtype(); @@ -668,10 +614,10 @@ void JabberPlugin::onNewMessage( const Jreen::Message& message ) } -void JabberPlugin::onPresenceReceived( const Jreen::RosterItem::Ptr &item, const Jreen::Presence& presence ) +void XmppSipPlugin::onPresenceReceived( const Jreen::RosterItem::Ptr &item, const Jreen::Presence& presence ) { Q_UNUSED(item); - if ( m_state != Connected ) + if ( m_state != Account::Connected ) return; Jreen::JID jid = presence.from(); @@ -712,9 +658,9 @@ void JabberPlugin::onPresenceReceived( const Jreen::RosterItem::Ptr &item, const } -void JabberPlugin::onSubscriptionReceived( const Jreen::RosterItem::Ptr& item, const Jreen::Presence& presence ) +void XmppSipPlugin::onSubscriptionReceived( const Jreen::RosterItem::Ptr& item, const Jreen::Presence& presence ) { - if ( m_state != Connected ) + if ( m_state != Account::Connected ) return; // qDebug() << Q_FUNC_INFO << "presence type:" << presence.subtype(); @@ -766,7 +712,7 @@ void JabberPlugin::onSubscriptionReceived( const Jreen::RosterItem::Ptr& item, c void -JabberPlugin::onSubscriptionRequestConfirmed( int result ) +XmppSipPlugin::onSubscriptionRequestConfirmed( int result ) { #ifndef ENABLE_HEADLESS qDebug() << Q_FUNC_INFO << result; @@ -803,9 +749,9 @@ JabberPlugin::onSubscriptionRequestConfirmed( int result ) } -void JabberPlugin::onNewIq( const Jreen::IQ& iq ) +void XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) { - if ( m_state != Connected ) + if ( m_state != Account::Connected ) return; Jreen::IQReply *reply = qobject_cast< Jreen::IQReply* >( sender() ); @@ -855,7 +801,7 @@ void JabberPlugin::onNewIq( const Jreen::IQ& iq ) }*/ else { - TomahawkSipMessage::Ptr sipMessage = iq.payload< TomahawkSipMessage >(); + TomahawkXmppMessage::Ptr sipMessage = iq.payload< TomahawkXmppMessage >(); if(sipMessage) { iq.accept(); @@ -885,7 +831,7 @@ void JabberPlugin::onNewIq( const Jreen::IQ& iq ) } -bool JabberPlugin::presenceMeansOnline( Jreen::Presence::Type p ) +bool XmppSipPlugin::presenceMeansOnline( Jreen::Presence::Type p ) { switch( p ) { @@ -900,7 +846,7 @@ bool JabberPlugin::presenceMeansOnline( Jreen::Presence::Type p ) } -void JabberPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type presenceType ) +void XmppSipPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type presenceType ) { QString fulljid = jid.full(); @@ -950,11 +896,11 @@ void JabberPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Typ } -void JabberPlugin::onNewAvatar( const QString& jid ) +void XmppSipPlugin::onNewAvatar( const QString& jid ) { #ifndef ENABLE_HEADLESS // qDebug() << Q_FUNC_INFO << jid; - if ( m_state != Connected ) + if ( m_state != Account::Connected ) return; Q_ASSERT( !m_avatarManager->avatar( jid ).isNull() ); @@ -980,84 +926,46 @@ void JabberPlugin::onNewAvatar( const QString& jid ) bool -JabberPlugin::readXmlConsoleEnabled() +XmppSipPlugin::readXmlConsoleEnabled() { - return TomahawkSettings::instance()->value( pluginId() + "/xmlconsole", QVariant( false ) ).toBool(); + QVariantHash configuration = m_account->configuration(); + return configuration.contains( "xmlconsole" ) && configuration[ "xmlconsole" ].toBool(); } QString -JabberPlugin::readPassword() +XmppSipPlugin::readUsername() { - return TomahawkSettings::instance()->value( pluginId() + "/password" ).toString(); + QVariantHash credentials = m_account->credentials(); + return credentials.contains( "username" ) ? credentials[ "username" ].toString() : QString(); +} + +QString +XmppSipPlugin::readPassword() +{ + QVariantHash credentials = m_account->credentials(); + return credentials.contains( "password" ) ? credentials[ "password" ].toString() : QString(); } int -JabberPlugin::readPort() +XmppSipPlugin::readPort() { - return TomahawkSettings::instance()->value( pluginId() + "/port", 5222 ).toInt(); + QVariantHash configuration = m_account->configuration(); + return configuration.contains( "port" ) ? configuration[ "port" ].toInt() : 5222; } QString -JabberPlugin::readServer() +XmppSipPlugin::readServer() { - return TomahawkSettings::instance()->value( pluginId() + "/server" ).toString(); + QVariantHash configuration = m_account->configuration(); + return configuration.contains( "server" ) ? configuration[ "server" ].toString() : QString(); } -void -JabberPlugin::onCheckJidExists( QString jid ) -{ - for ( int i=0; isipPlugins().count(); i++ ) - { - QString savedUsername = TomahawkSettings::instance()->value( - TomahawkSettings::instance()->sipPlugins().at( i ) + "/username" ).toString(); - QStringList splitUserName = TomahawkSettings::instance()->value( - TomahawkSettings::instance()->sipPlugins().at( i ) + "/username" ).toString().split("@"); - QString server = TomahawkSettings::instance()->value( - TomahawkSettings::instance()->sipPlugins().at( i ) + "/server" ).toString(); - - if ( ( savedUsername == jid || splitUserName.contains( jid ) ) && - server == m_ui->jabberServer->text() && !jid.trimmed().isEmpty() ) - { - m_ui->jidExistsLabel->show(); - // the already jid exists - emit dataError( true ); - return; - } - } - m_ui->jidExistsLabel->hide(); - emit dataError( false ); -} - - -void -JabberPlugin::saveConfig() -{ - TomahawkSettings::instance()->setValue( pluginId() + "/username", m_ui->jabberUsername->text() ); - TomahawkSettings::instance()->setValue( pluginId() + "/password", m_ui->jabberPassword->text() ); - TomahawkSettings::instance()->setValue( pluginId() + "/port", m_ui->jabberPort->value() ); - TomahawkSettings::instance()->setValue( pluginId() + "/server", m_ui->jabberServer->text() ); - - checkSettings(); -} - - -void -JabberPlugin::deletePlugin() -{ - TomahawkSettings::instance()->remove( pluginId() ); -} - - -SipPlugin::ConnectionState -JabberPlugin::connectionState() const +Account::ConnectionState +XmppSipPlugin::connectionState() const { return m_state; } - -#ifndef GOOGLE_WRAPPER -Q_EXPORT_PLUGIN2( sipfactory, JabberFactory ) -#endif diff --git a/src/sip/jabber/jabber.h b/src/accounts/xmpp/sip/xmppsip.h similarity index 70% rename from src/sip/jabber/jabber.h rename to src/accounts/xmpp/sip/xmppsip.h index 0b706056d..45cb5d8c0 100644 --- a/src/sip/jabber/jabber.h +++ b/src/accounts/xmpp/sip/xmppsip.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Dominik Schmidt * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,8 +18,8 @@ * along with Tomahawk. If not, see . */ -#ifndef JABBER_H -#define JABBER_H +#ifndef XMPPSIP_H +#define XMPPSIP_H #include "sip/SipPlugin.h" @@ -45,70 +46,49 @@ #include #endif -#define MYNAME "SIPJREEN" #define TOMAHAWK_FEATURE QLatin1String( "tomahawk:sip:v1" ) #define TOMAHAWK_CAP_NODE_NAME QLatin1String( "http://tomahawk-player.org/" ) -#include "../sipdllmacro.h" +#include "accounts/accountdllmacro.h" -class Ui_JabberConfig; - -class SIPDLLEXPORT JabberFactory : public SipPluginFactory -{ - Q_OBJECT - Q_INTERFACES( SipPluginFactory ) - -public: - JabberFactory() {} - virtual ~JabberFactory() {} - - virtual QString prettyName() const { return "Jabber"; } - virtual QString factoryId() const { return "sipjabber"; } - virtual QIcon icon() const; - virtual SipPlugin* createPlugin( const QString& pluginId ); -}; - -class SIPDLLEXPORT JabberPlugin : public SipPlugin +class ACCOUNTDLLEXPORT XmppSipPlugin : public SipPlugin { Q_OBJECT public: - JabberPlugin( const QString& pluginId ); - virtual ~JabberPlugin(); + XmppSipPlugin( Tomahawk::Accounts::Account* account ); + virtual ~XmppSipPlugin(); //FIXME: Make this more correct virtual bool isValid() const { return true; } - virtual const QString name() const; - virtual const QString friendlyName() const; - virtual const QString accountName() const; - virtual ConnectionState connectionState() const; #ifndef ENABLE_HEADLESS virtual QMenu* menu(); - virtual QIcon icon() const; - virtual QWidget* configWidget(); #endif - virtual void saveConfig(); - virtual void deletePlugin(); + + // used by XmppAccount to expose connection state and controls + Tomahawk::Accounts::Account::ConnectionState connectionState() const; signals: - void dataError( bool exists ); void jidChanged( const QString& ); + // Used by XmppAccount + void stateChanged( Tomahawk::Accounts::Account::ConnectionState state ); + void error( int errorId, const QString& errorStr ); + public slots: - virtual bool connectPlugin( bool startup ); - void disconnectPlugin(); - void checkSettings(); - void sendMsg( const QString& to, const QString& msg ); + virtual void connectPlugin(); + virtual void disconnectPlugin(); + virtual void checkSettings(); + virtual void configurationChanged(); + virtual void sendMsg( const QString& to, const QString& msg ); void broadcastMsg( const QString &msg ); - void addContact( const QString &jid, const QString& msg = QString() ); + virtual void addContact( const QString &jid, const QString& msg = QString() ); void showAddFriendDialog(); protected: virtual QString defaultSuffix() const; - Ui_JabberConfig* m_ui; // so the google wrapper can change the config dialog a bit - private slots: void showXmlConsole(); void onConnect(); @@ -122,10 +102,10 @@ private slots: void onError( const Jreen::Connection::SocketError& e ); void onNewIq( const Jreen::IQ &iq ); void onNewAvatar( const QString &jid ); - void onCheckJidExists( QString jid ); private: bool readXmlConsoleEnabled(); + QString readUsername(); QString readPassword(); QString readServer(); int readPort(); @@ -138,15 +118,11 @@ private: bool presenceMeansOnline( Jreen::Presence::Type p ); void handlePeerStatus( const Jreen::JID &jid, Jreen::Presence::Type presenceType ); - using SipPlugin::errorMessage; - QString m_currentUsername; QString m_currentPassword; QString m_currentServer; int m_currentPort; - ConnectionState m_state; - - QWeakPointer< QWidget > m_configWidget; + Tomahawk::Accounts::Account::ConnectionState m_state; QString m_currentResource; diff --git a/src/sip/jabber/jabber-icon.png b/src/accounts/xmpp/xmpp-icon.png similarity index 100% rename from src/sip/jabber/jabber-icon.png rename to src/accounts/xmpp/xmpp-icon.png diff --git a/src/accounts/xmpp/xmppaccount.cpp b/src/accounts/xmpp/xmppaccount.cpp new file mode 100644 index 000000000..1c23f45c3 --- /dev/null +++ b/src/accounts/xmpp/xmppaccount.cpp @@ -0,0 +1,114 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "xmppaccount.h" +#include "xmppconfigwidget.h" +#include "sip/SipPlugin.h" +#include "ui_xmppconfigwidget.h" + +#include + +namespace Tomahawk +{ + +namespace Accounts +{ + +Account* +XmppAccountFactory::createAccount( const QString& accountId ) +{ + return new XmppAccount( accountId.isEmpty() ? Tomahawk::Accounts::generateId( factoryId() ) : accountId ); +} + + +XmppAccount::XmppAccount( const QString &accountId ) + : Account( accountId ) +{ + setAccountServiceName( "XMPP (Jabber)" ); + setTypes( SipType ); + + m_configWidget = QWeakPointer< QWidget >( new XmppConfigWidget( this, 0 ) ); +} + + +XmppAccount::~XmppAccount() +{ + delete m_xmppSipPlugin.data(); +} + + +void +XmppAccount::authenticate() +{ + if ( connectionState() != Account::Connected ) + sipPlugin()->connectPlugin(); +} + + +void +XmppAccount::deauthenticate() +{ + if ( connectionState() != Account::Disconnected ) + sipPlugin()->disconnectPlugin(); +} + +bool +XmppAccount::isAuthenticated() const +{ + return m_xmppSipPlugin.data()->connectionState() == Account::Connected; +} + + +Account::ConnectionState +XmppAccount::connectionState() const +{ + return m_xmppSipPlugin.data()->connectionState(); +} + +void +XmppAccount::saveConfig() +{ + if ( !m_configWidget.isNull() ) + static_cast< XmppConfigWidget* >( m_configWidget.data() )->saveConfig(); +} + + +SipPlugin* +XmppAccount::sipPlugin() +{ + if ( m_xmppSipPlugin.isNull() ) + { + m_xmppSipPlugin = QWeakPointer< XmppSipPlugin >( new XmppSipPlugin( this ) ); + + connect( m_xmppSipPlugin.data(), SIGNAL( stateChanged( Tomahawk::Accounts::Account::ConnectionState ) ), this, SIGNAL( connectionStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ) ); + connect( m_xmppSipPlugin.data(), SIGNAL( error( int, QString ) ), this, SIGNAL( error( int, QString ) ) ); + + return m_xmppSipPlugin.data(); + } + return m_xmppSipPlugin.data(); +} + + +} + +} + +#ifndef GOOGLE_WRAPPER +Q_EXPORT_PLUGIN2( Tomahawk::Accounts::AccountFactory, Tomahawk::Accounts::XmppAccountFactory ) +#endif diff --git a/src/accounts/xmpp/xmppaccount.h b/src/accounts/xmpp/xmppaccount.h new file mode 100644 index 000000000..a6c50ae20 --- /dev/null +++ b/src/accounts/xmpp/xmppaccount.h @@ -0,0 +1,89 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Dominik Schmidt + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef XMPPACCOUNT_H +#define XMPPACCOUNT_H + +#include "sip/xmppsip.h" +#include "accounts/accountdllmacro.h" +#include "accounts/Account.h" + +#define MYNAME "ACCOUNTJABBER" + +class Ui_XmppConfigWidget; + +namespace Tomahawk +{ + +namespace Accounts +{ + +class ACCOUNTDLLEXPORT XmppAccountFactory : public AccountFactory +{ + Q_OBJECT + Q_INTERFACES( Tomahawk::Accounts::AccountFactory ) + + // for settings access + friend class XmppConfigWidget; +public: + XmppAccountFactory() {} + virtual ~XmppAccountFactory() {} + + QString prettyName() const { return "XMPP (Jabber)"; } + QString description() const { return tr( "Log on to your Jabber/XMPP account to connect to your friends" ); } + QString factoryId() const { return "xmppaccount"; } + QPixmap icon() const { return QPixmap( ":/xmpp-icon.png" ); } + AccountTypes types() const { return AccountTypes( SipType ); }; + Account* createAccount( const QString& pluginId = QString() ); +}; + +class ACCOUNTDLLEXPORT XmppAccount : public Account +{ + Q_OBJECT + +public: + XmppAccount( const QString &accountId ); + virtual ~XmppAccount(); + + QPixmap icon() const { return QPixmap( ":/xmpp-icon.png" ); } + + void authenticate(); + void deauthenticate(); + bool isAuthenticated() const; + + Tomahawk::InfoSystem::InfoPlugin* infoPlugin() { return 0; } + SipPlugin* sipPlugin(); + + QWidget* configurationWidget() { return m_configWidget.data(); } + QWidget* aclWidget() { return 0; } + void saveConfig(); + + virtual Tomahawk::Accounts::Account::ConnectionState connectionState() const; + +protected: + QWeakPointer< QWidget > m_configWidget; // so the google wrapper can change the config dialog a bit + QWeakPointer< XmppSipPlugin > m_xmppSipPlugin; +}; + +}; + +}; + +#endif diff --git a/src/accounts/xmpp/xmppconfigwidget.cpp b/src/accounts/xmpp/xmppconfigwidget.cpp new file mode 100644 index 000000000..b85b6e7c3 --- /dev/null +++ b/src/accounts/xmpp/xmppconfigwidget.cpp @@ -0,0 +1,108 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "xmppaccount.h" +#include "xmppconfigwidget.h" +#include "ui_xmppconfigwidget.h" + +#include + +#include +#include + +namespace Tomahawk +{ + +namespace Accounts +{ + +XmppConfigWidget::XmppConfigWidget( XmppAccount* account, QWidget *parent ) : + QWidget( parent ), + m_ui( new Ui::XmppConfigWidget ), + m_account( account ) +{ + m_ui->setupUi( this ); + + m_ui->xmppUsername->setText( account->credentials().contains( "username" ) ? account->credentials()[ "username" ].toString() : QString() ); + m_ui->xmppPassword->setText( account->credentials().contains( "password" ) ? account->credentials()[ "password" ].toString() : QString() ); + m_ui->xmppServer->setText( account->configuration().contains( "server" ) ? account->configuration()[ "server" ].toString() : QString() ); + m_ui->xmppPort->setValue( account->configuration().contains( "port" ) ? account->configuration()[ "port" ].toInt() : 5222 ); + m_ui->jidExistsLabel->hide(); + + connect( m_ui->xmppUsername, SIGNAL( textChanged( QString ) ), SLOT( onCheckJidExists( QString ) ) ); +} + + +XmppConfigWidget::~XmppConfigWidget() +{ + delete m_ui; +} + +void +XmppConfigWidget::saveConfig() +{ + QVariantHash credentials = m_account->credentials(); + credentials[ "username" ] = m_ui->xmppUsername->text().trimmed(); + credentials[ "password" ] = m_ui->xmppPassword->text().trimmed(); + + QVariantHash configuration = m_account->configuration(); + configuration[ "server" ] = m_ui->xmppServer->text().trimmed(); + configuration[ "port" ] = m_ui->xmppPort->text().trimmed(); + + m_account->setAccountFriendlyName( m_ui->xmppUsername->text() ); + m_account->setCredentials( credentials ); + m_account->setConfiguration( configuration); + m_account->sync(); + + static_cast< XmppSipPlugin* >( m_account->sipPlugin() )->checkSettings(); +} + + +void +XmppConfigWidget::onCheckJidExists( QString jid ) +{ + QList< Tomahawk::Accounts::Account* > accounts = Tomahawk::Accounts::AccountManager::instance()->accounts( Tomahawk::Accounts::SipType ); + foreach( Tomahawk::Accounts::Account* account, accounts ) + { + if ( account->accountId() == m_account->accountId() ) + continue; + + QString savedUsername = account->credentials()[ "username" ].toString(); + QStringList savedSplitUsername = account->credentials()[ "username" ].toString().split("@"); + QString savedServer = account->configuration()[ "server" ].toString(); + int savedPort = account->configuration()[ "port" ].toInt(); + + if ( ( savedUsername == jid || savedSplitUsername.contains( jid ) ) && + savedServer == m_ui->xmppServer->text() && + savedPort == m_ui->xmppPort->value() && + !jid.trimmed().isEmpty() ) + { + m_ui->jidExistsLabel->show(); + // the already jid exists + emit dataError( true ); + return; + } + } + m_ui->jidExistsLabel->hide(); + emit dataError( false ); +} + + +} + +} diff --git a/src/accounts/xmpp/xmppconfigwidget.h b/src/accounts/xmpp/xmppconfigwidget.h new file mode 100644 index 000000000..93f41f44d --- /dev/null +++ b/src/accounts/xmpp/xmppconfigwidget.h @@ -0,0 +1,68 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef JABBERACCOUNTCONFIGWIDGET_H +#define JABBERACCOUNTCONFIGWIDGET_H + +#include "accounts/accountdllmacro.h" + +#include + +namespace Ui +{ + class XmppConfigWidget; +} + +namespace Tomahawk +{ + +namespace Accounts +{ + +class XmppAccount; +class GoogleWrapper; + + +class ACCOUNTDLLEXPORT XmppConfigWidget : public QWidget +{ + Q_OBJECT + +public: + explicit XmppConfigWidget( XmppAccount* account = 0, QWidget *parent = 0 ); + virtual ~XmppConfigWidget(); + + void saveConfig(); + +signals: + void dataError( bool exists ); + +private slots: + void onCheckJidExists( QString jid ); + +private: + Ui::XmppConfigWidget *m_ui; + XmppAccount *m_account; + + friend class GoogleWrapper; // So google wrapper can modify the labels and text +}; + +} + +} + +#endif // TWITTERCONFIGWIDGET_H diff --git a/src/sip/jabber/configwidget.ui b/src/accounts/xmpp/xmppconfigwidget.ui similarity index 89% rename from src/sip/jabber/configwidget.ui rename to src/accounts/xmpp/xmppconfigwidget.ui index cd5fd72b1..99876aa6a 100644 --- a/src/sip/jabber/configwidget.ui +++ b/src/accounts/xmpp/xmppconfigwidget.ui @@ -1,7 +1,7 @@ - JabberConfig - + XmppConfigWidget + 0 @@ -11,7 +11,7 @@ - Jabber Configuration + Xmpp Configuration @@ -35,7 +35,7 @@ - :/jabber-icon.png + :/xmpp-icon.png @@ -49,7 +49,7 @@ - Configure this Jabber account + Configure this Xmpp account @@ -84,9 +84,9 @@ - + - Enter your Jabber login to connect with your friends using Tomahawk! + Enter your Xmpp login to connect with your friends using Tomahawk! @@ -128,15 +128,15 @@ - Jabber ID: + Xmpp ID: - jabberUsername + xmppUsername - + 0 @@ -160,12 +160,12 @@ Password: - jabberPassword + xmppPassword - + 0 @@ -196,7 +196,7 @@ - + true @@ -207,13 +207,13 @@ - Advanced Jabber Settings + Advanced Xmpp Settings - + 0 @@ -233,12 +233,12 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - jabberServer + xmppServer - + 0 @@ -248,7 +248,7 @@ - + Port: @@ -258,7 +258,7 @@ - + 0 diff --git a/src/sip/zeroconf/CMakeLists.txt b/src/accounts/zeroconf/CMakeLists.txt similarity index 70% rename from src/sip/zeroconf/CMakeLists.txt rename to src/accounts/zeroconf/CMakeLists.txt index 3a33cc577..772a24350 100644 --- a/src/sip/zeroconf/CMakeLists.txt +++ b/src/accounts/zeroconf/CMakeLists.txt @@ -4,15 +4,17 @@ include( ${QT_USE_FILE} ) add_definitions( ${QT_DEFINITIONS} ) add_definitions( -DQT_PLUGIN ) add_definitions( -DQT_SHARED ) -add_definitions( -DSIPDLLEXPORT_PRO ) +add_definitions( -DACCOUNTDLLEXPORT_PRO ) set( zeroconfSources zeroconf.cpp + zeroconfaccount.cpp ) set( zeroconfHeaders zeroconf.h tomahawkzeroconf.h + zeroconfaccount.h ) include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. @@ -22,7 +24,7 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. qt4_wrap_ui( UI_SRCS configwidget.ui ) qt4_add_resources( RC_SRCS "resources.qrc" ) qt4_wrap_cpp( zeroconfMoc ${zeroconfHeaders} ) -add_library( tomahawk_sipzeroconf SHARED ${zeroconfSources} ${zeroconfMoc} ${RC_SRCS} ${UI_SRCS} ) +add_library( tomahawk_account_zeroconf MODULE ${zeroconfSources} ${zeroconfMoc} ${RC_SRCS} ${UI_SRCS} ) IF( WIN32 ) SET( OS_SPECIFIC_LINK_LIBRARIES @@ -32,7 +34,7 @@ SET( OS_SPECIFIC_LINK_LIBRARIES ) ENDIF( WIN32 ) -target_link_libraries( tomahawk_sipzeroconf +target_link_libraries( tomahawk_account_zeroconf ${QT_LIBRARIES} ${OS_SPECIFIC_LINK_LIBRARIES} ${TOMAHAWK_LIBRARIES} @@ -42,4 +44,4 @@ IF( APPLE ) # SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) ENDIF( APPLE ) -install( TARGETS tomahawk_sipzeroconf DESTINATION ${CMAKE_INSTALL_LIBDIR} ) +install( TARGETS tomahawk_account_zeroconf DESTINATION ${CMAKE_INSTALL_LIBDIR} ) \ No newline at end of file diff --git a/src/sip/zeroconf/configwidget.ui b/src/accounts/zeroconf/configwidget.ui similarity index 100% rename from src/sip/zeroconf/configwidget.ui rename to src/accounts/zeroconf/configwidget.ui diff --git a/src/sip/zeroconf/resources.qrc b/src/accounts/zeroconf/resources.qrc similarity index 100% rename from src/sip/zeroconf/resources.qrc rename to src/accounts/zeroconf/resources.qrc diff --git a/src/sip/zeroconf/tomahawkzeroconf.h b/src/accounts/zeroconf/tomahawkzeroconf.h similarity index 97% rename from src/sip/zeroconf/tomahawkzeroconf.h rename to src/accounts/zeroconf/tomahawkzeroconf.h index f875ef1dd..f54b162e3 100644 --- a/src/sip/zeroconf/tomahawkzeroconf.h +++ b/src/accounts/zeroconf/tomahawkzeroconf.h @@ -30,10 +30,9 @@ #include "database/database.h" #include "network/servent.h" +#include "accounts/accountdllmacro.h" -#include "../sipdllmacro.h" - -class SIPDLLEXPORT Node : public QObject +class Node : public QObject { Q_OBJECT @@ -61,7 +60,7 @@ public slots: emit tomahawkHostFound( ip, port, "Unknown", nid ); this->deleteLater(); } - + void resolve() { QHostInfo::lookupHost( ip, this, SLOT( resolved( QHostInfo ) ) ); @@ -74,7 +73,7 @@ private: }; -class SIPDLLEXPORT TomahawkZeroconf : public QObject +class ACCOUNTDLLEXPORT TomahawkZeroconf : public QObject { Q_OBJECT diff --git a/src/sip/zeroconf/zeroconf-icon.png b/src/accounts/zeroconf/zeroconf-icon.png similarity index 100% rename from src/sip/zeroconf/zeroconf-icon.png rename to src/accounts/zeroconf/zeroconf-icon.png diff --git a/src/sip/zeroconf/zeroconf.cpp b/src/accounts/zeroconf/zeroconf.cpp similarity index 81% rename from src/sip/zeroconf/zeroconf.cpp rename to src/accounts/zeroconf/zeroconf.cpp index e983b4cdd..88fdf6016 100644 --- a/src/sip/zeroconf/zeroconf.cpp +++ b/src/accounts/zeroconf/zeroconf.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,20 +25,15 @@ #include "tomahawksettings.h" #include "utils/logger.h" +#include "zeroconfaccount.h" +using namespace Tomahawk; +using namespace Accounts; -SipPlugin* -ZeroconfFactory::createPlugin( const QString& pluginId ) -{ - return new ZeroconfPlugin( pluginId.isEmpty() ? generateId() : pluginId ); -} - -ZeroconfPlugin::ZeroconfPlugin() : SipPlugin( "" ) {} - -ZeroconfPlugin::ZeroconfPlugin ( const QString& pluginId ) - : SipPlugin( pluginId ) +ZeroconfPlugin::ZeroconfPlugin ( ZeroconfAccount* parent ) + : SipPlugin( parent ) , m_zeroconf( 0 ) - , m_state( Disconnected ) + , m_state( Account::Disconnected ) , m_cachedNodes() { qDebug() << Q_FUNC_INFO; @@ -66,22 +62,15 @@ ZeroconfPlugin::friendlyName() const return QString( MYNAME ); } -SipPlugin::ConnectionState +Account::ConnectionState ZeroconfPlugin::connectionState() const { return m_state; } -#ifndef ENABLE_HEADLESS -QIcon -ZeroconfFactory::icon() const -{ - return QIcon( ":/zeroconf-icon.png" ); -} -#endif -bool -ZeroconfPlugin::connectPlugin( bool /*startup*/ ) +void +ZeroconfPlugin::connectPlugin() { delete m_zeroconf; m_zeroconf = new TomahawkZeroconf( Servent::instance()->port(), this ); @@ -89,7 +78,7 @@ ZeroconfPlugin::connectPlugin( bool /*startup*/ ) SLOT( lanHostFound( QString, int, QString, QString ) ) ); advertise(); - m_state = Connected; + m_state = Account::Connected; foreach( const QStringList& nodeSet, m_cachedNodes ) { @@ -99,15 +88,13 @@ ZeroconfPlugin::connectPlugin( bool /*startup*/ ) m_cachedNodes.clear(); m_advertisementTimer.start(); - - return true; } void ZeroconfPlugin::disconnectPlugin() { m_advertisementTimer.stop(); - m_state = Disconnected; + m_state = Account::Disconnected; delete m_zeroconf; m_zeroconf = 0; @@ -118,7 +105,7 @@ ZeroconfPlugin::disconnectPlugin() QIcon ZeroconfPlugin::icon() const { - return QIcon( ":/zeroconf-icon.png" ); + return account()->icon(); } #endif @@ -138,7 +125,7 @@ ZeroconfPlugin::lanHostFound( const QString& host, int port, const QString& name qDebug() << "Found LAN host:" << host << port << nodeid; - if ( m_state != Connected ) + if ( m_state != Account::Connected ) { qDebug() << "Not online, so not connecting."; QStringList nodeSet; @@ -153,5 +140,3 @@ ZeroconfPlugin::lanHostFound( const QString& host, int port, const QString& name qDebug() << "Already connected to" << host; } - -Q_EXPORT_PLUGIN2( sipfactory, ZeroconfFactory ) diff --git a/src/sip/zeroconf/zeroconf.h b/src/accounts/zeroconf/zeroconf.h similarity index 69% rename from src/sip/zeroconf/zeroconf.h rename to src/accounts/zeroconf/zeroconf.h index 9a4c660b6..98e8a865c 100644 --- a/src/sip/zeroconf/zeroconf.h +++ b/src/accounts/zeroconf/zeroconf.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,55 +21,45 @@ #define ZEROCONF_H #include "sip/SipPlugin.h" +#include "accounts/Account.h" #include "tomahawkzeroconf.h" -#include "../sipdllmacro.h" +#include "../accountdllmacro.h" #include #define MYNAME "Local Network" -class SIPDLLEXPORT ZeroconfFactory : public SipPluginFactory +namespace Tomahawk +{ +namespace Accounts { - Q_OBJECT - Q_INTERFACES( SipPluginFactory ) -public: - ZeroconfFactory() {} - virtual ~ZeroconfFactory() {} - virtual QString factoryId() const { return "sipzeroconf"; } - virtual QString prettyName() const { return "Local Network"; } - virtual bool isUnique() const { return true; } -#ifndef ENABLE_HEADLESS - virtual QIcon icon() const; -#endif +class ZeroconfAccount; - virtual SipPlugin* createPlugin ( const QString& pluginId = QString() ); -}; - -class SIPDLLEXPORT ZeroconfPlugin : public SipPlugin +class ACCOUNTDLLEXPORT ZeroconfPlugin : public SipPlugin { Q_OBJECT public: - ZeroconfPlugin(); - ZeroconfPlugin( const QString& pluginId ); + ZeroconfPlugin( ZeroconfAccount* acc ); virtual ~ZeroconfPlugin(); virtual const QString name() const; virtual const QString friendlyName() const; virtual const QString accountName() const; - virtual ConnectionState connectionState() const; + virtual Account::ConnectionState connectionState() const; virtual bool isValid() const { return true; } #ifndef ENABLE_HEADLESS virtual QIcon icon() const; #endif virtual void checkSettings() {} + virtual void configurationChanged() {} public slots: - virtual bool connectPlugin( bool startup ); + void connectPlugin(); void disconnectPlugin(); void advertise(); @@ -82,9 +73,12 @@ private slots: private: TomahawkZeroconf* m_zeroconf; - ConnectionState m_state; + Account::ConnectionState m_state; QVector m_cachedNodes; QTimer m_advertisementTimer; }; +} +} + #endif diff --git a/src/accounts/zeroconf/zeroconfaccount.cpp b/src/accounts/zeroconf/zeroconfaccount.cpp new file mode 100644 index 000000000..97548ecbc --- /dev/null +++ b/src/accounts/zeroconf/zeroconfaccount.cpp @@ -0,0 +1,128 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "zeroconfaccount.h" + +#include "sip/SipPlugin.h" +#include "zeroconf.h" + +#include + +using namespace Tomahawk; +using namespace Accounts; + +QPixmap* s_icon = 0; + +ZeroconfFactory::ZeroconfFactory() +{ +#ifndef ENABLE_HEADLESS + if ( s_icon == 0 ) + s_icon = new QPixmap( ":/zeroconf-icon.png" ); +#endif +} + + +ZeroconfFactory::~ZeroconfFactory() +{ + if ( s_icon ) + { + delete s_icon; + s_icon = 0; + } +} + + +Account* +ZeroconfFactory::createAccount( const QString& pluginId ) +{ + return new ZeroconfAccount( pluginId.isEmpty() ? generateId( factoryId() ) : pluginId ); +} + +QPixmap +ZeroconfFactory::icon() const +{ + return *s_icon; +} + + +ZeroconfAccount::ZeroconfAccount( const QString& accountId ) + : Account( accountId ) +{ + setAccountServiceName( "Local Network" ); + setAccountFriendlyName( "Local Network" ); + + setTypes( SipType ); +} + +ZeroconfAccount::~ZeroconfAccount() +{ + +} + +QPixmap +ZeroconfAccount::icon() const +{ + return *s_icon; +} + + +void +ZeroconfAccount::authenticate() +{ + if ( !isAuthenticated() ) + static_cast< ZeroconfPlugin* >( m_sipPlugin.data() )->connectPlugin(); +} + + +void +ZeroconfAccount::deauthenticate() +{ + if ( isAuthenticated() ) + static_cast< ZeroconfPlugin* >( m_sipPlugin.data() )->disconnectPlugin(); +} + + +bool +ZeroconfAccount::isAuthenticated() const +{ + return connectionState() == Connected; +} + + +Account::ConnectionState +ZeroconfAccount::connectionState() const +{ + if ( m_sipPlugin.isNull() ) + return Disconnected; + + // TODO can we get called before sipPlugin()? + return static_cast< ZeroconfPlugin* >( m_sipPlugin.data() )->connectionState(); +} + + +SipPlugin* +ZeroconfAccount::sipPlugin() +{ + if ( m_sipPlugin.isNull() ) + m_sipPlugin = QWeakPointer< SipPlugin >( new ZeroconfPlugin( this ) ); + + return m_sipPlugin.data(); +} + + +Q_EXPORT_PLUGIN2( Tomahawk::Accounts::AccountFactory, Tomahawk::Accounts::ZeroconfFactory ) \ No newline at end of file diff --git a/src/accounts/zeroconf/zeroconfaccount.h b/src/accounts/zeroconf/zeroconfaccount.h new file mode 100644 index 000000000..29a6673fd --- /dev/null +++ b/src/accounts/zeroconf/zeroconfaccount.h @@ -0,0 +1,80 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef ZEROCONF_ACCOUNTS_H +#define ZEROCONF_ACCOUNTS_H + +#include "accounts/Account.h" +#include "../accountdllmacro.h" + +class SipPlugin; + +namespace Tomahawk +{ +namespace Accounts +{ + +class ACCOUNTDLLEXPORT ZeroconfFactory : public AccountFactory +{ + Q_OBJECT + Q_INTERFACES( Tomahawk::Accounts::AccountFactory ) +public: + ZeroconfFactory(); + virtual ~ZeroconfFactory(); + + virtual QString factoryId() const { return "zeroconfaccount"; } + virtual QString prettyName() const { return "Local Network"; } + QString description() const { return tr( "Automatically connect to Tomahawks on the local network" ); } + virtual bool isUnique() const { return true; } + AccountTypes types() const { return AccountTypes( SipType ); }; +#ifndef ENABLE_HEADLESS +virtual QPixmap icon() const; +#endif + + + virtual Account* createAccount ( const QString& pluginId = QString() ); +}; + +class ACCOUNTDLLEXPORT ZeroconfAccount : public Account +{ + Q_OBJECT +public: + ZeroconfAccount( const QString &accountId ); + virtual ~ZeroconfAccount(); + + QPixmap icon() const; + + void authenticate(); + void deauthenticate(); + bool isAuthenticated() const; + ConnectionState connectionState() const; + + Tomahawk::InfoSystem::InfoPlugin* infoPlugin() { return 0; } + SipPlugin* sipPlugin(); + + QWidget* configurationWidget() { return 0; } + QWidget* aclWidget() { return 0; } + +private: + QWeakPointer< SipPlugin > m_sipPlugin; +}; + +} +} + +#endif diff --git a/src/config.h.in b/src/config.h.in index 42e6a346b..0cdbb948e 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -20,6 +20,5 @@ #cmakedefine LIBLASTFM_FOUND #cmakedefine GLOOX_FOUND #cmakedefine QCA2_FOUND -#cmakedefine LIBATTICA_FOUND #endif // CONFIG_H_IN diff --git a/src/configdelegatebase.cpp b/src/configdelegatebase.cpp deleted file mode 100644 index ce6f2baae..000000000 --- a/src/configdelegatebase.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - Copyright (C) 2011 Leo Franchi - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - - -#include "configdelegatebase.h" - -#include -#include -#include - -#include "utils/tomahawkutils.h" -#include "utils/logger.h" - -ConfigDelegateBase::ConfigDelegateBase ( QObject* parent ) - : QStyledItemDelegate ( parent ) -{ - -} - - -QSize -ConfigDelegateBase::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const -{ - int width = QStyledItemDelegate::sizeHint( option, index ).width(); - - QStyleOptionViewItemV4 opt = option; - initStyleOption( &opt, index ); - - - QFont name = opt.font; - name.setPointSize( name.pointSize() + 2 ); - name.setBold( true ); - - QFont path = opt.font; - path.setItalic( true ); - path.setPointSize( path.pointSize() - 1 ); - - - QFontMetrics bfm( name ); - QFontMetrics sfm( path ); - return QSize( width, 2 * PADDING + bfm.height() + sfm.height() ); -} - -void -ConfigDelegateBase::drawCheckBox( QStyleOptionViewItemV4& opt, QPainter* p, const QWidget* w ) const -{ - QStyle* style = w ? w->style() : QApplication::style(); - opt.checkState == Qt::Checked ? opt.state |= QStyle::State_On : opt.state |= QStyle::State_Off; - style->drawPrimitive( QStyle::PE_IndicatorViewItemCheck, &opt, p, w ); -} - - -void -ConfigDelegateBase::drawConfigWrench ( QPainter* painter, QStyleOptionViewItemV4& opt, QStyleOptionToolButton& topt ) const -{ - const QWidget* w = opt.widget; - QStyle* style = w ? w->style() : QApplication::style(); - - // draw it the same size as the check belox - topt.font = opt.font; - topt.icon = QIcon( RESPATH "images/configure.png" ); - topt.iconSize = QSize( 16, 16 ); - topt.subControls = QStyle::SC_ToolButton; - topt.activeSubControls = QStyle::SC_None; - topt.features = QStyleOptionToolButton::None; - bool pressed = ( m_configPressed == opt.index ); - topt.state = pressed ? QStyle::State_On : QStyle::State_Raised; - if( opt.state & QStyle::State_MouseOver || pressed ) - topt.state |= QStyle::State_HasFocus; - style->drawComplexControl( QStyle::CC_ToolButton, &topt, painter, w ); -} - -bool -ConfigDelegateBase::editorEvent ( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) -{ - QStyleOptionViewItemV4 viewOpt( option ); - initStyleOption( &viewOpt, index ); - - if( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick ) { - m_configPressed = QModelIndex(); - - QMouseEvent* me = static_cast< QMouseEvent* >( event ); - if( me->button() != Qt::LeftButton || !checkRectForIndex( option, index ).contains( me->pos() ) ) - return false; - - // eat the double click events inside the check rect - if( event->type() == QEvent::MouseButtonDblClick ) { - return true; - } - - Qt::CheckState curState = static_cast< Qt::CheckState >( index.data( Qt::CheckStateRole ).toInt() ); - Qt::CheckState newState = curState == Qt::Checked ? Qt::Unchecked : Qt::Checked; - return model->setData( index, newState, Qt::CheckStateRole ); - - } else if( event->type() == QEvent::MouseButtonPress ) { - QMouseEvent* me = static_cast< QMouseEvent* >( event ); - if( me->button() == Qt::LeftButton && configRectForIndex( option, index ).contains( me->pos() ) ) { - m_configPressed = index; - - emit configPressed( index ); - return true; - } - } - - return QStyledItemDelegate::editorEvent( event, model, option, index ); -} diff --git a/src/configdelegatebase.h b/src/configdelegatebase.h index d86baad1a..bce0581c9 100644 --- a/src/configdelegatebase.h +++ b/src/configdelegatebase.h @@ -36,16 +36,15 @@ public: virtual bool editorEvent ( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); // if you want to use a checkbox, you need to have this say where to paint it - virtual QRect checkRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const = 0; + virtual QRect checkRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx, int role ) const = 0; // if you want to use a config wrench, you need to have this say where to paint it virtual QRect configRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const = 0; + virtual QList extraCheckRoles() const { return QList(); } signals: void configPressed( const QModelIndex& idx ); protected: - void drawCheckBox( QStyleOptionViewItemV4& opt, QPainter* p, const QWidget* w ) const; - void drawConfigWrench( QPainter* painter, QStyleOptionViewItemV4& option, QStyleOptionToolButton& topt ) const; private: QModelIndex m_configPressed; diff --git a/src/delegateconfigwrapper.h b/src/delegateconfigwrapper.h index ef737b33b..4a2d4bfd1 100644 --- a/src/delegateconfigwrapper.h +++ b/src/delegateconfigwrapper.h @@ -22,28 +22,28 @@ #include #include #include +#include class DelegateConfigWrapper : public QDialog { Q_OBJECT public: - DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent, Qt::WindowFlags flags = 0 ) : QDialog( parent, flags ), m_widget( conf ) + DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent, Qt::WindowFlags flags = 0 ) : QDialog( parent, flags ), m_widget( conf ), m_deleted( false ) { m_widget->setWindowFlags( Qt::Sheet ); #ifdef Q_WS_MAC m_widget->setVisible( true ); #endif - setWindowTitle( title ); QVBoxLayout* v = new QVBoxLayout( this ); v->setContentsMargins( 0, 0, 0, 0 ); v->addWidget( m_widget ); - QDialogButtonBox* buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this ); - m_okButton = buttons->button( QDialogButtonBox::Ok ); - connect( buttons, SIGNAL( clicked( QAbstractButton*) ), this, SLOT( closed( QAbstractButton* ) ) ); + m_buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this ); + m_okButton = m_buttons->button( QDialogButtonBox::Ok ); + connect( m_buttons, SIGNAL( clicked( QAbstractButton*) ), this, SLOT( closed( QAbstractButton* ) ) ); connect( this, SIGNAL( rejected() ), this, SLOT( rejected() ) ); - v->addWidget( buttons ); + v->addWidget( m_buttons ); setLayout( v ); @@ -59,6 +59,17 @@ public: #endif } + + ~DelegateConfigWrapper() {} + + void setShowDelete( bool del ) + { + if ( del ) + m_deleteButton = m_buttons->addButton( tr( "Delete Account" ), QDialogButtonBox::DestructiveRole ); + } + + bool deleted() const { return m_deleted; } + public slots: void toggleOkButton( bool dataError ) { @@ -75,6 +86,12 @@ public slots: QDialogButtonBox* buttons = qobject_cast< QDialogButtonBox* >( sender() ); if( buttons->standardButton( b ) == QDialogButtonBox::Ok ) done( QDialog::Accepted ); + else if ( b == m_deleteButton ) + { + m_deleted = true; + emit closedWithDelete(); + reject(); + } else done( QDialog::Rejected ); } @@ -97,9 +114,14 @@ public slots: show(); } +signals: + void closedWithDelete(); + private: + QDialogButtonBox* m_buttons; QWidget* m_widget; - QPushButton* m_okButton; + QPushButton *m_okButton, *m_deleteButton; + bool m_deleted; }; #endif diff --git a/src/diagnosticsdialog.cpp b/src/diagnosticsdialog.cpp index 050c435a6..cc77abab5 100644 --- a/src/diagnosticsdialog.cpp +++ b/src/diagnosticsdialog.cpp @@ -90,81 +90,81 @@ void DiagnosticsDialog::updateLogView() log.append("\n\n"); - // Peers - log.append("SIP PLUGINS:\n"); - QList< Tomahawk::source_ptr > sources = SourceList::instance()->sources( true ); - Q_FOREACH(SipPlugin *sip, SipHandler::instance()->allPlugins()) - { - Q_ASSERT(sip); - QString stateString; - switch( sip->connectionState() ) - { - case SipPlugin::Connecting: - stateString = "Connecting"; - break; + // Peers / Accounts, TODO + log.append("ACCOUNTS:\n"); +// QList< Tomahawk::source_ptr > sources = SourceList::instance()->sources( true ); +// Q_FOREACH(SipPlugin *sip, SipHandler::instance()->allPlugins()) +// { +// Q_ASSERT(sip); +// QString stateString; +// switch( sip->connectionState() ) +// { +// case SipPlugin::Connecting: +// stateString = "Connecting"; +// break; +// +// case SipPlugin::Connected: +// stateString = "Connected"; +// break; +// +// case SipPlugin::Disconnected: +// stateString = "Disconnected"; +// break; +// case SipPlugin::Disconnecting: +// stateString = "Disconnecting"; +// } +// log.append( +// QString(" %2 (%1): %3 (%4)\n") +// .arg(sip->account()->accountServiceName()) +// .arg(sip->friendlyName()) +// .arg(sip->account()->accountFriendlyName()) +// .arg(stateString) +// ); - case SipPlugin::Connected: - stateString = "Connected"; - break; - - case SipPlugin::Disconnected: - stateString = "Disconnected"; - break; - case SipPlugin::Disconnecting: - stateString = "Disconnecting"; - } - log.append( - QString(" %2 (%1): %3 (%4)\n") - .arg(sip->name()) - .arg(sip->friendlyName()) - .arg(sip->accountName()) - .arg(stateString) - ); - - Q_FOREACH( const QString &peerId, sip->peersOnline() ) - { - /* enable this again, when we check the source has this peerId - bool connected = false; - Q_FOREACH( const Tomahawk::source_ptr &source, sources ) - { - if( source->controlConnection() ) - { - connected = true; - break; - } - }*/ - - QString versionString = SipHandler::instance()->versionString( peerId ); - SipInfo sipInfo = SipHandler::instance()->sipInfo( peerId ); - if( !sipInfo.isValid() ) - log.append( - QString(" %1: %2 %3" /*"(%4)"*/ "\n") - .arg( peerId ) - .arg( "sipinfo invalid" ) - .arg( versionString ) - // .arg( connected ? "connected" : "not connected") - ); - else if( sipInfo.isVisible() ) - log.append( - QString(" %1: %2:%3 %4" /*" (%5)"*/ "\n") - .arg( peerId ) - .arg( sipInfo.host().hostName() ) - .arg( sipInfo.port() ) - .arg( versionString ) - // .arg( connected ? "connected" : "not connected") - - ); - else - log.append( - QString(" %1: visible: false %2" /*" (%3)"*/ "\n") - .arg( peerId ) - .arg( versionString ) - // .arg( connected ? "connected" : "not connected") - - ); - } - log.append("\n"); - } +// Q_FOREACH( const QString &peerId, sip->peersOnline() ) +// { +// /* enable this again, when we check the source has this peerId +// bool connected = false; +// Q_FOREACH( const Tomahawk::source_ptr &source, sources ) +// { +// if( source->controlConnection() ) +// { +// connected = true; +// break; +// } +// }*/ +// +// QString versionString = SipHandler::instance()->versionString( peerId ); +// SipInfo sipInfo = SipHandler::instance()->sipInfo( peerId ); +// if( !sipInfo.isValid() ) +// log.append( +// QString(" %1: %2 %3" /*"(%4)"*/ "\n") +// .arg( peerId ) +// .arg( "sipinfo invalid" ) +// .arg( versionString ) +// // .arg( connected ? "connected" : "not connected") +// ); +// else if( sipInfo.isVisible() ) +// log.append( +// QString(" %1: %2:%3 %4" /*" (%5)"*/ "\n") +// .arg( peerId ) +// .arg( sipInfo.host().hostName() ) +// .arg( sipInfo.port() ) +// .arg( versionString ) +// // .arg( connected ? "connected" : "not connected") +// +// ); +// else +// log.append( +// QString(" %1: visible: false %2" /*" (%3)"*/ "\n") +// .arg( peerId ) +// .arg( versionString ) +// // .arg( connected ? "connected" : "not connected") +// +// ); +// } +// log.append("\n"); +// } ui->logView->setPlainText(log); } diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 1fe452142..2ecbc6fe3 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -32,6 +32,8 @@ #include #include "utils/logger.h" +#include "accounts/ResolverAccount.h" +#include "accounts/AccountManager.h" using namespace Attica; @@ -133,6 +135,20 @@ AtticaManager::resolvers() const } +Content +AtticaManager::resolverForId( const QString& id ) const +{ + foreach ( const Attica::Content& c, m_resolvers ) + { + if ( c.id() == id ) + return c; + } + + return Content(); +} + + + AtticaManager::ResolverState AtticaManager::resolverState ( const Content& resolver ) const { @@ -196,6 +212,38 @@ AtticaManager::userHasRated( const Content& c ) const } +bool +AtticaManager::hasCustomAccountForAttica( const QString &id ) const +{ + // Only last.fm at the moment contains a custom account + if ( id == "lastfm" ) + return true; + + return false; +} + + +Tomahawk::Accounts::Account* +AtticaManager::customAccountForAttica( const QString &id ) const +{ + return m_customAccounts.value( id ); +} + + +void +AtticaManager::registerCustomAccount( const QString &atticaId, Tomahawk::Accounts::Account *account ) +{ + m_customAccounts.insert( atticaId, account ); +} + + +AtticaManager::Resolver +AtticaManager::resolverData(const QString &atticaId) const +{ + return m_resolverStates.value( atticaId ); +} + + void AtticaManager::providerAdded( const Provider& provider ) { @@ -218,6 +266,24 @@ AtticaManager::resolversList( BaseJob* j ) m_resolvers = job->itemList(); m_resolverStates = TomahawkSettingsGui::instanceGui()->atticaResolverStates(); + // Sanity check. if any resolvers are installed that don't exist on the hd, remove them. + foreach ( const QString& rId, m_resolverStates.keys() ) + { + if ( m_resolverStates[ rId ].state == Installed || + m_resolverStates[ rId ].state == NeedsUpgrade ) + { + // Guess location on disk + QDir dir( QString( "%1/atticaresolvers/%2" ).arg( TomahawkUtils::appDataDir().absolutePath() ).arg( rId ) ); + if ( !dir.exists() ) + { + // Uh oh + qWarning() << "Found attica resolver marked as installed that didn't exist on disk! Setting to uninstalled: " << rId << dir.absolutePath(); + m_resolverStates[ rId ].state = Uninstalled; + TomahawkSettingsGui::instanceGui()->setAtticaResolverState( rId, Uninstalled ); + } + } + } + // load icon cache from disk, and fetch any we are missing loadPixmapsFromCache(); @@ -236,6 +302,8 @@ AtticaManager::resolversList( BaseJob* j ) } syncServerData(); + + emit resolversLoaded( m_resolvers ); } @@ -299,7 +367,7 @@ AtticaManager::syncServerData() void -AtticaManager::installResolver( const Content& resolver, bool autoEnable ) +AtticaManager::installResolver( const Content& resolver, bool autoCreateAccount ) { Q_ASSERT( !resolver.id().isNull() ); @@ -313,7 +381,7 @@ AtticaManager::installResolver( const Content& resolver, bool autoEnable ) ItemJob< DownloadItem >* job = m_resolverProvider.downloadLink( resolver.id() ); connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( resolverDownloadFinished( Attica::BaseJob* ) ) ); job->setProperty( "resolverId", resolver.id() ); - job->setProperty( "autoEnable", autoEnable ); + job->setProperty( "createAccount", autoCreateAccount ); job->start(); } @@ -328,12 +396,11 @@ AtticaManager::upgradeResolver( const Content& resolver ) if ( !m_resolverStates.contains( resolver.id() ) || m_resolverStates[ resolver.id() ].state != NeedsUpgrade ) return; - const bool enabled = TomahawkSettings::instance()->enabledScriptResolvers().contains( m_resolverStates[ resolver.id() ].scriptPath ); m_resolverStates[ resolver.id() ].state = Upgrading; emit resolverStateChanged( resolver.id() ); uninstallResolver( resolver ); - installResolver( resolver, enabled ); + installResolver( resolver, false ); } @@ -350,7 +417,7 @@ AtticaManager::resolverDownloadFinished ( BaseJob* j ) QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) ); connect( reply, SIGNAL( finished() ), this, SLOT( payloadFetched() ) ); reply->setProperty( "resolverId", job->property( "resolverId" ) ); - reply->setProperty( "autoEnable", job->property( "autoEnable" ) ); + reply->setProperty( "createAccount", job->property( "createAccount" ) ); } else { @@ -386,10 +453,14 @@ AtticaManager::payloadFetched() // update with absolute, not relative, path m_resolverStates[ resolverId ].scriptPath = resolverPath; - const bool autoEnable = reply->property( "autoEnable" ).toBool(); + if ( reply->property( "createAccount" ).toBool() ) + { + // Do the install / add to tomahawk + Tomahawk::Accounts::Account* resolver = Tomahawk::Accounts::ResolverAccountFactory::createFromPath( resolverPath, true ); + Tomahawk::Accounts::AccountManager::instance()->addAccount( resolver ); + TomahawkSettings::instance()->addAccount( resolver->accountId() ); + } - // Do the install / add to tomahawk - Tomahawk::Pipeline::instance()->addScriptResolver( resolverPath, autoEnable ); m_resolverStates[ resolverId ].state = Installed; TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_resolverStates ); emit resolverInstalled( resolverId ); @@ -475,6 +546,9 @@ AtticaManager::extractPayload( const QString& filename, const QString& resolverI void AtticaManager::uninstallResolver( const QString& pathToResolver ) { + // when is this used? find and fix + Q_ASSERT(false); + // User manually removed a resolver not through attica dialog, simple remove QRegExp r( ".*([^/]*)/contents/code/main.js" ); r.indexIn( pathToResolver ); @@ -506,9 +580,21 @@ AtticaManager::uninstallResolver( const Content& resolver ) m_resolverStates[ resolver.id() ].state = Uninstalled; TomahawkSettingsGui::instanceGui()->setAtticaResolverState( resolver.id(), Uninstalled ); + + // remove account as well + QList< Tomahawk::Accounts::Account* > accounts = Tomahawk::Accounts::AccountManager::instance()->accounts( Tomahawk::Accounts::ResolverType ); + foreach ( Tomahawk::Accounts::Account* account, accounts ) + { + if ( Tomahawk::Accounts::AtticaResolverAccount* atticaAccount = qobject_cast< Tomahawk::Accounts::AtticaResolverAccount* >( account ) ) + { + if ( atticaAccount->atticaId() == resolver.id() ) // this is the account we want to remove + { + Tomahawk::Accounts::AccountManager::instance()->removeAccount( atticaAccount ); + } + } + } } - Tomahawk::Pipeline::instance()->removeScriptResolver( pathFromId( resolver.id() ) ); doResolverRemove( resolver.id() ); } diff --git a/src/libtomahawk/AtticaManager.h b/src/libtomahawk/AtticaManager.h index 5c302ddda..09b441dd5 100644 --- a/src/libtomahawk/AtticaManager.h +++ b/src/libtomahawk/AtticaManager.h @@ -26,12 +26,12 @@ #include #include "dllmacro.h" +#include "accounts/Account.h" -#ifdef LIBATTICA_FOUND #include #include #include -#endif + class DLLEXPORT AtticaManager : public QObject { @@ -71,18 +71,13 @@ public: } explicit AtticaManager ( QObject* parent = 0 ); -#ifdef LIBATTICA_FOUND - virtual ~AtticaManager(); -#else - virtual ~AtticaManager() {} -#endif - -#ifdef LIBATTICA_FOUND bool resolversLoaded() const; Attica::Content::List resolvers() const; + Attica::Content resolverForId( const QString& id ) const; + ResolverState resolverState( const Attica::Content& resolver ) const; QPixmap iconForResolver( const Attica::Content& id ); // Looks up in icon cache @@ -93,12 +88,22 @@ public: void uploadRating( const Attica::Content& c ); bool userHasRated( const Attica::Content& c ) const; + /** + If the resolver coming from libattica has a native custom c++ account + as well. For example the last.fm account. + */ + bool hasCustomAccountForAttica( const QString& id ) const; + Tomahawk::Accounts::Account* customAccountForAttica( const QString& id ) const; + void registerCustomAccount( const QString& atticaId, Tomahawk::Accounts::Account* account ); + + AtticaManager::Resolver resolverData( const QString& atticaId ) const; + public slots: - void installResolver( const Attica::Content& resolver, bool autoEnable = true ); + void installResolver( const Attica::Content& resolver, bool autoCreateAccount = true ); void upgradeResolver( const Attica::Content& resolver ); signals: - void resolversReloaded( const Attica::Content::List& resolvers ); + void resolversLoaded( const Attica::Content::List& resolvers ); void resolverStateChanged( const QString& resolverId ); void resolverInstalled( const QString& resolverId ); @@ -125,13 +130,25 @@ private: Attica::Provider m_resolverProvider; Attica::Content::List m_resolvers; StateHash m_resolverStates; -#endif + + QMap< QString, Tomahawk::Accounts::Account* > m_customAccounts; static AtticaManager* s_instance; }; -#ifdef LIBATTICA_FOUND +class DLLEXPORT CustomAtticaAccount : public Tomahawk::Accounts::Account +{ + Q_OBJECT +public: + virtual ~CustomAtticaAccount() {} + + virtual Attica::Content atticaContent() const = 0; + +protected: + // No, you can't. + CustomAtticaAccount( const QString& id ) : Tomahawk::Accounts::Account( id ) {} +}; + Q_DECLARE_METATYPE( Attica::Content ); -#endif #endif // ATTICAMANAGER_H diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 55fbc8da8..36fc583ae 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -102,8 +102,6 @@ set( libGuiSources resolvers/scriptresolver.cpp resolvers/qtscriptresolver.cpp - sip/SipModel.cpp - utils/widgetdragfilter.cpp utils/xspfgenerator.cpp utils/jspfloader.cpp @@ -229,8 +227,6 @@ set( libGuiHeaders resolvers/scriptresolver.h resolvers/qtscriptresolver.h - sip/SipModel.h - utils/widgetdragfilter.h utils/xspfgenerator.h utils/jspfloader.h @@ -311,6 +307,14 @@ set( libSources EchonestCatalogSynchronizer.cpp + accounts/AccountManager.cpp + accounts/Account.cpp + accounts/AccountModel.cpp + accounts/AccountModelFilterProxy.cpp + accounts/ResolverAccount.cpp + accounts/LastFmAccount.cpp + accounts/LastFmConfig.cpp + sip/SipPlugin.cpp sip/SipHandler.cpp sip/sipinfo.cpp @@ -445,9 +449,16 @@ set( libHeaders albumplaylistinterface.h playlist.h playlistplaylistinterface.h - viewpage.h + accounts/Account.h + accounts/AccountManager.h + accounts/AccountModel.h + accounts/AccountModelFilterProxy.h + accounts/ResolverAccount.h + accounts/LastFmAccount.h + accounts/LastFmConfig.h + EchonestCatalogSynchronizer.h sip/SipPlugin.h @@ -548,6 +559,10 @@ set( libHeaders ) set( libHeaders_NoMOC + viewpage.h + + accounts/Account.h + infosystem/infoplugins/unix/imageconverter.h taghandlers/tag.h @@ -575,6 +590,7 @@ set( libUI ${libUI} playlist/queueview.ui context/ContextWidget.ui infobar/infobar.ui + accounts/LastFmConfig.ui ) include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.. .. diff --git a/src/libtomahawk/ExternalResolver.h b/src/libtomahawk/ExternalResolver.h index a63f42396..bf3a01082 100644 --- a/src/libtomahawk/ExternalResolver.h +++ b/src/libtomahawk/ExternalResolver.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +25,7 @@ #include "dllmacro.h" #include "resolver.h" +#include #include @@ -64,7 +66,7 @@ public slots: virtual void stop() = 0; signals: - void changed(); // if config widget was added/removed + void changed(); // if config widget was added/removed, name changed, etc protected: void setFilePath( const QString& path ) { m_filePath = path; } diff --git a/src/libtomahawk/TomahawkSettingsGui.cpp b/src/libtomahawk/TomahawkSettingsGui.cpp index b9b95bfc1..32f70a4b8 100644 --- a/src/libtomahawk/TomahawkSettingsGui.cpp +++ b/src/libtomahawk/TomahawkSettingsGui.cpp @@ -21,53 +21,8 @@ #include #include "settingsdialog.h" -// #include -// -// #include "sip/SipHandler.h" -// #include "playlistinterface.h" -// -// #include "utils/logger.h" -// #include "utils/tomahawkutils.h" -// -// #include "database/databasecommand_updatesearchindex.h" -// #include "database/database.h" - -#define VERSION 5 - using namespace Tomahawk; -inline QDataStream& operator<<(QDataStream& out, const AtticaManager::StateHash& states) -{ - out << VERSION; - out << (quint32)states.count(); - foreach( const QString& key, states.keys() ) - { - AtticaManager::Resolver resolver = states[ key ]; - out << key << resolver.version << resolver.scriptPath << (qint32)resolver.state << resolver.userRating; - } - return out; -} - - -inline QDataStream& operator>>(QDataStream& in, AtticaManager::StateHash& states) -{ - quint32 count = 0, version = 0; - in >> version; - in >> count; - for ( uint i = 0; i < count; i++ ) - { - QString key, version, scriptPath; - qint32 state, userRating; - in >> key; - in >> version; - in >> scriptPath; - in >> state; - in >> userRating; - states[ key ] = AtticaManager::Resolver( version, scriptPath, userRating, (AtticaManager::ResolverState)state ); - } - return in; -} - TomahawkSettingsGui* TomahawkSettingsGui::instanceGui() { @@ -78,8 +33,6 @@ TomahawkSettingsGui::instanceGui() TomahawkSettingsGui::TomahawkSettingsGui( QObject* parent ) : TomahawkSettings( parent ) { - qRegisterMetaType< AtticaManager::StateHash >( "AtticaManager::StateHash" ); - qRegisterMetaTypeStreamOperators("AtticaManager::StateHash"); } diff --git a/src/libtomahawk/accounts/Account.cpp b/src/libtomahawk/accounts/Account.cpp new file mode 100644 index 000000000..208cc7bac --- /dev/null +++ b/src/libtomahawk/accounts/Account.cpp @@ -0,0 +1,211 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "Account.h" + +namespace Tomahawk +{ + +namespace Accounts +{ + +QString +accountTypeToString( AccountType type ) +{ + switch ( type ) + { + case SipType: + return QObject::tr( "Friend Finders" ); + case ResolverType: + return QObject::tr( "Music Finders" ); + case InfoType: + case StatusPushType: + return QObject::tr( "Status Updaters" ); + } + + return QString(); +} + + +Account::Account( const QString& accountId ) + : QObject() + , m_enabled( false ) + , m_autoConnect( false ) + , m_accountId( accountId ) +{ + connect( this, SIGNAL( error( int, QString ) ), this, SLOT( onError( int,QString ) ) ); + connect( this, SIGNAL( connectionStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ) , this, SLOT( onConnectionStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ) ); + + loadFromConfig( accountId ); +} + + +Account::~Account() +{ + sync(); +} + + +QWidget* +Account::configurationWidget() +{ + return 0; +} + + +QWidget* +Account::aclWidget() +{ + return 0; +} + + +QPixmap +Account::icon() const +{ + return QPixmap(); +} + +void +Account::authenticate() +{ + return; +} + + +void +Account::deauthenticate() +{ + return; +} + + +bool +Account::isAuthenticated() const +{ + return false; +} + + +void +Account::onError( int errorCode, const QString& error ) +{ + Q_UNUSED( errorCode ); + + QMutexLocker locker( &m_mutex ); + m_cachedError = error; +} + + +void +Account::onConnectionStateChanged( Account::ConnectionState ) +{ + m_cachedError.clear(); +} + + +void +Account::syncConfig() +{ + TomahawkSettings* s = TomahawkSettings::instance(); + s->beginGroup( "accounts/" + m_accountId ); + s->setValue( "accountfriendlyname", m_accountFriendlyName ); + s->setValue( "enabled", m_enabled ); + s->setValue( "autoconnect", m_autoConnect ); + s->setValue( "credentials", m_credentials ); + s->setValue( "configuration", m_configuration ); + s->setValue( "acl", m_acl ); + s->setValue( "types", m_types ); + s->endGroup(); + s->sync(); +} + + +void +Account::loadFromConfig( const QString& accountId ) +{ + m_accountId = accountId; + TomahawkSettings* s = TomahawkSettings::instance(); + s->beginGroup( "accounts/" + m_accountId ); + m_accountFriendlyName = s->value( "accountfriendlyname", QString() ).toString(); + m_enabled = s->value( "enabled", false ).toBool(); + m_autoConnect = s->value( "autoconnect", false ).toBool(); + m_credentials = s->value( "credentials", QVariantHash() ).toHash(); + m_configuration = s->value( "configuration", QVariantHash() ).toHash(); + m_acl = s->value( "acl", QVariantMap() ).toMap(); + m_types = s->value( "types", QStringList() ).toStringList(); + s->endGroup(); +} + + +void +Account::removeFromConfig() +{ + TomahawkSettings* s = TomahawkSettings::instance(); + s->beginGroup( "accounts/" + m_accountId ); + s->remove( "accountfriendlyname" ); + s->remove( "enabled" ); + s->remove( "autoconnect" ); + s->remove( "credentials" ); + s->remove( "configuration" ); + s->remove( "acl" ); + s->remove( "types" ); + s->endGroup(); + s->remove( "accounts/" + m_accountId ); +} + + +void +Account::setTypes( AccountTypes types ) +{ + QMutexLocker locker( &m_mutex ); + m_types = QStringList(); + if ( types & InfoType ) + m_types << "InfoType"; + if ( types & SipType ) + m_types << "SipType"; + if ( types & ResolverType ) + m_types << "ResolverType"; + if ( types & StatusPushType ) + m_types << "StatusPushType"; + syncConfig(); +} + + +AccountTypes +Account::types() const +{ + QMutexLocker locker( &m_mutex ); + AccountTypes types; + if ( m_types.contains( "InfoType" ) ) + types |= InfoType; + if ( m_types.contains( "SipType" ) ) + types |= SipType; + if ( m_types.contains( "ResolverType" ) ) + types |= ResolverType; + if ( m_types.contains( "StatusPushType" ) ) + types |= StatusPushType; + + return types; +} + + +} + +} \ No newline at end of file diff --git a/src/libtomahawk/accounts/Account.h b/src/libtomahawk/accounts/Account.h new file mode 100644 index 000000000..73a7dedee --- /dev/null +++ b/src/libtomahawk/accounts/Account.h @@ -0,0 +1,193 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef ACCOUNT_H +#define ACCOUNT_H + +#include +#include +#include +#include +#include +#include + +#include "typedefs.h" +#include "dllmacro.h" +#include "tomahawksettings.h" + +class SipPlugin; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + class InfoPlugin; +} + +namespace Accounts +{ + +enum AccountType +{ + NoType = 0x00, + + InfoType = 0x01, + SipType = 0x02, + ResolverType = 0x04, + StatusPushType = 0x08 +}; + +DLLEXPORT QString accountTypeToString( AccountType type ); + +Q_DECLARE_FLAGS(AccountTypes, AccountType); + +inline QString generateId( const QString &factoryId ) +{ + QString uniq = QUuid::createUuid().toString().mid( 1, 8 ); + return factoryId + "_" + uniq; +} + +class DLLEXPORT Account : public QObject +{ + Q_OBJECT + +public: + enum AuthErrorCode { AuthError, ConnectionError }; + enum ConnectionState { Disconnected, Connecting, Connected, Disconnecting }; + + explicit Account( const QString &accountId ); + virtual ~Account(); + + QString accountServiceName() const { QMutexLocker locker( &m_mutex ); return m_accountServiceName; } // e.g. "Twitter", "Last.fm" + QString accountFriendlyName() const { QMutexLocker locker( &m_mutex ); return m_accountFriendlyName; } // e.g. screen name on the service, JID, etc. + bool enabled() const { QMutexLocker locker( &m_mutex ); return m_enabled; } + bool autoConnect() const { QMutexLocker locker( &m_mutex ); return m_autoConnect; } + QString accountId() const { QMutexLocker locker( &m_mutex ); return m_accountId; } + + QVariantHash configuration() const { QMutexLocker locker( &m_mutex ); return m_configuration; } + + /** + * Configuration widgets can have a "dataError( bool )" signal to enable/disable the OK button in their wrapper dialogs. + */ + virtual QWidget* configurationWidget() = 0; + virtual void saveConfig() {} // called when the widget has been edited. save values from config widget, call sync() to write to disk account generic settings + + QVariantHash credentials() const { QMutexLocker locker( &m_mutex ); return m_credentials; } + + QVariantMap acl() const { QMutexLocker locker( &m_mutex ); return m_acl; } + virtual QWidget* aclWidget() = 0; + + virtual QPixmap icon() const = 0; + + virtual ConnectionState connectionState() const = 0; + virtual bool isAuthenticated() const = 0; + + virtual QString errorMessage() const { QMutexLocker locker( &m_mutex ); return m_cachedError; } + + virtual Tomahawk::InfoSystem::InfoPlugin* infoPlugin() = 0; + virtual SipPlugin* sipPlugin() = 0; + + AccountTypes types() const; + + void setAccountServiceName( const QString &serviceName ) { QMutexLocker locker( &m_mutex ); m_accountServiceName = serviceName; } + void setAccountFriendlyName( const QString &friendlyName ) { QMutexLocker locker( &m_mutex ); m_accountFriendlyName = friendlyName; } + void setEnabled( bool enabled ) { QMutexLocker locker( &m_mutex ); m_enabled = enabled; } + void setAutoConnect( bool autoConnect ) { QMutexLocker locker( &m_mutex ); m_autoConnect = autoConnect; } + void setAccountId( const QString &accountId ) { QMutexLocker locker( &m_mutex ); m_accountId = accountId; } + void setCredentials( const QVariantHash &credentialHash ) { QMutexLocker locker( &m_mutex ); m_credentials = credentialHash; } + void setConfiguration( const QVariantHash &configuration ) { QMutexLocker locker( &m_mutex ); m_configuration = configuration; } + void setAcl( const QVariantMap &acl ) { QMutexLocker locker( &m_mutex ); m_acl = acl; } + void setTypes( AccountTypes types ); + + void sync() { QMutexLocker locker( &m_mutex ); syncConfig(); }; + + /** + * Removes all the settings held in the config file for this account instance + * + * Re-implement if you have saved additional files or config settings outside the built-in ones + */ + virtual void removeFromConfig(); + +public slots: + virtual void authenticate() = 0; + virtual void deauthenticate() = 0; + +signals: + void error( int errorId, const QString& errorStr ); + void connectionStateChanged( Tomahawk::Accounts::Account::ConnectionState state ); + + void configurationChanged(); + +protected: + virtual void loadFromConfig( const QString &accountId ); + virtual void syncConfig(); + +private slots: + void onConnectionStateChanged( Tomahawk::Accounts::Account::ConnectionState ); + void onError( int, const QString& ); + +private: + QString m_accountServiceName; + QString m_accountFriendlyName; + QString m_cachedError; + bool m_enabled; + bool m_autoConnect; + QString m_accountId; + QVariantHash m_credentials; + QVariantHash m_configuration; + QVariantMap m_acl; + QStringList m_types; + mutable QMutex m_mutex; +}; + +class DLLEXPORT AccountFactory : public QObject +{ + Q_OBJECT +public: + AccountFactory() {} + virtual ~AccountFactory() {} + + // display name for plugin + virtual QString prettyName() const = 0; + // internal name + virtual QString factoryId() const = 0; + // description to be shown when user views a list of account types + virtual QString description() const = 0; + // if the user can create multiple + virtual bool isUnique() const { return false; } + + virtual QPixmap icon() const { return QPixmap(); } + virtual bool allowUserCreation() const { return true; } + + // What are the supported types for accounts this factory creates? + virtual AccountTypes types() const = 0; + + virtual Account* createAccount( const QString& accountId = QString() ) = 0; +}; + +}; + +}; + +Q_DECLARE_INTERFACE( Tomahawk::Accounts::AccountFactory, "tomahawk.AccountFactory/1.0" ) + +Q_DECLARE_METATYPE( QList< Tomahawk::Accounts::Account* > ) +Q_DECLARE_METATYPE( Tomahawk::Accounts::AccountTypes ) +#endif diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp new file mode 100644 index 000000000..bef708937 --- /dev/null +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -0,0 +1,410 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "AccountManager.h" +#include "config.h" +#include "sourcelist.h" +#include "ResolverAccount.h" +#include "LastFmAccount.h" + +#include +#include +#include +#include +#include +#include + +namespace Tomahawk +{ + +namespace Accounts +{ + + +AccountManager* AccountManager::s_instance = 0; + + +AccountManager* +AccountManager::instance() +{ + return s_instance; +} + + +AccountManager::AccountManager( QObject *parent ) + : QObject( parent ) +{ + s_instance = this; + + connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) ); + + loadPluginFactories( findPluginFactories() ); + + // We include the resolver factory manually, not in a plugin + ResolverAccountFactory* f = new ResolverAccountFactory(); + m_accountFactories[ f->factoryId() ] = f; + + LastFmAccountFactory* l = new LastFmAccountFactory(); + m_accountFactories[ l->factoryId() ] = l; +} + + +AccountManager::~AccountManager() +{ + delete SipHandler::instance(); + + disconnectAll(); + qDeleteAll( m_accounts ); +} + + +QStringList +AccountManager::findPluginFactories() +{ + QStringList paths; + QList< QDir > pluginDirs; + + QDir appDir( qApp->applicationDirPath() ); +#ifdef Q_WS_MAC + if ( appDir.dirName() == "MacOS" ) + { + // Development convenience-hack + appDir.cdUp(); + appDir.cdUp(); + appDir.cdUp(); + } +#endif + + QDir libDir( CMAKE_INSTALL_PREFIX "/lib" ); + + QDir lib64Dir( appDir ); + lib64Dir.cdUp(); + lib64Dir.cd( "lib64" ); + + pluginDirs << appDir << libDir << lib64Dir << QDir( qApp->applicationDirPath() ); + foreach ( const QDir& pluginDir, pluginDirs ) + { + tDebug() << Q_FUNC_INFO << "Checking directory for plugins:" << pluginDir; + foreach ( QString fileName, pluginDir.entryList( QStringList() << "*tomahawk_account_*.so" << "*tomahawk_account_*.dylib" << "*tomahawk_account_*.dll", QDir::Files ) ) + { + if ( fileName.startsWith( "libtomahawk_account" ) ) + { + const QString path = pluginDir.absoluteFilePath( fileName ); + if ( !paths.contains( path ) ) + paths << path; + } + } + } + + return paths; +} + + +void +AccountManager::loadPluginFactories( const QStringList& paths ) +{ + foreach ( QString fileName, paths ) + { + if ( !QLibrary::isLibrary( fileName ) ) + continue; + + tDebug() << Q_FUNC_INFO << "Trying to load plugin:" << fileName; + loadPluginFactory( fileName ); + } +} + + +bool +AccountManager::hasPluginWithFactory( const QString& factory ) const +{ + foreach( Account* account, m_accounts ) { + if( factoryFromId( account->accountId() ) == factory ) + return true; + } + return false; + +} + + +QString +AccountManager::factoryFromId( const QString& accountId ) const +{ + return accountId.split( "_" ).first(); +} + +AccountFactory* +AccountManager::factoryForAccount( Account* account ) const +{ + const QString factoryId = factoryFromId( account->accountId() ); + return m_accountFactories.value( factoryId, 0 ); +} + + +void +AccountManager::loadPluginFactory( const QString& path ) +{ + QPluginLoader loader( path ); + QObject* plugin = loader.instance(); + if ( !plugin ) + { + tDebug() << Q_FUNC_INFO << "Error loading plugin:" << loader.errorString(); + } + + AccountFactory* accountfactory = qobject_cast( plugin ); + if ( accountfactory ) + { + tDebug() << Q_FUNC_INFO << "Loaded plugin factory:" << loader.fileName() << accountfactory->factoryId() << accountfactory->prettyName(); + m_accountFactories[ accountfactory->factoryId() ] = accountfactory; + } else + { + tDebug() << Q_FUNC_INFO << "Loaded invalid plugin.." << loader.fileName(); + } +} + + + +void +AccountManager::enableAccount( Account* account ) +{ + if ( account->isAuthenticated() ) + return; + + account->authenticate(); + + account->setEnabled( true ); + m_enabledAccounts << account; + + account->sync(); +} + + +void +AccountManager::disableAccount( Account* account ) +{ + if ( !account->isAuthenticated() ) + return; + + account->deauthenticate(); + account->setEnabled( false ); + m_enabledAccounts.removeAll( account ); + + account->sync(); +} + + +void +AccountManager::connectAll() +{ + foreach( Account* acc, m_accounts ) + { + acc->authenticate(); + m_enabledAccounts << acc; + + } + m_connected = true; +} + + +void +AccountManager::disconnectAll() +{ + foreach( Account* acc, m_enabledAccounts ) + acc->deauthenticate(); + + m_enabledAccounts.clear(); + SourceList::instance()->removeAllRemote(); + m_connected = false; +} + + +void +AccountManager::toggleAccountsConnected() +{ + if ( m_connected ) + disconnectAll(); + else + connectAll(); +} + + +void +AccountManager::loadFromConfig() +{ + QStringList accountIds = TomahawkSettings::instance()->accounts(); + + foreach( const QString& accountId, accountIds ) + { + QString pluginFactory = factoryFromId( accountId ); + if( m_accountFactories.contains( pluginFactory ) ) + { + Account* account = loadPlugin( accountId ); + addAccount( account ); + } + } +} + +void +AccountManager::initSIP() +{ + tDebug() << Q_FUNC_INFO; + foreach( Account* account, accounts() ) + { + hookupAndEnable( account, true ); + } +} + + +Account* +AccountManager::loadPlugin( const QString& accountId ) +{ + QString factoryName = factoryFromId( accountId ); + + Q_ASSERT( m_accountFactories.contains( factoryName ) ); + + Account* account = m_accountFactories[ factoryName ]->createAccount( accountId ); + hookupAccount( account ); + + return account; +} + + +void +AccountManager::addAccount( Account* account ) +{ + tDebug() << Q_FUNC_INFO << "adding account plugin"; + m_accounts.append( account ); + + if ( account->types() & Accounts::SipType ) + m_accountsByAccountType[ Accounts::SipType ].append( account ); + if ( account->types() & Accounts::InfoType ) + m_accountsByAccountType[ Accounts::InfoType ].append( account ); + if ( account->types() & Accounts::ResolverType ) + m_accountsByAccountType[ Accounts::ResolverType ].append( account ); + if ( account->types() & Accounts::StatusPushType ) + m_accountsByAccountType[ Accounts::StatusPushType ].append( account ); + + if ( account->infoPlugin() ) + InfoSystem::InfoSystem::instance()->addInfoPlugin( account->infoPlugin() ); + + emit added( account ); +} + + +void +AccountManager::removeAccount( Account* account ) +{ + account->deauthenticate(); + + // emit before moving from list so accountmodel can get indexOf + emit removed( account ); + + m_accounts.removeAll( account ); + m_enabledAccounts.removeAll( account ); + m_connectedAccounts.removeAll( account ); + foreach ( AccountType type, m_accountsByAccountType.keys() ) + { + QList< Account* > accounts = m_accountsByAccountType.value( type ); + accounts.removeAll( account ); + m_accountsByAccountType[ type ] = accounts; + } + + TomahawkSettings::instance()->removeAccount( account->accountId() ); + + account->removeFromConfig(); + account->deleteLater(); +} + + +void +AccountManager::hookupAccount( Account* account ) const +{ + connect( account, SIGNAL( error( int, QString ) ), SLOT( onError( int, QString ) ) ); + connect( account, SIGNAL( connectionStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ), SLOT( onStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ) ); +} + + +void +AccountManager::hookupAndEnable( Account* account, bool startup ) +{ + SipPlugin* p = account->sipPlugin(); + if ( p ) + SipHandler::instance()->hookUpPlugin( p ); + + if ( account->enabled() && ( !startup || account->autoConnect() ) ) + { + account->authenticate(); + m_enabledAccounts << account; + } +} + + +void +AccountManager::onError( int code, const QString& msg ) +{ + Account* account = qobject_cast< Account* >( sender() ); + Q_ASSERT( account ); + + + qWarning() << "Failed to connect to SIP:" << account->accountFriendlyName() << code << msg; + + if ( code == Account::AuthError ) + { + emit authError( account ); + } + else + { + QTimer::singleShot( 10000, account, SLOT( authenticate() ) ); + } +} + +void +AccountManager::onSettingsChanged() +{ + foreach( Account* account, m_accounts ) + { + if ( account->types() & Accounts::SipType && account->sipPlugin() ) + account->sipPlugin()->checkSettings(); + } +} + + +void +AccountManager::onStateChanged( Account::ConnectionState state ) +{ + Account* account = qobject_cast< Account* >( sender() ); + Q_ASSERT( account ); + + if ( account->connectionState() == Account::Disconnected ) + { + m_connectedAccounts.removeAll( account ); + emit disconnected( account ); + } + else if ( account->connectionState() == Account::Connected ) + { + m_connectedAccounts << account; + emit connected( account ); + } + + emit stateChanged( account, state ); +} + + +}; + +}; diff --git a/src/libtomahawk/accounts/AccountManager.h b/src/libtomahawk/accounts/AccountManager.h new file mode 100644 index 000000000..969edd27f --- /dev/null +++ b/src/libtomahawk/accounts/AccountManager.h @@ -0,0 +1,108 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef ACCOUNTMANAGER_H +#define ACCOUNTMANAGER_H + +#include + +#include "typedefs.h" +#include "dllmacro.h" +#include "infosystem/infosystem.h" +#include "sip/SipPlugin.h" +#include "Account.h" + +namespace Tomahawk +{ + +namespace Accounts +{ + +class DLLEXPORT AccountManager : public QObject +{ + Q_OBJECT + +public: + static AccountManager* instance(); + + explicit AccountManager( QObject *parent ); + virtual ~AccountManager(); + + void loadFromConfig(); + void initSIP(); + + void enableAccount( Account* account ); + void disableAccount( Account* account ); + + QList< AccountFactory* > factories() const { return m_accountFactories.values(); } + bool hasPluginWithFactory( const QString& factory ) const; + AccountFactory* factoryForAccount( Account* account ) const; + + void addAccount( Account* account ); + void hookupAndEnable( Account* account, bool startup = false ); /// Hook up signals and start the plugin + void removeAccount( Account* account ); + + QList< Account* > accounts() const { return m_accounts; }; + QList< Account* > accounts( Tomahawk::Accounts::AccountType type ) const { return m_accountsByAccountType[ type ]; } + +public slots: + void connectAll(); + void disconnectAll(); + void toggleAccountsConnected(); + +signals: + void added( Tomahawk::Accounts::Account* ); + void removed( Tomahawk::Accounts::Account* ); + + void connected( Tomahawk::Accounts::Account* ); + void disconnected( Tomahawk::Accounts::Account* ); + void authError( Tomahawk::Accounts::Account* ); + + void stateChanged( Account* p, Accounts::Account::ConnectionState state ); + +private slots: + void onStateChanged( Tomahawk::Accounts::Account::ConnectionState state ); + void onError( int code, const QString& msg ); + + void onSettingsChanged(); +private: + QStringList findPluginFactories(); + void loadPluginFactories( const QStringList &paths ); + void loadPluginFactory( const QString &path ); + QString factoryFromId( const QString& accountId ) const; + + Account* loadPlugin( const QString &accountId ); + void hookupAccount( Account* ) const; + + QList< Account* > m_accounts; + QList< Account* > m_enabledAccounts; + QList< Account* > m_connectedAccounts; + bool m_connected; + + QHash< AccountType, QList< Account* > > m_accountsByAccountType; + QHash< QString, AccountFactory* > m_accountFactories; + + static AccountManager* s_instance; +}; + +}; + +}; + +#endif diff --git a/src/libtomahawk/accounts/AccountModel.cpp b/src/libtomahawk/accounts/AccountModel.cpp new file mode 100644 index 000000000..5775e1d24 --- /dev/null +++ b/src/libtomahawk/accounts/AccountModel.cpp @@ -0,0 +1,670 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "AccountModel.h" + +#include "Account.h" +#include "AccountModelNode.h" +#include "AccountManager.h" +#include "AtticaManager.h" +#include "ResolverAccount.h" + +#include + +using namespace Tomahawk; +using namespace Accounts; + +AccountModel::AccountModel( QObject* parent ) + : QAbstractListModel( parent ) +{ + connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( loadData() ) ); + + connect( AccountManager::instance(), SIGNAL( added( Tomahawk::Accounts::Account* ) ), this, SLOT( accountAdded( Tomahawk::Accounts::Account* ) ) ); + connect( AccountManager::instance(), SIGNAL( removed( Tomahawk::Accounts::Account* ) ), this, SLOT( accountRemoved( Tomahawk::Accounts::Account* ) ) ); + connect( AccountManager::instance(), SIGNAL( stateChanged( Account* ,Accounts::Account::ConnectionState ) ), this, SLOT( accountStateChanged( Account*, Accounts::Account::ConnectionState ) ) ); + + loadData(); +} + +void +AccountModel::loadData() +{ + beginResetModel(); + + qDeleteAll( m_accounts ); + m_accounts.clear(); + + // Add all factories + QList< AccountFactory* > factories = AccountManager::instance()->factories(); + QList< Account* > allAccounts = AccountManager::instance()->accounts(); + foreach ( AccountFactory* fac, factories ) + { + if ( !fac->allowUserCreation() ) + continue; + + qDebug() << "Creating factory node:" << fac->prettyName(); + m_accounts << new AccountModelNode( fac ); + + // remove the accounts we are dealing with + foreach ( Account* acct, allAccounts ) + { + if ( AccountManager::instance()->factoryForAccount( acct ) == fac ) + allAccounts.removeAll( acct ); + } + } + + // add all attica resolvers (installed or uninstalled) + Attica::Content::List fromAttica = AtticaManager::instance()->resolvers(); + foreach ( const Attica::Content& content, fromAttica ) + { + qDebug() << "Loading ATTICA ACCOUNT with content:" << content.id() << content.name(); + if ( AtticaManager::instance()->hasCustomAccountForAttica( content.id() ) ) + { + Account* acct = AtticaManager::instance()->customAccountForAttica( content.id() ); + Q_ASSERT( acct ); + if ( acct ) + { + m_accounts << new AccountModelNode( acct ); + allAccounts.removeAll( acct ); + } + } else + { + m_accounts << new AccountModelNode( content ); + + foreach ( Account* acct, AccountManager::instance()->accounts( Accounts::ResolverType ) ) + { + if ( AtticaResolverAccount* resolver = qobject_cast< AtticaResolverAccount* >( acct ) ) + { + if ( resolver->atticaId() == content.id() ) + { + allAccounts.removeAll( acct ); + } + } + } + } + } + + // All other accounts we haven't dealt with yet + foreach ( Account* acct, allAccounts ) + { + Q_ASSERT( !qobject_cast< AtticaResolverAccount* >( acct ) ); // This should be caught above in the attica list + + if ( qobject_cast< ResolverAccount* >( acct ) && !qobject_cast< AtticaResolverAccount* >( acct ) ) + m_accounts << new AccountModelNode( qobject_cast< ResolverAccount* >( acct ) ); + else + m_accounts << new AccountModelNode( acct ); + } + + endResetModel(); +} + + +QVariant +AccountModel::data( const QModelIndex& index, int role ) const +{ + if ( !index.isValid() ) + return QVariant(); + + if ( !hasIndex( index.row(), index.column(), index.parent() ) ) + return QVariant(); + + const AccountModelNode* node = m_accounts.at( index.row() ); + // This is a top-level item. 3 cases + + switch ( node->type ) + { + case AccountModelNode::FactoryType: + { + AccountFactory* fac = node->factory; + Q_ASSERT( fac ); + + switch ( role ) + { + case Qt::DisplayRole: + return fac->prettyName(); + case Qt::DecorationRole: + return fac->icon(); + case StateRole: + return ShippedWithTomahawk; + case Qt::ToolTipRole: + case DescriptionRole: + return fac->description(); + case CanRateRole: + return false; + case RowTypeRole: + return TopLevelFactory; + case AccountData: + return QVariant::fromValue< QObject* >( node->factory ); + case ChildrenOfFactoryRole: + return QVariant::fromValue< QList< Tomahawk::Accounts::Account* > >( node->accounts ); + case HasConfig: + return !node->accounts.isEmpty(); + case AccountTypeRole: + return QVariant::fromValue< AccountTypes >( node->factory->types() ); + case Qt::CheckStateRole: + { + if ( node->accounts.isEmpty() ) + return Qt::Unchecked; + + // If all are checked or unchecked, return that + bool someOn = false, someOff = false; + foreach ( const Account* acct, node->accounts ) + { + if ( acct->enabled() ) + someOn = true; + else + someOff = true; + } + if ( someOn && !someOff ) + return Qt::Checked; + else if ( someOff & !someOn ) + return Qt::Unchecked; + else + return Qt::PartiallyChecked; + } + default: + return QVariant(); + } + } + case AccountModelNode::AtticaType: + { + Attica::Content c = node->atticaContent; + Q_ASSERT( !c.id().isNull() ); + + switch( role ) + { + case Qt::DisplayRole: + return c.name(); + case Qt::DecorationRole: + return QVariant::fromValue< QPixmap >( AtticaManager::instance()->iconForResolver( c ) ); + case StateRole: + return (int)AtticaManager::instance()->resolverState( c ); + case Qt::ToolTipRole: + case DescriptionRole: + return c.description(); + case AuthorRole: + return c.author(); + case RowTypeRole: + return TopLevelAccount; + case RatingRole: + return c.rating() / 20; // rating is out of 100 + case DownloadCounterRole: + return c.downloads(); + case CanRateRole: + return true; + case AccountTypeRole: + return QVariant::fromValue< AccountTypes >( AccountTypes( ResolverType ) ); + case VersionRole: + return c.version(); + case UserHasRatedRole: + return AtticaManager::instance()->userHasRated( c ); + default: + ; + } + + AtticaResolverAccount* atticaAcct = node->atticaAccount; + if ( atticaAcct ) + { + // If the resolver is installed or on disk, we expose some additional data + switch ( role ) + { + case HasConfig: + return atticaAcct->configurationWidget() != 0; + case Qt::CheckStateRole: + return atticaAcct->enabled() ? Qt::Checked : Qt::Unchecked; + case AccountData: + return QVariant::fromValue< QObject* >( atticaAcct ); + case ConnectionStateRole: + return atticaAcct->connectionState(); + default: + ; + } + } + return QVariant(); + } + case AccountModelNode::ManualResolverType: + case AccountModelNode::UniqueFactoryType: + { + if ( role == RowTypeRole ) + { + if ( node->type == AccountModelNode::ManualResolverType ) + return TopLevelAccount; + else + return UniqueFactory; + } + else if ( role == CanDeleteRole ) + { + return node->type == AccountModelNode::ManualResolverType; + } + + Account* acct = 0; + if ( node->type == AccountModelNode::ManualResolverType ) + acct = node->resolverAccount; + else if ( node->type == AccountModelNode::UniqueFactoryType ) + acct = node->accounts.isEmpty() ? 0 : node->accounts.first(); + + // If there's no account*, then it means it's a unique factory that hasn't been created + if ( !acct ) + { + Q_ASSERT( node->type == AccountModelNode::UniqueFactoryType ); + Q_ASSERT( node->factory ); + + switch( role ) + { + case Qt::DisplayRole: + return node->factory->prettyName(); + case Qt::DecorationRole: + return node->factory->icon(); + case Qt::ToolTipRole: + case DescriptionRole: + return node->factory->description(); + case StateRole: + return Uninstalled; + case CanRateRole: + return false; + case AccountTypeRole: + return QVariant::fromValue< AccountTypes >( node->factory->types() ); + default: + return QVariant(); + } + } + else + { + switch ( role ) + { + case Qt::DisplayRole: + return acct->accountFriendlyName(); + case Qt::DecorationRole: + return acct->icon(); + case DescriptionRole: + return node->type == AccountModelNode::ManualResolverType ? QString() : node->factory->description(); + case Qt::CheckStateRole: + return acct->enabled() ? Qt::Checked : Qt::Unchecked; + case AccountData: + return QVariant::fromValue< QObject* >( acct ); + case ConnectionStateRole: + return acct->connectionState(); + case HasConfig: + return acct->configurationWidget() != 0; + case StateRole: + return Installed; + case ChildrenOfFactoryRole: + return QVariant::fromValue< QList< Tomahawk::Accounts::Account* > >( node->accounts ); + case AccountTypeRole: + return QVariant::fromValue< AccountTypes >( acct->types() ); + default: + return QVariant(); + } + } + } + case AccountModelNode::CustomAccountType: + { + Q_ASSERT( node->customAccount ); + Q_ASSERT( node->factory ); + + Account* account = node->customAccount; + // This is sort of ugly. CustomAccounts are pure Account*, but we know that + // some might also be linked to attica resolvers (not always). If that is the case + // they have a Attica::Content set on the node, so we use that to display some + // extra metadata and rating + const bool hasAttica = !node->atticaContent.id().isEmpty(); + + switch ( role ) + { + case Qt::DisplayRole: + return account->accountFriendlyName(); + case Qt::DecorationRole: + return account->icon(); + case StateRole: + return ShippedWithTomahawk; + case Qt::ToolTipRole: + case DescriptionRole: + return hasAttica ? node->atticaContent.description() : node->factory->description(); + case CanRateRole: + return hasAttica; + case AuthorRole: + return hasAttica ? node->atticaContent.author() : QString(); + case RatingRole: + return hasAttica ? node->atticaContent.rating() / 20 : 0; // rating is out of 100 + case DownloadCounterRole: + return hasAttica ? node->atticaContent.downloads() : QVariant(); + case RowTypeRole: + return CustomAccount; + case AccountData: + return QVariant::fromValue< QObject* >( account ); + case HasConfig: + return account->configurationWidget() != 0; + case AccountTypeRole: + return QVariant::fromValue< AccountTypes >( account->types() ); + case Qt::CheckStateRole: + return account->enabled() ? Qt::Checked : Qt::Unchecked; + case ConnectionStateRole: + return account->connectionState(); + default: + return QVariant(); + } + } + } + + return QVariant(); +} + + + +bool +AccountModel::setData( const QModelIndex& index, const QVariant& value, int role ) +{ + if ( !index.isValid() || !hasIndex( index.row(), index.column(), index.parent() ) ) + return false; + + AccountModelNode* node = m_accounts.at( index.row() ); + + if ( role == CheckboxClickedRole ) + { + Account* acct = 0; + switch ( node->type ) + { + case AccountModelNode::UniqueFactoryType: + { + const Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() ); + if ( node->accounts.isEmpty() ) + { + Q_ASSERT( state == Qt::Checked ); // How could we have a checked unique factory w/ no account?? + // No account for this unique factory, create it + // Don't add it to node->accounts here, slot attached to accountmanager::accountcreated will do it for us + acct = node->factory->createAccount(); + AccountManager::instance()->addAccount( acct ); + TomahawkSettings::instance()->addAccount( acct->accountId() ); + } + else + { + Q_ASSERT( node->accounts.size() == 1 ); + acct = node->accounts.first(); + } + break; + } + case AccountModelNode::AtticaType: + { + // This may or may not be installed. if it's not installed yet, install it, then go ahead and enable it + Q_ASSERT( node->atticaContent.isValid() ); + + Attica::Content resolver = node->atticaContent; + AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( resolver ); + qDebug() << "Attica resolver was checked! Current state is:" << state << "and so.."; + if ( state == AtticaManager::Installed && !node->atticaAccount ) + { + // Something is wrong, reinstall + qDebug() << "Found installed state but no resolver, uninstalling first"; + AtticaManager::instance()->uninstallResolver( resolver ); + state = AtticaManager::Uninstalled; + } + + if ( state == AtticaManager::Installed ) + { + qDebug() << "Already installed with resolver, just enabling"; + acct = node->atticaAccount; + break; + } + else + { + if ( m_waitingForAtticaInstall.contains( resolver.id() ) ) + { + // in progress, ignore + return true; + } + + qDebug() << "Kicked off fetch+install, now waiting"; + m_waitingForAtticaInstall.insert( resolver.id() ); + + AtticaManager::instance()->installResolver( resolver ); + return true; + } + + } + case AccountModelNode::ManualResolverType: + acct = node->resolverAccount; + break; + case AccountModelNode::CustomAccountType: + acct = node->customAccount; + break; + default: + ; + }; + + if ( node->type == AccountModelNode::FactoryType ) + { + // Turn on or off all accounts for this factory + + Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() ); + + foreach ( Account* acct, node->accounts ) + { + state == Qt::Checked ? AccountManager::instance()->enableAccount( acct ) + : AccountManager::instance()->disableAccount( acct ); + } + + emit dataChanged( index, index ); + return true; + } + + Q_ASSERT( acct ); + Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() ); + + if ( state == Qt::Checked && !acct->enabled() ) + AccountManager::instance()->enableAccount( acct ); + else if( state == Qt::Unchecked ) + AccountManager::instance()->disableAccount( acct ); + + emit dataChanged( index, index ); + + return true; + } + + // The install/create/remove/etc button was clicked. Handle it properly depending on this item + if ( role == CustomButtonRole ) + { + if ( node->type == AccountModelNode::FactoryType ) + { + // Make a new account of this factory type + emit createAccount( node->factory ); + return true; + } + else if ( node->type == AccountModelNode::ManualResolverType ) + { + Q_ASSERT( node->resolverAccount ); + AccountManager::instance()->removeAccount( node->resolverAccount ); + + return true; + } + Q_ASSERT( false ); // Should not be here, only the above two types should have this button + return false; + } + + + if ( role == RatingRole ) + { + // We only support rating Attica resolvers for the moment. + Q_ASSERT( node->type == AccountModelNode::AtticaType ); + + AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( node->atticaContent ); + // For now only allow rating if a resolver is installed! + if ( state != AtticaManager::Installed && state != AtticaManager::NeedsUpgrade ) + return false; + if ( AtticaManager::instance()->userHasRated( node->atticaContent ) ) + return false; + node->atticaContent.setRating( value.toInt() * 20 ); + AtticaManager::instance()->uploadRating( node->atticaContent ); + + emit dataChanged( index, index ); + + return true; + } + + return false; +} + + +void +AccountModel::accountAdded( Account* account ) +{ + // Find the factory this belongs up, and update + qDebug() << "IN ACCOUNT ADDED, new account:" << account->accountFriendlyName(); + AccountFactory* factory = AccountManager::instance()->factoryForAccount( account ); + AtticaResolverAccount* attica = qobject_cast< AtticaResolverAccount* >( account ); + for ( int i = 0; i < m_accounts.size(); i++ ) + { + AccountModelNode* n = m_accounts.at( i ); + bool thisIsTheOne = false; + qDebug() << "Checking for added account's related factory or attica:" << n->factory << attica; + if ( attica ) + qDebug() << n->atticaContent.id() << n->atticaContent.name() << attica->atticaId(); + if ( n->factory == factory ) + { + n->accounts << account; + thisIsTheOne = true; + } + else if ( attica && n->atticaContent.id() == attica->atticaId() ) + { + + n->atticaAccount = attica; + n->atticaContent = AtticaManager::instance()->resolverForId( attica->atticaId() ); + thisIsTheOne = true; + + if ( m_waitingForAtticaInstall.contains( attica->atticaId() ) ) + AccountManager::instance()->enableAccount( account ); + + m_waitingForAtticaInstall.remove( attica->atticaId() ); + } + + if ( thisIsTheOne ) + { + const QModelIndex idx = index( i, 0, QModelIndex() ); + dataChanged( idx, idx ); + + return; + } + } + + // Ok, just a plain resolver. add it at the end + if ( ResolverAccount* resolver = qobject_cast< ResolverAccount* >( account ) ) + { + Q_ASSERT( qobject_cast< AtticaResolverAccount* >( account ) == 0 ); // should NOT get attica accounts here, should be caught above + const int count = m_accounts.size(); + beginInsertRows( QModelIndex(), count, count ); + m_accounts << new AccountModelNode( resolver ); + endInsertRows(); + + emit scrollTo( index( m_accounts.size() - 1, 0, QModelIndex() ) ); + } +} + + +void +AccountModel::accountStateChanged( Account* account , Account::ConnectionState ) +{ + // Find the factory this belongs up, and update + AccountFactory* factory = AccountManager::instance()->factoryForAccount( account ); + for ( int i = 0; i < m_accounts.size(); i++ ) + { + AccountModelNode* n = m_accounts.at( i ); + if ( n->type != AccountModelNode::FactoryType ) + { + // If this is not a non-unique factory, it has as top-level account, so find that and update it + // For each type that this node could be, check the corresponding data + if ( ( n->type == AccountModelNode::UniqueFactoryType && n->accounts.size() && n->accounts.first() == account ) || + ( n->type == AccountModelNode::AtticaType && n->atticaAccount && n->atticaAccount == account ) || + ( n->type == AccountModelNode::ManualResolverType && n->resolverAccount && n->resolverAccount == account ) ) + { + const QModelIndex idx = index( i, 0, QModelIndex() ); + emit dataChanged( idx, idx ); + } + } + else + { + for ( int k = 0; k < n->accounts.size(); k++ ) + { + Account* childAccount = n->accounts.at( k ); + + if ( childAccount == account ) + { + const QModelIndex idx = index( i, 0, QModelIndex() ); + emit dataChanged( idx, idx ); + } + } + } + + } +} + + +void +AccountModel::accountRemoved( Account* account ) +{ + // Find the row this belongs to and update/remove + AccountFactory* factory = AccountManager::instance()->factoryForAccount( account ); + qDebug() << "AccountModel got account removed:" << account->accountFriendlyName(); + for ( int i = 0; i < m_accounts.size(); i++ ) + { + AccountModelNode* n = m_accounts.at( i ); + + bool found = false; + // Account in a factory, remove child and update + if ( ( n->type == AccountModelNode::FactoryType && n->factory == factory ) || + ( n->type == AccountModelNode::UniqueFactoryType && n->accounts.size() && n->accounts.first() == account ) ) + { + n->accounts.removeAll( account ); + found = true; + } + + // Attica account, just clear the account but leave the attica shell + if ( n->type == AccountModelNode::AtticaType && n->atticaAccount && n->atticaAccount == account ) + { + n->atticaAccount = 0; + found = true; + } + + if ( found ) + { + qDebug() << "Found account removed but we don't want to delete a row!" << i << n->type << n->factory; + const QModelIndex idx = index( i, 0, QModelIndex() ); + emit dataChanged( idx, idx ); + + return; + } + + // Manual resolver added, remove the row now + if ( n->type == AccountModelNode::ManualResolverType && n->resolverAccount && n->resolverAccount == account ) + { + qDebug() << "Found account removed AND REMOVING IT FROM THE LIST!" << n->factory << n->type << n->accounts << i; + + beginRemoveRows( QModelIndex(), i, i ); + m_accounts.removeAt( i ); + endRemoveRows(); + + return; + } + } +} + + +int +AccountModel::rowCount( const QModelIndex& ) const +{ + return m_accounts.size(); +} + diff --git a/src/libtomahawk/accounts/AccountModel.h b/src/libtomahawk/accounts/AccountModel.h new file mode 100644 index 000000000..4396dcd7a --- /dev/null +++ b/src/libtomahawk/accounts/AccountModel.h @@ -0,0 +1,113 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef TOMAHAWK_ACCOUNTS_ACCOUNTMODEL_H +#define TOMAHAWK_ACCOUNTS_ACCOUNTMODEL_H + +#include "dllmacro.h" + +#include "Account.h" + +#include + + +namespace Tomahawk { + +namespace Accounts { + +class AccountModelNode; + +class DLLEXPORT AccountModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + RowTypeRole = Qt::UserRole + 1, // RowType enum + + // Used by top-level accounts + DescriptionRole = Qt::UserRole + 17, + StateRole = Qt::UserRole + 18, // ItemState, + RatingRole = Qt::UserRole + 19, + DownloadCounterRole = Qt::UserRole + 20, + VersionRole = Qt::UserRole + 21, + AuthorRole = Qt::UserRole + 22, + UserHasRatedRole = Qt::UserRole + 24, + + // used by both + ConnectionStateRole = Qt::UserRole + 25, + HasConfig = Qt::UserRole + 26, + ErrorString = Qt::UserRole + 27, + + // used by individual accounts + AccountData = Qt::UserRole + 28, // raw plugin + CanRateRole = Qt::UserRole + 32, + AccountTypeRole = Qt::UserRole + 33, + CanDeleteRole = Qt::UserRole + 34, + + CheckboxClickedRole = Qt::UserRole + 29, // the checkbox for this row was toggled + CustomButtonRole = Qt::UserRole + 30, // the add account or remove account button + + // Used by factories + ChildrenOfFactoryRole = Qt::UserRole + 31 + }; + + enum RowType { + TopLevelFactory, + TopLevelAccount, + UniqueFactory, + CustomAccount + }; + + enum ItemState { + Uninstalled = 0, // Attica resolver states + Installing, + Installed, + NeedsUpgrade, + Upgrading, + Failed, + ShippedWithTomahawk, // Built-in account/factory state: Can't uninstall or uninstall, just create + }; + + explicit AccountModel( QObject* parent = 0 ); + + virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; + virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + +signals: + void createAccount( Tomahawk::Accounts::AccountFactory* factory ); + void scrollTo( const QModelIndex& idx ); + +private slots: + void loadData(); + + void accountAdded( Tomahawk::Accounts::Account* ); + void accountRemoved( Tomahawk::Accounts::Account* ); + void accountStateChanged( Account*, Accounts::Account::ConnectionState ); + +private: + QList< AccountModelNode* > m_accounts; + QSet< QString > m_waitingForAtticaInstall; +}; + +} + +} + +#endif // TOMAHAWK_ACCOUNTS_ACCOUNTMODEL_H diff --git a/src/libtomahawk/accounts/AccountModelFilterProxy.cpp b/src/libtomahawk/accounts/AccountModelFilterProxy.cpp new file mode 100644 index 000000000..23dca5dfc --- /dev/null +++ b/src/libtomahawk/accounts/AccountModelFilterProxy.cpp @@ -0,0 +1,72 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "AccountModelFilterProxy.h" + +#include "AccountModel.h" + + +using namespace Tomahawk; +using namespace Accounts; + +AccountModelFilterProxy::AccountModelFilterProxy( QObject* parent ) + : QSortFilterProxyModel(parent) + , m_filterType( NoType ) +{ + +} + + +void +AccountModelFilterProxy::setSourceModel( QAbstractItemModel* sourceModel ) +{ + connect( sourceModel, SIGNAL( scrollTo( QModelIndex ) ), this, SLOT( onScrollTo( QModelIndex ) ) ); + QSortFilterProxyModel::setSourceModel( sourceModel ); +} + + +bool +AccountModelFilterProxy::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const +{ + if ( m_filterType == NoType ) + return true; + + const QModelIndex idx = sourceModel()->index( sourceRow, 0, sourceParent ); + const AccountTypes types = static_cast< AccountTypes >( idx.data( AccountModel::AccountTypeRole ).value< AccountTypes >() ); + + return types.testFlag( m_filterType ); + +} + + +void +AccountModelFilterProxy::setFilterType( AccountType type ) +{ + if ( type == m_filterType ) + return; + + m_filterType = type; + invalidate(); +} + + +void +AccountModelFilterProxy::onScrollTo( const QModelIndex& idx ) +{ + emit scrollTo( mapFromSource( idx ) ); +} diff --git a/src/libtomahawk/accounts/AccountModelFilterProxy.h b/src/libtomahawk/accounts/AccountModelFilterProxy.h new file mode 100644 index 000000000..92c61d469 --- /dev/null +++ b/src/libtomahawk/accounts/AccountModelFilterProxy.h @@ -0,0 +1,56 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2012, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef ACCOUNTMODELFILTERPROXY_H +#define ACCOUNTMODELFILTERPROXY_H + +#include "Account.h" +#include "dllmacro.h" + +#include + +namespace Tomahawk { +namespace Accounts { + +class DLLEXPORT AccountModelFilterProxy : public QSortFilterProxyModel +{ + Q_OBJECT +public: + AccountModelFilterProxy( QObject* parent = 0 ); + + void setFilterType( Tomahawk::Accounts::AccountType type ); + + virtual void setSourceModel( QAbstractItemModel* sourceModel ); + +signals: + void scrollTo( const QModelIndex& idx ); + +protected: + virtual bool filterAcceptsRow ( int sourceRow, const QModelIndex& sourceParent ) const; + +private slots: + void onScrollTo( const QModelIndex& idx ); + +private: + Tomahawk::Accounts::AccountType m_filterType; +}; + +} + +} +#endif // ACCOUNTMODELFILTERPROXY_H diff --git a/src/libtomahawk/accounts/AccountModelNode.h b/src/libtomahawk/accounts/AccountModelNode.h new file mode 100644 index 000000000..042d913cf --- /dev/null +++ b/src/libtomahawk/accounts/AccountModelNode.h @@ -0,0 +1,145 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef TOMAHAWK_ACCOUNTS_ACCOUNTMODELNODE_H +#define TOMAHAWK_ACCOUNTS_ACCOUNTMODELNODE_H + +#include "Account.h" +#include "AccountManager.h" +#include "ResolverAccount.h" +#include "AtticaManager.h" + +#include + +namespace Tomahawk { + +namespace Accounts { + +/** + * Node for account tree. + * + * Basically a union with possible types: + * 1) AccountFactory* for all factories that have child accounts. Also a list of children + * 2) Attica::Content for AtticaResolverAccounts (with associated AtticaResolverAccount*) (all synchrotron resolvers) + * 3) ResolverAccount* for manually added resolvers (from file). + * 4) Account* for custom accounts. These may be hybrid infosystem/resolver/sip plugins or other special accounts + * + * These are the top-level items in tree. + * + * Top level nodes all look the same to the user. The only difference is that services that have login (and thus + * can have multiple logins at once) allow a user to create multiple children with specific login information. + * All other top level accounts (Account*, Attica::Content, ResolverAccount*) behave the same to the user, they can + * simply toggle on/off. + * + */ + +struct AccountModelNode { + enum NodeType { + FactoryType, + UniqueFactoryType, + AtticaType, + ManualResolverType, + CustomAccountType + }; + AccountModelNode* parent; + NodeType type; + + /// 1, 4 + AccountFactory* factory; + QList< Account* > accounts; // list of children accounts (actually existing and configured accounts) + + /// 2. + Attica::Content atticaContent; + AtticaResolverAccount* atticaAccount; + + /// 3. + ResolverAccount* resolverAccount; + + /// 4. + Account* customAccount; + + // Construct in one of four ways. Then access the corresponding members + explicit AccountModelNode( AccountFactory* fac ) : type( FactoryType ) + { + init(); + factory = fac; + + if ( fac->isUnique() ) + type = UniqueFactoryType; + + // Initialize factory nodes with their children + foreach ( Account* acct, AccountManager::instance()->accounts() ) + { + if ( AccountManager::instance()->factoryForAccount( acct ) == fac ) + { + qDebug() << "Found account for factory:" << acct->accountFriendlyName(); + accounts.append( acct ); + } + } + } + + explicit AccountModelNode( Attica::Content cnt ) : type( AtticaType ) + { + init(); + atticaContent = cnt; + + qDebug() << "Creating attica model node for resolver:" << cnt.id(); + + foreach ( Account* acct, AccountManager::instance()->accounts( Accounts::ResolverType ) ) + { + if ( AtticaResolverAccount* resolver = qobject_cast< AtticaResolverAccount* >( acct ) ) + { + if ( resolver->atticaId() == atticaContent.id() ) + { + qDebug() << "found atticaaccount :" << resolver->accountFriendlyName(); + atticaAccount = resolver; + break; + } + } + } + } + + explicit AccountModelNode( ResolverAccount* ra ) : type( ManualResolverType ) + { + init(); + resolverAccount = ra; + } + + explicit AccountModelNode( Account* account ) : type( CustomAccountType ) + { + init(); + customAccount = account; + factory = AccountManager::instance()->factoryForAccount( account ); + + if ( CustomAtticaAccount* customAtticaAccount = qobject_cast< CustomAtticaAccount* >( account ) ) + atticaContent = customAtticaAccount->atticaContent(); + } + + void init() + { + factory = 0; + atticaAccount = 0; + resolverAccount = 0; + customAccount = 0; + } +}; + +} + +} +#endif // TOMAHAWK_ACCOUNTS_ACCOUNTMODELNODE_H diff --git a/src/libtomahawk/accounts/LastFmAccount.cpp b/src/libtomahawk/accounts/LastFmAccount.cpp new file mode 100644 index 000000000..2b61666ee --- /dev/null +++ b/src/libtomahawk/accounts/LastFmAccount.cpp @@ -0,0 +1,268 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2012, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "LastFmAccount.h" +#include "LastFmConfig.h" + +#include "infosystem/infosystem.h" +#include "infosystem/infoplugins/generic/lastfmplugin.h" +#include "utils/tomahawkutils.h" +#include "resolvers/qtscriptresolver.h" +#include "AtticaManager.h" +#include "pipeline.h" +#include "accounts/AccountManager.h" + +using namespace Tomahawk; +using namespace InfoSystem; +using namespace Accounts; + +LastFmAccountFactory::LastFmAccountFactory() +{ + m_icon.load( RESPATH "images/lastfm-icon.png" ); +} + + +Account* +LastFmAccountFactory::createAccount( const QString& accountId ) +{ + return new LastFmAccount( accountId.isEmpty() ? generateId( factoryId() ) : accountId ); +} + + +QPixmap +LastFmAccountFactory::icon() const +{ + return m_icon; +} + + +LastFmAccount::LastFmAccount( const QString& accountId ) + : CustomAtticaAccount( accountId ) +{ + m_infoPlugin = QWeakPointer< LastFmPlugin >( new LastFmPlugin( this ) ); + + setAccountFriendlyName( "Last.Fm" ); + m_icon.load( RESPATH "images/lastfm-icon.png" ); + + AtticaManager::instance()->registerCustomAccount( "lastfm", this ); + + connect( AtticaManager::instance(), SIGNAL( resolverInstalled( QString ) ), this, SLOT( resolverInstalled( QString ) ) ); + + const Attica::Content res = AtticaManager::instance()->resolverForId( "lastfm" ); + const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + + if ( state == AtticaManager::Installed ) + { + hookupResolver(); + } +} + + +LastFmAccount::~LastFmAccount() +{ + delete m_infoPlugin.data(); + delete m_resolver.data(); +} + + +void +LastFmAccount::authenticate() +{ + const Attica::Content res = AtticaManager::instance()->resolverForId( "lastfm" ); + const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + + qDebug() << "Last.FM account authenticating..."; + if ( m_resolver.isNull() && state == AtticaManager::Installed ) + { + hookupResolver(); + } + else if ( m_resolver.isNull() ) + { + AtticaManager::instance()->installResolver( res, false ); + } + else + { + m_resolver.data()->start(); + } + + emit connectionStateChanged( connectionState() ); +} + + +void +LastFmAccount::deauthenticate() +{ + if ( m_resolver.data()->running() ) + m_resolver.data()->stop(); + + emit connectionStateChanged( connectionState() ); +} + + +QWidget* +LastFmAccount::configurationWidget() +{ + if ( m_configWidget.isNull() ) + m_configWidget = QWeakPointer( new LastFmConfig( this ) ); + + return m_configWidget.data(); +} + + +Account::ConnectionState +LastFmAccount::connectionState() const +{ + return (!m_resolver.isNull() && m_resolver.data()->running()) ? Account::Connected : Account::Disconnected; +} + + +QPixmap +LastFmAccount::icon() const +{ + return m_icon; +} + + +InfoPlugin* +LastFmAccount::infoPlugin() +{ + return m_infoPlugin.data(); +} + +bool +LastFmAccount::isAuthenticated() const +{ + return !m_resolver.isNull() && m_resolver.data()->running(); +} + + +void +LastFmAccount::saveConfig() +{ + if ( !m_configWidget.isNull() ) + { + setUsername( m_configWidget.data()->username() ); + setPassword( m_configWidget.data()->password() ); + setScrobble( m_configWidget.data()->scrobble() ); + } + + m_infoPlugin.data()->settingsChanged(); +} + + +QString +LastFmAccount::password() const +{ + return credentials().value( "password" ).toString(); +} + + +void +LastFmAccount::setPassword( const QString& password ) +{ + QVariantHash creds = credentials(); + creds[ "password" ] = password; + setCredentials( creds ); +} + +QString +LastFmAccount::sessionKey() const +{ + return credentials().value( "sessionkey" ).toString(); +} + + +void +LastFmAccount::setSessionKey( const QString& sessionkey ) +{ + QVariantHash creds = credentials(); + creds[ "sessionkey" ] = sessionkey; + setCredentials( creds ); +} + + +QString +LastFmAccount::username() const +{ + return credentials().value( "username" ).toString(); +} + + +void +LastFmAccount::setUsername( const QString& username ) +{ + QVariantHash creds = credentials(); + creds[ "username" ] = username; + setCredentials( creds ); +} + + +bool +LastFmAccount::scrobble() const +{ + return configuration().value( "scrobble" ).toBool(); +} + + +void +LastFmAccount::setScrobble( bool scrobble ) +{ + QVariantHash conf; + conf[ "scrobble" ] = scrobble; + setConfiguration( conf ); +} + + +void +LastFmAccount::resolverInstalled( const QString &resolverId ) +{ + if ( resolverId == "lastfm" ) + { + // We requested this install, so we want to launch it + hookupResolver(); + AccountManager::instance()->enableAccount( this ); + } +} + +void +LastFmAccount::resolverChanged() +{ + emit connectionStateChanged( connectionState() ); +} + + +void +LastFmAccount::hookupResolver() +{ + // If there is a last.fm resolver from attica installed, create the corresponding ExternalResolver* and hook up to it + const Attica::Content res = AtticaManager::instance()->resolverForId( "lastfm" ); + const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + Q_ASSERT( state == AtticaManager::Installed ); + + const AtticaManager::Resolver data = AtticaManager::instance()->resolverData( res.id() ); + + m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( data.scriptPath, enabled() ) ) ); + connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); +} + + +Attica::Content +LastFmAccount::atticaContent() const +{ + return AtticaManager::instance()->resolverForId( "lastfm" ); +} diff --git a/src/libtomahawk/accounts/LastFmAccount.h b/src/libtomahawk/accounts/LastFmAccount.h new file mode 100644 index 000000000..3e7aaf651 --- /dev/null +++ b/src/libtomahawk/accounts/LastFmAccount.h @@ -0,0 +1,113 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2012, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef LASTFMACCOUNT_H +#define LASTFMACCOUNT_H + +#include "accounts/Account.h" +#include "AtticaManager.h" + +#include + +#include + +namespace Tomahawk { +class ExternalResolverGui; + +namespace InfoSystem { + class LastFmPlugin; +} + +namespace Accounts { + +class LastFmConfig; + +class LastFmAccountFactory : public AccountFactory +{ + Q_OBJECT +public: + LastFmAccountFactory(); + + virtual Account* createAccount(const QString& accountId = QString()); + virtual QString description() const { return tr( "Scrobble your tracks to last.fm, and find freely downloadable tracks to play" ); } + virtual QString factoryId() const { return "lastfmaccount"; } + virtual QString prettyName() const { return "Last.fm"; } + virtual AccountTypes types() const { return AccountTypes( InfoType | StatusPushType ); } + virtual bool allowUserCreation() const { return false; } + virtual QPixmap icon() const; + virtual bool isUnique() const { return true; } + +private: + QPixmap m_icon; +}; + +/** + * 3.Last.Fm account is special. It is both an attica resolver *and* a InfoPlugin. We always want the infoplugin, + * but the user can install the attica resolver on-demand. So we take care of both there. + * + */ +class LastFmAccount : public CustomAtticaAccount +{ + Q_OBJECT +public: + explicit LastFmAccount( const QString& accountId ); + ~LastFmAccount(); + + virtual void deauthenticate(); + virtual void authenticate(); + + virtual SipPlugin* sipPlugin() { return 0; } + virtual Tomahawk::InfoSystem::InfoPlugin* infoPlugin(); + + virtual bool isAuthenticated() const; + + virtual ConnectionState connectionState() const; + virtual QPixmap icon() const; + virtual QWidget* aclWidget() { return 0; } + virtual QWidget* configurationWidget(); + virtual void saveConfig(); + + QString username() const; + void setUsername( const QString& ); + QString password() const; + void setPassword( const QString& ); + QString sessionKey() const; + void setSessionKey( const QString& ); + bool scrobble() const; + void setScrobble( bool scrobble ); + + Attica::Content atticaContent() const; + +private slots: + void resolverInstalled( const QString& resolverId ); + + void resolverChanged(); +private: + void hookupResolver(); + + QWeakPointer m_resolver; + QWeakPointer m_infoPlugin; + QWeakPointer m_configWidget; + QPixmap m_icon; +}; + + +} +} + +#endif // LASTFMACCOUNT_H diff --git a/src/libtomahawk/accounts/LastFmConfig.cpp b/src/libtomahawk/accounts/LastFmConfig.cpp new file mode 100644 index 000000000..70648d305 --- /dev/null +++ b/src/libtomahawk/accounts/LastFmConfig.cpp @@ -0,0 +1,134 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "LastFmConfig.h" + +#include "LastFmAccount.h" +#include +#include "ui_LastFmConfig.h" +#include "lastfm/ws.h" +#include "lastfm/XmlQuery" + +using namespace Tomahawk::Accounts; + +LastFmConfig::LastFmConfig( LastFmAccount* account ) + : QWidget( 0 ) + , m_account( account ) +{ + m_ui = new Ui_LastFmConfig; + m_ui->setupUi( this ); + + m_ui->username->setText( m_account->username() ); + m_ui->password->setText( m_account->password() ); + m_ui->enable->setChecked( m_account->scrobble() ); + + connect( m_ui->testLogin, SIGNAL( clicked( bool ) ), this, SLOT( testLogin( bool ) ) ); + +// #ifdef Q_WS_MAC // FIXME +// m_ui->testLogin->setVisible( false ); +// #endif +} + + +QString +LastFmConfig::password() const +{ + return m_ui->password->text(); +} + + +bool +LastFmConfig::scrobble() const +{ + return m_ui->enable->isChecked(); +} + + +QString +LastFmConfig::username() const +{ + return m_ui->username->text().trimmed(); +} + + +void +LastFmConfig::testLogin(bool ) +{ + m_ui->testLogin->setEnabled( false ); + m_ui->testLogin->setText( "Testing..." ); + + QString authToken = TomahawkUtils::md5( ( m_ui->username->text().toLower() + TomahawkUtils::md5( m_ui->password->text().toUtf8() ) ).toUtf8() ); + + // now authenticate w/ last.fm and get our session key + QMap query; + query[ "method" ] = "auth.getMobileSession"; + query[ "username" ] = m_ui->username->text().toLower(); + query[ "authToken" ] = authToken; + + // ensure they have up-to-date settings + lastfm::setNetworkAccessManager( TomahawkUtils::nam() ); + + QNetworkReply* authJob = lastfm::ws::post( query ); + + connect( authJob, SIGNAL( finished() ), SLOT( onLastFmFinished() ) ); +} + + +void +LastFmConfig::onLastFmFinished() +{ + QNetworkReply* authJob = dynamic_cast( sender() ); + if( !authJob ) + { + qDebug() << Q_FUNC_INFO << "No auth job returned!"; + return; + } + if( authJob->error() == QNetworkReply::NoError ) + { + lastfm::XmlQuery lfm = lastfm::XmlQuery( authJob->readAll() ); + + if( lfm.children( "error" ).size() > 0 ) + { + qDebug() << "ERROR from last.fm:" << lfm.text(); + m_ui->testLogin->setText( tr( "Failed" ) ); + m_ui->testLogin->setEnabled( true ); + } + else + { + m_ui->testLogin->setText( tr( "Success" ) ); + m_ui->testLogin->setEnabled( false ); + } + } + else + { + switch( authJob->error() ) + { + case QNetworkReply::ContentOperationNotPermittedError: + case QNetworkReply::AuthenticationRequiredError: + m_ui->testLogin->setText( tr( "Failed" ) ); + m_ui->testLogin->setEnabled( true ); + break; + + default: + qDebug() << "Couldn't get last.fm auth result"; + m_ui->testLogin->setText( tr( "Could not contact server" ) ); + m_ui->testLogin->setEnabled( true ); + return; + } + } +} diff --git a/src/libtomahawk/accounts/LastFmConfig.h b/src/libtomahawk/accounts/LastFmConfig.h new file mode 100644 index 000000000..a86d73f0e --- /dev/null +++ b/src/libtomahawk/accounts/LastFmConfig.h @@ -0,0 +1,53 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef LASTFMCONFIG_H +#define LASTFMCONFIG_H + +#include + +class Ui_LastFmConfig; + +namespace Tomahawk { +namespace Accounts { + +class LastFmAccount; + +class LastFmConfig : public QWidget +{ + Q_OBJECT +public: + explicit LastFmConfig( LastFmAccount* account ); + + QString username() const; + QString password() const; + bool scrobble() const; + +public slots: + void testLogin( bool ); + void onLastFmFinished(); + +private: + LastFmAccount* m_account; + Ui_LastFmConfig* m_ui; +}; + +} +} + +#endif // LASTFMCONFIG_H diff --git a/src/libtomahawk/accounts/LastFmConfig.ui b/src/libtomahawk/accounts/LastFmConfig.ui new file mode 100644 index 000000000..5cdf0e5e8 --- /dev/null +++ b/src/libtomahawk/accounts/LastFmConfig.ui @@ -0,0 +1,88 @@ + + + LastFmConfig + + + + 0 + 0 + 400 + 220 + + + + Form + + + + 4 + + + + + + + + :/data/images/lastfm-icon.png + + + Qt::AlignCenter + + + + + + + + + Qt::LeftToRight + + + Scrobble tracks to Last.fm + + + + + + + + + Username: + + + + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + + + Test Login + + + + + + + + + + + + diff --git a/src/libtomahawk/accounts/ResolverAccount.cpp b/src/libtomahawk/accounts/ResolverAccount.cpp new file mode 100644 index 000000000..f1010e6ad --- /dev/null +++ b/src/libtomahawk/accounts/ResolverAccount.cpp @@ -0,0 +1,245 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "ResolverAccount.h" + +#include "ExternalResolver.h" +#include "ExternalResolverGui.h" +#include "AccountManager.h" +#include +#include +#include +#include + +using namespace Tomahawk; +using namespace Accounts; + +Account* +ResolverAccountFactory::createAccount( const QString& accountId ) +{ + // Can't use this to create new accounts. Needs to be able to find account in config + // to load proper resolver account type. Creation is done from AtticaManager when path is known + Q_ASSERT( !accountId.isEmpty() ); + + // If it's an attica resolver, return it instead so we get an icon + const bool isFromAttica = TomahawkSettings::instance()->value( QString( "accounts/%1/atticaresolver" ).arg( accountId ), false ).toBool(); + if ( isFromAttica ) + return new AtticaResolverAccount( accountId ); + else + return new ResolverAccount( accountId ); +} + + +Account* +ResolverAccountFactory::createFromPath( const QString& path, bool isAttica ) +{ + if ( isAttica ) + { + QFileInfo info( path ); + return new AtticaResolverAccount( generateId( "resolveraccount" ), path, info.baseName() ); + } + else + return new ResolverAccount( generateId( "resolveraccount" ), path ); +} + + +ResolverAccount::ResolverAccount( const QString& accountId ) + : Account( accountId ) +{ + + const QString path = configuration()[ "path" ].toString(); + + // We should have a valid saved path + Q_ASSERT( !path.isEmpty() ); + + m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( path, enabled() ) ) ); + connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); + + // What resolver do we have here? Should only be types that are 'real' resolvers + Q_ASSERT ( !m_resolver.isNull() ); + + setAccountFriendlyName( m_resolver.data()->name() ); + setTypes( AccountType( ResolverType ) ); +} + + +ResolverAccount::ResolverAccount( const QString& accountId, const QString& path ) + : Account( accountId ) +{ + QVariantHash configuration; + configuration[ "path" ] = path; + setConfiguration( configuration ); + setEnabled( true ); + + m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( path, true ) ) ); + connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); + + // What resolver do we have here? Should only be types that are 'real' resolvers + Q_ASSERT ( m_resolver.data() ); + + setAccountFriendlyName( m_resolver.data()->name() ); + setTypes( AccountType( ResolverType ) ); +} + + +ResolverAccount::~ResolverAccount() +{ + if ( m_resolver.isNull() ) + return; + + Pipeline::instance()->removeScriptResolver( m_resolver.data()->filePath() ); + delete m_resolver.data(); +} + + + +void +ResolverAccount::authenticate() +{ + Q_ASSERT( !m_resolver.isNull() ); + qDebug() << Q_FUNC_INFO << "Authenticating/starting resolver, exists?" << m_resolver; + + if ( !m_resolver.data()->running() ) + m_resolver.data()->start(); + + emit connectionStateChanged( connectionState() ); +} + + +bool +ResolverAccount::isAuthenticated() const +{ + return m_resolver.data()->running(); +} + + +void +ResolverAccount::deauthenticate() +{ + if ( m_resolver.data()->running() ) + m_resolver.data()->stop(); + + emit connectionStateChanged( connectionState() ); + +} + + +Account::ConnectionState +ResolverAccount::connectionState() const +{ + if ( m_resolver.data()->running() ) + return Connected; + else + return Disconnected; +} + + +QWidget* +ResolverAccount::configurationWidget() +{ + return m_resolver.data()->configUI(); +} + + +QString +ResolverAccount::errorMessage() const +{ + // TODO +// return m_resolver->error(); + return QString(); +} + + +void +ResolverAccount::removeFromConfig() +{ + // TODO + Account::removeFromConfig(); +} + + +void ResolverAccount::saveConfig() +{ + Account::saveConfig(); + m_resolver.data()->saveConfig(); +} + + +QString +ResolverAccount::path() const +{ + return m_resolver.data()->filePath(); +} + + +void +ResolverAccount::resolverChanged() +{ + setAccountFriendlyName( m_resolver.data()->name() ); + emit connectionStateChanged( connectionState() ); +} + + +/// AtticaResolverAccount + +AtticaResolverAccount::AtticaResolverAccount( const QString& accountId ) + : ResolverAccount( accountId ) +{ + TomahawkSettings::instance()->setValue( QString( "accounts/%1/atticaresolver" ).arg( accountId ), true ); + + m_atticaId = configuration().value( "atticaId" ).toString(); + loadIcon(); +} + +AtticaResolverAccount::AtticaResolverAccount( const QString& accountId, const QString& path, const QString& atticaId ) + : ResolverAccount( accountId, path ) + , m_atticaId( atticaId ) +{ + QVariantHash conf = configuration(); + conf[ "atticaId" ] = atticaId; + setConfiguration( conf ); + + TomahawkSettings::instance()->setValue( QString( "accounts/%1/atticaresolver" ).arg( accountId ), true ); + + loadIcon(); +} + + +AtticaResolverAccount::~AtticaResolverAccount() +{ + +} + +void +AtticaResolverAccount::loadIcon() +{ + const QFileInfo fi( m_resolver.data()->filePath() ); + QDir codeDir = fi.absoluteDir(); + codeDir.cd( "../images" ); + + if ( codeDir.exists() && codeDir.exists( "icon.png" ) ) + m_icon.load( codeDir.absoluteFilePath( "icon.png" ) ); + +} + + +QPixmap +AtticaResolverAccount::icon() const +{ + return m_icon; +} diff --git a/src/libtomahawk/accounts/ResolverAccount.h b/src/libtomahawk/accounts/ResolverAccount.h new file mode 100644 index 000000000..04da6d283 --- /dev/null +++ b/src/libtomahawk/accounts/ResolverAccount.h @@ -0,0 +1,124 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef RESOLVERACCOUNT_H +#define RESOLVERACCOUNT_H + +#include "accounts/Account.h" +#include "dllmacro.h" + +namespace Tomahawk { + +class ExternalResolverGui; + +namespace Accounts { + +class DLLEXPORT ResolverAccountFactory : public AccountFactory +{ + Q_OBJECT +public: + ResolverAccountFactory() {} + virtual ~ResolverAccountFactory() {} + + virtual Account* createAccount(const QString& accountId = QString()); + virtual QString factoryId() const { return "resolveraccount"; } + virtual QString description() const { return QString(); } + virtual QString prettyName() const { return QString(); } // Internal, not displayed + AccountTypes types() const { return AccountTypes( ResolverType ); }; + virtual bool allowUserCreation() const { return false; } + + // Used to create a new resolver from a script on disk, either chosen by + // the user, or installed from synchrotron + static Account* createFromPath( const QString& path, bool isAttica ); +}; + +/** + * Helper wrapper class that is a resolver-only account. + * + * Contains the resolver* that is it wrapping + */ +class DLLEXPORT ResolverAccount : public Account +{ + Q_OBJECT +public: + // Loads from config. Must already exist. + explicit ResolverAccount( const QString& accountId ); + virtual ~ResolverAccount(); + + virtual void authenticate(); + virtual void deauthenticate(); + virtual bool isAuthenticated() const; + virtual Tomahawk::Accounts::Account::ConnectionState connectionState() const; + + virtual QWidget* configurationWidget(); + virtual QString errorMessage() const; + + virtual void saveConfig(); + virtual void removeFromConfig(); + + QString path() const; + + // Not relevant + virtual QPixmap icon() const { return QPixmap(); } + virtual SipPlugin* sipPlugin() { return 0; } + virtual Tomahawk::InfoSystem::InfoPlugin* infoPlugin() { return 0; } + virtual QWidget* aclWidget() { return 0; } + +private slots: + void resolverChanged(); + +protected: + // Created by factory, when user installs a new resolver + ResolverAccount( const QString& accountId, const QString& path ); + QWeakPointer m_resolver; + + friend class ResolverAccountFactory; +}; + + +/** + * Extends ResolverAccount with what attica additionally provides---e.g. icon + * Assumes certain file layout on disk. + */ +class AtticaResolverAccount : public ResolverAccount +{ + Q_OBJECT +public: + // Loads from config + explicit AtticaResolverAccount( const QString& accountId ); + virtual ~AtticaResolverAccount(); + + virtual QPixmap icon() const; + + QString atticaId() const { return m_atticaId; } +private: + // Created by factory, when user installs a new resolver + AtticaResolverAccount( const QString& accountId, const QString& path, const QString& atticaId ); + + void loadIcon(); + + QPixmap m_icon; + QString m_atticaId; + + friend class ResolverAccountFactory; +}; + +} +} + +#endif // RESOLVERACCOUNT_H diff --git a/src/libtomahawk/accounts/account.h b/src/libtomahawk/accounts/account.h deleted file mode 100644 index f1219d80c..000000000 --- a/src/libtomahawk/accounts/account.h +++ /dev/null @@ -1,74 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef ACCOUNT_H -#define ACCOUNT_H - -#include -#include -#include - -#include "typedefs.h" -#include "dllmacro.h" -#include "infosystem/infosystem.h" -#include "sip/SipPlugin.h" - -namespace Tomahawk -{ - -class DLLEXPORT Account -{ - - typedef QMap< QString, bool > ACLMap; - -public: - enum AccountTypes { InfoType, SipType }; - - explicit Account(); - virtual ~Account(); - - QString accountServiceName(); // e.g. "Twitter", "Last.fm" - void setAccountServiceName( const QString &serviceName ); - - QString accountFriendlyName(); // e.g. screen name on the service, JID, etc. - void setAccountFriendlyName( const QString &friendlyName ); - - bool autoConnect(); - void setAutoConnect( bool autoConnect ); - - QStringMap credentials(); - void setCredentials( const QStringMap &credentialMap ); - - QVariantMap configuration(); - void setConfiguration( const QVariantMap &configuration ); - QWidget* configurationWidget(); - - ACLMap acl(); - void setAcl( const ACLMap &acl ); - QWidget* aclWidget(); - - QSet< AccountTypes > types(); - void setTypes( const QSet< AccountTypes > types ); - - Tomahawk::InfoSystem::InfoPlugin* infoPlugin(); - SipPlugin* sipPlugin(); -}; - -}; - -#endif // ACCOUNT_H diff --git a/src/libtomahawk/database/databaseworker.cpp b/src/libtomahawk/database/databaseworker.cpp index ab22f60d4..23afc5b86 100644 --- a/src/libtomahawk/database/databaseworker.cpp +++ b/src/libtomahawk/database/databaseworker.cpp @@ -250,6 +250,7 @@ void DatabaseWorker::logOp( DatabaseCommandLoggable* command ) { TomahawkSqlQuery oplogquery = m_dbimpl->newquery(); + qDebug() << "INSERTING INTO OPTLOG:" << command->source()->id() << command->guid() << command->commandname(); oplogquery.prepare( "INSERT INTO oplog(source, guid, command, singleton, compressed, json) " "VALUES(?, ?, ?, ?, ?, ?)" ); diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp index 9eab12814..20220043b 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp @@ -323,12 +323,12 @@ hypemPlugin::chartReturned() QList< InfoStringHash > top_tracks; QStringList top_artists; - if( url.contains( "artists" ) ) + if ( url.contains( "artists" ) ) setChartType( Artist ); else setChartType( Track ); - foreach(QVariant result, res ) + foreach ( QVariant result, res ) { QString title, artist; QVariantMap chartMap = result.toMap(); @@ -339,28 +339,21 @@ hypemPlugin::chartReturned() title = chartMap.value( "title" ).toString(); artist = chartMap.value( "artist" ).toString(); - if( chartType() == Track ) + if ( chartType() == Track ) { InfoStringHash pair; pair["artist"] = artist; pair["track"] = title; top_tracks << pair; - - qDebug() << "HypemChart type is track"; } - if( chartType() == Artist ) - { - + if ( chartType() == Artist ) top_artists << artist; - qDebug() << "HypemChart type is artist"; - - } } } - if( chartType() == Track ) + if ( chartType() == Track ) { tDebug() << "HypemPlugin:" << "\tgot " << top_tracks.size() << " tracks"; returnedData["tracks"] = QVariant::fromValue( top_tracks ); @@ -369,7 +362,7 @@ hypemPlugin::chartReturned() - if( chartType() == Artist ) + if ( chartType() == Artist ) { tDebug() << "HypemPlugin:" << "\tgot " << top_artists.size() << " artists"; returnedData["artists"] = top_artists; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp index df14af143..5a9ede354 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp @@ -26,34 +26,37 @@ #include "album.h" #include "typedefs.h" #include "audio/audioengine.h" -#include "tomahawksettings.h" #include "utils/tomahawkutils.h" #include "utils/logger.h" +#include "accounts/LastFmAccount.h" #include #include #include +using namespace Tomahawk::Accounts; using namespace Tomahawk::InfoSystem; -LastFmPlugin::LastFmPlugin() +LastFmPlugin::LastFmPlugin( LastFmAccount* account ) : InfoPlugin() + , m_account( account ) , m_scrobbler( 0 ) { m_supportedGetTypes << InfoAlbumCoverArt << InfoArtistImages << InfoArtistSimilars << InfoArtistSongs << InfoChart << InfoChartCapabilities; m_supportedPushTypes << InfoSubmitScrobble << InfoSubmitNowPlaying << InfoLove << InfoUnLove; // Flush session key cache - TomahawkSettings::instance()->setLastFmSessionKey( QByteArray() ); + // TODO WHY FLUSH +// m_account->setSessionKey( QByteArray() ); lastfm::ws::ApiKey = "7194b85b6d1f424fe1668173a78c0c4a"; lastfm::ws::SharedSecret = "ba80f1df6d27ae63e9cb1d33ccf2052f"; - lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername(); + lastfm::ws::Username = m_account->username(); lastfm::setNetworkAccessManager( TomahawkUtils::nam() ); - m_pw = TomahawkSettings::instance()->lastFmPassword(); + m_pw = m_account->password(); //HACK work around a bug in liblastfm---it doesn't create its config dir, so when it // tries to write the track cache, it fails silently. until we have a fixed version, do this @@ -69,9 +72,6 @@ LastFmPlugin::LastFmPlugin() m_badUrls << QUrl( "http://cdn.last.fm/flatness/catalogue/noimage" ); - connect( TomahawkSettings::instance(), SIGNAL( changed() ), - SLOT( settingsChanged() ), Qt::QueuedConnection ); - QTimer::singleShot( 0, this, SLOT( settingsChanged() ) ); } @@ -706,23 +706,23 @@ LastFmPlugin::artistImagesReturned() void LastFmPlugin::settingsChanged() { - if ( !m_scrobbler && TomahawkSettings::instance()->scrobblingEnabled() ) + if ( !m_scrobbler && m_account->scrobble() ) { // can simply create the scrobbler - lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername(); - m_pw = TomahawkSettings::instance()->lastFmPassword(); + lastfm::ws::Username = m_account->username(); + m_pw = m_account->password(); createScrobbler(); } - else if ( m_scrobbler && !TomahawkSettings::instance()->scrobblingEnabled() ) + else if ( m_scrobbler && !m_account->scrobble() ) { delete m_scrobbler; m_scrobbler = 0; } - else if ( TomahawkSettings::instance()->lastFmUsername() != lastfm::ws::Username || - TomahawkSettings::instance()->lastFmPassword() != m_pw ) + else if ( m_account->username() != lastfm::ws::Username || + m_account->password() != m_pw ) { - lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername(); - m_pw = TomahawkSettings::instance()->lastFmPassword(); + lastfm::ws::Username = m_account->username(); + m_pw = m_account->password(); // credentials have changed, have to re-create scrobbler for them to take effect if ( m_scrobbler ) { @@ -752,16 +752,16 @@ LastFmPlugin::onAuthenticated() if ( lfm.children( "error" ).size() > 0 ) { tLog() << "Error from authenticating with Last.fm service:" << lfm.text(); - TomahawkSettings::instance()->setLastFmSessionKey( QByteArray() ); + m_account->setSessionKey( QByteArray() ); } else { lastfm::ws::SessionKey = lfm[ "session" ][ "key" ].text(); - TomahawkSettings::instance()->setLastFmSessionKey( lastfm::ws::SessionKey.toLatin1() ); + m_account->setSessionKey( lastfm::ws::SessionKey.toLatin1() ); // qDebug() << "Got session key from last.fm"; - if ( TomahawkSettings::instance()->scrobblingEnabled() ) + if ( m_account->scrobble() ) m_scrobbler = new lastfm::Audioscrobbler( "thk" ); } } @@ -777,7 +777,7 @@ LastFmPlugin::onAuthenticated() void LastFmPlugin::createScrobbler() { - if ( TomahawkSettings::instance()->lastFmSessionKey().isEmpty() ) // no session key, so get one + if ( m_account->sessionKey().isEmpty() ) // no session key, so get one { qDebug() << "LastFmPlugin::createScrobbler Session key is empty"; QString authToken = TomahawkUtils::md5( ( lastfm::ws::Username.toLower() + TomahawkUtils::md5( m_pw.toUtf8() ) ).toUtf8() ); @@ -793,7 +793,7 @@ LastFmPlugin::createScrobbler() else { qDebug() << "LastFmPlugin::createScrobbler Already have session key"; - lastfm::ws::SessionKey = TomahawkSettings::instance()->lastFmSessionKey(); + lastfm::ws::SessionKey = m_account->sessionKey(); m_scrobbler = new lastfm::Audioscrobbler( "thk" ); } diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h index 4850fb44c..00b133a58 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h @@ -32,6 +32,11 @@ class QNetworkReply; namespace Tomahawk { +namespace Accounts +{ + class LastFmAccount; +} + namespace InfoSystem { @@ -40,7 +45,7 @@ class LastFmPlugin : public InfoPlugin Q_OBJECT public: - LastFmPlugin(); + LastFmPlugin( Accounts::LastFmAccount* account ); virtual ~LastFmPlugin(); public slots: @@ -74,6 +79,7 @@ private: void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); + Accounts::LastFmAccount* m_account; QList parseTrackList( QNetworkReply * reply ); lastfm::MutableTrack m_track; @@ -88,3 +94,5 @@ private: } #endif // LASTFMPLUGIN_H + +class A; diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp index 019a69a50..877a3b345 100644 --- a/src/libtomahawk/infosystem/infosystem.cpp +++ b/src/libtomahawk/infosystem/infosystem.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2012 Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,6 +46,9 @@ InfoSystem* InfoSystem::s_instance = 0; InfoSystem* InfoSystem::instance() { + if ( !s_instance ) + s_instance = new InfoSystem( 0 ); + return s_instance; } @@ -100,6 +104,9 @@ void InfoSystem::init() { tDebug() << Q_FUNC_INFO; + if ( m_inited ) + return; + if ( !m_infoSystemCacheThreadController->cache() || !m_infoSystemWorkerThreadController->worker() ) { QTimer::singleShot( 0, this, SLOT( init() ) ); @@ -111,17 +118,17 @@ InfoSystem::init() connect( cache, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), worker, SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); - + connect( worker, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), this, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); - + connect( worker, SIGNAL( finished( QString ) ), this, SIGNAL( finished( QString ) ), Qt::UniqueConnection ); - + connect( worker, SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), this, SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), Qt::UniqueConnection ); QMetaObject::invokeMethod( worker, "init", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoSystemCache*, cache ) ); - + m_inited = true; } @@ -195,6 +202,19 @@ InfoSystem::pushInfo( const QString &caller, const InfoTypeMap &input ) } +void +InfoSystem::addInfoPlugin( InfoPlugin* plugin ) +{ + // Init is not complete (waiting for worker th read to start and create worker object) so keep trying till then + if ( !m_inited || !m_infoSystemWorkerThreadController->worker() ) + { + QMetaObject::invokeMethod( this, "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPlugin*, plugin ) ); + return; + } + QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPlugin*, plugin ) ); +} + + InfoSystemCacheThread::InfoSystemCacheThread( QObject *parent ) : QThread( parent ) { diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h index 077821882..a8f0fda66 100644 --- a/src/libtomahawk/infosystem/infosystem.h +++ b/src/libtomahawk/infosystem/infosystem.h @@ -134,7 +134,7 @@ struct InfoRequestData { QVariantMap customData; uint timeoutMillis; bool allSources; - + InfoRequestData() : requestId( TomahawkUtils::infosystemRequestId() ) , internalId( TomahawkUtils::infosystemRequestId() ) @@ -145,7 +145,7 @@ struct InfoRequestData { , timeoutMillis( 10000 ) , allSources( false ) {} - + InfoRequestData( const quint64 rId, const QString &callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant &inputvar, const QVariantMap &custom ) : requestId( rId ) , internalId( TomahawkUtils::infosystemRequestId() ) @@ -242,6 +242,10 @@ public: bool pushInfo( const QString &caller, const InfoType type, const QVariant &input ); bool pushInfo( const QString &caller, const InfoTypeMap &input ); +public slots: + // InfoSystem takes ownership of InfoPlugins + void addInfoPlugin( Tomahawk::InfoSystem::InfoPlugin* plugin ); + signals: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); @@ -249,7 +253,7 @@ signals: private slots: void init(); - + private: bool m_inited; InfoSystemCacheThread* m_infoSystemCacheThreadController; @@ -289,5 +293,6 @@ Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoRequestData ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoStringHash ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoSystemCache* ); Q_DECLARE_METATYPE( QList< Tomahawk::InfoSystem::InfoStringHash > ); +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoPlugin* ); #endif // TOMAHAWK_INFOSYSTEM_H diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 4a54d5cda..25947e223 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2012 Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,7 +28,6 @@ #include "infoplugins/generic/musixmatchplugin.h" #include "infoplugins/generic/chartsplugin.h" #include "infoplugins/generic/spotifyPlugin.h" -#include "infoplugins/generic/lastfmplugin.h" #include "infoplugins/generic/musicbrainzPlugin.h" #include "infoplugins/generic/hypemPlugin.h" #include "utils/tomahawkutils.h" @@ -41,7 +41,6 @@ #include "infoplugins/unix/mprisplugin.h" #endif -#include "lastfm/NetworkAccessManager" #include "infoplugins/generic/RoviPlugin.h" namespace Tomahawk @@ -78,71 +77,56 @@ void InfoSystemWorker::init( Tomahawk::InfoSystem::InfoSystemCache* cache ) { tDebug() << Q_FUNC_INFO; + m_cache = cache; #ifndef ENABLE_HEADLESS - InfoPluginPtr enptr( new EchoNestPlugin() ); - m_plugins.append( enptr ); - registerInfoTypes( enptr, enptr.data()->supportedGetTypes(), enptr.data()->supportedPushTypes() ); - InfoPluginPtr mmptr( new MusixMatchPlugin() ); - m_plugins.append( mmptr ); - registerInfoTypes( mmptr, mmptr.data()->supportedGetTypes(), mmptr.data()->supportedPushTypes() ); - InfoPluginPtr mbptr( new MusicBrainzPlugin() ); - m_plugins.append( mbptr ); - registerInfoTypes( mbptr, mbptr.data()->supportedGetTypes(), mbptr.data()->supportedPushTypes() ); - InfoPluginPtr lfmptr( new LastFmPlugin() ); - m_plugins.append( lfmptr ); - registerInfoTypes( lfmptr, lfmptr.data()->supportedGetTypes(), lfmptr.data()->supportedPushTypes() ); - InfoPluginPtr sptr( new ChartsPlugin() ); - m_plugins.append( sptr ); - registerInfoTypes( sptr, sptr.data()->supportedGetTypes(), sptr.data()->supportedPushTypes() ); - InfoPluginPtr roviptr( new RoviPlugin() ); - m_plugins.append( roviptr ); - registerInfoTypes( roviptr, roviptr.data()->supportedGetTypes(), roviptr.data()->supportedPushTypes() ); - InfoPluginPtr spotptr( new SpotifyPlugin() ); - m_plugins.append( spotptr ); - registerInfoTypes( spotptr, spotptr.data()->supportedGetTypes(), spotptr.data()->supportedPushTypes() ); - InfoPluginPtr hypeptr( new hypemPlugin() ); - m_plugins.append( hypeptr ); - registerInfoTypes( hypeptr, hypeptr.data()->supportedGetTypes(), hypeptr.data()->supportedPushTypes() ); + addInfoPlugin( new EchoNestPlugin() ); + addInfoPlugin( new MusixMatchPlugin() ); + addInfoPlugin( new MusicBrainzPlugin() ); + addInfoPlugin( new ChartsPlugin() ); + addInfoPlugin( new RoviPlugin() ); + addInfoPlugin( new SpotifyPlugin() ); + addInfoPlugin( new hypemPlugin() ); #endif - #ifdef Q_WS_MAC - InfoPluginPtr admptr( new AdiumPlugin() ); - m_plugins.append( admptr ); - registerInfoTypes( admptr, admptr.data()->supportedGetTypes(), admptr.data()->supportedPushTypes() ); - #endif -#ifndef ENABLE_HEADLESS - #ifdef Q_WS_X11 - InfoPluginPtr fdonotifyptr( new FdoNotifyPlugin() ); - m_plugins.append( fdonotifyptr ); - registerInfoTypes( fdonotifyptr, fdonotifyptr.data()->supportedGetTypes(), fdonotifyptr.data()->supportedPushTypes() ); - InfoPluginPtr mprisptr( new MprisPlugin() ); - m_plugins.append( mprisptr ); - registerInfoTypes( mprisptr, mprisptr.data()->supportedGetTypes(), mprisptr.data()->supportedPushTypes() ); - #endif +#ifdef Q_WS_MAC + addInfoPlugin( new AdiumPlugin() ); #endif - Q_FOREACH( InfoPluginPtr plugin, m_plugins ) - { - connect( - plugin.data(), - SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - this, - SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - Qt::UniqueConnection - ); +#ifndef ENABLE_HEADLESS +#ifdef Q_WS_X11 + addInfoPlugin( new FdoNotifyPlugin() ); + addInfoPlugin( new MprisPlugin() ); +#endif +#endif +} - connect( - plugin.data(), - SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ), - cache, - SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ) - ); - connect( - plugin.data(), - SIGNAL( updateCache( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ), - cache, - SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ) - ); - } + +void +InfoSystemWorker::addInfoPlugin( InfoPlugin* plugin ) +{ + InfoPluginPtr weakptr( plugin ); + m_plugins.append( weakptr ); + registerInfoTypes( weakptr, weakptr.data()->supportedGetTypes(), weakptr.data()->supportedPushTypes() ); + + connect( + plugin, + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + this, + SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + Qt::UniqueConnection + ); + + connect( + plugin, + SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ), + m_cache, + SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ) + ); + connect( + plugin, + SIGNAL( updateCache( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ), + m_cache, + SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ) + ); } diff --git a/src/libtomahawk/infosystem/infosystemworker.h b/src/libtomahawk/infosystem/infosystemworker.h index 45634c6a2..12c4d1ae8 100644 --- a/src/libtomahawk/infosystem/infosystemworker.h +++ b/src/libtomahawk/infosystem/infosystemworker.h @@ -37,7 +37,9 @@ namespace Tomahawk { namespace InfoSystem { - +class InfoSystemCache; + + class DLLEXPORT InfoSystemWorker : public QObject { Q_OBJECT @@ -47,33 +49,37 @@ public: ~InfoSystemWorker(); void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &getTypes, const QSet< InfoType > &pushTypes ); - QNetworkAccessManager* nam() const; - + signals: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); void finished( QString target, Tomahawk::InfoSystem::InfoType type ); - + public slots: void init( Tomahawk::InfoSystem::InfoSystemCache* cache ); void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ); void infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); - + + void addInfoPlugin( Tomahawk::InfoSystem::InfoPlugin* plugin ); + private slots: void checkTimeoutsTimerFired(); - + private: void checkFinished( const Tomahawk::InfoSystem::InfoRequestData &target ); QList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; - + QHash< QString, QHash< InfoType, int > > m_dataTracker; QMultiMap< qint64, quint64 > m_timeRequestMapper; QHash< uint, bool > m_requestSatisfiedMap; QHash< uint, InfoRequestData* > m_savedRequestMap; - + + // NOTE Cache object lives in a different thread, do not call methods on it directly + InfoSystemCache* m_cache; + // For now, statically instantiate plugins; this is just somewhere to keep them QList< InfoPluginPtr > m_plugins; diff --git a/src/libtomahawk/pipeline.cpp b/src/libtomahawk/pipeline.cpp index 2560edbb1..6184875b1 100644 --- a/src/libtomahawk/pipeline.cpp +++ b/src/libtomahawk/pipeline.cpp @@ -65,7 +65,10 @@ Pipeline::~Pipeline() m_running = false; // stop script resolvers - qDeleteAll( m_scriptResolvers ); + foreach ( QWeakPointer< ExternalResolver > r, m_scriptResolvers ) + if ( !r.isNull() ) + r.data()->deleteLater(); + m_scriptResolvers.clear(); } @@ -134,7 +137,7 @@ Pipeline::addScriptResolver( const QString& path, bool start ) if ( !res ) continue; - m_scriptResolvers << res; + m_scriptResolvers << QWeakPointer< ExternalResolver >( res ); if ( start ) res->start(); @@ -148,10 +151,10 @@ Pipeline::addScriptResolver( const QString& path, bool start ) void Pipeline::stopScriptResolver( const QString& path ) { - foreach ( ExternalResolver* res, m_scriptResolvers ) + foreach ( QWeakPointer< ExternalResolver > res, m_scriptResolvers ) { - if ( res->filePath() == path ) - res->stop(); + if ( res.data()->filePath() == path ) + res.data()->stop(); } } @@ -159,18 +162,21 @@ Pipeline::stopScriptResolver( const QString& path ) void Pipeline::removeScriptResolver( const QString& scriptPath ) { - ExternalResolver* r = 0; - foreach ( ExternalResolver* res, m_scriptResolvers ) + QWeakPointer< ExternalResolver > r; + foreach ( QWeakPointer< ExternalResolver > res, m_scriptResolvers ) { - if ( res->filePath() == scriptPath ) + if ( res.isNull() ) + continue; + + if ( res.data()->filePath() == scriptPath ) r = res; } m_scriptResolvers.removeAll( r ); - if ( r ) + if ( !r.isNull() ) { - r->stop(); - r->deleteLater(); + r.data()->stop(); + r.data()->deleteLater(); } } @@ -178,10 +184,10 @@ Pipeline::removeScriptResolver( const QString& scriptPath ) ExternalResolver* Pipeline::resolverForPath( const QString& scriptPath ) { - foreach ( ExternalResolver* res, m_scriptResolvers ) + foreach ( QWeakPointer< ExternalResolver > res, m_scriptResolvers ) { - if ( res->filePath() == scriptPath ) - return res; + if ( res.data()->filePath() == scriptPath ) + return res.data(); } return 0; } diff --git a/src/libtomahawk/pipeline.h b/src/libtomahawk/pipeline.h index fdbd519d6..50e33ab57 100644 --- a/src/libtomahawk/pipeline.h +++ b/src/libtomahawk/pipeline.h @@ -61,7 +61,7 @@ public: Tomahawk::ExternalResolver* addScriptResolver( const QString& scriptPath, bool start = true ); void stopScriptResolver( const QString& scriptPath ); void removeScriptResolver( const QString& scriptPath ); - QList< Tomahawk::ExternalResolver* > scriptResolvers() const { return m_scriptResolvers; } + QList< QWeakPointer< ExternalResolver > > scriptResolvers() const { return m_scriptResolvers; } Tomahawk::ExternalResolver* resolverForPath( const QString& scriptPath ); void addResolver( Resolver* r ); @@ -108,7 +108,7 @@ private: int decQIDState( const Tomahawk::query_ptr& query ); QList< Resolver* > m_resolvers; - QList< Tomahawk::ExternalResolver* > m_scriptResolvers; + QList< QWeakPointer > m_scriptResolvers; QList< ResolverFactoryFunc > m_resolverFactories; QMap< QID, bool > m_qidsTimeout; QMap< QID, unsigned int > m_qidsState; diff --git a/src/libtomahawk/resolvers/scriptresolver.cpp b/src/libtomahawk/resolvers/scriptresolver.cpp index 0cdd371a6..9fd1b96cc 100644 --- a/src/libtomahawk/resolvers/scriptresolver.cpp +++ b/src/libtomahawk/resolvers/scriptresolver.cpp @@ -339,6 +339,8 @@ ScriptResolver::doSetup( const QVariantMap& m ) if ( !m_stopped ) Tomahawk::Pipeline::instance()->addResolver( this ); + + emit changed(); } diff --git a/src/libtomahawk/sip/SipHandler.cpp b/src/libtomahawk/sip/SipHandler.cpp index d954154c2..ef333ec75 100644 --- a/src/libtomahawk/sip/SipHandler.cpp +++ b/src/libtomahawk/sip/SipHandler.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +36,7 @@ #include "sourcelist.h" #include "tomahawksettings.h" #include "utils/logger.h" +#include "accounts/AccountManager.h" #include "config.h" @@ -53,21 +55,15 @@ SipHandler::instance() SipHandler::SipHandler( QObject* parent ) : QObject( parent ) - , m_connected( false ) { s_instance = this; - - loadPluginFactories( findPluginFactories() ); - - connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) ); } SipHandler::~SipHandler() { qDebug() << Q_FUNC_INFO; - disconnectAll(); - qDeleteAll( m_allPlugins ); + s_instance = 0; } @@ -91,122 +87,18 @@ SipHandler::avatar( const QString& name ) const #endif const SipInfo -SipHandler::sipInfo(const QString& peerId) const +SipHandler::sipInfo( const QString& peerId ) const { return m_peersSipInfos.value( peerId ); } const QString -SipHandler::versionString(const QString& peerId) const +SipHandler::versionString( const QString& peerId ) const { return m_peersSoftwareVersions.value( peerId ); } -void -SipHandler::onSettingsChanged() -{ - checkSettings(); -} - - -QStringList -SipHandler::findPluginFactories() -{ - QStringList paths; - QList< QDir > pluginDirs; - - QDir appDir( qApp->applicationDirPath() ); - #ifdef Q_WS_MAC - if ( appDir.dirName() == "MacOS" ) - { - // Development convenience-hack - appDir.cdUp(); - appDir.cdUp(); - appDir.cdUp(); - } - #endif - - QDir libDir( CMAKE_INSTALL_PREFIX "/lib" ); - - QDir lib64Dir( appDir ); - lib64Dir.cdUp(); - lib64Dir.cd( "lib64" ); - - pluginDirs << appDir << libDir << lib64Dir << QDir( qApp->applicationDirPath() ); - foreach ( const QDir& pluginDir, pluginDirs ) - { - qDebug() << "Checking directory for plugins:" << pluginDir; - foreach ( QString fileName, pluginDir.entryList( QStringList() << "*tomahawk_sip*.so" << "*tomahawk_sip*.dylib" << "*tomahawk_sip*.dll", QDir::Files ) ) - { - if ( fileName.startsWith( "libtomahawk_sip" ) ) - { - const QString path = pluginDir.absoluteFilePath( fileName ); - if ( !paths.contains( path ) ) - paths << path; - } - } - } - - return paths; -} - - -void -SipHandler::loadPluginFactories( const QStringList& paths ) -{ - foreach ( QString fileName, paths ) - { - if ( !QLibrary::isLibrary( fileName ) ) - continue; - - qDebug() << "Trying to load plugin:" << fileName; - loadPluginFactory( fileName ); - } -} - - -SipPlugin* -SipHandler::createPlugin( const QString& factoryId ) -{ - Q_ASSERT( m_pluginFactories.contains( factoryId ) ); - - SipPlugin* sip = m_pluginFactories[ factoryId ]->createPlugin(); - hookUpPlugin( sip ); - - emit pluginAdded( sip ); - return sip; -} - - -SipPlugin* -SipHandler::loadPlugin( const QString& pluginId ) -{ - QString factoryName = factoryFromId( pluginId ); - - Q_ASSERT( m_pluginFactories.contains( factoryName ) ); - - SipPlugin* sip = m_pluginFactories[ factoryName ]->createPlugin( pluginId ); - - // caller responsible for calling pluginAdded() and hookupPlugin - return sip; -} - - -void -SipHandler::removePlugin( SipPlugin* p ) -{ - p->disconnectPlugin(); - - m_allPlugins.removeAll( p ); - m_enabledPlugins.removeAll( p ); - - TomahawkSettings::instance()->removeSipPlugin( p->pluginId() ); - - emit pluginRemoved( p ); -} - - void SipHandler::hookUpPlugin( SipPlugin* sip ) { @@ -216,250 +108,10 @@ SipHandler::hookUpPlugin( SipPlugin* sip ) QObject::connect( sip, SIGNAL( sipInfoReceived( QString, SipInfo ) ), SLOT( onSipInfo( QString, SipInfo ) ) ); QObject::connect( sip, SIGNAL( softwareVersionReceived( QString, QString ) ), SLOT( onSoftwareVersion( QString, QString ) ) ); - QObject::connect( sip, SIGNAL( error( int, QString ) ), SLOT( onError( int, QString ) ) ); - QObject::connect( sip, SIGNAL( stateChanged( SipPlugin::ConnectionState ) ), SLOT( onStateChanged( SipPlugin::ConnectionState ) ) ); - QObject::connect( sip, SIGNAL( avatarReceived( QString, QPixmap ) ), SLOT( onAvatarReceived( QString, QPixmap ) ) ); QObject::connect( sip, SIGNAL( avatarReceived( QPixmap ) ), SLOT( onAvatarReceived( QPixmap ) ) ); -} - -void -SipHandler::loadPluginFactory( const QString& path ) -{ - QPluginLoader loader( path ); - QObject* plugin = loader.instance(); - if ( !plugin ) - { - qDebug() << "Error loading plugin:" << loader.errorString(); - } - - SipPluginFactory* sipfactory = qobject_cast(plugin); - if ( sipfactory ) - { - qDebug() << "Loaded plugin factory:" << loader.fileName() << sipfactory->factoryId() << sipfactory->prettyName(); - m_pluginFactories[ sipfactory->factoryId() ] = sipfactory; - } else - { - qDebug() << "Loaded invalid plugin.." << loader.fileName(); - } -} - - -bool -SipHandler::pluginLoaded( const QString& pluginId ) const -{ - foreach( SipPlugin* plugin, m_allPlugins ) - { - if ( plugin->pluginId() == pluginId ) - return true; - } - - return false; -} - - -void -SipHandler::checkSettings() -{ - foreach( SipPlugin* sip, m_allPlugins ) - { - sip->checkSettings(); - } -} - - -void -SipHandler::addSipPlugin( SipPlugin* p, bool enabled, bool startup ) -{ - m_allPlugins << p; - - hookUpPlugin( p ); - if ( enabled ) - { - p->connectPlugin( startup ); - m_enabledPlugins << p; - } - - emit pluginAdded( p ); -} - - -void -SipHandler::removeSipPlugin( SipPlugin* p ) -{ - p->disconnectPlugin(); - p->deletePlugin(); - - emit pluginRemoved( p ); - // emit first so sipmodel can find the indexOf - - TomahawkSettings::instance()->removeSipPlugin( p->pluginId() ); - m_allPlugins.removeAll( p ); - m_enabledPlugins.removeAll( p ); -} - - -bool -SipHandler::hasPluginType( const QString& factoryId ) const -{ - foreach( SipPlugin* p, m_allPlugins ) { - if( factoryFromId( p->pluginId() ) == factoryId ) - return true; - } - return false; -} - - -void -SipHandler::loadFromConfig( bool startup ) -{ - QStringList pluginIds = TomahawkSettings::instance()->sipPlugins(); - QStringList enabled = TomahawkSettings::instance()->enabledSipPlugins(); - foreach( const QString& pluginId, pluginIds ) - { - QString pluginFactory = factoryFromId( pluginId ); - if( m_pluginFactories.contains( pluginFactory ) ) - { - SipPlugin* p = loadPlugin( pluginId ); - addSipPlugin( p, enabled.contains( pluginId ), startup ); - } - } - m_connected = true; -} - - -void -SipHandler::connectAll() -{ - foreach( SipPlugin* sip, m_enabledPlugins ) - { - sip->connectPlugin(); - } - m_connected = true; -} - - -void -SipHandler::disconnectAll() -{ - foreach( SipPlugin* p, m_connectedPlugins ) - p->disconnectPlugin(); - - SourceList::instance()->removeAllRemote(); - m_connected = false; -} - - -void -SipHandler::disablePlugin( SipPlugin* p ) -{ - Q_ASSERT( m_enabledPlugins.contains( p ) ); - - TomahawkSettings::instance()->disableSipPlugin( p->pluginId() ); - p->disconnectPlugin(); - - m_enabledPlugins.removeAll( p ); -} - - -void -SipHandler::enablePlugin( SipPlugin* p ) -{ - Q_ASSERT( !m_enabledPlugins.contains( p ) ); - p->connectPlugin(); - - TomahawkSettings::instance()->enableSipPlugin( p->pluginId() ); - m_enabledPlugins << p; -} - - -void -SipHandler::connectPlugin( bool startup, const QString &pluginId ) -{ -#ifndef ENABLE_HEADLESS - if ( !TomahawkSettings::instance()->acceptedLegalWarning() ) - { - int result = QMessageBox::question( - //TomahawkApp::instance()->mainWindow(), - 0, tr( "Legal Warning" ), - tr( "By pressing I Agree below, you agree that your use of Tomahawk will be in accordance with any applicable laws, including copyright and intellectual property laws, in effect in your country of residence, and indemnify the Tomahawk developers and project from liability should you choose to break those laws.\n\nFor more information, please see http://gettomahawk.com/legal" ), - tr( "I Do Not Agree" ), tr( "I Agree" ) - ); - if ( result != 1 ) - return; - else - TomahawkSettings::instance()->setAcceptedLegalWarning( true ); - } -#endif - foreach( SipPlugin* sip, m_allPlugins ) - { - if ( sip->pluginId() == pluginId ) - { - Q_ASSERT( m_enabledPlugins.contains( sip ) ); // make sure the plugin we're connecting is enabled. should always be the case - //each sip should refreshProxy() or take care of that function in some other way during connection - sip->connectPlugin( startup ); - } - } -} - - -void -SipHandler::disconnectPlugin( const QString &pluginName ) -{ - foreach( SipPlugin* sip, m_connectedPlugins ) - { - if ( sip->name() == pluginName ) - sip->disconnectPlugin(); - } -} - - -QList< SipPlugin* > -SipHandler::allPlugins() const -{ - return m_allPlugins; -} - - -QList< SipPlugin* > -SipHandler::enabledPlugins() const -{ - return m_enabledPlugins; -} - - -QList< SipPlugin* > -SipHandler::connectedPlugins() const -{ - return m_connectedPlugins; -} - - -QList< SipPluginFactory* > -SipHandler::pluginFactories() const -{ - return m_pluginFactories.values(); -} - - -void -SipHandler::toggleConnect() -{ - if( m_connected ) - disconnectAll(); - else - connectAll(); -} - - -void -SipHandler::refreshProxy() -{ - qDebug() << Q_FUNC_INFO; - - foreach( SipPlugin* sip, m_allPlugins ) - sip->refreshProxy(); + QObject::connect( sip->account(), SIGNAL( configurationChanged() ), sip, SLOT( configurationChanged() ) ); } @@ -557,46 +209,6 @@ SipHandler::onMessage( const QString& from, const QString& msg ) qDebug() << Q_FUNC_INFO << from << msg; } - -void -SipHandler::onError( int code, const QString& msg ) -{ - SipPlugin* sip = qobject_cast< SipPlugin* >( sender() ); - Q_ASSERT( sip ); - - qWarning() << "Failed to connect to SIP:" << sip->accountName() << code << msg; - - if ( code == SipPlugin::AuthError ) - { - emit authError( sip ); - } - else - { - QTimer::singleShot( 10000, sip, SLOT( connectPlugin() ) ); - } -} - - -void -SipHandler::onStateChanged( SipPlugin::ConnectionState state ) -{ - SipPlugin* sip = qobject_cast< SipPlugin* >( sender() ); - Q_ASSERT( sip ); - - if ( sip->connectionState() == SipPlugin::Disconnected ) - { - m_connectedPlugins.removeAll( sip ); - emit disconnected( sip ); - } - else if ( sip->connectionState() == SipPlugin::Connected ) - { - m_connectedPlugins << sip; - emit connected( sip ); - } - - emit stateChanged( sip, state ); -} - #ifndef ENABLE_HEADLESS void SipHandler::onAvatarReceived( const QString& from, const QPixmap& avatar ) @@ -643,18 +255,3 @@ SipHandler::onAvatarReceived( const QPixmap& avatar ) SourceList::instance()->getLocal()->setAvatar( avatar ); } #endif - - -QString -SipHandler::factoryFromId( const QString& pluginId ) const -{ - return pluginId.split( "_" ).first(); -} - - -SipPluginFactory* -SipHandler::factoryFromPlugin( SipPlugin* p ) const -{ - QString factoryId = factoryFromId( p->pluginId() ); - return m_pluginFactories.value( factoryId, 0 ); -} diff --git a/src/libtomahawk/sip/SipHandler.h b/src/libtomahawk/sip/SipHandler.h index 9a510ba2d..fabf25f9e 100644 --- a/src/libtomahawk/sip/SipHandler.h +++ b/src/libtomahawk/sip/SipHandler.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,8 +31,12 @@ #include #endif +/** + * Manages SIP plugins for connecting to friends. External interface to SIP plugins is + * through AccountManager, this is an internal class. + */ -class DLLEXPORT SipHandler : public QObject +class SipHandler : public QObject { Q_OBJECT @@ -41,17 +46,7 @@ public: SipHandler( QObject* parent ); ~SipHandler(); - QList< SipPluginFactory* > pluginFactories() const; - QList< SipPlugin* > allPlugins() const; - QList< SipPlugin* > enabledPlugins() const; - QList< SipPlugin* > connectedPlugins() const; - void loadFromConfig( bool startup = false ); - - void addSipPlugin( SipPlugin* p, bool enable = true, bool connectImmediately = true ); - void removeSipPlugin( SipPlugin* p ); - - bool hasPluginType( const QString& factoryId ) const; - SipPluginFactory* factoryFromPlugin( SipPlugin* p ) const; + void loadFromAccountManager(); #ifndef ENABLE_HEADLESS const QPixmap avatar( const QString& name ) const; @@ -61,37 +56,7 @@ public: const SipInfo sipInfo( const QString& peerId ) const; const QString versionString( const QString& peerId ) const; -public slots: - void checkSettings(); - - void enablePlugin( SipPlugin* p ); - void disablePlugin( SipPlugin* p ); - - void connectPlugin( bool startup = false, const QString &pluginId = QString() ); - void disconnectPlugin( const QString &pluginId = QString() ); - void connectAll(); - void disconnectAll(); - - void toggleConnect(); - - void refreshProxy(); - - // create a new plugin of the given name. the name is the value returned in SipPluginFactory::pluginName - // be default sip plugins are NOt connected when created - SipPlugin* createPlugin( const QString& factoryName ); - // load a plugin with the given id - SipPlugin* loadPlugin( const QString& pluginId ); - void removePlugin( SipPlugin* p ); - -signals: - void connected( SipPlugin* ); - void disconnected( SipPlugin* ); - void authError( SipPlugin* ); - - void stateChanged( SipPlugin* p, SipPlugin::ConnectionState state ); - - void pluginAdded( SipPlugin* p ); - void pluginRemoved( SipPlugin* p ); + void hookUpPlugin( SipPlugin* p ); private slots: void onSipInfo( const QString& peerId, const SipInfo& info ); @@ -99,10 +64,6 @@ private slots: void onMessage( const QString&, const QString& ); void onPeerOffline( const QString& ); void onPeerOnline( const QString& ); - void onError( int code, const QString& msg ); - void onStateChanged( SipPlugin::ConnectionState ); - - void onSettingsChanged(); #ifndef ENABLE_HEADLESS // set data for local source @@ -115,20 +76,6 @@ private slots: private: static SipHandler *s_instance; - QStringList findPluginFactories(); - bool pluginLoaded( const QString& pluginId ) const; - void hookUpPlugin( SipPlugin* p ); - - void loadPluginFactories( const QStringList& paths ); - void loadPluginFactory( const QString& path ); - QString factoryFromId( const QString& pluginId ) const; - - QHash< QString, SipPluginFactory* > m_pluginFactories; - QList< SipPlugin* > m_allPlugins; - QList< SipPlugin* > m_enabledPlugins; - QList< SipPlugin* > m_connectedPlugins; - bool m_connected; - //TODO: move this to source QHash m_peersSipInfos; QHash m_peersSoftwareVersions; diff --git a/src/libtomahawk/sip/SipModel.cpp b/src/libtomahawk/sip/SipModel.cpp deleted file mode 100644 index 863769cbb..000000000 --- a/src/libtomahawk/sip/SipModel.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - Copyright (C) 2011 Leo Franchi - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - - -#include "SipModel.h" - -#include "tomahawksettings.h" -#include "sip/SipHandler.h" -#include "sip/SipPlugin.h" - -#include "utils/logger.h" - - -SipModel::SipModel( QObject* parent ) - : QAbstractItemModel( parent ) -{ - connect( SipHandler::instance(), SIGNAL( stateChanged( SipPlugin*, SipPlugin::ConnectionState ) ), this, SLOT( pluginStateChanged( SipPlugin* ) ) ); - connect( SipHandler::instance(), SIGNAL( pluginAdded( SipPlugin* ) ), this, SLOT( pluginAdded( SipPlugin* ) ) ); - connect( SipHandler::instance(), SIGNAL( pluginRemoved( SipPlugin* ) ), this, SLOT( pluginRemoved( SipPlugin* ) ) ); - - // TODO disable inline factories for now - /* - foreach( SipPluginFactory* f, SipHandler::instance()->pluginFactories() ) { - if( f->isCreatable() ) - m_factories << f; - } */ - -} - - -SipModel::~SipModel() -{ -} - - -QVariant -SipModel::data( const QModelIndex& index, int role ) const -{ - if( !index.isValid() ) - return QVariant(); - - if( !index.parent().isValid() && index.row() == SipHandler::instance()->allPlugins().count() ) { // last row, this is the factory - if( role == Qt::DisplayRole ) - return tr( "Add New Account..." ); - else if( role == FactoryRole ) - return true; - else - return QVariant(); - } - - if( !index.parent().isValid() ) { // account - QList< SipPlugin* > plugins = SipHandler::instance()->allPlugins(); - Q_ASSERT( index.row() <= plugins.size() ); - SipPlugin* p = plugins[ index.row() ]; - switch( role ) - { - case Qt::DisplayRole: - case SipModel::PluginName: - return p->accountName(); - case SipModel::ConnectionStateRole: - return p->connectionState(); - case SipModel::HasConfig: - return ( p->configWidget() != 0 ); - case SipModel::FactoryRole: - return false; - case Qt::DecorationRole: - return p->icon(); - case SipModel::SipPluginData: - return QVariant::fromValue< QObject* >( p ); - case Qt::CheckStateRole: - return SipHandler::instance()->enabledPlugins().contains( p ) ? Qt::Checked : Qt::Unchecked; - default: - return QVariant(); - } - } - - if( index.parent().isValid() ) { // this is a factory type - SipPluginFactory* p = m_factories.at( index.row() ); - switch( role ) - { - case Qt::DisplayRole: - return p->prettyName(); - case SipModel::FactoryItemRole: - return true; - case SipModel::FactoryItemIcon: - return p->icon(); - case SipModel::SipPluginFactoryData: - return QVariant::fromValue< QObject* >( p ); - default: - return QVariant(); - } - } - - return QVariant(); -} - - -bool -SipModel::setData( const QModelIndex& index, const QVariant& value, int role ) -{ - Q_ASSERT( index.isValid() && index.row() <= SipHandler::instance()->allPlugins().count() ); - - if( role == Qt::CheckStateRole ) { - Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() ); - QList< SipPlugin* > plugins = SipHandler::instance()->allPlugins(); - SipPlugin* p = plugins[ index.row() ]; - - if( state == Qt::Checked && !SipHandler::instance()->enabledPlugins().contains( p ) ) { - SipHandler::instance()->enablePlugin( p ); - } else if( state == Qt::Unchecked ) { - SipHandler::instance()->disablePlugin( p ); - } - dataChanged( index, index ); - - return true; - } - return false; -} - - -QModelIndex -SipModel::index( int row, int column, const QModelIndex& parent ) const -{ - if( !parent.isValid() ) - return hasIndex( row, column, parent ) ? createIndex( row, column, 0 ) : QModelIndex(); - -// qDebug() << "Creating index for non-top level row!"; - // it's a child of the Add Account, e.g. a factory - if( hasIndex( row, column, parent ) ) { - return createIndex( row, column, 1 /* magic */ ); - } - - return QModelIndex(); -} - - -QModelIndex -SipModel::parent( const QModelIndex& child ) const -{ - if( !child.isValid() ) - return QModelIndex(); - - if( child.internalId() == 1 ) { - return index( SipHandler::instance()->allPlugins().size(), 0, QModelIndex() ); - } - - return QModelIndex(); -} - - -int -SipModel::rowCount( const QModelIndex& parent ) const -{ - if( !parent.isValid() ) // invalid root node - return SipHandler::instance()->allPlugins().size() /* TODO inline factories disabled + 1*/; - if( parent.isValid() && !parent.parent().isValid() ) { // top level item - if( parent.row() == SipHandler::instance()->allPlugins().count() ) {// last row, this is the factory - return m_factories.count(); - } - } - - return 0; -} - - -int -SipModel::columnCount(const QModelIndex& parent) const -{ - Q_UNUSED( parent ); - return 1; -} - - -Qt::ItemFlags -SipModel::flags( const QModelIndex& index ) const -{ - if( index.data( SipModel::FactoryRole ).toBool() || index.data( SipModel::FactoryItemRole ).toBool() ) - return QAbstractItemModel::flags( index ) & ~Qt::ItemIsSelectable; - return QAbstractItemModel::flags( index ) | Qt::ItemIsUserCheckable; -} - - -void -SipModel::pluginAdded( SipPlugin* p ) -{ - Q_UNUSED( p ); - // we assume sip plugins are added at the end of the list. - Q_ASSERT( SipHandler::instance()->allPlugins().last() == p ); - int size = SipHandler::instance()->allPlugins().count() - 1; - beginInsertRows( QModelIndex(), size, size ); - endInsertRows(); -} - - -void -SipModel::pluginRemoved( SipPlugin* p ) -{ - int idx = SipHandler::instance()->allPlugins().indexOf( p ); - beginRemoveRows( QModelIndex(), idx, idx ); - endRemoveRows(); -} - - -void -SipModel::pluginStateChanged( SipPlugin* p ) -{ - int at = SipHandler::instance()->allPlugins().indexOf( p ); - QModelIndex idx = index( at, 0, QModelIndex() ); - emit dataChanged( idx, idx ); -} - diff --git a/src/libtomahawk/sip/SipModel.h b/src/libtomahawk/sip/SipModel.h deleted file mode 100644 index 18e15cf0b..000000000 --- a/src/libtomahawk/sip/SipModel.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - - Copyright (C) 2011 Leo Franchi - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - - -#ifndef SIPMODEL_H -#define SIPMODEL_H - -#include "dllmacro.h" - -#include -#include - -class SipPluginFactory; -class SipPlugin; - -class DLLEXPORT SipModel : public QAbstractItemModel -{ - Q_OBJECT -public: - enum Roles { - PluginName = Qt::UserRole + 15, - ConnectionStateRole = Qt::UserRole + 17, - HasConfig = Qt::UserRole + 18, - FactoryRole = Qt::UserRole + 19, - ErrorString = Qt::UserRole + 20, - FactoryItemRole = Qt::UserRole + 21, - FactoryItemIcon = Qt::UserRole + 22, - SipPluginData = Qt::UserRole + 23, - SipPluginFactoryData = Qt::UserRole + 24 - }; - - explicit SipModel( QObject* parent = 0 ); - virtual ~SipModel(); - - virtual QModelIndex index ( int row, int column, const QModelIndex& parent = QModelIndex() ) const; - virtual QModelIndex parent ( const QModelIndex& child ) const; - virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; - virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; - virtual int columnCount( const QModelIndex& parent ) const; - virtual Qt::ItemFlags flags(const QModelIndex& index) const; - virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); - -private slots: - void pluginAdded( SipPlugin* p ); - void pluginRemoved( SipPlugin* p ); - void pluginStateChanged( SipPlugin* p ); - -private: - QList< SipPluginFactory* > m_factories; -}; - -#endif // SIPMODEL_H diff --git a/src/libtomahawk/sip/SipPlugin.cpp b/src/libtomahawk/sip/SipPlugin.cpp index 482ea75dc..ddd151707 100644 --- a/src/libtomahawk/sip/SipPlugin.cpp +++ b/src/libtomahawk/sip/SipPlugin.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * 2011, Dominik Schmidt + * 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,40 +20,40 @@ #include "sip/SipPlugin.h" -#include - #include "utils/logger.h" -QString -SipPluginFactory::generateId() -{ - QString uniq = QUuid::createUuid().toString().mid( 1, 8 ); - return factoryId() + "_" + uniq; -} - SipPlugin::SipPlugin() : QObject() {} SipPlugin::~SipPlugin() {} -SipPlugin::SipPlugin( const QString& pluginId, QObject* parent ) +SipPlugin::SipPlugin( Tomahawk::Accounts::Account *account, QObject* parent ) : QObject( parent ) - , m_pluginId( pluginId ) + , m_account( account ) { - connect( this, SIGNAL( error( int, QString ) ), this, SLOT( onError( int,QString ) ) ); - connect( this, SIGNAL( stateChanged( SipPlugin::ConnectionState ) ), this, SLOT( onStateChange( SipPlugin::ConnectionState ) ) ); - connect( this, SIGNAL( peerOnline( QString ) ), this, SLOT( onPeerOnline( QString ) ) ); - connect( this, SIGNAL( peerOffline( QString ) ), this, SLOT( onPeerOffline( QString ) ) ); } QString SipPlugin::pluginId() const { - return m_pluginId; + return m_account->accountId(); +} + + +const QString +SipPlugin::friendlyName() const +{ + return m_account->accountFriendlyName(); +} + + +const QString +SipPlugin::serviceName() const +{ + return m_account->accountServiceName(); } -#ifndef ENABLE_HEADLESS QMenu* SipPlugin::menu() { @@ -60,25 +61,10 @@ SipPlugin::menu() } -QWidget* -SipPlugin::configWidget() +Tomahawk::Accounts::Account* +SipPlugin::account() const { - return 0; -} - - -QIcon -SipPlugin::icon() const -{ - return QIcon(); -} -#endif - - -QString -SipPlugin::errorMessage() const -{ - return m_cachedError; + return m_account; } @@ -89,29 +75,6 @@ SipPlugin::peersOnline() const } -void -SipPlugin::refreshProxy() -{ - qDebug() << Q_FUNC_INFO << "Not implemented"; -} - - -void -SipPlugin::onError( int code, const QString& error ) -{ - Q_UNUSED( code ); - m_cachedError = error; -} - - -void -SipPlugin::onStateChange( SipPlugin::ConnectionState state ) -{ - Q_UNUSED( state ); - m_cachedError.clear(); -} - - void SipPlugin::onPeerOnline( const QString& peerId ) { @@ -127,9 +90,3 @@ SipPlugin::onPeerOffline( const QString& peerId ) { m_peersOnline.removeAll( peerId ); } - - -void -SipPlugin::deletePlugin() -{ -} diff --git a/src/libtomahawk/sip/SipPlugin.h b/src/libtomahawk/sip/SipPlugin.h index 9e33d3b8b..ea70ec9d6 100644 --- a/src/libtomahawk/sip/SipPlugin.h +++ b/src/libtomahawk/sip/SipPlugin.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * 2011, Dominik Schmidt + * 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +27,7 @@ #include #include +#include "accounts/Account.h" #ifndef ENABLE_HEADLESS #include #endif @@ -34,79 +36,39 @@ class SipPlugin; -class DLLEXPORT SipPluginFactory : public QObject -{ - Q_OBJECT -public: - SipPluginFactory() {} - virtual ~SipPluginFactory() {} - - // display name for plugin - virtual QString prettyName() const = 0; - // internal name - virtual QString factoryId() const = 0; - -#ifndef ENABLE_HEADLESS - // if the user can create multiple - virtual QIcon icon() const { return QIcon(); } -#endif - - virtual bool isUnique() const { return false; } - - virtual SipPlugin* createPlugin( const QString& pluginId = QString() ) = 0; - -protected: - QString generateId(); -}; - class DLLEXPORT SipPlugin : public QObject { Q_OBJECT public: - enum SipErrorCode { AuthError, ConnectionError }; // Placeholder for errors, to be defined - enum ConnectionState { Disconnected, Connecting, Connected, Disconnecting }; - SipPlugin(); - explicit SipPlugin( const QString& pluginId, QObject* parent = 0 ); + explicit SipPlugin( Tomahawk::Accounts::Account *account, QObject* parent = 0 ); virtual ~SipPlugin(); // plugin id is "pluginfactoryname_someuniqueid". get it from SipPluginFactory::generateId QString pluginId() const; virtual bool isValid() const = 0; - virtual const QString name() const = 0; - virtual const QString friendlyName() const = 0; - virtual const QString accountName() const = 0; - virtual ConnectionState connectionState() const = 0; - virtual QString errorMessage() const; + virtual const QString friendlyName() const; + virtual const QString serviceName() const; #ifndef ENABLE_HEADLESS virtual QMenu* menu(); - virtual QWidget* configWidget(); - virtual QIcon icon() const; #endif - virtual void saveConfig() {} // called when the widget has been edited + virtual Tomahawk::Accounts::Account* account() const; // peer infos virtual const QStringList peersOnline() const; public slots: - virtual bool connectPlugin( bool startup = false ) = 0; + virtual void connectPlugin() = 0; virtual void disconnectPlugin() = 0; virtual void checkSettings() = 0; + virtual void configurationChanged() = 0; virtual void addContact( const QString &jid, const QString& msg = QString() ) = 0; virtual void sendMsg( const QString& to, const QString& msg ) = 0; - virtual void refreshProxy(); - - // so plugins can clean up after themselves - virtual void deletePlugin(); - signals: - void error( int, const QString& ); - void stateChanged( SipPlugin::ConnectionState state ); - void peerOnline( const QString& ); void peerOffline( const QString& ); void msgReceived( const QString& from, const QString& msg ); @@ -127,18 +89,14 @@ signals: void dataError( bool ); private slots: - void onError( int, const QString& ); - void onStateChange( SipPlugin::ConnectionState state ); - void onPeerOnline( const QString &peerId ); void onPeerOffline( const QString &peerId ); +protected: + Tomahawk::Accounts::Account *m_account; + private: - QString m_pluginId; - QString m_cachedError; QStringList m_peersOnline; }; -Q_DECLARE_INTERFACE( SipPluginFactory, "tomahawk.SipFactory/1.0" ) - #endif diff --git a/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp b/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp index 1319b6837..b7c04c2d6 100644 --- a/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp +++ b/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp @@ -341,7 +341,7 @@ d( new Private( this ) ) #endif // now listen for commands - startTimer( 250 ); + startTimer( 750 ); } /*! diff --git a/src/libtomahawk/tomahawksettings.cpp b/src/libtomahawk/tomahawksettings.cpp index d278299d5..aaf1ae8d8 100644 --- a/src/libtomahawk/tomahawksettings.cpp +++ b/src/libtomahawk/tomahawksettings.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011 Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,8 +30,6 @@ #include "database/databasecommand_updatesearchindex.h" #include "database/database.h" -#define VERSION 6 - using namespace Tomahawk; TomahawkSettings* TomahawkSettings::s_instance = 0; @@ -54,24 +53,24 @@ TomahawkSettings::TomahawkSettings( QObject* parent ) if ( !contains( "configversion" ) ) { - setValue( "configversion", VERSION ); + setValue( "configversion", TOMAHAWK_SETTINGS_VERSION ); doInitialSetup(); } - else if ( value( "configversion" ).toUInt() != VERSION ) + else if ( value( "configversion" ).toUInt() != TOMAHAWK_SETTINGS_VERSION ) { qDebug() << "Config version outdated, old:" << value( "configversion" ).toUInt() - << "new:" << VERSION + << "new:" << TOMAHAWK_SETTINGS_VERSION << "Doing upgrade, if any..."; int current = value( "configversion" ).toUInt(); - while ( current < VERSION ) + while ( current < TOMAHAWK_SETTINGS_VERSION ) { doUpgrade( current, current + 1 ); current++; } // insert upgrade code here as required - setValue( "configversion", VERSION ); + setValue( "configversion", TOMAHAWK_SETTINGS_VERSION ); } } @@ -86,7 +85,17 @@ void TomahawkSettings::doInitialSetup() { // by default we add a local network resolver - addSipPlugin( "sipzeroconf_autocreated" ); + addAccount( "sipzeroconf_autocreated" ); + + // Add a last.fm account for scrobbling and infosystem + const QString accountKey = QString( "lastfmaccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) ); + addAccount( accountKey ); + + beginGroup( "accounts/" + accountKey ); + setValue( "enabled", false ); + setValue( "autoconnect", true ); + setValue( "types", QStringList() << "ResolverType" << "StatusPushType" ); + endGroup(); } @@ -116,7 +125,7 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) setValue( QString( "%1_legacy/username" ).arg( sipName ), value( "jabber/username" ) ); setValue( QString( "%1_legacy/password" ).arg( sipName ), value( "jabber/password" ) ); - setValue( QString( "%1r_legacy/autoconnect" ).arg( sipName ), value( "jabber/autoconnect" ) ); + setValue( QString( "%1_legacy/autoconnect" ).arg( sipName ), value( "jabber/autoconnect" ) ); setValue( QString( "%1_legacy/port" ).arg( sipName ), value( "jabber/port" ) ); setValue( QString( "%1_legacy/server" ).arg( sipName ), value( "jabber/server" ) ); @@ -163,8 +172,8 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) QStringList toremove; QStringList resolvers = resolverDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ); - QStringList listedResolvers = allScriptResolvers(); - QStringList enabledResolvers = enabledScriptResolvers(); + QStringList listedResolvers = value( "script/resolvers" ).toStringList(); + QStringList enabledResolvers = value( "script/loadedresolvers" ).toStringList(); foreach ( const QString& resolver, resolvers ) { foreach ( const QString& r, listedResolvers ) @@ -184,8 +193,8 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) } } } - setAllScriptResolvers( listedResolvers ); - setEnabledScriptResolvers( enabledResolvers ); + setValue( "script/resolvers", listedResolvers ); + setValue( "script/loadedresolvers", enabledResolvers ); tDebug() << "UPGRADING AND DELETING:" << resolverDir.absolutePath(); TomahawkUtils::removeDirectory( resolverDir.absolutePath() ); } @@ -194,6 +203,142 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) { // 0.3.0 contained a bug which prevent indexing local files. Force a reindex. QTimer::singleShot( 0, this, SLOT( updateIndex() ) ); + } else if ( oldVersion == 6 ) + { + // Migrate to accounts from sipplugins. + // collect old connected and enabled sip plugins + const QStringList allSip = sipPlugins(); + const QStringList enabledSip = enabledSipPlugins(); + + QStringList accounts; + foreach ( const QString& sipPlugin, allSip ) + { + const QStringList parts = sipPlugin.split( "_" ); + Q_ASSERT( parts.size() == 2 ); + + const QString pluginName = parts[ 0 ]; + const QString pluginId = parts[ 1 ]; + + // new key, account_ + QString rawpluginname = pluginName; + rawpluginname.replace( "sip", "" ); + if ( rawpluginname.contains( "jabber" ) ) + rawpluginname.replace( "jabber", "xmpp" ); + + QString accountKey = QString( "%1account_%2" ).arg( rawpluginname ).arg( pluginId ); + + if ( pluginName == "sipjabber" || pluginName == "sipgoogle" ) + { + QVariantHash credentials; + credentials[ "username" ] = value( sipPlugin + "/username" ); + credentials[ "password" ] = value( sipPlugin + "/password" ); + credentials[ "port" ] = value( sipPlugin + "/port" ); + credentials[ "server" ] = value( sipPlugin + "/server" ); + + setValue( QString( "accounts/%1/credentials" ).arg( accountKey ), credentials ); + setValue( QString( "accounts/%1/accountfriendlyname" ).arg( accountKey ), value( sipPlugin + "/username" ) ); + + } + else if ( pluginName == "siptwitter" ) + { + // Only port twitter plugin if there's a valid twitter config + if ( value( sipPlugin + "/oauthtokensecret" ).toString().isEmpty() && + value( sipPlugin + "/oauthtoken" ).toString().isEmpty() && + value( sipPlugin + "/screenname" ).toString().isEmpty() ) + continue; + + QVariantHash credentials; + credentials[ "oauthtoken" ] = value( sipPlugin + "/oauthtoken" ); + credentials[ "oauthtokensecret" ] = value( sipPlugin + "/oauthtokensecret" ); + credentials[ "username" ] = value( sipPlugin + "/screenname" ); + + QVariantHash configuration; + configuration[ "cachedfriendssinceid" ] = value( sipPlugin + "/cachedfriendssinceid" ); + configuration[ "cacheddirectmessagessinceid" ] = value( sipPlugin + "/cacheddirectmessagessinceid" ); + configuration[ "cachedpeers" ] = value( sipPlugin + "/cachedpeers" ); + qDebug() << "FOUND CACHED PEERS:" << value( sipPlugin + "/cachedpeers" ).toHash(); + configuration[ "cachedmentionssinceid" ] = value( sipPlugin + "/cachedmentionssinceid" ); + configuration[ "saveddbid" ] = value( sipPlugin + "/saveddbid" ); + + setValue( QString( "accounts/%1/credentials" ).arg( accountKey ), credentials ); + setValue( QString( "accounts/%1/configuration" ).arg( accountKey ), configuration ); + setValue( QString( "accounts/%1/accountfriendlyname" ).arg( accountKey ), "@" + value( sipPlugin + "/screenname" ).toString() ); + sync(); + } + else if ( pluginName == "sipzeroconf" ) + { + setValue( QString( "accounts/%1/accountfriendlyname" ).arg( accountKey ), "Local Network" ); + } + + beginGroup( "accounts/" + accountKey ); + setValue( "enabled", enabledSip.contains( sipPlugin ) == true ); + setValue( "autoconnect", true ); + setValue( "acl", QVariantMap() ); + setValue( "types", QStringList() << "SipType" ); + endGroup(); + accounts << accountKey; + + remove( sipPlugin ); + } + remove( "sip" ); + + // Migrate all resolvers from old resolvers settings to new accounts system + const QStringList allResolvers = value( "script/resolvers" ).toStringList(); + const QStringList enabledResolvers = value( "script/loadedresolvers" ).toStringList(); + + foreach ( const QString& resolver, allResolvers ) + { + const QString accountKey = QString( "resolveraccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) ); + accounts << accountKey; + + beginGroup( "accounts/" + accountKey ); + setValue( "enabled", enabledResolvers.contains( resolver ) == true ); + setValue( "autoconnect", true ); + setValue( "types", QStringList() << "ResolverType" ); + + QVariantHash configuration; + configuration[ "path" ] = resolver; + + // reasonably ugly check for attica resolvers + if ( resolver.contains( "atticaresolvers" ) && resolver.contains( "code" ) ) + { + setValue( "atticaresolver", true ); + + QFileInfo info( resolver ); + configuration[ "atticaId" ] = info.baseName(); + } + + setValue( "configuration", configuration ); + endGroup(); + + } + + // Add a Last.Fm account since we now moved the infoplugin into the account + const QString accountKey = QString( "lastfmaccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) ); + accounts << accountKey; + const QString lfmUsername = value( "lastfm/username" ).toString(); + const QString lfmPassword = value( "lastfm/password" ).toString(); + const bool scrobble = value( "lastfm/enablescrobbling", false ).toBool(); + beginGroup( "accounts/" + accountKey ); + setValue( "enabled", enabledResolvers.contains( "lastfm" ) == true ); + setValue( "autoconnect", true ); + setValue( "types", QStringList() << "ResolverType" << "StatusPushType" ); + QVariantHash credentials; + credentials[ "username" ] = lfmUsername; + credentials[ "password" ] = lfmPassword; + credentials[ "session" ] = value( "lastfm/session" ).toString(); + setValue( "credentials", credentials ); + QVariantHash configuration; + configuration[ "scrobble" ] = scrobble; + setValue( "configuration", configuration ); + endGroup(); + + remove( "lastfm" ); + + remove( "script/resolvers" ); + remove( "script/loadedresolvers" ); + + setValue( "accounts/allaccounts", accounts ); } } @@ -690,6 +835,38 @@ TomahawkSettings::removeSipPlugin( const QString& pluginId ) } +QStringList +TomahawkSettings::accounts() const +{ + return value( "accounts/allaccounts", QStringList() ).toStringList(); +} + + +void +TomahawkSettings::setAccounts( const QStringList& accountIds ) +{ + setValue( "accounts/allaccounts", accountIds ); +} + + +void +TomahawkSettings::addAccount( const QString& accountId ) +{ + QStringList list = accounts(); + list << accountId; + setAccounts( list ); +} + + +void +TomahawkSettings::removeAccount( const QString& accountId ) +{ + QStringList list = accounts(); + list.removeAll( accountId ); + setAccounts( list ); +} + + TomahawkSettings::ExternalAddressMode TomahawkSettings::externalAddressMode() const { @@ -754,62 +931,6 @@ TomahawkSettings::setExternalPort(int externalPort) } -QString -TomahawkSettings::lastFmPassword() const -{ - return value( "lastfm/password" ).toString(); -} - - -void -TomahawkSettings::setLastFmPassword( const QString& password ) -{ - setValue( "lastfm/password", password ); -} - - -QByteArray -TomahawkSettings::lastFmSessionKey() const -{ - return value( "lastfm/session" ).toByteArray(); -} - - -void -TomahawkSettings::setLastFmSessionKey( const QByteArray& key ) -{ - setValue( "lastfm/session", key ); -} - - -QString -TomahawkSettings::lastFmUsername() const -{ - return value( "lastfm/username" ).toString(); -} - - -void -TomahawkSettings::setLastFmUsername( const QString& username ) -{ - setValue( "lastfm/username", username ); -} - - -bool -TomahawkSettings::scrobblingEnabled() const -{ - return value( "lastfm/enablescrobbling", false ).toBool(); -} - - -void -TomahawkSettings::setScrobblingEnabled( bool enable ) -{ - setValue( "lastfm/enablescrobbling", enable ); -} - - QString TomahawkSettings::xmppBotServer() const { @@ -866,41 +987,6 @@ TomahawkSettings::setXmppBotPort( const int port ) } -void -TomahawkSettings::addScriptResolver(const QString& resolver) -{ - setValue( "script/resolvers", allScriptResolvers() << resolver ); -} - - -QStringList -TomahawkSettings::allScriptResolvers() const -{ - return value( "script/resolvers" ).toStringList(); -} - - -void -TomahawkSettings::setAllScriptResolvers( const QStringList& resolver ) -{ - setValue( "script/resolvers", resolver ); -} - - -QStringList -TomahawkSettings::enabledScriptResolvers() const -{ - return value( "script/loadedresolvers" ).toStringList(); -} - - -void -TomahawkSettings::setEnabledScriptResolvers( const QStringList& resolvers ) -{ - setValue( "script/loadedresolvers", resolvers ); -} - - QString TomahawkSettings::scriptDefaultPath() const { diff --git a/src/libtomahawk/tomahawksettings.h b/src/libtomahawk/tomahawksettings.h index 817352b96..1a1d67100 100644 --- a/src/libtomahawk/tomahawksettings.h +++ b/src/libtomahawk/tomahawksettings.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011 Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +26,9 @@ #include #include "dllmacro.h" + +#define TOMAHAWK_SETTINGS_VERSION 7 + /** * Convenience wrapper around QSettings for tomahawk-specific config */ @@ -97,9 +101,6 @@ public: void setSipPlugins( const QStringList& plugins ); QStringList sipPlugins() const; - void setBookmarkPlaylist( const QString& guid ); - QString bookmarkPlaylist() const; - // just the enabled sip plugins. void setEnabledSipPlugins( const QStringList& list ); QStringList enabledSipPlugins() const; @@ -109,6 +110,15 @@ public: void addSipPlugin( const QString& pluginId, bool enable = true ); void removeSipPlugin( const QString& pluginId ); + void setAccounts( const QStringList& accountIds ); + QStringList accounts() const; + void addAccount( const QString& accountId ); + void removeAccount( const QString& accountId ); + + + void setBookmarkPlaylist( const QString& guid ); + QString bookmarkPlaylist() const; + /// Network settings enum ExternalAddressMode { Lan, Upnp }; ExternalAddressMode externalAddressMode() const; @@ -155,19 +165,6 @@ public: QStringList aclEntries() const; void setAclEntries( const QStringList &entries ); - /// Last.fm settings - bool scrobblingEnabled() const; /// false by default - void setScrobblingEnabled( bool enable ); - - QString lastFmUsername() const; - void setLastFmUsername( const QString& username ); - - QString lastFmPassword() const; - void setLastFmPassword( const QString& password ); - - QByteArray lastFmSessionKey() const; - void setLastFmSessionKey( const QByteArray& key ); - /// XMPP Component Settings QString xmppBotServer() const; void setXmppBotServer( const QString &server ); @@ -181,14 +178,6 @@ public: int xmppBotPort() const; void setXmppBotPort( const int port ); - /// Script resolver settings - QStringList allScriptResolvers() const; - void setAllScriptResolvers( const QStringList& resolvers ); - void addScriptResolver( const QString& resolver ); - QStringList enabledScriptResolvers() const; - void setEnabledScriptResolvers( const QStringList& resolvers ); - - QString scriptDefaultPath() const; void setScriptDefaultPath( const QString& path ); QString playlistDefaultPath() const; diff --git a/src/libtomahawk/typedefs.h b/src/libtomahawk/typedefs.h index b972009ba..ffe6b23ab 100644 --- a/src/libtomahawk/typedefs.h +++ b/src/libtomahawk/typedefs.h @@ -22,6 +22,7 @@ #include #include #include +#include //template class QSharedPointer; @@ -69,6 +70,9 @@ namespace Tomahawk InfoSystemMode }; + class ExternalResolver; + typedef boost::function ResolverFactoryFunc; + }; // ns typedef int AudioErrorCode; diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h index 91b673a6a..42adc2efe 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h @@ -87,11 +87,6 @@ public slots: } signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); - void shuffleModeChanged( bool enabled ); - - void trackCountChanged( unsigned int tracks ); - void sourceTrackCountChanged( unsigned int tracks ); void nextTrackReady(); private slots: diff --git a/src/libtomahawk/widgets/welcomewidget.cpp b/src/libtomahawk/widgets/welcomewidget.cpp index ec86ca545..145771d1c 100644 --- a/src/libtomahawk/widgets/welcomewidget.cpp +++ b/src/libtomahawk/widgets/welcomewidget.cpp @@ -325,7 +325,7 @@ PlaylistDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, painter->setFont( font ); QString author = index.data( RecentlyPlayedPlaylistsModel::PlaylistRole ).value< Tomahawk::playlist_ptr >()->author()->friendlyName(); - if ( author.contains( "@" ) ) + if ( author.indexOf( '@' ) > 0 ) author = author.mid( 0, author.indexOf( '@' ) ); const int w = painter->fontMetrics().width( author ) + 2; diff --git a/src/libtomahawk/widgets/whatshotwidget.h b/src/libtomahawk/widgets/whatshotwidget.h index 5c10581f5..55e30e283 100644 --- a/src/libtomahawk/widgets/whatshotwidget.h +++ b/src/libtomahawk/widgets/whatshotwidget.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main.cpp b/src/main.cpp index 1c20c03a6..f25a34a58 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,6 +36,38 @@ #include "breakpad/BreakPad.h" #endif +inline QDataStream& operator<<(QDataStream& out, const AtticaManager::StateHash& states) +{ + out << TOMAHAWK_SETTINGS_VERSION; + out << (quint32)states.count(); + foreach( const QString& key, states.keys() ) + { + AtticaManager::Resolver resolver = states[ key ]; + out << key << resolver.version << resolver.scriptPath << (qint32)resolver.state << resolver.userRating; + } + return out; +} + + +inline QDataStream& operator>>(QDataStream& in, AtticaManager::StateHash& states) +{ + quint32 count = 0, version = 0; + in >> version; + in >> count; + for ( uint i = 0; i < count; i++ ) + { + QString key, version, scriptPath; + qint32 state, userRating; + in >> key; + in >> version; + in >> scriptPath; + in >> state; + in >> userRating; + states[ key ] = AtticaManager::Resolver( version, scriptPath, userRating, (AtticaManager::ResolverState)state ); + } + return in; +} + #ifdef Q_OS_WIN #include #define argc __argc @@ -64,6 +96,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, } } #else // Q_OS_WIN + int main( int argc, char *argv[] ) { @@ -78,17 +111,12 @@ main( int argc, char *argv[] ) #endif // Q_WS_MAC #endif //Q_OS_WIN -/* // Unity hack taken from Clementine's main.cpp -#ifdef Q_OS_LINUX - // In 11.04 Ubuntu decided that the system tray should be reserved for certain - // whitelisted applications. Tomahawk will override this setting and insert - // itself into the list of whitelisted apps. - setenv( "QT_X11_NO_NATIVE_MENUBAR", "1", true ); - UbuntuUnityHack hack; -#endif*/ - TomahawkApp a( argc, argv ); + // MUST register StateHash ****before*** initing TomahawkSettingsGui as constructor of settings does upgrade before Gui subclass registers type + qRegisterMetaType< AtticaManager::StateHash >( "AtticaManager::StateHash" ); + qRegisterMetaTypeStreamOperators("AtticaManager::StateHash"); + #ifdef ENABLE_HEADLESS new TomahawkSettings( &a ); #else diff --git a/src/resolverconfigdelegate.cpp b/src/resolverconfigdelegate.cpp index 5df004827..96f2274f2 100644 --- a/src/resolverconfigdelegate.cpp +++ b/src/resolverconfigdelegate.cpp @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ #include "resolverconfigdelegate.h" -#include "resolversmodel.h" +// #include "resolversmodel.h" #include "ExternalResolverGui.h" #include @@ -133,8 +133,10 @@ ResolverConfigDelegate::configRectForIndex( const QStyleOptionViewItem& option, } QRect -ResolverConfigDelegate::checkRectForIndex( const QStyleOptionViewItem &option, const QModelIndex &idx ) const +ResolverConfigDelegate::checkRectForIndex( const QStyleOptionViewItem &option, const QModelIndex &idx, int role ) const { + Q_UNUSED( role ); + QStyleOptionViewItemV4 opt = option; initStyleOption( &opt, idx ); QRect itemRect = opt.rect; diff --git a/src/resolverconfigdelegate.h b/src/resolverconfigdelegate.h index 62e3fe9ee..a6c8f32d8 100644 --- a/src/resolverconfigdelegate.h +++ b/src/resolverconfigdelegate.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ public: explicit ResolverConfigDelegate( QObject* parent = 0 ); virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; - virtual QRect checkRectForIndex( const QStyleOptionViewItem &option, const QModelIndex &idx ) const; + virtual QRect checkRectForIndex( const QStyleOptionViewItem &option, const QModelIndex &idx, int role ) const; virtual QRect configRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const; private slots: diff --git a/src/resolversmodel.cpp b/src/resolversmodel.cpp deleted file mode 100644 index c38b02b44..000000000 --- a/src/resolversmodel.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Leo Franchi - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "resolversmodel.h" - -#include - -#include "tomahawksettings.h" -#include "tomahawkapp.h" -#include "ExternalResolverGui.h" -#include "pipeline.h" -#include "config.h" -#include "AtticaManager.h" -#include "utils/logger.h" - -ResolversModel::ResolversModel( QObject* parent ) - : QAbstractListModel( parent ) -{ - addInstalledResolvers(); -} - - -ResolversModel::~ResolversModel() -{ - -} - - -QVariant -ResolversModel::data( const QModelIndex& index, int role ) const -{ - if( !index.isValid() || !hasIndex( index.row(), index.column(), QModelIndex() ) ) - return QVariant(); - - Tomahawk::ExternalResolver* r = Tomahawk::Pipeline::instance()->scriptResolvers().at( index.row() ); - Tomahawk::ExternalResolverGui* res = qobject_cast< Tomahawk::ExternalResolverGui* >( r ); - Q_ASSERT(res); // this is part of the gui, so there should be no non-gui resolvers - switch( role ) - { - case Qt::DisplayRole: - case ResolversModel::ResolverName: - return res->name(); - case ResolversModel::ResolverPath: - return res->filePath(); - case ResolversModel::HasConfig: - return res->configUI() != 0; - case ResolversModel::ErrorState: - return res->error(); - case Qt::CheckStateRole: - return res->running() ? Qt::Checked : Qt::Unchecked; - case Qt::ToolTipRole: - return res->filePath(); - default: - return QVariant(); - } -} - - -bool -ResolversModel::setData( const QModelIndex& index, const QVariant& value, int role ) -{ - if ( !hasIndex( index.row(), index.column(), QModelIndex() ) ) - return false; - - Tomahawk::ExternalResolver* r = Tomahawk::Pipeline::instance()->scriptResolvers().at( index.row() ); - if ( r && r->error() == Tomahawk::ExternalResolver::FileNotFound ) // give it a shot to see if the user manually fixed paths - { - r->reload(); - - if( r->error() == Tomahawk::ExternalResolver::FileNotFound ) // Nope, no luck. Doesn't exist on disk, don't let user mess with it - return false; - } - else if ( !r && !QFile::exists( r->filePath() ) ) - { - return false; - } - - if ( role == Qt::CheckStateRole ) - { - Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() ); - - if ( state == Qt::Checked && !r->running() ) { - r->start(); - } - else if ( state == Qt::Unchecked ) - { - r->stop(); - } - - emit dataChanged( index, index ); - return true; - } - return false; -} - - -int -ResolversModel::rowCount( const QModelIndex& parent ) const -{ - Q_UNUSED( parent ); - return Tomahawk::Pipeline::instance()->scriptResolvers().count(); -} - - -int -ResolversModel::columnCount(const QModelIndex& parent) const -{ - Q_UNUSED( parent ); - return 1; -} - - -Qt::ItemFlags -ResolversModel::flags( const QModelIndex& index ) const -{ - return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable; -} - - -void -ResolversModel::addResolver( const QString& resolver, bool enable ) -{ - const int count = rowCount( QModelIndex() ); - beginInsertRows( QModelIndex(), count, count ); - Tomahawk::ExternalResolver* r = Tomahawk::Pipeline::instance()->addScriptResolver( resolver, enable ); - Tomahawk::ExternalResolverGui* res = qobject_cast< Tomahawk::ExternalResolverGui* >( r ); - Q_ASSERT(res); // this is part of the gui, so there should be no non-gui resolvers - connect( res, SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); - endInsertRows(); - - if ( res->configUI() ) - emit openConfig( res->filePath() ); - else - m_waitingForLoad << resolver; -} - - -void -ResolversModel::atticaResolverInstalled( const QString& resolverId ) -{ -#ifdef LIBATTICA_FOUND - Tomahawk::ExternalResolver* r = Tomahawk::Pipeline::instance()->resolverForPath( AtticaManager::instance()->pathFromId( resolverId ) ); - if ( !r ) - return; - const int idx = Tomahawk::Pipeline::instance()->scriptResolvers().indexOf( r ); - if ( idx >= 0 ) - { - beginInsertRows( QModelIndex(), idx, idx ); - endInsertRows(); - } -#endif -} - - -void -ResolversModel::removeResolver( const QString& resolver ) -{ - const int idx = Tomahawk::Pipeline::instance()->scriptResolvers().indexOf( Tomahawk::Pipeline::instance()->resolverForPath( resolver ) ); - if ( idx < 0 ) - return; - - beginRemoveRows( QModelIndex(), idx, idx ); - Tomahawk::Pipeline::instance()->removeScriptResolver( resolver ); - endRemoveRows(); -} - - -void -ResolversModel::resolverChanged() -{ - Tomahawk::ExternalResolver* res = qobject_cast< Tomahawk::ExternalResolver* >( sender() ); - Q_ASSERT( res ); - - if ( Tomahawk::Pipeline::instance()->scriptResolvers().contains( res ) ) - { - qDebug() << "Got resolverChanged signal, does it have a config UI yet?" << qobject_cast< Tomahawk::ExternalResolverGui* >(res)->configUI(); - const QModelIndex idx = index( Tomahawk::Pipeline::instance()->scriptResolvers().indexOf( res ), 0, QModelIndex() ); - emit dataChanged( idx, idx ); - - if ( m_waitingForLoad.contains( res->filePath() ) ) - { - m_waitingForLoad.remove( res->filePath() ); - emit openConfig( res->filePath() ); - } - } -} - - -void -ResolversModel::addInstalledResolvers() -{ - QList< QDir > pluginDirs; - - QDir appDir( qApp->applicationDirPath() ); - QDir libDir( CMAKE_INSTALL_PREFIX "/lib" ); - QDir libexecDir( CMAKE_INSTALL_FULL_LIBEXECDIR ); - - QDir lib64Dir( appDir ); - lib64Dir.cdUp(); - lib64Dir.cd( "lib64" ); - - pluginDirs << appDir << libDir << lib64Dir << libexecDir; - foreach ( const QDir& pluginDir, pluginDirs ) - { - qDebug() << "Checking directory for resolvers:" << pluginDir; - foreach ( QString fileName, pluginDir.entryList( QStringList() << "*_tomahawkresolver*", QDir::Files ) ){ - if ( fileName.contains( "_tomahawkresolver" ) ) { - const QString path = pluginDir.absoluteFilePath( fileName ); - bool found = false; - foreach ( Tomahawk::ExternalResolver* res, Tomahawk::Pipeline::instance()->scriptResolvers() ) - { - if ( res->filePath() == path ) - found = true; - } - if ( !found ) { - Tomahawk::Pipeline::instance()->addScriptResolver( path, false ); - } - } - } - } -} - - -void -ResolversModel::saveScriptResolvers() -{ - QStringList enabled, all; - foreach ( Tomahawk::ExternalResolver* res, Tomahawk::Pipeline::instance()->scriptResolvers() ) - { - all << res->filePath(); - if ( res->running() ) - enabled << res->filePath(); - } - TomahawkSettings::instance()->setAllScriptResolvers( all ); - TomahawkSettings::instance()->setEnabledScriptResolvers( enabled ); -} diff --git a/src/resolversmodel.h b/src/resolversmodel.h deleted file mode 100644 index 9aee76f72..000000000 --- a/src/resolversmodel.h +++ /dev/null @@ -1,64 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Leo Franchi - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - - -#ifndef RESOLVERSMODEL_H -#define RESOLVERSMODEL_H - -#include -#include -#include - -class ResolversModel : public QAbstractListModel -{ - Q_OBJECT -public: - enum Roles { - ResolverName = Qt::UserRole + 15, - ResolverPath = Qt::UserRole + 16, - HasConfig = Qt::UserRole + 17, - ErrorState = Qt::UserRole + 18 - }; - - explicit ResolversModel( QObject* parent = 0 ); - virtual ~ResolversModel(); - - virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; - virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; - virtual int columnCount( const QModelIndex& parent ) const; - virtual Qt::ItemFlags flags(const QModelIndex& index) const; - virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); - - void addResolver( const QString& resolver, bool enable = false ); - void atticaResolverInstalled ( const QString& resolverId ); - void removeResolver( const QString& resolver ); - - void saveScriptResolvers(); - -signals: - void openConfig( const QString& filePath ); - -private slots: - void resolverChanged(); - -private: - void addInstalledResolvers(); - QSet m_waitingForLoad; -}; - -#endif // RESOLVERSMODEL_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 850701b86..4809ae401 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -30,43 +30,40 @@ #include #include -#ifdef LIBLASTFM_FOUND -#include -#include -#endif - #include "AtticaManager.h" #include "tomahawkapp.h" #include "tomahawksettings.h" #include "delegateconfigwrapper.h" -#include "GetNewStuffDialog.h" #include "musicscanner.h" #include "pipeline.h" #include "resolver.h" #include "ExternalResolverGui.h" -#include "resolverconfigdelegate.h" -#include "resolversmodel.h" #include "scanmanager.h" #include "settingslistdelegate.h" -#include "sipconfigdelegate.h" +#include "AccountDelegate.h" #include "database/database.h" #include "network/servent.h" #include "playlist/dynamic/widgets/LoadingSpinner.h" -#include "sip/SipHandler.h" -#include "sip/SipModel.h" +#include "accounts/AccountModel.h" +#include "accounts/Account.h" +#include "accounts/AccountManager.h" +#include +#include #include "utils/logger.h" +#include "AccountFactoryWrapper.h" #include "ui_proxydialog.h" #include "ui_stackedsettingsdialog.h" +using namespace Tomahawk; +using namespace Accounts; SettingsDialog::SettingsDialog( QWidget *parent ) : QDialog( parent ) , ui( new Ui_StackedSettingsDialog ) , m_proxySettings( this ) , m_rejected( false ) - , m_sipModel( 0 ) - , m_resolversModel( 0 ) + , m_accountModel( 0 ) , m_sipSpinner( 0 ) { ui->setupUi( this ); @@ -75,11 +72,6 @@ SettingsDialog::SettingsDialog( QWidget *parent ) TomahawkUtils::unmarginLayout( layout() ); ui->stackedWidget->setContentsMargins( 4, 4, 4, 0 ); - ui->addSipButton->setFixedWidth( 42 ); - ui->removeSipButton->setFixedWidth( ui->addSipButton->width() ); - ui->addScript->setFixedWidth( 42 ); - ui->removeScript->setFixedWidth( ui->addScript->width() ); - ui->checkBoxReporter->setChecked( s->crashReporterEnabled() ); ui->checkBoxHttp->setChecked( s->httpEnabled() ); ui->checkBoxStaticPreferred->setChecked( s->preferStaticHostPort() ); @@ -106,30 +98,44 @@ SettingsDialog::SettingsDialog( QWidget *parent ) p->setFixedSize( 0, 0 ); #endif - // SIP PLUGINS - SipConfigDelegate* sipdel = new SipConfigDelegate( this ); - ui->accountsView->setItemDelegate( sipdel ); + // Accounts + AccountDelegate* accountDelegate = new AccountDelegate( this ); + ui->accountsView->setItemDelegate( accountDelegate ); ui->accountsView->setContextMenuPolicy( Qt::CustomContextMenu ); ui->accountsView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); + ui->accountsView->setMouseTracking( true ); - connect( ui->accountsView, SIGNAL( clicked( QModelIndex ) ), this, SLOT( sipItemClicked( QModelIndex ) ) ); - connect( sipdel, SIGNAL( openConfig( SipPlugin* ) ), this, SLOT( openSipConfig( SipPlugin* ) ) ); - connect( ui->accountsView, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( sipContextMenuRequest( QPoint ) ) ); - m_sipModel = new SipModel( this ); - ui->accountsView->setModel( m_sipModel ); + connect( accountDelegate, SIGNAL( openConfig( Tomahawk::Accounts::Account* ) ), this, SLOT( openAccountConfig( Tomahawk::Accounts::Account* ) ) ); + connect( accountDelegate, SIGNAL( openConfig( Tomahawk::Accounts::AccountFactory* ) ), this, SLOT( openAccountFactoryConfig( Tomahawk::Accounts::AccountFactory* ) ) ); + connect( accountDelegate, SIGNAL( update( QModelIndex ) ), ui->accountsView, SLOT( update( QModelIndex ) ) ); + + m_accountModel = new AccountModel( this ); + m_accountProxy = new AccountModelFilterProxy( m_accountModel ); + m_accountProxy->setSourceModel( m_accountModel ); + + connect( m_accountProxy, SIGNAL( scrollTo( QModelIndex ) ), this, SLOT( scrollTo( QModelIndex ) ) ); + + ui->accountsView->setModel( m_accountProxy ); + + connect( ui->installFromFileBtn, SIGNAL( clicked( bool ) ), this, SLOT( installFromFile() ) ); + connect( m_accountModel, SIGNAL( createAccount( Tomahawk::Accounts::AccountFactory* ) ), this, SLOT( createAccountFromFactory( Tomahawk::Accounts::AccountFactory* ) ) ); + + ui->accountsFilterCombo->addItem( tr( "All" ), Accounts::NoType ); + ui->accountsFilterCombo->addItem( accountTypeToString( SipType ), SipType ); + ui->accountsFilterCombo->addItem( accountTypeToString( ResolverType ), ResolverType ); + ui->accountsFilterCombo->addItem( accountTypeToString( InfoType ), InfoType ); + + connect( ui->accountsFilterCombo, SIGNAL( activated( int ) ), this, SLOT( accountsFilterChanged( int ) ) ); if ( !Servent::instance()->isReady() ) { m_sipSpinner = new LoadingSpinner( ui->accountsView ); m_sipSpinner->fadeIn(); - ui->addSipButton->setEnabled( false ); - ui->removeSipButton->setEnabled( false ); connect( Servent::instance(), SIGNAL( ready() ), this, SLOT( serventReady() ) ); } - setupSipButtons(); - + // ADVANCED ui->staticHostName->setText( s->externalHostname() ); ui->staticPort->setValue( s->externalPort() ); ui->proxyButton->setVisible( true ); @@ -157,44 +163,12 @@ SettingsDialog::SettingsDialog( QWidget *parent ) } // NOW PLAYING -#ifdef Q_WS_MAC - ui->checkBoxEnableAdium->setChecked( s->nowPlayingEnabled() ); -#else - ui->checkBoxEnableAdium->hide(); -#endif +// #ifdef Q_WS_MAC +// ui->checkBoxEnableAdium->setChecked( s->nowPlayingEnabled() ); +// #else +// ui->checkBoxEnableAdium->hide(); +// #endif - // LAST FM - ui->checkBoxEnableLastfm->setChecked( s->scrobblingEnabled() ); - ui->lineEditLastfmUsername->setText( s->lastFmUsername() ); - ui->lineEditLastfmPassword->setText(s->lastFmPassword() ); - connect( ui->pushButtonTestLastfmLogin, SIGNAL( clicked( bool) ), SLOT( testLastFmLogin() ) ); - -#ifdef Q_WS_MAC // FIXME - ui->pushButtonTestLastfmLogin->setVisible( false ); -#endif - - // SCRIPT RESOLVER - ui->removeScript->setEnabled( false ); - ResolverConfigDelegate* del = new ResolverConfigDelegate( this ); - connect( del, SIGNAL( openConfig( QString ) ), SLOT( openResolverConfig( QString ) ) ); - ui->scriptList->setItemDelegate( del ); - m_resolversModel = new ResolversModel( this ); - ui->scriptList->setModel( m_resolversModel ); - ui->scriptList->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); - connect( m_resolversModel, SIGNAL( openConfig( QString ) ), SLOT( openResolverConfig( QString ) ) ); - -#ifdef LIBATTICA_FOUND - connect( ui->getMoreResolvers, SIGNAL( clicked() ), this, SLOT( getMoreResolvers() ) ); - - connect( AtticaManager::instance(), SIGNAL( resolverInstalled( QString ) ), this, SLOT( atticaResolverInstalled( QString ) ) ); - connect( AtticaManager::instance(), SIGNAL( resolverUninstalled( QString ) ), this, SLOT( atticaResolverUninstalled( QString ) ) ); -#else - ui->getMoreResolvers->hide(); -#endif - - connect( ui->scriptList->selectionModel(), SIGNAL( selectionChanged( QItemSelection,QItemSelection ) ), this, SLOT( scriptSelectionChanged() ) ); - connect( ui->addScript, SIGNAL( clicked( bool ) ), this, SLOT( addScriptResolver() ) ); - connect( ui->removeScript, SIGNAL( clicked( bool ) ), this, SLOT( removeScriptResolver() ) ); connect( ui->proxyButton, SIGNAL( clicked() ), SLOT( showProxySettings() ) ); connect( ui->checkBoxStaticPreferred, SIGNAL( toggled(bool) ), SLOT( toggleUpnp(bool) ) ); connect( ui->checkBoxStaticPreferred, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) ); @@ -228,13 +202,7 @@ SettingsDialog::~SettingsDialog() s->setScannerTime( ui->scannerTimeSpinBox->value() ); s->setEnableEchonestCatalogs( ui->enableEchonestCatalog->isChecked() ); - s->setNowPlayingEnabled( ui->checkBoxEnableAdium->isChecked() ); - - s->setScrobblingEnabled( ui->checkBoxEnableLastfm->isChecked() ); - s->setLastFmUsername( ui->lineEditLastfmUsername->text() ); - s->setLastFmPassword( ui->lineEditLastfmPassword->text() ); - - m_resolversModel->saveScriptResolvers(); +// s->setNowPlayingEnabled( ui->checkBoxEnableAdium->isChecked() ); s->applyChanges(); s->sync(); @@ -250,8 +218,6 @@ void SettingsDialog::serventReady() { m_sipSpinner->fadeOut(); - ui->addSipButton->setEnabled( true ); - ui->removeSipButton->setEnabled( true ); } @@ -268,7 +234,7 @@ SettingsDialog::createIcons() QFontMetrics fm( font() ); QListWidgetItem *accountsButton = new QListWidgetItem( ui->listWidget ); accountsButton->setIcon( QIcon( RESPATH "images/account-settings.png" ) ); - accountsButton->setText( tr( "Accounts" ) ); + accountsButton->setText( tr( "Services" ) ); accountsButton->setTextAlignment( Qt::AlignHCenter ); accountsButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); maxlen = fm.width( accountsButton->text() ); @@ -280,20 +246,6 @@ SettingsDialog::createIcons() musicButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); maxlen = qMax( fm.width( musicButton->text() ), maxlen ); - QListWidgetItem *lastfmButton = new QListWidgetItem( ui->listWidget ); - lastfmButton->setIcon( QIcon( RESPATH "images/lastfm-settings.png" ) ); - lastfmButton->setText( tr( "Last.fm" ) ); - lastfmButton->setTextAlignment( Qt::AlignHCenter ); - lastfmButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); - maxlen = qMax( fm.width( lastfmButton->text() ), maxlen ); - - QListWidgetItem *resolversButton = new QListWidgetItem( ui->listWidget ); - resolversButton->setIcon( QIcon( RESPATH "images/resolvers-settings.png" ) ); - resolversButton->setText( tr( "Resolvers" ) ); - resolversButton->setTextAlignment( Qt::AlignHCenter ); - resolversButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); - maxlen = qMax( fm.width( resolversButton->text() ), maxlen ); - QListWidgetItem *advancedButton = new QListWidgetItem( ui->listWidget ); advancedButton->setIcon( QIcon( RESPATH "images/advanced-settings.png" ) ); advancedButton->setText( tr( "Advanced" ) ); @@ -304,8 +256,6 @@ SettingsDialog::createIcons() maxlen += 15; // padding accountsButton->setSizeHint( QSize( maxlen, 60 ) ); musicButton->setSizeHint( QSize( maxlen, 60 ) ); - lastfmButton->setSizeHint( QSize( maxlen, 60 ) ); - resolversButton->setSizeHint( QSize( maxlen, 60 ) ); advancedButton->setSizeHint( QSize( maxlen, 60 ) ); #ifndef Q_WS_MAC @@ -317,27 +267,6 @@ SettingsDialog::createIcons() } -void -SettingsDialog::setupSipButtons() -{ - foreach( SipPluginFactory* f, SipHandler::instance()->pluginFactories() ) - { - if( f->isUnique() && SipHandler::instance()->hasPluginType( f->factoryId() ) ) - { - continue; - } - - QAction* action = new QAction( f->icon(), f->prettyName(), ui->addSipButton ); - action->setProperty( "factory", QVariant::fromValue< QObject* >( f ) ); - ui->addSipButton->addAction( action ); - - connect( action, SIGNAL( triggered(bool) ), this, SLOT( factoryActionTriggered( bool ) ) ); - } - - connect( ui->removeSipButton, SIGNAL( clicked( bool ) ), this, SLOT( sipPluginDeleted( bool ) ) ); -} - - void SettingsDialog::changePage( QListWidgetItem* current, QListWidgetItem* previous ) { @@ -407,413 +336,214 @@ SettingsDialog::updateScanOptionsView() void -SettingsDialog::testLastFmLogin() +SettingsDialog::accountsFilterChanged( int ) { -#ifdef LIBLASTFM_FOUND - ui->pushButtonTestLastfmLogin->setEnabled( false ); - ui->pushButtonTestLastfmLogin->setText( "Testing..." ); - - QString authToken = TomahawkUtils::md5( ( ui->lineEditLastfmUsername->text().toLower() + TomahawkUtils::md5( ui->lineEditLastfmPassword->text().toUtf8() ) ).toUtf8() ); - - // now authenticate w/ last.fm and get our session key - QMap query; - query[ "method" ] = "auth.getMobileSession"; - query[ "username" ] = ui->lineEditLastfmUsername->text().toLower(); - query[ "authToken" ] = authToken; - - // ensure they have up-to-date settings - lastfm::setNetworkAccessManager( TomahawkUtils::nam() ); - - QNetworkReply* authJob = lastfm::ws::post( query ); - - connect( authJob, SIGNAL( finished() ), SLOT( onLastFmFinished() ) ); -#endif + AccountType filter = static_cast< AccountType >( ui->accountsFilterCombo->itemData( ui->accountsFilterCombo->currentIndex() ).toInt() ); + m_accountProxy->setFilterType( filter ); } void -SettingsDialog::onLastFmFinished() +SettingsDialog::openAccountConfig( Account* account, bool showDelete ) { -#ifdef LIBLASTFM_FOUND - QNetworkReply* authJob = dynamic_cast( sender() ); - if( !authJob ) + if( account->configurationWidget() ) { - qDebug() << Q_FUNC_INFO << "No auth job returned!"; +#ifndef Q_OS_MAC + DelegateConfigWrapper dialog( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), this ); + dialog.setShowDelete( showDelete ); + QWeakPointer< DelegateConfigWrapper > watcher( &dialog ); + int ret = dialog.exec(); + if ( !watcher.isNull() && dialog.deleted() ) + { + AccountManager::instance()->removeAccount( account ); + } + else if( !watcher.isNull() && ret == QDialog::Accepted ) + { + // send changed config to resolver + account->saveConfig(); + } +#else + // on osx a sheet needs to be non-modal + DelegateConfigWrapper* dialog = new DelegateConfigWrapper( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), this, Qt::Sheet ); + dialog->setShowDelete( showDelete ); + dialog->setProperty( "accountplugin", QVariant::fromValue< QObject* >( account ) ); + connect( dialog, SIGNAL( finished( int ) ), this, SLOT( accountConfigClosed( int ) ) ); + connect( dialog, SIGNAL( closedWithDelete() ), this, SLOT( accountConfigDelete() ) ); + + dialog->show(); +#endif + } +} + + +void +SettingsDialog::accountConfigClosed( int value ) +{ + if( value == QDialog::Accepted ) + { + DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() ); + Account* account = qobject_cast< Account* >( dialog->property( "accountplugin" ).value< QObject* >() ); + account->saveConfig(); + } +} + + +void +SettingsDialog::accountConfigDelete() +{ + DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() ); + Account* account = qobject_cast< Account* >( dialog->property( "accountplugin" ).value< QObject* >() ); + Q_ASSERT( account ); + AccountManager::instance()->removeAccount( account ); +} + + +void +SettingsDialog::openAccountFactoryConfig( AccountFactory* factory ) +{ + QList< Account* > accts; + foreach ( Account* acct, AccountManager::instance()->accounts() ) + { + if ( AccountManager::instance()->factoryForAccount( acct ) == factory ) + accts << acct; + if ( accts.size() > 1 ) + break; + } + Q_ASSERT( accts.size() > 0 ); // Shouldn't have a config wrench if there are no accounts! + if ( accts.size() == 1 ) + { + // If there's just one, open the config directly w/ the delete button. Otherwise open the multi dialog + openAccountConfig( accts.first(), true ); return; } - if( authJob->error() == QNetworkReply::NoError ) - { - lastfm::XmlQuery lfm = lastfm::XmlQuery( authJob->readAll() ); - if( lfm.children( "error" ).size() > 0 ) - { - qDebug() << "ERROR from last.fm:" << lfm.text(); - ui->pushButtonTestLastfmLogin->setText( tr( "Failed" ) ); - ui->pushButtonTestLastfmLogin->setEnabled( true ); - } - else - { - ui->pushButtonTestLastfmLogin->setText( tr( "Success" ) ); - ui->pushButtonTestLastfmLogin->setEnabled( false ); - } - } - else - { - switch( authJob->error() ) - { - case QNetworkReply::ContentOperationNotPermittedError: - case QNetworkReply::AuthenticationRequiredError: - ui->pushButtonTestLastfmLogin->setText( tr( "Failed" ) ); - ui->pushButtonTestLastfmLogin->setEnabled( true ); - break; +#ifndef Q_OS_MAC + AccountFactoryWrapper dialog( factory, this ); + QWeakPointer< AccountFactoryWrapper > watcher( &dialog ); - default: - qDebug() << "Couldn't get last.fm auth result"; - ui->pushButtonTestLastfmLogin->setText( tr( "Could not contact server" ) ); - ui->pushButtonTestLastfmLogin->setEnabled( true ); - return; - } - } -#endif -} - - -void -SettingsDialog::addScriptResolver() -{ - QString resolver = QFileDialog::getOpenFileName( this, tr( "Load script resolver file" ), TomahawkSettings::instance()->scriptDefaultPath() ); - if( !resolver.isEmpty() ) - { - m_resolversModel->addResolver( resolver, true ); - - QFileInfo resolverAbsoluteFilePath = resolver; - TomahawkSettings::instance()->setScriptDefaultPath( resolverAbsoluteFilePath.absolutePath() ); - } -} - - -void -SettingsDialog::removeScriptResolver() -{ - // only one selection - if( !ui->scriptList->selectionModel()->selectedIndexes().isEmpty() ) - { - QString resolver = ui->scriptList->selectionModel()->selectedIndexes().first().data( ResolversModel::ResolverPath ).toString(); -#ifdef LIBATTICA_FOUND - AtticaManager::instance()->uninstallResolver( resolver ); -#endif - m_resolversModel->removeResolver( resolver ); - } -} - - -void -SettingsDialog::getMoreResolvers() -{ -#if defined(Q_WS_MAC) && defined(LIBATTICA_FOUND) - GetNewStuffDialog* diag = new GetNewStuffDialog( this, Qt::Sheet ); - connect( diag, SIGNAL( finished( int ) ), this, SLOT( getMoreResolversFinished( int ) ) ); - diag->show(); -#elif defined(LIBATTICA_FOUND) - GetNewStuffDialog diag( this ); - int ret = diag.exec(); - Q_UNUSED( ret ); -#endif - -} - - -#ifdef LIBATTICA_FOUND -void -SettingsDialog::atticaResolverInstalled( const QString& resolverId ) -{ - m_resolversModel->atticaResolverInstalled( resolverId ); -} - - -void -SettingsDialog::atticaResolverUninstalled ( const QString& resolverId ) -{ - m_resolversModel->removeResolver( AtticaManager::instance()->pathFromId( resolverId ) ); -} -#endif - - -void -SettingsDialog::scriptSelectionChanged() -{ - if( !ui->scriptList->selectionModel()->selectedIndexes().isEmpty() ) - { - ui->removeScript->setEnabled( true ); - } - else - { - ui->removeScript->setEnabled( false ); - } -} - - -void -SettingsDialog::getMoreResolversFinished( int ret ) -{ - Q_UNUSED( ret ); -} - - -void -SettingsDialog::openResolverConfig( const QString& resolver ) -{ - Tomahawk::ExternalResolver* r = Tomahawk::Pipeline::instance()->resolverForPath( resolver ); - Tomahawk::ExternalResolverGui* res = qobject_cast< Tomahawk::ExternalResolverGui* >( r ); - if( res && res->configUI() ) - { -#ifndef Q_WS_MAC - DelegateConfigWrapper dialog( res->configUI(), "Resolver Configuration", this ); - QWeakPointer< DelegateConfigWrapper > watcher( &dialog ); - int ret = dialog.exec(); - if( !watcher.isNull() && ret == QDialog::Accepted ) - { - // send changed config to resolver - r->saveConfig(); - } + int ret = dialog.exec(); + if ( !watcher.isNull() && dialog.doCreateAccount() ) + createAccountFromFactory( factory ); #else - // on osx a sheet needs to be non-modal - DelegateConfigWrapper* dialog = new DelegateConfigWrapper( res->configUI(), "Resolver Configuration", this, Qt::Sheet ); - dialog->setProperty( "resolver", QVariant::fromValue< QObject* >( res ) ); - connect( dialog, SIGNAL( finished( int ) ), this, SLOT( resolverConfigClosed( int ) ) ); + // on osx a sheet needs to be non-modal + AccountFactoryWrapper* dialog = new AccountFactoryWrapper( factory, this ); + connect( dialog, SIGNAL( createAccount( Tomahawk::Accounts::AccountFactory* ) ), this, SLOT( createAccountFromFactory( Tomahawk::Accounts::AccountFactory* ) ) ); - dialog->show(); + dialog->show(); #endif - } } void -SettingsDialog::resolverConfigClosed( int value ) +SettingsDialog::createAccountFromFactory( AccountFactory* factory ) { - if( value == QDialog::Accepted ) +#ifdef Q_WS_MAC + // On mac we need to close the dialog we came from before showing another dialog, if we are b eing called from a dialog + if ( sender() ) { - DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() ); - Tomahawk::ExternalResolver* r = qobject_cast< Tomahawk::ExternalResolver* >( dialog->property( "resolver" ).value< QObject* >() ); - r->saveConfig(); + Q_ASSERT( qobject_cast< AccountFactoryWrapper* >( sender() ) ); + AccountFactoryWrapper* dialog = qobject_cast< AccountFactoryWrapper* >( sender() ); + dialog->accept(); } -} - - -void -SettingsDialog::sipItemClicked( const QModelIndex& item ) -{ - if( item.data( SipModel::FactoryRole ).toBool() ) - if( ui->accountsView->isExpanded( item ) ) - ui->accountsView->collapse( item ); - else - ui->accountsView->expand( item ); - else if( item.data( SipModel::FactoryItemRole ).toBool() ) - sipFactoryClicked( qobject_cast( item.data( SipModel::SipPluginFactoryData ).value< QObject* >() ) ); -} - - -void -SettingsDialog::openSipConfig( SipPlugin* p ) -{ - if( p->configWidget() ) - { -#ifndef Q_WS_MAC - DelegateConfigWrapper dialog( p->configWidget(), QString("%1 Configuration" ).arg( p->friendlyName() ), this ); - QWeakPointer< DelegateConfigWrapper > watcher( &dialog ); - int ret = dialog.exec(); - if( !watcher.isNull() && ret == QDialog::Accepted ) - { - // send changed config to resolver - p->saveConfig(); - } -#else - // on osx a sheet needs to be non-modal - DelegateConfigWrapper* dialog = new DelegateConfigWrapper( p->configWidget(), QString("%1 Configuration" ).arg( p->friendlyName() ), this, Qt::Sheet ); - dialog->setProperty( "sipplugin", QVariant::fromValue< QObject* >( p ) ); - connect( dialog, SIGNAL( finished( int ) ), this, SLOT( sipConfigClosed( int ) ) ); - - dialog->show(); #endif - } -} - -void -SettingsDialog::sipConfigClosed( int value ) -{ - if( value == QDialog::Accepted ) - { - DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() ); - SipPlugin* p = qobject_cast< SipPlugin* >( dialog->property( "sipplugin" ).value< QObject* >() ); - p->saveConfig(); - } -} - - -void -SettingsDialog::factoryActionTriggered( bool ) -{ - Q_ASSERT( sender() && qobject_cast< QAction* >( sender() ) ); - - QAction* a = qobject_cast< QAction* >( sender() ); - Q_ASSERT( qobject_cast< SipPluginFactory* >( a->property( "factory" ).value< QObject* >() ) ); - - SipPluginFactory* f = qobject_cast< SipPluginFactory* >( a->property( "factory" ).value< QObject* >() ); - sipFactoryClicked( f ); -} - - -void -SettingsDialog::sipFactoryClicked( SipPluginFactory* factory ) -{ //if exited with OK, create it, if not, delete it immediately! - SipPlugin* p = factory->createPlugin(); + Account* account = factory->createAccount(); bool added = false; - if( p->configWidget() ) + if( account->configurationWidget() ) { #ifdef Q_WS_MAC // on osx a sheet needs to be non-modal - DelegateConfigWrapper* dialog = new DelegateConfigWrapper( p->configWidget(), QString("%1 Config" ).arg( p->friendlyName() ), this, Qt::Sheet ); - dialog->setProperty( "sipplugin", QVariant::fromValue< QObject* >( p ) ); - connect( dialog, SIGNAL( finished( int ) ), this, SLOT( sipCreateConfigClosed( int ) ) ); - connect( p, SIGNAL( datatError( bool ) ), dialog, SLOT( toggleOkButton( bool ) ) ); + DelegateConfigWrapper* dialog = new DelegateConfigWrapper( account->configurationWidget(), QString("%1 Config" ).arg( account->accountFriendlyName() ), this, Qt::Sheet ); + dialog->setProperty( "accountplugin", QVariant::fromValue< QObject* >( account ) ); + connect( dialog, SIGNAL( finished( int ) ), this, SLOT( accountCreateConfigClosed( int ) ) ); + + if( account->configurationWidget()->metaObject()->indexOfSignal( "dataError(bool)" ) > -1 ) + connect( account->configurationWidget(), SIGNAL( dataError( bool ) ), dialog, SLOT( toggleOkButton( bool ) ), Qt::UniqueConnection ); dialog->show(); #else - DelegateConfigWrapper dialog( p->configWidget(), QString("%1 Config" ).arg( p->friendlyName() ), this ); + DelegateConfigWrapper dialog( account->configurationWidget(), QString("%1 Config" ).arg( account->accountFriendlyName() ), this ); QWeakPointer< DelegateConfigWrapper > watcher( &dialog ); - connect( p, SIGNAL( dataError( bool ) ), &dialog, SLOT( toggleOkButton( bool ) ) ); + + if( account->configurationWidget()->metaObject()->indexOfSignal( "dataError(bool)" ) > -1 ) + connect( account->configurationWidget(), SIGNAL( dataError( bool ) ), &dialog, SLOT( toggleOkButton( bool ) ), Qt::UniqueConnection ); + int ret = dialog.exec(); - if( !watcher.isNull() && ret == QDialog::Accepted ) - { - // send changed config to resolver - p->saveConfig(); - - // accepted, so add it to tomahawk - TomahawkSettings::instance()->addSipPlugin( p->pluginId() ); - SipHandler::instance()->addSipPlugin( p ); - + if( !watcher.isNull() && ret == QDialog::Accepted ) // send changed config to account added = true; - } - else - { - // canceled, delete it + else // canceled, delete it added = false; - } - handleSipPluginAdded( p, added ); + handleAccountAdded( account, added ); #endif } else { // no config, so just add it added = true; - TomahawkSettings::instance()->addSipPlugin( p->pluginId() ); - SipHandler::instance()->addSipPlugin( p ); - - handleSipPluginAdded( p, added ); + handleAccountAdded( account, added ); } } void -SettingsDialog::sipCreateConfigClosed( int finished ) +SettingsDialog::accountCreateConfigClosed( int finished ) { DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() ); - SipPlugin* p = qobject_cast< SipPlugin* >( dialog->property( "sipplugin" ).value< QObject* >() ); - Q_ASSERT( p ); + Account* account = qobject_cast< Account* >( dialog->property( "accountplugin" ).value< QObject* >() ); + Q_ASSERT( account ); - bool added = false; - if( finished == QDialog::Accepted ) - { + bool added = ( finished == QDialog::Accepted ); - p->saveConfig(); - TomahawkSettings::instance()->addSipPlugin( p->pluginId() ); - SipHandler::instance()->addSipPlugin( p ); - - added = true; - } - - handleSipPluginAdded( p, added ); + handleAccountAdded( account, added ); } void -SettingsDialog::handleSipPluginAdded( SipPlugin* p, bool added ) +SettingsDialog::handleAccountAdded( Account* account, bool added ) { - SipPluginFactory* f = SipHandler::instance()->factoryFromPlugin( p ); - if( added && f && f->isUnique() ) + if ( added ) { - // remove from actions list - QAction* toremove = 0; - foreach( QAction* a, ui->addSipButton->actions() ) - { - if( f == qobject_cast< SipPluginFactory* >( a->property( "factory" ).value< QObject* >() ) ) - { - toremove = a; - break; - } - } - if( toremove ) - ui->addSipButton->removeAction( toremove ); + account->setEnabled( true ); + account->setAutoConnect( true ); + account->saveConfig(); + + TomahawkSettings::instance()->addAccount( account->accountId() ); + AccountManager::instance()->addAccount( account ); + AccountManager::instance()->hookupAndEnable( account ); } - else if( added == false ) - { // user pressed cancel - delete p; + else + { + // user pressed cancel + delete account; } } void -SettingsDialog::sipContextMenuRequest( const QPoint& p ) +SettingsDialog::installFromFile() { - QModelIndex idx = ui->accountsView->indexAt( p ); - // if it's an account, allow to delete - if( idx.isValid() && !idx.data( SipModel::FactoryRole ).toBool() && !idx.data( SipModel::FactoryItemRole ).toBool() ) + const QString resolver = QFileDialog::getOpenFileName( this, tr( "Install resolver from file" ), TomahawkSettings::instance()->scriptDefaultPath() ); + + if( !resolver.isEmpty() ) { - QList< QAction* > acts; - acts << new QAction( tr( "Delete Account" ), this ); - acts.first()->setProperty( "sipplugin", idx.data( SipModel::SipPluginData ) ); - connect( acts.first(), SIGNAL( triggered( bool ) ), this, SLOT( sipPluginRowDeleted( bool ) ) ); - QMenu::exec( acts, ui->accountsView->mapToGlobal( p ) ); + Account* acct = ResolverAccountFactory::createFromPath( resolver, false ); + AccountManager::instance()->addAccount( acct ); + TomahawkSettings::instance()->addAccount( acct->accountId() ); + AccountManager::instance()->enableAccount( acct ); + + + QFileInfo resolverAbsoluteFilePath( resolver ); + TomahawkSettings::instance()->setScriptDefaultPath( resolverAbsoluteFilePath.absolutePath() ); } } void -SettingsDialog::sipPluginRowDeleted( bool ) +SettingsDialog::scrollTo( const QModelIndex& idx ) { - SipPlugin* p = qobject_cast< SipPlugin* >( qobject_cast< QAction* >( sender() )->property( "sipplugin" ).value< QObject* >() ); - SipHandler::instance()->removeSipPlugin( p ); -} - - -void -SettingsDialog::sipPluginDeleted( bool ) -{ - QModelIndexList indexes = ui->accountsView->selectionModel()->selectedIndexes(); - // if it's an account, allow to delete - foreach( const QModelIndex& idx, indexes ) - { - if( idx.isValid() && !idx.data( SipModel::FactoryRole ).toBool() && !idx.data( SipModel::FactoryItemRole ).toBool() ) - { - SipPlugin* p = qobject_cast< SipPlugin* >( idx.data( SipModel::SipPluginData ).value< QObject* >() ); - - if( SipPluginFactory* f = SipHandler::instance()->factoryFromPlugin( p ) ) - { - if( f->isUnique() ) // just deleted a unique plugin->re-add to add menu - { - QAction* action = new QAction( f->icon(), f->prettyName(), ui->addSipButton ); - action->setProperty( "factory", QVariant::fromValue< QObject* >( f ) ); - ui->addSipButton->addAction( action ); - - connect( action, SIGNAL( triggered(bool) ), this, SLOT( factoryActionTriggered( bool ) ) ); - } - } - SipHandler::instance()->removeSipPlugin( p ); - } - } + ui->accountsView->scrollTo( idx, QAbstractItemView::PositionAtBottom ); } diff --git a/src/settingsdialog.h b/src/settingsdialog.h index ef2bdb90e..ca146efb3 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,9 +28,7 @@ class LoadingSpinner; class QListWidgetItem; class Ui_StackedSettingsDialog; -class SipPluginFactory; class SipPlugin; -class SipModel; class ResolversModel; class QNetworkReply; @@ -39,6 +38,18 @@ namespace Ui class ProxyDialog; } +namespace Tomahawk +{ + namespace Accounts + { + class AccountModel; + class Account; + class AccountFactory; +class Account; +class AccountModelFilterProxy; +} +} + class ProxyDialog : public QDialog { Q_OBJECT @@ -75,35 +86,21 @@ private slots: void toggleUpnp( bool preferStaticEnabled ); void showProxySettings(); - void testLastFmLogin(); - void onLastFmFinished(); + void accountsFilterChanged( int ); - void addScriptResolver(); - void scriptSelectionChanged(); - void removeScriptResolver(); - void getMoreResolvers(); - void getMoreResolversFinished( int ); -#ifdef LIBATTICA_FOUND - void atticaResolverInstalled( const QString& ); - void atticaResolverUninstalled( const QString& ); -#endif + void createAccountFromFactory( Tomahawk::Accounts::AccountFactory* ); - void openResolverConfig( const QString& ); - void sipItemClicked ( const QModelIndex& ); - void openSipConfig( SipPlugin* ); - void factoryActionTriggered ( bool ); - void sipFactoryClicked( SipPluginFactory* ); - void sipContextMenuRequest( const QPoint& ); - void sipPluginDeleted( bool ); - void sipPluginRowDeleted( bool ); + void openAccountConfig( Tomahawk::Accounts::Account*, bool showDelete = false ); + void openAccountFactoryConfig( Tomahawk::Accounts::AccountFactory* ); + void accountConfigClosed( int value ); + void accountConfigDelete(); + void accountCreateConfigClosed( int value ); + + void installFromFile(); + void scrollTo( const QModelIndex& ); void updateScanOptionsView(); - // dialog slots - void resolverConfigClosed( int value ); - void sipConfigClosed( int value ); - void sipCreateConfigClosed( int value ); - void changePage( QListWidgetItem*, QListWidgetItem* ); void serventReady(); @@ -111,16 +108,16 @@ private slots: private: void createIcons(); - void setupSipButtons(); - void handleSipPluginAdded( SipPlugin* p, bool added ); + void handleAccountAdded( Tomahawk::Accounts::Account* p, bool added ); Ui_StackedSettingsDialog* ui; ProxyDialog m_proxySettings; bool m_rejected; - SipModel* m_sipModel; - ResolversModel* m_resolversModel; + Tomahawk::Accounts::AccountModel* m_accountModel; + Tomahawk::Accounts::AccountModelFilterProxy* m_accountProxy; LoadingSpinner* m_sipSpinner; }; #endif // SETTINGSDIALOG_H + diff --git a/src/sip/jabber/googlewrapper/googlewrapper.cpp b/src/sip/jabber/googlewrapper/googlewrapper.cpp deleted file mode 100644 index b04d80ad7..000000000 --- a/src/sip/jabber/googlewrapper/googlewrapper.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - - Copyright (C) 2011 Leo Franchi - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - - -#include "googlewrapper.h" -#include "ui_configwidget.h" - -#include "utils/tomahawkutilsgui.h" - -#include -#include - - -SipPlugin* -GoogleWrapperFactory::createPlugin( const QString& pluginId ) -{ - return new GoogleWrapper( pluginId.isEmpty() ? generateId() : pluginId ); -} - - -QIcon -GoogleWrapperFactory::icon() const -{ - return QIcon( ":/gmail-logo.png" ); -} - - -GoogleWrapper::GoogleWrapper ( const QString& pluginID ) - : JabberPlugin ( pluginID ) -{ - m_ui->headerLabel->setText( tr( "Configure this Google Account" ) ); - m_ui->emailLabel->setText( tr( "Google Address" ) ); - m_ui->jabberBlurb->setText( tr( "Enter your Google login to connect with your friends using Tomahawk!" ) ); - m_ui->logoLabel->setPixmap( QPixmap( ":/gmail-logo.png" ) ); - m_ui->jabberServer->setText( "talk.google.com" ); - m_ui->jabberPort->setValue( 5222 ); - m_ui->groupBoxJabberAdvanced->hide(); -} - - -QIcon -GoogleWrapper::icon() const -{ - return QIcon( ":/gmail-logo.png" ); -} - - -QString -GoogleWrapper::defaultSuffix() const -{ - return "@gmail.com"; -} - - -void -GoogleWrapper::showAddFriendDialog() -{ - bool ok; - QString id = QInputDialog::getText( TomahawkUtils::tomahawkWindow(), tr( "Add Friend" ), - tr( "Enter Google Address:" ), QLineEdit::Normal, "", &ok ).trimmed(); - if ( !ok ) - return; - - qDebug() << "Attempting to add google contact to roster:" << id; - addContact( id ); -} - - -#ifdef GOOGLE_WRAPPER -Q_EXPORT_PLUGIN2( sipfactory, GoogleWrapperFactory ) -#endif diff --git a/src/sip/twitter/tomahawkoauthtwitter.h b/src/sip/twitter/tomahawkoauthtwitter.h deleted file mode 100644 index 6b1a75465..000000000 --- a/src/sip/twitter/tomahawkoauthtwitter.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef TOMAHAWKOAUTHTWITTER -#define TOMAHAWKOAUTHTWITTER - -#include "../sipdllmacro.h" - -#include -#include - -class SIPDLLEXPORT TomahawkOAuthTwitter : public OAuthTwitter -{ - Q_OBJECT - -public: - TomahawkOAuthTwitter( QNetworkAccessManager *nam, QObject *parent = 0 ); - - ~TomahawkOAuthTwitter() {} - -protected: - virtual int authorizationWidget(); - -private slots: - void error(); -}; - -#endif diff --git a/src/sipconfigdelegate.cpp b/src/sipconfigdelegate.cpp deleted file mode 100644 index 20cfcb891..000000000 --- a/src/sipconfigdelegate.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/* - Copyright (C) 2011 Leo Franchi - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - - -#include "sipconfigdelegate.h" - -#include -#include - -#include "sip/SipModel.h" -#include "sip/SipPlugin.h" - -#include "utils/tomahawkutils.h" -#include "utils/logger.h" - -#define ICONSIZE 24 -#define CHECK_LEFT_EDGE 8 - - -SipConfigDelegate::SipConfigDelegate( QObject* parent ) - : ConfigDelegateBase ( parent ) -{ - connect( this, SIGNAL( configPressed( QModelIndex ) ), this, SLOT( askedForEdit( QModelIndex ) ) ); -} - -bool -SipConfigDelegate::editorEvent ( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) -{ - return ConfigDelegateBase::editorEvent( event, model, option, index ); -} - -void -SipConfigDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const -{ - QStyleOptionViewItemV4 opt = option; - initStyleOption( &opt, index ); - QRect itemRect = opt.rect; - int top = itemRect.top(); - int mid = itemRect.height() / 2; - - // one line bold for account name - // space below it fro an error - // checkbox, icon, name, online/offline status, config icon - QFont name = opt.font; - name.setPointSize( name.pointSize() + 2 ); - name.setBold( true ); - - QFont error = opt.font; - error.setItalic( true ); - error.setPointSize( error.pointSize() - 2 ); - - // draw the background - const QWidget* w = opt.widget; - QStyle* style = w ? w->style() : QApplication::style(); - style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, w ); - - int iconLeftEdge = CHECK_LEFT_EDGE + ICONSIZE + PADDING; - int textLeftEdge = iconLeftEdge + ICONSIZE + PADDING; - - if( index.data( SipModel::FactoryRole ).toBool() ) { // this is the "add new account" row - // draw a border and background - painter->save(); - painter->setRenderHints( QPainter::Antialiasing ); - painter->setBrush( QApplication::palette().color( QPalette::Active, QPalette::Highlight ).lighter( 150 ) ); - QPainterPath roundedRect; - roundedRect.addRoundedRect( itemRect.adjusted( 1, 1, -1, -1 ), 3, 3 ); - painter->drawPath( roundedRect ); - painter->setBrush( QApplication::palette().color( QPalette::Active, QPalette::Highlight ).lighter( 170 ) ); - painter->fillPath( roundedRect, painter->brush() ); - painter->restore(); - - // draw "+" icon in checkbox column - int rectW = 18; - int diff = ( ICONSIZE/ 2 ) - ( rectW / 2) ; - int pos = ( mid ) - ( rectW / 2 ); - QRect plusRect = QRect( CHECK_LEFT_EDGE + diff, pos + top, rectW, rectW ); - QPixmap p( RESPATH "images/list-add.png" ); - painter->drawPixmap( plusRect, p ); - - // draw text - QFont f = opt.font; - f.setPointSize( f.pointSize() ); - f.setBold( true ); - QFontMetrics fm( f ); - QString text = index.data( Qt::DisplayRole ).toString(); - QRect textR = fm.boundingRect( text ); - textR.moveLeft( textLeftEdge ); - textR.moveTop( mid - ( textR.height() / 2 ) + top ); - textR.setRight( itemRect.right() ); - painter->setFont( f ); - painter->drawText( textR, text ); - } else if( index.data( SipModel::FactoryItemRole ).toBool() ) { // this is an account type - -// ConfigDelegateBase::paint( painter, opt, index ); -// int indent = 10; - // draw a border and background - painter->save(); - painter->setRenderHints( QPainter::Antialiasing ); - painter->setBrush( QApplication::palette().color( QPalette::Active, QPalette::Highlight ).lighter( 170 ) ); - QPainterPath roundedRect; - roundedRect.addRoundedRect( itemRect.adjusted( 1, 1, -1, -1 ), 3, 3 ); - painter->drawPath( roundedRect ); - painter->setBrush( QApplication::palette().color( QPalette::Active, QPalette::Highlight ).lighter( 180 ) ); - painter->fillPath( roundedRect, painter->brush() ); - painter->restore(); - - QIcon icon = index.data( SipModel::FactoryItemIcon ).value< QIcon >(); - if( !icon.isNull() ) { - int rectW = 18; - int diff = ( ICONSIZE/ 2 ) - ( rectW / 2) ; - int pos = ( mid ) - ( rectW / 2 ); - QRect rect = QRect( CHECK_LEFT_EDGE + diff, pos + top, rectW, rectW ); - QPixmap p( icon.pixmap( rect.size() ) ); - painter->drawPixmap( rect, p ); - } - - // draw text - QFont f = opt.font; - f.setPointSize( f.pointSize() ); - f.setBold( true ); - QFontMetrics fm( f ); - QString text = index.data( Qt::DisplayRole ).toString(); - QRect textR = fm.boundingRect( text ); - textR.moveLeft( textLeftEdge ); - textR.moveTop( mid - ( textR.height() / 2 ) + top ); - textR.setRight( itemRect.right() ); - painter->setFont( f ); - painter->drawText( textR, text ); - } else { // this is an existing account to show - // draw checkbox first - int pos = ( mid ) - ( ICONSIZE / 2 ); - QRect checkRect = QRect( CHECK_LEFT_EDGE, pos + top, ICONSIZE, ICONSIZE ); - opt.rect = checkRect; - drawCheckBox( opt, painter, w ); - - // draw the icon if it exists - pos = ( mid ) - ( ICONSIZE / 2 ); - if( !index.data( Qt::DecorationRole ).value< QIcon >().isNull() ) { - QRect prect = QRect( iconLeftEdge, pos + top, ICONSIZE, ICONSIZE ); - - painter->save(); - painter->drawPixmap( prect, index.data( Qt::DecorationRole ).value< QIcon >().pixmap( prect.size() ) ); - painter->restore(); - } - - // from the right edge--config status and online/offline - QRect confRect = QRect( itemRect.width() - ICONSIZE - 2 * PADDING, mid - ICONSIZE / 2 + top, ICONSIZE, ICONSIZE ); - if( index.data( SipModel::HasConfig ).toBool() ) { - - QStyleOptionToolButton topt; - topt.rect = confRect; - topt.pos = confRect.topLeft(); - - drawConfigWrench( painter, opt, topt ); - } - - // draw the online/offline status - int statusIconSize = 18; - int statusX = confRect.left() - 2*PADDING - statusIconSize; - QFont statusF = opt.font; - statusF.setPointSize( statusF.pointSize() - 2 ); - QFontMetrics statusFM( statusF ); - - QPixmap p; - QString statusText; - SipPlugin::ConnectionState state = static_cast< SipPlugin::ConnectionState >( index.data( SipModel::ConnectionStateRole ).toInt() ); - if( state == SipPlugin::Connected ) { - p = QPixmap( RESPATH "images/sipplugin-online.png" ); - statusText = tr( "Online" ); - } else if( state == SipPlugin::Connecting ) { - p = QPixmap( RESPATH "images/sipplugin-offline.png" ); - statusText = tr( "Connecting..." ); - } else { - p = QPixmap( RESPATH "images/sipplugin-offline.png" ); - statusText = tr( "Offline" ); - } - p = p.scaled( statusIconSize, statusIconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ); - painter->drawPixmap( statusX, mid - statusIconSize / 2 + top, statusIconSize, statusIconSize, p ); - int width = statusFM.width( statusText ); - statusX = statusX - PADDING - width; - painter->save(); - painter->setFont( statusF ); - painter->drawText( QRect( statusX, mid - statusFM.height() / 2 + top, width, statusFM.height() ), statusText ); - painter->restore(); - - // name - painter->save(); - QFontMetrics namefm( name ); - int nameHeight = namefm.boundingRect( "test" ).height(); - // pos will the top-left point of the text rect - pos = mid - ( nameHeight / 2 ); - // TODO bound with config icon and offline/online status - width = itemRect.width() - textLeftEdge; - - if( !index.data( SipModel::ErrorString ).toString().isEmpty() ) { // error, show that too - QRect errorRect( textLeftEdge, mid + top, width, mid - PADDING + 1 ); - - QFontMetrics errorFm( error ); - QString str = errorFm.elidedText( index.data( SipModel::ErrorString ).toString(), Qt::ElideRight, errorRect.width() ); - painter->setFont( error ); - painter->drawText( errorRect, str ); - - pos = mid - errorRect.height() - 2; // move the name rect up - } - QString nameStr = namefm.elidedText( index.data( Qt::DisplayRole ).toString(), Qt::ElideRight, width ); - painter->setFont( name ); - painter->drawText( QRect( textLeftEdge, pos + top, width, nameHeight + 1 ), nameStr ); - painter->restore(); - } -} - -QRect -SipConfigDelegate::checkRectForIndex( const QStyleOptionViewItem &option, const QModelIndex &idx ) const -{ - if( !idx.data( SipModel::FactoryItemRole ).toBool() && !idx.data( SipModel::FactoryRole ).toBool() ) - { - QStyleOptionViewItemV4 opt = option; - initStyleOption( &opt, idx ); - int mid = opt.rect.height() / 2; - int pos = ( mid ) - ( ICONSIZE / 2 ); - QRect checkRect = QRect( CHECK_LEFT_EDGE, pos + opt.rect.top(), ICONSIZE, ICONSIZE ); - - return checkRect; - } - return QRect(); -} - -QRect -SipConfigDelegate::configRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const -{ - if( !idx.data( SipModel::FactoryItemRole ).toBool() && !idx.data( SipModel::FactoryRole ).toBool() ) - { - QStyleOptionViewItemV4 opt = option; - initStyleOption( &opt, idx ); - QRect itemRect = opt.rect; - QRect confRect = QRect( itemRect.width() - ICONSIZE - 2 * PADDING, (opt.rect.height() / 2) - ICONSIZE / 2 + opt.rect.top(), ICONSIZE, ICONSIZE ); - return confRect; - } - return QRect(); -} - - -QSize -SipConfigDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const -{ - if( index.data( SipModel::FactoryRole ).toBool() || index.data( SipModel::FactoryItemRole ).toBool() ) { // this is the "add new account" row - // enough space for one line of text - QStyleOptionViewItemV4 opt = option; - initStyleOption( &opt, index ); - int width = QStyledItemDelegate::sizeHint( option, index ).width(); - - QFont name = opt.font; - name.setPointSize( name.pointSize() + 1 ); - name.setBold( true ); - QFontMetrics sfm( name ); - return QSize( width, 3 * PADDING + sfm.height() ); - } else { // this is an existing account to show - return ConfigDelegateBase::sizeHint( option, index ); - } -} - -void -SipConfigDelegate::askedForEdit( const QModelIndex& idx ) -{ - emit openConfig( qobject_cast< SipPlugin* >( idx.data( SipModel::SipPluginData ).value< QObject* >() ) ); -} - - diff --git a/src/sipconfigdelegate.h b/src/sipconfigdelegate.h deleted file mode 100644 index cccc82a61..000000000 --- a/src/sipconfigdelegate.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright (C) 2011 Leo Franchi - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - - -#ifndef SIPCONFIGDELEGATE_H -#define SIPCONFIGDELEGATE_H - -#include "configdelegatebase.h" - -class SipPlugin; -class SipPluginFactory; -class SipConfigDelegate : public ConfigDelegateBase -{ - Q_OBJECT -public: - SipConfigDelegate( QObject* parent = 0); - - virtual void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; - virtual bool editorEvent ( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); - virtual QSize sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const; - - virtual QRect checkRectForIndex( const QStyleOptionViewItem &option, const QModelIndex &idx ) const; - virtual QRect configRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const; -private slots: - void askedForEdit( const QModelIndex& idx ); - -signals: - void sipFactoryClicked( SipPluginFactory* ); - void openConfig( SipPlugin* ); -}; - -#endif // SIPCONFIGDELEGATE_H diff --git a/src/stackedsettingsdialog.ui b/src/stackedsettingsdialog.ui index 704c60c40..191f09827 100644 --- a/src/stackedsettingsdialog.ui +++ b/src/stackedsettingsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 641 - 393 + 655 + 500 @@ -85,7 +85,7 @@ - 0 + 2 @@ -95,90 +95,49 @@ - Accounts + Internet Services - + 2 - - - - Connect to your friends with Google Chat, Twitter, and more. - - - - - - 0 - - - false - - - false - - - true - - - true - - - false + + + Install from file... - - - - - ... - - - - :/data/images/sipplugin-add.png:/data/images/sipplugin-add.png - - - QToolButton::InstantPopup - - - - - - - ... - - - - :/data/images/sipplugin-remove.png:/data/images/sipplugin-remove.png - - - QToolButton::DelayedPopup - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Filter by capability: + + + + + + + + @@ -265,220 +224,6 @@ - - - - 0 - - - - - Now Playing Information - - - - - - Applications to update with currently playing track: - - - - - - - - - - - Adium - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Qt::LeftToRight - - - Scrobble tracks to Last.fm - - - - - - - - - Username: - - - - - - - - - - Password: - - - - - - - QLineEdit::Password - - - - - - - - - Test Login - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - 0 - - - - - Script Resolvers - - - - 2 - - - - - Script resolvers search for a given track to make it playable. - - - - - - - Get more resolvers... - - - - - - - - - true - - - QAbstractItemView::SingleSelection - - - false - - - true - - - false - - - true - - - true - - - - - - - - - - - - - :/data/images/list-add.png:/data/images/list-add.png - - - - - - - - - - - :/data/images/list-remove.png:/data/images/list-remove.png - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - @@ -692,9 +437,7 @@
widgets/checkdirtree.h
- - - + buttonBox diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 145a55f8a..669a3383d 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -34,11 +34,11 @@ #include "album.h" #include "collection.h" #include "infosystem/infosystem.h" +#include "accounts/AccountManager.h" #include "database/database.h" #include "database/databasecollection.h" #include "database/databasecommand_collectionstats.h" #include "database/databaseresolver.h" -#include "sip/SipHandler.h" #include "playlist/dynamic/GeneratorFactory.h" #include "playlist/dynamic/echonest/EchonestGenerator.h" #include "playlist/dynamic/database/DatabaseGenerator.h" @@ -221,7 +221,11 @@ TomahawkApp::init() } tDebug() << "Init InfoSystem."; - m_infoSystem = QWeakPointer( new Tomahawk::InfoSystem::InfoSystem( this ) ); + m_infoSystem = QWeakPointer( Tomahawk::InfoSystem::InfoSystem::instance() ); + + tDebug() << "Init AccountManager."; + m_accountManager = QWeakPointer< Tomahawk::Accounts::AccountManager >( new Tomahawk::Accounts::AccountManager( this ) ); + Tomahawk::Accounts::AccountManager::instance()->loadFromConfig(); Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); #ifndef ENABLE_HEADLESS @@ -245,11 +249,9 @@ TomahawkApp::init() tDebug() << "Init Pipeline."; initPipeline(); -#ifdef LIBATTICA_FOUND #ifndef ENABLE_HEADLESS // load remote list of resolvers able to be installed AtticaManager::instance(); -#endif #endif if ( arguments().contains( "--http" ) || TomahawkSettings::instance()->value( "network/http", true ).toBool() ) @@ -307,13 +309,11 @@ TomahawkApp::~TomahawkApp() if ( !m_audioEngine.isNull() ) delete m_audioEngine.data(); - delete SipHandler::instance(); + delete Tomahawk::Accounts::AccountManager::instance(); #ifndef ENABLE_HEADLESS delete m_mainwindow; - #ifdef LIBATTICA_FOUND delete AtticaManager::instance(); - #endif #endif if ( !m_database.isNull() ) @@ -428,6 +428,7 @@ TomahawkApp::registerMetaTypes() qRegisterMetaType< Tomahawk::InfoSystem::InfoType >( "Tomahawk::InfoSystem::InfoType" ); qRegisterMetaType< Tomahawk::InfoSystem::InfoRequestData >( "Tomahawk::InfoSystem::InfoRequestData" ); qRegisterMetaType< Tomahawk::InfoSystem::InfoSystemCache* >( "Tomahawk::InfoSystem::InfoSystemCache*" ); + qRegisterMetaType< Tomahawk::InfoSystem::InfoPlugin* >( "Tomahawk::InfoSystem::InfoPlugin*" ); qRegisterMetaType< QList< Tomahawk::InfoSystem::InfoStringHash > >("QList< Tomahawk::InfoSystem::InfoStringHash > "); qRegisterMetaTypeStreamOperators< QList< Tomahawk::InfoSystem::InfoStringHash > >("QList< Tomahawk::InfoSystem::InfoStringHash > "); @@ -476,13 +477,6 @@ TomahawkApp::initPipeline() { // setup resolvers for local content, and (cached) remote collection content Pipeline::instance()->addResolver( new DatabaseResolver( 100 ) ); - // load script resolvers - QStringList enabled = TomahawkSettings::instance()->enabledScriptResolvers(); - foreach ( QString resolver, TomahawkSettings::instance()->allScriptResolvers() ) - { - const bool enable = enabled.contains( resolver ); - Pipeline::instance()->addScriptResolver( resolver, enable ); - } } @@ -526,6 +520,7 @@ TomahawkApp::initServent() void TomahawkApp::initSIP() { + tDebug() << Q_FUNC_INFO; //FIXME: jabber autoconnect is really more, now that there is sip -- should be renamed and/or split out of jabber-specific settings if ( !arguments().contains( "--nosip" ) ) { @@ -534,8 +529,7 @@ TomahawkApp::initSIP() #endif tDebug( LOGINFO ) << "Connecting SIP classes"; - //SipHandler::instance()->refreshProxy(); - SipHandler::instance()->loadFromConfig( true ); + Accounts::AccountManager::instance()->initSIP(); } m_loaded = true; diff --git a/src/tomahawkapp.h b/src/tomahawkapp.h index e97e359b8..dcfe6336d 100644 --- a/src/tomahawkapp.h +++ b/src/tomahawkapp.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -53,6 +54,11 @@ namespace Tomahawk { class InfoSystem; } + + namespace Accounts + { + class AccountManager; + } } #ifdef LIBLASTFM_FOUND @@ -123,6 +129,7 @@ private: QWeakPointer m_infoSystem; QWeakPointer m_xmppBot; QWeakPointer m_shortcutHandler; + QWeakPointer< Tomahawk::Accounts::AccountManager > m_accountManager; bool m_scrubFriendlyName; #ifdef LIBLASTFM_FOUND @@ -144,3 +151,7 @@ Q_DECLARE_METATYPE( PairList ) #endif // TOMAHAWKAPP_H + +struct A; + +struct A; diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 8c6d0c9fe..8d8c086b8 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -38,7 +38,7 @@ #include "query.h" #include "artist.h" #include "viewmanager.h" -#include "sip/SipHandler.h" +#include "accounts/AccountManager.h" #include "sourcetree/sourcetreeview.h" #include "network/servent.h" #include "utils/proxystyle.h" @@ -74,7 +74,7 @@ #include using namespace Tomahawk; - +using namespace Accounts; TomahawkWindow::TomahawkWindow( QWidget* parent ) : QMainWindow( parent ) @@ -115,7 +115,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) } // set initial state - onSipDisconnected(); + onAccountDisconnected(); vm->setQueue( m_queueView ); vm->showWelcomePage(); } @@ -310,7 +310,7 @@ TomahawkWindow::setupSignals() // connect( ui->actionAddPeerManually, SIGNAL( triggered() ), SLOT( addPeerManually() ) ); connect( ui->actionPreferences, SIGNAL( triggered() ), SLOT( showSettingsDialog() ) ); connect( ui->actionDiagnostics, SIGNAL( triggered() ), SLOT( showDiagnosticsDialog() ) ); - connect( ui->actionToggleConnect, SIGNAL( triggered() ), SipHandler::instance(), SLOT( toggleConnect() ) ); + connect( ui->actionToggleConnect, SIGNAL( triggered() ), AccountManager::instance(), SLOT( toggleAccountsConnected() ) ); connect( ui->actionUpdateCollection, SIGNAL( triggered() ), SLOT( updateCollectionManually() ) ); connect( ui->actionRescanCollection, SIGNAL( triggered() ), SLOT( rescanCollectionManually() ) ); connect( ui->actionLoadXSPF, SIGNAL( triggered() ), SLOT( loadSpiff() )); @@ -332,18 +332,20 @@ TomahawkWindow::setupSignals() ui->menuWindow->menuAction()->setVisible( false ); #endif - // - connect( SipHandler::instance(), SIGNAL( connected( SipPlugin* ) ), SLOT( onSipConnected() ) ); - connect( SipHandler::instance(), SIGNAL( disconnected( SipPlugin* ) ), SLOT( onSipDisconnected() ) ); - connect( SipHandler::instance(), SIGNAL( authError( SipPlugin* ) ), SLOT( onSipError() ) ); + // + connect( AccountManager::instance(), SIGNAL( connected( Tomahawk::Accounts::Account* ) ), SLOT( onAccountConnected() ) ); + connect( AccountManager::instance(), SIGNAL( disconnected( Tomahawk::Accounts::Account* ) ), SLOT( onAccountDisconnected() ) ); + connect( AccountManager::instance(), SIGNAL( authError( Tomahawk::Accounts::Account* ) ), SLOT( onAccountError() ) ); - // - connect( SipHandler::instance(), SIGNAL( pluginAdded( SipPlugin* ) ), this, SLOT( onSipPluginAdded( SipPlugin* ) ) ); - connect( SipHandler::instance(), SIGNAL( pluginRemoved( SipPlugin* ) ), this, SLOT( onSipPluginRemoved( SipPlugin* ) ) ); - foreach( SipPlugin *plugin, SipHandler::instance()->allPlugins() ) + // Menus for accounts that support them + connect( AccountManager::instance(), SIGNAL( added( Tomahawk::Accounts::Account* ) ), this, SLOT( onAccountAdded( Tomahawk::Accounts::Account* ) ) ); + foreach( Account* account, AccountManager::instance()->accounts( Tomahawk::Accounts::SipType ) ) { - connect( plugin, SIGNAL( addMenu( QMenu* ) ), this, SLOT( pluginMenuAdded( QMenu* ) ) ); - connect( plugin, SIGNAL( removeMenu( QMenu* ) ), this, SLOT( pluginMenuRemoved( QMenu* ) ) ); + if ( !account || !account->sipPlugin() ) + continue; + + connect( account->sipPlugin(), SIGNAL( addMenu( QMenu* ) ), this, SLOT( pluginMenuAdded( QMenu* ) ) ); + connect( account->sipPlugin(), SIGNAL( removeMenu( QMenu* ) ), this, SLOT( pluginMenuRemoved( QMenu* ) ) ); } } @@ -683,39 +685,36 @@ TomahawkWindow::onPlaybackLoading( const Tomahawk::result_ptr& result ) void -TomahawkWindow::onSipConnected() +TomahawkWindow::onAccountConnected() { ui->actionToggleConnect->setText( tr( "Go &offline" ) ); } void -TomahawkWindow::onSipDisconnected() +TomahawkWindow::onAccountDisconnected() { ui->actionToggleConnect->setText( tr( "Go &online" ) ); } void -TomahawkWindow::onSipPluginAdded( SipPlugin* p ) +TomahawkWindow::onAccountAdded( Account* acc ) { - connect( p, SIGNAL( addMenu( QMenu* ) ), SLOT( pluginMenuAdded( QMenu* ) ) ); - connect( p, SIGNAL( removeMenu( QMenu* ) ), SLOT( pluginMenuRemoved( QMenu* ) ) ); + if ( !acc->types() & SipType || !acc->sipPlugin() ) + return; + + connect( acc->sipPlugin(), SIGNAL( addMenu( QMenu* ) ), this, SLOT( pluginMenuAdded( QMenu* ) ) ); + connect( acc->sipPlugin(), SIGNAL( removeMenu( QMenu* ) ), this, SLOT( pluginMenuRemoved( QMenu* ) ) ); } - void -TomahawkWindow::onSipPluginRemoved( SipPlugin* p ) +TomahawkWindow::onAccountError() { - Q_UNUSED( p ); -} - - -void -TomahawkWindow::onSipError() -{ - onSipDisconnected(); + // TODO fix. +// onAccountDisconnected(); + // TODO real error message from plugin kthxbbq QMessageBox::warning( this, tr( "Authentication Error" ), QString( "Error connecting to SIP: Authentication failed!" ), diff --git a/src/tomahawkwindow.h b/src/tomahawkwindow.h index cc261453c..adc3d98f0 100644 --- a/src/tomahawkwindow.h +++ b/src/tomahawkwindow.h @@ -30,9 +30,14 @@ #include "audio/audioengine.h" #include "utils/xspfloader.h" +namespace Tomahawk { +namespace Accounts { + class Account; +} +} + class JobStatusModel; class QSearchField; -class SipPlugin; class SourceTreeView; class QAction; @@ -82,9 +87,10 @@ public slots: void showOfflineSources(); private slots: - void onSipConnected(); - void onSipDisconnected(); - void onSipError(); + void onAccountAdded( Tomahawk::Accounts::Account* account ); + void onAccountConnected(); + void onAccountDisconnected(); + void onAccountError(); void onAudioEngineError( AudioEngine::AudioErrorCode error ); @@ -101,9 +107,6 @@ private slots: void showAboutTomahawk(); void checkForUpdates(); - void onSipPluginAdded( SipPlugin* p ); - void onSipPluginRemoved( SipPlugin* p ); - void onSearch( const QString& search ); void onFilterEdited();