1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-06 20:20:41 +02:00

Compare commits

...

37 Commits

Author SHA1 Message Date
Jason Herskowitz
13ab10adea Edited ChangeLog via GitHub 2011-08-22 12:29:31 -04:00
Christian Muehlhaeuser
20d2a5ff59 * Bumped version to 0.2.2. 2011-08-22 17:46:55 +02:00
Leo Franchi
6b9eec00fd Changelog 2011-08-22 07:34:47 -04:00
Leo Franchi
d7416ddd57 TWK-415: Don't crash if the trackproxymodel has an invalid current track
(cherry picked from commit 498d7052de)
2011-08-22 07:32:48 -04:00
Leo Franchi
2e9aa7f3b3 Changelog. 2011-08-21 21:41:31 -04:00
Leo Franchi
208393deb0 TWK-401: Only update source stats after addfiles dbcmd is finished, instead of playing a race condition game. Also, refresh recent albums on stats change
(cherry picked from commit 6521198127)
2011-08-21 21:34:26 -04:00
Leo Franchi
b923d4f994 TWK-420: Show resolver wrench immediately after enabling a resolver.
(cherry picked from commit f0a6d4e8bb)
2011-08-21 21:34:20 -04:00
Christian Muehlhaeuser
edbee5a922 * Fixed filtering out unwanted dupes when viewing a local collection. 2011-08-21 22:52:53 +02:00
Michael Zanetti
e0bdc4b8bc TWK-411: Dragging an album/artist to "New Station" creates a station based on that artist instead of all the tracks 2011-08-21 16:00:21 +02:00
Christian Muehlhaeuser
4b53d20663 * Updated ChangeLog. 2011-08-21 04:51:52 +02:00
Christian Muehlhaeuser
74c4f4ddc2 * Fixed TWK-442: Provide stable sort ordering in Track- / TreeProxyModel. 2011-08-21 03:53:42 +02:00
Christian Muehlhaeuser
ec03ece35c * Fixed TWK-435: Don't block pipeline if a resolver times out. 2011-08-21 03:33:32 +02:00
Dominik Schmidt
d217e7fae0 TWK-410: possible fix 2011-08-21 03:33:25 +02:00
Christian Muehlhaeuser
bc3bcc708f * Don't filter out some wanted tracks, e.g. when multiple tracks of an album have the same name, but different tracks numbers. 2011-08-21 02:59:09 +02:00
Christian Muehlhaeuser
7a90ba11bb * Fixed TWK-387: Stop showing a loading spinner, if there are no items to load. 2011-08-21 02:58:59 +02:00
Michael Zanetti
ed219a56cd don't add duplicates when dropping albums or artists and fix losing items when combining synchronous and asynchrounous drop operations 2011-08-20 14:38:43 +02:00
Christian Muehlhaeuser
1537d5b08a * Updated ChangeLog. 2011-08-18 00:05:45 +02:00
Christian Muehlhaeuser
263340a270 * No need for clucene in Tomahawk binary. 2011-08-17 22:13:24 +02:00
Jason Herskowitz
bf54ebf7fe De-decapitate avatar placeholder
(cherry picked from commit 77d60fa02d)
2011-08-17 14:40:04 -04:00
Leo Franchi
bfbac296b0 Fix resolvers on qt 4.8, baseUrl is required for html5 security policy stuff for localStorage
(cherry picked from commit 4abb53ccdf)
2011-08-17 14:36:02 -04:00
Christian Muehlhaeuser
7971e7aadc * More verbose debug output in DBSyncConnection. 2011-08-17 04:15:28 +02:00
Christian Muehlhaeuser
4cc1690dea * Fixed sorting of identical items in Tree- & TrackProxyModel. 2011-08-17 03:34:21 +02:00
Christian Muehlhaeuser
2090d76955 * Fixed sidebar sorting for items with identical names. 2011-08-17 03:20:23 +02:00
Christian Muehlhaeuser
d09397a8ab * Moved the AudioControl QSlider stylesheets into SeekSlider. 2011-08-17 03:20:12 +02:00
Christian Muehlhaeuser
a734cad4e1 * No need for mouse tracking. 2011-08-17 02:27:39 +02:00
Christian Muehlhaeuser
9b433379ee * Fixed TWK-212: Sliders should jump to the position they are clicked on. 2011-08-17 02:27:25 +02:00
Christian Muehlhaeuser
2e11376d01 * Fix sorting for real. 2011-08-17 01:10:56 +02:00
Christian Muehlhaeuser
9ba11290ae * Fixed sidebar sorting. 2011-08-17 01:10:45 +02:00
Leo Franchi
97e0bbaf38 Allow to show the setting dialog always, and disable +/- buttons till loaded
(cherry picked from commit 713243cc5d)
2011-08-16 19:08:03 -04:00
Leo Franchi
eb4242622d Show spinner on initial load while loading sip plugins in preferences. Disable Settings action till servent is loaded
(cherry picked from commit ee8da33e06)
2011-08-16 19:07:57 -04:00
Michael Zanetti
469bea43ba added drag capabilities to albumview ins sourceinfowidget and welcomewidget 2011-08-16 23:49:24 +02:00
Michael Zanetti
f9f3a45a31 fix copyright headers in SettingsListDelegate 2011-08-16 19:53:50 +02:00
Michael Zanetti
0579d63013 fixed copyright headers 2011-08-16 19:53:41 +02:00
Michael Zanetti
1bb115ec6f Created DropJob which will start a query for the dropped items and wait for the results 2011-08-16 19:53:30 +02:00
Michael Zanetti
5f320028b8 more work to be able to drag and drop mixed mimetypes (e.g. an artist and some tracks) 2011-08-16 19:53:15 +02:00
Michael Zanetti
b0d445c262 added drag and drop to playlists for albums and artists 2011-08-16 19:53:06 +02:00
Christian Muehlhaeuser
479a0c469f * Un-regress listen-along. 2011-08-16 03:25:24 +02:00
53 changed files with 1066 additions and 382 deletions

View File

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

View File

@@ -203,7 +203,7 @@ Function PageReinstall
IntCmp $R0 ${VER_MINOR} build_check new_version older_version
build_check:
ReadRegDWORD $R0 HKLM "Software\Tomahawk" "VersionBuild"
IntCmp $R0 ${VER_BUILD} new_version older_version
IntCmp $R0 ${VER_BUILD} same_version new_version older_version
new_version:
!insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 1" "Text" "An older version of Tomahawk is installed on your system. It is recommended that you uninstall the current version before installing. Select the operation you want to perform and click Next to continue."

View File

@@ -1,3 +1,12 @@
Version 0.2.2:
* Fixed crash when pressing previous and next when playing a song from the Queue.
* Fixed issue where wrench for newly added resolvers would not show up immediately.
* Fixed sidebar statistics not updating after collection scan finished.
* Fixed omitting a few tracks in the Collection tree-view.
* Fixed sidebar & track sorting issues.
* Seek- & volume sliders now directly jump to the position you clicked on.
* Added ability to drag artists and albums within Tomahawk (to playlists, queue, etc.).
Version 0.2.1:
* Fixed crashing trying to play an unavailable track.
* Fixed a crash caused by using Javascript resolvers.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -237,7 +237,6 @@ TARGET_LINK_LIBRARIES( tomahawk
${QXTWEB_LIBRARIES}
${QJSON_LIBRARIES}
${TAGLIB_LIBRARIES}
${CLUCENE_LIBRARIES}
)

View File

