1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-12 23:12:05 +02:00

Compare commits

..

51 Commits
0.4.0 ... 0.4.2

Author SHA1 Message Date
Christian Muehlhaeuser
a104e92471 * Merged ChangeLog. 2012-04-07 10:10:52 +02:00
Christian Muehlhaeuser
1929804541 * Merged audio backend fixes from master. 2012-04-07 10:10:10 +02:00
Christian Muehlhaeuser
a564b3b272 * Don't update seek slider too often, use less cpu. 2012-04-07 10:07:05 +02:00
Christian Muehlhaeuser
d083528ad5 * Updated ChangeLog. 2012-04-07 08:10:31 +02:00
Christian Muehlhaeuser
4d63a9462d * Updated translations. 2012-04-07 08:09:37 +02:00
Christian Muehlhaeuser
5a64886b7c * Bumped to 0.4.2 and updated ChangeLog. 2012-04-07 06:46:13 +02:00
Leo Franchi
76986e8908 Backport case-insensitivity fix from master 2012-04-06 22:07:38 -03:00
Christian Muehlhaeuser
9a9a7148af * Properly fix time display in stable. 2012-04-05 06:17:40 +02:00
Christian Muehlhaeuser
aec7a0b140 * Fixed crash in AudioControls. (merge) 2012-04-05 05:39:50 +02:00
Christian Muehlhaeuser
eff42af593 * Updated ChangeLog. 2012-04-04 02:44:18 +02:00
Christian Muehlhaeuser
ad2b54ad90 * Bump version to 0.4.1. 2012-04-04 01:55:00 +02:00
Christian Muehlhaeuser
4e316a48ea * Fixed non debug builds. 2012-04-04 01:54:14 +02:00
Christian Muehlhaeuser
87863ae7e2 * Fixed about dialog for non debug releases. 2012-04-04 01:54:05 +02:00
Jeff Mitchell
5e439b990f See if this fixes Chris' time-updating problem 2012-03-31 13:06:50 -04:00
Leo Franchi
5dcf426cdf TWK-770: Fix echonest sentence summary grammar 2012-03-31 10:03:53 -04:00
Leo Franchi
5b6c4560ae TWK-785: Rename playlist after revision is created so plitem is selectable 2012-03-31 09:53:47 -04:00
Leo Franchi
fec670f27c TWK-793: Let the sourcetreeview sort before we scroll otherwise we might scroll the wrong amount 2012-03-31 09:26:30 -04:00
Leo Franchi
fb13ccd285 Oops, fix signals/slot params 2012-03-31 09:26:07 -04:00
Leo Franchi
b3b70cea82 TWK-795: Filter dups from last.fm top artists 2012-03-31 09:05:14 -04:00
Leo Franchi
f84d08e011 Don't sort related artists in footnotes 2012-03-31 08:49:18 -04:00
Leo Franchi
9c5966000c TWK-799: Don't set autoupdate when loading playlist view 2012-03-30 17:47:28 -04:00
Leo Franchi
79bfdec895 TWK-798: Start playing first resolvable track when double-clicking on a playlist item 2012-03-30 17:14:08 -04:00
Leo Franchi
9f5215302c various updater fixes 2012-03-30 14:32:01 -04:00
Leo Franchi
73d7ba03f5 TWK-815: Try using foreground text color when drawing grey bg, white on grey is hard to read 2012-03-30 13:34:48 -04:00
Leo Franchi
d623bbefc2 Fix crash on exit 2012-03-30 13:27:36 -04:00
Leo Franchi
907dad95e1 TWK-721: Don't flicker from officialtracks to supercollection tracks.
If the second infosystem albumtracks request comes back empty but the first one
came back with tracks, this is not a failure and so keep the official tracks
2012-03-30 12:09:59 -04:00
Leo Franchi
565217a53a TWK-725: Some extra pointer safety 2012-03-30 10:42:11 -04:00
Christian Muehlhaeuser
bee6485475 * Fixed crash situation in AudioControls.
(cherry picked from commit a2bfd73d55)

Conflicts:

	src/audiocontrols.cpp
2012-03-30 10:31:07 -04:00
Christian Muehlhaeuser
ace18dfa1f * Prevent race condition.
(cherry picked from commit ebbedb2b99)
2012-03-30 10:30:29 -04:00
Christian Muehlhaeuser
95f1162b6e * Fixed cached resolving.
(cherry picked from commit bd098e3ff2)
2012-03-30 10:30:09 -04:00
Christian Muehlhaeuser
e5bdd2242f * Fixed race condition during resolving.
(cherry picked from commit b70114a225)
2012-03-30 10:30:01 -04:00
Christian Muehlhaeuser
deb0eb819c * Fetch square covers from Last.fm.
(cherry picked from commit d5aed7b6df)
2012-03-30 10:29:48 -04:00
Jeff Mitchell
04864c7d79 Set scanning threads idle priority
(cherry picked from commit 5a57e285ec)
2012-03-30 10:28:04 -04:00
Jeff Mitchell
43c4daa4e3 Change wording 2012-03-29 10:41:16 -04:00
Jeff Mitchell
fc95cee6ce Dynamically enable/disable the web API, and change the wording for the option to make it more enticing 2012-03-29 10:41:14 -04:00
Dominik Schmidt
fff8fbfe80 win: fix qca2 usage/grooveshark 2012-03-28 17:23:27 +02:00
Leo Franchi
eec8b76de0 fix xspf auto-updating
(cherry picked from commit c54ca78769)
2012-03-27 11:27:07 -04:00
Leo Franchi
68f03dbd13 autoupdate fixes: init on creation, and every 10 not 100 mins
(cherry picked from commit ff04ab3b92)
2012-03-27 11:27:03 -04:00
Leo Franchi
753e1b3c90 aFix crash in dragging artist, don't assume AlbumModel only contains albums as it's hacked to also contain artists.
(cherry picked from commit 68d541d2e8)
2012-03-20 11:29:30 -04:00
Leo Franchi
e828dadec8 All QSharedPointers used in different threads need deleteLater as custom deleter. This is some of them.
(cherry picked from commit e3f5605c4e)
2012-03-19 22:24:41 -04:00
Leo Franchi
6e929986cb A bit of extra safety
(cherry picked from commit 2a58d53145)
2012-03-19 22:22:28 -04:00
mack-t
e1b086e3a4 Add albumpos, discnumber and score information from tomahawk resolver
(cherry picked from commit 21eeab61d8)
2012-03-19 22:20:32 -04:00
Leo Franchi
27147a0140 Guard against null sharedptr that shouldn't be null anyway -.- 2012-03-19 22:20:07 -04:00
Christian Muehlhaeuser
66836ae0bd * Something is wonky about CLucene scoring. Rely on our internal scoring. 2012-03-15 09:05:56 +01:00
Christian Muehlhaeuser
6342e6a9f8 * Fixed TWK-758: Sorting playlist by album position. 2012-03-15 09:05:47 +01:00
Jeff Mitchell
042ccbe730 Fix merge issue 2012-03-14 16:31:20 -04:00
Jeff Mitchell
6c6411561b Bump dependencies 2012-03-14 16:20:24 -04:00
Jeff Mitchell
18e3669911 Fix XMPP proxy. Make no proxy hosts static so changing it works across
all threads; make a duplication method so jreen doesn't scopepointer us
to death on shutdown; pass in proxyfactory to jreen.

Conflicts:

	src/settingsdialog.cpp
	src/sip/jabber/jabber.cpp
2012-03-14 16:19:32 -04:00
Dominik Schmidt
240517b0cd Bump the required QTweetLib version 2012-03-11 18:46:00 +01:00
Jeff Mitchell
fab36b2262 Use new QTweetLib 0.5 API for PIN values so leading zeros are respected
Conflicts:

	src/accounts/twitter/tomahawkoauthtwitter.h
2012-03-11 18:44:43 +01:00
Christian Muehlhaeuser
dd74ce04e6 Moved QuaZip from "shipped" to "recommended". 2012-03-07 17:51:20 +01:00
77 changed files with 13525 additions and 7102 deletions

View File

@@ -16,7 +16,7 @@ SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" )
SET( TOMAHAWK_VERSION_MAJOR 0 )
SET( TOMAHAWK_VERSION_MINOR 4 )
SET( TOMAHAWK_VERSION_PATCH 0 )
SET( TOMAHAWK_VERSION_PATCH 2 )
#SET( TOMAHAWK_VERSION_RC 0 )

View File

@@ -1,38 +1,55 @@
Version 0.4.2:
* Updated translations for various languages.
* Resuming playback restores correct volume settings.
* Reduced CPU usage during playback.
* Fixed not starting up due to case sensitivity issue on OS X.
* Fixed volume issue (too quiet) on Windows.
Version 0.4.1:
* Fixed various crashes.
* Fixed issues with auto-updating XSPF playlists.
* Double-clicking a playlist starts playing it.
* Resolvers can now return disc number and album position for results.
* Fixed sorting playlists by track number.
* Fixed issues with changing proxy.
* Fixed Twitter authentication issues.
* Fixed Grooveshark support on Windows.
Version 0.4.0:
* Added visual notification for database indexing job.
* Fixed icons not appearing in resolvers list.
* Fixed various UI glitches and stray error messages in stations.
* Fixed bug where album page would resolve bottom-to-top.
* Fixed bug where Footnotes would not update when changing selected album in Album View.
* Fixed dragging albums and artists from charts, album, and artist views.
* Fixed bug where filter text would be one step behind filter value.
* Fixed bug where resolvers would enable themselves after auto-updating.
* Fixed occasional crash when dropping tracks onto New Station item.
* Added jump-to-current-track support for search results page.
* Fixed out of sync Show/Hide menu items on OS X when hidden with cmd-h.
* Fixed non-resolving tracks when dragging from album view.
* Fixed /Volumes directory not showing up on OS X.
* Fixed fetching album covers for albums with special characters.
* Show errors and continue gracefully when resolved audio is not available.
* Fixed various crashes on exit.
* Added basic command-line options for playback control.
* Bumped up web api timeouts to allow web clients to finish resolving.
* Added filename suggestion when exporting a playlist.
* Cleaned up highlighting of artist names in album view.
* Cleaned up alignment of playlist items.
* Fixed potential crash when searching.
* Added support for disc number.
* Added SoundCloudWall.com charts.
* Added ability to "lock on" to a user when listening along, to skip along.
* Fixed bug where loved tracks would be refreshed much too often.
* Fixed startup crash on OS X.
* Fixed some font size issues.
* Sped up Tomahawk startup by moving chart loading into a separate thread.
* Added support for parsing Grooveshark and Tinysong tracks and playlists.
* Reorganized sidebar to follow more logical item groupings.
* Added artist and album results to global searches.
* Fixed style and contrast issues when using GTK styles.
* Fixed paths to artwork when using MPRIS2 interface.
* Added visual notification for database indexing job.
* Fixed icons not appearing in resolvers list.
* Fixed various UI glitches and stray error messages in stations.
* Fixed bug where album page would resolve bottom-to-top.
* Fixed bug where Footnotes would not update when changing selected album in Album View.
* Fixed dragging albums and artists from charts, album, and artist views.
* Fixed bug where filter text would be one step behind filter value.
* Fixed bug where resolvers would enable themselves after auto-updating.
* Fixed occasional crash when dropping tracks onto New Station item.
* Added jump-to-current-track support for search results page.
* Fixed non-resolving tracks when dragging from album view.
* Fixed fetching album covers for albums with special characters.
* Show errors and continue gracefully when resolved audio is not available.
* Fixed various crashes on exit.
* Added basic command-line options for playback control.
* Bumped up web api timeouts to allow web clients to finish resolving.
* Added filename suggestion when exporting a playlist.
* Cleaned up highlighting of artist names in album view.
* Cleaned up alignment of playlist items.
* Fixed potential crash when searching.
* Added support for disc number.
* Added SoundCloudWall.com charts.
* Added ability to "lock on" to a user when listening along, to skip along.
* Fixed bug where loved tracks would be refreshed much too often.
* Fixed some font size issues.
* Sped up Tomahawk startup by moving chart loading into a separate thread.
* Added support for parsing Grooveshark and Tinysong tracks and playlists.
* Reorganized sidebar to follow more logical item groupings.
* Added artist and album results to global searches.
* Fixed style and contrast issues when using GTK styles.
* Fixed paths to artwork when using MPRIS2 interface.
* Fixed out of sync Show/Hide menu items on OS X when hidden with cmd-h.
* Fixed /Volumes directory not showing up on OS X.
* Fixed startup crash on OS X.
Version 0.3.3:
* Automatically load Super Collection tracks when no official release

