mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-17 19:37:09 +02:00
Merge branch 'master' into dynamic
Conflicts: src/libtomahawk/result.h
This commit is contained in:
@@ -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();
|
||||
|
||||
|
@@ -54,6 +54,7 @@ SET( tomahawkSources ${tomahawkSources}
|
||||
SET( tomahawkSourcesGui ${tomahawkSourcesGui}
|
||||
xspfloader.cpp
|
||||
|
||||
utils/querylabel.cpp
|
||||
utils/elidedlabel.cpp
|
||||
utils/imagebutton.cpp
|
||||
utils/progresstreeview.cpp
|
||||
@@ -137,6 +138,7 @@ SET( tomahawkHeaders ${tomahawkHeaders}
|
||||
SET( tomahawkHeadersGui ${tomahawkHeadersGui}
|
||||
xspfloader.h
|
||||
|
||||
utils/querylabel.h
|
||||
utils/elidedlabel.h
|
||||
utils/animatedcounterlabel.h
|
||||
utils/imagebutton.h
|
||||
|
@@ -23,17 +23,17 @@ AudioControls::AudioControls( QWidget* parent )
|
||||
ui->setupUi( this );
|
||||
|
||||
ui->buttonAreaLayout->setSpacing( 2 );
|
||||
ui->trackLabelLayout->setSpacing( 3 );
|
||||
|
||||
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 +229,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 );
|
||||
|
||||
|
@@ -168,7 +168,7 @@
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>8</number>
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
@@ -177,14 +177,35 @@
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0,0">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="trackLabelLayout">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ElidedLabel" name="artistTrackLabel">
|
||||
<widget class="QueryLabel" name="artistTrackLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
@@ -194,7 +215,19 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ElidedLabel" name="albumLabel">
|
||||
<widget class="QueryLabel" name="albumLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
@@ -212,8 +245,8 @@
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
<width>4</width>
|
||||
<height>8</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
@@ -246,13 +279,16 @@
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
<height>4</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="timeLabel">
|
||||
<property name="text">
|
||||
@@ -459,9 +495,9 @@
|
||||
<header>imagebutton.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ElidedLabel</class>
|
||||
<class>QueryLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header location="global">elidedlabel.h</header>
|
||||
<header>querylabel.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
|
@@ -29,6 +29,7 @@ set( libSources
|
||||
network/filetransferconnection.cpp
|
||||
network/dbsyncconnection.cpp
|
||||
network/remotecollection.cpp
|
||||
network/portfwdthread.cpp
|
||||
|
||||
database/fuzzyindex.cpp
|
||||
database/databaseworker.cpp
|
||||
@@ -146,6 +147,7 @@ set( libHeaders
|
||||
network/servent.h
|
||||
network/connection.h
|
||||
network/controlconnection.h
|
||||
network/portfwdthread.h
|
||||
)
|
||||
|
||||
include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ..
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -476,6 +476,7 @@ DatabaseImpl::result( const QString& url )
|
||||
{
|
||||
TomahawkSqlQuery query = newquery();
|
||||
Tomahawk::source_ptr s;
|
||||
QVariantMap m;
|
||||
QString fileUrl;
|
||||
|
||||
if ( url.contains( "servent://" ) )
|
||||
@@ -483,6 +484,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://" ) )
|
||||
{
|
||||
@@ -517,7 +521,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();
|
||||
|
@@ -6,7 +6,7 @@
|
||||
#include <QSqlError>
|
||||
#include <QTime>
|
||||
|
||||
#define TOMAHAWK_QUERY_THRESHOLD 20
|
||||
#define TOMAHAWK_QUERY_THRESHOLD 60
|
||||
|
||||
class TomahawkSqlQuery : public QSqlQuery
|
||||
{
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
: QSqlQuery()
|
||||
{}
|
||||
|
||||
TomahawkSqlQuery( QSqlDatabase db )
|
||||
TomahawkSqlQuery( const QSqlDatabase& db )
|
||||
: QSqlQuery( db )
|
||||
{}
|
||||
|
||||
|
@@ -1,11 +1,18 @@
|
||||
#include <QDebug>
|
||||
#include "bufferiodevice.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
|
||||
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,102 @@ 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;
|
||||
|
||||
return m_buffer.at( block ).isEmpty();
|
||||
}
|
||||
|
||||
|
||||
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 );
|
||||
}
|
||||
|
@@ -4,35 +4,58 @@
|
||||
#include <QIODevice>
|
||||
#include <QMutexLocker>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
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<QByteArray> m_buffer;
|
||||
mutable QMutex m_mut; //const methods need to lock
|
||||
unsigned int m_size, m_received;
|
||||
|
||||
unsigned int m_pos;
|
||||
};
|
||||
|
||||
#endif // BUFFERIODEVICE_H
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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 );
|
||||
@@ -176,21 +175,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();
|
||||
@@ -199,26 +218,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
|
||||
@@ -231,3 +250,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 ) );
|
||||
}
|
||||
|
@@ -55,6 +55,8 @@ private slots:
|
||||
void sendSome();
|
||||
void showStats( qint64 tx, qint64 rx );
|
||||
|
||||
void onBlockRequest( int pos );
|
||||
|
||||
private:
|
||||
QSharedPointer<QIODevice> m_iodev;
|
||||
ControlConnection* m_cc;
|
||||
@@ -62,6 +64,8 @@ private:
|
||||
Type m_type;
|
||||
QSharedPointer<QIODevice> m_readdev;
|
||||
|
||||
int m_curBlock;
|
||||
|
||||
int m_badded, m_bsent;
|
||||
bool m_allok; // got last msg ok, transfer complete?
|
||||
|
||||
|
89
src/libtomahawk/network/portfwdthread.cpp
Normal file
89
src/libtomahawk/network/portfwdthread.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "portfwdthread.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
|
||||
#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 );
|
||||
}
|
||||
}
|
32
src/libtomahawk/network/portfwdthread.h
Normal file
32
src/libtomahawk/network/portfwdthread.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef PORTFWDTHREAD_H
|
||||
#define PORTFWDTHREAD_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QHostAddress>
|
||||
|
||||
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
|
@@ -4,7 +4,6 @@
|
||||
#include <QMutexLocker>
|
||||
#include <QNetworkInterface>
|
||||
#include <QFile>
|
||||
#include <QTime>
|
||||
#include <QThread>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
@@ -18,7 +17,7 @@
|
||||
#include "filetransferconnection.h"
|
||||
#include "sourcelist.h"
|
||||
|
||||
#include "portfwd/portfwd.h"
|
||||
#include "portfwdthread.h"
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@@ -36,12 +35,10 @@ Servent::Servent( QObject* parent )
|
||||
: QTcpServer( parent )
|
||||
, m_port( 0 )
|
||||
, m_externalPort( 0 )
|
||||
, pf( new Portfwd() )
|
||||
, m_portfwd( 0 )
|
||||
{
|
||||
s_instance = this;
|
||||
|
||||
qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) );
|
||||
|
||||
{
|
||||
boost::function<QSharedPointer<QIODevice>(result_ptr)> fac =
|
||||
boost::bind( &Servent::localFileIODeviceFactory, this, _1 );
|
||||
@@ -64,11 +61,7 @@ Servent::Servent( QObject* parent )
|
||||
|
||||
Servent::~Servent()
|
||||
{
|
||||
if( m_externalPort )
|
||||
{
|
||||
qDebug() << "Unregistering port fwd";
|
||||
pf->remove( m_externalPort );
|
||||
}
|
||||
delete m_portfwd;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,52 +81,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 +96,17 @@ 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 ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
emit ready();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -160,7 +118,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 +126,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 +182,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 +348,6 @@ Servent::socketConnected()
|
||||
qDebug() << "Servent::SocketConnected" << thread() << "socket:" << sock;
|
||||
|
||||
Connection* conn = sock->_conn;
|
||||
|
||||
handoverSocket( conn, sock );
|
||||
}
|
||||
|
||||
@@ -421,7 +387,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 +715,7 @@ QSharedPointer<QIODevice>
|
||||
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 );
|
||||
|
||||
|
@@ -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<QSharedPointer<QIODevice>(Tomahawk::result_ptr)> > m_iofactories;
|
||||
|
||||
PortFwdThread* m_portfwd;
|
||||
static Servent* s_instance;
|
||||
};
|
||||
|
||||
|
@@ -5,6 +5,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();
|
||||
}
|
||||
|
@@ -77,6 +77,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()
|
||||
{
|
||||
|
@@ -23,6 +23,8 @@ public:
|
||||
virtual ~Result();
|
||||
|
||||
QVariant toVariant() const { return m_v; }
|
||||
QString toString() const;
|
||||
Tomahawk::query_ptr toQuery() const;
|
||||
|
||||
float score() const;
|
||||
RID id() const;
|
||||
@@ -45,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();
|
||||
|
@@ -240,7 +240,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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -21,6 +21,15 @@ SourceTreeItemWidget::SourceTreeItemWidget( const source_ptr& source, QWidget* p
|
||||
|
||||
ui->setupUi( this );
|
||||
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() )
|
||||
@@ -58,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() ) );
|
||||
|
||||
@@ -132,7 +145,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 );
|
||||
}
|
||||
|
||||
|
||||
|
@@ -7,27 +7,15 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>359</width>
|
||||
<height>58</height>
|
||||
<height>56</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>58</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>58</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
@@ -92,7 +80,7 @@
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>4</width>
|
||||
<width>2</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -101,10 +89,10 @@
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>4</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ElidedLabel" name="nameLabel">
|
||||
@@ -127,7 +115,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ElidedLabel" name="activityLabel">
|
||||
<widget class="QueryLabel" name="activityLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
@@ -196,6 +184,11 @@
|
||||
<extends>QPushButton</extends>
|
||||
<header>imagebutton.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QueryLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>querylabel.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ElidedLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
|
@@ -110,10 +110,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
|
||||
@@ -136,6 +132,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();
|
||||
|
||||
GeneratorFactory::registerFactory( "echonest", new EchonestFactory );
|
||||
@@ -164,8 +167,8 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] )
|
||||
{
|
||||
qDebug() << "Setting proxy to saved values";
|
||||
m_proxy = new QNetworkProxy( static_cast<QNetworkProxy::ProxyType>(TomahawkSettings::instance()->proxyType()), TomahawkSettings::instance()->proxyHost(), TomahawkSettings::instance()->proxyPort(), TomahawkSettings::instance()->proxyUsername(), TomahawkSettings::instance()->proxyPassword() );
|
||||
qDebug() << "Proxy type = " << QString::number( static_cast<int>(m_proxy->type()) );
|
||||
qDebug() << "Proxy host = " << m_proxy->hostName();
|
||||
qDebug() << "Proxy type =" << QString::number( static_cast<int>(m_proxy->type()) );
|
||||
qDebug() << "Proxy host =" << m_proxy->hostName();
|
||||
QNetworkAccessManager* nam = TomahawkApp::instance()->nam();
|
||||
nam->setProxy( *m_proxy );
|
||||
}
|
||||
@@ -174,14 +177,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 )
|
||||
{
|
||||
@@ -200,8 +198,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() )
|
||||
{
|
||||
@@ -262,6 +258,7 @@ TomahawkApp::registerMetaTypes()
|
||||
qRegisterMetaType< QTcpSocket* >("QTcpSocket*");
|
||||
qRegisterMetaType< QSharedPointer<QIODevice> >("QSharedPointer<QIODevice>");
|
||||
qRegisterMetaType< QFileInfo >("QFileInfo");
|
||||
qRegisterMetaType< QHostAddress >("QHostAddress");
|
||||
qRegisterMetaType< QMap<QString, unsigned int> >("QMap<QString, unsigned int>");
|
||||
qRegisterMetaType< QMap< QString, plentry_ptr > >("QMap< QString, plentry_ptr >");
|
||||
qRegisterMetaType< QHash< QString, QMap<quint32, quint16> > >("QHash< QString, QMap<quint32, quint16> >");
|
||||
@@ -366,9 +363,6 @@ TomahawkApp::startServent()
|
||||
qDebug() << "Failed to start listening with servent";
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
//QString key = m_servent.createConnectionKey();
|
||||
//qDebug() << "Generated an offer key: " << key;
|
||||
}
|
||||
|
||||
|
||||
@@ -414,8 +408,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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -180,6 +180,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();
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
#include "elidedlabel.h"
|
||||
|
||||
#include <QTime>
|
||||
#include <QEvent>
|
||||
#include <QPainter>
|
||||
#include <QFontMetrics>
|
||||
|
@@ -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 );
|
||||
|
||||
|
530
src/utils/querylabel.cpp
Normal file
530
src/utils/querylabel.cpp
Normal file
@@ -0,0 +1,530 @@
|
||||
#include "querylabel.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QEvent>
|
||||
#include <QFontMetrics>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
#include "tomahawkutils.h"
|
||||
#include "artist.h"
|
||||
#include "album.h"
|
||||
#include "query.h"
|
||||
|
||||
|
||||
#define BOXMARGIN 2
|
||||
#define DASH " - "
|
||||
|
||||
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( m_textMargins );
|
||||
|
||||
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;
|
||||
|
||||
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() )
|
||||
{
|
||||
m_result = result;
|
||||
|
||||
m_query = m_result->toQuery();
|
||||
QList<Tomahawk::result_ptr> rl;
|
||||
rl << m_result;
|
||||
m_query->addResults( rl );
|
||||
|
||||
updateLabel();
|
||||
|
||||
emit textChanged( text() );
|
||||
emit resultChanged( m_result );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
QueryLabel::setQuery( const Tomahawk::query_ptr& query )
|
||||
{
|
||||
if ( query.isNull() )
|
||||
return;
|
||||
|
||||
setContentsMargins( BOXMARGIN * 2, BOXMARGIN / 2, BOXMARGIN * 2, BOXMARGIN / 2 );
|
||||
|
||||
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()
|
||||
{
|
||||
m_hoverArea = QRect();
|
||||
|
||||
updateGeometry();
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
QSize
|
||||
QueryLabel::sizeHint() const
|
||||
{
|
||||
const QFontMetrics& fm = fontMetrics();
|
||||
QSize size( fm.width( text() ) + contentsMargins().left() * 2, fm.height() + contentsMargins().top() * 2 );
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
QSize
|
||||
QueryLabel::minimumSizeHint() const
|
||||
{
|
||||
switch ( mode )
|
||||
{
|
||||
case Qt::ElideNone:
|
||||
return sizeHint();
|
||||
|
||||
default:
|
||||
{
|
||||
const QFontMetrics& fm = fontMetrics();
|
||||
QSize size( fm.width( "..." ), fm.height() + contentsMargins().top() * 2 );
|
||||
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().mid().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().color( foregroundRole() ) );
|
||||
p.drawText( r, align, elidedText );
|
||||
}
|
||||
else
|
||||
{
|
||||
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 & Artist )
|
||||
{
|
||||
p.setBrush( palette().window() );
|
||||
p.setPen( palette().color( foregroundRole() ) );
|
||||
|
||||
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().color( foregroundRole() ) );
|
||||
|
||||
if ( m_type & Artist )
|
||||
{
|
||||
p.drawText( r, align, DASH );
|
||||
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().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() )
|
||||
{
|
||||
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 );
|
||||
|
||||
m_dragPos = QPoint();
|
||||
if ( time.elapsed() < qApp->doubleClickInterval() )
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
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;
|
||||
}
|
||||
|
||||
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( 0 );
|
||||
hoverArea.setRight( artistX + contentsMargins().left() );
|
||||
}
|
||||
else if ( m_type & Album && x < albumX && x > artistX )
|
||||
{
|
||||
int spacing = ( m_type & Artist ) ? dashX : 0;
|
||||
hoverArea.setLeft( artistX + spacing );
|
||||
hoverArea.setRight( albumX + spacing + contentsMargins().left() );
|
||||
}
|
||||
else if ( m_type & Track && x < trackX && x > albumX )
|
||||
{
|
||||
int spacing = ( m_type & Album ) ? dashX : 0;
|
||||
hoverArea.setLeft( albumX + spacing );
|
||||
hoverArea.setRight( trackX + contentsMargins().left() );
|
||||
}
|
||||
|
||||
if ( hoverArea.width() )
|
||||
{
|
||||
hoverArea.setY( 1 );
|
||||
hoverArea.setHeight( height() - 2 );
|
||||
}
|
||||
if ( hoverArea != m_hoverArea )
|
||||
{
|
||||
m_hoverArea = hoverArea;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
QueryLabel::leaveEvent( QEvent* event )
|
||||
{
|
||||
m_hoverArea = QRect();
|
||||
repaint();
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
QString s;
|
||||
if ( !text.isEmpty() )
|
||||
s = DASH;
|
||||
|
||||
text += s + appendage;
|
||||
return text;
|
||||
}
|
94
src/utils/querylabel.h
Normal file
94
src/utils/querylabel.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#ifndef QUERYLABEL_H
|
||||
#define QUERYLABEL_H
|
||||
|
||||
#include <QFrame>
|
||||
#include <QTime>
|
||||
|
||||
#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 );
|
||||
|
||||
virtual void startDrag();
|
||||
|
||||
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;
|
||||
QPoint m_dragPos;
|
||||
QMargins m_textMargins;
|
||||
};
|
||||
|
||||
#endif // QUERYLABEL_H
|
@@ -4,6 +4,8 @@
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
@@ -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
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user