@@ -35,6 +35,7 @@
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include <globalactionmanager.h>
#include "dropjob.h"
using namespace Tomahawk;
@@ -90,56 +91,9 @@ AudioControls::AudioControls( QWidget* parent )
ui->ownerLabel->setForegroundRole( QPalette::Dark );
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->setFixedHeight( 20 );
ui->seekSlider->setEnabled( true );
ui->seekSlider->setStyleSheet( "QSlider::groove::horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-bkg.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::sub-page:horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-level.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::handle::horizontal {"
"margin-bottom: -7px; margin-top: -7px;"
"margin-left: -4px; margin-right: -4px;"
"height: 17px; width: 16px;"
"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
"background-repeat: no-repeat;"
"}" );
ui->volumeSlider->setFixedHeight( 20 );
ui->volumeSlider->setRange( 0, 100 );
ui->volumeSlider->setValue( AudioEngine::instance()->volume() );
ui->volumeSlider->setStyleSheet( "QSlider::groove::horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/volume-slider-bkg.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::sub-page:horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-level.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::handle::horizontal {"
"margin-bottom: -7px; margin-top: -7px;"
"margin-left: -4px; margin-right: -4px;"
"height: 17px; width: 16px;"
"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
"background-repeat: no-repeat;"
"}" );
/* m_playAction = new QAction( this );
m_pauseAction = new QAction( this );
m_prevAction = new QAction( this );
m_nextAction = new QAction( this );
connect( m_playAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( play() ) );
connect( m_pauseAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( pause() ) );
connect( m_prevAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( previous() ) );
connect( m_nextAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( next() ) ); */
connect( ui->seekSlider, SIGNAL( valueChanged( int ) ), AudioEngine::instance(), SLOT( seek( int ) ) );
connect( ui->volumeSlider, SIGNAL( valueChanged( int ) ), AudioEngine::instance(), SLOT( setVolume( int ) ) );
@@ -179,7 +133,6 @@ AudioControls::AudioControls( QWidget* parent )
connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) );
ui->buttonAreaLayout->setSpacing( 0 );
ui->stackedLayout->setSpacing( 0 );
ui->stackedLayout->setContentsMargins( 0, 0, 0, 0 );
@@ -305,9 +258,6 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result )
ui->seekSlider->setValue( 0 );
ui->seekSlider->setVisible( true );
/* m_playAction->setEnabled( false );
m_pauseAction->setEnabled( true ); */
ui->stackedLayout->setCurrentWidget( ui->pauseButton );
ui->loveButton->setEnabled( true );
@@ -343,18 +293,12 @@ AudioControls::socialActionsLoaded()
void
AudioControls::onPlaybackPaused()
{
/* m_pauseAction->setEnabled( false );
m_playAction->setEnabled( true ); */
ui->stackedLayout->setCurrentWidget( ui->playPauseButton );
}
void
AudioControls::onPlaybackResumed()
{
/* m_playAction->setEnabled( false );
m_pauseAction->setEnabled( true ); */
ui->stackedLayout->setCurrentWidget( ui->pauseButton );
ui->loveButton->setVisible( true );
}
@@ -376,9 +320,6 @@ AudioControls::onPlaybackStopped()
ui->stackedLayout->setCurrentWidget( ui->playPauseButton );
ui->loveButton->setEnabled( false );
ui->loveButton->setVisible( false );
/* m_pauseAction->setEnabled( false );
m_playAction->setEnabled( true ); */
}
@@ -521,7 +462,7 @@ AudioControls::onTrackClicked()
void
AudioControls::dragEnterEvent( QDragEnterEvent* e )
{
if ( GlobalActionManager::instance()->acceptsMimeData( e->mimeData() ) )
if ( DropJob::acceptsMimeData( e->mimeData() ) )
e->acceptProposedAction();
}
@@ -538,10 +479,11 @@ void
AudioControls::dropEvent( QDropEvent* e )
{
tDebug() << "AudioControls got drop:" << e->mimeData()->formats();
if ( GlobalActionManager::instance()->acceptsMimeData( e->mimeData() ) )
if ( DropJob::acceptsMimeData( e->mimeData() ) )
{
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( droppedTracks( QList<Tomahawk::query_ptr> ) ) );
GlobalActionManager::instance()->tracksFromMimeData( e->mimeData() );
DropJob *dj = new DropJob();
connect( dj, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( droppedTracks( QList<Tomahawk::query_ptr> ) ) );
dj->tracksFromMimeData( e->mimeData() );
e->accept();
}
@@ -551,8 +493,6 @@ AudioControls::dropEvent( QDropEvent* e )
void
AudioControls::droppedTracks( QList< query_ptr > tracks )
{
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( droppedTracks( QList<Tomahawk::query_ptr> ) ) );
if ( !tracks.isEmpty() )
{
// queue and play the first if nothign is playing

View File

@@ -82,11 +82,6 @@ private slots:
private:
Ui::AudioControls *ui;
QAction* m_playAction;
QAction* m_pauseAction;
QAction* m_prevAction;
QAction* m_nextAction;
QPixmap m_defaultCover;
Tomahawk::result_ptr m_currentTrack;

View File

@@ -337,7 +337,7 @@
</widget>
</item>
<item>
<widget class="QSlider" name="seekSlider">
<widget class="SeekSlider" name="seekSlider">
<property name="minimumSize">
<size>
<width>0</width>
@@ -489,7 +489,7 @@
</widget>
</item>
<item>
<widget class="QSlider" name="volumeSlider">
<widget class="SeekSlider" name="volumeSlider">
<property name="minimumSize">
<size>
<width>0</width>
@@ -529,6 +529,11 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SeekSlider</class>
<extends>QSlider</extends>
<header location="global">widgets/SeekSlider.h</header>
</customwidget>
<customwidget>
<class>ImageButton</class>
<extends>QPushButton</extends>

View File

@@ -31,6 +31,7 @@ set( libSources
viewmanager.cpp
globalactionmanager.cpp
contextmenu.cpp
dropjob.cpp
sip/SipPlugin.cpp
sip/SipHandler.cpp
@@ -176,6 +177,7 @@ set( libSources
widgets/newplaylistwidget.cpp
widgets/searchwidget.cpp
widgets/SeekSlider.cpp
widgets/playlisttypeselectordlg.cpp
widgets/welcomewidget.cpp
widgets/welcomeplaylistmodel.cpp
@@ -208,6 +210,7 @@ set( libHeaders
viewmanager.h
globalactionmanager.h
contextmenu.h
dropjob.h
artist.h
album.h
@@ -354,6 +357,7 @@ set( libHeaders
widgets/newplaylistwidget.h
widgets/searchwidget.h
widgets/SeekSlider.h
widgets/playlisttypeselectordlg.h
widgets/welcomewidget.h
widgets/welcomeplaylistmodel.h

View File

@@ -212,7 +212,7 @@ AudioEngine::next()
return;
if ( !m_currentTrack.isNull() && !m_playlist.data()->hasNextItem() &&
m_currentTrack->id() == m_playlist.data()->currentItem()->id() )
( m_playlist.data()->currentItem().isNull() || ( m_currentTrack->id() == m_playlist.data()->currentItem()->id() ) ) )
{
//For instance, when doing a catch-up while listening along, but the person
//you're following hasn't started a new track yet...don't do anything
@@ -487,8 +487,6 @@ AudioEngine::playItem( Tomahawk::PlaylistInterface* playlist, const Tomahawk::re
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO << ( result.isNull() ? QString() : result->url() );
if ( !result->isOnline() )
return;
if ( !m_playlist.isNull() )
m_playlist.data()->reset();

View File

@@ -27,6 +27,7 @@
#include "databasecommand_collectionstats.h"
#include "databaseimpl.h"
#include "network/controlconnection.h"
#include "sourcelist.h"
#include "utils/logger.h"
@@ -72,7 +73,15 @@ DatabaseCommand_AddFiles::postCommitHook()
emit notify( m_queries );
if( source()->isLocal() )
{
Servent::instance()->triggerDBSync();
// Re-calculate local db stats
DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( SourceList::instance()->getLocal() );
connect( cmd, SIGNAL( done( QVariantMap ) ),
SourceList::instance()->getLocal().data(), SLOT( setStats( QVariantMap ) ), Qt::QueuedConnection );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
}

View File

@@ -74,8 +74,7 @@ DatabaseCommand_AllAlbums::execForArtist( DatabaseImpl* dbi )
al << album;
}
if ( al.count() )
emit albums( al, data() );
emit albums( al, data() );
emit done();
}
@@ -124,8 +123,7 @@ DatabaseCommand_AllAlbums::execForCollection( DatabaseImpl* dbi )
al << album;
}
if ( al.count() )
emit albums( al, data() );
emit albums( al, data() );
emit done();
}

358
src/libtomahawk/dropjob.cpp Normal file
View File

@@ -0,0 +1,358 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Michael Zanetti <mzanetti@kde.org>
* Copyright 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dropjob.h"
#include "artist.h"
#include "album.h"
#include "utils/spotifyparser.h"
#include "utils/rdioparser.h"
#include "utils/shortenedlinkparser.h"
#include "utils/logger.h"
#include "globalactionmanager.h"
using namespace Tomahawk;
DropJob::DropJob( QObject *parent )
: QObject( parent )
, m_queryCount( 0 )
{
}
DropJob::~DropJob()
{
qDebug() << "destryong DropJob";
}
/// QMIMEDATA HANDLING
QStringList
DropJob::mimeTypes()
{
QStringList mimeTypes;
mimeTypes << "application/tomahawk.query.list"
<< "application/tomahawk.plentry.list"
<< "application/tomahawk.result.list"
<< "application/tomahawk.result"
<< "application/tomahawk.metadata.artist"
<< "application/tomahawk.metadata.album"
<< "application/tomahawk.mixed"
<< "text/plain";
return mimeTypes;
}
bool
DropJob::acceptsMimeData( const QMimeData* data, bool tracksOnly )
{
if ( data->hasFormat( "application/tomahawk.query.list" )
|| data->hasFormat( "application/tomahawk.plentry.list" )
|| data->hasFormat( "application/tomahawk.result.list" )
|| data->hasFormat( "application/tomahawk.result" )
|| data->hasFormat( "application/tomahawk.mixed" )
|| data->hasFormat( "application/tomahawk.metadata.album" )
|| data->hasFormat( "application/tomahawk.metadata.artist" ) )
{
return true;
}
// crude check for spotify tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "spotify" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// crude check for rdio tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rdio.com" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// We whitelist t.co and bit.ly (and j.mp) since they do some link checking. Often playable (e.g. spotify..) links hide behind them,
// so we do an extra level of lookup
if ( ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "bit.ly" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "j.mp" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "t.co" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rd.io" ) ) )
return true;
return false;
}
void
DropJob::tracksFromMimeData( const QMimeData* data, bool allowDuplicates )
{
m_allowDuplicates = allowDuplicates;
parseMimeData( data );
if ( m_queryCount == 0 )
{
if ( !allowDuplicates )
removeDuplicates();
emit tracks( m_resultList );
deleteLater();
}
}
void
DropJob::parseMimeData( const QMimeData *data )
{
QList< query_ptr > results;
if ( data->hasFormat( "application/tomahawk.query.list" ) )
results = tracksFromQueryList( data );
else if ( data->hasFormat( "application/tomahawk.result.list" ) )
results = tracksFromResultList( data );
else if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
results = tracksFromAlbumMetaData( data );
else if ( data->hasFormat( "application/tomahawk.metadata.artist" ) )
results = tracksFromArtistMetaData( data );
else if ( data->hasFormat( "application/tomahawk.mixed" ) )
tracksFromMixedData( data );
else if ( data->hasFormat( "text/plain" ) )
{
QString plainData = QString::fromUtf8( data->data( "text/plain" ).constData() );
tDebug() << "Got text/plain mime data:" << data->data( "text/plain" ) << "decoded to:" << plainData;
handleTrackUrls ( plainData );
}
m_resultList.append( results );
}
QList< query_ptr >
DropJob::tracksFromQueryList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.query.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
query_ptr* query = reinterpret_cast<query_ptr*>(qptr);
if ( query && !query->isNull() )
{
tDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track();
queries << *query;
}
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromResultList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.result.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
result_ptr* result = reinterpret_cast<result_ptr*>(qptr);
if ( result && !result->isNull() )
{
tDebug() << "Dropped result item:" << result->data()->artist()->name() << "-" << result->data()->track();
query_ptr q = result->data()->toQuery();
q->addResults( QList< result_ptr >() << *result );
queries << q;
}
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromAlbumMetaData( const QMimeData *data )
{
QList<query_ptr> queries;
QByteArray itemData = data->data( "application/tomahawk.metadata.album" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
QString artist;
stream >> artist;
QString album;
stream >> album;
artist_ptr artistPtr = Artist::get( artist );
album_ptr albumPtr = Album::get( artistPtr, album );
if ( albumPtr->tracks().isEmpty() )
{
connect( albumPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
m_queryCount++;
}
else
queries << albumPtr->tracks();
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromArtistMetaData( const QMimeData *data )
{
QList<query_ptr> queries;
QByteArray itemData = data->data( "application/tomahawk.metadata.artist" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
QString artist;
stream >> artist;
artist_ptr artistPtr = Artist::get( artist );
if ( artistPtr->tracks().isEmpty() )
{
connect( artistPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
m_queryCount++;
}
else
queries << artistPtr->tracks();
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromMixedData( const QMimeData *data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.mixed" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
QString mimeType;
while ( !stream.atEnd() )
{
stream >> mimeType;
qDebug() << "mimetype is" << mimeType;
QByteArray singleData;
QDataStream singleStream( &singleData, QIODevice::WriteOnly );
QMimeData singleMimeData;
if ( mimeType == "application/tomahawk.query.list" || mimeType == "application/tomahawk.result.list" )
{
qlonglong query;
stream >> query;
singleStream << query;
}
else if ( mimeType == "application/tomahawk.metadata.album" )
{
QString artist;
stream >> artist;
singleStream << artist;
QString album;
stream >> album;
singleStream << album;
qDebug() << "got artist" << artist << "and album" << album;
}
else if ( mimeType == "application/tomahawk.metadata.artist" )
{
QString artist;
stream >> artist;
singleStream << artist;
qDebug() << "got artist" << artist;
}
singleMimeData.setData( mimeType, singleData );
parseMimeData( &singleMimeData );
}
return queries;
}
void
DropJob::handleTrackUrls( const QString& urls )
{
if ( urls.contains( "open.spotify.com/track") ||
urls.contains( "spotify:track" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of spotify urls!" << tracks;
SpotifyParser* spot = new SpotifyParser( tracks, this );
connect( spot, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ) );
} else if ( urls.contains( "rdio.com" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of rdio urls!" << tracks;
RdioParser* rdio = new RdioParser( this );
connect( rdio, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ) );
rdio->parse( tracks );
} else if ( urls.contains( "bit.ly" ) ||
urls.contains( "j.mp" ) ||
urls.contains( "t.co" ) ||
urls.contains( "rd.io" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of shortened urls!" << tracks;
ShortenedLinkParser* parser = new ShortenedLinkParser( tracks, this );
connect( parser, SIGNAL( urls( QStringList ) ), this, SLOT( expandedUrls( QStringList ) ) );
}
}
void
DropJob::expandedUrls( QStringList urls )
{
handleTrackUrls( urls.join( "\n" ) );
}
void
DropJob::onTracksAdded( const QList<Tomahawk::query_ptr>& tracksList )
{
m_resultList.append( tracksList );
if ( --m_queryCount == 0 )
{
if ( !m_allowDuplicates )
removeDuplicates();
emit tracks( m_resultList );
deleteLater();
}
}
void
DropJob::removeDuplicates()
{
QList< Tomahawk::query_ptr > list;
foreach ( const Tomahawk::query_ptr& item, m_resultList )
{
bool contains = false;
foreach( const Tomahawk::query_ptr &tmpItem, list )
if ( item->album() == tmpItem->album()
&& item->artist() == tmpItem->artist()
&& item->track() == tmpItem->track() )
contains = true;
if ( !contains )
list.append( item );
}
m_resultList = list;
}

76
src/libtomahawk/dropjob.h Normal file
View File

@@ -0,0 +1,76 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Michael Zanetti <mzanetti@kde.org>
* Copyright 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DROPJOB_H
#define DROPJOB_H
#include "query.h"
#include <QObject>
#include <QStringList>
#include <QMimeData>
class DLLEXPORT DropJob : public QObject
{
Q_OBJECT
public:
explicit DropJob( QObject *parent = 0 );
~DropJob();
/**
* QMimeData helpers
*
* Call this to parse the tracks in a QMimeData object to query_ptrs. This will parse internal tomahawk
* data as well as all other formats supported (spotify, etc).
*
* Connect to tracks( QList< query_ptr> ); for the extracted tracks.
*/
static bool acceptsMimeData( const QMimeData* data, bool tracksOnly = true );
static QStringList mimeTypes();
void tracksFromMimeData( const QMimeData* data, bool allowDuplicates = false );
signals:
/// QMimeData parsing results
void tracks( const QList< Tomahawk::query_ptr >& tracks );
private slots:
void expandedUrls( QStringList );
void onTracksAdded( const QList<Tomahawk::query_ptr>& );
private:
/// handle parsing mime data
void parseMimeData( const QMimeData* data );
void handleTrackUrls( const QString& urls );
QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromResultList( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromArtistMetaData( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromAlbumMetaData( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromMixedData( const QMimeData* d );
void removeDuplicates();
int m_queryCount;
bool m_allowDuplicates;
QList< Tomahawk::query_ptr > m_resultList;
};
#endif // DROPJOB_H

View File

@@ -64,7 +64,6 @@ GlobalActionManager::instance()
GlobalActionManager::GlobalActionManager( QObject* parent )
: QObject( parent )
{
m_mimeTypes << "application/tomahawk.query.list" << "application/tomahawk.plentry.list" << "application/tomahawk.result.list" << "text/plain";
}
GlobalActionManager::~GlobalActionManager()
@@ -779,149 +778,6 @@ GlobalActionManager::hostname() const
return QString( "http://toma.hk" );
}
/// QMIMEDATA HANDLING
QStringList
GlobalActionManager::mimeTypes() const
{
return m_mimeTypes;
}
bool
GlobalActionManager::acceptsMimeData( const QMimeData* data, bool tracksOnly )
{
if ( data->hasFormat( "application/tomahawk.query.list" )
|| data->hasFormat( "application/tomahawk.plentry.list" )
|| data->hasFormat( "application/tomahawk.result.list" ) )
{
return true;
}
// crude check for spotify tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "spotify" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// crude check for rdio tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rdio.com" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// We whitelist t.co and bit.ly (and j.mp) since they do some link checking. Often playable (e.g. spotify..) links hide behind them,
// so we do an extra level of lookup
if ( ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "bit.ly" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "j.mp" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "t.co" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rd.io" ) ) )
return true;
return false;
}
void
GlobalActionManager::tracksFromMimeData( const QMimeData* data )
{
if ( data->hasFormat( "application/tomahawk.query.list" ) )
emit tracks( tracksFromQueryList( data ) );
else if ( data->hasFormat( "application/tomahawk.result.list" ) )
emit tracks( tracksFromResultList( data ) );
else if ( data->hasFormat( "text/plain" ) )
{
QString plainData = QString::fromUtf8( data->data( "text/plain" ).constData() );
tDebug() << "Got text/plain mime data:" << data->data( "text/plain" ) << "decoded to:" << plainData;
handleTrackUrls ( plainData );
}
}
void
GlobalActionManager::handleTrackUrls( const QString& urls )
{
if ( urls.contains( "open.spotify.com/track") ||
urls.contains( "spotify:track" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of spotify urls!" << tracks;
SpotifyParser* spot = new SpotifyParser( tracks, this );
connect( spot, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ) );
} else if ( urls.contains( "rdio.com" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of rdio urls!" << tracks;
RdioParser* rdio = new RdioParser( this );
connect( rdio, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ) );
rdio->parse( tracks );
} else if ( urls.contains( "bit.ly" ) ||
urls.contains( "j.mp" ) ||
urls.contains( "t.co" ) ||
urls.contains( "rd.io" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of shortened urls!" << tracks;
ShortenedLinkParser* parser = new ShortenedLinkParser( tracks, this );
connect( parser, SIGNAL( urls( QStringList ) ), this, SLOT( expandedUrls( QStringList ) ) );
}
}
void
GlobalActionManager::expandedUrls( QStringList urls )
{
handleTrackUrls( urls.join( "\n" ) );
}
QList< query_ptr >
GlobalActionManager::tracksFromQueryList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.query.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
query_ptr* query = reinterpret_cast<query_ptr*>(qptr);
if ( query && !query->isNull() )
{
tDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track();
queries << *query;
}
}
return queries;
}
QList< query_ptr >
GlobalActionManager::tracksFromResultList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.result.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
result_ptr* result = reinterpret_cast<result_ptr*>(qptr);
if ( result && !result->isNull() )
{
tDebug() << "Dropped result item:" << result->data()->artist()->name() << "-" << result->data()->track();
query_ptr q = result->data()->toQuery();
q->addResults( QList< result_ptr >() << *result );
queries << q;
}
}
return queries;
}
/// SPOTIFY URL HANDLING

View File

@@ -51,18 +51,6 @@ public:
QString copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist );
void savePlaylistToFile( const Tomahawk::playlist_ptr& playlist, const QString& filename );
/**
* QMimeData helpers
*
* Call this to parse the tracks in a QMimeData object to query_ptrs. This will parse internal tomahawk
* data as well as all other formats supported (spotify, etc).
*
* Connect to tracks( QList< query_ptr> ); for the extracted tracks.
*/
bool acceptsMimeData( const QMimeData* data, bool tracksOnly = true );
void tracksFromMimeData( const QMimeData* data );
QStringList mimeTypes() const;
public slots:
bool parseTomahawkLink( const QString& link );
void waitingForResolved( bool );
@@ -70,16 +58,12 @@ public slots:
Tomahawk::dynplaylist_ptr loadDynamicPlaylist( const QUrl& url, bool station );
void handleOpenTrack( const Tomahawk::query_ptr& qry );
signals:
/// QMimeData parsing results
void tracks( const QList< Tomahawk::query_ptr >& tracks );
private slots:
void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl );
void showPlaylist();
void xspfCreated( const QByteArray& xspf );
void expandedUrls( QStringList );
void spotifyToPlay( const Tomahawk::query_ptr& );
private:
@@ -101,18 +85,12 @@ private:
bool playSpotify( const QUrl& url );
bool queueSpotify( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems );
/// handle parsing mime data
void handleTrackUrls( const QString& urls );
QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromResultList( const QMimeData* d );
QString hostname() const;
Tomahawk::playlist_ptr m_toShow;
Tomahawk::query_ptr m_waitingToBookmark;
Tomahawk::query_ptr m_waitingToPlay;
QStringList m_mimeTypes;
static GlobalActionManager* s_instance;
};

View File

@@ -283,7 +283,7 @@ DBSyncConnection::lastOpApplied()
void
DBSyncConnection::sendOps()
{
tLog() << "Will send peer all ops since" << m_uscache.value( "lastop" ).toString();
tLog() << "Will send peer" << m_source->id() << "all ops since" << m_uscache.value( "lastop" ).toString();
source_ptr src = SourceList::instance()->getLocal();

View File

@@ -60,6 +60,7 @@ Servent::Servent( QObject* parent )
: QTcpServer( parent )
, m_port( 0 )
, m_externalPort( 0 )
, m_ready( false )
, m_portfwd( 0 )
{
s_instance = this;
@@ -133,6 +134,7 @@ Servent::startListening( QHostAddress ha, bool upnp, int port )
tLog() << "Forcing static preferred host and port";
m_externalHostname = TomahawkSettings::instance()->externalHostname();
m_externalPort = TomahawkSettings::instance()->externalPort();
m_ready = true;
emit ready();
return true;
}
@@ -155,7 +157,10 @@ Servent::startListening( QHostAddress ha, bool upnp, int port )
QMetaObject::invokeMethod( this, "setExternalAddress", Qt::QueuedConnection, Q_ARG( QHostAddress, ha ), Q_ARG( unsigned int, m_port ) );
}
else
{
m_ready = true;
emit ready();
}
break;
}
break;
@@ -227,6 +232,7 @@ Servent::setExternalAddress( QHostAddress ha, unsigned int port )
qDebug() << "No external access, LAN and outbound connections only!";
}
m_ready = true;
emit ready();
}

View File

@@ -1,5 +1,5 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@@ -123,6 +123,8 @@ public:
QSharedPointer<QIODevice> localFileIODeviceFactory( const Tomahawk::result_ptr& result );
QSharedPointer<QIODevice> httpIODeviceFactory( const Tomahawk::result_ptr& result );
bool isReady() const { return m_ready; };
signals:
void streamStarted( StreamConnection* );
void streamFinished( StreamConnection* );
@@ -159,6 +161,7 @@ private:
int m_port, m_externalPort;
QHostAddress m_externalAddress;
QString m_externalHostname;
bool m_ready;
// currently active file transfers:
QList< StreamConnection* > m_scsessions;

View File

@@ -196,22 +196,7 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results )
}
}
if ( decQIDState( q ) == 0 )
{
if ( !q->solved() || q->isFullTextQuery() )
q->onResolvingFinished();
if ( !m_queries_temporary.contains( q ) )
m_qids.remove( q->id() );
if ( m_qidsTimeout.contains( q->id() ) )
m_qidsTimeout.remove( q->id() );
shuntNext();
}
else
{
new FuncTimeout( 0, boost::bind( &Pipeline::timeoutShunt, this, q ), this );
}
decQIDState( q );
}
@@ -260,8 +245,7 @@ Pipeline::timeoutShunt( const query_ptr& q )
// are we still waiting for a timeout?
if ( m_qidsTimeout.contains( q->id() ) )
{
m_qidsTimeout.remove( q->id() );
shunt( q );
decQIDState( q );
}
}
@@ -278,7 +262,7 @@ Pipeline::shunt( const query_ptr& q )
if ( r )
{
qDebug() << "Dispatching to resolver" << r->name() << q->toString() << q->solved() << q->id();
tDebug() << "Dispatching to resolver" << r->name() << q->toString() << q->solved() << q->id();
q->setCurrentResolver( r );
r->resolve( q );
@@ -374,6 +358,25 @@ Pipeline::decQIDState( const Tomahawk::query_ptr& query )
// qDebug() << "Queries running:" << m_qidsState.count();
}
if ( state == 0 )
{
if ( !query->solved() || query->isFullTextQuery() )
query->onResolvingFinished();
if ( !m_queries_temporary.contains( query ) )
m_qids.remove( query->id() );
if ( m_qidsTimeout.contains( query->id() ) )
m_qidsTimeout.remove( query->id() );
new FuncTimeout( 0, boost::bind( &Pipeline::shuntNext, this ), this );
}
else
{
if ( m_qidsTimeout.contains( query->id() ) )
m_qidsTimeout.remove( query->id() );
new FuncTimeout( 0, boost::bind( &Pipeline::shunt, this, query ), this );
}
return state;
}