6
README
View File

@@ -39,13 +39,13 @@ Dependencies
The following dependencies are optional, but recommended:
Attica 0.2.0 - ftp://ftp.kde.org/pub/kde/stable/attica/
Jreen 1.0.1 - https://github.com/euroelessar/jreen
QTweetLib 0.3.0 - https://github.com/minimoog/QTweetLib
QuaZip 0.4.3 - http://quazip.sourceforge.net/
Jreen 1.0.3 - https://github.com/euroelessar/jreen
QTweetLib 0.5.0 - https://github.com/minimoog/QTweetLib
Third party libraries that we ship with our source:
MiniUPnP 1.6 - http://miniupnp.free.fr/
liblastfm 0.4.0 - http://github.com/jonocole/liblastfm/
QuaZip 0.4.3 - http://quazip.sourceforge.net/
Enjoy!

View File

@@ -250,7 +250,7 @@ frameworks_dir = os.path.join(bundle_dir, 'Contents', 'Frameworks')
commands.append(['mkdir', '-p', frameworks_dir])
resources_dir = os.path.join(bundle_dir, 'Contents', 'Resources')
commands.append(['mkdir', '-p', resources_dir])
plugins_dir = os.path.join(bundle_dir, 'Contents', 'plugins')
plugins_dir = os.path.join(bundle_dir, 'Contents', 'PlugIns')
binary = os.path.join(bundle_dir, 'Contents', 'MacOS', bundle_name)
fixed_libraries = []

3541
lang/tomahawk_bg.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,8 @@
<file>tomahawk_de.qm</file>
<file>tomahawk_sv.qm</file>
<file>tomahawk_es.qm</file>
<file>tomahawk_bg.qm</file>
<file>tomahawk_pl.qm</file>
<file>tomahawk_pt_BR.qm</file>
</qresource>
</RCC>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -92,14 +92,12 @@ AudioControls::AudioControls( QWidget* parent )
ui->metaDataArea->setStyleSheet( "QWidget#metaDataArea {\nborder-width: 4px;\nborder-image: url(" RESPATH "images/now-playing-panel.png) 4 4 4 4 stretch stretch; }" );
ui->seekSlider->setEnabled( true );
ui->seekSlider->setTimeLine( &m_sliderTimeLine );
ui->volumeSlider->setRange( 0, 100 );
ui->volumeSlider->setValue( AudioEngine::instance()->volume() );
m_phononTickCheckTimer.setSingleShot( true );
m_sliderTimeLine.setCurveShape( QTimeLine::LinearCurve );
ui->seekSlider->setTimeLine( &m_sliderTimeLine );
connect( &m_phononTickCheckTimer, SIGNAL( timeout() ), SLOT( phononTickCheckTimeout() ) );
connect( &m_sliderTimeLine, SIGNAL( frameChanged( int ) ), ui->seekSlider, SLOT( setValue( int ) ) );
@@ -199,6 +197,7 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result )
ui->seekSlider->setRange( 0, duration );
ui->seekSlider->setValue( 0 );
ui->seekSlider->setEnabled( AudioEngine::instance()->canSeek() );
m_phononTickCheckTimer.stop();
@@ -207,9 +206,12 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result )
m_sliderTimeLine.setFrameRange( 0, duration );
m_sliderTimeLine.setCurrentTime( 0 );
m_seekMsecs = -1;
ui->seekSlider->setVisible( true );
int updateRate = (double)1000 / ( (double)ui->seekSlider->contentsRect().width() / (double)( duration / 1000 ) );
m_sliderTimeLine.setUpdateInterval( qBound( 40, updateRate, 500 ) );
m_noTimeChange = false;
m_lastSliderCheck = 0;
}
@@ -237,6 +239,7 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result )
ui->timeLabel->setText( TomahawkUtils::timeToString( 0 ) );
ui->timeLeftLabel->setFixedWidth( ui->timeLeftLabel->fontMetrics().width( QString( duration.length() + 1, QChar( '0' ) ) ) );
ui->timeLeftLabel->setText( "-" + duration );
m_lastTextSecondShown = 0;
ui->stackedLayout->setCurrentWidget( ui->pauseButton );
@@ -277,7 +280,7 @@ void
AudioControls::onSocialActionsLoaded()
{
Query* query = qobject_cast< Query* >( sender() );
if ( !query )
if ( !query || !m_currentTrack || query != m_currentTrack->toQuery().data() )
return;
query_ptr currentQuery = m_currentTrack->toQuery();
@@ -361,6 +364,14 @@ AudioControls::onPlaybackStopped()
void
AudioControls::onPlaybackTimer( qint64 msElapsed )
{
const int seconds = msElapsed / 1000;
if ( seconds != m_lastTextSecondShown && !m_currentTrack.isNull() )
{
ui->timeLabel->setText( TomahawkUtils::timeToString( seconds ) );
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( m_currentTrack->duration() - seconds ) );
m_lastTextSecondShown = seconds;
}
//tDebug( LOGEXTRA ) << Q_FUNC_INFO << "msElapsed =" << msElapsed << "and timer current time =" << m_sliderTimeLine.currentTime() << "and m_seekMsecs =" << m_seekMsecs;
if ( msElapsed > 0 && msElapsed != m_lastSliderCheck && m_seekMsecs == -1 && msElapsed - 500 < m_lastSliderCheck )
return;
@@ -376,14 +387,11 @@ AudioControls::onPlaybackTimer( qint64 msElapsed )
if ( sender() != &m_phononTickCheckTimer )
m_phononTickCheckTimer.start( 1000 );
const int seconds = msElapsed / 1000;
ui->timeLabel->setText( TomahawkUtils::timeToString( seconds ) );
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( m_currentTrack->duration() - seconds ) );
int currentTime = m_sliderTimeLine.currentTime();
if ( m_noTimeChange )
{
if ( m_sliderTimeLine.currentTime() != msElapsed )
if ( currentTime != msElapsed )
{
m_sliderTimeLine.setPaused( true );
m_noTimeChange = false;
@@ -392,12 +400,12 @@ AudioControls::onPlaybackTimer( qint64 msElapsed )
m_sliderTimeLine.resume();
}
}
else if ( m_sliderTimeLine.currentTime() >= msElapsed || m_seekMsecs != -1 )
else if ( currentTime >= msElapsed || m_seekMsecs != -1 )
{
m_sliderTimeLine.setPaused( true );
m_noTimeChange = false;
if ( m_sliderTimeLine.currentTime() == msElapsed )
if ( currentTime == msElapsed )
m_noTimeChange = true;
m_sliderTimeLine.setCurrentTime( msElapsed );
@@ -407,12 +415,10 @@ AudioControls::onPlaybackTimer( qint64 msElapsed )
}
else if ( m_sliderTimeLine.duration() > msElapsed && m_sliderTimeLine.state() == QTimeLine::NotRunning && AudioEngine::instance()->state() == AudioEngine::Playing )
{
ui->seekSlider->setEnabled( AudioEngine::instance()->canSeek() );
m_sliderTimeLine.start();
}
else if ( m_sliderTimeLine.state() == QTimeLine::Paused && AudioEngine::instance()->state() != AudioEngine::Paused )
{
ui->seekSlider->setEnabled( AudioEngine::instance()->canSeek() );
m_sliderTimeLine.resume();
}

View File

@@ -100,6 +100,7 @@ private:
qint64 m_seekMsecs;
qint64 m_lastSliderCheck;
bool m_noTimeChange;
qint64 m_lastTextSecondShown;
};
#endif // AUDIOCONTROLS_H

View File

@@ -116,6 +116,7 @@ set( libGuiSources
utils/dropjobnotifier.cpp
utils/proxystyle.cpp
utils/tomahawkutilsgui.cpp
utils/closure.cpp
widgets/checkdirtree.cpp
widgets/querylabel.cpp
@@ -240,6 +241,7 @@ set( libGuiHeaders
utils/rdioparser.h
utils/shortenedlinkparser.h
utils/dropjobnotifier.h
utils/closure.h
widgets/checkdirtree.h
widgets/querylabel.h

View File

@@ -61,7 +61,7 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar
return s_albums.value( id );
}
album_ptr a = album_ptr( new Album( id, name, artist ) );
album_ptr a = album_ptr( new Album( id, name, artist ), &QObject::deleteLater );
if ( id > 0 )
s_albums.insert( id, a );

View File

@@ -61,7 +61,7 @@ Artist::get( unsigned int id, const QString& name )
return s_artists.value( id );
}
artist_ptr a = artist_ptr( new Artist( id, name ) );
artist_ptr a = artist_ptr( new Artist( id, name ), &QObject::deleteLater );
if ( id > 0 )
s_artists.insert( id, a );

View File

