diff --git a/ChangeLog b/ChangeLog index f7f941008..bc542c1b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,6 @@ Version 0.5.0: + * Initial Access Control support. Entries can be cleared en masse in the Advanced area of Settings. + * Priortize resolution of a track on double-click. * Spotify Resolver can now be easily installed on-demand from the settings. * You can now sync selected playlists (and updates) with Spotify. * Support .aiff (AIFF mimetype) files. diff --git a/src/SettingsDialog.cpp b/src/SettingsDialog.cpp index 54fab5fa5..f8d3860f9 100644 --- a/src/SettingsDialog.cpp +++ b/src/SettingsDialog.cpp @@ -30,6 +30,7 @@ #include #include "AtticaManager.h" +#include "AclRegistry.h" #include "TomahawkApp.h" #include "TomahawkSettings.h" #include "accounts/DelegateConfigWrapper.h" @@ -98,6 +99,8 @@ SettingsDialog::SettingsDialog( QWidget *parent ) ui->enableProxyCheckBox->setChecked( useProxy ); ui->proxyButton->setEnabled( useProxy ); + ui->aclEntryClearButton->setEnabled( TomahawkSettings::instance()->aclEntries().size() > 0 ); + connect( ui->aclEntryClearButton, SIGNAL( clicked( bool ) ), this, SLOT( aclEntryClearButtonClicked() ) ); createIcons(); #ifdef Q_WS_X11 @@ -497,6 +500,24 @@ SettingsDialog::installFromFile() } +void +SettingsDialog::aclEntryClearButtonClicked() +{ + QMessageBox::StandardButton button = QMessageBox::question( + ui->stackedWidget, + tr( "Delete all Access Control entries?" ), + tr( "Do you really want to delete all Access Control entries? You will be be asked for a decision again for each peer that you connect to." ), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok + ); + if ( button == QMessageBox::Ok ) + { + ACLRegistry::instance()->wipeEntries(); + ui->aclEntryClearButton->setEnabled( false ); + } +} + + void SettingsDialog::scrollTo( const QModelIndex& idx ) { diff --git a/src/SettingsDialog.h b/src/SettingsDialog.h index cbd8eb724..487f6e4ae 100644 --- a/src/SettingsDialog.h +++ b/src/SettingsDialog.h @@ -97,6 +97,8 @@ private slots: void changePage( QListWidgetItem*, QListWidgetItem* ); void serventReady(); + void aclEntryClearButtonClicked(); + void requiresRestart(); private: diff --git a/src/StackedSettingsDialog.ui b/src/StackedSettingsDialog.ui index 9c1d9ef37..08dcdf48b 100644 --- a/src/StackedSettingsDialog.ui +++ b/src/StackedSettingsDialog.ui @@ -418,6 +418,30 @@ + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Clear All Access Control Entries + + + + + diff --git a/src/accounts/xmpp/sip/XmppSip.cpp b/src/accounts/xmpp/sip/XmppSip.cpp index 2c202f589..7375bb041 100644 --- a/src/accounts/xmpp/sip/XmppSip.cpp +++ b/src/accounts/xmpp/sip/XmppSip.cpp @@ -799,7 +799,7 @@ XmppSipPlugin::onSubscriptionReceived( const Jreen::RosterItem::Ptr& item, const QMessageBox *confirmBox = new QMessageBox( QMessageBox::Question, tr( "Authorize User" ), - QString( tr( "Do you want to grant %1 access to your Collection?" ) ).arg( presence.from().bare() ), + QString( tr( "Do you want to add %1 to your friend list?" ) ).arg( presence.from().bare() ), QMessageBox::Yes | QMessageBox::No, TomahawkUtils::tomahawkWindow() ); diff --git a/src/libtomahawk/AclRegistry.cpp b/src/libtomahawk/AclRegistry.cpp index 56653187f..ef99879fb 100644 --- a/src/libtomahawk/AclRegistry.cpp +++ b/src/libtomahawk/AclRegistry.cpp @@ -35,6 +35,51 @@ #include "utils/Logger.h" +QDataStream& operator<<( QDataStream &out, const ACLRegistry::User &user ) +{ + out << ACLUSERVERSION; + out << user.uuid; + out << user.knownDbids.length(); + foreach( QString knownDbid, user.knownDbids ) + out << knownDbid; + out << user.knownAccountIds.length(); + foreach( QString knownAccount, user.knownAccountIds ) + out << knownAccount; + out << (int)( user.acl ); + return out; +} + +QDataStream& operator>>( QDataStream &in, ACLRegistry::User &user ) +{ + int ver; + in >> ver; + if ( ver == ACLUSERVERSION ) + { + in >> user.uuid; + int dbidsLength; + in >> dbidsLength; + QString knownDbid; + for ( int i = 0; i < dbidsLength; i++ ) + { + in >> knownDbid; + user.knownDbids << knownDbid; + } + int accountsLength; + in >> accountsLength; + QString knownAccountId; + for ( int i = 0; i < accountsLength; i++ ) + { + in >> knownAccountId; + user.knownAccountIds << knownAccountId; + } + int aclIn; + in >> aclIn; + user.acl = (ACLRegistry::ACL)( aclIn ); + } + return in; +} + + ACLRegistry* ACLRegistry::s_instance = 0; ACLRegistry* @@ -53,7 +98,7 @@ ACLRegistry::ACLRegistry( QObject* parent ) s_instance = this; qRegisterMetaType< ACLRegistry::ACL >( "ACLRegistry::ACL" ); qRegisterMetaType< ACLRegistry::User >( "ACLRegistry::User" ); - + qRegisterMetaTypeStreamOperators< ACLRegistry::User >( "ACLRegistry::User" ); load(); } @@ -66,17 +111,17 @@ ACLRegistry::~ACLRegistry() ACLRegistry::ACL ACLRegistry::isAuthorizedUser( const QString& dbid, const QString &username, ACLRegistry::ACL globalType, bool skipEmission ) { + tLog() << Q_FUNC_INFO; if ( QThread::currentThread() != TOMAHAWK_APPLICATION::instance()->thread() ) { if ( !skipEmission ) QMetaObject::invokeMethod( this, "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( const QString&, dbid ), Q_ARG( const QString &, username ), Q_ARG( ACLRegistry::ACL, globalType ), Q_ARG( bool, skipEmission ) ); return ACLRegistry::NotFound; } - + tLog() << Q_FUNC_INFO << "in right thread"; //FIXME: Remove when things are working - emit aclResult( dbid, username, ACLRegistry::Stream ); - return ACLRegistry::NotFound; - +// emit aclResult( dbid, username, ACLRegistry::Stream ); +// return ACLRegistry::NotFound; bool found = false; QMutableListIterator< ACLRegistry::User > i( m_cache ); while ( i.hasNext() ) @@ -139,28 +184,37 @@ ACLRegistry::isAuthorizedUser( const QString& dbid, const QString &username, ACL void ACLRegistry::getUserDecision( ACLRegistry::User user, const QString &username ) { + tLog() << Q_FUNC_INFO; AclJobItem* job = new AclJobItem( user, username ); m_jobQueue.enqueue( job ); - queueNextJob(); + QTimer::singleShot( 0, this, SLOT( queueNextJob() ) ); } void ACLRegistry::userDecision( ACLRegistry::User user ) { + tLog() << Q_FUNC_INFO; m_cache.append( user ); save(); emit aclResult( user.knownDbids.first(), user.knownAccountIds.first(), user.acl ); m_jobCount--; if ( !m_jobQueue.isEmpty() ) - queueNextJob(); + QTimer::singleShot( 0, this, SLOT( queueNextJob() ) ); } void ACLRegistry::queueNextJob() { + if ( QThread::currentThread() != TOMAHAWK_APPLICATION::instance()->thread() ) + { + QMetaObject::invokeMethod( this, "queueNextJob", Qt::QueuedConnection ); + return; + } + tLog() << Q_FUNC_INFO << "jobCount = " << m_jobCount; + tLog() << Q_FUNC_INFO << "jobQueue size = " << m_jobQueue.length(); if ( m_jobCount != 0 ) return; @@ -174,18 +228,21 @@ ACLRegistry::queueNextJob() ACLRegistry::ACL acl = isAuthorizedUser( dbid, job->username(), ACLRegistry::NotFound, true ); if ( acl != ACLRegistry::NotFound ) { + tLog() << Q_FUNC_INFO << "Found existing acl entry for = " << user.knownAccountIds.first(); found = true; break; } } if ( found ) { + tLog() << Q_FUNC_INFO << "deleting job, already have ACL for " << user.knownAccountIds.first(); delete job; QTimer::singleShot( 0, this, SLOT( queueNextJob() ) ); return; } else { + tLog() << Q_FUNC_INFO << "activating job for user" << user.knownAccountIds.first(); m_jobCount++; JobStatusView::instance()->model()->addJob( job ); connect( job, SIGNAL( userDecision( ACLRegistry::User ) ), this, SLOT( userDecision( ACLRegistry::User ) ) ); @@ -199,12 +256,22 @@ ACLRegistry::queueNextJob() void ACLRegistry::load() { + tLog() << Q_FUNC_INFO; QVariantList entryList = TomahawkSettings::instance()->aclEntries(); foreach ( QVariant entry, entryList ) { if ( !entry.isValid() || !entry.canConvert< ACLRegistry::User >() ) + { + tLog() << Q_FUNC_INFO << "entry is invalid"; continue; + } + tLog() << Q_FUNC_INFO << "loading entry"; ACLRegistry::User entryUser = entry.value< ACLRegistry::User >(); + if ( entryUser.knownAccountIds.empty() || entryUser.knownDbids.empty() ) + { + tLog() << Q_FUNC_INFO << "user known account/dbids is empty"; + continue; + } m_cache.append( entryUser ); } } @@ -213,8 +280,23 @@ ACLRegistry::load() void ACLRegistry::save() { + tLog() << Q_FUNC_INFO; QVariantList entryList; foreach ( ACLRegistry::User user, m_cache ) - entryList.append( QVariant::fromValue< ACLRegistry::User >( user ) ); + { + tLog() << Q_FUNC_INFO << "user is " << user.uuid << " with known name " << user.knownAccountIds.first(); + QVariant val = QVariant::fromValue< ACLRegistry::User >( user ); + if ( val.isValid() ) + entryList.append( val ); + } TomahawkSettings::instance()->setAclEntries( entryList ); -} \ No newline at end of file +} + + +void +ACLRegistry::wipeEntries() +{ + tLog() << Q_FUNC_INFO; + m_cache.clear(); + save(); +} diff --git a/src/libtomahawk/AclRegistry.h b/src/libtomahawk/AclRegistry.h index 5405635c7..f2ee66997 100644 --- a/src/libtomahawk/AclRegistry.h +++ b/src/libtomahawk/AclRegistry.h @@ -33,6 +33,8 @@ #include "HeadlessCheck.h" #include "DllMacro.h" +#define ACLUSERVERSION 1 + class AclJobItem; class DLLEXPORT ACLRegistry : public QObject @@ -54,19 +56,31 @@ public: QString uuid; QStringList knownDbids; QStringList knownAccountIds; - ACL acl; + ACLRegistry::ACL acl; User() : uuid( QUuid::createUuid().toString() ) + , knownDbids() + , knownAccountIds() , acl( ACLRegistry::NotFound ) {} - User( QString p_uuid, QStringList p_knownDbids, QStringList p_knownAccountIds, ACL p_acl ) + ~User() + {} + + User( QString p_uuid, QStringList p_knownDbids, QStringList p_knownAccountIds, ACLRegistry::ACL p_acl ) : uuid( p_uuid ) , knownDbids( p_knownDbids ) , knownAccountIds( p_knownAccountIds ) , acl( p_acl ) {} + + User( const User &other ) + : uuid( other.uuid ) + , knownDbids( other.knownDbids ) + , knownAccountIds( other.knownAccountIds ) + , acl( other.acl ) + {} }; ACLRegistry( QObject *parent = 0 ); @@ -85,10 +99,11 @@ public slots: * @return ACLRegistry::ACL **/ ACLRegistry::ACL isAuthorizedUser( const QString &dbid, const QString &username, ACLRegistry::ACL globalType = ACLRegistry::NotFound, bool skipEmission = false ); - + void wipeEntries(); + #ifndef ENABLE_HEADLESS void getUserDecision( ACLRegistry::User user, const QString &username ); - + private slots: void userDecision( ACLRegistry::User user ); void queueNextJob(); diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index 654a7e2b2..c51f5acff 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -839,7 +839,10 @@ TomahawkSettings::aclEntries() const void TomahawkSettings::setAclEntries( const QVariantList &entries ) { + tDebug() << "Setting entries"; setValue( "acl/entries", entries ); + sync(); + tDebug() << "Done setting entries"; } diff --git a/src/libtomahawk/accounts/AccountDelegate.cpp b/src/libtomahawk/accounts/AccountDelegate.cpp index 8b77aabca..8996b9aa9 100644 --- a/src/libtomahawk/accounts/AccountDelegate.cpp +++ b/src/libtomahawk/accounts/AccountDelegate.cpp @@ -27,6 +27,7 @@ #include "accounts/AccountManager.h" #include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" #include "utils/AnimatedSpinner.h" #include "utils/Closure.h" @@ -549,53 +550,10 @@ AccountDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QS 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) ); - } + TomahawkUtils::drawRoundedButton( painter, btnRect, QColor(54, 127, 211), QColor(43, 104, 182), QColor(34, 85, 159), QColor(35, 79, 147) ); 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 ); + TomahawkUtils::drawRoundedButton( painter, btnRect, QColor(206, 63, 63), QColor(170, 52, 52), QColor(150, 50, 50), QColor(130, 40, 40) ); } diff --git a/src/libtomahawk/jobview/AclJobItem.cpp b/src/libtomahawk/jobview/AclJobItem.cpp index 3d086c99e..7d94bf9fa 100644 --- a/src/libtomahawk/jobview/AclJobItem.cpp +++ b/src/libtomahawk/jobview/AclJobItem.cpp @@ -21,31 +21,39 @@ #include "JobStatusModel.h" #include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" +#include "libtomahawk/infosystem/InfoSystem.h" #include "utils/Logger.h" #include #include #include #include +#include -#define ROW_HEIGHT 40 +#define ROW_HEIGHT 20 #define ICON_PADDING 1 #define PADDING 2 AclJobDelegate::AclJobDelegate( QObject* parent ) : QStyledItemDelegate ( parent ) - , m_parentView( qobject_cast< QListView* >( parent ) ) { - Q_ASSERT( m_parentView ); + tLog() << Q_FUNC_INFO; +} + + +AclJobDelegate::~AclJobDelegate() +{ + tLog() << Q_FUNC_INFO; } void AclJobDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO; + //tDebug( LOGVERBOSE ) << Q_FUNC_INFO; QStyleOptionViewItemV4 opt = option; initStyleOption( &opt, index ); QFontMetrics fm( opt.font ); @@ -55,96 +63,91 @@ AclJobDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co painter->setRenderHint( QPainter::Antialiasing ); + painter->fillRect( opt.rect, Qt::lightGray ); + QString mainText; AclJobItem* item = dynamic_cast< AclJobItem* >( index.data( JobStatusModel::JobDataRole ).value< JobStatusItem* >() ); if ( !item ) mainText = tr( "Error displaying ACL info" ); else mainText = QString( tr( "Allow %1 to\nconnect and stream from you?" ) ).arg( item->username() ); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Displaying text:" << mainText; + //tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Displaying text:" << mainText; + + const QRect rRect( opt.rect.left() + PADDING, opt.rect.top() + 4*PADDING, opt.rect.width() - 2*PADDING, opt.rect.height() - 2*PADDING ); + painter->drawText( rRect, Qt::AlignHCenter, mainText ); - const QString text = QString( tr( "Allow %1 to\nconnect and stream from you?" ) ).arg( item->username() ); - const int w = fm.width( text ); - Q_UNUSED( w ); // looks obsolete - const QRect rRect( opt.rect.left() + PADDING, ROW_HEIGHT + PADDING, opt.rect.width() - 2*PADDING, opt.rect.height() - 2*PADDING ); - painter->drawText( rRect, Qt::AlignCenter, text ); + //tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Using rect " << rRect << ", opt rect is " << opt.rect; + int totalwidth = opt.rect.width(); + int thirds = totalwidth/3; + QRect btnRect; + painter->setPen( Qt::white ); + + QString btnText = tr( "Allow Streaming" ); + int btnWidth = fm.width( btnText ) + 2*PADDING; + btnRect = QRect( opt.rect.left() + thirds - btnWidth/2, opt.rect.bottom() - fm.height() - 4*PADDING, btnWidth + 2*PADDING, fm.height() + 2*PADDING ); + drawRoundedButton( painter, btnRect, btnRect.contains( m_savedHoverPos ) ); + painter->drawText( btnRect, Qt::AlignCenter, btnText ); + m_savedAcceptRect = btnRect; -/* - QStyleOptionViewItemV4 opt = option; - initStyleOption( &opt, index ); - QFontMetrics fm( opt.font ); - const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool(); - - opt.state &= ~QStyle::State_MouseOver; - QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget ); - -// painter->drawLine( opt.rect.topLeft(), opt.rect.topRight() ); - - painter->setRenderHint( QPainter::Antialiasing ); - QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING ); - if ( allowMultiLine ) - iconRect.moveTop( opt.rect.top() + opt.rect.height() / 2 - iconRect.height() / 2); - QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >(); - p = p.scaledToHeight( iconRect.height(), Qt::SmoothTransformation ); - painter->drawPixmap( iconRect, p ); - - // draw right column if there is one - const QString rCol = index.data( JobStatusModel::RightColumnRole ).toString(); - int rightEdge = opt.rect.right(); - if ( !rCol.isEmpty() ) - { - const int w = fm.width( rCol ); - const QRect rRect( opt.rect.right() - PADDING - w, PADDING + opt.rect.y(), w, opt.rect.height() - 2*PADDING ); - painter->drawText( rRect, Qt::AlignCenter, rCol ); - - rightEdge = rRect.left(); - } - - QString mainText; - AclJobItem* item = dynamic_cast< AclJobItem* >( index.data( JobStatusModel::JobDataRole ).value< JobStatusItem* >() ); - //QString mainText = index.data( Qt::DisplayRole ).toString(); - if ( !item ) - mainText = tr( "Error displaying ACL info" ); - else - mainText = QString( tr( "Allow %1 to\nconnect and stream from you?" ) ).arg( item->username() ); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Displaying text:" << mainText; - const int mainW = rightEdge - 3*PADDING - iconRect.right(); - QTextOption to( Qt::AlignLeft | Qt::AlignVCenter ); - if ( !allowMultiLine ) - mainText = fm.elidedText( mainText, Qt::ElideRight, mainW ); - else - to.setWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere ); - painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), mainText, to ); - */ + btnText = tr( "Deny Access" ); + btnWidth = fm.width( btnText ) + 2*PADDING; + btnRect = QRect( opt.rect.right() - thirds - btnWidth/2, opt.rect.bottom() - fm.height() - 4*PADDING, btnWidth + 2*PADDING, fm.height() + 2*PADDING ); + drawRoundedButton( painter, btnRect, btnRect.contains( m_savedHoverPos ) ); + painter->drawText( btnRect, Qt::AlignCenter, btnText ); + m_savedDenyRect = btnRect; } QSize AclJobDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO; - return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), ROW_HEIGHT ); + QSize size( QStyledItemDelegate::sizeHint ( option, index ).width(), ROW_HEIGHT * 3 ); + return size; +} - /* - const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool(); - if ( !allowMultiLine ) - return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), ROW_HEIGHT ); - else if ( m_cachedMultiLineHeights.contains( index ) ) - return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), m_cachedMultiLineHeights[ index ] ); +void +AclJobDelegate::drawRoundedButton( QPainter* painter, const QRect& btnRect, bool red ) const +{ + if ( !red ) + TomahawkUtils::drawRoundedButton( painter, btnRect, QColor(54, 127, 211), QColor(43, 104, 182), QColor(34, 85, 159), QColor(35, 79, 147) ); + else + TomahawkUtils::drawRoundedButton( painter, btnRect, QColor(206, 63, 63), QColor(170, 52, 52), QColor(150, 50, 50), QColor(130, 40, 40) ); +} - // Don't elide, but stretch across as many rows as required - QStyleOptionViewItemV4 opt = option; - initStyleOption( &opt, index ); - const QString text = index.data( Qt::DisplayRole ).toString(); - const int leftEdge = ICON_PADDING + ROW_HEIGHT + 2*PADDING; - const QRect rect = opt.fontMetrics.boundingRect( leftEdge, opt.rect.top(), m_parentView->width() - leftEdge, 200, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, text ); +bool +AclJobDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) +{ + Q_UNUSED( option ) + Q_UNUSED( model ) + //tDebug( LOGVERBOSE ) << Q_FUNC_INFO; + if ( event->type() != QEvent::MouseButtonPress && + event->type() != QEvent::MouseButtonRelease && + event->type() != QEvent::MouseButtonDblClick && + event->type() != QEvent::MouseMove ) + return false; - m_cachedMultiLineHeights.insert( index, rect.height() + 4*PADDING ); + if ( event->type() == QEvent::MouseMove ) + { + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + m_savedHoverPos = me->pos(); + //tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Setting position to " << m_savedHoverPos; + emit update( index ); + return true; + } - return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), rect.height() + 4*PADDING ); - */ + if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick ) + { + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + if ( m_savedAcceptRect.contains( me->pos() ) ) + emit aclResult( ACLRegistry::Stream ); + else if ( m_savedDenyRect.contains( me->pos() ) ) + emit aclResult( ACLRegistry::Deny ); + return true; + } + + return false; } @@ -154,27 +157,44 @@ AclJobItem::AclJobItem( ACLRegistry::User user, const QString &username ) , m_user( user ) , m_username( username ) { + tLog() << Q_FUNC_INFO; } AclJobItem::~AclJobItem() { + tLog() << Q_FUNC_INFO; } void AclJobItem::createDelegate( QObject* parent ) { + tLog() << Q_FUNC_INFO; + if ( m_delegate ) return; m_delegate = new AclJobDelegate( parent ); + + Tomahawk::InfoSystem::InfoPushData pushData( "AclJobItem", Tomahawk::InfoSystem::InfoNotifyUser, tr( "Tomahawk needs you to decide whether %1 is allowed to connect." ).arg( m_username ), Tomahawk::InfoSystem::PushNoFlag ); + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData ); } void -AclJobItem::done() +AclJobDelegate::emitSizeHintChanged( const QModelIndex& index ) { + emit sizeHintChanged( index ); +} + + +void +AclJobItem::aclResult( ACLRegistry::ACL result ) +{ + tLog() << Q_FUNC_INFO; + m_user.acl = result; + emit userDecision( m_user ); emit finished(); } diff --git a/src/libtomahawk/jobview/AclJobItem.h b/src/libtomahawk/jobview/AclJobItem.h index fd5377ca1..18c90bcba 100644 --- a/src/libtomahawk/jobview/AclJobItem.h +++ b/src/libtomahawk/jobview/AclJobItem.h @@ -33,14 +33,26 @@ class AclJobDelegate : public QStyledItemDelegate public: explicit AclJobDelegate ( QObject* parent = 0 ); - virtual ~AclJobDelegate() {} + virtual ~AclJobDelegate(); virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + virtual void emitSizeHintChanged( const QModelIndex &index ); + +signals: + void update( const QModelIndex& idx ); + void aclResult( ACLRegistry::ACL result ); + +protected: + virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); + private: - mutable QHash< QPersistentModelIndex, int > m_cachedMultiLineHeights; - QListView* m_parentView; + void drawRoundedButton( QPainter* painter, const QRect& btnRect, bool red = false ) const; + + QPoint m_savedHoverPos; + mutable QRect m_savedAcceptRect; + mutable QRect m_savedDenyRect; }; @@ -51,8 +63,6 @@ public: explicit AclJobItem( ACLRegistry::User user, const QString &username ); virtual ~AclJobItem(); - void done(); - virtual QString rightColumnText() const { return QString(); } virtual QString mainText() const { return QString(); } virtual QPixmap icon() const { return QPixmap(); } @@ -61,7 +71,7 @@ public: virtual int concurrentJobLimit() const { return 3; } virtual bool hasCustomDelegate() const { return true; } - virtual void createDelegate( QObject* parent ); + virtual void createDelegate( QObject* parent = 0 ); virtual QStyledItemDelegate* customDelegate() const { return m_delegate; } virtual ACLRegistry::User user() const { return m_user; } @@ -69,6 +79,9 @@ public: signals: void userDecision( ACLRegistry::User user ); + +public slots: + void aclResult( ACLRegistry::ACL result ); private: QStyledItemDelegate* m_delegate; diff --git a/src/libtomahawk/jobview/JobStatusDelegate.cpp b/src/libtomahawk/jobview/JobStatusDelegate.cpp index cf07b6b92..e78e53837 100644 --- a/src/libtomahawk/jobview/JobStatusDelegate.cpp +++ b/src/libtomahawk/jobview/JobStatusDelegate.cpp @@ -59,8 +59,11 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, if ( allowMultiLine ) iconRect.moveTop( opt.rect.top() + opt.rect.height() / 2 - iconRect.height() / 2); QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >(); - p = p.scaledToHeight( iconRect.height(), Qt::SmoothTransformation ); - painter->drawPixmap( iconRect, p ); + if ( !p.isNull() ) + { + p = p.scaledToHeight( iconRect.height(), Qt::SmoothTransformation ); + painter->drawPixmap( iconRect, p ); + } // draw right column if there is one const QString rCol = index.data( JobStatusModel::RightColumnRole ).toString(); diff --git a/src/libtomahawk/jobview/JobStatusModel.cpp b/src/libtomahawk/jobview/JobStatusModel.cpp index e3ec86e9a..08df1b614 100644 --- a/src/libtomahawk/jobview/JobStatusModel.cpp +++ b/src/libtomahawk/jobview/JobStatusModel.cpp @@ -41,6 +41,7 @@ JobStatusModel::~JobStatusModel() void JobStatusModel::addJob( JobStatusItem* item ) { + tLog() << Q_FUNC_INFO << "current jobs of item type: " << m_jobTypeCount[ item->type() ] << ", current queue size of item type: " << m_jobQueue[ item->type() ].size(); if ( item->concurrentJobLimit() > 0 ) { if ( m_jobTypeCount[ item->type() ] >= item->concurrentJobLimit() ) @@ -53,6 +54,8 @@ JobStatusModel::addJob( JobStatusItem* item ) m_jobTypeCount[ item->type() ] = currentJobCount; } + tLog() << Q_FUNC_INFO << "new current jobs of item type: " << m_jobTypeCount[ item->type() ]; + connect( item, SIGNAL( statusChanged() ), SLOT( itemUpdated() ) ); connect( item, SIGNAL( finished() ), SLOT( itemFinished() ) ); @@ -70,7 +73,7 @@ JobStatusModel::addJob( JobStatusItem* item ) } } - qDebug() << "Adding item:" << item; + tLog() << Q_FUNC_INFO << "Adding item:" << item; int currentEndRow = m_items.count(); beginInsertRows( QModelIndex(), currentEndRow, currentEndRow ); @@ -79,7 +82,7 @@ JobStatusModel::addJob( JobStatusItem* item ) if ( item->hasCustomDelegate() ) { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "job has custom delegate"; + tLog() << Q_FUNC_INFO << "job has custom delegate"; emit customDelegateJobInserted( currentEndRow, item ); } } @@ -184,6 +187,7 @@ JobStatusModel::itemFinished() // One less to count, but item is still there const QModelIndex idx = index( indexOf, 0, QModelIndex() ); emit dataChanged( idx, idx ); + emit refreshDelegates(); return; } } @@ -200,6 +204,9 @@ JobStatusModel::itemFinished() if ( item->customDelegate() ) emit customDelegateJobRemoved( idx ); + emit refreshDelegates(); + + tLog() << Q_FUNC_INFO << "current jobs of item type: " << m_jobTypeCount[ item->type() ] << ", current queue size of item type: " << m_jobQueue[ item->type() ].size(); if ( item->concurrentJobLimit() > 0 ) { int currentJobs = m_jobTypeCount[ item->type() ]; diff --git a/src/libtomahawk/jobview/JobStatusModel.h b/src/libtomahawk/jobview/JobStatusModel.h index ff4d19b83..7db63267f 100644 --- a/src/libtomahawk/jobview/JobStatusModel.h +++ b/src/libtomahawk/jobview/JobStatusModel.h @@ -49,6 +49,7 @@ public: signals: void customDelegateJobInserted( int row, JobStatusItem* item ); void customDelegateJobRemoved( int row ); + void refreshDelegates(); public slots: /// Takes ownership of job diff --git a/src/libtomahawk/jobview/JobStatusView.cpp b/src/libtomahawk/jobview/JobStatusView.cpp index 59f823bf1..8b29013fe 100644 --- a/src/libtomahawk/jobview/JobStatusView.cpp +++ b/src/libtomahawk/jobview/JobStatusView.cpp @@ -20,6 +20,7 @@ #include "JobStatusView.h" #include "Pipeline.h" +#include "AclJobItem.h" #include "JobStatusModel.h" #include "JobStatusItem.h" #include "JobStatusDelegate.h" @@ -75,6 +76,9 @@ JobStatusView::JobStatusView( AnimatedSplitter* parent ) new PipelineStatusManager( this ); new TransferStatusManager( this ); new LatchedStatusManager( this ); + + setMouseTracking( true ); + m_view->setMouseTracking( true ); } @@ -90,27 +94,66 @@ JobStatusView::setModel( JobStatusModel* m ) connect( m_view->model(), SIGNAL( modelReset() ), this, SLOT( checkCount() ) ); connect( m_view->model(), SIGNAL( customDelegateJobInserted( int, JobStatusItem* ) ), this, SLOT( customDelegateJobInserted( int, JobStatusItem* ) ) ); connect( m_view->model(), SIGNAL( customDelegateJobRemoved( int ) ), this, SLOT( customDelegateJobRemoved( int ) ) ); + connect( m_view->model(), SIGNAL( refreshDelegates() ), this, SLOT( refreshDelegates() ) ); } void JobStatusView::customDelegateJobInserted( int row, JobStatusItem* item ) { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO; + tLog() << Q_FUNC_INFO << "item is " << item << ", row is " << row; if ( !item ) return; - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "telling item to create delegate"; + tLog() << Q_FUNC_INFO << "telling item to create delegate"; item->createDelegate( m_view ); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "item delegate is " << item->customDelegate(); + tLog() << Q_FUNC_INFO << "item delegate is " << item->customDelegate(); m_view->setItemDelegateForRow( row, item->customDelegate() ); + AclJobDelegate* delegate = qobject_cast< AclJobDelegate* >( item->customDelegate() ); + if ( delegate ) + { + tLog() << Q_FUNC_INFO << "delegate found"; + connect( delegate, SIGNAL( update( const QModelIndex& ) ), m_view, SLOT( update( const QModelIndex & ) ) ); + connect( delegate, SIGNAL( aclResult( ACLRegistry::ACL ) ), item, SLOT( aclResult( ACLRegistry::ACL ) ) ); + delegate->emitSizeHintChanged( m_model->index( row ) ); + } + else + tLog() << Q_FUNC_INFO << "delegate was not properly found!"; + + checkCount(); } void JobStatusView::customDelegateJobRemoved( int row ) { - m_view->setItemDelegateForRow( row, m_view->itemDelegate() ); + tLog() << Q_FUNC_INFO << "row is " << row; +} + + +void +JobStatusView::refreshDelegates() +{ + tLog() << Q_FUNC_INFO; + int count = m_model->rowCount(); + for ( int i = 0; i < count; i++ ) + { + tLog() << Q_FUNC_INFO << "checking row " << i; + QModelIndex index = m_model->index( i ); + QVariant itemVar = index.data( JobStatusModel::JobDataRole ); + if ( !itemVar.canConvert< JobStatusItem* >() || !itemVar.value< JobStatusItem* >() ) + { + tLog() << Q_FUNC_INFO << "unable to fetch JobStatusItem* at row " << i; + continue; + } + JobStatusItem* item = itemVar.value< JobStatusItem* >(); + if ( item->hasCustomDelegate() ) + m_view->setItemDelegateForRow( i, item->customDelegate() ); + else + m_view->setItemDelegateForRow( i, m_view->itemDelegate() ); + } + + checkCount(); } diff --git a/src/libtomahawk/jobview/JobStatusView.h b/src/libtomahawk/jobview/JobStatusView.h index 1a5edf665..c845815de 100644 --- a/src/libtomahawk/jobview/JobStatusView.h +++ b/src/libtomahawk/jobview/JobStatusView.h @@ -55,6 +55,7 @@ private slots: void checkCount(); void customDelegateJobInserted( int row, JobStatusItem* item ); void customDelegateJobRemoved( int row ); + void refreshDelegates(); private: QListView* m_view; diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 286965d88..bdc52646c 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -200,9 +200,10 @@ Connection::checkACL() } QString nodeid = property( "nodeid" ).toString(); + QString bareName = name().left( name().indexOf( "/" ) ); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Checking ACL for" << name(); connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, ACLRegistry::ACL ) ), this, SLOT( checkACLResult( QString, QString, ACLRegistry::ACL ) ), Qt::QueuedConnection ); - QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, nodeid ), Q_ARG( QString, name() ), Q_ARG( ACLRegistry::ACL, ACLRegistry::NotFound ) ); + QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, nodeid ), Q_ARG( QString, bareName ), Q_ARG( ACLRegistry::ACL, ACLRegistry::NotFound ) ); } diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index ec409f041..3bbbf4750 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -535,6 +535,8 @@ Servent::connectToPeer( const QString& ha, int port, const QString &key, const Q if( id.length() ) conn->setId( id ); + conn->setProperty( "nodeid", id ); + connectToPeer( ha, port, key, conn ); } diff --git a/src/libtomahawk/sip/SipHandler.cpp b/src/libtomahawk/sip/SipHandler.cpp index dbfdad92b..4044ddd6d 100644 --- a/src/libtomahawk/sip/SipHandler.cpp +++ b/src/libtomahawk/sip/SipHandler.cpp @@ -169,6 +169,11 @@ SipHandler::onSipInfo( const QString& peerId, const SipInfo& info ) { tDebug() << Q_FUNC_INFO << "SIP Message:" << peerId << info; + QString barePeerId = peerId.left( peerId.indexOf( "/" ) ); + + //FIXME: We should probably be using barePeerId in the connectToPeer call below. + //But, verify this doesn't cause any problems (there is still a uniquename after all) + /* If only one party is externally visible, connection is obvious If both are, peer with lowest IP address initiates the connection. @@ -199,7 +204,7 @@ SipHandler::onSipInfo( const QString& peerId, const SipInfo& info ) m_peersSipInfos.insert( peerId, info ); } -void SipHandler::onSoftwareVersion(const QString& peerId, const QString& versionString) +void SipHandler::onSoftwareVersion( const QString& peerId, const QString& versionString ) { m_peersSoftwareVersions.insert( peerId, versionString ); } diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.cpp b/src/libtomahawk/utils/TomahawkUtilsGui.cpp index 9a814a3e5..4dfef653b 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.cpp +++ b/src/libtomahawk/utils/TomahawkUtilsGui.cpp @@ -443,6 +443,55 @@ prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, Pl } +void +drawRoundedButton( QPainter* painter, const QRect& btnRect, const QColor& color, const QColor &gradient1bottom, const QColor& gradient2top, const QColor& gradient2bottom ) +{ + 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 ( gradient1bottom.isValid() ) + { + g.setColorAt( 0, color ); + g.setColorAt( 0.5, gradient1bottom ); + painter->fillPath( btnPath, g ); + } + else + painter->fillPath( btnPath, color ); + //painter->setPen( bg.darker() ); + + //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 ( gradient2top.isValid() && gradient2bottom.isValid() ) + { + g.setColorAt( 0, gradient2top ); + g.setColorAt( 0.5, gradient2bottom ); + painter->fillPath( btnPath, g ); + } + else + painter->fillPath( btnPath, color ); + +} + + void styleScrollBar( QScrollBar* scrollBar ) { diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.h b/src/libtomahawk/utils/TomahawkUtilsGui.h index df89d9bc8..6e8f7d8bf 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.h +++ b/src/libtomahawk/utils/TomahawkUtilsGui.h @@ -22,6 +22,8 @@ #include #include +#include +#include #include #include "TomahawkUtils.h" @@ -30,7 +32,6 @@ class PlayableItem; class QStyleOptionViewItemV4; class QPainter; -class QColor; class QPixmap; class QLayout; class QPalette; @@ -60,7 +61,9 @@ namespace TomahawkUtils DLLEXPORT QPixmap defaultPixmap( ImageType type, ImageMode mode = TomahawkUtils::Original, const QSize& size = QSize( 0, 0 ) ); DLLEXPORT void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ); - + + DLLEXPORT void drawRoundedButton( QPainter* painter, const QRect& btnRect, const QColor& color, const QColor &gradient1bottom = QColor(), const QColor& gradient2top = QColor(), const QColor& gradient2bottom = QColor() ); + DLLEXPORT void styleScrollBar( QScrollBar* scrollBar ); }