View File

@@ -159,6 +159,7 @@ AlbumModel::headerData( int section, Qt::Orientation orientation, int role ) con
Qt::ItemFlags
AlbumModel::flags( const QModelIndex& index ) const
{
qDebug() << "asking for flags for index" << index;
Qt::ItemFlags defaultFlags = QAbstractItemModel::flags( index );
if ( index.isValid() && index.column() == 0 )
@@ -195,12 +196,13 @@ AlbumModel::mimeData( const QModelIndexList &indexes ) const
if ( item )
{
const album_ptr& album = item->album();
queryStream << qlonglong( &album );
queryStream << album->artist()->name();
queryStream << album->name();
}
}
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.query.list", queryData );
mimeData->setData( "application/tomahawk.metadata.album", queryData );
return mimeData;
}

View File

@@ -188,27 +188,6 @@ AlbumView::onScrollTimeout()
}
void
AlbumView::dragEnterEvent( QDragEnterEvent* event )
{
QListView::dragEnterEvent( event );
}
void
AlbumView::dragMoveEvent( QDragMoveEvent* event )
{
QListView::dragMoveEvent( event );
}
void
AlbumView::dropEvent( QDropEvent* event )
{
QListView::dropEvent( event );
}
void
AlbumView::paintEvent( QPaintEvent* event )
{
@@ -227,14 +206,30 @@ AlbumView::onFilterChanged( const QString& )
void
AlbumView::startDrag( Qt::DropActions supportedActions )
{
Q_UNUSED( supportedActions );
}
QList<QPersistentModelIndex> pindexes;
QModelIndexList indexes;
foreach( const QModelIndex& idx, selectedIndexes() )
{
if ( ( m_proxyModel->flags( idx ) & Qt::ItemIsDragEnabled ) )
{
indexes << idx;
pindexes << idx;
}
}
if ( indexes.count() == 0 )
return;
// Inspired from dolphin's draganddrophelper.cpp
QPixmap
AlbumView::createDragPixmap( int itemCount ) const
{
Q_UNUSED( itemCount );
return QPixmap();
qDebug() << "Dragging" << indexes.count() << "indexes";
QMimeData* data = m_proxyModel->mimeData( indexes );
if ( !data )
return;
QDrag* drag = new QDrag( this );
drag->setMimeData( data );
const QPixmap p = TomahawkUtils::createDragPixmap( indexes.count() );
drag->setPixmap( p );
drag->setHotSpot( QPoint( -20, -20 ) );
Qt::DropAction action = drag->exec( supportedActions, Qt::CopyAction );
}

View File

@@ -60,9 +60,6 @@ public slots:
protected:
virtual void startDrag( Qt::DropActions supportedActions );
virtual void dragEnterEvent( QDragEnterEvent* event );
virtual void dragMoveEvent( QDragMoveEvent* event );
virtual void dropEvent( QDropEvent* event );
void paintEvent( QPaintEvent* event );
@@ -73,8 +70,6 @@ private slots:
void onScrollTimeout();
private:
QPixmap createDragPixmap( int itemCount ) const;
AlbumModel* m_model;
AlbumProxyModel* m_proxyModel;
// PlaylistItemDelegate* m_delegate;

View File

@@ -1,5 +1,5 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@@ -21,31 +21,33 @@
#include <QWidget>
#include "dllmacro.h"
class QMovie;
class QTimeLine;
/**
* A small widget that displays an animated loading spinner
*/
class LoadingSpinner : public QWidget {
class DLLEXPORT LoadingSpinner : public QWidget {
Q_OBJECT
public:
LoadingSpinner( QWidget* parent );
virtual ~LoadingSpinner();
virtual QSize sizeHint() const;
virtual void paintEvent( QPaintEvent* );
virtual void resizeEvent( QResizeEvent* );
public slots:
public slots:
void fadeIn();
void fadeOut();
private slots:
void hideFinished();
private:
void reposition();
QTimeLine* m_showHide;
QMovie* m_anim;
};

View File

@@ -27,7 +27,7 @@
#include "database/databasecommand_playbackhistory.h"
#include "dynamic/GeneratorInterface.h"
#include "utils/logger.h"
#include "globalactionmanager.h"
#include "dropjob.h"
using namespace Tomahawk;
@@ -358,14 +358,15 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
if ( action == Qt::IgnoreAction || isReadOnly() )
return true;
if ( !GlobalActionManager::instance()->acceptsMimeData( data ) )
if ( !DropJob::acceptsMimeData( data ) )
return false;
m_dropStorage.row = row;
m_dropStorage.parent = QPersistentModelIndex( parent );
m_dropStorage.action = action;
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
GlobalActionManager::instance()->tracksFromMimeData( data );
DropJob *dj = new DropJob();
connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
dj->tracksFromMimeData( data );
return true;
}
@@ -377,8 +378,6 @@ PlaylistModel::parsedDroppedTracks( QList< query_ptr > tracks )
if ( m_dropStorage.row == -10 ) // nope
return;
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
int beginRow;
if ( m_dropStorage.row != -1 )
beginRow = m_dropStorage.row;

View File

@@ -177,7 +177,7 @@ Tomahawk::result_ptr
TrackProxyModel::currentItem() const
{
TrackModelItem* item = itemFromIndex( mapToSource( currentIndex() ) );
if ( item && item->query()->playable() )
if ( item && !item->query().isNull() && item->query()->playable() )
return item->query()->results().at( 0 );
return Tomahawk::result_ptr();
}
@@ -318,8 +318,8 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
unsigned int albumpos1 = 0, albumpos2 = 0;
unsigned int bitrate1 = 0, bitrate2 = 0;
unsigned int mtime1 = 0, mtime2 = 0;
unsigned int id1 = 0, id2 = 0;
unsigned int size1 = 0, size2 = 0;
qint64 id1 = 0, id2 = 0;
if ( q1->numResults() )
{
@@ -346,6 +346,13 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
size2 = r->size();
}
// This makes it a stable sorter and prevents items from randomly jumping about.
if ( id1 == id2 )
{
id1 = (qint64)&q1;
id2 = (qint64)&q2;
}
if ( left.column() == TrackModel::Artist ) // sort by artist
{
if ( artist1 == artist2 )
@@ -396,6 +403,11 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
return size1 < size2;
}
return QString::localeAwareCompare( sourceModel()->data( left ).toString(),
sourceModel()->data( right ).toString() ) < 0;
const QString& lefts = sourceModel()->data( left ).toString();
const QString& rights = sourceModel()->data( right ).toString();
if ( lefts == rights )
return id1 < id2;
return QString::localeAwareCompare( lefts, rights ) < 0;
}