@@ -131,8 +131,11 @@ AudioEngine::play()
if ( isPaused() )
{
setVolume( m_volume );
m_mediaObject->play();
setVolume( m_volume );
emit resumed();
Tomahawk::InfoSystem::InfoStringHash trackInfo;
trackInfo["title"] = m_currentTrack->track();
@@ -152,6 +155,7 @@ AudioEngine::pause()
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
m_volume = volume();
m_mediaObject->pause();
emit paused();

View File

@@ -155,6 +155,7 @@ private:
mutable QStringList m_supportedMimeTypes;
AudioState m_state;
unsigned int m_volume;
static AudioEngine* s_instance;
};

View File

@@ -37,6 +37,8 @@ RelatedArtistsContext::RelatedArtistsContext()
m_relatedModel->setColumnStyle( TreeModel::TrackOnly );
m_relatedView->setTreeModel( m_relatedModel );
m_relatedView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
m_relatedView->setSortingEnabled( false );
m_relatedView->proxyModel()->sort( -1 );
QPalette pal = m_relatedView->palette();
pal.setColor( QPalette::Window, QColor( 0, 0, 0, 0 ) );

View File

@@ -142,7 +142,7 @@ DatabaseCollection::autoPlaylistCreated( const source_ptr& source, const QVarian
static_cast<GeneratorMode>(data[6].toInt()), // dynamic mode
data[7].toBool(), //shared
data[8].toInt(), //lastmod
data[9].toString() ) ); //GUID
data[9].toString() ), &QObject::deleteLater ); //GUID
addAutoPlaylist( p );
}
@@ -160,7 +160,7 @@ DatabaseCollection::stationCreated( const source_ptr& source, const QVariantList
static_cast<GeneratorMode>(data[6].toInt()), // dynamic mode
data[7].toBool(), //shared
data[8].toInt(), //lastmod
data[9].toString() ) ); //GUID
data[9].toString() ), &QObject::deleteLater ); //GUID
addStation( p );
}

View File

@@ -71,7 +71,7 @@ DatabaseCommand_LoadAllPlaylists::exec( DatabaseImpl* dbi )
query.value(5).toBool(), //shared
query.value(4).toInt(), //lastmod
query.value(0).toString() //GUID
) );
), &QObject::deleteLater );
plists.append( p );
}

View File

@@ -55,9 +55,6 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
qDebug() << "Using result-hint to speed up resolving:" << m_query->resultHint();
Tomahawk::result_ptr result = lib->resultFromHint( m_query );
/* qDebug() << "Result null:" << result.isNull();
* qDebug() << "Collection null:" << result->collection().isNull();
* qDebug() << "Source null:" << result->collection()->source().isNull();*/
if ( !result.isNull() && !result->collection().isNull() && result->collection()->source()->isOnline() )
{
QList<Tomahawk::result_ptr> res;
@@ -137,7 +134,7 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
else
{
s = SourceList::instance()->get( files_query.value( 16 ).toUInt() );
if( s.isNull() )
if ( s.isNull() )
{
qDebug() << "Could not find source" << files_query.value( 16 ).toUInt();
continue;
@@ -146,13 +143,18 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
url = QString( "servent://%1\t%2" ).arg( s->userName() ).arg( url );
}
bool cached = Tomahawk::Result::isCached( url );
Tomahawk::result_ptr result = Tomahawk::Result::get( url );
Tomahawk::artist_ptr artist =
Tomahawk::Artist::get( files_query.value( 18 ).toUInt(), files_query.value( 12 ).toString() );
Tomahawk::album_ptr album =
Tomahawk::Album::get( files_query.value( 19 ).toUInt(), files_query.value( 13 ).toString(), artist );
Tomahawk::artist_ptr composer =
Tomahawk::Artist::get( files_query.value( 20 ).toUInt(), files_query.value( 15 ).toString() );
if ( cached )
{
qDebug() << "Result already cached:" << result->toString();
res << result;
continue;
}
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( files_query.value( 18 ).toUInt(), files_query.value( 12 ).toString() );
Tomahawk::album_ptr album = Tomahawk::Album::get( files_query.value( 19 ).toUInt(), files_query.value( 13 ).toString(), artist );
Tomahawk::artist_ptr composer = Tomahawk::Artist::get( files_query.value( 20 ).toUInt(), files_query.value( 15 ).toString() );
result->setModificationTime( files_query.value( 1 ).toUInt() );
result->setSize( files_query.value( 2 ).toUInt() );
@@ -181,6 +183,7 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
result->setAttributes( attr );
result->setCollection( s->collection() );
res << result;
}
@@ -270,7 +273,7 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
else
{
s = SourceList::instance()->get( files_query.value( 16 ).toUInt() );
if( s.isNull() )
if ( s.isNull() )
{
qDebug() << "Could not find source" << files_query.value( 16 ).toUInt();
continue;
@@ -279,13 +282,18 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
url = QString( "servent://%1\t%2" ).arg( s->userName() ).arg( url );
}
bool cached = Tomahawk::Result::isCached( url );
Tomahawk::result_ptr result = Tomahawk::Result::get( url );
Tomahawk::artist_ptr artist =
Tomahawk::Artist::get( files_query.value( 18 ).toUInt(), files_query.value( 12 ).toString() );
Tomahawk::album_ptr album =
Tomahawk::Album::get( files_query.value( 19 ).toUInt(), files_query.value( 13 ).toString(), artist );
Tomahawk::artist_ptr composer =
Tomahawk::Artist::get( files_query.value( 20 ).toUInt(), files_query.value( 15 ).toString() );
if ( cached )
{
qDebug() << "Result already cached:" << result->toString();
res << result;
continue;
}
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( files_query.value( 18 ).toUInt(), files_query.value( 12 ).toString() );
Tomahawk::album_ptr album = Tomahawk::Album::get( files_query.value( 19 ).toUInt(), files_query.value( 13 ).toString(), artist );
Tomahawk::artist_ptr composer = Tomahawk::Artist::get( files_query.value( 20 ).toUInt(), files_query.value( 15 ).toString() );
result->setModificationTime( files_query.value( 1 ).toUInt() );
result->setSize( files_query.value( 2 ).toUInt() );
@@ -322,8 +330,8 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
}
result->setAttributes( attr );
result->setCollection( s->collection() );
res << result;
}

View File

@@ -227,7 +227,7 @@ FuzzyIndex::search( const Tomahawk::query_ptr& query )
fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::MUST );
minScore = 0.05;
minScore = 0.00;
}
Hits* hits = m_luceneSearcher->search( qry );

View File

@@ -85,7 +85,7 @@ InfoBar::InfoBar( QWidget* parent )
m_autoUpdate->setText( tr( "Automatically update" ) );
m_autoUpdate->setLayoutDirection( Qt::RightToLeft );
m_autoUpdate->setPalette( whitePal );
connect( m_autoUpdate, SIGNAL( stateChanged( int ) ), this, SIGNAL( autoUpdateChanged( int ) ) );
connect( m_autoUpdate, SIGNAL( toggled( bool ) ), this, SIGNAL( autoUpdateChanged( bool ) ) );
ui->horizontalLayout->addWidget( m_autoUpdate );

View File

@@ -60,7 +60,7 @@ public slots:
void setAutoUpdateAvailable( bool b );
signals:
void filterTextChanged( const QString& filter );
void autoUpdateChanged( int state );
void autoUpdateChanged( bool checked );
protected:
void changeEvent( QEvent* e );

View File

@@ -456,7 +456,7 @@ LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
imgurl.addEncodedQueryItem( "artist", QUrl::toPercentEncoding( artistName, "", "+" ) );
imgurl.addEncodedQueryItem( "album", QUrl::toPercentEncoding( albumName, "", "+" ) );
imgurl.addQueryItem( "autocorrect", QString::number( 1 ) );
imgurl.addQueryItem( "size", "large" );
imgurl.addQueryItem( "size", "largesquare" );
imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" );
QNetworkRequest req( imgurl );
@@ -475,7 +475,7 @@ LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
imgurl.addQueryItem( "method", "artist.imageredirect" );
imgurl.addEncodedQueryItem( "artist", QUrl::toPercentEncoding( artistName, "", "+" ) );
imgurl.addQueryItem( "autocorrect", QString::number( 1 ) );
imgurl.addQueryItem( "size", "large" );
imgurl.addQueryItem( "size", "largesquare" );
imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" );
QNetworkRequest req( imgurl );
@@ -585,6 +585,8 @@ LastFmPlugin::topTracksReturned()
QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
QStringList topTracks = lastfm::Artist::getTopTracks( reply );
topTracks.removeDuplicates();
QVariantMap returnedData;
returnedData["tracks"] = topTracks;

View File

@@ -185,7 +185,7 @@ Playlist::create( const source_ptr& author,
entries << p;
}
playlist_ptr playlist( new Playlist( author, guid, title, info, creator, shared, entries ) );
playlist_ptr playlist( new Playlist( author, guid, title, info, creator, shared, entries ), &QObject::deleteLater );
// save to DB in the background
// Watch for the created() signal if you need to be sure it's written.

View File

@@ -182,7 +182,7 @@ public:
QList<plentry_ptr> entriesFromQueries( const QList<Tomahawk::query_ptr>& queries, bool clearFirst = false );
void setUpdater( PlaylistUpdaterInterface* interface ) { m_updater = interface; }
void setUpdater( PlaylistUpdaterInterface* pluinterface ) { m_updater = pluinterface; }
PlaylistUpdaterInterface* updater() const { return m_updater; }
Tomahawk::playlistinterface_ptr playlistInterface();

View File

@@ -55,31 +55,34 @@ XspfUpdater::~XspfUpdater()
void
XspfUpdater::updateNow()
{
if ( m_url.isEmpty() )
{
qWarning() << "XspfUpdater not updating because we have an empty url...";
return;
}
XSPFLoader* l = new XSPFLoader( false, false );
l->setAutoResolveTracks( false );
l->setErrorTitle( playlist()->title() );
l->load( m_url );
connect( l, SIGNAL( ok ( Tomahawk::playlist_ptr ) ), this, SLOT( playlistLoaded() ) );
connect( l, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( playlistLoaded( QList<Tomahawk::query_ptr> ) ) );
}
void
XspfUpdater::playlistLoaded()
XspfUpdater::playlistLoaded( const QList<Tomahawk::query_ptr>& newEntries )
{
XSPFLoader* loader = qobject_cast<XSPFLoader*>( sender() );
Q_ASSERT( loader );
QList< query_ptr > tracks;
foreach ( const plentry_ptr ple, playlist()->entries() )
tracks << ple->query();
bool changed = false;
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, loader->entries(), changed );
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, newEntries, changed );
if ( !changed )
return;
QList<Tomahawk::plentry_ptr> el = playlist()->entriesFromQueries( mergedTracks, true );
playlist()->createNewRevision( uuid(), playlist()->currentrevision(), el );
}
void

View File

