From 0dd7d1e0e069390526a1334ae08f384b5b7a6414 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jan 2011 12:00:52 +0100 Subject: [PATCH 01/14] * Fixed using resolv-hints for offline sources. --- .../database/databasecommand_resolve.cpp | 15 +++++++++++---- src/libtomahawk/database/databaseimpl.cpp | 5 ++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/database/databasecommand_resolve.cpp b/src/libtomahawk/database/databasecommand_resolve.cpp index 394e449c8..3434d43eb 100644 --- a/src/libtomahawk/database/databasecommand_resolve.cpp +++ b/src/libtomahawk/database/databasecommand_resolve.cpp @@ -37,14 +37,21 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib ) if ( !m.isEmpty() ) { if ( m.value( "srcid" ).toUInt() > 0 ) - coll = SourceList::instance()->get( m.value( "srcid" ).toUInt() )->collection(); + { + source_ptr s = SourceList::instance()->get( m.value( "srcid" ).toUInt() ); + if ( !s.isNull() ) + coll = s->collection(); + } else coll = SourceList::instance()->getLocal()->collection(); - res << Tomahawk::result_ptr( new Tomahawk::Result( m, coll ) ); - emit results( qid, res ); + if ( !coll.isNull() ) + { + res << Tomahawk::result_ptr( new Tomahawk::Result( m, coll ) ); + emit results( qid, res ); - return; + return; + } } } diff --git a/src/libtomahawk/database/databaseimpl.cpp b/src/libtomahawk/database/databaseimpl.cpp index df6e55f7e..e23b9f9db 100644 --- a/src/libtomahawk/database/databaseimpl.cpp +++ b/src/libtomahawk/database/databaseimpl.cpp @@ -477,6 +477,7 @@ DatabaseImpl::result( const QString& url ) { TomahawkSqlQuery query = newquery(); Tomahawk::source_ptr s; + QVariantMap m; QString fileUrl; if ( url.contains( "servent://" ) ) @@ -484,6 +485,9 @@ DatabaseImpl::result( const QString& url ) QStringList parts = url.mid( QString( "servent://" ).length() ).split( "\t" ); s = SourceList::instance()->get( parts.at( 0 ) ); fileUrl = parts.at( 1 ); + + if ( s.isNull() ) + return m; } else if ( url.contains( "file://" ) ) { @@ -518,7 +522,6 @@ DatabaseImpl::result( const QString& url ) query.bindValue( 0, fileUrl ); query.exec(); - QVariantMap m; if( query.next() ) { const QString url_str = query.value( 0 ).toString(); From 2edd93ab3c2a07086010094c039c89fc1f197377 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jan 2011 15:00:33 +0100 Subject: [PATCH 02/14] * BufferIODevice & FileTransferConnection now support seeking (needs testing). --- src/libtomahawk/network/bufferiodevice.cpp | 209 +++++++++++++++--- src/libtomahawk/network/bufferiodevice.h | 33 ++- src/libtomahawk/network/connection.cpp | 6 +- .../network/filetransferconnection.cpp | 70 ++++-- .../network/filetransferconnection.h | 4 + 5 files changed, 264 insertions(+), 58 deletions(-) diff --git a/src/libtomahawk/network/bufferiodevice.cpp b/src/libtomahawk/network/bufferiodevice.cpp index b4e533643..2bea719ef 100644 --- a/src/libtomahawk/network/bufferiodevice.cpp +++ b/src/libtomahawk/network/bufferiodevice.cpp @@ -1,11 +1,18 @@ -#include #include "bufferiodevice.h" +#include +#include +#include -BufferIODevice::BufferIODevice( unsigned int size, QObject *parent ) : - QIODevice( parent ), - m_size( size ), - m_received( 0 ) +// Msgs are framed, this is the size each msg we send containing audio data: +#define BLOCKSIZE 4096 + + +BufferIODevice::BufferIODevice( unsigned int size, QObject *parent ) + : QIODevice( parent ) + , m_size( size ) + , m_received( 0 ) + , m_pos( 0 ) { } @@ -16,7 +23,7 @@ BufferIODevice::open( OpenMode mode ) QMutexLocker lock( &m_mut ); qDebug() << Q_FUNC_INFO; - QIODevice::open( QIODevice::ReadWrite ); // FIXME? + QIODevice::open( QIODevice::ReadOnly | QIODevice::Unbuffered ); // FIXME? return true; } @@ -31,6 +38,32 @@ BufferIODevice::close() } +bool +BufferIODevice::seek( qint64 pos ) +{ + qDebug() << Q_FUNC_INFO << pos << m_size; + + if ( pos >= m_size ) + return false; + + int block = blockForPos( pos ); + if ( isBlockEmpty( block ) ) + emit blockRequest( block ); + + m_pos = pos; + qDebug() << "Finished seeking"; + + return true; +} + + +void +BufferIODevice::seeked( int block ) +{ + qDebug() << Q_FUNC_INFO << block << m_size; +} + + void BufferIODevice::inputComplete( const QString& errmsg ) { @@ -41,62 +74,76 @@ BufferIODevice::inputComplete( const QString& errmsg ) void -BufferIODevice::addData( QByteArray ba ) +BufferIODevice::addData( int block, const QByteArray& ba ) { - writeData( ba.data(), ba.length() ); + { + QMutexLocker lock( &m_mut ); + + while ( m_buffer.count() <= block ) + m_buffer << QByteArray(); + + m_buffer.replace( block, ba ); + } + + // If this was the last block of the transfer, check if we need to fill up gaps + if ( block + 1 == maxBlocks() ) + { + if ( nextEmptyBlock() >= 0 ) + { + emit blockRequest( nextEmptyBlock() ); + } + } + + emit bytesWritten( ba.count() ); + emit readyRead(); } qint64 BufferIODevice::bytesAvailable() const { - QMutexLocker lock( &m_mut ); - return m_buffer.length(); + return m_size - m_pos; } qint64 -BufferIODevice::readData( char * data, qint64 maxSize ) +BufferIODevice::readData( char* data, qint64 maxSize ) { - //qDebug() << Q_FUNC_INFO << maxSize; - QMutexLocker lock( &m_mut ); +// qDebug() << Q_FUNC_INFO << m_pos << maxSize << 1; - qint64 size = maxSize; - if ( m_buffer.length() < maxSize ) - size = m_buffer.length(); + if ( atEnd() ) + return 0; - memcpy( data, m_buffer.data(), size ); - m_buffer.remove( 0, size ); + QByteArray ba; + ba.append( getData( m_pos, maxSize ) ); + m_pos += ba.count(); - //qDebug() << "readData ends, bufersize:" << m_buffer.length(); - return size; +// qDebug() << Q_FUNC_INFO << maxSize << ba.count() << 2; + memcpy( data, ba.data(), ba.count() ); + + return ba.count(); } -qint64 BufferIODevice::writeData( const char * data, qint64 maxSize ) +qint64 BufferIODevice::writeData( const char* data, qint64 maxSize ) { - { - QMutexLocker lock( &m_mut ); - m_buffer.append( data, maxSize ); - m_received += maxSize; - } - - emit bytesWritten( maxSize ); - emit readyRead(); - return maxSize; + // call addData instead + Q_ASSERT( false ); + return 0; } qint64 BufferIODevice::size() const { + qDebug() << Q_FUNC_INFO << m_size; return m_size; } bool BufferIODevice::atEnd() const { - QMutexLocker lock( &m_mut ); - return ( m_size == m_received && m_buffer.length() == 0 ); +// qDebug() << Q_FUNC_INFO << ( m_size <= m_pos ); + return ( m_size <= m_pos ); } @@ -104,5 +151,103 @@ void BufferIODevice::clear() { QMutexLocker lock( &m_mut ); + + m_pos = 0; m_buffer.clear(); } + + +unsigned int +BufferIODevice::blockSize() +{ + return BLOCKSIZE; +} + + +int +BufferIODevice::blockForPos( qint64 pos ) const +{ + // 0 / 4096 -> block 0 + // 4095 / 4096 -> block 0 + // 4096 / 4096 -> block 1 + + return pos / BLOCKSIZE; +} + + +int +BufferIODevice::offsetForPos( qint64 pos ) const +{ + // 0 % 4096 -> offset 0 + // 4095 % 4096 -> offset 4095 + // 4096 % 4096 -> offset 0 + + return pos % BLOCKSIZE; +} + + +int +BufferIODevice::nextEmptyBlock() const +{ + int i = 0; + foreach( const QByteArray& ba, m_buffer ) + { + if ( ba.isEmpty() ) + return i; + + i++; + } + + if ( i == maxBlocks() ) + return -1; + + return i; +} + + +int +BufferIODevice::maxBlocks() const +{ + int i = m_size / BLOCKSIZE; + + if ( ( m_size % BLOCKSIZE ) > 0 ) + i++; + + return i; +} + + +bool +BufferIODevice::isBlockEmpty( int block ) const +{ + if ( block >= m_buffer.count() ) + return true; + + if ( m_buffer.at( block ).isEmpty() ) + return true; +} + + +QByteArray +BufferIODevice::getData( qint64 pos, qint64 size ) +{ +// qDebug() << Q_FUNC_INFO << pos << size << 1; + QByteArray ba; + int block = blockForPos( pos ); + int offset = offsetForPos( pos ); + + QMutexLocker lock( &m_mut ); + while( ba.count() < size ) + { + if ( block > maxBlocks() ) + break; + + if ( isBlockEmpty( block ) ) + break; + + ba.append( m_buffer.at( block++ ).mid( offset ) ); + } + +// qDebug() << Q_FUNC_INFO << pos << size << 2; + return ba.left( size ); +} diff --git a/src/libtomahawk/network/bufferiodevice.h b/src/libtomahawk/network/bufferiodevice.h index 69a661d76..0fca19b94 100644 --- a/src/libtomahawk/network/bufferiodevice.h +++ b/src/libtomahawk/network/bufferiodevice.h @@ -4,35 +4,58 @@ #include #include #include +#include class BufferIODevice : public QIODevice { Q_OBJECT + public: explicit BufferIODevice( unsigned int size = 0, QObject *parent = 0 ); virtual bool open( OpenMode mode ); virtual void close(); + virtual bool seek( qint64 pos ); + void seeked( int block ); + virtual qint64 bytesAvailable() const; virtual qint64 size() const; virtual bool atEnd() const; + virtual qint64 pos() const { qDebug() << Q_FUNC_INFO << m_pos; return m_pos; } - void addData( QByteArray ba ); + void addData( int block, const QByteArray& ba ); void clear(); - OpenMode openMode() const { qDebug() << "openMode"; return QIODevice::ReadWrite; } + OpenMode openMode() const { qDebug() << "openMode"; return QIODevice::ReadOnly | QIODevice::Unbuffered; } void inputComplete( const QString& errmsg = "" ); + virtual bool isSequential() const { return false; } + + static unsigned int blockSize(); + + int maxBlocks() const; + int nextEmptyBlock() const; + bool isBlockEmpty( int block ) const; + +signals: + void blockRequest( int block ); + protected: - virtual qint64 readData( char * data, qint64 maxSize ); - virtual qint64 writeData( const char * data, qint64 maxSize ); + virtual qint64 readData( char* data, qint64 maxSize ); + virtual qint64 writeData( const char* data, qint64 maxSize ); private: - QByteArray m_buffer; + int blockForPos( qint64 pos ) const; + int offsetForPos( qint64 pos ) const; + QByteArray getData( qint64 pos, qint64 size ); + + QList m_buffer; mutable QMutex m_mut; //const methods need to lock unsigned int m_size, m_received; + + unsigned int m_pos; }; #endif // BUFFERIODEVICE_H diff --git a/src/libtomahawk/network/connection.cpp b/src/libtomahawk/network/connection.cpp index 2dc64db52..42c78bd74 100644 --- a/src/libtomahawk/network/connection.cpp +++ b/src/libtomahawk/network/connection.cpp @@ -5,7 +5,7 @@ #include "network/servent.h" -#define PROTOVER "3" // must match remote peer, or we can't talk. +#define PROTOVER "4" // must match remote peer, or we can't talk. Connection::Connection( Servent* parent ) @@ -70,8 +70,8 @@ Connection::handleIncomingQueueEmpty() if( m_sock->bytesAvailable() == 0 && m_peer_disconnected ) { qDebug() << "No more data to read, peer disconnected. shutting down connection." - << "bytesavail" << m_sock->bytesAvailable() - << "bytesrx" << m_rx_bytes; + << "bytesavail" << m_sock->bytesAvailable() + << "bytesrx" << m_rx_bytes; shutdown(); } } diff --git a/src/libtomahawk/network/filetransferconnection.cpp b/src/libtomahawk/network/filetransferconnection.cpp index ea9e9a924..dbfe3570d 100644 --- a/src/libtomahawk/network/filetransferconnection.cpp +++ b/src/libtomahawk/network/filetransferconnection.cpp @@ -10,9 +10,6 @@ #include "database/database.h" #include "sourcelist.h" -// Msgs are framed, this is the size each msg we send containing audio data: -#define BLOCKSIZE 4096 - using namespace Tomahawk; @@ -21,6 +18,7 @@ FileTransferConnection::FileTransferConnection( Servent* s, ControlConnection* c , m_cc( cc ) , m_fid( fid ) , m_type( RECEIVING ) + , m_curBlock( 0 ) , m_badded( 0 ) , m_bsent( 0 ) , m_allok( false ) @@ -37,7 +35,8 @@ FileTransferConnection::FileTransferConnection( Servent* s, ControlConnection* c // if the audioengine closes the iodev (skip/stop/etc) then kill the connection // immediately to avoid unnecessary network transfer - connect( m_iodev.data(), SIGNAL( aboutToClose() ), this, SLOT( shutdown() ), Qt::QueuedConnection ); + connect( m_iodev.data(), SIGNAL( aboutToClose() ), SLOT( shutdown() ), Qt::QueuedConnection ); + connect( m_iodev.data(), SIGNAL( blockRequest( int ) ), SLOT( onBlockRequest( int ) ) ); // auto delete when connection closes: connect( this, SIGNAL( finished() ), SLOT( deleteLater() ), Qt::QueuedConnection ); @@ -170,21 +169,41 @@ FileTransferConnection::startSending( const Tomahawk::result_ptr& result ) void FileTransferConnection::handleMsg( msg_ptr msg ) { - Q_ASSERT( m_type == FileTransferConnection::RECEIVING ); Q_ASSERT( msg->is( Msg::RAW ) ); - m_badded += msg->payload().length(); - ((BufferIODevice*)m_iodev.data())->addData( msg->payload() ); + if ( msg->payload().startsWith( "block" ) ) + { + int block = QString( msg->payload() ).mid( 5 ).toInt(); + m_readdev->seek( block * BufferIODevice::blockSize() ); + + qDebug() << "Seeked to block:" << block; + + QByteArray sm; + sm.append( QString( "doneblock%1" ).arg( block ) ); + + sendMsg( Msg::factory( sm, Msg::RAW | Msg::FRAGMENT ) ); + QTimer::singleShot( 0, this, SLOT( sendSome() ) ); + } + else if ( msg->payload().startsWith( "doneblock" ) ) + { + int block = QString( msg->payload() ).mid( 9 ).toInt(); + ((BufferIODevice*)m_iodev.data())->seeked( block ); + + m_curBlock = block; + qDebug() << "Next block is now:" << block; + } + else if ( msg->payload().startsWith( "data" ) ) + { + m_badded += msg->payload().length() - 4; + ((BufferIODevice*)m_iodev.data())->addData( m_curBlock++, msg->payload().mid( 4 ) ); + } //qDebug() << Q_FUNC_INFO << "flags" << (int) msg->flags() // << "payload len" << msg->payload().length() // << "written to device so far: " << m_badded; - if( !msg->is( Msg::FRAGMENT ) ) + if ( ((BufferIODevice*)m_iodev.data())->nextEmptyBlock() < 0 ) { - qDebug() << "*** Got last msg in filetransfer. added" << m_badded - << "io size" << m_iodev->size(); - m_allok = true; // tell our iodev there is no more data to read, no args meaning a success: ((BufferIODevice*)m_iodev.data())->inputComplete(); @@ -193,26 +212,26 @@ FileTransferConnection::handleMsg( msg_ptr msg ) } -Connection* FileTransferConnection::clone() +Connection* +FileTransferConnection::clone() { Q_ASSERT( false ); return 0; } -void FileTransferConnection::sendSome() +void +FileTransferConnection::sendSome() { Q_ASSERT( m_type == FileTransferConnection::SENDING ); - QByteArray ba = m_readdev->read( BLOCKSIZE ); - m_bsent += ba.length(); - //qDebug() << "Sending" << ba.length() << "bytes of audiofile"; + QByteArray ba = "data"; + ba.append( m_readdev->read( BufferIODevice::blockSize() ) ); + m_bsent += ba.length() - 4; if( m_readdev->atEnd() ) { sendMsg( Msg::factory( ba, Msg::RAW ) ); - qDebug() << "Sent all. DONE." << m_bsent; - shutdown( true ); return; } else @@ -225,3 +244,18 @@ void FileTransferConnection::sendSome() // (this is where upload throttling could be implemented) QTimer::singleShot( 0, this, SLOT( sendSome() ) ); } + + +void +FileTransferConnection::onBlockRequest( int block ) +{ + qDebug() << Q_FUNC_INFO << block; + + if ( m_curBlock == block ) + return; + + QByteArray sm; + sm.append( QString( "block%1" ).arg( block ) ); + + sendMsg( Msg::factory( sm, Msg::RAW | Msg::FRAGMENT ) ); +} diff --git a/src/libtomahawk/network/filetransferconnection.h b/src/libtomahawk/network/filetransferconnection.h index 467eb8d46..d0ec0007e 100644 --- a/src/libtomahawk/network/filetransferconnection.h +++ b/src/libtomahawk/network/filetransferconnection.h @@ -54,6 +54,8 @@ private slots: void sendSome(); void showStats( qint64 tx, qint64 rx ); + void onBlockRequest( int pos ); + private: QSharedPointer m_iodev; ControlConnection* m_cc; @@ -61,6 +63,8 @@ private: Type m_type; QSharedPointer m_readdev; + int m_curBlock; + int m_badded, m_bsent; bool m_allok; // got last msg ok, transfer complete? From 80269932adae008220f2258e4c558698a6c4a3a3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jan 2011 13:18:32 +0100 Subject: [PATCH 03/14] * Added QueryLabel, a QLabel-like widget which displays any combination of a query/result's Artist, Album and Track. * Paints a nice rounded-rect around the artist / album / track item when hovered with the mouse. * Drag and drop / detailed click signal support coming up. --- src/CMakeLists.txt | 2 + src/audiocontrols.cpp | 9 +- src/audiocontrols.ui | 8 +- src/libtomahawk/database/tomahawksqlquery.h | 4 +- src/sourcetree/sourcetreeitemwidget.cpp | 4 +- src/sourcetree/sourcetreeitemwidget.ui | 7 +- src/utils/elidedlabel.cpp | 1 - src/utils/elidedlabel.h | 4 +- src/utils/querylabel.cpp | 464 ++++++++++++++++++++ src/utils/querylabel.h | 90 ++++ 10 files changed, 578 insertions(+), 15 deletions(-) create mode 100644 src/utils/querylabel.cpp create mode 100644 src/utils/querylabel.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 59a236c5d..667488832 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET( tomahawkSources ${tomahawkSources} SET( tomahawkSourcesGui ${tomahawkSourcesGui} xspfloader.cpp + utils/querylabel.cpp utils/elidedlabel.cpp utils/imagebutton.cpp utils/progresstreeview.cpp @@ -133,6 +134,7 @@ SET( tomahawkHeaders ${tomahawkHeaders} SET( tomahawkHeadersGui ${tomahawkHeadersGui} xspfloader.h + utils/querylabel.h utils/elidedlabel.h utils/animatedcounterlabel.h utils/imagebutton.h diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp index 640b44b46..f49bd9bb2 100644 --- a/src/audiocontrols.cpp +++ b/src/audiocontrols.cpp @@ -27,13 +27,14 @@ AudioControls::AudioControls( QWidget* parent ) QFont font( ui->artistTrackLabel->font() ); font.setPixelSize( 12 ); -/* ui->artistTrackLabel->setMinimumSize( ui->artistTrackLabel->minimumSizeHint() ); - ui->albumLabel->setMinimumSize( ui->albumLabel->minimumSizeHint() );*/ ui->artistTrackLabel->setFont( font ); ui->artistTrackLabel->setElideMode( Qt::ElideMiddle ); + ui->artistTrackLabel->setType( QueryLabel::ArtistAndTrack ); ui->albumLabel->setFont( font ); + ui->albumLabel->setType( QueryLabel::Album ); + ui->timeLabel->setFont( font ); ui->timeLeftLabel->setFont( font ); @@ -229,8 +230,8 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result ) m_currentTrack = result; - ui->artistTrackLabel->setText( QString( "%1 - %2" ).arg( result->artist()->name() ).arg( result->track() ) ); - ui->albumLabel->setText( result->album()->name() ); + ui->artistTrackLabel->setResult( result ); + ui->albumLabel->setResult( result ); ui->ownerLabel->setText( result->collection()->source()->friendlyName() ); ui->coverImage->setPixmap( m_defaultCover ); diff --git a/src/audiocontrols.ui b/src/audiocontrols.ui index 0c2a8976d..2a90a1ee9 100644 --- a/src/audiocontrols.ui +++ b/src/audiocontrols.ui @@ -184,7 +184,7 @@ 0 - + PointingHandCursor @@ -194,7 +194,7 @@ - + PointingHandCursor @@ -459,9 +459,9 @@
imagebutton.h
- ElidedLabel + QueryLabel QLabel -
elidedlabel.h
+
querylabel.h
diff --git a/src/libtomahawk/database/tomahawksqlquery.h b/src/libtomahawk/database/tomahawksqlquery.h index 543072786..e54030ed0 100644 --- a/src/libtomahawk/database/tomahawksqlquery.h +++ b/src/libtomahawk/database/tomahawksqlquery.h @@ -5,7 +5,7 @@ #include #include -#define TOMAHAWK_QUERY_THRESHOLD 20 +#define TOMAHAWK_QUERY_THRESHOLD 60 class TomahawkSqlQuery : public QSqlQuery { @@ -16,7 +16,7 @@ public: : QSqlQuery() {} - TomahawkSqlQuery( QSqlDatabase db ) + TomahawkSqlQuery( const QSqlDatabase& db ) : QSqlQuery( db ) {} diff --git a/src/sourcetree/sourcetreeitemwidget.cpp b/src/sourcetree/sourcetreeitemwidget.cpp index 2102f5488..4ad1419c8 100644 --- a/src/sourcetree/sourcetreeitemwidget.cpp +++ b/src/sourcetree/sourcetreeitemwidget.cpp @@ -21,6 +21,7 @@ SourceTreeItemWidget::SourceTreeItemWidget( const source_ptr& source, QWidget* p ui->setupUi( this ); ui->verticalLayout->setSpacing( 3 ); + ui->activityLabel->setType( QueryLabel::ArtistAndTrack ); QString displayname; if ( source.isNull() ) @@ -132,7 +133,8 @@ void SourceTreeItemWidget::onPlaybackStarted( const Tomahawk::query_ptr& query ) { qDebug() << Q_FUNC_INFO << query->toString(); - ui->activityLabel->setText( tr( "Playing: %1 by %2" ).arg( query->track() ).arg( query->artist() ) ); +// ui->activityLabel->setText( tr( "Playing: %1 by %2" ).arg( query->track() ).arg( query->artist() ) ); + ui->activityLabel->setQuery( query ); } diff --git a/src/sourcetree/sourcetreeitemwidget.ui b/src/sourcetree/sourcetreeitemwidget.ui index 3933cba59..6e889e6a3 100644 --- a/src/sourcetree/sourcetreeitemwidget.ui +++ b/src/sourcetree/sourcetreeitemwidget.ui @@ -127,7 +127,7 @@
- + TextLabel @@ -201,6 +201,11 @@ QLabel
elidedlabel.h
+ + QueryLabel + QLabel +
querylabel.h
+
diff --git a/src/utils/elidedlabel.cpp b/src/utils/elidedlabel.cpp index 8c581997c..a17f31dca 100644 --- a/src/utils/elidedlabel.cpp +++ b/src/utils/elidedlabel.cpp @@ -1,6 +1,5 @@ #include "elidedlabel.h" -#include #include #include #include diff --git a/src/utils/elidedlabel.h b/src/utils/elidedlabel.h index a3e4f5d4f..f05b766c5 100644 --- a/src/utils/elidedlabel.h +++ b/src/utils/elidedlabel.h @@ -30,10 +30,10 @@ public: void init( const QString& txt = QString() ); void updateLabel(); -public Q_SLOTS: +public slots: void setText( const QString& text ); -Q_SIGNALS: +signals: void clicked(); void textChanged( const QString& text ); diff --git a/src/utils/querylabel.cpp b/src/utils/querylabel.cpp new file mode 100644 index 000000000..55d8f95b5 --- /dev/null +++ b/src/utils/querylabel.cpp @@ -0,0 +1,464 @@ +#include "querylabel.h" + +#include +#include +#include +#include +#include + + +QueryLabel::QueryLabel( QWidget* parent, Qt::WindowFlags flags ) + : QFrame( parent, flags ) + , m_type( Complete ) +{ + init(); +} + + +QueryLabel::QueryLabel( DisplayType type, QWidget* parent, Qt::WindowFlags flags ) + : QFrame( parent, flags ) + , m_type( type ) +{ + init(); +} + + +QueryLabel::QueryLabel( const Tomahawk::result_ptr& result, DisplayType type, QWidget* parent, Qt::WindowFlags flags ) + : QFrame( parent, flags ) + , m_type( type ) + , m_result( result ) +{ + init(); +} + + +QueryLabel::QueryLabel( const Tomahawk::query_ptr& query, DisplayType type, QWidget* parent, Qt::WindowFlags flags ) + : QFrame( parent, flags ) + , m_type( type ) + , m_query( query ) +{ + init(); +} + + +QueryLabel::~QueryLabel() +{ +} + + +void +QueryLabel::init() +{ + setContentsMargins( 0, 0, 0, 0 ); + setMouseTracking( true ); + + align = Qt::AlignLeft; + mode = Qt::ElideMiddle; +} + + +QString +QueryLabel::text() const +{ + QString text; + + if ( m_result.isNull() && m_query.isNull() ) + return m_text; + + if ( !m_result.isNull() ) + { + if ( m_type & Artist ) + { + text += m_result->artist()->name(); + } + if ( m_type & Album ) + { + smartAppend( text, m_result->album()->name() ); + } + if ( m_type & Track ) + { + smartAppend( text, m_result->track() ); + } + } + else + { + if ( m_type & Artist ) + { + text += m_query->artist(); + } + if ( m_type & Album ) + { + smartAppend( text, m_query->album() ); + } + if ( m_type & Track ) + { + smartAppend( text, m_query->track() ); + } + } + + return text; +} + + +QString +QueryLabel::artist() const +{ + if ( m_result.isNull() && m_query.isNull() ) + return QString(); + + if ( !m_result.isNull() ) + return m_result->artist()->name(); + else + return m_query->artist(); +} + + +QString +QueryLabel::album() const +{ + if ( m_result.isNull() && m_query.isNull() ) + return QString(); + + if ( !m_result.isNull() ) + return m_result->album()->name(); + else + return m_query->album(); +} + + +QString +QueryLabel::track() const +{ + if ( m_result.isNull() && m_query.isNull() ) + return QString(); + + if ( !m_result.isNull() ) + return m_result->track(); + else + return m_query->track(); +} + + +void +QueryLabel::setText( const QString& text ) +{ + setContentsMargins( 0, 0, 0, 0 ); + + m_result.clear(); + m_query.clear(); + m_text = text; + + updateLabel(); + + emit textChanged( m_text ); + emit resultChanged( m_result ); +} + + +void +QueryLabel::setResult( const Tomahawk::result_ptr& result ) +{ + if ( result.isNull() ) + return; + + setContentsMargins( 2, 0, 2, 0 ); + + if ( m_result.isNull() || m_result.data() != result.data() ) + { + m_result = result; + m_query.clear(); + updateLabel(); + + emit textChanged( text() ); + emit resultChanged( m_result ); + } +} + + +void +QueryLabel::setQuery( const Tomahawk::query_ptr& query ) +{ + if ( query.isNull() ) + return; + + setContentsMargins( 2, 0, 2, 0 ); + + if ( m_query.isNull() || m_query.data() != query.data() ) + { + m_query = query; + m_result.clear(); + updateLabel(); + + emit textChanged( text() ); + emit queryChanged( m_query ); + } +} + + +Qt::Alignment +QueryLabel::alignment() const +{ + return align; +} + + +void +QueryLabel::setAlignment( Qt::Alignment alignment ) +{ + if ( this->align != alignment ) + { + this->align = alignment; + update(); // no geometry change, repaint is sufficient + } +} + + +Qt::TextElideMode +QueryLabel::elideMode() const +{ + return mode; +} + + +void +QueryLabel::setElideMode( Qt::TextElideMode mode ) +{ + if ( this->mode != mode ) + { + this->mode = mode; + updateLabel(); + } +} + + +void +QueryLabel::updateLabel() +{ + updateGeometry(); + update(); +} + + +QSize +QueryLabel::sizeHint() const +{ + const QFontMetrics& fm = fontMetrics(); + QSize size( fm.width( text() ) + contentsMargins().left() * 2, fm.height() ); + return size; +} + + +QSize +QueryLabel::minimumSizeHint() const +{ + switch ( mode ) + { + case Qt::ElideNone: + return sizeHint(); + + default: + { + const QFontMetrics& fm = fontMetrics(); + QSize size( fm.width( "..." ), fm.height() ); + return size; + } + } +} + + +void +QueryLabel::paintEvent( QPaintEvent* event ) +{ + QFrame::paintEvent( event ); + QPainter p( this ); + QRect r = contentsRect(); + QString s = text(); + const QString elidedText = fontMetrics().elidedText( s, mode, r.width() ); + + p.save(); + p.setRenderHint( QPainter::Antialiasing ); + + if ( elidedText == s && m_hoverArea.width() ) + { + p.setPen( palette().highlight().color() ); + p.setBrush( palette().highlight() ); + p.drawRoundedRect( m_hoverArea, 4.0, 4.0 ); + } + + if ( elidedText != s || ( m_result.isNull() && m_query.isNull() ) ) + { + p.setBrush( palette().window() ); + p.setPen( palette().text().color() ); + p.drawText( r, align, elidedText ); + } + else + { + const QFontMetrics& fm = fontMetrics(); + int dashX = fm.width( " - " ); + int artistX = m_type & Artist ? fm.width( artist() ) : 0; + int albumX = m_type & Album ? fm.width( album() ) : 0; + int trackX = m_type & Track ? fm.width( track() ) : 0; + + if ( m_type & Artist ) + { + p.setBrush( palette().window() ); + p.setPen( palette().text().color() ); + + if ( m_hoverArea.width() && m_hoverArea.left() + contentsMargins().left() == r.left() ) + { + p.setPen( palette().highlightedText().color() ); + p.setBrush( palette().highlight() ); + } + + p.drawText( r, align, artist() ); + r.adjust( artistX, 0, 0, 0 ); + } + if ( m_type & Album ) + { + p.setBrush( palette().window() ); + p.setPen( palette().text().color() ); + + if ( m_type & Artist ) + { + p.drawText( r, align, " - " ); + r.adjust( dashX, 0, 0, 0 ); + } + if ( m_hoverArea.width() && m_hoverArea.left() + contentsMargins().left() == r.left() ) + { + p.setPen( palette().highlightedText().color() ); + p.setBrush( palette().highlight() ); + } + + p.drawText( r, align, album() ); + r.adjust( albumX, 0, 0, 0 ); + } + if ( m_type & Track ) + { + p.setBrush( palette().window() ); + p.setPen( palette().text().color() ); + + if ( m_type & Artist || m_type & Album ) + { + p.drawText( r, align, " - " ); + r.adjust( dashX, 0, 0, 0 ); + } + if ( m_hoverArea.width() && m_hoverArea.left() + contentsMargins().left() == r.left() ) + { + p.setPen( palette().highlightedText().color() ); + p.setBrush( palette().highlight() ); + } + + p.drawText( r, align, track() ); + r.adjust( trackX, 0, 0, 0 ); + } + } + + p.restore(); +} + + +void +QueryLabel::changeEvent( QEvent* event ) +{ + QFrame::changeEvent( event ); + switch ( event->type() ) + { + case QEvent::FontChange: + case QEvent::ApplicationFontChange: + updateLabel(); + break; + + default: + break; + } +} + + +void +QueryLabel::mousePressEvent( QMouseEvent* event ) +{ + QFrame::mousePressEvent( event ); + time.start(); +} + + +void +QueryLabel::mouseReleaseEvent( QMouseEvent* event ) +{ + QFrame::mouseReleaseEvent( event ); + if ( time.elapsed() < qApp->doubleClickInterval() ) + emit clicked(); +} + + +void +QueryLabel::mouseMoveEvent( QMouseEvent* event ) +{ + QFrame::mouseMoveEvent( event ); + int x = event->x(); + + const QFontMetrics& fm = fontMetrics(); + int dashX = fm.width( " - " ); + int artistX = m_type & Artist ? fm.width( artist() ) : 0; + int albumX = m_type & Album ? fm.width( album() ) : 0; + int trackX = m_type & Track ? fm.width( track() ) : 0; + + if ( m_type & Album ) + { + trackX += albumX + dashX; + } + if ( m_type & Artist ) + { + albumX += artistX + dashX; + trackX += artistX + dashX; + } + + QRect hoverArea; + if ( m_type & Artist && x < artistX ) + { + hoverArea.setLeft( 0 ); + hoverArea.setRight( artistX + contentsMargins().left() ); + } + else if ( m_type & Album && x < albumX ) + { + int spacing = ( m_type & Artist ) ? dashX : 0; + hoverArea.setLeft( artistX + spacing ); + hoverArea.setRight( albumX + spacing + contentsMargins().left() ); + } + else if ( m_type & Track && x < trackX ) + { + int spacing = ( m_type & Album ) ? dashX : 0; + hoverArea.setLeft( albumX + spacing ); + hoverArea.setRight( trackX + contentsMargins().left() ); + } + + if ( hoverArea.width() ) + { + hoverArea.setY( 0 ); + hoverArea.setHeight( height() - 1 ); + } + if ( hoverArea != m_hoverArea ) + { + m_hoverArea = hoverArea; + repaint(); + } +} + + +void +QueryLabel::leaveEvent( QEvent* event ) +{ + m_hoverArea = QRect(); +} + + +QString +QueryLabel::smartAppend( QString& text, const QString& appendage ) const +{ + QString s; + if ( !text.isEmpty() ) + s = " - "; + + text += s + appendage; + return text; +} diff --git a/src/utils/querylabel.h b/src/utils/querylabel.h new file mode 100644 index 000000000..95c7f5418 --- /dev/null +++ b/src/utils/querylabel.h @@ -0,0 +1,90 @@ +#ifndef QUERYLABEL_H +#define QUERYLABEL_H + +#include +#include + +#include "result.h" + +class QueryLabel : public QFrame +{ +Q_OBJECT + +public: + enum DisplayType + { + Artist = 1, + Album = 2, + Track = 4, + ArtistAndAlbum = 3, + ArtistAndTrack = 5, + AlbumAndTrack = 6, + Complete = 7 + }; + + explicit QueryLabel( QWidget* parent = 0, Qt::WindowFlags flags = 0 ); + explicit QueryLabel( DisplayType type = Complete, QWidget* parent = 0, Qt::WindowFlags flags = 0 ); + explicit QueryLabel( const Tomahawk::result_ptr& result, DisplayType type = Complete, QWidget* parent = 0, Qt::WindowFlags flags = 0 ); + explicit QueryLabel( const Tomahawk::query_ptr& query, DisplayType type = Complete, QWidget* parent = 0, Qt::WindowFlags flags = 0 ); + virtual ~QueryLabel(); + + QString text() const; + QString artist() const; + QString album() const; + QString track() const; + + Tomahawk::result_ptr result() const { return m_result; } + Tomahawk::query_ptr query() const { return m_query; } + + DisplayType type() const { return m_type; } + void setType( DisplayType type ) { m_type = type; } + + Qt::Alignment alignment() const; + void setAlignment( Qt::Alignment alignment ); + + Qt::TextElideMode elideMode() const; + void setElideMode( Qt::TextElideMode mode ); + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + void init(); + void updateLabel(); + +public slots: + void setText( const QString& text ); + void setResult( const Tomahawk::result_ptr& result ); + void setQuery( const Tomahawk::query_ptr& query ); + +signals: + void clicked(); + + void textChanged( const QString& text ); + void resultChanged( const Tomahawk::result_ptr& result ); + void queryChanged( const Tomahawk::query_ptr& query ); + +protected: + virtual void mousePressEvent( QMouseEvent* event ); + virtual void mouseReleaseEvent( QMouseEvent* event ); + virtual void mouseMoveEvent( QMouseEvent* event ); + virtual void leaveEvent( QEvent* event ); + + virtual void changeEvent( QEvent* event ); + virtual void paintEvent( QPaintEvent* event ); + +private: + QString smartAppend( QString& text, const QString& appendage ) const; + QTime time; + + DisplayType m_type; + QString m_text; + Tomahawk::result_ptr m_result; + Tomahawk::query_ptr m_query; + + Qt::Alignment align; + Qt::TextElideMode mode; + + QRect m_hoverArea; +}; + +#endif // QUERYLABEL_H From c3c6da389e57176af97e673f3fe0afe931407167 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jan 2011 13:25:10 +0100 Subject: [PATCH 04/14] * Fixed resetting the label while an area is hovered. --- src/utils/querylabel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/querylabel.cpp b/src/utils/querylabel.cpp index 55d8f95b5..5a7dcd062 100644 --- a/src/utils/querylabel.cpp +++ b/src/utils/querylabel.cpp @@ -234,6 +234,8 @@ QueryLabel::setElideMode( Qt::TextElideMode mode ) void QueryLabel::updateLabel() { + m_hoverArea = QRect(); + updateGeometry(); update(); } From d09fb9a4283bacce90570a1b6cf9c8862cd98f5c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jan 2011 13:30:21 +0100 Subject: [PATCH 05/14] * Forgot to force a repaint when leaveEvent() is triggered. --- src/utils/querylabel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/querylabel.cpp b/src/utils/querylabel.cpp index 5a7dcd062..ab1a8b57c 100644 --- a/src/utils/querylabel.cpp +++ b/src/utils/querylabel.cpp @@ -451,6 +451,7 @@ void QueryLabel::leaveEvent( QEvent* event ) { m_hoverArea = QRect(); + repaint(); } From 81c42672636d852ec172d386f33f6778b7128e8d Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jan 2011 14:04:30 +0100 Subject: [PATCH 06/14] * Fixed DatabaseCommand_PlaybackHistory's sql limiter. --- .../database/databasecommand_playbackhistory.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/database/databasecommand_playbackhistory.cpp b/src/libtomahawk/database/databasecommand_playbackhistory.cpp index 849d5ce8b..d6727a4e8 100644 --- a/src/libtomahawk/database/databasecommand_playbackhistory.cpp +++ b/src/libtomahawk/database/databasecommand_playbackhistory.cpp @@ -21,7 +21,9 @@ DatabaseCommand_PlaybackHistory::exec( DatabaseImpl* dbi ) "SELECT track, playtime, secs_played " "FROM playback_log " "%1 " - "ORDER BY playtime DESC").arg( whereToken ); + "ORDER BY playtime DESC " + "%2" ).arg( whereToken ) + .arg( m_amount > 0 ? QString( "LIMIT 0, %1" ).arg( m_amount ) : QString() ); query.prepare( sql ); query.exec(); @@ -34,10 +36,8 @@ DatabaseCommand_PlaybackHistory::exec( DatabaseImpl* dbi ) "SELECT track.name, artist.name " "FROM track, artist " "WHERE artist.id = track.artist " - "AND track.id = %1 " - "%2" - ).arg( query.value( 0 ).toUInt() ) - .arg( m_amount > 0 ? QString( "LIMIT 0, %1" ).arg( m_amount ) : QString() ); + "AND track.id = %1" + ).arg( query.value( 0 ).toUInt() ); query_track.prepare( sql ); query_track.exec(); From 6ebafa26a62614e6295646ab9edc5c552d1032e8 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jan 2011 08:40:21 +0100 Subject: [PATCH 07/14] * QueryLabel now supports dragging. * Needs support for dragging Artist / Album. --- src/libtomahawk/query.cpp | 10 +++-- src/libtomahawk/result.cpp | 8 ++++ src/libtomahawk/result.h | 6 +-- src/playlist/playlistmodel.cpp | 4 +- src/playlist/trackview.cpp | 74 +++++----------------------------- src/playlist/trackview.h | 2 - src/utils/querylabel.cpp | 50 +++++++++++++++++++++-- src/utils/querylabel.h | 3 ++ src/utils/tomahawkutils.cpp | 63 +++++++++++++++++++++++++++++ src/utils/tomahawkutils.h | 3 ++ 10 files changed, 146 insertions(+), 77 deletions(-) diff --git a/src/libtomahawk/query.cpp b/src/libtomahawk/query.cpp index 611757985..5a9d5ceea 100644 --- a/src/libtomahawk/query.cpp +++ b/src/libtomahawk/query.cpp @@ -4,6 +4,7 @@ using namespace Tomahawk; + Query::Query( const QVariant& v ) : m_v( v ) , m_solved( false ) @@ -40,7 +41,8 @@ Query::addResults( const QList< Tomahawk::result_ptr >& newresults ) } } emit resultsAdded( newresults ); - if( becameSolved ) emit solvedStateChanged( true ); + if( becameSolved ) + emit solvedStateChanged( true ); } @@ -97,7 +99,8 @@ Query::numResults() const } -QID Query::id() const +QID +Query::id() const { if ( m_qid.isEmpty() ) { @@ -112,7 +115,8 @@ QID Query::id() const } -bool Query::resultSorter( const result_ptr& left, const result_ptr& right ) +bool +Query::resultSorter( const result_ptr& left, const result_ptr& right ) { return left->score() > right->score(); } diff --git a/src/libtomahawk/result.cpp b/src/libtomahawk/result.cpp index ea875f66b..f16eb0a22 100644 --- a/src/libtomahawk/result.cpp +++ b/src/libtomahawk/result.cpp @@ -56,6 +56,14 @@ Result::toString() const } +Tomahawk::query_ptr +Result::toQuery() const +{ + Tomahawk::query_ptr query = Tomahawk::query_ptr( new Tomahawk::Query( toVariant() ) ); + return query; +} + + void Result::updateAttributes() { diff --git a/src/libtomahawk/result.h b/src/libtomahawk/result.h index b10a65087..ed1b93dd7 100644 --- a/src/libtomahawk/result.h +++ b/src/libtomahawk/result.h @@ -19,7 +19,10 @@ Q_OBJECT public: explicit Result( const QVariant& v, const collection_ptr& collection ); + QVariant toVariant() const { return m_v; } + QString toString() const; + Tomahawk::query_ptr toQuery() const; float score() const; RID id() const; @@ -44,9 +47,6 @@ public: unsigned int dbid() const { return m_id; } - // for debug output: - QString toString() const; - signals: // emitted when the collection this result comes from is going offline: void becomingUnavailable(); diff --git a/src/playlist/playlistmodel.cpp b/src/playlist/playlistmodel.cpp index acfb853c0..5063434d2 100644 --- a/src/playlist/playlistmodel.cpp +++ b/src/playlist/playlistmodel.cpp @@ -239,7 +239,9 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r if ( action == Qt::IgnoreAction || isReadOnly() ) return true; - if ( !data->hasFormat( "application/tomahawk.query.list" ) && !data->hasFormat( "application/tomahawk.plentry.list" ) ) + if ( !data->hasFormat( "application/tomahawk.query.list" ) + && !data->hasFormat( "application/tomahawk.plentry.list" ) + && !data->hasFormat( "application/tomahawk.result.list" ) ) return false; int beginRow; diff --git a/src/playlist/trackview.cpp b/src/playlist/trackview.cpp index c6ebd3173..5e769039f 100644 --- a/src/playlist/trackview.cpp +++ b/src/playlist/trackview.cpp @@ -161,7 +161,9 @@ TrackView::dragEnterEvent( QDragEnterEvent* event ) qDebug() << Q_FUNC_INFO; QTreeView::dragEnterEvent( event ); - if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) || event->mimeData()->hasFormat( "application/tomahawk.plentry.list" ) ) + if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) + || event->mimeData()->hasFormat( "application/tomahawk.plentry.list" ) + || event->mimeData()->hasFormat( "application/tomahawk.result.list" ) ) { m_dragging = true; m_dropRect = QRect(); @@ -183,7 +185,9 @@ TrackView::dragMoveEvent( QDragMoveEvent* event ) return; } - if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) || event->mimeData()->hasFormat( "application/tomahawk.plentry.list" ) ) + if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) + || event->mimeData()->hasFormat( "application/tomahawk.plentry.list" ) + || event->mimeData()->hasFormat( "application/tomahawk.result.list" ) ) { setDirtyRegion( m_dropRect ); const QPoint pos = event->pos(); @@ -216,6 +220,7 @@ TrackView::dropEvent( QDropEvent* event ) qDebug() << "Ignoring accepted event!"; } else + { if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) ) { const QPoint pos = event->pos(); @@ -229,6 +234,7 @@ TrackView::dropEvent( QDropEvent* event ) model()->dropMimeData( event->mimeData(), event->proposedAction(), index.row(), 0, index.parent() ); } } + } m_dragging = false; } @@ -298,7 +304,7 @@ TrackView::startDrag( Qt::DropActions supportedActions ) QDrag* drag = new QDrag( this ); drag->setMimeData( data ); - const QPixmap p = createDragPixmap( indexes.count() ); + const QPixmap p = TomahawkUtils::createDragPixmap( indexes.count() ); drag->setPixmap( p ); drag->setHotSpot( QPoint( -20, -20 ) ); @@ -308,65 +314,3 @@ TrackView::startDrag( Qt::DropActions supportedActions ) m_proxyModel->removeIndexes( pindexes ); } } - - -// Inspired from dolphin's draganddrophelper.cpp -QPixmap -TrackView::createDragPixmap( int itemCount ) const -{ - // If more than one item is dragged, align the items inside a - // rectangular grid. The maximum grid size is limited to 5 x 5 items. - int xCount = 3; - int size = 32; - - if ( itemCount > 16 ) - { - xCount = 5; - size = 16; - } else if( itemCount > 9 ) - { - xCount = 4; - size = 22; - } - - if( itemCount < xCount ) - { - xCount = itemCount; - } - - int yCount = itemCount / xCount; - if( itemCount % xCount != 0 ) - { - ++yCount; - } - if( yCount > xCount ) - { - yCount = xCount; - } - // Draw the selected items into the grid cells - QPixmap dragPixmap( xCount * size + xCount - 1, yCount * size + yCount - 1 ); - dragPixmap.fill( Qt::transparent ); - - QPainter painter( &dragPixmap ); - painter.setRenderHint( QPainter::Antialiasing ); - int x = 0; - int y = 0; - for( int i = 0; i < itemCount; ++i ) - { - const QPixmap pixmap = QPixmap( QString( ":/data/icons/audio-x-generic-%2x%2.png" ).arg( size ) ); - painter.drawPixmap( x, y, pixmap ); - - x += size + 1; - if ( x >= dragPixmap.width() ) - { - x = 0; - y += size + 1; - } - if ( y >= dragPixmap.height() ) - { - break; - } - } - - return dragPixmap; -} diff --git a/src/playlist/trackview.h b/src/playlist/trackview.h index 1b89d28e7..018a97c2c 100644 --- a/src/playlist/trackview.h +++ b/src/playlist/trackview.h @@ -55,8 +55,6 @@ private slots: void onFilterChanged( const QString& filter ); private: - QPixmap createDragPixmap( int itemCount ) const; - TrackModel* m_model; TrackProxyModel* m_proxyModel; PlaylistItemDelegate* m_delegate; diff --git a/src/utils/querylabel.cpp b/src/utils/querylabel.cpp index ab1a8b57c..0dcdd39e3 100644 --- a/src/utils/querylabel.cpp +++ b/src/utils/querylabel.cpp @@ -6,6 +6,10 @@ #include #include +#include "tomahawkutils.h" + +#define BOXMARGIN 2 + QueryLabel::QueryLabel( QWidget* parent, Qt::WindowFlags flags ) : QFrame( parent, flags ) @@ -161,12 +165,17 @@ QueryLabel::setResult( const Tomahawk::result_ptr& result ) if ( result.isNull() ) return; - setContentsMargins( 2, 0, 2, 0 ); + setContentsMargins( BOXMARGIN, 0, BOXMARGIN, 0 ); if ( m_result.isNull() || m_result.data() != result.data() ) { m_result = result; - m_query.clear(); + + m_query = m_result->toQuery(); + QList rl; + rl << m_result; + m_query->addResults( rl ); + updateLabel(); emit textChanged( text() ); @@ -181,7 +190,7 @@ QueryLabel::setQuery( const Tomahawk::query_ptr& query ) if ( query.isNull() ) return; - setContentsMargins( 2, 0, 2, 0 ); + setContentsMargins( BOXMARGIN, 0, BOXMARGIN, 0 ); if ( m_query.isNull() || m_query.data() != query.data() ) { @@ -388,6 +397,8 @@ void QueryLabel::mouseReleaseEvent( QMouseEvent* event ) { QFrame::mouseReleaseEvent( event ); + + m_dragPos = QPoint(); if ( time.elapsed() < qApp->doubleClickInterval() ) emit clicked(); } @@ -399,6 +410,14 @@ QueryLabel::mouseMoveEvent( QMouseEvent* event ) QFrame::mouseMoveEvent( event ); int x = event->x(); + if ( event->buttons() & Qt::LeftButton && + ( m_dragPos - event->pos() ).manhattanLength() >= QApplication::startDragDistance() ) + { + startDrag(); + leaveEvent( 0 ); + return; + } + const QFontMetrics& fm = fontMetrics(); int dashX = fm.width( " - " ); int artistX = m_type & Artist ? fm.width( artist() ) : 0; @@ -455,6 +474,31 @@ QueryLabel::leaveEvent( QEvent* event ) } +void +QueryLabel::startDrag() +{ + if ( m_query.isNull() ) + return; + + QByteArray queryData; + QDataStream queryStream( &queryData, QIODevice::WriteOnly ); + QMimeData* mimeData = new QMimeData(); + mimeData->setText( text() ); + + queryStream << qlonglong( &m_query ); + + mimeData->setData( "application/tomahawk.query.list", queryData ); + QDrag *drag = new QDrag( this ); + drag->setMimeData( mimeData ); + drag->setPixmap( TomahawkUtils::createDragPixmap() ); + +// QPoint hotSpot = event->pos() - child->pos(); +// drag->setHotSpot( hotSpot ); + + drag->exec( Qt::CopyAction ); +} + + QString QueryLabel::smartAppend( QString& text, const QString& appendage ) const { diff --git a/src/utils/querylabel.h b/src/utils/querylabel.h index 95c7f5418..3d16e3ced 100644 --- a/src/utils/querylabel.h +++ b/src/utils/querylabel.h @@ -71,6 +71,8 @@ protected: virtual void changeEvent( QEvent* event ); virtual void paintEvent( QPaintEvent* event ); + + virtual void startDrag(); private: QString smartAppend( QString& text, const QString& appendage ) const; @@ -85,6 +87,7 @@ private: Qt::TextElideMode mode; QRect m_hoverArea; + QPoint m_dragPos; }; #endif // QUERYLABEL_H diff --git a/src/utils/tomahawkutils.cpp b/src/utils/tomahawkutils.cpp index a181651f9..d9a5e6c50 100644 --- a/src/utils/tomahawkutils.cpp +++ b/src/utils/tomahawkutils.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #ifdef WIN32 #include @@ -210,4 +212,65 @@ filesizeToString( unsigned int size ) return QString::number( size ); } + +QPixmap +createDragPixmap( int itemCount ) +{ + // If more than one item is dragged, align the items inside a + // rectangular grid. The maximum grid size is limited to 5 x 5 items. + int xCount = 3; + int size = 32; + + if ( itemCount > 16 ) + { + xCount = 5; + size = 16; + } else if( itemCount > 9 ) + { + xCount = 4; + size = 22; + } + + if( itemCount < xCount ) + { + xCount = itemCount; + } + + int yCount = itemCount / xCount; + if( itemCount % xCount != 0 ) + { + ++yCount; + } + if( yCount > xCount ) + { + yCount = xCount; + } + // Draw the selected items into the grid cells + QPixmap dragPixmap( xCount * size + xCount - 1, yCount * size + yCount - 1 ); + dragPixmap.fill( Qt::transparent ); + + QPainter painter( &dragPixmap ); + painter.setRenderHint( QPainter::Antialiasing ); + int x = 0; + int y = 0; + for( int i = 0; i < itemCount; ++i ) + { + const QPixmap pixmap = QPixmap( QString( ":/data/icons/audio-x-generic-%2x%2.png" ).arg( size ) ); + painter.drawPixmap( x, y, pixmap ); + + x += size + 1; + if ( x >= dragPixmap.width() ) + { + x = 0; + y += size + 1; + } + if ( y >= dragPixmap.height() ) + { + break; + } + } + + return dragPixmap; +} + } // ns diff --git a/src/utils/tomahawkutils.h b/src/utils/tomahawkutils.h index d902250bc..737f56612 100644 --- a/src/utils/tomahawkutils.h +++ b/src/utils/tomahawkutils.h @@ -4,6 +4,7 @@ class QDir; class QDateTime; class QString; +class QPixmap; namespace TomahawkUtils { @@ -13,6 +14,8 @@ namespace TomahawkUtils QString timeToString( int seconds ); QString ageToString( const QDateTime& time ); QString filesizeToString( unsigned int size ); + + QPixmap createDragPixmap( int itemCount = 1 ); } #endif // TOMAHAWKUTILS_H From 05a919c16afbff7e2c67fae99def3bb671f6a340 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jan 2011 11:12:11 +0100 Subject: [PATCH 08/14] * A few more QueryLabel fixes / design changes. --- src/sourcetree/sourcetreeitemwidget.ui | 16 +++++------ src/utils/querylabel.cpp | 38 +++++++++++++------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/sourcetree/sourcetreeitemwidget.ui b/src/sourcetree/sourcetreeitemwidget.ui index 6e889e6a3..3db519633 100644 --- a/src/sourcetree/sourcetreeitemwidget.ui +++ b/src/sourcetree/sourcetreeitemwidget.ui @@ -7,7 +7,7 @@ 0 0 359 - 58 + 60 @@ -19,13 +19,13 @@ 0 - 58 + 60 16777215 - 58 + 60 @@ -196,16 +196,16 @@ QPushButton
imagebutton.h
- - ElidedLabel - QLabel -
elidedlabel.h
-
QueryLabel QLabel
querylabel.h
+ + ElidedLabel + QLabel +
elidedlabel.h
+
diff --git a/src/utils/querylabel.cpp b/src/utils/querylabel.cpp index 0dcdd39e3..9d1d917db 100644 --- a/src/utils/querylabel.cpp +++ b/src/utils/querylabel.cpp @@ -9,7 +9,7 @@ #include "tomahawkutils.h" #define BOXMARGIN 2 - +#define DASH " - " QueryLabel::QueryLabel( QWidget* parent, Qt::WindowFlags flags ) : QFrame( parent, flags ) @@ -165,7 +165,7 @@ QueryLabel::setResult( const Tomahawk::result_ptr& result ) if ( result.isNull() ) return; - setContentsMargins( BOXMARGIN, 0, BOXMARGIN, 0 ); + setContentsMargins( BOXMARGIN * 2, BOXMARGIN / 2, BOXMARGIN * 2, BOXMARGIN / 2); if ( m_result.isNull() || m_result.data() != result.data() ) { @@ -190,7 +190,7 @@ QueryLabel::setQuery( const Tomahawk::query_ptr& query ) if ( query.isNull() ) return; - setContentsMargins( BOXMARGIN, 0, BOXMARGIN, 0 ); + setContentsMargins( BOXMARGIN * 2, BOXMARGIN / 2, BOXMARGIN * 2, BOXMARGIN / 2 ); if ( m_query.isNull() || m_query.data() != query.data() ) { @@ -254,7 +254,7 @@ QSize QueryLabel::sizeHint() const { const QFontMetrics& fm = fontMetrics(); - QSize size( fm.width( text() ) + contentsMargins().left() * 2, fm.height() ); + QSize size( fm.width( text() ) + contentsMargins().left() * 2, fm.height() + contentsMargins().top() * 2 ); return size; } @@ -270,7 +270,7 @@ QueryLabel::minimumSizeHint() const default: { const QFontMetrics& fm = fontMetrics(); - QSize size( fm.width( "..." ), fm.height() ); + QSize size( fm.width( "..." ), fm.height() + contentsMargins().top() * 2 ); return size; } } @@ -291,7 +291,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) if ( elidedText == s && m_hoverArea.width() ) { - p.setPen( palette().highlight().color() ); + p.setPen( palette().shadow().color() ); p.setBrush( palette().highlight() ); p.drawRoundedRect( m_hoverArea, 4.0, 4.0 ); } @@ -305,7 +305,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) else { const QFontMetrics& fm = fontMetrics(); - int dashX = fm.width( " - " ); + int dashX = fm.width( DASH ); int artistX = m_type & Artist ? fm.width( artist() ) : 0; int albumX = m_type & Album ? fm.width( album() ) : 0; int trackX = m_type & Track ? fm.width( track() ) : 0; @@ -331,7 +331,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) if ( m_type & Artist ) { - p.drawText( r, align, " - " ); + p.drawText( r, align, DASH ); r.adjust( dashX, 0, 0, 0 ); } if ( m_hoverArea.width() && m_hoverArea.left() + contentsMargins().left() == r.left() ) @@ -350,7 +350,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) if ( m_type & Artist || m_type & Album ) { - p.drawText( r, align, " - " ); + p.drawText( r, align, DASH ); r.adjust( dashX, 0, 0, 0 ); } if ( m_hoverArea.width() && m_hoverArea.left() + contentsMargins().left() == r.left() ) @@ -419,7 +419,7 @@ QueryLabel::mouseMoveEvent( QMouseEvent* event ) } const QFontMetrics& fm = fontMetrics(); - int dashX = fm.width( " - " ); + int dashX = fm.width( DASH ); int artistX = m_type & Artist ? fm.width( artist() ) : 0; int albumX = m_type & Album ? fm.width( album() ) : 0; int trackX = m_type & Track ? fm.width( track() ) : 0; @@ -437,26 +437,26 @@ QueryLabel::mouseMoveEvent( QMouseEvent* event ) QRect hoverArea; if ( m_type & Artist && x < artistX ) { - hoverArea.setLeft( 0 ); - hoverArea.setRight( artistX + contentsMargins().left() ); + hoverArea.setLeft( 1 ); + hoverArea.setRight( artistX + contentsMargins().left() + 2 ); } else if ( m_type & Album && x < albumX ) { int spacing = ( m_type & Artist ) ? dashX : 0; - hoverArea.setLeft( artistX + spacing ); - hoverArea.setRight( albumX + spacing + contentsMargins().left() ); + hoverArea.setLeft( artistX + spacing + 1 ); + hoverArea.setRight( albumX + spacing + contentsMargins().left() + 2 ); } else if ( m_type & Track && x < trackX ) { int spacing = ( m_type & Album ) ? dashX : 0; - hoverArea.setLeft( albumX + spacing ); - hoverArea.setRight( trackX + contentsMargins().left() ); + hoverArea.setLeft( albumX + spacing + 1 ); + hoverArea.setRight( trackX + contentsMargins().left() + 2 ); } if ( hoverArea.width() ) { - hoverArea.setY( 0 ); - hoverArea.setHeight( height() - 1 ); + hoverArea.setY( 1 ); + hoverArea.setHeight( height() - 2 ); } if ( hoverArea != m_hoverArea ) { @@ -504,7 +504,7 @@ QueryLabel::smartAppend( QString& text, const QString& appendage ) const { QString s; if ( !text.isEmpty() ) - s = " - "; + s = DASH; text += s + appendage; return text; From c0852e88718272e74c27ff3ad029845cd3b4f00f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jan 2011 14:09:15 +0100 Subject: [PATCH 09/14] * Fixed a few margin / spacing issues across all platforms (well, hopefully). --- src/audiocontrols.cpp | 1 - src/audiocontrols.ui | 46 ++++++++++++++++++++++--- src/sourcetree/sourcetreeitemwidget.cpp | 12 +++++++ src/sourcetree/sourcetreeitemwidget.ui | 22 +++--------- src/tomahawkapp.cpp | 3 -- src/utils/querylabel.cpp | 45 ++++++++++++++++-------- src/utils/querylabel.h | 1 + 7 files changed, 89 insertions(+), 41 deletions(-) diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp index f49bd9bb2..deb883d09 100644 --- a/src/audiocontrols.cpp +++ b/src/audiocontrols.cpp @@ -23,7 +23,6 @@ AudioControls::AudioControls( QWidget* parent ) ui->setupUi( this ); ui->buttonAreaLayout->setSpacing( 2 ); - ui->trackLabelLayout->setSpacing( 3 ); QFont font( ui->artistTrackLabel->font() ); font.setPixelSize( 12 ); diff --git a/src/audiocontrols.ui b/src/audiocontrols.ui index 2a90a1ee9..033f51bef 100644 --- a/src/audiocontrols.ui +++ b/src/audiocontrols.ui @@ -168,7 +168,7 @@ - 8 + 4 6 @@ -177,14 +177,35 @@ 2 - + + + 0 + + + 0 + 0 + + 0 + + + + 0 + 0 + + + + + 0 + 16 + + PointingHandCursor @@ -195,6 +216,18 @@ + + + 0 + 0 + + + + + 0 + 16 + + PointingHandCursor @@ -212,8 +245,8 @@
- 40 - 20 + 4 + 8 @@ -246,13 +279,16 @@ 20 - 40 + 4
+ + 4 + diff --git a/src/sourcetree/sourcetreeitemwidget.cpp b/src/sourcetree/sourcetreeitemwidget.cpp index 4ad1419c8..d175d6766 100644 --- a/src/sourcetree/sourcetreeitemwidget.cpp +++ b/src/sourcetree/sourcetreeitemwidget.cpp @@ -23,6 +23,14 @@ SourceTreeItemWidget::SourceTreeItemWidget( const source_ptr& source, QWidget* p ui->verticalLayout->setSpacing( 3 ); ui->activityLabel->setType( QueryLabel::ArtistAndTrack ); + QFont font = ui->nameLabel->font(); +// font.setPointSize( font.pointSize() - 1 ); + ui->nameLabel->setFont( font ); + + font.setPointSize( font.pointSize() - 1 ); + ui->infoLabel->setFont( font ); + ui->activityLabel->setFont( font ); + QString displayname; if ( source.isNull() ) { @@ -59,6 +67,10 @@ SourceTreeItemWidget::SourceTreeItemWidget( const source_ptr& source, QWidget* p ui->infoLabel->setForegroundRole( QPalette::Dark ); ui->activityLabel->setForegroundRole( QPalette::Dark ); + ui->nameLabel->setContentsMargins( 4, 0, 0, 0 ); + ui->infoLabel->setContentsMargins( 4, 0, 0, 0 ); + ui->activityLabel->setContentsMargins( 4, 0, 0, 0 ); + connect( ui->onOffButton, SIGNAL( clicked() ), SIGNAL( clicked() ) ); connect( ui->infoButton, SIGNAL( clicked() ), SLOT( onInfoButtonClicked() ) ); diff --git a/src/sourcetree/sourcetreeitemwidget.ui b/src/sourcetree/sourcetreeitemwidget.ui index 3db519633..17457afa9 100644 --- a/src/sourcetree/sourcetreeitemwidget.ui +++ b/src/sourcetree/sourcetreeitemwidget.ui @@ -7,27 +7,15 @@ 0 0 359 - 60 + 56 - + 0 0 - - - 0 - 60 - - - - - 16777215 - 60 - - Form @@ -92,7 +80,7 @@ - 4 + 2 20 @@ -101,10 +89,10 @@ - 4 + 2 - 4 + 2 diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index bfa94e4a4..ecc3c1777 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -354,9 +354,6 @@ TomahawkApp::startServent() qDebug() << "Failed to start listening with servent"; exit( 1 ); } - - //QString key = m_servent.createConnectionKey(); - //qDebug() << "Generated an offer key: " << key; } diff --git a/src/utils/querylabel.cpp b/src/utils/querylabel.cpp index 9d1d917db..d5b0b5314 100644 --- a/src/utils/querylabel.cpp +++ b/src/utils/querylabel.cpp @@ -146,7 +146,7 @@ QueryLabel::track() const void QueryLabel::setText( const QString& text ) { - setContentsMargins( 0, 0, 0, 0 ); + setContentsMargins( m_textMargins ); m_result.clear(); m_query.clear(); @@ -165,6 +165,9 @@ QueryLabel::setResult( const Tomahawk::result_ptr& result ) if ( result.isNull() ) return; + if ( !m_text.isEmpty() && contentsMargins().left() != 0 ) // FIXME: hacky + m_textMargins = contentsMargins(); + setContentsMargins( BOXMARGIN * 2, BOXMARGIN / 2, BOXMARGIN * 2, BOXMARGIN / 2); if ( m_result.isNull() || m_result.data() != result.data() ) @@ -291,7 +294,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) if ( elidedText == s && m_hoverArea.width() ) { - p.setPen( palette().shadow().color() ); + p.setPen( palette().mid().color() ); p.setBrush( palette().highlight() ); p.drawRoundedRect( m_hoverArea, 4.0, 4.0 ); } @@ -299,7 +302,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) if ( elidedText != s || ( m_result.isNull() && m_query.isNull() ) ) { p.setBrush( palette().window() ); - p.setPen( palette().text().color() ); + p.setPen( palette().color( foregroundRole() ) ); p.drawText( r, align, elidedText ); } else @@ -313,7 +316,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) if ( m_type & Artist ) { p.setBrush( palette().window() ); - p.setPen( palette().text().color() ); + p.setPen( palette().color( foregroundRole() ) ); if ( m_hoverArea.width() && m_hoverArea.left() + contentsMargins().left() == r.left() ) { @@ -327,7 +330,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) if ( m_type & Album ) { p.setBrush( palette().window() ); - p.setPen( palette().text().color() ); + p.setPen( palette().color( foregroundRole() ) ); if ( m_type & Artist ) { @@ -346,14 +349,14 @@ QueryLabel::paintEvent( QPaintEvent* event ) if ( m_type & Track ) { p.setBrush( palette().window() ); - p.setPen( palette().text().color() ); + p.setPen( palette().color( foregroundRole() ) ); if ( m_type & Artist || m_type & Album ) { p.drawText( r, align, DASH ); r.adjust( dashX, 0, 0, 0 ); } - if ( m_hoverArea.width() && m_hoverArea.left() + contentsMargins().left() == r.left() ) + if ( m_hoverArea.width() && m_hoverArea.left() + contentsMargins().left() == r.left() ) { p.setPen( palette().highlightedText().color() ); p.setBrush( palette().highlight() ); @@ -418,39 +421,51 @@ QueryLabel::mouseMoveEvent( QMouseEvent* event ) return; } + if ( m_query.isNull() && m_result.isNull() ) + { + m_hoverArea = QRect(); + return; + } + const QFontMetrics& fm = fontMetrics(); int dashX = fm.width( DASH ); int artistX = m_type & Artist ? fm.width( artist() ) : 0; int albumX = m_type & Album ? fm.width( album() ) : 0; int trackX = m_type & Track ? fm.width( track() ) : 0; + if ( m_type & Track ) + { + trackX += contentsMargins().left(); + } if ( m_type & Album ) { trackX += albumX + dashX; + albumX += contentsMargins().left(); } if ( m_type & Artist ) { albumX += artistX + dashX; trackX += artistX + dashX; + artistX += contentsMargins().left(); } QRect hoverArea; if ( m_type & Artist && x < artistX ) { - hoverArea.setLeft( 1 ); - hoverArea.setRight( artistX + contentsMargins().left() + 2 ); + hoverArea.setLeft( 0 ); + hoverArea.setRight( artistX + contentsMargins().left() ); } - else if ( m_type & Album && x < albumX ) + else if ( m_type & Album && x < albumX && x > artistX ) { int spacing = ( m_type & Artist ) ? dashX : 0; - hoverArea.setLeft( artistX + spacing + 1 ); - hoverArea.setRight( albumX + spacing + contentsMargins().left() + 2 ); + hoverArea.setLeft( artistX + spacing ); + hoverArea.setRight( albumX + spacing + contentsMargins().left() ); } - else if ( m_type & Track && x < trackX ) + else if ( m_type & Track && x < trackX && x > albumX ) { int spacing = ( m_type & Album ) ? dashX : 0; - hoverArea.setLeft( albumX + spacing + 1 ); - hoverArea.setRight( trackX + contentsMargins().left() + 2 ); + hoverArea.setLeft( albumX + spacing ); + hoverArea.setRight( trackX + contentsMargins().left() ); } if ( hoverArea.width() ) diff --git a/src/utils/querylabel.h b/src/utils/querylabel.h index 3d16e3ced..a9974966c 100644 --- a/src/utils/querylabel.h +++ b/src/utils/querylabel.h @@ -88,6 +88,7 @@ private: QRect m_hoverArea; QPoint m_dragPos; + QMargins m_textMargins; }; #endif // QUERYLABEL_H From b95d8a6b93b8149a1452264ef0f37f882e57e1f9 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jan 2011 15:05:50 +0100 Subject: [PATCH 10/14] * Fixed missing return statement. --- src/libtomahawk/network/bufferiodevice.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libtomahawk/network/bufferiodevice.cpp b/src/libtomahawk/network/bufferiodevice.cpp index 2bea719ef..c3be04748 100644 --- a/src/libtomahawk/network/bufferiodevice.cpp +++ b/src/libtomahawk/network/bufferiodevice.cpp @@ -223,8 +223,7 @@ BufferIODevice::isBlockEmpty( int block ) const if ( block >= m_buffer.count() ) return true; - if ( m_buffer.at( block ).isEmpty() ) - return true; + return m_buffer.at( block ).isEmpty(); } From e828972b952008cf3f60d2f26d8500824369c2a7 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jan 2011 17:23:52 +0100 Subject: [PATCH 11/14] * Port-Forwards are now being setuped in a separate thread to avoid blocking the GUI. --- include/tomahawk/tomahawkapp.h | 4 +- src/libtomahawk/CMakeLists.txt | 2 + src/libtomahawk/network/portfwdthread.cpp | 89 +++++++++++++++++++++++ src/libtomahawk/network/portfwdthread.h | 32 ++++++++ src/libtomahawk/network/servent.cpp | 83 ++++++--------------- src/libtomahawk/network/servent.h | 8 +- src/tomahawkapp.cpp | 33 +++++---- 7 files changed, 170 insertions(+), 81 deletions(-) create mode 100644 src/libtomahawk/network/portfwdthread.cpp create mode 100644 src/libtomahawk/network/portfwdthread.h diff --git a/include/tomahawk/tomahawkapp.h b/include/tomahawk/tomahawkapp.h index 7be7cf29e..e048778dc 100644 --- a/include/tomahawk/tomahawkapp.h +++ b/include/tomahawk/tomahawkapp.h @@ -77,13 +77,15 @@ public: signals: void settingsChanged(); +private slots: + void setupSIP(); + private: void initLocalCollection(); void loadPlugins(); void registerMetaTypes(); void startServent(); void setupDatabase(); - void setupSIP(); void setupPipeline(); void startHTTP(); diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 5341f6d8a..7a75ab38d 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -29,6 +29,7 @@ set( libSources network/filetransferconnection.cpp network/dbsyncconnection.cpp network/remotecollection.cpp + network/portfwdthread.cpp database/fuzzyindex.cpp database/databaseworker.cpp @@ -122,6 +123,7 @@ set( libHeaders network/servent.h network/connection.h network/controlconnection.h + network/portfwdthread.h ) include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. diff --git a/src/libtomahawk/network/portfwdthread.cpp b/src/libtomahawk/network/portfwdthread.cpp new file mode 100644 index 000000000..4da5c8a35 --- /dev/null +++ b/src/libtomahawk/network/portfwdthread.cpp @@ -0,0 +1,89 @@ +#include "portfwdthread.h" + +#include +#include +#include +#include +#include + +#include "portfwd/portfwd.h" + + +PortFwdThread::PortFwdThread( unsigned int port ) + : QThread() + , m_externalPort( 0 ) + , m_port( port ) +{ + moveToThread( this ); + start(); +} + + +PortFwdThread::~PortFwdThread() +{ + qDebug() << Q_FUNC_INFO << "waiting for event loop to finish..."; + quit(); + wait( 2500 ); + + delete m_portfwd; +} + + +void +PortFwdThread::work() +{ + qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); + m_portfwd = new Portfwd(); + + // try and pick an available port: + if( m_portfwd->init( 2000 ) ) + { + int tryport = m_port; + + // last.fm office firewall policy hack + // (corp. firewall allows outgoing connections to this port, + // so listen on this if you want lastfmers to connect to you) + if( qApp->arguments().contains( "--porthack" ) ) + { + tryport = 3389; + m_portfwd->remove( tryport ); + } + + for( int r = 0; r < 5; ++r ) + { + qDebug() << "Trying to setup portfwd on" << tryport; + if( m_portfwd->add( tryport, m_port ) ) + { + QString pubip = QString( m_portfwd->external_ip().c_str() ); + m_externalAddress = QHostAddress( pubip ); + m_externalPort = tryport; + qDebug() << "External servent address detected as" << pubip << ":" << m_externalPort; + qDebug() << "Max upstream " << m_portfwd->max_upstream_bps() << "bps"; + qDebug() << "Max downstream" << m_portfwd->max_downstream_bps() << "bps"; + break; + } + tryport = qAbs( 10000 + 50000 * (float)qrand() / RAND_MAX ); + } + } + else + qDebug() << "No UPNP Gateway device found?"; + + if( !m_externalPort ) + qDebug() << "Could not setup fwd for port:" << m_port; + + emit externalAddressDetected( m_externalAddress, m_externalPort ); +} + + +void +PortFwdThread::run() +{ + QTimer::singleShot( 0, this, SLOT( work() ) ); + exec(); + + if ( m_externalPort ) + { + qDebug() << "Unregistering port fwd"; + m_portfwd->remove( m_externalPort ); + } +} diff --git a/src/libtomahawk/network/portfwdthread.h b/src/libtomahawk/network/portfwdthread.h new file mode 100644 index 000000000..d18c699e4 --- /dev/null +++ b/src/libtomahawk/network/portfwdthread.h @@ -0,0 +1,32 @@ +#ifndef PORTFWDTHREAD_H +#define PORTFWDTHREAD_H + +#include +#include +#include + +class Portfwd; + +class PortFwdThread : public QThread +{ +Q_OBJECT + +public: + explicit PortFwdThread( unsigned int port ); + ~PortFwdThread(); + +signals: + void externalAddressDetected( QHostAddress ha, unsigned int port ); + +private slots: + void work(); + +private: + void run(); + + Portfwd* m_portfwd; + QHostAddress m_externalAddress; + unsigned int m_externalPort, m_port; +}; + +#endif // PORTFWDTHREAD_H diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index 73cc749db..3c53be79c 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -18,7 +17,7 @@ #include "filetransferconnection.h" #include "sourcelist.h" -#include "portfwd/portfwd.h" +#include "portfwdthread.h" using namespace Tomahawk; @@ -36,12 +35,9 @@ Servent::Servent( QObject* parent ) : QTcpServer( parent ) , m_port( 0 ) , m_externalPort( 0 ) - , pf( new Portfwd() ) { s_instance = this; - qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); - { boost::function(result_ptr)> fac = boost::bind( &Servent::localFileIODeviceFactory, this, _1 ); @@ -64,11 +60,7 @@ Servent::Servent( QObject* parent ) Servent::~Servent() { - if( m_externalPort ) - { - qDebug() << "Unregistering port fwd"; - pf->remove( m_externalPort ); - } + delete m_portfwd; } @@ -88,52 +80,6 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) qDebug() << "Servent listening on port" << m_port << " servent thread:" << thread(); } - // TODO check if we have a public/internet IP on this machine directly - // FIXME the portfwd stuff is blocking, so we hang here for 2 secs atm - if( upnp ) - { - // try and pick an available port: - if( pf->init( 2000 ) ) - { - int tryport = m_port; - - // last.fm office firewall policy hack - // (corp. firewall allows outgoing connections to this port, - // so listen on this if you want lastfmers to connect to you) - if( qApp->arguments().contains( "--porthack" ) ) - { - tryport = 3389; - pf->remove( tryport ); - } - - for( int r=0; r<5; ++r ) - { - qDebug() << "Trying to setup portfwd on" << tryport; - if( pf->add( tryport, m_port ) ) - { - QString pubip = QString( pf->external_ip().c_str() ); - m_externalAddress = QHostAddress( pubip ); - m_externalPort = tryport; - qDebug() << "External servent address detected as" << pubip << ":" << m_externalPort; - qDebug() << "Max upstream " << pf->max_upstream_bps() << "bps"; - qDebug() << "Max downstream" << pf->max_downstream_bps() << "bps"; - break; - } - tryport = 10000 + 50000 * (float)qrand()/RAND_MAX; - } - if( !m_externalPort ) - { - qDebug() << "Could not setup fwd for port:" << m_port; - } - } - else qDebug() << "No UPNP Gateway device found?"; - } - - if( m_externalPort == 0 ) - { - qDebug() << "No external access, LAN and outbound connections only!"; - } - // --lanhack means to advertise your LAN IP over jabber as if it were externallyVisible if( qApp->arguments().contains( "--lanhack" ) ) { @@ -149,6 +95,13 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) break; } } + else if( upnp ) + { + // TODO check if we have a public/internet IP on this machine directly + m_portfwd = new PortFwdThread( m_port ); + connect( m_portfwd, SIGNAL( externalAddressDetected( QHostAddress, unsigned int ) ), + SLOT( setExternalAddress( QHostAddress, unsigned int ) ) ); + } return true; } @@ -160,7 +113,7 @@ Servent::createConnectionKey( const QString& name ) Q_ASSERT( this->thread() == QThread::currentThread() ); QString key = uuid(); - ControlConnection * cc = new ControlConnection( this ); + ControlConnection* cc = new ControlConnection( this ); cc->setName( name.isEmpty() ? QString( "KEY(%1)" ).arg( key ) : name ); registerOffer( key, cc ); return key; @@ -168,10 +121,17 @@ Servent::createConnectionKey( const QString& name ) void -Servent::setExternalAddress( QHostAddress ha, int port ) +Servent::setExternalAddress( QHostAddress ha, unsigned int port ) { m_externalAddress = ha; m_externalPort = port; + + if( m_externalPort == 0 ) + { + qDebug() << "No external access, LAN and outbound connections only!"; + } + + emit ready(); } @@ -217,8 +177,10 @@ void Servent::incomingConnection( int sd ) { Q_ASSERT( this->thread() == QThread::currentThread() ); + QTcpSocketExtra* sock = new QTcpSocketExtra; qDebug() << Q_FUNC_INFO << "Accepting connection, sock" << sock; + sock->moveToThread( thread() ); sock->_disowned = false; sock->_outbound = false; @@ -381,7 +343,6 @@ Servent::socketConnected() qDebug() << "Servent::SocketConnected" << thread() << "socket:" << sock; Connection* conn = sock->_conn; - handoverSocket( conn, sock ); } @@ -421,7 +382,7 @@ Servent::socketError( QAbstractSocket::SocketError e ) Connection* conn = sock->_conn; qDebug() << "Servent::SocketError:" << e << conn->id() << conn->name(); - if(!sock->_disowned) + if( !sock->_disowned ) { // connection will delete if we already transferred ownership, otherwise: sock->deleteLater(); @@ -749,7 +710,7 @@ QSharedPointer Servent::localFileIODeviceFactory( const Tomahawk::result_ptr& result ) { // ignore "file://" at front of url - QFile * io = new QFile( result->url().mid( QString( "file://" ).length() ) ); + QFile* io = new QFile( result->url().mid( QString( "file://" ).length() ) ); if ( io ) io->open( QIODevice::ReadOnly ); diff --git a/src/libtomahawk/network/servent.h b/src/libtomahawk/network/servent.h index f0e2884bb..02d2843be 100644 --- a/src/libtomahawk/network/servent.h +++ b/src/libtomahawk/network/servent.h @@ -33,7 +33,7 @@ class ControlConnection; class FileTransferConnection; class ProxyConnection; class RemoteCollectionConnection; -class Portfwd; +class PortFwdThread; // this is used to hold a bit of state, so when a connected signal is emitted // from a socket, we can associate it with a Connection object etc. @@ -90,7 +90,6 @@ public: void connectToPeer( const QString& ha, int port, const QString &key, Connection* conn ); void reverseOfferRequest( ControlConnection* orig_conn, const QString& key, const QString& theirkey ); - void setExternalAddress( QHostAddress ha, int port ); bool visibleExternally() const { return m_externalPort > 0 && !m_externalAddress.isNull(); } QHostAddress externalAddress() const { return m_externalAddress; } int externalPort() const { return m_externalPort; } @@ -111,11 +110,14 @@ public: signals: void fileTransferStarted( FileTransferConnection* ); void fileTransferFinished( FileTransferConnection* ); + void ready(); protected: void incomingConnection( int sd ); public slots: + void setExternalAddress( QHostAddress ha, unsigned int port ); + void socketError( QAbstractSocket::SocketError ); void createParallelConnection( Connection* orig_conn, Connection* new_conn, const QString& key ); @@ -145,9 +147,9 @@ private: QList< FileTransferConnection* > m_ftsessions; QMutex m_ftsession_mut; - Portfwd* pf; QMap< QString,boost::function(Tomahawk::result_ptr)> > m_iofactories; + PortFwdThread* m_portfwd; static Servent* s_instance; }; diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index ecc3c1777..349f90821 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -108,10 +108,6 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) { qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); - new Pipeline( this ); - new SourceList( this ); - m_servent = new Servent( this ); - #ifdef TOMAHAWK_HEADLESS m_headless = true; #else @@ -134,6 +130,13 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) new TomahawkSettings( this ); m_audioEngine = new AudioEngine; + + new Pipeline( this ); + new SourceList( this ); + + m_servent = new Servent( this ); + connect( m_servent, SIGNAL( ready() ), SLOT( setupSIP() ) ); + setupDatabase(); #ifndef NO_LIBLASTFM @@ -160,8 +163,8 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) { qDebug() << "Setting proxy to saved values"; m_proxy = new QNetworkProxy( static_cast(TomahawkSettings::instance()->proxyType()), TomahawkSettings::instance()->proxyHost(), TomahawkSettings::instance()->proxyPort(), TomahawkSettings::instance()->proxyUsername(), TomahawkSettings::instance()->proxyPassword() ); - qDebug() << "Proxy type = " << QString::number( static_cast(m_proxy->type()) ); - qDebug() << "Proxy host = " << m_proxy->hostName(); + qDebug() << "Proxy type =" << QString::number( static_cast(m_proxy->type()) ); + qDebug() << "Proxy host =" << m_proxy->hostName(); QNetworkAccessManager* nam = TomahawkApp::instance()->nam(); nam->setProxy( *m_proxy ); } @@ -170,14 +173,9 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) QNetworkProxy::setApplicationProxy( *m_proxy ); + m_sipHandler = new SipHandler( this ); m_infoSystem = new Tomahawk::InfoSystem::InfoSystem( this ); - if( !arguments().contains("--nojabber") ) - { - setupSIP(); - m_xmppBot = new XMPPBot( this ); - } - #ifndef TOMAHAWK_HEADLESS if ( !m_headless ) { @@ -196,8 +194,6 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) if( arguments().contains( "--http" ) || TomahawkSettings::instance()->value( "network/http", true ).toBool() ) startHTTP(); - m_sipHandler->connect(); - #ifndef TOMAHAWK_HEADLESS if ( !TomahawkSettings::instance()->hasScannerPath() ) { @@ -258,6 +254,7 @@ TomahawkApp::registerMetaTypes() qRegisterMetaType< QTcpSocket* >("QTcpSocket*"); qRegisterMetaType< QSharedPointer >("QSharedPointer"); qRegisterMetaType< QFileInfo >("QFileInfo"); + qRegisterMetaType< QHostAddress >("QHostAddress"); qRegisterMetaType< QMap >("QMap"); qRegisterMetaType< QMap< QString, plentry_ptr > >("QMap< QString, plentry_ptr >"); qRegisterMetaType< QHash< QString, QMap > >("QHash< QString, QMap >"); @@ -399,8 +396,12 @@ TomahawkApp::setupSIP() { qDebug() << Q_FUNC_INFO; - m_sipHandler = new SipHandler( this ); + if( !arguments().contains( "--nojabber" ) ) + { + m_xmppBot = new XMPPBot( this ); -// m_sipHandler->setProxy( m_proxy ); + m_sipHandler->connect(); + // m_sipHandler->setProxy( m_proxy ); + } } From ff512d82898b30f97c392fa3fc047dd8037d7333 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jan 2011 17:28:14 +0100 Subject: [PATCH 12/14] * Fixed Servent not emitting ready() if it's not meant to setup UPNP. --- src/libtomahawk/network/servent.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index 3c53be79c..732da87f9 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -102,6 +102,10 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) connect( m_portfwd, SIGNAL( externalAddressDetected( QHostAddress, unsigned int ) ), SLOT( setExternalAddress( QHostAddress, unsigned int ) ) ); } + else + { + emit ready(); + } return true; } From 5beda42d571c5b5c091d681d26d3a6136a203fb9 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jan 2011 17:31:34 +0100 Subject: [PATCH 13/14] * Set initial connection state. --- src/tomahawkwindow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 426ea6793..622c35795 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -177,6 +177,9 @@ TomahawkWindow::setupSignals() connect( APP->sipHandler(), SIGNAL( connected() ), SLOT( onSipConnected() ) ); connect( APP->sipHandler(), SIGNAL( disconnected() ), SLOT( onSipDisconnected() ) ); connect( APP->sipHandler(), SIGNAL( authError() ), SLOT( onSipError() ) ); + + // set initial connection state + onSipDisconnected(); } From a4e140972e4b213bc954eaa7961125695c423070 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jan 2011 17:34:16 +0100 Subject: [PATCH 14/14] * Initialize m_portfwd so we don't crash deleting a bogus pointer. --- src/libtomahawk/network/servent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index 732da87f9..dbfbdc236 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -35,6 +35,7 @@ Servent::Servent( QObject* parent ) : QTcpServer( parent ) , m_port( 0 ) , m_externalPort( 0 ) + , m_portfwd( 0 ) { s_instance = this;