View File

@@ -33,7 +33,7 @@
#include "dynamic/widgets/LoadingSpinner.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include <globalactionmanager.h>
#include "dropjob.h"
using namespace Tomahawk;
@@ -214,7 +214,7 @@ TrackView::dragEnterEvent( QDragEnterEvent* event )
qDebug() << Q_FUNC_INFO;
QTreeView::dragEnterEvent( event );
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
if ( DropJob::acceptsMimeData( event->mimeData() ) )
{
m_dragging = true;
m_dropRect = QRect();
@@ -236,7 +236,7 @@ TrackView::dragMoveEvent( QDragMoveEvent* event )
return;
}
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
if ( DropJob::acceptsMimeData( event->mimeData() ) )
{
setDirtyRegion( m_dropRect );
const QPoint pos = event->pos();
@@ -278,7 +278,7 @@ TrackView::dropEvent( QDropEvent* event )
}
else
{
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
if ( DropJob::acceptsMimeData( event->mimeData() ) )
{
const QPoint pos = event->pos();
const QModelIndex index = indexAt( pos );

View File

@@ -293,6 +293,8 @@ TreeModel::flags( const QModelIndex& index ) const
TreeModelItem* item = itemFromIndex( index );
if ( item && !item->result().isNull() )
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
if ( item && ( !item->album().isNull() || !item->artist().isNull() ) )
return Qt::ItemIsDragEnabled | defaultFlags;
}
return defaultFlags;
@@ -303,7 +305,7 @@ QStringList
TreeModel::mimeTypes() const
{
QStringList types;
types << "application/tomahawk.result.list";
types << "application/tomahawk.mixed";
return types;
}
@@ -316,23 +318,124 @@ TreeModel::mimeData( const QModelIndexList &indexes ) const
QByteArray resultData;
QDataStream resultStream( &resultData, QIODevice::WriteOnly );
foreach ( const QModelIndex& i, indexes )
// lets try with artist only
bool fail = false;
foreach ( const QModelIndex& i, indexes)
{
if ( i.column() > 0 )
if ( i.column() > 0 || indexes.contains( i.parent() ) )
continue;
QModelIndex idx = index( i.row(), 0, i.parent() );
TreeModelItem* item = itemFromIndex( idx );
if ( item && !item->result().isNull() )
TreeModelItem* item = itemFromIndex( i );
if ( !item )
continue;
if ( !item->artist().isNull() )
{
const artist_ptr& artist = item->artist();
resultStream << artist->name();
}
else
{
fail = true;
break;
}
}
if ( !fail )
{
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.metadata.artist", resultData );
return mimeData;
}
// lets try with album only
fail = false;
foreach ( const QModelIndex& i, indexes)
{
if ( i.column() > 0 || indexes.contains( i.parent() ) )
continue;
TreeModelItem* item = itemFromIndex( i );
if ( !item )
continue;
if ( !item->album().isNull() )
{
const album_ptr& album = item->album();
resultStream << album->artist()->name();
resultStream << album->name();
}
else
{
fail = true;
break;
}
}
if ( !fail )
{
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.metadata.album", resultData );
return mimeData;
}
// lets try with tracks only
fail = false;
foreach ( const QModelIndex& i, indexes)
{
if ( i.column() > 0 || indexes.contains( i.parent() ) )
continue;
TreeModelItem* item = itemFromIndex( i );
if ( !item )
continue;
if ( !item->result().isNull() )
{
const result_ptr& result = item->result();
resultStream << qlonglong( &result );
}
else
{
fail = true;
break;
}
}
if ( !fail )
{
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.result.list", resultData );
return mimeData;
}
// Ok... we have to use mixed
foreach ( const QModelIndex& i, indexes )
{
if ( i.column() > 0 || indexes.contains( i.parent() ) )
continue;
TreeModelItem* item = itemFromIndex( i );
if ( !item )
continue;
if ( !item->artist().isNull() )
{
const artist_ptr& artist = item->artist();
resultStream << QString( "application/tomahawk.metadata.artist" ) << artist->name();
}
else if ( !item->album().isNull() )
{
const album_ptr& album = item->album();
resultStream << QString( "application/tomahawk.metadata.album" ) << album->artist()->name() << album->name();
}
else if ( !item->result().isNull() )
{
const result_ptr& result = item->result();
resultStream << QString( "application/tomahawk.result.list" ) << qlonglong( &result );
}
}
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.result.list", resultData );
mimeData->setData( "application/tomahawk.mixed", resultData );
return mimeData;
}
@@ -513,6 +616,8 @@ void
TreeModel::onAlbumsAdded( const QList<Tomahawk::album_ptr>& albums, const QVariant& data )
{
qDebug() << Q_FUNC_INFO << albums.count() << data.toInt();
emit loadingFinished();
if ( !albums.count() )
return;
@@ -555,8 +660,6 @@ TreeModel::onAlbumsAdded( const QList<Tomahawk::album_ptr>& albums, const QVaria
emit endInsertRows();
else
emit dataChanged( albumitem->index, albumitem->index.sibling( albumitem->index.row(), columnCount( QModelIndex() ) - 1 ) );
emit loadingFinished();
}
@@ -564,6 +667,8 @@ void
TreeModel::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks, const QVariant& data )
{
qDebug() << Q_FUNC_INFO << tracks.count();
emit loadingFinished();
if ( !tracks.count() )
return;
@@ -597,8 +702,6 @@ TreeModel::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks, const QVaria
emit endInsertRows();
emit dataChanged( item->index.sibling( 0, 0 ), item->index.sibling( item->index.row(), columnCount( QModelIndex() ) - 1 ) );
emit loadingFinished();
}