@@ -37,6 +37,7 @@ public:
virtual ~XspfUpdater();
virtual QString type() const { return "xspf"; }
public slots:
void updateNow();
@@ -46,7 +47,7 @@ protected:
virtual void removeFromSettings(const QString& group) const;
private slots:
void playlistLoaded();
void playlistLoaded( const QList<Tomahawk::query_ptr> & );
private:
QString m_url;

View File

@@ -178,6 +178,7 @@ AlbumModel::mimeData( const QModelIndexList &indexes ) const
QByteArray queryData;
QDataStream queryStream( &queryData, QIODevice::WriteOnly );
bool isAlbumData = true;
foreach ( const QModelIndex& i, indexes )
{
if ( i.column() > 0 )
@@ -185,16 +186,25 @@ AlbumModel::mimeData( const QModelIndexList &indexes ) const
QModelIndex idx = index( i.row(), 0, i.parent() );
AlbumItem* item = itemFromIndex( idx );
if ( item )
if ( item && !item->album().isNull() )
{
const album_ptr& album = item->album();
queryStream << album->artist()->name();
queryStream << album->name();
isAlbumData = true;
}
else if ( item && !item->artist().isNull() )
{
const artist_ptr& artist = item->artist();
queryStream << artist->name();
isAlbumData = false;
}
}
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.metadata.album", queryData );
QMimeData* mimeData = new QMimeData;
mimeData->setData( isAlbumData ? "application/tomahawk.metadata.album" : "application/tomahawk.metadata.artist", queryData );
return mimeData;
}
@@ -431,7 +441,7 @@ AlbumModel::findItem( const artist_ptr& artist ) const
return item;
}
}
return 0;
}
@@ -447,6 +457,6 @@ AlbumModel::findItem( const album_ptr& album ) const
return item;
}
}
return 0;
}

View File

@@ -152,7 +152,7 @@ DynamicPlaylist::create( const Tomahawk::source_ptr& author,
bool autoLoad
)
{
dynplaylist_ptr dynplaylist = Tomahawk::dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared, autoLoad ) );
dynplaylist_ptr dynplaylist = Tomahawk::dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared, autoLoad ), &QObject::deleteLater );
DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad );
connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) );

View File

@@ -597,7 +597,9 @@ EchonestGenerator::sentenceSummary()
suffix = ", ";
else
suffix = ".";
} else
} else if ( i < required.size() - 2 ) // An item in the list that is before the second to last one, don't use ", and", we only want that for the last item
suffix += ", ";
else
suffix += ", and ";
}
sentence += center + suffix;

View File

@@ -104,10 +104,13 @@ PlaylistItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const
if ( item->isPlaying() )
{
option->palette.setColor( QPalette::Highlight, option->palette.color( QPalette::Mid ) );
option->state |= QStyle::State_Selected;
option->backgroundBrush = option->palette.color( QPalette::Mid );
option->palette.setColor( QPalette::Text, option->palette.color( QPalette::Text ) );
}
if ( option->state & QStyle::State_Selected )
if ( option->state & QStyle::State_Selected && !item->isPlaying() )
{
option->palette.setColor( QPalette::Text, option->palette.color( QPalette::HighlightedText ) );
}

View File

@@ -69,7 +69,7 @@ TrackProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParen
return false;
const Tomahawk::query_ptr& q = pi->query();
if( q.isNull() ) // uh oh? filter out invalid queries i guess
if ( q.isNull() ) // uh oh? filter out invalid queries i guess
return false;
Tomahawk::result_ptr r;
@@ -190,7 +190,7 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
album1 = r->album()->name();
track1 = r->track();
albumpos1 = r->albumpos();
discnumber1 = r->discnumber();
discnumber1 = qMax( 1, (int)r->discnumber() );
bitrate1 = r->bitrate();
mtime1 = r->modificationTime();
id1 = r->trackId();
@@ -203,7 +203,7 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
album2 = r->album()->name();
track2 = r->track();
albumpos2 = r->albumpos();
discnumber2 = r->discnumber();
discnumber2 = qMax( 1, (int)r->discnumber() );
bitrate2 = r->bitrate();
mtime2 = r->modificationTime();
id2 = r->trackId();
@@ -223,7 +223,7 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
{
if ( album1 == album2 )
{
if( discnumber1 == discnumber2 )
if ( discnumber1 == discnumber2 )
{
if ( albumpos1 == albumpos2 )
return id1 < id2;
@@ -243,7 +243,7 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
{
if ( album1 == album2 )
{
if( discnumber1 == discnumber2 )
if ( discnumber1 == discnumber2 )
{
if ( albumpos1 == albumpos2 )
return id1 < id2;
@@ -277,6 +277,18 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
return size1 < size2;
}
else if ( left.column() == TrackModel::AlbumPos ) // sort by album pos
{
if ( discnumber1 != discnumber2 )
{
return discnumber1 < discnumber2;
}
else
{
if ( albumpos1 != albumpos2 )
return albumpos1 < albumpos2;
}
}
const QString& lefts = sourceModel()->data( left ).toString();
const QString& rights = sourceModel()->data( right ).toString();

View File

@@ -32,6 +32,7 @@
#include "dynamic/widgets/LoadingSpinner.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include "utils/closure.h"
#include "dropjob.h"
#include "artist.h"
#include "album.h"
@@ -152,6 +153,41 @@ TrackView::setTrackModel( TrackModel* model )
}
void
TrackView::startPlayingFromStart()
{
if ( m_proxyModel->rowCount() == 0 )
return;
const QModelIndex index = m_proxyModel->index( 0, 0 );
startAutoPlay( index );
}
void
TrackView::autoPlayResolveFinished( const query_ptr& query, int row )
{
Q_ASSERT( !query.isNull() );
Q_ASSERT( row >= 0 );
if ( query.isNull() || row < 0 || query != m_autoPlaying )
return;
const QModelIndex index = m_proxyModel->index( row, 0 );
if ( query->playable() )
{
onItemActivated( index );
return;
}
// Try the next one..
const QModelIndex sib = index.sibling( index.row() + 1, index.column() );
if ( sib.isValid() )
startAutoPlay( sib );
}
void
TrackView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
{
@@ -174,15 +210,48 @@ TrackView::onItemActivated( const QModelIndex& index )
if ( !index.isValid() )
return;
tryToPlayItem( index );
emit itemActivated( index );
}
void
TrackView::startAutoPlay( const QModelIndex& index )
{
if ( tryToPlayItem( index ) )
return;
// item isn't playable but still resolving
TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) );
if ( item && !item->query().isNull() && !item->query()->resolvingFinished() )
{
m_autoPlaying = item->query(); // So we can kill it if user starts autoplaying this playlist again
NewClosure( item->query().data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( autoPlayResolveFinished( Tomahawk::query_ptr, int ) ),
item->query(), index.row() );
return;
}
// not playable at all, try next
const QModelIndex sib = index.sibling( index.row() + 1, index.column() );
if ( sib.isValid() )
startAutoPlay( sib );
}
bool
TrackView::tryToPlayItem( const QModelIndex& index )
{
TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) );
if ( item && !item->query().isNull() && item->query()->numResults() )
{
tDebug() << "Result activated:" << item->query()->toString() << item->query()->results().first()->url();
m_proxyModel->setCurrentIndex( index );
AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), item->query()->results().first() );
return true;
}
emit itemActivated( index );
return false;
}

View File

@@ -64,6 +64,9 @@ public:
bool updatesContextView() const { return m_updateContextView; }
void setUpdatesContextView( bool b ) { m_updateContextView = b; }
// Starts playing from the beginning if resolved, or waits until a track is playable
void startPlayingFromStart();
public slots:
virtual void onItemActivated( const QModelIndex& index );
@@ -98,7 +101,11 @@ private slots:
void onCustomContextMenu( const QPoint& pos );
void autoPlayResolveFinished( const Tomahawk::query_ptr& query, int row );
private:
void startAutoPlay( const QModelIndex& index );
bool tryToPlayItem( const QModelIndex& index );
void updateHoverIndex( const QPoint& pos );
QString m_guid;
@@ -117,6 +124,9 @@ private:
QModelIndex m_hoveredIndex;
QModelIndex m_contextMenuIndex;
Tomahawk::query_ptr m_autoPlaying;
Tomahawk::ContextMenu* m_contextMenu;
};

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -622,7 +623,7 @@ TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent, bool
requestData.caller = m_infoId;
requestData.customData["row"] = parent.row();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
requestData.customData["refetch"] = QVariant( autoRefetch );
requestData.customData["refetch"] = autoRefetch;
requestData.type = Tomahawk::InfoSystem::InfoArtistReleases;
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
}
@@ -660,8 +661,8 @@ TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent, bool au
m_receivedInfoData.removeAll( artistInfo );
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = m_infoId;
requestData.customData["rows"] = QVariant( rows );
requestData.customData["refetch"] = QVariant( autoRefetch );
requestData.customData["rows"] = rows;
requestData.customData["refetch"] = autoRefetch;
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs;
requestData.timeoutMillis = 0;
@@ -796,7 +797,7 @@ TreeModel::onAlbumsAdded( const QList<Tomahawk::album_ptr>& albums, const QModel
albumitem = new TreeModelItem( album, parentItem );
albumitem->index = createIndex( parentItem->children.count() - 1, 0, albumitem );
connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
getCover( albumitem->index );
}
@@ -886,7 +887,7 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV
QModelIndex idx = index( requestData.customData[ "row" ].toInt(), 0, QModelIndex() );
if ( requestData.customData[ "refetch" ].toInt() > 0 && !al.count() )
if ( requestData.customData[ "refetch" ].toBool() && !al.count() )
{
setMode( DatabaseMode );
@@ -940,7 +941,13 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV
}
else if ( m_receivedInfoData.count() == 2 /* FIXME */ )
{
if ( requestData.customData[ "refetch" ].toInt() > 0 )
// If the second load got no data, but the first load did, don't do anything
QList< QVariant > rows = requestData.customData[ "rows" ].toList();
QModelIndex idx = index( rows.first().toUInt(), 0, index( rows.at( 1 ).toUInt(), 0, QModelIndex() ) );
if ( rowCount( idx ) )
return;
if ( requestData.customData[ "refetch" ].toBool() )
{
setMode( DatabaseMode );

View File

@@ -301,28 +301,35 @@ TreeProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) co
albumpos2 = p2->query()->albumpos();
discnumber2 = p2->query()->discnumber();
}
if ( !p1->result().isNull() )
{
if ( albumpos1 == 0 )
albumpos1 = p1->result()->albumpos();
if ( discnumber1 == 0 )
discnumber1 = p1->result()->discnumber();
}
if ( !p2->result().isNull() )
{
if ( albumpos2 == 0 )
albumpos2 = p2->result()->albumpos();
if ( discnumber2 == 0 )
discnumber2 = p2->result()->discnumber();
}
discnumber1 = qMax( 1, (int)discnumber1 );
discnumber2 = qMax( 1, (int)discnumber2 );
if ( albumpos1 == 0 && !p1->result().isNull() )
albumpos1 = p1->result()->albumpos();
if ( discnumber1 == 0 && !p1->result().isNull() )
discnumber1 = p1->result()->discnumber();
if ( albumpos2 == 0 && !p2->result().isNull() )
albumpos2 = p2->result()->albumpos();
if ( discnumber2 == 0 && !p2->result().isNull() )
discnumber2 = p2->result()->discnumber();
const QString& lefts = textForItem( p1 );
const QString& rights = textForItem( p2 );
if( discnumber1 != discnumber2 )
if ( discnumber1 != discnumber2 )
{
return discnumber1 < discnumber2;
}
else
{
if ( albumpos1 != albumpos2 )
return albumpos1 < albumpos2;
}
const QString& lefts = textForItem( p1 );
const QString& rights = textForItem( p2 );
if ( lefts == rights )
return (qint64)&p1 < (qint64)&p2;