View File

@@ -81,8 +81,11 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent
QList< Tomahawk::result_ptr > rl = m_cache.values( sourceParent );
foreach ( const Tomahawk::result_ptr& result, rl )
{
if ( result->track() == pi->result()->track() )
if ( result->track() == pi->result()->track() &&
( result->albumpos() == pi->result()->albumpos() || result->albumpos() == 0 ) )
{
return ( result.data() == pi->result().data() );
}
}
for ( int i = 0; i < sourceModel()->rowCount( sourceParent ); i++ )
@@ -91,7 +94,9 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent
continue;
TreeModelItem* ti = sourceModel()->itemFromIndex( sourceModel()->index( i, 0, sourceParent ) );
if ( ti->result()->track() == pi->result()->track() )
if ( ti->result()->track() == pi->result()->track() &&
( ti->result()->albumpos() == pi->result()->albumpos() || ti->result()->albumpos() == 0 ) )
{
if ( !pi->result()->isOnline() && ti->result()->isOnline() )
return false;
@@ -133,13 +138,19 @@ TreeProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) co
if ( !p2 )
return false;
const QString& lefts = textForItem( p1 );
const QString& rights = textForItem( p2 );
if ( !p1->result().isNull() )
{
if ( p1->result()->albumpos() != p2->result()->albumpos() )
return p1->result()->albumpos() < p2->result()->albumpos();
if ( lefts == rights )
return (qint64)&p1 < (qint64)&p2;
}
return QString::localeAwareCompare( textForItem( p1 ), textForItem( p2 ) ) < 0;
return QString::localeAwareCompare( lefts, rights ) < 0;
}

View File

@@ -0,0 +1,66 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SeekSlider.h"
#include <QMouseEvent>
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
SeekSlider::SeekSlider( QWidget* parent )
: QSlider( parent )
{
setFixedHeight( 20 );
setStyleSheet( "QSlider::groove::horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-bkg.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::sub-page:horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-level.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::handle::horizontal {"
"margin-bottom: -7px; margin-top: -7px;"
"margin-left: -4px; margin-right: -4px;"
"height: 17px; width: 16px;"
"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
"background-repeat: no-repeat;"
"}" );
}
SeekSlider::~SeekSlider()
{
}
void
SeekSlider::mousePressEvent( QMouseEvent* event )
{
if ( event->button() == Qt::LeftButton )
{
QMouseEvent eventSwap( QEvent::MouseButtonRelease, event->pos(), event->globalPos(), Qt::MidButton, Qt::MidButton, event->modifiers() );
QSlider::mousePressEvent( &eventSwap );
}
else
QSlider::mousePressEvent( event );
}

View File

@@ -0,0 +1,38 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SEEKSLIDER_H
#define SEEKSLIDER_H
#include <QSlider>
#include "dllmacro.h"
class DLLEXPORT SeekSlider : public QSlider
{
Q_OBJECT
public:
SeekSlider( QWidget* parent = 0 );
~SeekSlider();
protected:
void mousePressEvent( QMouseEvent* event );
};
#endif // SEEKSLIDER_H

View File

@@ -40,6 +40,15 @@
<height>192</height>
</size>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
@@ -84,16 +93,16 @@
<extends>QLabel</extends>
<header location="global">widgets/HeaderLabel.h</header>
</customwidget>
<customwidget>
<class>PlaylistView</class>
<extends>QTreeView</extends>
<header>playlist/playlistview.h</header>
</customwidget>
<customwidget>
<class>AlbumView</class>
<extends>QListView</extends>
<header>playlist/albumview.h</header>
</customwidget>
<customwidget>
<class>PlaylistView</class>
<extends>QTreeView</extends>
<header>playlist/playlistview.h</header>
</customwidget>
<customwidget>
<class>CollectionView</class>
<extends>QTreeView</extends>

View File

@@ -108,6 +108,15 @@ void
WelcomeWidget::updateRecentTracks()
{
m_tracksModel->loadHistory( Tomahawk::source_ptr(), HISTORY_TRACK_ITEMS );
connect( SourceList::instance()->getLocal().data(), SIGNAL( stats( QVariantMap ) ), this, SLOT( updateRecentAdditions() ) );
}
void
WelcomeWidget::updateRecentAdditions()
{
m_recentAlbumsModel->clear();
m_recentAlbumsModel->addFilteredCollection( collection_ptr(), 20, DatabaseCommand_AllAlbums::ModificationTime );
}