View File

@@ -44,7 +44,7 @@ Query::get( const QString& artist, const QString& track, const QString& album, c
if ( qid.isEmpty() )
autoResolve = false;
query_ptr q = query_ptr( new Query( artist, track, album, qid, autoResolve ) );
query_ptr q = query_ptr( new Query( artist, track, album, qid, autoResolve ), &QObject::deleteLater );
q->setWeakRef( q.toWeakRef() );
if ( autoResolve )
@@ -58,7 +58,7 @@ query_ptr
Query::get( const QString& query, const QID& qid )
{
query_ptr q = query_ptr( new Query( query, qid ) );
query_ptr q = query_ptr( new Query( query, qid ), &QObject::deleteLater );
q->setWeakRef( q.toWeakRef() );
if ( !qid.isEmpty() )
@@ -106,6 +106,7 @@ Query::~Query()
{
QMutexLocker lock( &m_mutex );
m_ownRef.clear();
m_results.clear();
}

View File

@@ -392,10 +392,13 @@ QtScriptResolver::parseResultVariantList( const QVariantList& reslist )
rp->setArtist( ap );
rp->setAlbum( Tomahawk::Album::get( ap, m.value( "album" ).toString(), false ) );
rp->setTrack( m.value( "track" ).toString() );
rp->setAlbumPos( m.value( "albumpos" ).toUInt() );
rp->setBitrate( m.value( "bitrate" ).toUInt() );
rp->setSize( m.value( "size" ).toUInt() );
rp->setRID( uuid() );
rp->setFriendlySource( name() );
rp->setScore( m.value( "score" ).toFloat() );
rp->setDiscNumber( m.value( "discnumber" ).toUInt() );
if ( m.contains( "year" ) )
{

View File

@@ -41,6 +41,7 @@ ScriptResolver::ScriptResolver( const QString& exe )
, m_ready( false )
, m_stopped( true )
, m_configSent( false )
, m_deleting( false )
, m_error( Tomahawk::ExternalResolver::NoError )
{
tLog() << Q_FUNC_INFO << "Created script resolver:" << exe;
@@ -61,9 +62,10 @@ ScriptResolver::ScriptResolver( const QString& exe )
ScriptResolver::~ScriptResolver()
{
disconnect( &m_proc, SIGNAL( finished( int, QProcess::ExitStatus ) ), this, SLOT( cmdExited( int, QProcess::ExitStatus ) ) );
m_deleting = true;
m_proc.kill();
m_proc.waitForFinished();
m_proc.waitForFinished(); // might call handleMsg
Tomahawk::Pipeline::instance()->removeResolver( this );
@@ -207,6 +209,10 @@ ScriptResolver::handleMsg( const QByteArray& msg )
{
// qDebug() << Q_FUNC_INFO << msg.size() << QString::fromAscii( msg );
// Might be called from waitForFinished() in ~ScriptResolver, no database in that case, abort.
if ( m_deleting )
return;
bool ok;
QVariant v = m_parser.parse( msg, &ok );
if ( !ok || v.type() != QVariant::Map )

View File

@@ -85,7 +85,7 @@ private:
quint32 m_msgsize;
QByteArray m_msg;
bool m_ready, m_stopped, m_configSent;
bool m_ready, m_stopped, m_configSent, m_deleting;
ExternalResolver::ErrorState m_error;
QJson::Parser m_parser;

View File

@@ -43,13 +43,21 @@ Result::get( const QString& url )
return s_results.value( url );
}
result_ptr r = result_ptr( new Result( url ) );
result_ptr r = result_ptr( new Result( url ), &Result::deleteLater );
s_results.insert( url, r );
return r;
}
bool
Result::isCached( const QString& url )
{
QMutexLocker lock( &s_mutex );
return ( s_results.contains( url ) );
}
Result::Result( const QString& url )
: QObject()
, m_url( url )
@@ -68,12 +76,21 @@ Result::Result( const QString& url )
Result::~Result()
{
}
void
Result::deleteLater()
{
QMutexLocker lock( &s_mutex );
if ( s_results.contains( m_url ) )
{
s_results.remove( m_url );
}
QObject::deleteLater();
}
@@ -170,7 +187,7 @@ Result::toVariant() const
QString
Result::toString() const
{
return QString( "Result(%1 %2\t%3 - %4 %5" ).arg( id() ).arg( score() ).arg( artist()->name() ).arg( track() ).arg( url() );
return QString( "Result(%1 %2\t%3 - %4 %5" ).arg( id() ).arg( score() ).arg( artist().isNull() ? QString() : artist()->name() ).arg( track() ).arg( url() );
}

View File

@@ -56,6 +56,7 @@ friend class ::DatabaseCommand_LoadFile;
public:
static Tomahawk::result_ptr get( const QString& url );
static bool isCached( const QString& url );
virtual ~Result();
QVariant toVariant() const;
@@ -108,6 +109,9 @@ public:
unsigned int trackId() const { return m_trackId; }
unsigned int fileId() const { return m_fileId; }
public slots:
void deleteLater();
signals:
// emitted when the collection this result comes from is going offline/online:
void statusChanged();
@@ -115,7 +119,7 @@ signals:
private slots:
void onOffline();
void onOnline();
private:
// private constructor
explicit Result( const QString& url );

View File

@@ -0,0 +1,89 @@
/* This file is part of Clementine.
Copyright 2011, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "closure.h"
namespace _detail {
Closure::Closure(QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const ClosureArgumentWrapper* val0,
const ClosureArgumentWrapper* val1,
const ClosureArgumentWrapper* val2,
const ClosureArgumentWrapper* val3)
: QObject(receiver),
callback_(NULL),
val0_(val0),
val1_(val1),
val2_(val2),
val3_(val3) {
const QMetaObject* meta_receiver = receiver->metaObject();
QByteArray normalised_slot = QMetaObject::normalizedSignature(slot + 1);
const int index = meta_receiver->indexOfSlot(normalised_slot.constData());
Q_ASSERT(index != -1);
slot_ = meta_receiver->method(index);
Connect(sender, signal);
}
Closure::Closure(QObject* sender,
const char* signal,
std::tr1::function<void()> callback)
: callback_(callback) {
Connect(sender, signal);
}
Closure::~Closure() {
}
void Closure::Connect(QObject* sender, const char* signal) {
bool success = connect(sender, signal, SLOT(Invoked()));
Q_ASSERT(success);
success = connect(sender, SIGNAL(destroyed()), SLOT(Cleanup()));
Q_ASSERT(success);
Q_UNUSED(success);
}
void Closure::Invoked() {
if (callback_) {
callback_();
} else {
slot_.invoke(
parent(),
val0_ ? val0_->arg() : QGenericArgument(),
val1_ ? val1_->arg() : QGenericArgument(),
val2_ ? val2_->arg() : QGenericArgument(),
val3_ ? val3_->arg() : QGenericArgument());
}
deleteLater();
}
void Closure::Cleanup() {
disconnect();
deleteLater();
}
} // namespace _detail
_detail::Closure* NewClosure(
QObject* sender, const char* signal,
QObject* receiver, const char* slot) {
return new _detail::Closure(sender, signal, receiver, slot);
}

View File

@@ -0,0 +1,225 @@
/* This file is part of Clementine.
Copyright 2011, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CLOSURE_H
#define CLOSURE_H
#include <tr1/functional>
#include <QMetaMethod>
#include <QObject>
#include <QSharedPointer>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
namespace _detail {
class ClosureArgumentWrapper {
public:
virtual ~ClosureArgumentWrapper() {}
virtual QGenericArgument arg() const = 0;
};
template<typename T>
class ClosureArgument : public ClosureArgumentWrapper {
public:
explicit ClosureArgument(const T& data) : data_(data) {}
virtual QGenericArgument arg() const {
return Q_ARG(T, data_);
}
private:
T data_;
};
class Closure : public QObject, boost::noncopyable {
Q_OBJECT
public:
Closure(QObject* sender, const char* signal,
QObject* receiver, const char* slot,
const ClosureArgumentWrapper* val0 = 0,
const ClosureArgumentWrapper* val1 = 0,
const ClosureArgumentWrapper* val2 = 0,
const ClosureArgumentWrapper* val3 = 0);
Closure(QObject* sender, const char* signal,
std::tr1::function<void()> callback);
virtual ~Closure();
private slots:
void Invoked();
void Cleanup();
private:
void Connect(QObject* sender, const char* signal);
QMetaMethod slot_;
std::tr1::function<void()> callback_;
boost::scoped_ptr<const ClosureArgumentWrapper> val0_;
boost::scoped_ptr<const ClosureArgumentWrapper> val1_;
boost::scoped_ptr<const ClosureArgumentWrapper> val2_;
boost::scoped_ptr<const ClosureArgumentWrapper> val3_;
};
class SharedPointerWrapper {
public:
virtual ~SharedPointerWrapper() {}
virtual QObject* data() const = 0;
};
template<typename T>
class SharedPointer : public SharedPointerWrapper {
public:
explicit SharedPointer(QSharedPointer<T> ptr)
: ptr_(ptr) {
}
QObject* data() const {
return ptr_.data();
}
private:
QSharedPointer<T> ptr_;
};
// For use with a QSharedPointer as a sender.
class SharedClosure : public Closure {
Q_OBJECT
public:
SharedClosure(SharedPointerWrapper* sender, const char* signal,
QObject* receiver, const char* slot,
const ClosureArgumentWrapper* val0 = 0,
const ClosureArgumentWrapper* val1 = 0,
const ClosureArgumentWrapper* val2 = 0,
const ClosureArgumentWrapper* val3 = 0)
: Closure(sender->data(), signal,
receiver, slot,
val0, val1, val2, val3),
shared_sender_(sender) {
}
private:
boost::scoped_ptr<SharedPointerWrapper> shared_sender_;
};
} // namespace _detail
#define C_ARG(type, data) new _detail::ClosureArgument<type>(data)
_detail::Closure* NewClosure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot);
template <typename T>
_detail::Closure* NewClosure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const T& val0) {
return new _detail::Closure(
sender, signal, receiver, slot,
C_ARG(T, val0));
}
template <typename T0, typename T1>
_detail::Closure* NewClosure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const T0& val0,
const T1& val1) {
return new _detail::Closure(
sender, signal, receiver, slot,
C_ARG(T0, val0), C_ARG(T1, val1));
}
template <typename T0, typename T1, typename T2>
_detail::Closure* NewClosure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const T0& val0,
const T1& val1,
const T2& val2) {
return new _detail::Closure(
sender, signal, receiver, slot,
C_ARG(T0, val0), C_ARG(T1, val1), C_ARG(T2, val2));
}
template <typename T0, typename T1, typename T2, typename T3>
_detail::Closure* NewClosure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const T0& val0,
const T1& val1,
const T2& val2,
const T3& val3) {
return new _detail::Closure(
sender, signal, receiver, slot,
C_ARG(T0, val0), C_ARG(T1, val1), C_ARG(T2, val2), C_ARG(T3, val3));
}
template <typename TP>
_detail::Closure* NewClosure(
QSharedPointer<TP> sender,
const char* signal,
QObject* receiver,
const char* slot) {
return new _detail::SharedClosure(
new _detail::SharedPointer<TP>(sender), signal, receiver, slot);
}
template <typename TP, typename T0>
_detail::Closure* NewClosure(
QSharedPointer<TP> sender,
const char* signal,
QObject* receiver,
const char* slot,
const T0& val0) {
return new _detail::SharedClosure(
new _detail::SharedPointer<TP>(sender), signal, receiver, slot,
C_ARG(T0, val0));
}
template <typename TP, typename T0, typename T1>
_detail::Closure* NewClosure(
QSharedPointer<TP> sender,
const char* signal,
QObject* receiver,
const char* slot,
const T0& val0,
const T1& val1) {
return new _detail::SharedClosure(
new _detail::SharedPointer<TP>(sender), signal, receiver, slot,
C_ARG(T0, val0), C_ARG(T1, val1));
}
#endif // CLOSURE_H

View File

@@ -301,10 +301,11 @@ extensionToMimetype( const QString& extension )
return s_ext2mime.value( extension, "unknown" );
}
static QMutex s_noProxyHostsMutex;
static QStringList s_noProxyHosts;
NetworkProxyFactory::NetworkProxyFactory( const NetworkProxyFactory& other )
{
m_noProxyHosts = QStringList( other.m_noProxyHosts );
m_proxy = QNetworkProxy( other.m_proxy );
}
@@ -312,13 +313,17 @@ NetworkProxyFactory::NetworkProxyFactory( const NetworkProxyFactory& other )
QList< QNetworkProxy >
NetworkProxyFactory::queryProxy( const QNetworkProxyQuery& query )
{
tDebug() << Q_FUNC_INFO << "query hostname is " << query.peerHostName();
QList< QNetworkProxy > proxies;
QString hostname = query.peerHostName();
if ( m_proxy.hostName().isEmpty() || hostname.isEmpty() || m_noProxyHosts.contains( hostname ) || TomahawkSettings::instance()->proxyType() == QNetworkProxy::NoProxy )
s_noProxyHostsMutex.lock();
if ( s_noProxyHosts.contains( hostname ) )
proxies << QNetworkProxy::NoProxy << systemProxyForQuery( query );
else if ( m_proxy.hostName().isEmpty() || hostname.isEmpty() || TomahawkSettings::instance()->proxyType() == QNetworkProxy::NoProxy )
proxies << systemProxyForQuery( query );
else
proxies << m_proxy << systemProxyForQuery( query );
s_noProxyHostsMutex.unlock();
return proxies;
}
@@ -335,7 +340,9 @@ NetworkProxyFactory::setNoProxyHosts( const QStringList& hosts )
//TODO: wildcard support
}
tDebug() << Q_FUNC_INFO << "New no-proxy hosts:" << newList;
m_noProxyHosts = newList;
s_noProxyHostsMutex.lock();
s_noProxyHosts = newList;
s_noProxyHostsMutex.unlock();
}
@@ -357,7 +364,6 @@ NetworkProxyFactory::operator=( const NetworkProxyFactory& rhs )
if ( this != &rhs )
{
m_proxy = QNetworkProxy( rhs.m_proxy );
m_noProxyHosts = QStringList( rhs.m_noProxyHosts );
}
return *this;
@@ -367,7 +373,7 @@ NetworkProxyFactory::operator=( const NetworkProxyFactory& rhs )
bool NetworkProxyFactory::operator==( const NetworkProxyFactory& other ) const
{
tDebug() << Q_FUNC_INFO;
if ( m_noProxyHosts != other.m_noProxyHosts || m_proxy != other.m_proxy )
if ( m_proxy != other.m_proxy )
return false;
return true;
@@ -378,25 +384,29 @@ static QMap< QThread*, NetworkProxyFactory* > s_threadProxyFactoryHash;
static QMutex s_namAccessMutex;
NetworkProxyFactory*
proxyFactory( bool noMutexLocker )
proxyFactory( bool makeClone, bool noMutexLocker )
{
// Don't lock if being called from nam()
tDebug() << Q_FUNC_INFO;
QMutex otherMutex;
QMutexLocker locker( noMutexLocker ? &otherMutex : &s_namAccessMutex );
if ( s_threadProxyFactoryHash.contains( QThread::currentThread() ) )
return s_threadProxyFactoryHash[ QThread::currentThread() ];
if ( !makeClone )
{
if ( s_threadProxyFactoryHash.contains( QThread::currentThread() ) )
return s_threadProxyFactoryHash[ QThread::currentThread() ];
if ( !s_threadProxyFactoryHash.contains( TOMAHAWK_APPLICATION::instance()->thread() ) )
return 0;
if ( !s_threadProxyFactoryHash.contains( TOMAHAWK_APPLICATION::instance()->thread() ) )
return 0;
}
// create a new proxy factory for this thread
TomahawkUtils::NetworkProxyFactory *mainProxyFactory = s_threadProxyFactoryHash[ TOMAHAWK_APPLICATION::instance()->thread() ];
TomahawkUtils::NetworkProxyFactory *newProxyFactory = new TomahawkUtils::NetworkProxyFactory();
*newProxyFactory = *mainProxyFactory;
s_threadProxyFactoryHash[ QThread::currentThread() ] = newProxyFactory;
if ( !makeClone )
s_threadProxyFactoryHash[ QThread::currentThread() ] = newProxyFactory;
return newProxyFactory;
}
@@ -455,7 +465,7 @@ nam()
newNam->setConfiguration( QNetworkConfiguration( mainNam->configuration() ) );
newNam->setNetworkAccessible( mainNam->networkAccessible() );
newNam->setProxyFactory( proxyFactory( true ) );
newNam->setProxyFactory( proxyFactory( false, true ) );
s_threadNamHash[ QThread::currentThread() ] = newNam;
@@ -485,9 +495,15 @@ setNam( QNetworkAccessManager* nam, bool noMutexLocker )
QNetworkProxy proxy( s->proxyType(), s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() );
proxyFactory->setProxy( proxy );
//FIXME: Jreen is broke without this
QNetworkProxy::setApplicationProxy( proxy );
if ( !s->proxyNoProxyHosts().isEmpty() )
//QNetworkProxy::setApplicationProxy( proxy );
s_noProxyHostsMutex.lock();
if ( !s->proxyNoProxyHosts().isEmpty() && s_noProxyHosts.isEmpty() )
{
s_noProxyHostsMutex.unlock();
proxyFactory->setNoProxyHosts( s->proxyNoProxyHosts().split( ',', QString::SkipEmptyParts ) );
}
else
s_noProxyHostsMutex.unlock();
}
nam->setProxyFactory( proxyFactory );

View File

@@ -92,7 +92,7 @@ namespace TomahawkUtils
DLLEXPORT QString extensionToMimetype( const QString& extension );
DLLEXPORT bool newerVersion( const QString& oldVersion, const QString& newVersion );
DLLEXPORT NetworkProxyFactory* proxyFactory( bool noMutexLocker = false );
DLLEXPORT NetworkProxyFactory* proxyFactory( bool makeClone = false, bool noMutexLocker = false );
DLLEXPORT void setProxyFactory( TomahawkUtils::NetworkProxyFactory* factory, bool noMutexLocker = false );
DLLEXPORT QNetworkAccessManager* nam();
DLLEXPORT void setNam( QNetworkAccessManager* nam, bool noMutexLocker = false );

View File

@@ -60,6 +60,7 @@ XSPFLoader::XSPFLoader( bool autoCreate, bool autoUpdate, QObject *parent )
, m_autoCreate( autoCreate )
, m_autoUpdate( autoUpdate )
, m_autoResolve( true )
, m_autoDelete( true )
, m_NS("http://xspf.org/ns/0/")
{
qRegisterMetaType< XSPFErrorCode >("XSPFErrorCode");
@@ -121,7 +122,11 @@ XSPFLoader::reportError()
{
emit error( FetchError );
#ifndef ENABLE_HEADLESS
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorToString( FetchError) ) );
const QString errorMsg = errorToString( FetchError);
if ( !m_errorTitle.isEmpty() )
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( QString( "%1: %2" ).arg( m_errorTitle ).arg( errorMsg ) ) );
else
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorMsg ) );
#endif
deleteLater();
}
@@ -262,15 +267,17 @@ XSPFLoader::gotBody()
// 10 minute default---for now, no way to change it
connect( m_playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistCreated() ) );
new Tomahawk::XspfUpdater( m_playlist, 6000000, m_autoUpdate, m_url.toString() );
new Tomahawk::XspfUpdater( m_playlist, 600000, m_autoUpdate, m_url.toString() );
emit ok( m_playlist );
}
else{
else
{
if( !m_entries.isEmpty() )
emit tracks( m_entries );
}
deleteLater();
if ( m_autoDelete )
deleteLater();
}

View File

@@ -48,6 +48,8 @@ public:
void setOverrideTitle( const QString& newTitle );
void setAutoResolveTracks( bool autoResolve ) { m_autoResolve = autoResolve; }
void setAutoDelete( bool autoDelete ) { m_autoDelete = autoDelete; }
void setErrorTitle( const QString& error ) { m_errorTitle = error; }
static QString errorToString( XSPFErrorCode error );
@@ -69,10 +71,10 @@ private:
void reportError();
void gotBody();
bool m_autoCreate, m_autoUpdate, m_autoResolve;
bool m_autoCreate, m_autoUpdate, m_autoResolve, m_autoDelete;
QString m_NS,m_overrideTitle;
QList< Tomahawk::query_ptr > m_entries;
QString m_title, m_info, m_creator;
QString m_title, m_info, m_creator, m_errorTitle;
QUrl m_url;
QByteArray m_body;

View File

@@ -112,7 +112,7 @@ ViewManager::ViewManager( QObject* parent )
connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( applyFilter() ) );
connect( m_infobar, SIGNAL( filterTextChanged( QString ) ), SLOT( setFilter( QString ) ) );
connect( m_infobar, SIGNAL( autoUpdateChanged( int ) ), SLOT( autoUpdateChanged( int ) ) );
connect( m_infobar, SIGNAL( autoUpdateChanged( bool ) ), SLOT( autoUpdateChanged( bool ) ) );
connect( this, SIGNAL( tomahawkLoaded() ), m_whatsHotWidget, SLOT( fetchData() ) );
connect( this, SIGNAL( tomahawkLoaded() ), m_welcomeWidget, SLOT( loadData() ) );
@@ -537,9 +537,9 @@ ViewManager::applyFilter()
void
ViewManager::autoUpdateChanged( int state )
ViewManager::autoUpdateChanged( bool toggled )
{
currentPage()->setAutoUpdate( state == Qt::Checked );
currentPage()->setAutoUpdate( toggled );
}

View File

@@ -84,7 +84,7 @@ public:
Tomahawk::playlistinterface_ptr currentPlaylistInterface() const;
Tomahawk::ViewPage* currentPage() const;
Tomahawk::ViewPage* pageForInterface( Tomahawk::playlistinterface_ptr interface ) const;
Tomahawk::ViewPage* pageForInterface( Tomahawk::playlistinterface_ptr plInterface ) const;
Tomahawk::ViewPage* show( Tomahawk::ViewPage* page );
@@ -174,7 +174,7 @@ private slots:
void setFilter( const QString& filter );
void applyFilter();
void autoUpdateChanged( int );
void autoUpdateChanged( bool );
void onWidgetDestroyed( QWidget* widget );
@@ -185,9 +185,9 @@ private:
void saveCurrentPlaylistSettings();
void loadCurrentPlaylistSettings();
Tomahawk::playlist_ptr playlistForInterface( Tomahawk::playlistinterface_ptr interface ) const;
Tomahawk::dynplaylist_ptr dynamicPlaylistForInterface( Tomahawk::playlistinterface_ptr interface ) const;
Tomahawk::collection_ptr collectionForInterface( Tomahawk::playlistinterface_ptr interface ) const;
Tomahawk::playlist_ptr playlistForInterface( Tomahawk::playlistinterface_ptr plInterface ) const;
Tomahawk::dynplaylist_ptr dynamicPlaylistForInterface( Tomahawk::playlistinterface_ptr plInterface ) const;
Tomahawk::collection_ptr collectionForInterface( Tomahawk::playlistinterface_ptr plInterface ) const;
QWidget* m_widget;
InfoBar* m_infobar;

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -231,7 +232,7 @@ AlbumInfoWidget::loadAlbums( bool autoRefetch )
artistInfo["artist"] = m_album->artist()->name();
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.customData["refetch"] = QVariant( autoRefetch );
requestData.customData["refetch"] = autoRefetch;
requestData.caller = m_infoId;
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
requestData.type = Tomahawk::InfoSystem::InfoArtistReleases;
@@ -308,7 +309,7 @@ AlbumInfoWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDa
tDebug() << "Adding" << al.count() << "albums";
gotAlbums( al );
}
else if ( requestData.customData[ "refetch" ].toInt() > 0 )
else if ( requestData.customData[ "refetch" ].toBool() )
{
tDebug() << "Auto refetching";
m_buttonAlbums->setChecked( false );

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -173,6 +173,7 @@ MusicScanner::scan()
SLOT( commitBatch( QVariantList, QVariantList ) ), Qt::DirectConnection );
m_dirListerThreadController = new QThread( this );
m_dirListerThreadController->setPriority( QThread::IdlePriority );
m_dirLister = QWeakPointer< DirLister >( new DirLister( m_dirs ) );
m_dirLister.data()->moveToThread( m_dirListerThreadController );

View File

@@ -139,7 +139,8 @@
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>No Proxy Hosts:</string>
<string>No Proxy Hosts:
(Overrides system proxy)</string>
</property>
</widget>
</item>

View File

@@ -195,6 +195,7 @@ ScanManager::runDirScan()
{
m_scanTimer->stop();
m_musicScannerThreadController = new QThread( this );
m_musicScannerThreadController->setPriority( QThread::IdlePriority );
m_scanner = QWeakPointer< MusicScanner >( new MusicScanner( paths ) );
m_scanner.data()->moveToThread( m_musicScannerThreadController );
connect( m_scanner.data(), SIGNAL( finished() ), SLOT( scannerFinished() ) );

View File

@@ -22,13 +22,13 @@
#include "utils/tomahawkutilsgui.h"
#include <QDesktopServices>
#include <QFileDialog>
#include <QMessageBox>
#include <QNetworkConfiguration>
#include <QNetworkProxy>
#include <QVBoxLayout>
#include <QSizeGrip>
#include <QtGui/QDesktopServices>
#include <QtGui/QFileDialog>
#include <QtGui/QMessageBox>
#include <QtNetwork/QNetworkConfiguration>
#include <QtNetwork/QNetworkProxy>
#include <QtGui/QVBoxLayout>
#include <QtGui/QSizeGrip>
#ifdef LIBLASTFM_FOUND
#include <lastfm/ws.h>
@@ -859,7 +859,6 @@ ProxyDialog::ProxyDialog( QWidget *parent )
ui->userLineEdit->setEnabled( false );
ui->passwordLineEdit->setEnabled( false );
ui->checkBoxUseProxyForDns->setEnabled( false );
ui->noHostLineEdit->setEnabled( false );
}
connect( ui->typeBox, SIGNAL( currentIndexChanged( int ) ), SLOT( proxyTypeChangedSlot( int ) ) );
@@ -876,7 +875,6 @@ ProxyDialog::proxyTypeChangedSlot( int index )
ui->userLineEdit->setEnabled( false );
ui->passwordLineEdit->setEnabled( false );
ui->checkBoxUseProxyForDns->setEnabled( false );
ui->noHostLineEdit->setEnabled( false );
}
else
{
@@ -885,7 +883,6 @@ ProxyDialog::proxyTypeChangedSlot( int index )
ui->userLineEdit->setEnabled( true );
ui->passwordLineEdit->setEnabled( true );
ui->checkBoxUseProxyForDns->setEnabled( true );
ui->noHostLineEdit->setEnabled( true );
}
}
@@ -923,8 +920,8 @@ ProxyDialog::saveSettings()
proxyFactory->setProxy( QNetworkProxy( type, s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() ) );
if ( !ui->noHostLineEdit->text().isEmpty() )
{
tDebug() << Q_FUNC_INFO << "hosts line edit is " << ui->noHostLineEdit->text();
tDebug() << Q_FUNC_INFO << "split hosts line edit is " << ui->noHostLineEdit->text().split( ' ', QString::SkipEmptyParts );
tDebug() << Q_FUNC_INFO << "noproxy line edit is " << ui->noHostLineEdit->text();
tDebug() << Q_FUNC_INFO << "split noproxy line edit is " << ui->noHostLineEdit->text().split( ' ', QString::SkipEmptyParts );
proxyFactory->setNoProxyHosts( ui->noHostLineEdit->text().split( ' ', QString::SkipEmptyParts ) );
}
}

View File

@@ -19,8 +19,8 @@
#ifndef SETTINGSDIALOG_H
#define SETTINGSDIALOG_H
#include <QDialog>
#include <QModelIndex>
#include <QtGui/QDialog>
#include <QtCore/QModelIndex>
#include "config.h"

View File

@@ -102,6 +102,7 @@ JabberPlugin::JabberPlugin( const QString& pluginId )
// general client setup
m_client = new Jreen::Client( jid, m_currentPassword );
m_client->setProxyFactory( TomahawkUtils::proxyFactory( true ) );
setupClientHelper();
m_client->registerPayload(new TomahawkSipMessageFactory);

View File

@@ -12,15 +12,15 @@ TomahawkOAuthTwitter::TomahawkOAuthTwitter( QNetworkAccessManager *nam, QObject*
}
int
const QString
TomahawkOAuthTwitter::authorizationWidget()
{
bool ok;
int i = QInputDialog::getInt(0, tr( "Twitter PIN" ), tr( "After authenticating on Twitter's web site,\nenter the displayed PIN number here:" ), 0, 0, 2147483647, 1, &ok);
if (ok)
return i;
const QString str = QInputDialog::getText(0, tr( "Twitter PIN" ), tr( "After authenticating on Twitter's web site,\nenter the displayed PIN number here:" ), QLineEdit::Normal, QString(), &ok);
if ( ok && !str.isEmpty() )
return str;
return 0;
return QString();
}
void

View File

@@ -16,7 +16,7 @@ public:
~TomahawkOAuthTwitter() {}
protected:
virtual int authorizationWidget();
virtual const QString authorizationWidget();
private slots:
void error();

View File

@@ -295,6 +295,7 @@ CategoryAddItem::playlistToRenameLoaded()
QTimer::singleShot( 400, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
disconnect( pl, SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
disconnect( pl, SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
}
@@ -306,8 +307,7 @@ CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks )
playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, tracks );
ViewManager::instance()->show( newpl );
// Give a shot to try to rename it. The playlist has to be created first. ugly.
QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
connect( newpl.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
} else if( m_categoryType == SourcesModel::StationsCategory ) {
// seed the playlist with these song or artist filters
QString name;

View File

@@ -24,6 +24,7 @@
#include "query.h"
#include "viewmanager.h"
#include "playlist/dynamic/GeneratorInterface.h"
#include "playlist/playlistview.h"
#include "categoryitems.h"
#include "sourceitem.h"
#include "utils/tomahawkutils.h"
@@ -136,6 +137,17 @@ PlaylistItem::activate()
}
void
PlaylistItem::doubleClicked()
{
ViewPage* p = ViewManager::instance()->currentPage();
if ( PlaylistView* view = dynamic_cast< PlaylistView* >( p ) )
{
view->startPlayingFromStart();
}
}
void
PlaylistItem::setLoaded( bool loaded )
{

View File

@@ -31,7 +31,6 @@ public:
virtual QString text() const;
virtual Tomahawk::playlist_ptr playlist() const;
virtual Qt::ItemFlags flags() const;
virtual void activate();
virtual bool willAcceptDrag( const QMimeData* data ) const;
virtual DropTypes supportedDropTypes( const QMimeData* data ) const;
virtual bool dropMimeData( const QMimeData* data, Qt::DropAction action );
@@ -43,6 +42,10 @@ public:
virtual SourceTreeItem* activateCurrent();
public slots:
virtual void activate();
virtual void doubleClicked();
protected:
void setLoaded( bool loaded );

View File

@@ -78,6 +78,7 @@ public:
public slots:
virtual void activate() {}
virtual void doubleClicked() {}
signals:
void updated();

View File

@@ -43,6 +43,7 @@
SourceDelegate::SourceDelegate( QAbstractItemView* parent )
: QStyledItemDelegate( parent )
, m_parent( parent )
, m_lastClicked( -1 )
{
m_dropTypeMap.insert( 0, SourceTreeItem::DropTypeThisTrack );
m_dropTypeMap.insert( 1, SourceTreeItem::DropTypeThisAlbum );
@@ -631,7 +632,27 @@ SourceDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QSt
// a mouse press event. Since we want to swallow click events when they are on headphones other action items, here wemake sure we only
// emit if we really want to
if ( event->type() == QEvent::MouseButtonRelease )
emit clicked( index );
{
if ( m_lastClicked == -1 )
{
m_lastClicked = QDateTime::currentMSecsSinceEpoch();
emit clicked( index );
}
else
{
qint64 elapsed = QDateTime::currentMSecsSinceEpoch() - m_lastClicked;
if ( elapsed < QApplication::doubleClickInterval() )
{
m_lastClicked = -1;
emit doubleClicked( index );
} else
{
m_lastClicked = QDateTime::currentMSecsSinceEpoch();
emit clicked( index );
}
}
}
return QStyledItemDelegate::editorEvent ( event, model, option, index );
}

View File

@@ -43,6 +43,7 @@ public:
signals:
void clicked( const QModelIndex& idx );
void doubleClicked( const QModelIndex& idx );
void latchOn( const Tomahawk::source_ptr& idx );
void latchOff( const Tomahawk::source_ptr& idx );
void toggleRealtimeLatch( const Tomahawk::source_ptr& idx, bool realtime );
@@ -72,6 +73,7 @@ private:
mutable SourceTreeItem::DropType m_hoveredDropType; // Hack to keep easily track of the current highlighted DropType in paint()
QMap< QModelIndex, AnimationHelper* > m_expandedMap;
QPixmap m_headphonesOn, m_headphonesOff, m_realtimeLocked, m_realtimeUnlocked, m_nowPlayingSpeaker, m_nowPlayingSpeakerDark;
qint64 m_lastClicked;
QMap< int, SourceTreeItem::DropType > m_dropTypeMap;
QMap< int, QString > m_dropTypeTextMap;

View File

@@ -78,6 +78,7 @@ SourceTreeView::SourceTreeView( QWidget* parent )
sortByColumn( 0, Qt::AscendingOrder );
setVerticalScrollMode( QTreeView::ScrollPerPixel );
setMouseTracking( true );
setEditTriggers( NoEditTriggers );
// TODO animation conflicts with the expanding-playlists-when-collection-is-null
// so investigate
@@ -88,6 +89,7 @@ SourceTreeView::SourceTreeView( QWidget* parent )
connect( m_delegate, SIGNAL( latchOff( Tomahawk::source_ptr ) ), SLOT( latchOff( Tomahawk::source_ptr ) ) );
connect( m_delegate, SIGNAL( toggleRealtimeLatch( Tomahawk::source_ptr, bool ) ), m_latchManager, SLOT( latchModeChangeRequest( Tomahawk::source_ptr,bool ) ) );
connect( m_delegate, SIGNAL( clicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) );
connect( m_delegate, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemDoubleClicked( QModelIndex ) ) );
setItemDelegate( m_delegate );
@@ -96,7 +98,7 @@ SourceTreeView::SourceTreeView( QWidget* parent )
m_model = new SourcesModel( this );
m_proxyModel = new SourcesProxyModel( m_model, this );
connect( m_proxyModel, SIGNAL( selectRequest( QPersistentModelIndex ) ), SLOT( selectRequest( QPersistentModelIndex ) ) );
connect( m_proxyModel, SIGNAL( selectRequest( QPersistentModelIndex ) ), SLOT( selectRequest( QPersistentModelIndex ) ), Qt::QueuedConnection );
connect( m_proxyModel, SIGNAL( expandRequest( QPersistentModelIndex ) ), SLOT( expandRequest( QPersistentModelIndex ) ) );
connect( m_proxyModel, SIGNAL( toggleExpandRequest( QPersistentModelIndex ) ), SLOT( toggleExpandRequest( QPersistentModelIndex ) ) );
@@ -230,6 +232,17 @@ SourceTreeView::onItemActivated( const QModelIndex& index )
}
void
SourceTreeView::onItemDoubleClicked( const QModelIndex& idx )
{
if ( !selectionModel()->selectedIndexes().contains( idx ) )
onItemActivated( idx );
SourceTreeItem* item = itemFromIndex< SourceTreeItem >( idx );
item->doubleClicked();
}
void
SourceTreeView::onItemExpanded( const QModelIndex& idx )
{

View File

@@ -66,6 +66,7 @@ private slots:
void selectRequest( const QPersistentModelIndex& idx );
void expandRequest( const QPersistentModelIndex& idx );
void toggleExpandRequest( const QPersistentModelIndex& idx );
void onItemDoubleClicked( const QModelIndex& idx );
void loadPlaylist();
void deletePlaylist( const QModelIndex& = QModelIndex() );

View File

@@ -644,7 +644,7 @@
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Playdar HTTP API</string>
<string>Allow web browsers to interact with Tomahawk</string>
</property>
<property name="checked">
<bool>true</bool>

View File

@@ -28,7 +28,6 @@
#include <QtNetwork/QNetworkReply>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtNetwork/QNetworkProxy>
#include "artist.h"
#include "album.h"
@@ -256,6 +255,7 @@ TomahawkApp::init()
{
initHTTP();
}
connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( initHTTP() ) );
#ifndef ENABLE_HEADLESS
if ( !s->hasScannerPaths() )
@@ -297,6 +297,11 @@ TomahawkApp::~TomahawkApp()
{
tLog() << "Shutting down Tomahawk...";
if ( !m_session.isNull() )
delete m_session.data();
if ( !m_connector.isNull() )
delete m_connector.data();
Pipeline::instance()->stop();
if ( !m_servent.isNull() )
@@ -459,15 +464,43 @@ TomahawkApp::initDatabase()
void
TomahawkApp::initHTTP()
{
m_session.setPort( 60210 ); //TODO config
m_session.setListenInterface( QHostAddress::LocalHost );
m_session.setConnector( &m_connector );
if ( !TomahawkSettings::instance()->httpEnabled() )
{
tLog() << "Stopping HTTPd, not enabled";
if ( !m_session.isNull() )
delete m_session.data();
if ( !m_connector.isNull() )
delete m_connector.data();
return;
}
Api_v1* api = new Api_v1( &m_session );
m_session.setStaticContentService( api );
if ( m_session )
{
tLog() << "HTTPd session already exists, returning";
return;
}
m_session = QWeakPointer< QxtHttpSessionManager >( new QxtHttpSessionManager() );
m_connector = QWeakPointer< QxtHttpServerConnector >( new QxtHttpServerConnector );
if ( m_session.isNull() || m_connector.isNull() )
{
if ( !m_session.isNull() )
delete m_session.data();
if ( !m_connector.isNull() )
delete m_connector.data();
tLog() << "Failed to start HTTPd, could not create object";
return;
}
m_session.data()->setPort( 60210 ); //TODO config
m_session.data()->setListenInterface( QHostAddress::LocalHost );
m_session.data()->setConnector( m_connector.data() );
tLog() << "Starting HTTPd on" << m_session.listenInterface().toString() << m_session.port();
m_session.start();
Api_v1* api = new Api_v1( m_session.data() );
m_session.data()->setStaticContentService( api );
tLog() << "Starting HTTPd on" << m_session.data()->listenInterface().toString() << m_session.data()->port();
m_session.data()->start();
}

View File

@@ -101,6 +101,7 @@ public slots:
private slots:
void initServent();
void initSIP();
void initHTTP();
void spotifyApiCheckFinished();
@@ -114,8 +115,6 @@ private:
void initLocalCollection();
void initPipeline();
void initHTTP();
QWeakPointer<Database> m_database;
QWeakPointer<ScanManager> m_scanManager;
QWeakPointer<AudioEngine> m_audioEngine;
@@ -135,8 +134,8 @@ private:
bool m_headless, m_loaded;
QxtHttpServerConnector m_connector;
QxtHttpSessionManager m_session;
QWeakPointer< QxtHttpServerConnector > m_connector;
QWeakPointer< QxtHttpSessionManager > m_session;
};
Q_DECLARE_METATYPE( QPersistentModelIndex )

View File

@@ -741,11 +741,21 @@ TomahawkWindow::setWindowTitle( const QString& title )
void
TomahawkWindow::showAboutTomahawk()
{
QMessageBox::about( this, tr( "About Tomahawk" ),
tr( "<h2><b>Tomahawk %1<br/>(%2)</h2>Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>"
"Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson" )
.arg( TomahawkUtils::appFriendlyVersion() )
.arg( qApp->applicationVersion() ) );
QString head, desc;
#ifdef DEBUG_BUILD
head = tr( "<h2><b>Tomahawk %1<br/>(%2)</h2>" )
.arg( TomahawkUtils::appFriendlyVersion() )
.arg( qApp->applicationVersion() );
#else
head = tr( "<h2><b>Tomahawk %1</h2>" )
.arg( TomahawkUtils::appFriendlyVersion() );
#endif
desc = tr( "Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>"
"Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson" );
QMessageBox::about( this, tr( "About Tomahawk" ), head + desc );
}

View File

@@ -575,9 +575,14 @@ void QxtHttpSessionManager::processEvents()
*/
void QxtHttpSessionManager::chunkReadyRead(int requestID)
{
if (!connector()) return;
const QSharedPointer<QIODevice>& dataSource = connector()->getRequestDataSource( requestID );
if (!dataSource->bytesAvailable()) return;
QIODevice* device = connector()->getRequestConnection(requestID);
if (!device) return;
if (!device->bytesToWrite() || qxt_d().connectionState[device].readyRead == false)
{
qxt_d().connectionState[device].readyRead = true;
@@ -590,6 +595,9 @@ void QxtHttpSessionManager::chunkReadyRead(int requestID)
*/
void QxtHttpSessionManager::sendNextChunk(int requestID)
{
if ( !connector() )
return;
const QSharedPointer<QIODevice>& dataSource = connector()->getRequestDataSource( requestID );
QIODevice* device = connector()->getRequestConnection(requestID);
QxtHttpSessionManagerPrivate::ConnectionState& state = qxt_d().connectionState[device];