View File

@@ -103,6 +103,7 @@ signals:
public slots:
void updateRecentTracks();
void updatePlaylists();
void updateRecentAdditions();
private slots:
void onSourceAdded( const Tomahawk::source_ptr& source );

View File

@@ -30,7 +30,14 @@
</widget>
</item>
<item>
<widget class="AlbumView" name="additionsView"/>
<widget class="AlbumView" name="additionsView">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
@@ -49,7 +56,7 @@
</layout>
</widget>
</widget>
<widget class="QWidget" name="">
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="HeaderLabel" name="label_3">
@@ -80,16 +87,16 @@
<extends>QLabel</extends>
<header location="global">widgets/HeaderLabel.h</header>
</customwidget>
<customwidget>
<class>PlaylistView</class>
<extends>QTreeView</extends>
<header>playlist/playlistview.h</header>
</customwidget>
<customwidget>
<class>AlbumView</class>
<extends>QListView</extends>
<header>playlist/albumview.h</header>
</customwidget>
<customwidget>
<class>PlaylistView</class>
<extends>QTreeView</extends>
<header>playlist/playlistview.h</header>
</customwidget>
<customwidget>
<class>PlaylistWidget</class>
<extends>QListWidget</extends>

View File

@@ -82,7 +82,7 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode )
return;
}
tDebug( LOGVERBOSE ) << "DirLister::scanDir scanning: " << dir.canonicalPath() << " with mode " << mode;
tDebug( LOGVERBOSE ) << "DirLister::scanDir scanning:" << dir.canonicalPath() << "with mode" << mode;
if( !dir.exists() )
{
tDebug( LOGVERBOSE ) << "Dir no longer exists, not scanning";
@@ -283,14 +283,6 @@ MusicScanner::listerFinished( const QMap<QString, unsigned int>& newmtimes )
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
}
// re-calculate source stats
{
DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( SourceList::instance()->getLocal() );
connect( cmd, SIGNAL( done( QVariantMap ) ),
SourceList::instance()->getLocal().data(), SLOT( setStats( QVariantMap ) ), Qt::QueuedConnection );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
if ( !m_dirLister.isNull() )
{
m_dirListerThreadController->quit();;

View File

@@ -183,7 +183,7 @@ QtScriptResolver::init()
}
const QByteArray scriptContents = scriptFile.readAll();
m_engine->mainFrame()->setHtml( "<html><body></body></html>" );
m_engine->mainFrame()->setHtml( "<html><body></body></html>", QUrl( "file:///invalid/file/for/security/policy" ) );
// add c++ part of tomahawk javascript library
m_engine->mainFrame()->addToJavaScriptWindowObject( "Tomahawk", m_resolverHelper );

View File

@@ -109,6 +109,11 @@ ResolversModel::setData( const QModelIndex& index, const QVariant& value, int ro
m_enabledResolvers.append( resolver );
TomahawkApp::instance()->enableScriptResolver( resolver );
emit dataChanged( index, index );
if( Tomahawk::ExternalResolver* res = TomahawkApp::instance()->resolverForPath( resolver ) ) {
connect( res, SIGNAL( changed() ), this, SLOT( resolverChanged() ) );
}
} else if( state == Qt::Unchecked ) {
m_enabledResolvers.removeAll( resolver );

View File

@@ -51,6 +51,7 @@
#include "ui_proxydialog.h"
#include "ui_stackedsettingsdialog.h"
#include <playlist/dynamic/widgets/LoadingSpinner.h>
static QString
md5( const QByteArray& src )
@@ -66,6 +67,7 @@ SettingsDialog::SettingsDialog( QWidget *parent )
, m_rejected( false )
, m_sipModel( 0 )
, m_resolversModel( 0 )
, m_sipSpinner( 0 )
{
ui->setupUi( this );
TomahawkSettings* s = TomahawkSettings::instance();
@@ -107,6 +109,16 @@ SettingsDialog::SettingsDialog( QWidget *parent )
m_sipModel = new SipModel( this );
ui->accountsView->setModel( m_sipModel );
if ( !Servent::instance()->isReady() )
{
m_sipSpinner = new LoadingSpinner( ui->accountsView );
m_sipSpinner->fadeIn();
ui->addSipButton->setEnabled( false );
ui->removeSipButton->setEnabled( false );
connect( Servent::instance(), SIGNAL( ready() ), this, SLOT( serventReady() ) );
}
setupSipButtons();
ui->staticHostName->setText( s->externalHostname() );
@@ -228,6 +240,13 @@ SettingsDialog::~SettingsDialog()
delete ui;
}
void
SettingsDialog::serventReady()
{
m_sipSpinner->fadeOut();
ui->addSipButton->setEnabled( true );
ui->removeSipButton->setEnabled( true );
}
void
SettingsDialog::createIcons()

View File

@@ -22,6 +22,7 @@
#include <QDialog>
#include <QModelIndex>
class LoadingSpinner;
class QListWidgetItem;
class Ui_StackedSettingsDialog;
class SipPluginFactory;
@@ -100,6 +101,7 @@ private slots:
void sipCreateConfigClosed( int value );
void changePage( QListWidgetItem*, QListWidgetItem* );
void serventReady();
private:
void createIcons();
@@ -112,6 +114,7 @@ private:
bool m_rejected;
SipModel* m_sipModel;
ResolversModel* m_resolversModel;
LoadingSpinner* m_sipSpinner;
};
#endif // SETTINGSDIALOG_H

View File

@@ -1,3 +1,22 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Michael Zanetti <mzanetti@kde.org>
* Copyright 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "settingslistdelegate.h"
#include "utils/logger.h"

View File

@@ -1,3 +1,22 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Michael Zanetti <mzanetti@kde.org>
* Copyright 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SETTINGSLISTDELEGATE_H
#define SETTINGSLISTDELEGATE_H

View File

@@ -28,7 +28,7 @@
#include "widgets/playlisttypeselectordlg.h"
#include <playlist/dynamic/GeneratorInterface.h>
#include "utils/logger.h"
#include <globalactionmanager.h>
#include "dropjob.h"
using namespace Tomahawk;
@@ -122,7 +122,7 @@ CategoryAddItem::icon() const
bool
CategoryAddItem::willAcceptDrag( const QMimeData* data ) const
{
if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && GlobalActionManager::instance()->acceptsMimeData( data ) )
if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && DropJob::acceptsMimeData( data ) )
{
return true;
}
@@ -133,9 +133,96 @@ CategoryAddItem::willAcceptDrag( const QMimeData* data ) const
bool
CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction )
{
// As DropJob always converts dropped items to query_ptrs for all tracks we need to extract album/artist metadata ourselves for stations
if ( m_categoryType == SourcesModel::StationsCategory &&
( data->hasFormat( "application/tomahawk.metadata.artist" ) || data->hasFormat( "application/tomahawk.metadata.album" ) ) )
{
QByteArray mimeData;
if ( data->hasFormat( "application/tomahawk.metadata.artist" ) )
mimeData = data->data( "application/tomahawk.metadata.artist" );
else if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
mimeData = data->data( "application/tomahawk.metadata.album" );
QDataStream stream( &mimeData, QIODevice::ReadOnly );
dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
newpl->setMode( OnDemand );
QString firstArtist;
// now we want to add each artist as a filter...
QList< dyncontrol_ptr > contrls;
while ( !stream.atEnd() )
{
QString artist;
stream >> artist;
if ( firstArtist.isEmpty() )
firstArtist = artist;
QString album;
if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
stream >> album; // throw away album title... we only create artists filters for now
dyncontrol_ptr c = newpl->generator()->createControl( "Artist" );
c->setInput( QString( "%1" ).arg( artist ) );
contrls << c;
}
QString name = firstArtist.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( firstArtist );
newpl->rename( name );
newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
ViewManager::instance()->show( newpl );
return true;
}
// This could be needed once echonest supports filtering by album.
// If they never will, or if they do and this code still is not used, throw it away!
// If you enable this, make sure to remove the checks for album above.
/* if ( m_categoryType == SourcesModel::StationsCategory && data->hasFormat( "application/tomahawk.metadata.album" ) )
{
QByteArray mimeData = data->data( "application/tomahawk.metadata.album" );
QDataStream stream( &mimeData, QIODevice::ReadOnly );
dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
newpl->setMode( OnDemand );
QString firstAlbum;
// now we want to add each artist as a filter...
QList< dyncontrol_ptr > contrls;
while ( !stream.atEnd() )
{
QString artist;
stream >> artist;
QString album;
stream >> album;
if ( firstAlbum.isEmpty() )
{
firstAlbum = album;
}
dyncontrol_ptr c = newpl->generator()->createControl( "Album" );
c->setInput( QString( "%1" ).arg( artist ) );
contrls << c;
}
QString name = firstAlbum.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( firstAlbum );
newpl->rename( name );
newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
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() ) );
return true;
} */
// Create a new playlist seeded with these items
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
GlobalActionManager::instance()->tracksFromMimeData( data );
DropJob *dj = new DropJob();
connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
dj->tracksFromMimeData( data );
return true;
}
@@ -143,7 +230,6 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction )
void
CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks )
{
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
if( m_categoryType == SourcesModel::PlaylistsCategory ) {
playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, tracks );

View File

@@ -139,7 +139,7 @@ CollectionItem::text() const
int
CollectionItem::peerSortValue() const
CollectionItem::IDValue() const
{
if( m_source.isNull() )
return -1;
@@ -150,6 +150,18 @@ CollectionItem::peerSortValue() const
}
int
CollectionItem::peerSortValue() const
{
if( m_source.isNull() )
return -1;
if( m_source->isLocal() )
return 0;
return 1;
}
void
CollectionItem::activate()
{

View File

@@ -37,6 +37,7 @@ public:
virtual void activate();
virtual QIcon icon() const;
virtual int peerSortValue() const;
virtual int IDValue() const;
Tomahawk::source_ptr source() const;

View File

@@ -28,7 +28,7 @@
#include "collectionitem.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include "globalactionmanager.h"
#include "dropjob.h"
using namespace Tomahawk;
@@ -81,6 +81,14 @@ PlaylistItem::onPlaylistChanged()
int
PlaylistItem::peerSortValue() const
{
// return m_playlist->createdOn();
return 0;
}
int
PlaylistItem::IDValue() const
{
return m_playlist->createdOn();
}
@@ -142,8 +150,9 @@ PlaylistItem::dropMimeData( const QMimeData* data, Qt::DropAction action )
data->data( "application/tomahawk.playlist.id" ) == m_playlist->guid() )
return false; // don't allow dropping on ourselves
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
GlobalActionManager::instance()->tracksFromMimeData( data );
DropJob *dj = new DropJob();
connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
dj->tracksFromMimeData( data );
// TODO cant' know if it works or not yet...
return true;
@@ -152,7 +161,6 @@ PlaylistItem::dropMimeData( const QMimeData* data, Qt::DropAction action )
void
PlaylistItem::parsedDroppedTracks( const QList< query_ptr >& tracks)
{
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
if ( tracks.count() && !m_playlist.isNull() && m_playlist->author()->isLocal() )
{
qDebug() << "on playlist:" << m_playlist->title() << m_playlist->guid() << m_playlist->currentrevision();
@@ -237,6 +245,14 @@ DynamicPlaylistItem::onDynamicPlaylistLoaded( DynamicPlaylistRevision revision )
int
DynamicPlaylistItem::peerSortValue() const
{
// return m_dynplaylist->createdOn();
return 0;
}
int
DynamicPlaylistItem::IDValue() const
{
return m_dynplaylist->createdOn();
}

View File

@@ -38,6 +38,7 @@ public:
virtual QIcon icon() const;
virtual bool setData(const QVariant& v, bool role);
virtual int peerSortValue() const;
virtual int IDValue() const;
virtual bool activateCurrent();
@@ -67,6 +68,7 @@ public:
virtual bool willAcceptDrag( const QMimeData* data ) const;
virtual void activate();
virtual int peerSortValue() const;
virtual int IDValue() const;
virtual QIcon icon() const;
virtual bool activateCurrent();

View File

@@ -55,6 +55,7 @@ public:
virtual bool dropMimeData( const QMimeData*, Qt::DropAction ) { return false; }
virtual bool setData( const QVariant&, bool ) { return false; }
virtual int peerSortValue() const { return 0; } // How to sort relative to peers in the tree.
virtual int IDValue() const { return 0; }
/// don't call me unless you are a sourcetreeitem. i prefer this to making everyone a friend
void beginRowsAdded( int from, int to ) { emit beginChildRowsAdded( from, to ); }

View File

@@ -33,6 +33,7 @@
#include "utils/logger.h"
#include "globalactionmanager.h"
#include "dropjob.h"
#include "items/playlistitems.h"
using namespace Tomahawk;
@@ -105,6 +106,8 @@ SourcesModel::data( const QModelIndex& index, int role ) const
return itemFromIndex( index )->icon();
case SourcesModel::SortRole:
return itemFromIndex( index )->peerSortValue();
case SourcesModel::IDRole:
return itemFromIndex( index )->IDValue();
}
return QVariant();
}
@@ -175,7 +178,7 @@ SourcesModel::setData( const QModelIndex& index, const QVariant& value, int role
QStringList
SourcesModel::mimeTypes() const
{
return GlobalActionManager::instance()->mimeTypes();
return DropJob::mimeTypes();
}

View File

@@ -61,7 +61,8 @@ public:
enum Roles {
SourceTreeItemRole = Qt::UserRole + 10,
SourceTreeItemTypeRole = Qt::UserRole + 11,
SortRole = Qt::UserRole + 12
SortRole = Qt::UserRole + 12,
IDRole = Qt::UserRole + 13
};
SourcesModel( QObject* parent = 0 );

View File

@@ -20,6 +20,7 @@
#include <QTreeView>
#include "sourcelist.h"
#include "sourcesmodel.h"
#include "sourcetree/items/collectionitem.h"
@@ -36,7 +37,6 @@ SourcesProxyModel::SourcesProxyModel( SourcesModel* model, QObject* parent )
setSourceModel( model );
if ( model && model->metaObject()->indexOfSignal( "expandRequest(QModelIndex)" ) > -1 )
connect( model, SIGNAL( expandRequest( QModelIndex ) ), this, SLOT( expandRequested( QModelIndex ) ) );
if ( model && model->metaObject()->indexOfSignal( "selectRequest(QModelIndex)" ) > -1 )
@@ -51,13 +51,13 @@ SourcesProxyModel::showOfflineSources( bool offlineSourcesShown )
invalidateFilter();
}
bool
SourcesProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const
{
if ( !m_filtered )
return true;
CollectionItem* sti = qobject_cast< CollectionItem* >( m_model->data( sourceModel()->index( sourceRow, 0, sourceParent ), SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >() );
if ( sti )
{
@@ -70,6 +70,7 @@ SourcesProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourcePar
return true;
}
void
SourcesProxyModel::selectRequested( const QModelIndex& idx )
{
@@ -77,6 +78,7 @@ SourcesProxyModel::selectRequested( const QModelIndex& idx )
emit selectRequest( mapFromSource( idx ) );
}
void
SourcesProxyModel::expandRequested( const QModelIndex& idx )
{
@@ -84,3 +86,18 @@ SourcesProxyModel::expandRequested( const QModelIndex& idx )
emit expandRequest( mapFromSource( idx ) );
}
bool
SourcesProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const
{
if ( m_model->data( left, SourcesModel::SortRole ) != m_model->data( right, SourcesModel::SortRole ) )
return ( m_model->data( left, SourcesModel::SortRole ).toInt() < m_model->data( right, SourcesModel::SortRole ).toInt() );
const QString& lefts = left.data().toString().toLower();
const QString& rights = right.data().toString().toLower();
if ( lefts == rights )
return ( m_model->data( left, SourcesModel::IDRole ).toInt() < m_model->data( right, SourcesModel::IDRole ).toInt() );
else
return QString::localeAwareCompare( lefts, rights ) < 0;
}

View File

@@ -42,6 +42,7 @@ signals:
protected:
bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const;
bool lessThan( const QModelIndex& left, const QModelIndex& right ) const;
private:
SourcesModel* m_model;

View File

@@ -37,7 +37,8 @@
#include "audio/audioengine.h"
#include "sourceplaylistinterface.h"
#include "tomahawksettings.h"
#include <globalactionmanager.h>
#include "globalactionmanager.h"
#include "dropjob.h"
#include "utils/logger.h"
@@ -439,7 +440,7 @@ SourceTreeView::dragEnterEvent( QDragEnterEvent* event )
qDebug() << Q_FUNC_INFO;
QTreeView::dragEnterEvent( event );
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
if ( DropJob::acceptsMimeData( event->mimeData() ) )
{
m_dragging = true;
m_dropRect = QRect();
@@ -470,7 +471,7 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event )
bool accept = false;
QTreeView::dragMoveEvent( event );
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
if ( DropJob::acceptsMimeData( event->mimeData() ) )
{
setDirtyRegion( m_dropRect );
const QPoint pos = event->pos();