From ee583f20dadac0b37782c16fd70a337d67c15e37 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 27 May 2011 08:31:08 -0400 Subject: [PATCH 01/36] If Tomahawk crashed during one of these functions it could leave some messed up stuff in your config file, so separate out the two operations into distinct ones that can be run independently and do case-sensitive matching. --- src/sip/twitter/twitter.cpp | 202 ++++++++++++++++++++++++------------ 1 file changed, 133 insertions(+), 69 deletions(-) diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index c5b3010d0..3a6c8087f 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -782,20 +782,29 @@ TwitterPlugin::checkSettings() QString TwitterPlugin::twitterScreenName() const { - if ( TomahawkSettings::instance()->contains( pluginId() + "/ScreenName" ) ) + TomahawkSettings* s = TomahawkSettings::instance(); + s->beginGroup( pluginId() ); + QStringList keys = s->childKeys(); + if ( keys.contains( "ScreenName", Qt::CaseSensitive ) ) { - TomahawkSettings::instance()->setValue( pluginId() + "/screenname_tmp", - TomahawkSettings::instance()->value( pluginId() + "/ScreenName" ).toString() ); - TomahawkSettings::instance()->remove( pluginId() + "/ScreenName" ); + s->setValue( "screenname_tmp", + s->value( "ScreenName" ).toString() ); + s->remove( "ScreenName" ); - TomahawkSettings::instance()->sync(); - - TomahawkSettings::instance()->setValue( pluginId() + "/screenname", - TomahawkSettings::instance()->value( pluginId() + "/screenname_tmp" ).toString() ); - TomahawkSettings::instance()->remove( pluginId() + "/screenname_tmp" ); + s->sync(); } + keys = s->childKeys(); + if ( keys.contains( "screenname_tmp", Qt::CaseSensitive ) ) + { + s->setValue( "screenname", + s->value( "screenname_tmp" ).toString() ); + s->remove( "screenname_tmp" ); - return TomahawkSettings::instance()->value( pluginId() + "/screenname" ).toString(); + s->sync(); + } + s->endGroup(); + + return s->value( pluginId() + "/screenname" ).toString(); } void @@ -807,20 +816,30 @@ TwitterPlugin::setTwitterScreenName( const QString& screenName ) QString TwitterPlugin::twitterOAuthToken() const { - if ( TomahawkSettings::instance()->contains( pluginId() + "/OAuthToken" ) ) + TomahawkSettings* s = TomahawkSettings::instance(); + s->beginGroup( pluginId() ); + QStringList keys = s->childKeys(); + if ( keys.contains( "OAuthToken", Qt::CaseSensitive ) ) { - TomahawkSettings::instance()->setValue( pluginId() + "/oauthtoken_tmp", - TomahawkSettings::instance()->value( pluginId() + "/OAuthToken" ).toString() ); - TomahawkSettings::instance()->remove( pluginId() + "/OAuthToken" ); + s->setValue( "oauthtoken_tmp", + s->value( "OAuthToken" ).toString() ); + s->remove( "OAuthToken" ); - TomahawkSettings::instance()->sync(); + s->sync(); - TomahawkSettings::instance()->setValue( pluginId() + "/oauthtoken", - TomahawkSettings::instance()->value( pluginId() + "/oauthtoken_tmp" ).toString() ); - TomahawkSettings::instance()->remove( pluginId() + "/oauthtoken_tmp" ); } + keys = s->childKeys(); + if ( keys.contains( "oauthtoken_tmp", Qt::CaseSensitive ) ) + { + s->setValue( "oauthtoken", + s->value( "oauthtoken_tmp" ).toString() ); + s->remove( "oauthtoken_tmp" ); - return TomahawkSettings::instance()->value( pluginId() + "/oauthtoken" ).toString(); + s->sync(); + } + s->endGroup(); + + return s->value( pluginId() + "/oauthtoken" ).toString(); } void @@ -832,20 +851,29 @@ TwitterPlugin::setTwitterOAuthToken( const QString& oauthtoken ) QString TwitterPlugin::twitterOAuthTokenSecret() const { - if ( TomahawkSettings::instance()->contains( pluginId() + "/OAuthTokenSecret" ) ) + TomahawkSettings* s = TomahawkSettings::instance(); + s->beginGroup( pluginId() ); + QStringList keys = s->childKeys(); + if ( keys.contains( "OAuthTokenSecret", Qt::CaseSensitive ) ) { - TomahawkSettings::instance()->setValue( pluginId() + "/oauthtokensecret_tmp", - TomahawkSettings::instance()->value( pluginId() + "/OAuthTokenSecret" ).toString() ); - TomahawkSettings::instance()->remove( pluginId() + "/OAuthTokenSecret" ); + s->setValue( "oauthtokensecret_tmp", + s->value( "OAuthTokenSecret" ).toString() ); + s->remove( "OAuthTokenSecret" ); - TomahawkSettings::instance()->sync(); - - TomahawkSettings::instance()->setValue( pluginId() + "/oauthtokensecret", - TomahawkSettings::instance()->value( pluginId() + "/oauthtokensecret_tmp" ).toString() ); - TomahawkSettings::instance()->remove( pluginId() + "/oauthtokensecret_tmp" ); + s->sync(); } + keys = s->childKeys(); + if ( keys.contains( "oauthtokensecret_tmp", Qt::CaseSensitive ) ) + { + s->setValue( "oauthtokensecret", + s->value( "oauthtokensecret_tmp" ).toString() ); + s->remove( "oauthtokensecret_tmp" ); - return TomahawkSettings::instance()->value( pluginId() + "/oauthtokensecret" ).toString(); + s->sync(); + } + s->endGroup(); + + return s->value( pluginId() + "/oauthtokensecret" ).toString(); } void @@ -857,20 +885,29 @@ TwitterPlugin::setTwitterOAuthTokenSecret( const QString& oauthtokensecret ) qint64 TwitterPlugin::twitterCachedFriendsSinceId() const { - if ( TomahawkSettings::instance()->contains( pluginId() + "/CachedFriendsSinceID" ) ) + TomahawkSettings* s = TomahawkSettings::instance(); + s->beginGroup( pluginId() ); + QStringList keys = s->childKeys(); + if ( keys.contains( "CachedFriendsSinceID", Qt::CaseSensitive ) ) { - TomahawkSettings::instance()->setValue( pluginId() + "/cachedfriendssinceid_tmp", - TomahawkSettings::instance()->value( pluginId() + "/CachedFriendsSinceID" ).toLongLong() ); - TomahawkSettings::instance()->remove( pluginId() + "/CachedFriendsSinceID" ); + s->setValue( "cachedfriendssinceid_tmp", + s->value( "CachedFriendsSinceID" ).toLongLong() ); + s->remove( "CachedFriendsSinceID" ); - TomahawkSettings::instance()->sync(); - - TomahawkSettings::instance()->setValue( pluginId() + "/cachedfriendssinceid", - TomahawkSettings::instance()->value( pluginId() + "/cachedfriendssinceid_tmp" ).toLongLong() ); - TomahawkSettings::instance()->remove( pluginId() + "/cachedfriendssinceid_tmp" ); + s->sync(); } + keys = s->childKeys(); + if ( keys.contains( "cachedfriendssinceid_tmp", Qt::CaseSensitive ) ) + { + s->setValue( "cachedfriendssinceid", + s->value( "cachedfriendssinceid_tmp" ).toLongLong() ); + s->remove( "cachedfriendssinceid_tmp" ); - return TomahawkSettings::instance()->value( pluginId() + "/cachedfriendssinceid", 0 ).toLongLong(); + s->sync(); + } + s->endGroup(); + + return s->value( pluginId() + "/cachedfriendssinceid", 0 ).toLongLong(); } void @@ -882,20 +919,29 @@ TwitterPlugin::setTwitterCachedFriendsSinceId( qint64 cachedId ) qint64 TwitterPlugin::twitterCachedMentionsSinceId() const { - if ( TomahawkSettings::instance()->contains( pluginId() + "/CachedMentionsSinceID" ) ) + TomahawkSettings* s = TomahawkSettings::instance(); + s->beginGroup( pluginId() ); + QStringList keys = s->childKeys(); + if ( keys.contains( "CachedMentionsSinceID", Qt::CaseSensitive ) ) { - TomahawkSettings::instance()->setValue( pluginId() + "/cachedmentionssinceid_tmp", - TomahawkSettings::instance()->value( pluginId() + "/CachedMentionsSinceID" ).toLongLong() ); - TomahawkSettings::instance()->remove( pluginId() + "/CachedMentionsSinceID" ); + s->setValue( "cachedmentionssinceid_tmp", + s->value( "CachedMentionsSinceID" ).toLongLong() ); + s->remove( "CachedMentionsSinceID" ); - TomahawkSettings::instance()->sync(); - - TomahawkSettings::instance()->setValue( pluginId() + "/cachedmentionssinceid", - TomahawkSettings::instance()->value( pluginId() + "/cachedmentionssinceid_tmp" ).toLongLong() ); - TomahawkSettings::instance()->remove( pluginId() + "/cachedmentionssinceid_tmp" ); + s->sync(); } + keys = s->childKeys(); + if ( keys.contains( "cachedmentionssinceid_tmp", Qt::CaseSensitive ) ) + { + s->setValue( "cachedmentionssinceid", + s->value( "cachedmentionssinceid_tmp" ).toLongLong() ); + s->remove( "cachedmentionssinceid_tmp" ); - return TomahawkSettings::instance()->value( pluginId() + "/cachedmentionssinceid", 0 ).toLongLong(); + s->sync(); + } + s->endGroup(); + + return s->value( pluginId() + "/cachedmentionssinceid", 0 ).toLongLong(); } void @@ -907,20 +953,29 @@ TwitterPlugin::setTwitterCachedMentionsSinceId( qint64 cachedId ) qint64 TwitterPlugin::twitterCachedDirectMessagesSinceId() const { - if ( TomahawkSettings::instance()->contains( pluginId() + "/CachedDirectMessagesSinceID" ) ) + TomahawkSettings* s = TomahawkSettings::instance(); + s->beginGroup( pluginId() ); + QStringList keys = s->childKeys(); + if ( keys.contains( "CachedDirectMessagesSinceID", Qt::CaseSensitive ) ) { - TomahawkSettings::instance()->setValue( pluginId() + "/cacheddirectmessagessinceid_tmp", - TomahawkSettings::instance()->value( pluginId() + "/CachedDirectMessagesSinceID" ).toLongLong() ); - TomahawkSettings::instance()->remove( pluginId() + "/CachedDirectMessagesSinceID" ); + s->setValue( "cacheddirectmessagessinceid_tmp", + s->value( "CachedDirectMessagesSinceID" ).toLongLong() ); + s->remove( "CachedDirectMessagesSinceID" ); - TomahawkSettings::instance()->sync(); - - TomahawkSettings::instance()->setValue( pluginId() + "/cacheddirectmessagessinceid", - TomahawkSettings::instance()->value( pluginId() + "/cacheddirectmessagessinceid_tmp" ).toLongLong() ); - TomahawkSettings::instance()->remove( pluginId() + "/cacheddirectmessagessinceid_tmp" ); + s->sync(); } + keys = s->childKeys(); + if ( keys.contains( "cacheddirectmessagessinceid_tmp", Qt::CaseSensitive ) ) + { + s->setValue( "cacheddirectmessagessinceid", + s->value( "cacheddirectmessagessinceid_tmp" ).toLongLong() ); + s->remove( "cacheddirectmessagessinceid_tmp" ); - return TomahawkSettings::instance()->value( pluginId() + "/cacheddirectmessagessinceid", 0 ).toLongLong(); + s->sync(); + } + s->endGroup(); + + return s->value( pluginId() + "/cacheddirectmessagessinceid", 0 ).toLongLong(); } void @@ -932,20 +987,29 @@ TwitterPlugin::setTwitterCachedDirectMessagesSinceId( qint64 cachedId ) QHash TwitterPlugin::twitterCachedPeers() const { - if ( TomahawkSettings::instance()->contains( pluginId() + "/CachedPeers" ) ) + TomahawkSettings* s = TomahawkSettings::instance(); + s->beginGroup( pluginId() ); + QStringList keys = s->childKeys(); + if ( keys.contains( "CachedPeers", Qt::CaseSensitive ) ) { - TomahawkSettings::instance()->setValue( pluginId() + "/cachedpeers_tmp", - TomahawkSettings::instance()->value( pluginId() + "/CachedPeers" ).toHash() ); - TomahawkSettings::instance()->remove( pluginId() + "/CachedPeers" ); + s->setValue( "cachedpeers_tmp", + s->value( "CachedPeers" ).toHash() ); + s->remove( "CachedPeers" ); - TomahawkSettings::instance()->sync(); - - TomahawkSettings::instance()->setValue( pluginId() + "/cachedpeers", - TomahawkSettings::instance()->value( pluginId() + "/cachedpeers_tmp" ).toHash() ); - TomahawkSettings::instance()->remove( pluginId() + "/cachedpeers_tmp" ); + s->sync(); } + keys = s->childKeys(); + if ( keys.contains( "cachedpeers_tmp", Qt::CaseSensitive ) ) + { + s->setValue( "cachedpeers", + s->value( "cachedpeers_tmp" ).toHash() ); + s->remove( "cachedpeers_tmp" ); + + s->sync(); + } + s->endGroup(); - return TomahawkSettings::instance()->value( pluginId() + "/cachedpeers", QHash() ).toHash(); + return s->value( pluginId() + "/cachedpeers", QHash() ).toHash(); } void From 5c11841cec33f15ce3cddec9c073619abcacb4d3 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sat, 28 May 2011 16:54:06 +0200 Subject: [PATCH 02/36] Make tomahawk.nsi ready for 64bit --- admin/win/nsi/tomahawk.nsi | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi index 9f33b5472..dbed64660 100644 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -15,9 +15,10 @@ ;----------------------------------------------------------------------------- ; Some paths. ;----------------------------------------------------------------------------- -!define MING_PATH "/usr/i686-w64-mingw32/sys-root/mingw" +!ifndef MING_PATH + !define MING_PATH "/usr/i686-w64-mingw32/sys-root/mingw" +!endif !define MING_BIN "${MING_PATH}/bin" -!define MING_DLL_PATH "${MING_BIN}" !define MING_LIB "${MING_PATH}/lib" !define ROOT_PATH "..\..\.." ; assuming the script is in ROOT/admin/win/nsi !define BUILD_PATH "${ROOT_PATH}\build" @@ -316,20 +317,20 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER SetOutPath "$INSTDIR" ;Cygwin/c++ stuff - ;File "${MING_DLL_PATH}\cygmad-0.dll" - ;File "${MING_DLL_PATH}\libgcc_s_dw2-1.dll" - ;File "${MING_DLL_PATH}\mingwm10.dll" - File "${MING_DLL_PATH}\libgcc_s_sjlj-1.dll" - File "${MING_DLL_PATH}\libstdc++-6.dll" + ;File "${MING_BIN}\cygmad-0.dll" + ;File "${MING_BIN}\libgcc_s_dw2-1.dll" + ;File "${MING_BIN}\mingwm10.dll" + File "${MING_BIN}\libgcc_s_sjlj-1.dll" + File "${MING_BIN}\libstdc++-6.dll" ;Phonon stuff ;Fix the phonon build to not use Dbus File "${QT_DLL_PATH}\QtDbus4.dll" - File "${MING_DLL_PATH}\libdbus-1-3.dll" - File "${MING_DLL_PATH}\dbus-daemon.exe" + File "${MING_BIN}\libdbus-1-3.dll" + File "${MING_BIN}\dbus-daemon.exe" - File "${MING_DLL_PATH}\libphonon.dll" + File "${MING_BIN}\libphonon.dll" SetOutPath "$INSTDIR\phonon_backend" File "${MING_BIN}\phonon_backend\phonon_vlc.dll" SetOutPath "$INSTDIR" @@ -350,13 +351,13 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER ; Other - File "${MING_DLL_PATH}\libqjson.dll" - File "${MING_DLL_PATH}\libtag.dll" - File "${MING_DLL_PATH}\libpng15-15.dll" - File "${MING_DLL_PATH}\libjpeg-8.dll" - File "${MING_DLL_PATH}\zlib1.dll" + File "${MING_BIN}\libqjson.dll" + File "${MING_BIN}\libtag.dll" + File "${MING_BIN}\libpng15-15.dll" + File "${MING_BIN}\libjpeg-8.dll" + File "${MING_BIN}\zlib1.dll" - File "${MING_DLL_PATH}\libechonest.dll" + File "${MING_BIN}\libechonest.dll" File "${MING_BIN}\libQTweetLib.dll" ; Jabber From fb06624821cf9b15d1962a064d55a32e704daa86 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 26 May 2011 19:41:47 -0400 Subject: [PATCH 03/36] Add a new "Create Playlist" dialog that is shown when the user presses New Playlist. This lets the user select between a normal playlist and an automatic playlist. Thanks for Christopher Reichert for the patch! --- src/libtomahawk/CMakeLists.txt | 3 + src/libtomahawk/widgets/newplaylistwidget.ui | 6 + .../widgets/playlisttypeselectordlg.cpp | 88 +++++++++ .../widgets/playlisttypeselectordlg.h | 55 ++++++ .../widgets/playlisttypeselectordlg.ui | 184 ++++++++++++++++++ src/sourcetree/items/categoryitems.cpp | 29 ++- src/tomahawkwindow.cpp | 27 ++- src/tomahawkwindow.h | 2 +- src/tomahawkwindow.ui | 21 +- 9 files changed, 391 insertions(+), 24 deletions(-) create mode 100644 src/libtomahawk/widgets/playlisttypeselectordlg.cpp create mode 100644 src/libtomahawk/widgets/playlisttypeselectordlg.h create mode 100644 src/libtomahawk/widgets/playlisttypeselectordlg.ui diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index e48c4769c..991daedc1 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -161,6 +161,7 @@ set( libSources utils/xspfgenerator.cpp widgets/newplaylistwidget.cpp + widgets/playlisttypeselectordlg.cpp widgets/welcomewidget.cpp widgets/welcomeplaylistmodel.cpp widgets/overlaywidget.cpp @@ -324,6 +325,7 @@ set( libHeaders utils/xspfgenerator.h widgets/newplaylistwidget.h + widgets/playlisttypeselectordlg.h widgets/welcomewidget.h widgets/welcomeplaylistmodel.h widgets/overlaywidget.h @@ -339,6 +341,7 @@ set( libHeaders_NoMOC playlist/dynamic/GeneratorInterface.h ) set( libUI ${libUI} + widgets/playlisttypeselectordlg.ui widgets/newplaylistwidget.ui widgets/welcomewidget.ui widgets/infowidgets/sourceinfowidget.ui diff --git a/src/libtomahawk/widgets/newplaylistwidget.ui b/src/libtomahawk/widgets/newplaylistwidget.ui index 6a2701ee8..e3a145050 100644 --- a/src/libtomahawk/widgets/newplaylistwidget.ui +++ b/src/libtomahawk/widgets/newplaylistwidget.ui @@ -10,6 +10,9 @@ 460 + + Qt::TabFocus + @@ -31,6 +34,9 @@ 26 + + Qt::StrongFocus + diff --git a/src/libtomahawk/widgets/playlisttypeselectordlg.cpp b/src/libtomahawk/widgets/playlisttypeselectordlg.cpp new file mode 100644 index 000000000..5f3802661 --- /dev/null +++ b/src/libtomahawk/widgets/playlisttypeselectordlg.cpp @@ -0,0 +1,88 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * 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 . + */ + +#include "widgets/newplaylistwidget.h" +#include "viewmanager.h" +#include "viewpage.h" +#include "sourcelist.h" + +#include "playlisttypeselectordlg.h" +#include "ui_playlisttypeselectordlg.h" + + +PlaylistTypeSelectorDlg::PlaylistTypeSelectorDlg( QWidget* parent, Qt::WindowFlags f ) + : QDialog( parent, f ) + , ui( new Ui::PlaylistTypeSelectorDlg ) +{ + ui->setupUi( this ); + +#ifdef Q_WS_MAC +// ui-> + ui->horizontalLayout_2->setContentsMargins( 4, 4, 4, 4 ); + + setSizeGripEnabled( false ); + setMinimumSize( size() ); + setMaximumSize( size() ); // to remove the resize grip on osx this is the only way +#endif + + m_isAutoPlaylist = false; + m_playlistName = ""; + + connect( ui->manualPlaylistButton, SIGNAL( clicked() ), + this, SLOT( createNormalPlaylist() )); + connect( ui->autoPlaylistButton, SIGNAL( clicked() ), + this, SLOT( createAutomaticPlaylist() )); + connect( ui->autoPlaylistNameLine, SIGNAL( textChanged( const QString& )), + this, SLOT( enableAutoPlaylistButton( const QString& ))); +} + +PlaylistTypeSelectorDlg::~PlaylistTypeSelectorDlg() +{ + delete ui; +} + +void +PlaylistTypeSelectorDlg::createNormalPlaylist() +{ + m_isAutoPlaylist = false; + done( QDialog::Accepted ); // return code is used to vaidate we did not exit out of the Dialog +} + +void PlaylistTypeSelectorDlg::createAutomaticPlaylist() { m_isAutoPlaylist = true; + m_playlistName = ui->autoPlaylistNameLine->text(); + done( QDialog::Accepted ); // return code is used to vaidate we did not exit out of the Dialog successfully +} + +QString +PlaylistTypeSelectorDlg::playlistName() const +{ + return m_playlistName; +} + +bool +PlaylistTypeSelectorDlg::playlistTypeIsAuto() const +{ + return m_isAutoPlaylist; +} + +void +PlaylistTypeSelectorDlg::enableAutoPlaylistButton( const QString &text ) +{ + ui->autoPlaylistButton->setEnabled( !text.isEmpty() ); +} + diff --git a/src/libtomahawk/widgets/playlisttypeselectordlg.h b/src/libtomahawk/widgets/playlisttypeselectordlg.h new file mode 100644 index 000000000..a0331a112 --- /dev/null +++ b/src/libtomahawk/widgets/playlisttypeselectordlg.h @@ -0,0 +1,55 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * 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 . + */ + +#ifndef PLAYLISTTYPESELECTORDLG_H +#define PLAYLISTTYPESELECTORDLG_H + +#include + +#include "dllmacro.h" + +namespace Ui +{ + class PlaylistTypeSelectorDlg; +} + +class DLLEXPORT PlaylistTypeSelectorDlg : public QDialog +{ +Q_OBJECT + +public: + PlaylistTypeSelectorDlg( QWidget* parent = 0, Qt::WindowFlags = 0 ); + ~PlaylistTypeSelectorDlg(); + bool playlistTypeIsNormal() const; + bool playlistTypeIsAuto() const; + QString playlistName() const; + +private slots: + void createNormalPlaylist(); + void createAutomaticPlaylist(); + void enableAutoPlaylistButton( const QString& ); + +private: + bool m_isAutoPlaylist; // if not an auto playlist then its a normal playlist + + Ui::PlaylistTypeSelectorDlg *ui; + QString m_playlistName; + +}; + +#endif // PlaylistTypeSelectorDlg_H diff --git a/src/libtomahawk/widgets/playlisttypeselectordlg.ui b/src/libtomahawk/widgets/playlisttypeselectordlg.ui new file mode 100644 index 000000000..bc4cb8e32 --- /dev/null +++ b/src/libtomahawk/widgets/playlisttypeselectordlg.ui @@ -0,0 +1,184 @@ + + + PlaylistTypeSelectorDlg + + + + 0 + 0 + 554 + 169 + + + + + 0 + 0 + + + + + 482 + 145 + + + + + 10000 + 10000 + + + + New Playlist + + + false + + + + + + 0 + + + 2 + + + + + + 221 + 40 + + + + Just a regular old playlist... Give it a name, drag in some tracks, and go! + + + true + + + 2 + + + + + + + Qt::Vertical + + + + 20 + 28 + + + + + + + + Create Manual Playlist + + + + + + + + + Qt::Vertical + + + + + + + 4 + + + 1 + + + + + + 0 + 0 + + + + Don't know exactly what you want? Give Tomahawk a few pointers and let it build a playlist for you! + + + true + + + 1 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Name: + + + 1 + + + + + + + New Playlist... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + Create Automatic Playlist + + + + + + + + + + diff --git a/src/sourcetree/items/categoryitems.cpp b/src/sourcetree/items/categoryitems.cpp index a66e54719..c51a92748 100644 --- a/src/sourcetree/items/categoryitems.cpp +++ b/src/sourcetree/items/categoryitems.cpp @@ -19,6 +19,7 @@ #include "tomahawkapp.h" #include "utils/tomahawkutils.h" #include "widgets/newplaylistwidget.h" +#include "widgets/playlisttypeselectordlg.h" #include "viewmanager.h" #include "viewpage.h" #include "sourcelist.h" @@ -59,13 +60,28 @@ CategoryAddItem::activate() { switch( m_categoryType ) { - case SourcesModel::PlaylistsCategory: - // only show if none is shown yet - if( !ViewManager::instance()->isNewPlaylistPageVisible() ) { - ViewPage* p = ViewManager::instance()->show( new NewPlaylistWidget() ); - model()->linkSourceItemToPage( this, p ); + case SourcesModel::PlaylistsCategory: { + + PlaylistTypeSelectorDlg playlistSelectorDlg( TomahawkApp::instance()->mainWindow() ); + int successfulReturn = playlistSelectorDlg.exec(); + + if ( !playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) { + + // only show if none is shown yet + if( !ViewManager::instance()->isNewPlaylistPageVisible() ) { + //fix this namespace resolution problem, was not there before + Tomahawk::ViewPage* p = ViewManager::instance()->show( new NewPlaylistWidget() ); + model()->linkSourceItemToPage( this, p ); + } + + } else if ( playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) { + // create Auto Playlist + QString playlistName = playlistSelectorDlg.playlistName(); + APP->mainWindow()->createAutomaticPlaylist( playlistName ); } + break; + } case SourcesModel::StationsCategory: APP->mainWindow()->createStation(); break; @@ -217,5 +233,6 @@ CategoryItem::activate() { if( m_category == SourcesModel::StationsCategory ) { // TODO activate stations page - } + } + } diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 251fc7205..173b43f03 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -50,6 +50,7 @@ #include "utils/widgetdragfilter.h" #include "utils/xspfloader.h" #include "widgets/newplaylistwidget.h" +#include "widgets/playlisttypeselectordlg.h" #include "audiocontrols.h" #include "settingsdialog.h" @@ -274,7 +275,6 @@ TomahawkWindow::setupSignals() connect( ui->actionRescanCollection, SIGNAL( triggered() ), SLOT( updateCollectionManually() ) ); connect( ui->actionLoadXSPF, SIGNAL( triggered() ), SLOT( loadSpiff() )); connect( ui->actionCreatePlaylist, SIGNAL( triggered() ), SLOT( createPlaylist() )); - connect( ui->actionCreateAutomaticPlaylist, SIGNAL( triggered() ), SLOT( createAutomaticPlaylist() )); connect( ui->actionCreate_New_Station, SIGNAL( triggered() ), SLOT( createStation() )); connect( ui->actionAboutTomahawk, SIGNAL( triggered() ), SLOT( showAboutTomahawk() ) ); connect( ui->actionExit, SIGNAL( triggered() ), qApp, SLOT( quit() ) ); @@ -444,11 +444,11 @@ TomahawkWindow::loadSpiff() void -TomahawkWindow::createAutomaticPlaylist() +TomahawkWindow::createAutomaticPlaylist( QString playlistName ) { - bool ok; - QString name = QInputDialog::getText( this, tr( "Create New Automatic Playlist" ), tr( "Name:" ), QLineEdit::Normal, tr( "New Automatic Playlist" ), &ok ); - if ( !ok || name.isEmpty() ) + QString name = playlistName; + + if ( name.isEmpty() ) return; source_ptr author = SourceList::instance()->getLocal(); @@ -484,7 +484,22 @@ TomahawkWindow::createStation() void TomahawkWindow::createPlaylist() { - ViewManager::instance()->show( new NewPlaylistWidget() ); + PlaylistTypeSelectorDlg playlistSelectorDlg; + int successfulReturn = playlistSelectorDlg.exec(); + + qDebug() << "\n\nSTAT == " << successfulReturn; + if ( !playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) { + + // only show if none is shown yet + if( !ViewManager::instance()->isNewPlaylistPageVisible() ) { + ViewManager::instance()->show( new NewPlaylistWidget() ); + } + + } else if ( playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) { + // create Auto Playlist + QString playlistName = playlistSelectorDlg.playlistName(); + APP->mainWindow()->createAutomaticPlaylist( playlistName ); + } } diff --git a/src/tomahawkwindow.h b/src/tomahawkwindow.h index 730ba47ce..e724f7313 100644 --- a/src/tomahawkwindow.h +++ b/src/tomahawkwindow.h @@ -61,7 +61,7 @@ protected: void hideEvent( QHideEvent* e ); public slots: - void createAutomaticPlaylist(); + void createAutomaticPlaylist( QString ); void createStation(); void createPlaylist(); void loadSpiff(); diff --git a/src/tomahawkwindow.ui b/src/tomahawkwindow.ui index 092aea983..5e5bf7c3f 100644 --- a/src/tomahawkwindow.ui +++ b/src/tomahawkwindow.ui @@ -35,7 +35,7 @@ 0 0 1000 - 22 + 21 @@ -55,16 +55,6 @@ - - - &Playlist - - - - - - - &Network @@ -86,6 +76,15 @@ + + + &Playlist + + + + + + From 9f087b7da33401d4362cbb02d84dc70124c93d1f Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 28 May 2011 16:55:35 -0400 Subject: [PATCH 04/36] Add Christopher's copyright and less debug --- src/libtomahawk/widgets/playlisttypeselectordlg.cpp | 2 +- src/libtomahawk/widgets/playlisttypeselectordlg.h | 10 +++++----- src/tomahawkwindow.cpp | 11 +++++------ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/libtomahawk/widgets/playlisttypeselectordlg.cpp b/src/libtomahawk/widgets/playlisttypeselectordlg.cpp index 5f3802661..4458bcd9f 100644 --- a/src/libtomahawk/widgets/playlisttypeselectordlg.cpp +++ b/src/libtomahawk/widgets/playlisttypeselectordlg.cpp @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Christopher Reichert * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/widgets/playlisttypeselectordlg.h b/src/libtomahawk/widgets/playlisttypeselectordlg.h index a0331a112..de00c1504 100644 --- a/src/libtomahawk/widgets/playlisttypeselectordlg.h +++ b/src/libtomahawk/widgets/playlisttypeselectordlg.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Christopher Reichert * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,7 +28,7 @@ namespace Ui class PlaylistTypeSelectorDlg; } -class DLLEXPORT PlaylistTypeSelectorDlg : public QDialog +class DLLEXPORT PlaylistTypeSelectorDlg : public QDialog { Q_OBJECT @@ -43,13 +43,13 @@ private slots: void createNormalPlaylist(); void createAutomaticPlaylist(); void enableAutoPlaylistButton( const QString& ); - + private: bool m_isAutoPlaylist; // if not an auto playlist then its a normal playlist - + Ui::PlaylistTypeSelectorDlg *ui; QString m_playlistName; - + }; #endif // PlaylistTypeSelectorDlg_H diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 173b43f03..16d8fff53 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -447,7 +447,7 @@ void TomahawkWindow::createAutomaticPlaylist( QString playlistName ) { QString name = playlistName; - + if ( name.isEmpty() ) return; @@ -486,17 +486,16 @@ TomahawkWindow::createPlaylist() { PlaylistTypeSelectorDlg playlistSelectorDlg; int successfulReturn = playlistSelectorDlg.exec(); - - qDebug() << "\n\nSTAT == " << successfulReturn; + if ( !playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) { - + // only show if none is shown yet if( !ViewManager::instance()->isNewPlaylistPageVisible() ) { ViewManager::instance()->show( new NewPlaylistWidget() ); } - + } else if ( playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) { - // create Auto Playlist + // create Auto Playlist QString playlistName = playlistSelectorDlg.playlistName(); APP->mainWindow()->createAutomaticPlaylist( playlistName ); } From 12ac4079b1b02ce03e12b273917b52635ec73cb8 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 28 May 2011 17:10:27 -0400 Subject: [PATCH 05/36] activate current page in the sourcetree if new playlist dialog is cancelled --- src/sourcetree/items/categoryitems.cpp | 6 ++++-- src/sourcetree/sourcesmodel.cpp | 3 ++- src/sourcetree/sourcesmodel.h | 21 +++++++++++---------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/sourcetree/items/categoryitems.cpp b/src/sourcetree/items/categoryitems.cpp index c51a92748..82064636e 100644 --- a/src/sourcetree/items/categoryitems.cpp +++ b/src/sourcetree/items/categoryitems.cpp @@ -78,6 +78,8 @@ CategoryAddItem::activate() // create Auto Playlist QString playlistName = playlistSelectorDlg.playlistName(); APP->mainWindow()->createAutomaticPlaylist( playlistName ); + } else if ( !successfulReturn ) { + model()->viewPageActivated( ViewManager::instance()->currentPage() ); } break; @@ -233,6 +235,6 @@ CategoryItem::activate() { if( m_category == SourcesModel::StationsCategory ) { // TODO activate stations page - } - + } + } diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp index 9bc01f52c..b72f75613 100644 --- a/src/sourcetree/sourcesmodel.cpp +++ b/src/sourcetree/sourcesmodel.cpp @@ -30,11 +30,13 @@ #include #include +#include using namespace Tomahawk; SourcesModel::SourcesModel( QObject* parent ) : QAbstractItemModel( parent ) + , m_rootItem( 0 ) , m_viewPageDelayedCacheItem( 0 ) { m_rootItem = new SourceTreeItem( this, 0, Invalid ); @@ -389,7 +391,6 @@ SourcesModel::linkSourceItemToPage( SourceTreeItem* item, ViewPage* p ) m_viewPageDelayedCacheItem = 0; } - SourceTreeItem* SourcesModel::itemFromIndex( const QModelIndex& idx ) const { diff --git a/src/sourcetree/sourcesmodel.h b/src/sourcetree/sourcesmodel.h index 0ea3579e5..5455da673 100644 --- a/src/sourcetree/sourcesmodel.h +++ b/src/sourcetree/sourcesmodel.h @@ -90,16 +90,6 @@ public: QModelIndex indexFromItem( SourceTreeItem* item ) const; -signals: - void selectRequest( const QModelIndex& idx ); - -private slots: - void onSourcesAdded( const QList& sources ); - void onSourceAdded( const Tomahawk::source_ptr& source ); - void onSourceRemoved( const Tomahawk::source_ptr& source ); - - void viewPageActivated( Tomahawk::ViewPage* ); - public slots: void loadSources(); @@ -108,6 +98,17 @@ public slots: void onItemRowsAddedDone(); void onItemRowsRemovedBegin( int first, int last ); void onItemRowsRemovedDone(); + + void viewPageActivated( Tomahawk::ViewPage* ); + +signals: + void selectRequest( const QModelIndex& idx ); + +private slots: + void onSourcesAdded( const QList& sources ); + void onSourceAdded( const Tomahawk::source_ptr& source ); + void onSourceRemoved( const Tomahawk::source_ptr& source ); + private: SourceTreeItem* itemFromIndex( const QModelIndex& idx ) const; int rowForItem( SourceTreeItem* item ) const; From f03e74c777c82fd924900b692652541233a75193 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 28 May 2011 17:13:07 -0400 Subject: [PATCH 06/36] ooops, include slid through --- src/sourcetree/sourcesmodel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp index b72f75613..5113deb94 100644 --- a/src/sourcetree/sourcesmodel.cpp +++ b/src/sourcetree/sourcesmodel.cpp @@ -30,7 +30,6 @@ #include #include -#include using namespace Tomahawk; From d61f38a2061fc8190ab0311624f049eb61efdb72 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 28 May 2011 17:29:29 -0400 Subject: [PATCH 07/36] Only emit a rename signal if we are the currently active view. Fixes TWK-202. --- src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp | 3 ++- src/libtomahawk/playlist/playlistview.cpp | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp index 197334459..28ca29566 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp @@ -424,6 +424,7 @@ DynamicWidget::onDeleted() void DynamicWidget::onChanged() { - if( !m_playlist.isNull() ) + if( !m_playlist.isNull() && + ViewManager::instance()->currentPage() == this ) emit nameChanged( m_playlist->title() ); } diff --git a/src/libtomahawk/playlist/playlistview.cpp b/src/libtomahawk/playlist/playlistview.cpp index 47dd6e863..5b350adb8 100644 --- a/src/libtomahawk/playlist/playlistview.cpp +++ b/src/libtomahawk/playlist/playlistview.cpp @@ -24,6 +24,7 @@ #include "playlist/playlistproxymodel.h" #include "widgets/overlaywidget.h" +#include "viewmanager.h" using namespace Tomahawk; @@ -186,6 +187,7 @@ PlaylistView::onDeleted() void PlaylistView::onChanged() { - if ( m_model && !m_model->playlist().isNull() ) + if ( m_model && !m_model->playlist().isNull() && + ViewManager::instance()->currentPage() == this ) emit nameChanged( m_model->playlist()->title() ); } From a9b792f603c81d926da4e63d1c8836941bc43926 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 28 May 2011 19:46:27 -0400 Subject: [PATCH 08/36] allow deleting playlists with the delete key. fixes TWK-113 --- src/sourcetree/sourcetreeview.cpp | 29 ++++++++++++++++++++++++----- src/sourcetree/sourcetreeview.h | 3 ++- thirdparty/jreen | 2 +- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 0529caac7..bb22c5d98 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -212,23 +212,23 @@ SourceTreeView::loadPlaylist() void -SourceTreeView::deletePlaylist() +SourceTreeView::deletePlaylist( const QModelIndex& idxIn ) { qDebug() << Q_FUNC_INFO; - QModelIndex idx = m_contextMenuIndex; + QModelIndex idx = idxIn.isValid() ? idxIn : m_contextMenuIndex; if ( !idx.isValid() ) return; - SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); + SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( idx, SourcesModel::SourceTreeItemTypeRole ).toInt(); if ( type == SourcesModel::StaticPlaylist ) { - PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex ); + PlaylistItem* item = itemFromIndex< PlaylistItem >( idx ); playlist_ptr playlist = item->playlist(); Playlist::remove( playlist ); } else if( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) { - DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( m_contextMenuIndex ); + DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( idx ); dynplaylist_ptr playlist = item->dynPlaylist(); DynamicPlaylist::remove( playlist ); } @@ -358,6 +358,25 @@ SourceTreeView::dropEvent( QDropEvent* event ) m_dragging = false; } +void +SourceTreeView::keyPressEvent( QKeyEvent *event ) +{ + if( !selectionModel()->selectedIndexes().isEmpty() ) + { + QModelIndex idx = selectionModel()->selectedIndexes().first(); + if ( model()->data( idx, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::StaticPlaylist || + model()->data( idx, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::AutomaticPlaylist || + model()->data( idx, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::Station ) + { + PlaylistItem* item = itemFromIndex< PlaylistItem >( idx ); + Q_ASSERT( item ); + + if( item->playlist()->author()->isLocal() ) { + deletePlaylist( idx ); + } + } + } +} void SourceTreeView::paintEvent( QPaintEvent* event ) diff --git a/src/sourcetree/sourcetreeview.h b/src/sourcetree/sourcetreeview.h index 8058195a0..d3d6eb97e 100644 --- a/src/sourcetree/sourcetreeview.h +++ b/src/sourcetree/sourcetreeview.h @@ -52,7 +52,7 @@ private slots: void selectRequest( const QModelIndex& idx ); void loadPlaylist(); - void deletePlaylist(); + void deletePlaylist( const QModelIndex& = QModelIndex() ); void copyPlaylistLink(); void onCustomContextMenu( const QPoint& pos ); @@ -66,6 +66,7 @@ protected: virtual void dragLeaveEvent( QDragLeaveEvent* event ) { Q_UNUSED( event ); m_dragging = false; setDirtyRegion( m_dropRect ); } virtual void dragMoveEvent( QDragMoveEvent* event ); virtual void dropEvent( QDropEvent* event ); + virtual void keyPressEvent( QKeyEvent* event ); private: void setupMenus(); diff --git a/thirdparty/jreen b/thirdparty/jreen index a231a2b38..8f995f246 160000 --- a/thirdparty/jreen +++ b/thirdparty/jreen @@ -1 +1 @@ -Subproject commit a231a2b3868baf32312d65cb7e371828212d7745 +Subproject commit 8f995f246637f533feb7124744e113034a32b505 From dcef3952730acb19b386b727ef834422793a69d0 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 28 May 2011 19:56:22 -0400 Subject: [PATCH 09/36] add stuff to Changelog for 0.1.0, thanks J for all your work trawling through history! --- ChangeLog | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index e45fc3a3e..807c27f1f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,16 @@ Version 0.1.0: + Κ* Fixed stations so they resolve against all available sources instead of + only local and friend's collections. + * Add a Song seed for stations and automatic playlists, and allow dragging of + any tracks to the New Stations entry to create a pre-seeded station. + * Added auto-completion for artists while filling in a station or automatic + playlist. * SOCKS5 proxy support. Noproxy hosts are also supported but no wildcard support (yet). + * Support loading of - and exporting to - .xspf playlists + * Added Tomahawk:// protocol support and share links for many things including + tracks, playlists, and stations. + * Autoload automatically detected resolvers on startup. * Fix issue where track resolving spinner never stopped if tracks were removed from playlist while resolving. * Twitter & Jabber profile pictures are shown. @@ -9,18 +19,26 @@ Version 0.1.0: * Add new Google account type that is a thin wrapper around a Jabber plugin. * Overhaul the settings dialog interface. * Resolvers can now be enabled and disabled, and some can be configured - directly in Tomahawk. + directly in Tomahawk, for example the new Spotify resolver. * Split playlists and stations in sources sidebar. Show Recently Played as a node under the Super Collection. * Fix massive speed bottleneck on startup in the case of many recently played playlists. + * Removed filter and song view from Super Collection, coming back in the next + release. * Browse and play collections in our snappy tree-mode, which also shows images for artists and albums. * Fixed crash that could occur when playing a track from a browser. - * Fixed a crash caused by sources going on- or offline. + * Fixed a crash caused by sources going on or offline. + * Huge optimizations in the resolving pipeline. +ΚΚΚΚ* Improved the handling of automatic status messages for Google Talk +ΚΚΚΚ* Switch to Phonon sound system, allowing us to support a wide variety + of audio formats. +ΚΚΚΚ* UI tweaks and cleanup. * (OS X) Open configuration dialogs as sliding sheets. * (OS X) Increase our available file watches to the maximum that the system reports. +ΚΚΚΚ* (OS X) Added 'Window' menu with zoom/minimize actions Version 0.0.3: * Show spinner while resolving playlists. From 75cea47cfad675db6b69847f6b4621e98ba560dc Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 28 May 2011 19:58:03 -0400 Subject: [PATCH 10/36] byebye random special characters, hello w hitespace --- ChangeLog | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 807c27f1f..ae7c5afa0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ Version 0.1.0: - Κ* Fixed stations so they resolve against all available sources instead of + * Fixed stations so they resolve against all available sources instead of only local and friend's collections. * Add a Song seed for stations and automatic playlists, and allow dragging of any tracks to the New Stations entry to create a pre-seeded station. @@ -31,14 +31,14 @@ Version 0.1.0: * Fixed crash that could occur when playing a track from a browser. * Fixed a crash caused by sources going on or offline. * Huge optimizations in the resolving pipeline. -ΚΚΚΚ* Improved the handling of automatic status messages for Google Talk -ΚΚΚΚ* Switch to Phonon sound system, allowing us to support a wide variety + * Improved the handling of automatic status messages for Google Talk + * Switch to Phonon sound system, allowing us to support a wide variety of audio formats. -ΚΚΚΚ* UI tweaks and cleanup. + * UI tweaks and cleanup. * (OS X) Open configuration dialogs as sliding sheets. * (OS X) Increase our available file watches to the maximum that the system reports. -ΚΚΚΚ* (OS X) Added 'Window' menu with zoom/minimize actions + * (OS X) Added 'Window' menu with zoom/minimize actions Version 0.0.3: * Show spinner while resolving playlists. From ee8a0d6691c0a6362abbc1dd4728bb091418196c Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 28 May 2011 20:00:32 -0400 Subject: [PATCH 11/36] oops, fix oversight --- src/sourcetree/sourcetreeview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index bb22c5d98..30335c1ce 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -361,7 +361,7 @@ SourceTreeView::dropEvent( QDropEvent* event ) void SourceTreeView::keyPressEvent( QKeyEvent *event ) { - if( !selectionModel()->selectedIndexes().isEmpty() ) + if( event->key() == Qt::Key_Delete && !selectionModel()->selectedIndexes().isEmpty() ) { QModelIndex idx = selectionModel()->selectedIndexes().first(); if ( model()->data( idx, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::StaticPlaylist || From efa3708e79cea260948f8005618a72eedf21efab Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 28 May 2011 20:19:18 -0400 Subject: [PATCH 12/36] Add some padding on the right edge of the dyn controls --- .../playlist/dynamic/widgets/DynamicControlList.cpp | 4 ++++ .../playlist/dynamic/widgets/DynamicControlWrapper.cpp | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp index 5128562af..3324ff5bf 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp @@ -63,7 +63,11 @@ DynamicControlList::init() m_layout->setColumnStretch( 2, 1 ); m_layout->setMargin( 0 ); m_layout->setVerticalSpacing( 0 ); +#ifdef Q_WS_MAC // on OS X we don't want the right edge of the toolbuttons against the window + m_layout->setContentsMargins( 0, 0, 3, 0 ); +#else m_layout->setContentsMargins( 0, 0, 0, 0 ); +#endif m_layout->setSizeConstraint( QLayout::SetMinimumSize ); m_collapseLayout = new QHBoxLayout(); diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.cpp index c3ed4fed6..79fb4e87c 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.cpp @@ -41,7 +41,6 @@ DynamicControlWrapper::DynamicControlWrapper( const Tomahawk::dyncontrol_ptr& co , m_typeSelector( 0 ) , m_layout( QWeakPointer< QGridLayout >( layout ) ) { - qDebug() << "CREATING DYNAMIC CONTROL WRAPPER WITH ROW:" << row << layout; m_typeSelector = new QComboBox( m_parent ); @@ -74,8 +73,6 @@ DynamicControlWrapper::DynamicControlWrapper( const Tomahawk::dyncontrol_ptr& co m_layout.data()->addLayout( m_plusL, m_row, 3, Qt::AlignCenter ); m_plusL->setCurrentIndex( 0 ); - - } DynamicControlWrapper::~DynamicControlWrapper() From c24e90c978a1249a6807295cfc77bd3226bc4d8b Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 28 May 2011 20:19:37 -0400 Subject: [PATCH 13/36] allow to delete with the backspace as well as delete key --- src/libtomahawk/playlist/playlistview.cpp | 2 +- src/sourcetree/sourcetreeview.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/playlistview.cpp b/src/libtomahawk/playlist/playlistview.cpp index 5b350adb8..be01df8ec 100644 --- a/src/libtomahawk/playlist/playlistview.cpp +++ b/src/libtomahawk/playlist/playlistview.cpp @@ -135,7 +135,7 @@ PlaylistView::keyPressEvent( QKeyEvent* event ) if ( !model() ) return; - if ( event->key() == Qt::Key_Delete && !model()->isReadOnly() ) + if ( ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace ) && !model()->isReadOnly() ) { qDebug() << "Removing selected items"; proxyModel()->removeIndexes( selectedIndexes() ); diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 30335c1ce..cf73e7fb4 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -361,7 +361,7 @@ SourceTreeView::dropEvent( QDropEvent* event ) void SourceTreeView::keyPressEvent( QKeyEvent *event ) { - if( event->key() == Qt::Key_Delete && !selectionModel()->selectedIndexes().isEmpty() ) + if( ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace ) && !selectionModel()->selectedIndexes().isEmpty() ) { QModelIndex idx = selectionModel()->selectedIndexes().first(); if ( model()->data( idx, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::StaticPlaylist || From 635c49092458275326a10ee43003990a01d7cc0b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 29 May 2011 11:22:08 +0200 Subject: [PATCH 14/36] * Fixed InfoSystem DTOR freezing. --- src/libtomahawk/infosystem/infosystem.cpp | 57 ++++++----------------- 1 file changed, 13 insertions(+), 44 deletions(-) diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp index d0ca69cfa..c8f3febab 100644 --- a/src/libtomahawk/infosystem/infosystem.cpp +++ b/src/libtomahawk/infosystem/infosystem.cpp @@ -83,59 +83,28 @@ InfoSystem::InfoSystem(QObject *parent) InfoSystem::~InfoSystem() { qDebug() << Q_FUNC_INFO << " beginning"; - + if ( !m_worker.isNull() ) { - QMetaObject::invokeMethod( m_worker.data(), "deleteLater", Qt::QueuedConnection ); - while( !m_worker.isNull() ) - { - qDebug() << Q_FUNC_INFO << " worker not deleted, processing events"; - TomahawkUtils::Sleep::msleep( 50 ); - } + m_worker.clear(); + m_infoSystemWorkerThreadController->quit(); + m_infoSystemWorkerThreadController->wait( 60000 ); - if ( m_infoSystemWorkerThreadController ) - m_infoSystemWorkerThreadController->quit(); - - if( m_infoSystemWorkerThreadController ) - { - while( !m_infoSystemWorkerThreadController->isFinished() ) - { - qDebug() << Q_FUNC_INFO << " worker thread controller not finished, processing events"; - TomahawkUtils::Sleep::msleep( 50 ); - } - - delete m_infoSystemWorkerThreadController; - m_infoSystemWorkerThreadController = 0; - } + delete m_infoSystemWorkerThreadController; + m_infoSystemWorkerThreadController = 0; } - qDebug() << Q_FUNC_INFO << " done deleting worker"; if ( !m_cache.isNull() ) { - QMetaObject::invokeMethod( m_cache.data(), "deleteLater", Qt::QueuedConnection ); - while( !m_cache.isNull() ) - { - qDebug() << Q_FUNC_INFO << " worker not deleted, processing events"; - TomahawkUtils::Sleep::msleep( 50 ); - } - - if ( m_infoSystemCacheThreadController ) - m_infoSystemCacheThreadController->quit(); - - if( m_infoSystemCacheThreadController ) - { - while( !m_infoSystemCacheThreadController->isFinished() ) - { - qDebug() << Q_FUNC_INFO << " worker thread controller not finished, processing events"; - TomahawkUtils::Sleep::msleep( 50 ); - } - - delete m_infoSystemCacheThreadController; - m_infoSystemCacheThreadController = 0; - } + m_cache.clear(); + m_infoSystemCacheThreadController->quit(); + m_infoSystemCacheThreadController->wait( 60000 ); + + delete m_infoSystemCacheThreadController; + m_infoSystemCacheThreadController = 0; } - + qDebug() << Q_FUNC_INFO << " done deleting cache"; } From 9d92201c0c2b3a3668595c2504fed8add43037ab Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 29 May 2011 11:42:32 +0200 Subject: [PATCH 15/36] * Tweaked shutdown order. --- src/libtomahawk/audio/audioengine.cpp | 3 ++- src/tomahawkapp.cpp | 15 +++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index a64c8abac..27d4bf5f7 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -64,7 +64,8 @@ AudioEngine::~AudioEngine() { qDebug() << Q_FUNC_INFO; - stop(); + m_mediaObject->stop(); +// stop(); delete m_audioOutput; delete m_mediaObject; diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 158126a6b..4ba7ad016 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -330,26 +330,21 @@ TomahawkApp::~TomahawkApp() #ifndef TOMAHAWK_HEADLESS delete m_mainwindow; + delete m_audioEngine; #endif delete m_infoSystem; //FIXME: delete GeneratorFactory::registerFactory( "echonest", new EchonestFactory ); ? - delete m_database; - - delete SipHandler::instance(); - + delete SipHandler::instance(); delete m_servent; - - Pipeline::instance()->stop(); - delete Pipeline::instance(); delete m_scanManager; + delete m_database; -#ifndef TOMAHAWK_HEADLESS - delete m_audioEngine; -#endif + Pipeline::instance()->stop(); + delete Pipeline::instance(); delete TomahawkUtils::proxyFactory(); delete TomahawkUtils::nam(); From 8dbdf728b19468ba605a556196a2dcb57cc6bd8c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 29 May 2011 14:17:53 +0200 Subject: [PATCH 16/36] * Add some debug output for rotzbouw. --- admin/mac/deposx.sh | 6 +++--- src/libtomahawk/database/databasecommand_resolve.cpp | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/admin/mac/deposx.sh b/admin/mac/deposx.sh index 596fbcc0c..8da4cd3e0 100755 --- a/admin/mac/deposx.sh +++ b/admin/mac/deposx.sh @@ -85,8 +85,8 @@ function deplib_change install_name_tool -change $ORIGROOT/libtomahawk_sipzeroconf.dylib @executable_path/libtomahawk_sipzeroconf.dylib $1 install_name_tool -change $ORIGROOT/libtomahawk_qtweetlib.dylib @executable_path/libtomahawk_qtweetlib.dylib $1 install_name_tool -change $ORIGROOT/libtomahawk_portfwd.dylib @executable_path/libtomahawk_portfwd.dylib $1 - install_name_tool -change $ORIGROOT/libjreen.0.dylib @executable_path/libjreen.0.dylib $1 - install_name_tool -change /usr/local/Cellar/jreen/HEAD/lib/libjreen.dylib @executable_path/libjreen.0.dylib $1 + install_name_tool -change $ORIGROOT/libjreen.dylib @executable_path/libjreen.dylib $1 + install_name_tool -change /usr/local/Cellar/jreen/HEAD/lib/libjreen.dylib @executable_path/libjreen.dylib $1 install_name_tool -change /usr/local/Cellar/qca/2.0.2/lib/qca.framework/Versions/2/qca @executable_path/../Frameworks/qca.framework/Versions/2/qca $1 install_name_tool -change /usr/local/Cellar/gettext/0.18.1.1/lib/libintl.8.dylib @executable_path/libintl.8.dylib $1 install_name_tool -change /usr/local/Cellar/vlc-git/HEAD/lib/libvlc.5.dylib @executable_path/libvlc.5.dylib $1 @@ -127,7 +127,7 @@ import_lib /usr/local/Cellar/vlc-git/HEAD/lib/libvlc.5.dylib import_lib /usr/local/Cellar/vlc-git/HEAD/lib/libvlccore.4.dylib import_lib /usr/local/Cellar/gettext/0.18.1.1/lib/libintl.8.dylib -import_lib $ORIGROOT/libjreen.0.dylib +import_lib $ORIGROOT/libjreen.dylib import_lib $ORIGROOT/libtomahawklib.dylib import_lib $ORIGROOT/libtomahawk_sipjabber.dylib import_lib $ORIGROOT/libtomahawk_sipgoogle.dylib diff --git a/src/libtomahawk/database/databasecommand_resolve.cpp b/src/libtomahawk/database/databasecommand_resolve.cpp index e420f79bb..5fa2fa1d7 100644 --- a/src/libtomahawk/database/databasecommand_resolve.cpp +++ b/src/libtomahawk/database/databasecommand_resolve.cpp @@ -42,6 +42,9 @@ 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() << "Collection null:" << result.isNull(); + qDebug() << "Collection null:" << result->collection().isNull(); + qDebug() << "Source null:" << result->collection()->source().isNull(); if ( !result.isNull() && result->collection()->source()->isOnline() ) { res << result; From eef99c9e39188663fbc204cf7db222a566b30424 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Sun, 29 May 2011 20:56:49 +0200 Subject: [PATCH 17/36] Initialize variable. --- src/sourcetree/sourcesmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp index 5113deb94..8fc2971e7 100644 --- a/src/sourcetree/sourcesmodel.cpp +++ b/src/sourcetree/sourcesmodel.cpp @@ -194,7 +194,7 @@ SourcesModel::mimeData( const QModelIndexList& ) const bool SourcesModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent ) { - SourceTreeItem* item; + SourceTreeItem* item = 0; qDebug() << "Got mime data dropped:" << row << column << parent << itemFromIndex( parent )->text(); if( row == -1 && column == -1 ) item = itemFromIndex( parent ); From f2952f783e78633443befb343bc3a028fdb047c9 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Sun, 29 May 2011 21:14:35 +0200 Subject: [PATCH 18/36] Fix case --- src/tomahawkwindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tomahawkwindow.ui b/src/tomahawkwindow.ui index 5e5bf7c3f..b770211cd 100644 --- a/src/tomahawkwindow.ui +++ b/src/tomahawkwindow.ui @@ -106,7 +106,7 @@ - Go &online + Go &Online From 07cb94b1bcbdadae35427a06469125ea481467ad Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 29 May 2011 16:56:25 -0400 Subject: [PATCH 19/36] Add a new source item for temporary pages. This fixes TWK-182. Also, ensure that what is selected on the left is always in sync with what is on the right. In addition, expand the parent nodes when selecting an item automatically in the tree. --- .../playlist/dynamic/DynamicModel.cpp | 5 ++- src/libtomahawk/playlist/playlistmodel.cpp | 20 +++++++++ src/libtomahawk/playlist/playlistmodel.h | 3 +- src/libtomahawk/playlist/playlistview.cpp | 14 +++++++ src/libtomahawk/playlist/playlistview.h | 3 ++ src/libtomahawk/viewmanager.cpp | 18 ++------ src/libtomahawk/viewmanager.h | 8 +--- src/libtomahawk/viewpage.h | 3 ++ src/sourcetree/items/collectionitem.cpp | 42 +++++++++++++++++++ src/sourcetree/items/collectionitem.h | 12 ++++++ src/sourcetree/items/genericpageitems.cpp | 7 ++++ src/sourcetree/items/genericpageitems.h | 1 + src/sourcetree/items/sourcetreeitem.cpp | 2 +- src/sourcetree/items/sourcetreeitem.h | 1 + src/sourcetree/sourcesmodel.cpp | 6 +++ src/sourcetree/sourcesmodel.h | 1 + src/sourcetree/sourcetreeview.cpp | 6 ++- 17 files changed, 125 insertions(+), 27 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp index 6e7a16de8..74d1fb2cc 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp @@ -64,7 +64,10 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool load QString DynamicModel::description() const { - return m_playlist->generator()->sentenceSummary(); + if( !m_playlist.isNull() && !m_playlist->generator().isNull() ) + return m_playlist->generator()->sentenceSummary(); + else + return QString(); } diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index 1e1ee0889..65c157d9c 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -34,6 +34,7 @@ using namespace Tomahawk; PlaylistModel::PlaylistModel( QObject* parent ) : TrackModel( parent ) , m_waitForUpdate( false ) + , m_isTemporary( false ) { qDebug() << Q_FUNC_INFO; @@ -91,6 +92,7 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEn setTitle( playlist->title() ); setDescription( tr( "A playlist by %1" ).arg( playlist->author()->isLocal() ? tr( "you" ) : playlist->author()->friendlyName() ) ); + m_isTemporary = false; if ( !loadEntries ) return; @@ -192,6 +194,12 @@ PlaylistModel::append( const Tomahawk::album_ptr& album ) connect( album.data(), SIGNAL( tracksAdded( QList ) ), SLOT( onTracksAdded( QList ) ) ); + if( rowCount( QModelIndex() ) == 0 ) { + setTitle( album->name() ); + setDescription( tr( "All tracks by %1 on album %2" ).arg( album->artist()->name() ).arg( album->name() ) ); + m_isTemporary = true; + } + onTracksAdded( album->tracks() ); } @@ -205,6 +213,12 @@ PlaylistModel::append( const Tomahawk::artist_ptr& artist ) connect( artist.data(), SIGNAL( tracksAdded( QList ) ), SLOT( onTracksAdded( QList ) ) ); + if( rowCount( QModelIndex() ) == 0 ) { + setTitle( artist->name() ); + setDescription( tr( "All tracks by %1" ).arg( artist->name() ) ); + m_isTemporary = true; + } + onTracksAdded( artist->tracks() ); } @@ -494,3 +508,9 @@ PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome ) onPlaylistChanged(); } } + +bool +PlaylistModel::isTemporary() const +{ + return m_isTemporary; +} diff --git a/src/libtomahawk/playlist/playlistmodel.h b/src/libtomahawk/playlist/playlistmodel.h index 96cfb72ce..30af9f4ea 100644 --- a/src/libtomahawk/playlist/playlistmodel.h +++ b/src/libtomahawk/playlist/playlistmodel.h @@ -67,6 +67,7 @@ public: void remove( unsigned int row, bool moreToCome = false ); virtual void removeIndex( const QModelIndex& index, bool moreToCome = false ); + bool isTemporary() const; signals: void repeatModeChanged( PlaylistInterface::RepeatMode mode ); void shuffleModeChanged( bool enabled ); @@ -90,7 +91,7 @@ private: QList playlistEntries() const; Tomahawk::playlist_ptr m_playlist; - bool m_waitForUpdate; + bool m_waitForUpdate, m_isTemporary; QList< Tomahawk::Query* > m_waitingForResolved; }; diff --git a/src/libtomahawk/playlist/playlistview.cpp b/src/libtomahawk/playlist/playlistview.cpp index be01df8ec..5000bd80c 100644 --- a/src/libtomahawk/playlist/playlistview.cpp +++ b/src/libtomahawk/playlist/playlistview.cpp @@ -71,8 +71,12 @@ PlaylistView::setPlaylistModel( PlaylistModel* model ) if ( !m_model->playlist().isNull() ) setGuid( QString( "playlistview/%1" ).arg( m_model->playlist()->guid() ) ); else + { setGuid( "playlistview" ); + m_model->title(); + m_model->description(); + } connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); connect( m_model, SIGNAL( playlistDeleted() ), SLOT( onDeleted() ) ); connect( m_model, SIGNAL( playlistChanged() ), SLOT( onChanged() ) ); @@ -191,3 +195,13 @@ PlaylistView::onChanged() ViewManager::instance()->currentPage() == this ) emit nameChanged( m_model->playlist()->title() ); } + +bool +PlaylistView::isTemporaryPage() const +{ + if ( m_model ) { + return m_model->isTemporary(); + } else { + return false; + } +} diff --git a/src/libtomahawk/playlist/playlistview.h b/src/libtomahawk/playlist/playlistview.h index fe845d5b8..c42ff1248 100644 --- a/src/libtomahawk/playlist/playlistview.h +++ b/src/libtomahawk/playlist/playlistview.h @@ -52,6 +52,7 @@ public: virtual QPixmap pixmap() const { return QPixmap( RESPATH "images/playlist-icon.png" ); } virtual bool jumpToCurrentTrack(); + virtual bool isTemporaryPage() const; signals: void nameChanged( const QString& title ); @@ -75,6 +76,8 @@ private: PlaylistModel* m_model; QMenu m_itemMenu; + QString m_customTitle; + QString m_customDescripton; QAction* m_playItemAction; QAction* m_addItemsToQueueAction; diff --git a/src/libtomahawk/viewmanager.cpp b/src/libtomahawk/viewmanager.cpp index efb2ca5ad..6b9380aef 100644 --- a/src/libtomahawk/viewmanager.cpp +++ b/src/libtomahawk/viewmanager.cpp @@ -599,24 +599,12 @@ ViewManager::setPage( ViewPage* page, bool trackHistory ) setHistoryPosition( m_pageHistory.count() - 1 ); } - if ( !playlistForInterface( currentPlaylistInterface() ).isNull() ) - emit playlistActivated( playlistForInterface( currentPlaylistInterface() ) ); - - else if ( dynamicPlaylistForInterface( currentPlaylistInterface() ) ) - emit dynamicPlaylistActivated( dynamicPlaylistForInterface( currentPlaylistInterface() ) ); - else if ( collectionForInterface( currentPlaylistInterface() ) ) - emit collectionActivated( collectionForInterface( currentPlaylistInterface() ) ); - else if ( isSuperCollectionVisible() ) - emit superCollectionActivated(); - else if( isNewPlaylistPageVisible() ) - emit newPlaylistActivated(); - /* TODO refactor. now we have rows in the sourcetreeview that are connected to pages, e.g. Stations, Recently Updated, etc - else if ( !currentPlaylistInterface() ) - emit tempPageActivated();*/ - qDebug() << "View page shown:" << page->title(); emit viewPageActivated( page ); + if( page->isTemporaryPage() ) + emit tempPageActivated( page ); + if ( !AudioEngine::instance()->playlist() ) AudioEngine::instance()->setPlaylist( currentPlaylistInterface() ); diff --git a/src/libtomahawk/viewmanager.h b/src/libtomahawk/viewmanager.h index 0070f95e8..234e9c846 100644 --- a/src/libtomahawk/viewmanager.h +++ b/src/libtomahawk/viewmanager.h @@ -111,13 +111,7 @@ signals: void historyBackAvailable( bool avail ); void historyForwardAvailable( bool avail ); - void tempPageActivated(); - void superCollectionActivated(); - void collectionActivated( const Tomahawk::collection_ptr& collection ); - void playlistActivated( const Tomahawk::playlist_ptr& playlist ); - void dynamicPlaylistActivated( const Tomahawk::dynplaylist_ptr& playlist ); - - void newPlaylistActivated(); + void tempPageActivated( Tomahawk::ViewPage* ); void viewPageActivated( Tomahawk::ViewPage* ); public slots: diff --git a/src/libtomahawk/viewpage.h b/src/libtomahawk/viewpage.h index 0921998b2..31e18ba3e 100644 --- a/src/libtomahawk/viewpage.h +++ b/src/libtomahawk/viewpage.h @@ -51,7 +51,10 @@ public: virtual bool jumpToCurrentTrack() = 0; + virtual bool isTemporaryPage() const { return false; } + /** subclasses implementing ViewPage can emit the following signals: + * nameChanged( const QString& ) * descriptionChanged( const QString& ) * destroyed( QWidget* widget ); * diff --git a/src/sourcetree/items/collectionitem.cpp b/src/sourcetree/items/collectionitem.cpp index a559fbc83..4aa76867b 100644 --- a/src/sourcetree/items/collectionitem.cpp +++ b/src/sourcetree/items/collectionitem.cpp @@ -21,6 +21,7 @@ #include "playlistitems.h" #include "viewmanager.h" #include "playlist.h" +#include "genericpageitems.h" /// CollectionItem @@ -31,8 +32,12 @@ CollectionItem::CollectionItem( SourcesModel* mdl, SourceTreeItem* parent, cons , m_source( source ) , m_playlists( 0 ) , m_stations( 0 ) + , m_tempItem( 0 ) + , m_curTempPage( 0 ) { if( m_source.isNull() ) { // super collection + connect( ViewManager::instance(), SIGNAL( tempPageActivated( Tomahawk::ViewPage*) ), this, SLOT( tempPageActivated( Tomahawk::ViewPage* ) ) ); + return; } // create category items if there are playlists to show, or stations to show @@ -264,3 +269,40 @@ CollectionItem::onStationsDeleted( const QList< dynplaylist_ptr >& stations ) { playlistsDeletedInternal( m_stations, stations ); } + +void +CollectionItem::tempPageActivated( Tomahawk::ViewPage* v ) +{ + QString name = v->title(); + m_curTempPage = v; + if( !m_tempItem ) { + emit beginRowsAdded( children().count(), children().count() ); + m_tempItem = new GenericPageItem( model(), this, name, QIcon( RESPATH "images/playlist-icon.png" ), + boost::bind( &CollectionItem::tempItemClicked, this ), + boost::bind( &CollectionItem::getTempPage, this ) + ); + emit endRowsAdded(); + } else { + m_tempItem->setText( name ); + } + + model()->linkSourceItemToPage( m_tempItem, v ); + emit selectRequest( m_tempItem ); +} + +ViewPage* +CollectionItem::tempItemClicked() +{ + if( m_curTempPage ) { + // show the last temporary page the user displayed + return ViewManager::instance()->show( m_curTempPage ); + } + + return 0; +} + +ViewPage* +CollectionItem::getTempPage() const +{ + return m_curTempPage; +} diff --git a/src/sourcetree/items/collectionitem.h b/src/sourcetree/items/collectionitem.h index 1cb99e0cb..8bb23bee7 100644 --- a/src/sourcetree/items/collectionitem.h +++ b/src/sourcetree/items/collectionitem.h @@ -19,7 +19,11 @@ #include "sourcetreeitem.h" +class GenericPageItem; class CategoryItem; +namespace Tomahawk { + class ViewPage; +} class CollectionItem : public SourceTreeItem { @@ -38,6 +42,7 @@ public: CategoryItem* playlistsCategory() const { return m_playlists; } void setStationsCategory( CategoryItem* item ) { m_stations = item; } void setPlaylistsCategory( CategoryItem* item ) { m_playlists = item; } + private slots: void onPlaylistsAdded( const QList& playlists ); void onPlaylistsDeleted( const QList& playlists ); @@ -46,6 +51,10 @@ private slots: void onStationsAdded( const QList& stations ); void onStationsDeleted( const QList& stations ); + void tempPageActivated( Tomahawk::ViewPage* ); + Tomahawk::ViewPage* tempItemClicked(); + Tomahawk::ViewPage* getTempPage() const; + private: void playlistsAddedInternal( SourceTreeItem* parent, const QList< Tomahawk::dynplaylist_ptr >& playlists ); template< typename T > @@ -54,6 +63,9 @@ private: Tomahawk::source_ptr m_source; CategoryItem* m_playlists; CategoryItem* m_stations; + + GenericPageItem* m_tempItem; + Tomahawk::ViewPage* m_curTempPage; }; diff --git a/src/sourcetree/items/genericpageitems.cpp b/src/sourcetree/items/genericpageitems.cpp index 47f05605d..3739fa7cb 100644 --- a/src/sourcetree/items/genericpageitems.cpp +++ b/src/sourcetree/items/genericpageitems.cpp @@ -63,3 +63,10 @@ GenericPageItem::willAcceptDrag(const QMimeData* data) const { return false; } + +void +GenericPageItem::setText( const QString &text ) +{ + m_text = text; + emit updated(); +} diff --git a/src/sourcetree/items/genericpageitems.h b/src/sourcetree/items/genericpageitems.h index 1c0e954e4..5a980f7c3 100644 --- a/src/sourcetree/items/genericpageitems.h +++ b/src/sourcetree/items/genericpageitems.h @@ -36,6 +36,7 @@ public: virtual bool willAcceptDrag( const QMimeData* data ) const; virtual QIcon icon() const; + void setText( const QString& text ); signals: void activated(); diff --git a/src/sourcetree/items/sourcetreeitem.cpp b/src/sourcetree/items/sourcetreeitem.cpp index f1169cc28..ffe039ee6 100644 --- a/src/sourcetree/items/sourcetreeitem.cpp +++ b/src/sourcetree/items/sourcetreeitem.cpp @@ -30,7 +30,7 @@ SourceTreeItem::SourceTreeItem( SourcesModel* model, SourceTreeItem* parent, Sou connect( this, SIGNAL( childRowsAdded() ), m_model, SLOT( onItemRowsAddedDone() ) ); connect( this, SIGNAL( childRowsRemoved() ), m_model, SLOT( onItemRowsRemovedDone() ) ); connect( this, SIGNAL( updated() ), m_model, SLOT( itemUpdated() ) ); - + connect( this, SIGNAL( selectRequest( SourceTreeItem* ) ), m_model, SLOT( itemSelectRequest( SourceTreeItem* ) ) ); if( !m_parent ) return; diff --git a/src/sourcetree/items/sourcetreeitem.h b/src/sourcetree/items/sourcetreeitem.h index 290e72f15..57553988e 100644 --- a/src/sourcetree/items/sourcetreeitem.h +++ b/src/sourcetree/items/sourcetreeitem.h @@ -62,6 +62,7 @@ public: signals: void updated(); + void selectRequest( SourceTreeItem* ); void beginChildRowsAdded( int fromRow, int toRow ); void childRowsAdded(); diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp index 8fc2971e7..04384ae9c 100644 --- a/src/sourcetree/sourcesmodel.cpp +++ b/src/sourcetree/sourcesmodel.cpp @@ -448,3 +448,9 @@ SourcesModel::rowForItem( SourceTreeItem* item ) const { return item->parent()->children().indexOf( item ); } + +void +SourcesModel::itemSelectRequest( SourceTreeItem* item ) +{ + emit selectRequest( indexFromItem( item ) ); +} diff --git a/src/sourcetree/sourcesmodel.h b/src/sourcetree/sourcesmodel.h index 5455da673..c702b4b4b 100644 --- a/src/sourcetree/sourcesmodel.h +++ b/src/sourcetree/sourcesmodel.h @@ -101,6 +101,7 @@ public slots: void viewPageActivated( Tomahawk::ViewPage* ); + void itemSelectRequest( SourceTreeItem* item ); signals: void selectRequest( const QModelIndex& idx ); diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index cf73e7fb4..fe94ff5e9 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -198,9 +198,11 @@ SourceTreeView::onItemExpanded( const QModelIndex& idx ) void SourceTreeView::selectRequest( const QModelIndex& idx ) { - if( !selectionModel()->selectedIndexes().contains( idx ) ) + if ( !selectionModel()->selectedIndexes().contains( idx ) ) + { + scrollTo( idx, QTreeView::EnsureVisible ); selectionModel()->select( idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current ); - + } } From f5749a5db2e4ac743afa6025e954e1c554e2ddef Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 29 May 2011 19:03:22 -0400 Subject: [PATCH 20/36] Fix adding of controls that wouldn't go to the right place. --- src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp index 3324ff5bf..e041c38ae 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp @@ -143,7 +143,7 @@ void DynamicControlList::addNewControl() m_layout->removeItem( m_collapseLayout ); dyncontrol_ptr control = m_generator->createControl(); - m_controls.append( new DynamicControlWrapper( control, m_layout, m_controls.size(), this ) ); + m_controls.append( new DynamicControlWrapper( control, m_layout, m_layout->rowCount(), this ) ); connect( m_controls.last(), SIGNAL( removeControl() ), this, SLOT( removeControl() ) ); connect( m_controls.last(), SIGNAL( changed() ), this, SLOT( controlChanged() ) ); From 873fe1297a484aff6d79213246deb8f482b4c7ca Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 30 May 2011 04:59:59 +0200 Subject: [PATCH 21/36] * Fixed crash in DbCmd_Resolve. --- src/libtomahawk/database/databasecommand_resolve.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/database/databasecommand_resolve.cpp b/src/libtomahawk/database/databasecommand_resolve.cpp index 5fa2fa1d7..18975a8a5 100644 --- a/src/libtomahawk/database/databasecommand_resolve.cpp +++ b/src/libtomahawk/database/databasecommand_resolve.cpp @@ -42,10 +42,10 @@ 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() << "Collection null:" << result.isNull(); +/* qDebug() << "Result null:" << result.isNull(); qDebug() << "Collection null:" << result->collection().isNull(); - qDebug() << "Source null:" << result->collection()->source().isNull(); - if ( !result.isNull() && result->collection()->source()->isOnline() ) + qDebug() << "Source null:" << result->collection()->source().isNull();*/ + if ( !result.isNull() && !result->collection().isNull() && result->collection()->source()->isOnline() ) { res << result; emit results( m_query->id(), res ); From af2553294f2ca752b60cfff45d252d0f84a8677e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 30 May 2011 06:18:20 +0200 Subject: [PATCH 22/36] * More AudioEngine debug. --- src/libtomahawk/audio/audioengine.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index 27d4bf5f7..51f1aea1a 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -301,6 +301,12 @@ void AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState ) { qDebug() << Q_FUNC_INFO << oldState << newState; + + if ( newState == Phonon::ErrorState ) + { + qDebug() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType(); + } + if ( oldState == Phonon::PlayingState && newState == Phonon::StoppedState ) { if ( !m_expectStop ) From 76aa0461ad2220a775be304209c3645580425895 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 30 May 2011 15:18:28 +0200 Subject: [PATCH 23/36] * More InfoSystem DTOR fixes. --- src/libtomahawk/infosystem/infosystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp index c8f3febab..7802fc831 100644 --- a/src/libtomahawk/infosystem/infosystem.cpp +++ b/src/libtomahawk/infosystem/infosystem.cpp @@ -86,10 +86,10 @@ InfoSystem::~InfoSystem() if ( !m_worker.isNull() ) { - m_worker.clear(); m_infoSystemWorkerThreadController->quit(); m_infoSystemWorkerThreadController->wait( 60000 ); + delete m_worker.data(); delete m_infoSystemWorkerThreadController; m_infoSystemWorkerThreadController = 0; } @@ -97,10 +97,10 @@ InfoSystem::~InfoSystem() if ( !m_cache.isNull() ) { - m_cache.clear(); m_infoSystemCacheThreadController->quit(); m_infoSystemCacheThreadController->wait( 60000 ); + delete m_cache.data(); delete m_infoSystemCacheThreadController; m_infoSystemCacheThreadController = 0; } From d71bdb2fef4b566901659395cf93e568781c7ff3 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 30 May 2011 09:45:42 -0400 Subject: [PATCH 24/36] Update dtor logic of other threads --- src/libtomahawk/utils/tomahawkutils.h | 17 --------- src/musicscanner.cpp | 54 +++++---------------------- src/scanmanager.cpp | 54 +++++---------------------- 3 files changed, 20 insertions(+), 105 deletions(-) diff --git a/src/libtomahawk/utils/tomahawkutils.h b/src/libtomahawk/utils/tomahawkutils.h index ebe2562b4..331afa931 100644 --- a/src/libtomahawk/utils/tomahawkutils.h +++ b/src/libtomahawk/utils/tomahawkutils.h @@ -37,23 +37,6 @@ class QNetworkProxy; namespace TomahawkUtils { - class DLLEXPORT Sleep : public QThread - { - public: - static void sleep( unsigned long secs ) - { - QThread::sleep( secs ); - } - static void msleep( unsigned long msecs ) - { - QThread::msleep( msecs ); - } - static void usleep( unsigned long usecs ) - { - QThread::usleep( usecs ); - } - }; - class DLLEXPORT NetworkProxyFactory : public QNetworkProxyFactory { public: diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index 8472dcade..34ad9e823 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -136,29 +136,12 @@ MusicScanner::~MusicScanner() if ( !m_dirLister.isNull() ) { - QMetaObject::invokeMethod( m_dirLister.data(), "deleteLater", Qt::QueuedConnection ); - while( !m_dirLister.isNull() ) - { - qDebug() << Q_FUNC_INFO << " scanner not deleted, processing events"; - QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); - TomahawkUtils::Sleep::msleep( 100 ); - } + m_dirListerThreadController->quit();; + m_dirListerThreadController->wait( 60000 ); - if ( m_dirListerThreadController ) - m_dirListerThreadController->quit(); - - if( m_dirListerThreadController ) - { - while( !m_dirListerThreadController->isFinished() ) - { - qDebug() << Q_FUNC_INFO << " scanner thread controller not finished, processing events"; - QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); - TomahawkUtils::Sleep::msleep( 100 ); - } - - delete m_dirListerThreadController; - m_dirListerThreadController = 0; - } + delete m_dirLister.data(); + delete m_dirListerThreadController; + m_dirListerThreadController = 0; } } @@ -257,29 +240,12 @@ MusicScanner::deleteLister() { if ( !m_dirLister.isNull() ) { - QMetaObject::invokeMethod( m_dirLister.data(), "deleteLater", Qt::QueuedConnection ); - while( !m_dirLister.isNull() ) - { - qDebug() << Q_FUNC_INFO << " scanner not deleted, processing events"; - QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); - TomahawkUtils::Sleep::msleep( 100 ); - } + m_dirListerThreadController->quit();; + m_dirListerThreadController->wait( 60000 ); - if ( m_dirListerThreadController ) - m_dirListerThreadController->quit(); - - if( m_dirListerThreadController ) - { - while( !m_dirListerThreadController->isFinished() ) - { - qDebug() << Q_FUNC_INFO << " scanner thread controller not finished, processing events"; - QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); - TomahawkUtils::Sleep::msleep( 100 ); - } - - delete m_dirListerThreadController; - m_dirListerThreadController = 0; - } + delete m_dirLister.data(); + delete m_dirListerThreadController; + m_dirListerThreadController = 0; } emit finished(); } diff --git a/src/scanmanager.cpp b/src/scanmanager.cpp index cf12153c9..5a824f508 100644 --- a/src/scanmanager.cpp +++ b/src/scanmanager.cpp @@ -85,29 +85,12 @@ ScanManager::~ScanManager() if ( !m_scanner.isNull() ) { - QMetaObject::invokeMethod( m_scanner.data(), "deleteLater", Qt::QueuedConnection ); - while( !m_scanner.isNull() ) - { - qDebug() << Q_FUNC_INFO << " scanner not deleted, processing events"; - QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); - TomahawkUtils::Sleep::msleep( 100 ); - } + m_musicScannerThreadController->quit(); + m_musicScannerThreadController->wait( 60000 ); - if ( m_musicScannerThreadController ) - m_musicScannerThreadController->quit(); - - if( m_musicScannerThreadController ) - { - while( !m_musicScannerThreadController->isFinished() ) - { - qDebug() << Q_FUNC_INFO << " scanner thread controller not finished, processing events"; - QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); - TomahawkUtils::Sleep::msleep( 100 ); - } - - delete m_musicScannerThreadController; - m_musicScannerThreadController = 0; - } + delete m_scanner.data(); + delete m_musicScannerThreadController; + m_musicScannerThreadController = 0; } } @@ -250,29 +233,12 @@ ScanManager::scannerFinished() { if ( !m_scanner.isNull() ) { - QMetaObject::invokeMethod( m_scanner.data(), "deleteLater", Qt::QueuedConnection ); - while( !m_scanner.isNull() ) - { - qDebug() << Q_FUNC_INFO << " scanner not deleted, processing events"; - QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); - TomahawkUtils::Sleep::msleep( 100 ); - } + m_musicScannerThreadController->quit(); + m_musicScannerThreadController->wait( 60000 ); - if ( m_musicScannerThreadController ) - m_musicScannerThreadController->quit(); - - if( m_musicScannerThreadController ) - { - while( !m_musicScannerThreadController->isFinished() ) - { - qDebug() << Q_FUNC_INFO << " scanner thread controller not finished, processing events"; - QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); - TomahawkUtils::Sleep::msleep( 100 ); - } - - delete m_musicScannerThreadController; - m_musicScannerThreadController = 0; - } + delete m_scanner.data(); + delete m_musicScannerThreadController; + m_musicScannerThreadController = 0; } emit finished(); } From 9236da101e4917b04ae5b3b291684334961a959b Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Mon, 30 May 2011 13:42:11 -0400 Subject: [PATCH 25/36] Expire logplayback commands in the UI after 10minites, they are stale Fixes TWK-39 --- .../database/databasecommand_logplayback.cpp | 2 +- src/libtomahawk/source.cpp | 15 ++++++++++++++- src/libtomahawk/source.h | 2 ++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/database/databasecommand_logplayback.cpp b/src/libtomahawk/database/databasecommand_logplayback.cpp index 3bdc40ef5..12ffa5c5e 100644 --- a/src/libtomahawk/database/databasecommand_logplayback.cpp +++ b/src/libtomahawk/database/databasecommand_logplayback.cpp @@ -45,7 +45,7 @@ DatabaseCommand_LogPlayback::postCommitHook() { emit trackPlayed( q ); } - else if ( m_action == Started ) + else if ( m_action == Started && QDateTime::fromTime_t( playtime() ).secsTo( QDateTime::currentDateTime() ) < 600 ) // if the play time is more than 10 minutes in the past, ignore { emit trackPlaying( q ); } diff --git a/src/libtomahawk/source.cpp b/src/libtomahawk/source.cpp index d601fcf8f..e852e0c83 100644 --- a/src/libtomahawk/source.cpp +++ b/src/libtomahawk/source.cpp @@ -24,7 +24,6 @@ #include "network/controlconnection.h" #include "database/databasecommand_addsource.h" #include "database/databasecommand_sourceoffline.h" -#include "database/databasecommand_logplayback.h" #include "database/database.h" #include @@ -50,6 +49,10 @@ Source::Source( int id, const QString& username ) m_isLocal = true; m_online = true; } + + m_currentTrackTimer.setInterval( 600000 ); // 10 minutes + m_currentTrackTimer.setSingleShot( true ); + connect( &m_currentTrackTimer, SIGNAL( timeout() ), this, SLOT( trackTimerFired() ) ); } @@ -278,4 +281,14 @@ Source::onPlaybackFinished( const Tomahawk::query_ptr& query ) { qDebug() << Q_FUNC_INFO << query->toString(); emit playbackFinished( query ); + + m_currentTrackTimer.start(); +} + +void +Source::trackTimerFired() +{ + m_currentTrack.clear(); + + emit stateChanged(); } diff --git a/src/libtomahawk/source.h b/src/libtomahawk/source.h index b1d9cc6f1..1179fd267 100644 --- a/src/libtomahawk/source.h +++ b/src/libtomahawk/source.h @@ -105,6 +105,7 @@ private slots: void onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::State oldstate, const QString& info ); void onPlaybackStarted( const Tomahawk::query_ptr& query ); void onPlaybackFinished( const Tomahawk::query_ptr& query ); + void trackTimerFired(); private: bool m_isLocal; @@ -118,6 +119,7 @@ private: Tomahawk::query_ptr m_currentTrack; QString m_textStatus; + QTimer m_currentTrackTimer; ControlConnection* m_cc; From 2aab17ba8ed2615ea6869ebf5754728e85902fb0 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 30 May 2011 21:19:15 +0000 Subject: [PATCH 26/36] * Updated tomahawk.nsi, now depends on the vlc stuff being extracted to ../vlc/ --- admin/win/nsi/tomahawk.nsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi index dbed64660..a135b08d8 100644 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -25,8 +25,8 @@ !define QT_DLL_PATH "${MING_BIN}" !define SQLITE_DLL_PATH "${MING_LIB}/qt4/plugins/sqldrivers" !define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt4/plugins/imageformats" -!define VLC_PATH "${MING_BIN}" -!define VLC_PLUGIN_PATH "${MING_LIB}\vlc\plugins" +!define VLC_PATH "${ROOT_PATH}\..\vlc" +!define VLC_PLUGIN_PATH "${VLC_PATH}\plugins" ;----------------------------------------------------------------------------- ; Increment installer revision number as part of this script. From 30323c2d8a2229920b574c0b6d0eec8b1fb55d6e Mon Sep 17 00:00:00 2001 From: Christopher Reichert Date: Sun, 29 May 2011 00:26:20 -0500 Subject: [PATCH 27/36] Shuffle Mode and Repeat mode are now stored on a playlist by playlist basis in Tomahawk.conf --- src/libtomahawk/playlist.cpp | 4 +++ src/libtomahawk/tomahawksettings.cpp | 32 +++++++++++++++++++++- src/libtomahawk/tomahawksettings.h | 8 ++++++ src/libtomahawk/viewmanager.cpp | 40 ++++++++++++++++++++++++++++ src/libtomahawk/viewmanager.h | 4 ++- src/tomahawkwindow.cpp | 1 - 6 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/playlist.cpp b/src/libtomahawk/playlist.cpp index 8d12dd00e..d3e88a764 100644 --- a/src/libtomahawk/playlist.cpp +++ b/src/libtomahawk/playlist.cpp @@ -28,6 +28,7 @@ #include "database/databasecommand_deleteplaylist.h" #include "database/databasecommand_renameplaylist.h" +#include "tomahawksettings.h" #include "pipeline.h" #include "source.h" #include "sourcelist.h" @@ -219,6 +220,9 @@ Playlist::load( const QString& guid ) bool Playlist::remove( const playlist_ptr& playlist ) { + TomahawkSettings *s = TomahawkSettings::instance(); + s->removePlaylistSettings( playlist->guid() ); + DatabaseCommand_DeletePlaylist* cmd = new DatabaseCommand_DeletePlaylist( playlist->author(), playlist->guid() ); Database::instance()->enqueue( QSharedPointer(cmd) ); diff --git a/src/libtomahawk/tomahawksettings.cpp b/src/libtomahawk/tomahawksettings.cpp index a2751484a..0333eeb4f 100644 --- a/src/libtomahawk/tomahawksettings.cpp +++ b/src/libtomahawk/tomahawksettings.cpp @@ -26,6 +26,7 @@ #include #include #include "sip/SipHandler.h" +#include "playlistinterface.h" #define VERSION 3 @@ -187,7 +188,6 @@ TomahawkSettings::setWatchForChanges( bool watch ) setValue( "watchForChanges", watch ); } - void TomahawkSettings::setAcceptedLegalWarning( bool accept ) { @@ -381,6 +381,36 @@ TomahawkSettings::setPlaylistColumnSizes( const QString& playlistid, const QByte setValue( QString( "ui/playlist/%1/columnSizes" ).arg( playlistid ), state ); } +bool +TomahawkSettings::shuffleState( const QString& playlistid ) const +{ + return value( QString( "ui/playlist/%1/shuffleState" ).arg( playlistid )).toBool(); +} + +void +TomahawkSettings::setShuffleState( const QString& playlistid, bool state) +{ + setValue( QString( "ui/playlist/%1/shuffleState" ).arg( playlistid ), state ); +} + +void +TomahawkSettings::removePlaylistSettings( const QString& playlistid ) +{ + remove( QString( "ui/playlist/%1/shuffleState" ).arg( playlistid ) ); + remove( QString( "ui/playlist/%1/repeatMode" ).arg( playlistid ) ); +} + +void +TomahawkSettings::setRepeatMode( const QString& playlistid, PlaylistInterface::RepeatMode mode ) +{ + setValue( QString( "ui/playlist/%1/repeatMode" ).arg( playlistid ), (int)mode ); +} + +PlaylistInterface::RepeatMode +TomahawkSettings::repeatMode( const QString& playlistid ) +{ + return (PlaylistInterface::RepeatMode)value( QString( "ui/playlist/%1/repeatMode" ).arg( playlistid )).toInt(); +} QList TomahawkSettings::recentlyPlayedPlaylists() const diff --git a/src/libtomahawk/tomahawksettings.h b/src/libtomahawk/tomahawksettings.h index 90ce075c0..a127b8d05 100644 --- a/src/libtomahawk/tomahawksettings.h +++ b/src/libtomahawk/tomahawksettings.h @@ -68,6 +68,14 @@ public: QList recentlyPlayedPlaylists() const; QStringList recentlyPlayedPlaylistGuids( unsigned int amount = 0 ) const; void appendRecentlyPlayedPlaylist( const Tomahawk::playlist_ptr& playlist ); + + bool shuffleState( const QString& playlistid ) const; + void setShuffleState( const QString& playlistid, bool state ); + PlaylistInterface::RepeatMode repeatMode( const QString& playlistid ); + void setRepeatMode( const QString& playlistid, PlaylistInterface::RepeatMode mode); + + // remove shuffle state and repeat state + void removePlaylistSettings( const QString& playlistid ); /// SIP plugins // all plugins we know about. loaded, unloaded, enabled, disabled. diff --git a/src/libtomahawk/viewmanager.cpp b/src/libtomahawk/viewmanager.cpp index 6b9380aef..1fbd10fb9 100644 --- a/src/libtomahawk/viewmanager.cpp +++ b/src/libtomahawk/viewmanager.cpp @@ -142,6 +142,7 @@ ViewManager::ViewManager( QObject* parent ) ViewManager::~ViewManager() { + saveCurrentPlaylistSettings(); delete m_widget; } @@ -581,6 +582,9 @@ ViewManager::setPage( ViewPage* page, bool trackHistory ) if ( !page ) return; + // save the old playlist shuffle state in config before we change playlists + saveCurrentPlaylistSettings(); + unlinkPlaylist(); if ( !m_pageHistory.contains( page ) ) @@ -652,6 +656,24 @@ ViewManager::unlinkPlaylist() } } +void +ViewManager::saveCurrentPlaylistSettings() +{ + TomahawkSettings* s = TomahawkSettings::instance(); + Tomahawk::playlist_ptr pl = playlistForInterface( currentPlaylistInterface() ); + + if ( !pl.isNull() ) { + s->setShuffleState( pl->guid(), currentPlaylistInterface()->shuffled() ); + s->setRepeatMode( pl->guid(), currentPlaylistInterface()->repeatMode() ); + } else { + Tomahawk::dynplaylist_ptr dynPl = dynamicPlaylistForInterface( currentPlaylistInterface() ); + if ( !dynPl.isNull() ) { + s->setShuffleState( dynPl->guid(), currentPlaylistInterface()->shuffled() ); + s->setRepeatMode( dynPl->guid(), currentPlaylistInterface()->repeatMode() ); + } + } +} + void ViewManager::updateView() @@ -704,8 +726,26 @@ ViewManager::updateView() m_infobar->setCaption( currentPage()->title() ); m_infobar->setDescription( currentPage()->description() ); m_infobar->setPixmap( currentPage()->pixmap() ); + + // turn on shuffle/repeat mode for the new playlist view if specified in config + loadCurrentPlaylistSettings(); } +void +ViewManager::loadCurrentPlaylistSettings() +{ + TomahawkSettings* s = TomahawkSettings::instance(); + Tomahawk::playlist_ptr pl = playlistForInterface( currentPlaylistInterface() ); + if ( !pl.isNull() ) { + currentPlaylistInterface()->setShuffled( s->shuffleState( pl->guid() )); + currentPlaylistInterface()->setRepeatMode( s->repeatMode( pl->guid() )); + } else { + Tomahawk::dynplaylist_ptr dynPl = dynamicPlaylistForInterface( currentPlaylistInterface() ); + if ( !dynPl.isNull() ) { + currentPlaylistInterface()->setShuffled( s->shuffleState( dynPl->guid() )); + } + } +} void ViewManager::onWidgetDestroyed( QWidget* widget ) diff --git a/src/libtomahawk/viewmanager.h b/src/libtomahawk/viewmanager.h index 234e9c846..0dbd21e15 100644 --- a/src/libtomahawk/viewmanager.h +++ b/src/libtomahawk/viewmanager.h @@ -162,7 +162,9 @@ private: void setPage( Tomahawk::ViewPage* page, bool trackHistory = true ); void updateView(); void unlinkPlaylist(); - + void saveCurrentPlaylistSettings(); + void loadCurrentPlaylistSettings(); + Tomahawk::playlist_ptr playlistForInterface( PlaylistInterface* interface ) const; Tomahawk::dynplaylist_ptr dynamicPlaylistForInterface( PlaylistInterface* interface ) const; Tomahawk::collection_ptr collectionForInterface( PlaylistInterface* interface ) const; diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 16d8fff53..13989b4d4 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -233,7 +233,6 @@ TomahawkWindow::loadSettings() restoreState( s->mainWindowState() ); if ( !s->mainWindowSplitterState().isEmpty() ) ui->splitter->restoreState( s->mainWindowSplitterState() ); - #ifdef QT_MAC_USE_COCOA if( workaround ) { // Make it visible again From 5cd9e49629b920d0d48c884bb2a827ef75a1f2f7 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 31 May 2011 18:17:46 -0400 Subject: [PATCH 28/36] Add support for SPMediaKeyTap. This lets tomahawk stop iTunes from opening when a user presses one of the media keys. Thanks to the Clementine project and tyler.s.rhodes@gmail.com for much of the code in this patch. --- src/CMakeLists.osx.txt | 2 + src/CMakeLists.txt | 2 + src/mac/macdelegate.h | 40 +++++++++++++++ src/mac/tomahawkapp_mac.mm | 95 +++++++++++++++++++++++++---------- src/tomahawkapp_macdelegate.h | 43 ---------------- thirdparty/CMakeLists.txt | 3 ++ 6 files changed, 116 insertions(+), 69 deletions(-) create mode 100644 src/mac/macdelegate.h delete mode 100644 src/tomahawkapp_macdelegate.h diff --git a/src/CMakeLists.osx.txt b/src/CMakeLists.osx.txt index f1a5b2d20..f88939b80 100644 --- a/src/CMakeLists.osx.txt +++ b/src/CMakeLists.osx.txt @@ -5,6 +5,8 @@ SET( OS_SPECIFIC_LINK_LIBRARIES ${COREAUDIO_LIBRARY} ${COREFOUNDATION_LIBRARY} + SPMediaKeyTap + /System/Library/Frameworks/AppKit.framework /System/Library/Frameworks/Carbon.framework /System/Library/Frameworks/DiskArbitration.framework diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d292c954..efedc8706 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -160,6 +160,8 @@ IF( UNIX ) ENDIF( UNIX ) IF( APPLE ) + INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/thirdparty/SPMediaKeyTap ) + SET( tomahawkHeaders ${tomahawkHeaders} mac/tomahawkapp_mac.h mac/macshortcuthandler.h ) SET( tomahawkSources ${tomahawkSources} mac/tomahawkapp_mac.mm mac/macshortcuthandler.cpp ) diff --git a/src/mac/macdelegate.h b/src/mac/macdelegate.h new file mode 100644 index 000000000..7d684d39e --- /dev/null +++ b/src/mac/macdelegate.h @@ -0,0 +1,40 @@ +#ifndef MACDELEGATE_H +#define MACDELEGATE_H + +// This file inspired by clementine's macdelegate.h + +#import + +#include "SPMediaKeyTap.h" + +namespace Tomahawk { + class MacShortcutHandler; + class PlatformInterface; +} + +#ifdef SNOW_LEOPARD +@interface AppDelegate :NSObject { +#else +@interface AppDelegate :NSObject { +#endif + Tomahawk::PlatformInterface* application_handler_; + NSMenu* dock_menu_; + SPMediaKeyTap* key_tap_; + Tomahawk::MacShortcutHandler* shortcut_handler_; +} + +- (id) initWithHandler: (Tomahawk::PlatformInterface*)handler; +// NSApplicationDelegate +- (BOOL) applicationShouldHandleReopen: (NSApplication*)app hasVisibleWindows:(BOOL)flag; +- (NSMenu*) applicationDockMenu: (NSApplication*)sender; +- (void) setDockMenu: (NSMenu*)menu; +- (Tomahawk::MacShortcutHandler*) shortcutHandler; +- (void) setShortcutHandler: (Tomahawk::MacShortcutHandler*)backend; +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification; +- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender; +- (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event; +@end + + + +#endif // MACDELEGATE_H diff --git a/src/mac/tomahawkapp_mac.mm b/src/mac/tomahawkapp_mac.mm index 44660e742..144a33853 100644 --- a/src/mac/tomahawkapp_mac.mm +++ b/src/mac/tomahawkapp_mac.mm @@ -17,7 +17,7 @@ */ #include "tomahawkapp_mac.h" -#include "tomahawkapp_macdelegate.h" +#include "macdelegate.h" #include "macshortcuthandler.h" #include @@ -42,6 +42,7 @@ // See: http://www.rogueamoeba.com/utm/2007/09/29/apple-keyboard-media-key-event-handling/ @interface MacApplication :NSApplication { + AppDelegate* delegate_; Tomahawk::MacShortcutHandler* shortcut_handler_; Tomahawk::PlatformInterface* application_handler_; } @@ -51,7 +52,6 @@ - (Tomahawk::PlatformInterface*) application_handler; - (void) setApplicationHandler: (Tomahawk::PlatformInterface*)handler; -- (void) mediaKeyEvent: (int)key state: (BOOL)state repeat: (BOOL)repeat; @end @@ -59,14 +59,22 @@ - (id) init { if ((self = [super init])) { - application_handler_ = nil; -// dock_menu_ = nil; + application_handler_ = nil; + shortcut_handler_ = nil; + //dock_menu_ = nil; } return self; } - (id) initWithHandler: (Tomahawk::PlatformInterface*)handler { application_handler_ = handler; + + // Register defaults for the whitelist of apps that want to use media keys + [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: + [SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], @"SPApplicationsNeedingMediaKeys", + nil]]; + + return self; } @@ -76,7 +84,7 @@ } return YES; } -/* + - (void) setDockMenu: (NSMenu*)menu { dock_menu_ = menu; } @@ -84,7 +92,45 @@ - (NSMenu*) applicationDockMenu: (NSApplication*)sender { return dock_menu_; } -*/ + + +- (Tomahawk::MacShortcutHandler*) shortcutHandler { + return shortcut_handler_; +} + +- (void) setShortcutHandler: (Tomahawk::MacShortcutHandler*)handler { + qDebug() << "Setting shortcut handler of MacApp"; + // should be the same as MacApplication's + shortcut_handler_ = handler; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + key_tap_ = [[SPMediaKeyTap alloc] initWithDelegate:self]; + if([SPMediaKeyTap usesGlobalMediaKeyTap]) + [key_tap_ startWatchingMediaKeys]; + else + qWarning()<<"Media key monitoring disabled"; + +} + +- (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event { + NSAssert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys, @"Unexpected NSEvent in mediaKeyTap:receivedMediaKeyEvent:"); + + int key_code = (([event data1] & 0xFFFF0000) >> 16); + int key_flags = ([event data1] & 0x0000FFFF); + BOOL key_is_pressed = (((key_flags & 0xFF00) >> 8)) == 0xA; + // not used. keep just in case + // int key_repeat = (key_flags & 0x1); + + if (!shortcut_handler_) { + qWarning() << "No shortcut handler when we get a media key event..."; + return; + } + if (key_is_pressed) { + shortcut_handler_->macMediaKeyPressed(key_code); + } +} + - (BOOL) application: (NSApplication*)app openFile:(NSString*)filename { qDebug() << "Wants to open:" << [filename UTF8String]; @@ -94,6 +140,11 @@ return NO; } + +- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*) sender { + return NSTerminateNow; +} + @end @implementation MacApplication @@ -127,7 +178,7 @@ } - (void) setShortcutHandler: (Tomahawk::MacShortcutHandler*)handler { - qDebug() << "Setting shortcut handler of MacAPp"; + // should be the same as AppDelegate's shortcut_handler_ = handler; } @@ -136,30 +187,22 @@ } - (void) setApplicationHandler: (Tomahawk::PlatformInterface*)handler { - AppDelegate* delegate = [[AppDelegate alloc] initWithHandler:handler]; - [self setDelegate:delegate]; + delegate_ = [[AppDelegate alloc] initWithHandler:handler]; + // App-shortcut-handler set before delegate is set. + // this makes sure the delegate's shortcut_handler is set + [delegate_ setShortcutHandler:shortcut_handler_]; + [self setDelegate:delegate_]; } -(void) sendEvent: (NSEvent*)event { - if ([event type] == NSSystemDefined && [event subtype] == 8) { - int keycode = (([event data1] & 0xFFFF0000) >> 16); - int keyflags = ([event data1] & 0x0000FFFF); - int keystate = (((keyflags & 0xFF00) >> 8)) == 0xA; - int keyrepeat = (keyflags & 0x1); + // If event tap is not installed, handle events that reach the app instead + BOOL shouldHandleMediaKeyEventLocally = ![SPMediaKeyTap usesGlobalMediaKeyTap]; - [self mediaKeyEvent: keycode state: keystate repeat: keyrepeat]; - } + if(shouldHandleMediaKeyEventLocally && [event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys) { + [(id)[self delegate] mediaKeyTap: nil receivedMediaKeyEvent: event]; + } - [super sendEvent: event]; -} - --(void) mediaKeyEvent: (int)key state: (BOOL)state repeat: (BOOL)repeat { - if (!shortcut_handler_) { - return; - } - if (state == 0) { - shortcut_handler_->macMediaKeyPressed(key); - } + [super sendEvent: event]; } @end diff --git a/src/tomahawkapp_macdelegate.h b/src/tomahawkapp_macdelegate.h deleted file mode 100644 index 01ef7a401..000000000 --- a/src/tomahawkapp_macdelegate.h +++ /dev/null @@ -1,43 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * - * 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 . - */ - -#import - -#include "config.h" - -// this file copied and inspired by mac_startup.* in clementine player, -// copyright David Sansome 2010 -namespace Tomahawk { - class PlatformInterface; -} - -#ifdef SNOW_LEOPARD -@interface AppDelegate : NSObject { -#else -@interface AppDelegate : NSObject { -#endif - Tomahawk::PlatformInterface* application_handler_; - //NSMenu* dock_menu_; -} - -- (id) initWithHandler: (Tomahawk::PlatformInterface*)handler; -// NSApplicationDelegate -- (BOOL) applicationShouldHandleReopen: (NSApplication*)app hasVisibleWindows:(BOOL)flag; -//- (NSMenu*) applicationDockMenu: (NSApplication*)sender; -//- (void) setDockMenu: (NSMenu*)menu; -@end diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 04dc95eb4..096668ed2 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -1,2 +1,5 @@ ADD_SUBDIRECTORY( qxt ) ADD_SUBDIRECTORY( liblastfm2 ) +IF( APPLE ) + ADD_SUBDIRECTORY( SPMediaKeyTap ) +ENDIF() From 7176b7f0a930150a3c9154a13339541798a5bf45 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 31 May 2011 18:20:29 -0400 Subject: [PATCH 29/36] Add the SPMediaKeyTap thirdparty lib. --- thirdparty/SPMediaKeyTap/CMakeLists.txt | 13 + thirdparty/SPMediaKeyTap/LICENSE | 8 + thirdparty/SPMediaKeyTap/README.md | 12 + .../SPInvocationGrabbing/.svn/all-wcprops | 35 ++ .../SPInvocationGrabbing/.svn/entries | 198 ++++++++++++ .../NSObject+SPInvocationGrabbing.h.svn-base | 30 ++ .../NSObject+SPInvocationGrabbing.m.svn-base | 128 ++++++++ .../.svn/text-base/gistfile3.m.svn-base | 28 ++ .../.svn/text-base/gistfile4.m.svn-base | 12 + .../.svn/text-base/main.m.svn-base | 38 +++ .../NSObject+SPInvocationGrabbing.h | 30 ++ .../NSObject+SPInvocationGrabbing.m | 128 ++++++++ .../SPInvocationGrabbing/gistfile3.m | 28 ++ .../SPInvocationGrabbing/gistfile4.m | 12 + .../SPMediaKeyTap/SPInvocationGrabbing/main.m | 38 +++ thirdparty/SPMediaKeyTap/SPMediaKeyTap.h | 34 ++ thirdparty/SPMediaKeyTap/SPMediaKeyTap.m | 300 ++++++++++++++++++ .../SPMediaKeyTap/SPMediaKeyTapDelegate.m | 25 ++ 18 files changed, 1097 insertions(+) create mode 100644 thirdparty/SPMediaKeyTap/CMakeLists.txt create mode 100644 thirdparty/SPMediaKeyTap/LICENSE create mode 100644 thirdparty/SPMediaKeyTap/README.md create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/all-wcprops create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/entries create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.h.svn-base create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.m.svn-base create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile3.m.svn-base create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile4.m.svn-base create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/main.m.svn-base create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile3.m create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile4.m create mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/main.m create mode 100644 thirdparty/SPMediaKeyTap/SPMediaKeyTap.h create mode 100644 thirdparty/SPMediaKeyTap/SPMediaKeyTap.m create mode 100644 thirdparty/SPMediaKeyTap/SPMediaKeyTapDelegate.m diff --git a/thirdparty/SPMediaKeyTap/CMakeLists.txt b/thirdparty/SPMediaKeyTap/CMakeLists.txt new file mode 100644 index 000000000..9f51aa5bb --- /dev/null +++ b/thirdparty/SPMediaKeyTap/CMakeLists.txt @@ -0,0 +1,13 @@ +set(SPMEDIAKEY-SOURCES + SPMediaKeyTap.m + SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m +) + +set(SPMEDIAKEY-HEADERS + SPMediaKeyTap.h + SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h +) + +ADD_LIBRARY(SPMediaKeyTap STATIC + ${SPMEDIAKEY-SOURCES} +) diff --git a/thirdparty/SPMediaKeyTap/LICENSE b/thirdparty/SPMediaKeyTap/LICENSE new file mode 100644 index 000000000..a43e4122e --- /dev/null +++ b/thirdparty/SPMediaKeyTap/LICENSE @@ -0,0 +1,8 @@ +Copyright (c) 2011, Joachim Bengtsson +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Neither the name of the organization nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/SPMediaKeyTap/README.md b/thirdparty/SPMediaKeyTap/README.md new file mode 100644 index 000000000..8027d2452 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/README.md @@ -0,0 +1,12 @@ +SPMediaKeyTap +============= + +`SPMediaKeyTap` abstracts a `CGEventHook` and other nastiness in order to give you a relatively simple API to receive media key events (prev/next/playpause, on F7 to F9 on modern MacBook Pros) exclusively, without them reaching other applications like iTunes. `SPMediaKeyTap` is clever enough to resign its exclusive lock on media keys by looking for which application was active most recently: if that application is in `SPMediaKeyTap`'s whitelist, it will resign the keys. This is similar to the behavior of Apple's applications collaborating on media key handling exclusivity, but unfortunately, Apple are not exposing any APIs allowing third-parties to join in on this collaboration. + +For now, the whitelist is just a hardcoded array in `+[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers]`. If your app starts using `SPMediaKeyTap`, please [mail me](mailto:nevyn@spotify.com) your bundle ID, and I'll include it in the canonical repository. This is a bad solution; a better solution would be to use distributed notifications to collaborate in creating this whitelist at runtime. Hopefully someone'll have the time and energy to write this soon. + +In `Example/SPMediaKeyTapExampleAppDelegate.m` is an example of both how you use `SPMediaKeyTap`, and how you handle the semi-private `NSEvent` subtypes involved in media keys, including on how to fall back to non-event tap handling of these events. + +`SPMediaKeyTap` and other `CGEventHook`s on the event type `NSSystemDefined` is known to interfere with each other and applications doing weird stuff with mouse input, because mouse clicks are also part of the `NSSystemDefined` category. The single issue we have had reported here at Spotify is Adobe Fireworks, in which item selection stops working with `SPMediaKeyTap` is active. + +`SPMediaKeyTap` requires 10.5 to work, and disables itself on 10.4. \ No newline at end of file diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/all-wcprops b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/all-wcprops new file mode 100644 index 000000000..657122656 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/all-wcprops @@ -0,0 +1,35 @@ +K 25 +svn:wc:ra_dav:version-url +V 68 +/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing +END +main.m +K 25 +svn:wc:ra_dav:version-url +V 75 +/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing/main.m +END +NSObject+SPInvocationGrabbing.h +K 25 +svn:wc:ra_dav:version-url +V 100 +/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h +END +gistfile3.m +K 25 +svn:wc:ra_dav:version-url +V 80 +/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile3.m +END +gistfile4.m +K 25 +svn:wc:ra_dav:version-url +V 80 +/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile4.m +END +NSObject+SPInvocationGrabbing.m +K 25 +svn:wc:ra_dav:version-url +V 100 +/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m +END diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/entries b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/entries new file mode 100644 index 000000000..83ab274af --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/entries @@ -0,0 +1,198 @@ +10 + +dir +3354 +http://clementine-player.googlecode.com/svn/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing +http://clementine-player.googlecode.com/svn + + + +2011-05-30T10:09:34.688096Z +3341 +john.maguire + + + + + + + + + + + + + + +94c5599e-fc6c-11de-b061-8119ef04aefe + +main.m +file + + + + +2011-05-30T19:32:29.000000Z +64cedb4901f14b8f82160d41834812c7 +2011-05-30T10:09:34.688096Z +3341 +john.maguire + + + + + + + + + + + + + + + + + + + + + +778 + +NSObject+SPInvocationGrabbing.h +file + + + + +2011-05-30T19:32:29.000000Z +21417129fd15adb3e8e88f53c96a5249 +2011-05-30T10:09:34.688096Z +3341 +john.maguire + + + + + + + + + + + + + + + + + + + + + +841 + +gistfile3.m +file + + + + +2011-05-30T19:32:29.000000Z +1794e4fc655ea831def78ad9b3b22f5d +2011-05-30T10:09:34.688096Z +3341 +john.maguire + + + + + + + + + + + + + + + + + + + + + +1080 + +gistfile4.m +file + + + + +2011-05-30T19:32:29.000000Z +4a243c33e49a1d8b7212def259a1873a +2011-05-30T10:09:34.688096Z +3341 +john.maguire + + + + + + + + + + + + + + + + + + + + + +385 + +NSObject+SPInvocationGrabbing.m +file + + + + +2011-05-30T19:32:29.000000Z +52da2f8cede4bf66b97df4df3a002086 +2011-05-30T10:09:34.688096Z +3341 +john.maguire + + + + + + + + + + + + + + + + + + + + + +2915 + diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.h.svn-base b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.h.svn-base new file mode 100644 index 000000000..d30233daf --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.h.svn-base @@ -0,0 +1,30 @@ +#import + +@interface SPInvocationGrabber : NSObject { + id _object; + NSInvocation *_invocation; + int frameCount; + char **frameStrings; + BOOL backgroundAfterForward; + BOOL onMainAfterForward; + BOOL waitUntilDone; +} +-(id)initWithObject:(id)obj; +-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; +@property (readonly, retain, nonatomic) id object; +@property (readonly, retain, nonatomic) NSInvocation *invocation; +@property BOOL backgroundAfterForward; +@property BOOL onMainAfterForward; +@property BOOL waitUntilDone; +-(void)invoke; // will release object and invocation +-(void)printBacktrace; +-(void)saveBacktrace; +@end + +@interface NSObject (SPInvocationGrabbing) +-(id)grab; +-(id)invokeAfter:(NSTimeInterval)delta; +-(id)nextRunloop; +-(id)inBackground; +-(id)onMainAsync:(BOOL)async; +@end diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.m.svn-base b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.m.svn-base new file mode 100644 index 000000000..8ba4adb8f --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.m.svn-base @@ -0,0 +1,128 @@ +#import "NSObject+SPInvocationGrabbing.h" +#import + +#pragma mark Invocation grabbing +@interface SPInvocationGrabber () +@property (readwrite, retain, nonatomic) id object; +@property (readwrite, retain, nonatomic) NSInvocation *invocation; + +@end + +@implementation SPInvocationGrabber +- (id)initWithObject:(id)obj; +{ + return [self initWithObject:obj stacktraceSaving:YES]; +} + +-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; +{ + self.object = obj; + + if(saveStack) + [self saveBacktrace]; + + return self; +} +-(void)dealloc; +{ + free(frameStrings); + self.object = nil; + self.invocation = nil; + [super dealloc]; +} +@synthesize invocation = _invocation, object = _object; + +@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone; +- (void)runInBackground; +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + @try { + [self invoke]; + } + @finally { + [pool drain]; + } +} + + +- (void)forwardInvocation:(NSInvocation *)anInvocation { + [anInvocation retainArguments]; + anInvocation.target = _object; + self.invocation = anInvocation; + + if(backgroundAfterForward) + [NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil]; + else if(onMainAfterForward) + [self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone]; +} +- (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector { + NSMethodSignature *signature = [super methodSignatureForSelector:inSelector]; + if (signature == NULL) + signature = [_object methodSignatureForSelector:inSelector]; + + return signature; +} + +- (void)invoke; +{ + + @try { + [_invocation invoke]; + } + @catch (NSException * e) { + NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e); + [self printBacktrace]; + printf("\n"); + [e raise]; + } + + self.invocation = nil; + self.object = nil; +} + +-(void)saveBacktrace; +{ + void *backtraceFrames[128]; + frameCount = backtrace(&backtraceFrames[0], 128); + frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount); +} +-(void)printBacktrace; +{ + int x; + for(x = 3; x < frameCount; x++) { + if(frameStrings[x] == NULL) { break; } + printf("%s\n", frameStrings[x]); + } +} +@end + +@implementation NSObject (SPInvocationGrabbing) +-(id)grab; +{ + return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease]; +} +-(id)invokeAfter:(NSTimeInterval)delta; +{ + id grabber = [self grab]; + [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO]; + return grabber; +} +- (id)nextRunloop; +{ + return [self invokeAfter:0]; +} +-(id)inBackground; +{ + SPInvocationGrabber *grabber = [self grab]; + grabber.backgroundAfterForward = YES; + return grabber; +} +-(id)onMainAsync:(BOOL)async; +{ + SPInvocationGrabber *grabber = [self grab]; + grabber.onMainAfterForward = YES; + grabber.waitUntilDone = !async; + return grabber; +} + +@end diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile3.m.svn-base b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile3.m.svn-base new file mode 100644 index 000000000..712293780 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile3.m.svn-base @@ -0,0 +1,28 @@ +// A ++(UIView*)flashAt:(CGRect)r in:(UIView*)parent color:(UIColor*)color; +{ + float duration = 0.5; + UIView *flash = [[[UIView alloc] initWithFrame:r] autorelease]; + flash.backgroundColor = color; + [parent addSubview:flash]; + [[flash invokeAfter:duration+0.1] removeFromSuperview]; + + [UIView beginAnimations:@"SPFlash" context:NULL]; + [UIView setAnimationDuration:duration]; + flash.alpha = 0.0; + [UIView commitAnimations]; + return flash; +} + +// B +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + // Force the animation to happen by calling this method again after a small + // delay - see http://blog.instapaper.com/post/53568356 + [[self nextRunloop] delayedTableViewDidSelectRowAtIndexPath: indexPath]; +} + +// C +[[tableView invokeAfter:0.15] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; +[[tableView invokeAfter:0.30] deselectRowAtIndexPath:indexPath animated:YES]; +[[tableView invokeAfter:0.45] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile4.m.svn-base b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile4.m.svn-base new file mode 100644 index 000000000..8255abab0 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile4.m.svn-base @@ -0,0 +1,12 @@ +@interface MyClass : NSObject +-(BOOL)areTheNewViewersGoneYet:(Duck*)duck; +@end +... +MyClass *myInstance = [[MyClass alloc] init]; +id invocationGrabber = [[[SPInvocationGrabber alloc] initWithTarget:myInstance] autorelease]; + + +[invocationGrabber areTheNewViewersGoneYet:[Duck yellowDuck]]; // line 9 + + +NSInvocation *invocationForAreTheNewViewersGoneYet = [invocationGrabber invocation]; diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/main.m.svn-base b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/main.m.svn-base new file mode 100644 index 000000000..a6ce17f39 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/main.m.svn-base @@ -0,0 +1,38 @@ +#import +#import "NSObject+SPInvocationGrabbing.h" + +@interface Foo : NSObject { + int a; +} +-(void)startIt; +-(void)theBackgroundStuff; +-(void)theForegroundStuff; +@end + +@implementation Foo +-(void)startIt; +{ + NSLog(@"Starting out on the main thread..."); + a = 3; + [[self inBackground] theBackgroundStuff]; +} +-(void)theBackgroundStuff; +{ + NSLog(@"Woah, this is a background thread!"); + a += 6; + [[self onMainAsync:YES] theForegroundStuff]; +} +-(void)theForegroundStuff; +{ + NSLog(@"Hey presto: %d", a); +} +@end + +int main() { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + Foo *foo = [Foo new]; + [foo startIt]; + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; + [pool release]; + return 0; +} \ No newline at end of file diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h new file mode 100644 index 000000000..d30233daf --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h @@ -0,0 +1,30 @@ +#import + +@interface SPInvocationGrabber : NSObject { + id _object; + NSInvocation *_invocation; + int frameCount; + char **frameStrings; + BOOL backgroundAfterForward; + BOOL onMainAfterForward; + BOOL waitUntilDone; +} +-(id)initWithObject:(id)obj; +-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; +@property (readonly, retain, nonatomic) id object; +@property (readonly, retain, nonatomic) NSInvocation *invocation; +@property BOOL backgroundAfterForward; +@property BOOL onMainAfterForward; +@property BOOL waitUntilDone; +-(void)invoke; // will release object and invocation +-(void)printBacktrace; +-(void)saveBacktrace; +@end + +@interface NSObject (SPInvocationGrabbing) +-(id)grab; +-(id)invokeAfter:(NSTimeInterval)delta; +-(id)nextRunloop; +-(id)inBackground; +-(id)onMainAsync:(BOOL)async; +@end diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m new file mode 100644 index 000000000..8ba4adb8f --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m @@ -0,0 +1,128 @@ +#import "NSObject+SPInvocationGrabbing.h" +#import + +#pragma mark Invocation grabbing +@interface SPInvocationGrabber () +@property (readwrite, retain, nonatomic) id object; +@property (readwrite, retain, nonatomic) NSInvocation *invocation; + +@end + +@implementation SPInvocationGrabber +- (id)initWithObject:(id)obj; +{ + return [self initWithObject:obj stacktraceSaving:YES]; +} + +-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; +{ + self.object = obj; + + if(saveStack) + [self saveBacktrace]; + + return self; +} +-(void)dealloc; +{ + free(frameStrings); + self.object = nil; + self.invocation = nil; + [super dealloc]; +} +@synthesize invocation = _invocation, object = _object; + +@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone; +- (void)runInBackground; +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + @try { + [self invoke]; + } + @finally { + [pool drain]; + } +} + + +- (void)forwardInvocation:(NSInvocation *)anInvocation { + [anInvocation retainArguments]; + anInvocation.target = _object; + self.invocation = anInvocation; + + if(backgroundAfterForward) + [NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil]; + else if(onMainAfterForward) + [self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone]; +} +- (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector { + NSMethodSignature *signature = [super methodSignatureForSelector:inSelector]; + if (signature == NULL) + signature = [_object methodSignatureForSelector:inSelector]; + + return signature; +} + +- (void)invoke; +{ + + @try { + [_invocation invoke]; + } + @catch (NSException * e) { + NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e); + [self printBacktrace]; + printf("\n"); + [e raise]; + } + + self.invocation = nil; + self.object = nil; +} + +-(void)saveBacktrace; +{ + void *backtraceFrames[128]; + frameCount = backtrace(&backtraceFrames[0], 128); + frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount); +} +-(void)printBacktrace; +{ + int x; + for(x = 3; x < frameCount; x++) { + if(frameStrings[x] == NULL) { break; } + printf("%s\n", frameStrings[x]); + } +} +@end + +@implementation NSObject (SPInvocationGrabbing) +-(id)grab; +{ + return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease]; +} +-(id)invokeAfter:(NSTimeInterval)delta; +{ + id grabber = [self grab]; + [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO]; + return grabber; +} +- (id)nextRunloop; +{ + return [self invokeAfter:0]; +} +-(id)inBackground; +{ + SPInvocationGrabber *grabber = [self grab]; + grabber.backgroundAfterForward = YES; + return grabber; +} +-(id)onMainAsync:(BOOL)async; +{ + SPInvocationGrabber *grabber = [self grab]; + grabber.onMainAfterForward = YES; + grabber.waitUntilDone = !async; + return grabber; +} + +@end diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile3.m b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile3.m new file mode 100644 index 000000000..712293780 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile3.m @@ -0,0 +1,28 @@ +// A ++(UIView*)flashAt:(CGRect)r in:(UIView*)parent color:(UIColor*)color; +{ + float duration = 0.5; + UIView *flash = [[[UIView alloc] initWithFrame:r] autorelease]; + flash.backgroundColor = color; + [parent addSubview:flash]; + [[flash invokeAfter:duration+0.1] removeFromSuperview]; + + [UIView beginAnimations:@"SPFlash" context:NULL]; + [UIView setAnimationDuration:duration]; + flash.alpha = 0.0; + [UIView commitAnimations]; + return flash; +} + +// B +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + // Force the animation to happen by calling this method again after a small + // delay - see http://blog.instapaper.com/post/53568356 + [[self nextRunloop] delayedTableViewDidSelectRowAtIndexPath: indexPath]; +} + +// C +[[tableView invokeAfter:0.15] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; +[[tableView invokeAfter:0.30] deselectRowAtIndexPath:indexPath animated:YES]; +[[tableView invokeAfter:0.45] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile4.m b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile4.m new file mode 100644 index 000000000..8255abab0 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile4.m @@ -0,0 +1,12 @@ +@interface MyClass : NSObject +-(BOOL)areTheNewViewersGoneYet:(Duck*)duck; +@end +... +MyClass *myInstance = [[MyClass alloc] init]; +id invocationGrabber = [[[SPInvocationGrabber alloc] initWithTarget:myInstance] autorelease]; + + +[invocationGrabber areTheNewViewersGoneYet:[Duck yellowDuck]]; // line 9 + + +NSInvocation *invocationForAreTheNewViewersGoneYet = [invocationGrabber invocation]; diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/main.m b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/main.m new file mode 100644 index 000000000..a6ce17f39 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/main.m @@ -0,0 +1,38 @@ +#import +#import "NSObject+SPInvocationGrabbing.h" + +@interface Foo : NSObject { + int a; +} +-(void)startIt; +-(void)theBackgroundStuff; +-(void)theForegroundStuff; +@end + +@implementation Foo +-(void)startIt; +{ + NSLog(@"Starting out on the main thread..."); + a = 3; + [[self inBackground] theBackgroundStuff]; +} +-(void)theBackgroundStuff; +{ + NSLog(@"Woah, this is a background thread!"); + a += 6; + [[self onMainAsync:YES] theForegroundStuff]; +} +-(void)theForegroundStuff; +{ + NSLog(@"Hey presto: %d", a); +} +@end + +int main() { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + Foo *foo = [Foo new]; + [foo startIt]; + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; + [pool release]; + return 0; +} \ No newline at end of file diff --git a/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h new file mode 100644 index 000000000..f33ad7040 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h @@ -0,0 +1,34 @@ +#include +#import +#import + +// http://overooped.com/post/2593597587/mediakeys + +#define SPSystemDefinedEventMediaKeys 8 + +@interface SPMediaKeyTap : NSObject { + EventHandlerRef _app_switching_ref; + EventHandlerRef _app_terminating_ref; + CFMachPortRef _eventPort; + CFRunLoopSourceRef _eventPortSource; + CFRunLoopRef _tapThreadRL; + BOOL _shouldInterceptMediaKeyEvents; + id _delegate; + // The app that is frontmost in this list owns media keys + NSMutableArray *_mediaKeyAppList; +} ++ (NSArray*)defaultMediaKeyUserBundleIdentifiers; + +-(id)initWithDelegate:(id)delegate; + ++(BOOL)usesGlobalMediaKeyTap; +-(void)startWatchingMediaKeys; +-(void)stopWatchingMediaKeys; +-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event; +@end + +@interface NSObject (SPMediaKeyTapDelegate) +-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event; +@end + +extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey; \ No newline at end of file diff --git a/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m new file mode 100644 index 000000000..a349f5922 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m @@ -0,0 +1,300 @@ +// Copyright (c) 2010 Spotify AB +#import "SPMediaKeyTap.h" +#import "SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h" // https://gist.github.com/511181, in submodule + +@interface SPMediaKeyTap () +-(BOOL)shouldInterceptMediaKeyEvents; +-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting; +-(void)startWatchingAppSwitching; +-(void)stopWatchingAppSwitching; +-(void)eventTapThread; +@end +static SPMediaKeyTap *singleton = nil; + +static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData); +static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData); +static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon); + + +// Inspired by http://gist.github.com/546311 + +@implementation SPMediaKeyTap + +#pragma mark - +#pragma mark Setup and teardown +-(id)initWithDelegate:(id)delegate; +{ + _delegate = delegate; + [self startWatchingAppSwitching]; + singleton = self; + _mediaKeyAppList = [NSMutableArray new]; + return self; +} +-(void)dealloc; +{ + [self stopWatchingMediaKeys]; + [self stopWatchingAppSwitching]; + [_mediaKeyAppList release]; + [super dealloc]; +} + +-(void)startWatchingAppSwitching; +{ + // Listen to "app switched" event, so that we don't intercept media keys if we + // weren't the last "media key listening" app to be active + EventTypeSpec eventType = { kEventClassApplication, kEventAppFrontSwitched }; + OSStatus err = InstallApplicationEventHandler(NewEventHandlerUPP(appSwitched), 1, &eventType, self, &_app_switching_ref); + assert(err == noErr); + + eventType.eventKind = kEventAppTerminated; + err = InstallApplicationEventHandler(NewEventHandlerUPP(appTerminated), 1, &eventType, self, &_app_terminating_ref); + assert(err == noErr); +} +-(void)stopWatchingAppSwitching; +{ + if(!_app_switching_ref) return; + RemoveEventHandler(_app_switching_ref); + _app_switching_ref = NULL; +} + +-(void)startWatchingMediaKeys;{ + [self setShouldInterceptMediaKeyEvents:YES]; + + // Add an event tap to intercept the system defined media key events + _eventPort = CGEventTapCreate(kCGSessionEventTap, + kCGHeadInsertEventTap, + kCGEventTapOptionDefault, + CGEventMaskBit(NX_SYSDEFINED), + tapEventCallback, + self); + assert(_eventPort != NULL); + + _eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0); + assert(_eventPortSource != NULL); + + // Let's do this in a separate thread so that a slow app doesn't lag the event tap + [NSThread detachNewThreadSelector:@selector(eventTapThread) toTarget:self withObject:nil]; +} +-(void)stopWatchingMediaKeys; +{ + // TODO: Shut down thread, remove event tap port and source +} + +#pragma mark - +#pragma mark Accessors + ++(BOOL)usesGlobalMediaKeyTap +{ +#ifdef _DEBUG + // breaking in gdb with a key tap inserted sometimes locks up all mouse and keyboard input forever, forcing reboot + return NO; +#else + // XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy. + return floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/; +#endif +} + ++ (NSArray*)defaultMediaKeyUserBundleIdentifiers; +{ + return [NSArray arrayWithObjects: + [[NSBundle mainBundle] bundleIdentifier], // your app + @"com.spotify.client", + @"com.apple.iTunes", + @"com.apple.QuickTimePlayerX", + @"com.apple.quicktimeplayer", + @"com.apple.iWork.Keynote", + @"com.apple.iPhoto", + @"org.videolan.vlc", + @"com.apple.Aperture", + @"com.plexsquared.Plex", + @"com.soundcloud.desktop", + @"com.macromedia.fireworks", // the tap messes up their mouse input + nil + ]; +} + + +-(BOOL)shouldInterceptMediaKeyEvents; +{ + BOOL shouldIntercept = NO; + @synchronized(self) { + shouldIntercept = _shouldInterceptMediaKeyEvents; + } + return shouldIntercept; +} + +-(void)pauseTapOnTapThread:(BOOL)yeahno; +{ + CGEventTapEnable(self->_eventPort, yeahno); +} +-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting; +{ + BOOL oldSetting; + @synchronized(self) { + oldSetting = _shouldInterceptMediaKeyEvents; + _shouldInterceptMediaKeyEvents = newSetting; + } + if(_tapThreadRL && oldSetting != newSetting) { + id grab = [self grab]; + [grab pauseTapOnTapThread:newSetting]; + NSTimer *timer = [NSTimer timerWithTimeInterval:0 invocation:[grab invocation] repeats:NO]; + CFRunLoopAddTimer(_tapThreadRL, (CFRunLoopTimerRef)timer, kCFRunLoopCommonModes); + } +} + +#pragma mark +#pragma mark - +#pragma mark Event tap callbacks + +// Note: method called on background thread + +static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) +{ + SPMediaKeyTap *self = refcon; + + if(type == kCGEventTapDisabledByTimeout) { + NSLog(@"Media key event tap was disabled by timeout"); + CGEventTapEnable(self->_eventPort, TRUE); + return event; + } else if(type == kCGEventTapDisabledByUserInput) { + // Was disabled manually by -[pauseTapOnTapThread] + return event; + } + NSEvent *nsEvent = nil; + @try { + nsEvent = [NSEvent eventWithCGEvent:event]; + } + @catch (NSException * e) { + NSLog(@"Strange CGEventType: %d: %@", type, e); + assert(0); + return event; + } + + if (type != NX_SYSDEFINED || [nsEvent subtype] != SPSystemDefinedEventMediaKeys) + return event; + + int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16); + if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_FAST && keyCode != NX_KEYTYPE_REWIND) + return event; + + if (![self shouldInterceptMediaKeyEvents]) + return event; + + [nsEvent retain]; // matched in handleAndReleaseMediaKeyEvent: + [self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO]; + + return NULL; +} + +static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) +{ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + CGEventRef ret = tapEventCallback2(proxy, type, event, refcon); + [pool drain]; + return ret; +} + + +// event will have been retained in the other thread +-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event { + [event autorelease]; + + [_delegate mediaKeyTap:self receivedMediaKeyEvent:event]; +} + + +-(void)eventTapThread; +{ + _tapThreadRL = CFRunLoopGetCurrent(); + CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes); + CFRunLoopRun(); +} + +#pragma mark Task switching callbacks + +NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey = @"SPApplicationsNeedingMediaKeys"; + + +-(void)mediaKeyAppListChanged; +{ + if([_mediaKeyAppList count] == 0) return; + + /*NSLog(@"--"); + int i = 0; + for (NSValue *psnv in _mediaKeyAppList) { + ProcessSerialNumber psn; [psnv getValue:&psn]; + NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary( + &psn, + kProcessDictionaryIncludeAllInformationMask + ) autorelease]; + NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey]; + NSLog(@"%d: %@", i++, bundleIdentifier); + }*/ + + ProcessSerialNumber mySerial, topSerial; + GetCurrentProcess(&mySerial); + [[_mediaKeyAppList objectAtIndex:0] getValue:&topSerial]; + + Boolean same; + OSErr err = SameProcess(&mySerial, &topSerial, &same); + [self setShouldInterceptMediaKeyEvents:(err == noErr && same)]; + +} +-(void)appIsNowFrontmost:(ProcessSerialNumber)psn; +{ + NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)]; + + NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary( + &psn, + kProcessDictionaryIncludeAllInformationMask + ) autorelease]; + NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey]; + + NSArray *whitelistIdentifiers = [[NSUserDefaults standardUserDefaults] arrayForKey:kMediaKeyUsingBundleIdentifiersDefaultsKey]; + if(![whitelistIdentifiers containsObject:bundleIdentifier]) return; + + [_mediaKeyAppList removeObject:psnv]; + [_mediaKeyAppList insertObject:psnv atIndex:0]; + [self mediaKeyAppListChanged]; +} +-(void)appTerminated:(ProcessSerialNumber)psn; +{ + NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)]; + [_mediaKeyAppList removeObject:psnv]; + [self mediaKeyAppListChanged]; +} + +static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData) +{ + SPMediaKeyTap *self = (id)userData; + + ProcessSerialNumber newSerial; + GetFrontProcess(&newSerial); + + [self appIsNowFrontmost:newSerial]; + + return CallNextEventHandler(nextHandler, evt); +} + +static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData) +{ + SPMediaKeyTap *self = (id)userData; + + ProcessSerialNumber deadPSN; + + GetEventParameter( + evt, + kEventParamProcessID, + typeProcessSerialNumber, + NULL, + sizeof(deadPSN), + NULL, + &deadPSN + ); + + + [self appTerminated:deadPSN]; + return CallNextEventHandler(nextHandler, evt); +} + +@end diff --git a/thirdparty/SPMediaKeyTap/SPMediaKeyTapDelegate.m b/thirdparty/SPMediaKeyTap/SPMediaKeyTapDelegate.m new file mode 100644 index 000000000..2e7a01541 --- /dev/null +++ b/thirdparty/SPMediaKeyTap/SPMediaKeyTapDelegate.m @@ -0,0 +1,25 @@ +-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event; +{ + assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys); + + int keyCode = (([event data1] & 0xFFFF0000) >> 16); + int keyFlags = ([event data1] & 0x0000FFFF); + int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA; + int keyRepeat = (keyFlags & 0x1); + + if (keyState == 1 && windowController != NULL) { + + + switch (keyCode) { + + case NX_KEYTYPE_PLAY: +... return; + + case NX_KEYTYPE_FAST: +... return; + + case NX_KEYTYPE_REWIND: +... return; + } + } +} From 615b2ff5cb8e3c6e9ee987a48548bba48563e9cd Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 31 May 2011 18:21:41 -0400 Subject: [PATCH 30/36] And remove cruft. --- .../SPInvocationGrabbing/.svn/all-wcprops | 35 ---- .../SPInvocationGrabbing/.svn/entries | 198 ------------------ .../NSObject+SPInvocationGrabbing.h.svn-base | 30 --- .../NSObject+SPInvocationGrabbing.m.svn-base | 128 ----------- .../.svn/text-base/gistfile3.m.svn-base | 28 --- .../.svn/text-base/gistfile4.m.svn-base | 12 -- .../.svn/text-base/main.m.svn-base | 38 ---- 7 files changed, 469 deletions(-) delete mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/all-wcprops delete mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/entries delete mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.h.svn-base delete mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.m.svn-base delete mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile3.m.svn-base delete mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile4.m.svn-base delete mode 100644 thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/main.m.svn-base diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/all-wcprops b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/all-wcprops deleted file mode 100644 index 657122656..000000000 --- a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/all-wcprops +++ /dev/null @@ -1,35 +0,0 @@ -K 25 -svn:wc:ra_dav:version-url -V 68 -/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing -END -main.m -K 25 -svn:wc:ra_dav:version-url -V 75 -/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing/main.m -END -NSObject+SPInvocationGrabbing.h -K 25 -svn:wc:ra_dav:version-url -V 100 -/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h -END -gistfile3.m -K 25 -svn:wc:ra_dav:version-url -V 80 -/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile3.m -END -gistfile4.m -K 25 -svn:wc:ra_dav:version-url -V 80 -/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile4.m -END -NSObject+SPInvocationGrabbing.m -K 25 -svn:wc:ra_dav:version-url -V 100 -/svn/!svn/ver/3341/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m -END diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/entries b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/entries deleted file mode 100644 index 83ab274af..000000000 --- a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/entries +++ /dev/null @@ -1,198 +0,0 @@ -10 - -dir -3354 -http://clementine-player.googlecode.com/svn/trunk/3rdparty/SPMediaKeyTap/SPInvocationGrabbing -http://clementine-player.googlecode.com/svn - - - -2011-05-30T10:09:34.688096Z -3341 -john.maguire - - - - - - - - - - - - - - -94c5599e-fc6c-11de-b061-8119ef04aefe - -main.m -file - - - - -2011-05-30T19:32:29.000000Z -64cedb4901f14b8f82160d41834812c7 -2011-05-30T10:09:34.688096Z -3341 -john.maguire - - - - - - - - - - - - - - - - - - - - - -778 - -NSObject+SPInvocationGrabbing.h -file - - - - -2011-05-30T19:32:29.000000Z -21417129fd15adb3e8e88f53c96a5249 -2011-05-30T10:09:34.688096Z -3341 -john.maguire - - - - - - - - - - - - - - - - - - - - - -841 - -gistfile3.m -file - - - - -2011-05-30T19:32:29.000000Z -1794e4fc655ea831def78ad9b3b22f5d -2011-05-30T10:09:34.688096Z -3341 -john.maguire - - - - - - - - - - - - - - - - - - - - - -1080 - -gistfile4.m -file - - - - -2011-05-30T19:32:29.000000Z -4a243c33e49a1d8b7212def259a1873a -2011-05-30T10:09:34.688096Z -3341 -john.maguire - - - - - - - - - - - - - - - - - - - - - -385 - -NSObject+SPInvocationGrabbing.m -file - - - - -2011-05-30T19:32:29.000000Z -52da2f8cede4bf66b97df4df3a002086 -2011-05-30T10:09:34.688096Z -3341 -john.maguire - - - - - - - - - - - - - - - - - - - - - -2915 - diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.h.svn-base b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.h.svn-base deleted file mode 100644 index d30233daf..000000000 --- a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.h.svn-base +++ /dev/null @@ -1,30 +0,0 @@ -#import - -@interface SPInvocationGrabber : NSObject { - id _object; - NSInvocation *_invocation; - int frameCount; - char **frameStrings; - BOOL backgroundAfterForward; - BOOL onMainAfterForward; - BOOL waitUntilDone; -} --(id)initWithObject:(id)obj; --(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; -@property (readonly, retain, nonatomic) id object; -@property (readonly, retain, nonatomic) NSInvocation *invocation; -@property BOOL backgroundAfterForward; -@property BOOL onMainAfterForward; -@property BOOL waitUntilDone; --(void)invoke; // will release object and invocation --(void)printBacktrace; --(void)saveBacktrace; -@end - -@interface NSObject (SPInvocationGrabbing) --(id)grab; --(id)invokeAfter:(NSTimeInterval)delta; --(id)nextRunloop; --(id)inBackground; --(id)onMainAsync:(BOOL)async; -@end diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.m.svn-base b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.m.svn-base deleted file mode 100644 index 8ba4adb8f..000000000 --- a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/NSObject+SPInvocationGrabbing.m.svn-base +++ /dev/null @@ -1,128 +0,0 @@ -#import "NSObject+SPInvocationGrabbing.h" -#import - -#pragma mark Invocation grabbing -@interface SPInvocationGrabber () -@property (readwrite, retain, nonatomic) id object; -@property (readwrite, retain, nonatomic) NSInvocation *invocation; - -@end - -@implementation SPInvocationGrabber -- (id)initWithObject:(id)obj; -{ - return [self initWithObject:obj stacktraceSaving:YES]; -} - --(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; -{ - self.object = obj; - - if(saveStack) - [self saveBacktrace]; - - return self; -} --(void)dealloc; -{ - free(frameStrings); - self.object = nil; - self.invocation = nil; - [super dealloc]; -} -@synthesize invocation = _invocation, object = _object; - -@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone; -- (void)runInBackground; -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - @try { - [self invoke]; - } - @finally { - [pool drain]; - } -} - - -- (void)forwardInvocation:(NSInvocation *)anInvocation { - [anInvocation retainArguments]; - anInvocation.target = _object; - self.invocation = anInvocation; - - if(backgroundAfterForward) - [NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil]; - else if(onMainAfterForward) - [self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone]; -} -- (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector { - NSMethodSignature *signature = [super methodSignatureForSelector:inSelector]; - if (signature == NULL) - signature = [_object methodSignatureForSelector:inSelector]; - - return signature; -} - -- (void)invoke; -{ - - @try { - [_invocation invoke]; - } - @catch (NSException * e) { - NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e); - [self printBacktrace]; - printf("\n"); - [e raise]; - } - - self.invocation = nil; - self.object = nil; -} - --(void)saveBacktrace; -{ - void *backtraceFrames[128]; - frameCount = backtrace(&backtraceFrames[0], 128); - frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount); -} --(void)printBacktrace; -{ - int x; - for(x = 3; x < frameCount; x++) { - if(frameStrings[x] == NULL) { break; } - printf("%s\n", frameStrings[x]); - } -} -@end - -@implementation NSObject (SPInvocationGrabbing) --(id)grab; -{ - return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease]; -} --(id)invokeAfter:(NSTimeInterval)delta; -{ - id grabber = [self grab]; - [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO]; - return grabber; -} -- (id)nextRunloop; -{ - return [self invokeAfter:0]; -} --(id)inBackground; -{ - SPInvocationGrabber *grabber = [self grab]; - grabber.backgroundAfterForward = YES; - return grabber; -} --(id)onMainAsync:(BOOL)async; -{ - SPInvocationGrabber *grabber = [self grab]; - grabber.onMainAfterForward = YES; - grabber.waitUntilDone = !async; - return grabber; -} - -@end diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile3.m.svn-base b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile3.m.svn-base deleted file mode 100644 index 712293780..000000000 --- a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile3.m.svn-base +++ /dev/null @@ -1,28 +0,0 @@ -// A -+(UIView*)flashAt:(CGRect)r in:(UIView*)parent color:(UIColor*)color; -{ - float duration = 0.5; - UIView *flash = [[[UIView alloc] initWithFrame:r] autorelease]; - flash.backgroundColor = color; - [parent addSubview:flash]; - [[flash invokeAfter:duration+0.1] removeFromSuperview]; - - [UIView beginAnimations:@"SPFlash" context:NULL]; - [UIView setAnimationDuration:duration]; - flash.alpha = 0.0; - [UIView commitAnimations]; - return flash; -} - -// B -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - - // Force the animation to happen by calling this method again after a small - // delay - see http://blog.instapaper.com/post/53568356 - [[self nextRunloop] delayedTableViewDidSelectRowAtIndexPath: indexPath]; -} - -// C -[[tableView invokeAfter:0.15] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; -[[tableView invokeAfter:0.30] deselectRowAtIndexPath:indexPath animated:YES]; -[[tableView invokeAfter:0.45] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile4.m.svn-base b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile4.m.svn-base deleted file mode 100644 index 8255abab0..000000000 --- a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/gistfile4.m.svn-base +++ /dev/null @@ -1,12 +0,0 @@ -@interface MyClass : NSObject --(BOOL)areTheNewViewersGoneYet:(Duck*)duck; -@end -... -MyClass *myInstance = [[MyClass alloc] init]; -id invocationGrabber = [[[SPInvocationGrabber alloc] initWithTarget:myInstance] autorelease]; - - -[invocationGrabber areTheNewViewersGoneYet:[Duck yellowDuck]]; // line 9 - - -NSInvocation *invocationForAreTheNewViewersGoneYet = [invocationGrabber invocation]; diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/main.m.svn-base b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/main.m.svn-base deleted file mode 100644 index a6ce17f39..000000000 --- a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/.svn/text-base/main.m.svn-base +++ /dev/null @@ -1,38 +0,0 @@ -#import -#import "NSObject+SPInvocationGrabbing.h" - -@interface Foo : NSObject { - int a; -} --(void)startIt; --(void)theBackgroundStuff; --(void)theForegroundStuff; -@end - -@implementation Foo --(void)startIt; -{ - NSLog(@"Starting out on the main thread..."); - a = 3; - [[self inBackground] theBackgroundStuff]; -} --(void)theBackgroundStuff; -{ - NSLog(@"Woah, this is a background thread!"); - a += 6; - [[self onMainAsync:YES] theForegroundStuff]; -} --(void)theForegroundStuff; -{ - NSLog(@"Hey presto: %d", a); -} -@end - -int main() { - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - Foo *foo = [Foo new]; - [foo startIt]; - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; - [pool release]; - return 0; -} \ No newline at end of file From b35679822931ba2aa7d3185a60fd2dc724dae651 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 31 May 2011 22:09:53 -0400 Subject: [PATCH 31/36] Add an 'Add to my ' option for non-local playlists to copy locally --- src/libtomahawk/globalactionmanager.cpp | 20 ++++++------ src/libtomahawk/globalactionmanager.h | 4 +-- src/sourcetree/sourcetreeview.cpp | 43 ++++++++++++++++++++++++- src/sourcetree/sourcetreeview.h | 2 ++ 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/libtomahawk/globalactionmanager.cpp b/src/libtomahawk/globalactionmanager.cpp index 664550688..1dd335571 100644 --- a/src/libtomahawk/globalactionmanager.cpp +++ b/src/libtomahawk/globalactionmanager.cpp @@ -85,14 +85,14 @@ GlobalActionManager::openLinkFromQuery( const Tomahawk::query_ptr& query ) const return link; } -void +QString GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist ) { QUrl link( QString( "tomahawk://%1/create/" ).arg( playlist->mode() == Tomahawk::OnDemand ? "station" : "autoplaylist" ) ); if( playlist->generator()->type() != "echonest" ) { qDebug() << "Only echonest generators are supported"; - return; + return QString(); } link.addEncodedQueryItem( "type", "echonest" ); @@ -123,6 +123,8 @@ GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& p QClipboard* cb = QApplication::clipboard(); cb->setText( link.toEncoded() ); + + return link.toString(); } void @@ -380,22 +382,22 @@ GlobalActionManager::handleSearchCommand( const QUrl& url ) bool GlobalActionManager::handleAutoPlaylistCommand( const QUrl& url ) { - return loadDynamicPlaylist( url, false ); + return !loadDynamicPlaylist( url, false ).isNull(); } -bool +Tomahawk::dynplaylist_ptr GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station ) { QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command if( parts.isEmpty() ) { qDebug() << "No specific station command:" << url.toString(); - return false; + return Tomahawk::dynplaylist_ptr(); } if( parts[ 0 ] == "create" ) { if( !url.hasQueryItem( "title" ) || !url.hasQueryItem( "type" ) ) { qDebug() << "Station create command needs title and type..." << url.toString(); - return false; + return Tomahawk::dynplaylist_ptr(); } QString title = url.queryItemValue( "title" ); QString type = url.queryItemValue( "type" ); @@ -520,17 +522,17 @@ GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station ) else pl->createNewRevision( uuid(), pl->currentrevision(), type, controls, pl->entries() ); - return true; + return pl; } - return false; + return Tomahawk::dynplaylist_ptr(); } bool GlobalActionManager::handleStationCommand( const QUrl& url ) { - return loadDynamicPlaylist( url, true ); + return !loadDynamicPlaylist( url, true ).isNull(); } bool diff --git a/src/libtomahawk/globalactionmanager.h b/src/libtomahawk/globalactionmanager.h index 061d1609a..c7b9d71dc 100644 --- a/src/libtomahawk/globalactionmanager.h +++ b/src/libtomahawk/globalactionmanager.h @@ -38,13 +38,14 @@ public: QUrl openLinkFromQuery( const Tomahawk::query_ptr& query ) const; void copyToClipboard( const Tomahawk::query_ptr& query ) const; - void copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist ); + QString copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist ); void savePlaylistToFile( const Tomahawk::playlist_ptr& playlist, const QString& filename ); public slots: bool parseTomahawkLink( const QString& link ); void waitingForResolved( bool ); + Tomahawk::dynplaylist_ptr loadDynamicPlaylist( const QUrl& url, bool station ); private slots: void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl ); void showPlaylist(); @@ -65,7 +66,6 @@ private: bool handleBookmarkCommand(const QUrl& url ); bool handleOpenCommand(const QUrl& url ); - bool loadDynamicPlaylist( const QUrl& url, bool station ); bool doQueueAdd( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems ); Tomahawk::playlist_ptr m_toShow; diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index fe94ff5e9..8b21bb176 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -143,10 +143,20 @@ SourceTreeView::setupMenus() m_copyPlaylistAction = m_playlistMenu.addAction( tr( "&Copy Link" ) ); m_deletePlaylistAction = m_playlistMenu.addAction( tr( "&Delete %1" ).arg( SourcesModel::rowTypeToString( type ) ) ); - m_roPlaylistMenu.addAction( m_copyPlaylistAction ); + QString addToText = QString( "Add to my %1" ); + if ( type == SourcesModel::StaticPlaylist ) + addToText = addToText.arg( "Playlists" ); + if ( type == SourcesModel::AutomaticPlaylist ) + addToText = addToText.arg( "Automatic Playlists" ); + else if ( type == SourcesModel::Station ) + addToText = addToText.arg( "Stations" ); + m_addToLocalAction = m_roPlaylistMenu.addAction( tr( addToText.toUtf8(), "Adds the given playlist, dynamic playlist, or station to the users's own list" ) ); + + m_roPlaylistMenu.addAction( m_copyPlaylistAction ); m_deletePlaylistAction->setEnabled( !readonly ); m_renamePlaylistAction->setEnabled( !readonly ); + m_addToLocalAction->setEnabled( readonly ); if ( type == SourcesModel::StaticPlaylist ) m_copyPlaylistAction->setText( tr( "&Export Playlist" ) ); @@ -155,6 +165,7 @@ SourceTreeView::setupMenus() connect( m_renamePlaylistAction, SIGNAL( triggered() ), SLOT( renamePlaylist() ) ); connect( m_deletePlaylistAction, SIGNAL( triggered() ), SLOT( deletePlaylist() ) ); connect( m_copyPlaylistAction, SIGNAL( triggered() ), SLOT( copyPlaylistLink() ) ); + connect( m_addToLocalAction, SIGNAL( triggered() ), SLOT( addToLocal() ) ); } @@ -259,6 +270,36 @@ SourceTreeView::copyPlaylistLink() } } +void SourceTreeView::addToLocal() +{ + QModelIndex idx = m_contextMenuIndex; + if ( !idx.isValid() ) + return; + + SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); + if( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) + { + DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( m_contextMenuIndex ); + dynplaylist_ptr playlist = item->dynPlaylist(); + + // copy to a link and then generate a new playlist from that + // this way we cheaply regenerate the needed controls + QString link = GlobalActionManager::instance()->copyPlaylistToClipboard( playlist ); + dynplaylist_ptr p = GlobalActionManager::instance()->loadDynamicPlaylist( link, type == SourcesModel::Station ); + } else if ( type == SourcesModel::StaticPlaylist ) + { + PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex ); + playlist_ptr playlist = item->playlist(); + + // just create the new playlist with the same values + QList< query_ptr > queries; + foreach( const plentry_ptr& e, playlist->entries() ) + queries << e->query(); + + playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), playlist->title(), playlist->info(), playlist->creator(), playlist->shared(), queries ); + } +} + void SourceTreeView::renamePlaylist() diff --git a/src/sourcetree/sourcetreeview.h b/src/sourcetree/sourcetreeview.h index d3d6eb97e..7964122f0 100644 --- a/src/sourcetree/sourcetreeview.h +++ b/src/sourcetree/sourcetreeview.h @@ -54,6 +54,7 @@ private slots: void loadPlaylist(); void deletePlaylist( const QModelIndex& = QModelIndex() ); void copyPlaylistLink(); + void addToLocal(); void onCustomContextMenu( const QPoint& pos ); protected: @@ -84,6 +85,7 @@ private: QAction* m_renamePlaylistAction; QAction* m_deletePlaylistAction; QAction* m_copyPlaylistAction; + QAction* m_addToLocalAction; bool m_dragging; QRect m_dropRect; From c52e7e4cdbf11e8852bf1c537fe75f85d7885d51 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 1 Jun 2011 04:21:01 +0200 Subject: [PATCH 32/36] * Allow seeking in songs - if possible. --- src/CMakeLists.txt | 2 + src/audiocontrols.cpp | 37 ++-- src/libtomahawk/audio/audioengine.cpp | 37 +++- src/libtomahawk/audio/audioengine.h | 2 + src/libtomahawk/pipeline.cpp | 1 + src/libtomahawk/pipeline.h | 4 + src/libtomahawk/playlist/playlistview.cpp | 1 + src/libtomahawk/playlist/queueview.cpp | 16 +- src/libtomahawk/playlist/queueview.h | 6 +- src/libtomahawk/utils/animatedsplitter.cpp | 240 ++++++++------------- src/libtomahawk/utils/animatedsplitter.h | 34 ++- src/tomahawkwindow.cpp | 8 +- src/transferview.cpp | 8 +- src/transferview.h | 4 +- 14 files changed, 183 insertions(+), 217 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index efedc8706..9c0c1223b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,6 +60,7 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui} sourcetree/items/genericpageitems.cpp transferview.cpp + PipelineStatusView.cpp tomahawktrayicon.cpp audiocontrols.cpp settingsdialog.cpp @@ -102,6 +103,7 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui} sourcetree/items/genericpageitems.h transferview.h + PipelineStatusView.h tomahawktrayicon.h audiocontrols.h settingsdialog.h diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp index 7be1bc26e..01536818c 100644 --- a/src/audiocontrols.cpp +++ b/src/audiocontrols.cpp @@ -80,27 +80,23 @@ 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->setFixedHeight( 20 ); - ui->seekSlider->setEnabled( false ); + 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::handle::horizontal {" - "margin-left: 5px; margin-right: -5px; " - "width: 0px;" - - //"margin-bottom: -7px; margin-top: -7px;" - //"height: 17px; width: 16px;" - //"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);" - //"background-repeat: no-repeat;" - "}" - "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;" + "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 ); @@ -120,9 +116,7 @@ AudioControls::AudioControls( QWidget* parent ) "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 ); @@ -134,6 +128,7 @@ AudioControls::AudioControls( QWidget* parent ) 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 ) ) ); connect( ui->prevButton, SIGNAL( clicked() ), AudioEngine::instance(), SLOT( previous() ) ); connect( ui->playPauseButton, SIGNAL( clicked() ), AudioEngine::instance(), SLOT( play() ) ); @@ -283,13 +278,11 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result ) ui->ownerLabel->setText( result->friendlySource() ); ui->coverImage->setPixmap( m_defaultCover ); - if ( ui->timeLabel->text().isEmpty() ) - ui->timeLabel->setText( TomahawkUtils::timeToString( 0 ) ); - - if ( ui->timeLeftLabel->text().isEmpty() ) - ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( result->duration() ) ); + ui->timeLabel->setText( TomahawkUtils::timeToString( 0 ) ); + ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( result->duration() ) ); ui->seekSlider->setRange( 0, m_currentTrack->duration() * 1000 ); + ui->seekSlider->setValue( 0 ); ui->seekSlider->setVisible( true ); /* m_playAction->setEnabled( false ); @@ -357,10 +350,14 @@ AudioControls::onPlaybackTimer( qint64 msElapsed ) if ( m_currentTrack.isNull() ) return; + ui->seekSlider->blockSignals( true ); + const int seconds = msElapsed / 1000; ui->timeLabel->setText( TomahawkUtils::timeToString( seconds ) ); ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( m_currentTrack->duration() - seconds ) ); ui->seekSlider->setValue( msElapsed ); + + ui->seekSlider->blockSignals( false ); } diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index 51f1aea1a..70d6f9afe 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -136,6 +136,17 @@ AudioEngine::next() } +void +AudioEngine::seek( int ms ) +{ + if ( isPlaying() || isPaused() ) + { + qDebug() << Q_FUNC_INFO << ms; + m_mediaObject->seek( ms ); + } +} + + void AudioEngine::setVolume( int percentage ) { @@ -176,7 +187,7 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) { setCurrentTrack( result ); - if ( !isHttpResult( m_currentTrack->url() ) ) + if ( !isHttpResult( m_currentTrack->url() ) && !isLocalResult( m_currentTrack->url() ) ) { io = Servent::instance()->getIODeviceForUrl( m_currentTrack ); @@ -204,7 +215,7 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) m_expectStop = true; } - if ( !isHttpResult( m_currentTrack->url() ) ) + if ( !isHttpResult( m_currentTrack->url() ) && !isLocalResult( m_currentTrack->url() ) ) { m_mediaObject->setCurrentSource( io.data() ); m_mediaObject->currentSource().setAutoDelete( false ); @@ -217,7 +228,6 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) { furl = QUrl( m_currentTrack->url().left( m_currentTrack->url().indexOf( '?' ) ) ); furl.setEncodedQuery( QString( m_currentTrack->url().mid( m_currentTrack->url().indexOf( '?' ) + 1 ) ).toLocal8Bit() ); - qDebug() << Q_FUNC_INFO << furl; } m_mediaObject->setCurrentSource( furl ); m_mediaObject->currentSource().setAutoDelete( true ); @@ -306,11 +316,22 @@ AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState ) { qDebug() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType(); } - if ( oldState == Phonon::PlayingState && newState == Phonon::StoppedState ) { + qDebug() << "Expecting stop?" << m_expectStop; if ( !m_expectStop ) { + qDebug() << "Loading next track."; + m_expectStop = false; + loadNextTrack(); + } + } + else if ( oldState == Phonon::PlayingState && newState == Phonon::PausedState ) + { + qDebug() << m_mediaObject->currentTime() << m_mediaObject->totalTime(); + if ( m_mediaObject->currentTime() == m_mediaObject->totalTime() ) + { + qDebug() << "Loading next track."; m_expectStop = false; loadNextTrack(); } @@ -365,8 +386,16 @@ AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result ) m_currentTrack = result; } + bool AudioEngine::isHttpResult( const QString& url ) const { return url.startsWith( "http://" ); } + + +bool +AudioEngine::isLocalResult( const QString& url ) const +{ + return url.startsWith( "file://" ); +} diff --git a/src/libtomahawk/audio/audioengine.h b/src/libtomahawk/audio/audioengine.h index 580f9e044..d7fc8c954 100644 --- a/src/libtomahawk/audio/audioengine.h +++ b/src/libtomahawk/audio/audioengine.h @@ -64,6 +64,7 @@ public slots: void previous(); void next(); + void seek( int ms ); void setVolume( int percentage ); void lowerVolume() { setVolume( volume() - AUDIO_VOLUME_STEP ); } void raiseVolume() { setVolume( volume() + AUDIO_VOLUME_STEP ); } @@ -106,6 +107,7 @@ private slots: private: bool isHttpResult( const QString& ) const; + bool isLocalResult( const QString& ) const; bool m_isPlayingHttp; QSharedPointer m_input; diff --git a/src/libtomahawk/pipeline.cpp b/src/libtomahawk/pipeline.cpp index 3bae02c19..70b7edcdf 100644 --- a/src/libtomahawk/pipeline.cpp +++ b/src/libtomahawk/pipeline.cpp @@ -311,6 +311,7 @@ Pipeline::shunt( const query_ptr& q ) qDebug() << "Dispatching to resolver" << r->name() << q->toString() << q->solved() << q->id(); r->resolve( q ); + emit resolving( q ); } else break; diff --git a/src/libtomahawk/pipeline.h b/src/libtomahawk/pipeline.h index d07608747..7c41e03ab 100644 --- a/src/libtomahawk/pipeline.h +++ b/src/libtomahawk/pipeline.h @@ -46,6 +46,9 @@ public: explicit Pipeline( QObject* parent = 0 ); virtual ~Pipeline(); + unsigned int pendingQueryCount() const { return m_queries_pending.count(); } + unsigned int activeQueryCount() const { return m_qidsState.count(); } + void reportResults( QID qid, const QList< result_ptr >& results ); /// sorter to rank resolver priority @@ -75,6 +78,7 @@ public slots: signals: void idle(); + void resolving( const Tomahawk::query_ptr& query ); private slots: void timeoutShunt( const query_ptr& q ); diff --git a/src/libtomahawk/playlist/playlistview.cpp b/src/libtomahawk/playlist/playlistview.cpp index 5000bd80c..8d59cb6d4 100644 --- a/src/libtomahawk/playlist/playlistview.cpp +++ b/src/libtomahawk/playlist/playlistview.cpp @@ -99,6 +99,7 @@ PlaylistView::setupMenus() foreach( QAction* a, actions() ) m_itemMenu.addAction( a ); + // m_addItemsToPlaylistAction = m_itemMenu.addAction( tr( "&Add to Playlist" ) ); // m_itemMenu.addSeparator(); m_deleteItemsAction = m_itemMenu.addAction( i > 1 ? tr( "&Delete Items" ) : tr( "&Delete Item" ) ); diff --git a/src/libtomahawk/playlist/queueview.cpp b/src/libtomahawk/playlist/queueview.cpp index 1d060db73..d361b7342 100644 --- a/src/libtomahawk/playlist/queueview.cpp +++ b/src/libtomahawk/playlist/queueview.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -45,7 +45,7 @@ QueueView::QueueView( AnimatedSplitter* parent ) m_queue->setFrameShape( QFrame::NoFrame ); m_queue->setAttribute( Qt::WA_MacShowFocusRect, 0 ); m_queue->overlay()->setEnabled( false ); - + m_button = new QPushButton(); m_button->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ); m_button->setText( tr( "Click to show queue" ) ); @@ -64,13 +64,13 @@ QueueView::~QueueView() void -QueueView::onShown( QWidget* widget ) +QueueView::onShown( QWidget* widget, bool animated ) { qDebug() << Q_FUNC_INFO << widget; if ( widget != this ) return; - AnimatedWidget::onShown( widget ); + AnimatedWidget::onShown( widget, animated ); m_button->setText( tr( "Click to hide queue" ) ); disconnect( m_button, SIGNAL( clicked() ), this, SIGNAL( showWidget() ) ); @@ -79,14 +79,14 @@ QueueView::onShown( QWidget* widget ) void -QueueView::onHidden( QWidget* widget ) +QueueView::onHidden( QWidget* widget, bool animated ) { qDebug() << Q_FUNC_INFO << widget; if ( widget != this ) return; - - AnimatedWidget::onHidden( widget ); - + + AnimatedWidget::onHidden( widget, animated ); + m_button->setText( tr( "Click to show queue" ) ); disconnect( m_button, SIGNAL( clicked() ), this, SIGNAL( hideWidget() ) ); connect( m_button, SIGNAL( clicked() ), SIGNAL( showWidget() ) ); diff --git a/src/libtomahawk/playlist/queueview.h b/src/libtomahawk/playlist/queueview.h index f617831f9..a8549fffa 100644 --- a/src/libtomahawk/playlist/queueview.h +++ b/src/libtomahawk/playlist/queueview.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -39,8 +39,8 @@ public: QSize sizeHint() const { return QSize( 0, 200 ); } public slots: - virtual void onShown( QWidget* ); - virtual void onHidden( QWidget* ); + virtual void onShown( QWidget*, bool animated ); + virtual void onHidden( QWidget*, bool animated ); private: PlaylistView* m_queue; diff --git a/src/libtomahawk/utils/animatedsplitter.cpp b/src/libtomahawk/utils/animatedsplitter.cpp index 3ec748052..fc5d4898d 100644 --- a/src/libtomahawk/utils/animatedsplitter.cpp +++ b/src/libtomahawk/utils/animatedsplitter.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -23,83 +23,25 @@ AnimatedSplitter::AnimatedSplitter( QWidget* parent ) : QSplitter( parent ) - , m_animateIndex( -1 ) , m_greedyIndex( 0 ) { setHandleWidth( 1 ); - - m_timeLine = new QTimeLine( ANIMATION_TIME, this ); - m_timeLine->setUpdateInterval( 5 ); - m_timeLine->setEasingCurve( QEasingCurve::OutBack ); - - connect( m_timeLine, SIGNAL( frameChanged( int ) ), SLOT( onAnimationStep( int ) ) ); - connect( m_timeLine, SIGNAL( finished() ), SLOT( onAnimationFinished() ) ); } void AnimatedSplitter::show( int index, bool animate ) { - m_animateIndex = index; - QWidget* w = widget( index ); - QSize size = w->sizeHint(); - - if ( w->height() == size.height() ) - return; - - emit shown( w ); - w->setMaximumHeight( QWIDGETSIZE_MAX ); - qDebug() << "animating to:" << size.height() << "from" << w->height(); - - m_animateForward = true; - if ( animate ) - { - if ( m_timeLine->state() == QTimeLine::Running ) - m_timeLine->stop(); - - m_timeLine->setFrameRange( w->height(), size.height() ); - m_timeLine->setDirection( QTimeLine::Forward ); - m_timeLine->start(); - } - else - { - onAnimationStep( size.height() ); - onAnimationFinished(); - } + emit shown( w, animate ); } void AnimatedSplitter::hide( int index, bool animate ) { - m_animateIndex = index; - QWidget* w = widget( index ); - int minHeight = m_sizes.at( index ).height(); - - if ( w->height() == minHeight ) - return; - - emit hidden( w ); - w->setMinimumHeight( minHeight ); -// qDebug() << "animating to:" << w->height() << "from" << minHeight; - - m_animateForward = false; - if ( animate ) - { - if ( m_timeLine->state() == QTimeLine::Running ) - m_timeLine->stop(); - - m_timeLine->setFrameRange( minHeight, w->height() ); - m_timeLine->setDirection( QTimeLine::Backward ); - m_timeLine->start(); - } - else - { - onAnimationStep( minHeight ); - onAnimationFinished(); - } + emit hidden( w, animate ); } @@ -107,7 +49,6 @@ void AnimatedSplitter::addWidget( QWidget* widget ) { QSplitter::addWidget( widget ); - m_sizes << widget->minimumSize(); } @@ -116,13 +57,11 @@ AnimatedSplitter::addWidget( AnimatedWidget* widget ) { qDebug() << Q_FUNC_INFO << widget; QSplitter::addWidget( widget ); - m_sizes << widget->hiddenSize(); connect( widget, SIGNAL( showWidget() ), SLOT( onShowRequest() ) ); connect( widget, SIGNAL( hideWidget() ), SLOT( onHideRequest() ) ); - connect( widget, SIGNAL( hiddenSizeChanged() ), SLOT( onHiddenSizeChanged() ) ); - connect( this, SIGNAL( shown( QWidget* ) ), widget, SLOT( onShown( QWidget* ) ) ); - connect( this, SIGNAL( hidden( QWidget* ) ), widget, SLOT( onHidden( QWidget* ) ) ); + connect( this, SIGNAL( shown( QWidget*, bool ) ), widget, SLOT( onShown( QWidget*, bool ) ) ); + connect( this, SIGNAL( hidden( QWidget*, bool ) ), widget, SLOT( onHidden( QWidget*, bool ) ) ); } @@ -131,18 +70,9 @@ AnimatedSplitter::onShowRequest() { qDebug() << Q_FUNC_INFO << sender(); - int j = -1; - for ( int i = 0; i < count(); i ++ ) - { - if ( widget( i ) == sender() ) - { - j = i; - break; - } - } - - if ( j > 0 ) - show( j ); + AnimatedWidget* w = (AnimatedWidget*)(sender()); + if ( indexOf( w ) > 0 ) + show( indexOf( w ) ); else qDebug() << "Could not find widget:" << sender(); } @@ -151,72 +81,16 @@ AnimatedSplitter::onShowRequest() void AnimatedSplitter::onHideRequest() { - int j = -1; - for ( int i = 0; i < count(); i ++ ) - { - if ( widget( i ) == sender() ) - { - j = i; - break; - } - } - - if ( j > 0 ) - hide( j ); + AnimatedWidget* w = (AnimatedWidget*)(sender()); + if ( indexOf( w ) > 0 ) + hide( indexOf( w ) ); else qDebug() << "Could not find widget:" << sender(); } void -AnimatedSplitter::onAnimationStep( int frame ) -{ - QList< int > sizes; - - for ( int i = 0; i < count(); i ++ ) - { - int j = 0; - - if ( i == m_greedyIndex ) - { - j = height() - frame; // FIXME - } - else if ( i == m_animateIndex ) - { - j = frame; - } - else - { - j = widget( i )->height(); - } - - sizes << j; - } - - setSizes( sizes ); -} - - -void -AnimatedSplitter::onAnimationFinished() -{ - qDebug() << Q_FUNC_INFO; - - QWidget* w = widget( m_animateIndex ); - if ( m_animateForward ) - { - w->setMinimumHeight( w->minimumHeight() ); - } - else - { - w->setMaximumHeight( m_sizes.at( m_animateIndex ).height() ); - } - - m_animateIndex = -1; -} - -void -AnimatedSplitter::setGreedyWidget(int index) +AnimatedSplitter::setGreedyWidget( int index ) { m_greedyIndex = index; if( !widget( index ) ) @@ -227,17 +101,7 @@ AnimatedSplitter::setGreedyWidget(int index) else policy.setVerticalStretch( 1 ); widget( m_greedyIndex )->setSizePolicy( policy ); - -} - -void -AnimatedSplitter::onHiddenSizeChanged() -{ - AnimatedWidget* w = (AnimatedWidget*)(sender()); - int i = indexOf( w ); - - m_sizes.replace( i, w->hiddenSize() ); } @@ -246,24 +110,98 @@ AnimatedWidget::AnimatedWidget( AnimatedSplitter* parent ) , m_isHidden( false ) { qDebug() << Q_FUNC_INFO; + + m_timeLine = new QTimeLine( ANIMATION_TIME, this ); + m_timeLine->setUpdateInterval( 5 ); + m_timeLine->setEasingCurve( QEasingCurve::OutBack ); + + connect( m_timeLine, SIGNAL( frameChanged( int ) ), SLOT( onAnimationStep( int ) ) ); + connect( m_timeLine, SIGNAL( finished() ), SLOT( onAnimationFinished() ) ); } + AnimatedWidget::~AnimatedWidget() { - } + void -AnimatedWidget::onShown( QWidget* ) +AnimatedWidget::onShown( QWidget* widget, bool animated ) { + if ( widget != this ) + return; + qDebug() << Q_FUNC_INFO << this; + + m_animateForward = true; + if ( animated ) + { + if ( m_timeLine->state() == QTimeLine::Running ) + m_timeLine->stop(); + + m_timeLine->setFrameRange( height(), sizeHint().height() ); + m_timeLine->setDirection( QTimeLine::Forward ); + m_timeLine->start(); + } + else + { + onAnimationStep( sizeHint().height() ); + onAnimationFinished(); + } + m_isHidden = false; } void -AnimatedWidget::onHidden( QWidget* ) +AnimatedWidget::onHidden( QWidget* widget, bool animated ) { + if ( widget != this ) + return; + qDebug() << Q_FUNC_INFO << this; + + m_animateForward = false; + int minHeight = hiddenSize().height(); + + if ( animated ) + { + if ( m_timeLine->state() == QTimeLine::Running ) + m_timeLine->stop(); + + m_timeLine->setFrameRange( minHeight, height() ); + m_timeLine->setDirection( QTimeLine::Backward ); + m_timeLine->start(); + } + else + { + onAnimationStep( minHeight ); + onAnimationFinished(); + } + m_isHidden = true; } + + +void +AnimatedWidget::onAnimationStep( int frame ) +{ + setFixedHeight( frame ); +} + + +void +AnimatedWidget::onAnimationFinished() +{ + qDebug() << Q_FUNC_INFO; + + if ( m_animateForward ) + { + setMinimumHeight( hiddenSize().height() ); + setMaximumHeight( QWIDGETSIZE_MAX ); + } + else + { + setFixedHeight( hiddenSize().height() ); + } +} diff --git a/src/libtomahawk/utils/animatedsplitter.h b/src/libtomahawk/utils/animatedsplitter.h index ba1671f4e..20cb283eb 100644 --- a/src/libtomahawk/utils/animatedsplitter.h +++ b/src/libtomahawk/utils/animatedsplitter.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -43,56 +43,52 @@ public: void addWidget( AnimatedWidget* widget ); signals: - void shown( QWidget* ); - void hidden( QWidget* ); + void shown( QWidget*, bool animated ); + void hidden( QWidget*, bool animated ); private slots: void onShowRequest(); void onHideRequest(); - void onAnimationStep( int frame ); - void onAnimationFinished(); - - void onHiddenSizeChanged(); - private: - int m_animateIndex; - bool m_animateForward; - int m_greedyIndex; - QList m_sizes; - QTimeLine* m_timeLine; }; class DLLEXPORT AnimatedWidget : public QWidget { Q_OBJECT public: - explicit AnimatedWidget( AnimatedSplitter* parent = 0 ); + explicit AnimatedWidget( AnimatedSplitter* parent ); virtual ~AnimatedWidget(); - + QSize hiddenSize() const { return m_hiddenSize; } void setHiddenSize( const QSize& size ) { m_hiddenSize = size; emit hiddenSizeChanged(); } bool isHidden() const { return m_isHidden; } public slots: - virtual void onShown( QWidget* ); - virtual void onHidden( QWidget* ); + virtual void onShown( QWidget*, bool animated ); + virtual void onHidden( QWidget*, bool animated ); signals: void showWidget(); void hideWidget(); void hiddenSizeChanged(); + +private slots: + void onAnimationStep( int frame ); + void onAnimationFinished(); + protected: - AnimatedSplitter* splitter() { return m_parent; } - + private: AnimatedSplitter* m_parent; + bool m_animateForward; QSize m_hiddenSize; bool m_isHidden; + QTimeLine* m_timeLine; }; #endif //ANIMATEDSPLITTER_H diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 13989b4d4..c83332f70 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -57,6 +57,7 @@ #include "diagnosticsdialog.h" #include "tomahawksettings.h" #include "sourcelist.h" +#include "PipelineStatusView.h" #include "transferview.h" #include "tomahawktrayicon.h" #include "playlist/dynamic/GeneratorInterface.h" @@ -107,18 +108,19 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) sidebar->setOrientation( Qt::Vertical ); sidebar->setChildrenCollapsible( false ); sidebar->setGreedyWidget( 0 ); - sidebar->setStretchFactor( 0, 3 ); - sidebar->setStretchFactor( 1, 1 ); m_sourcetree = new SourceTreeView(); - TransferView* transferView = new TransferView(); + TransferView* transferView = new TransferView( sidebar ); + PipelineStatusView* pipelineView = new PipelineStatusView( sidebar ); connect( ui->actionHideOfflineSources, SIGNAL( triggered() ), m_sourcetree, SLOT( hideOfflineSources() ) ); connect( ui->actionShowOfflineSources, SIGNAL( triggered() ), m_sourcetree, SLOT( showOfflineSources() ) ); sidebar->addWidget( m_sourcetree ); sidebar->addWidget( transferView ); + sidebar->addWidget( pipelineView ); sidebar->hide( 1, false ); + sidebar->hide( 2, false ); /* QWidget* buttonWidget = new QWidget(); buttonWidget->setLayout( new QVBoxLayout() ); diff --git a/src/transferview.cpp b/src/transferview.cpp index af8ba3ed2..4ae9948e3 100644 --- a/src/transferview.cpp +++ b/src/transferview.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -80,12 +80,6 @@ TransferView::streamFinished( StreamConnection* sc ) emit showWidget(); else emit hideWidget(); - -/* if ( m_index.contains( sc ) ) - { - int i = m_index.value( sc ); - m_tree->invisibleRootItem()->child( i )->setText( 1, tr( "Finished" ) ); - }*/ } diff --git a/src/transferview.h b/src/transferview.h index 560088bbf..d517c57bf 100644 --- a/src/transferview.h +++ b/src/transferview.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -32,7 +32,7 @@ class TransferView : public AnimatedWidget Q_OBJECT public: - explicit TransferView( AnimatedSplitter* parent = 0 ); + explicit TransferView( AnimatedSplitter* parent ); virtual ~TransferView() { qDebug() << Q_FUNC_INFO; From aeedf286eb64e0f281ae7951d32ac62f5e9de7d5 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 1 Jun 2011 04:21:35 +0200 Subject: [PATCH 33/36] * Added Pipeline status view. WIP. --- src/PipelineStatusView.cpp | 104 +++++++++++++++++++++++++++++++++++++ src/PipelineStatusView.h | 51 ++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 src/PipelineStatusView.cpp create mode 100644 src/PipelineStatusView.h diff --git a/src/PipelineStatusView.cpp b/src/PipelineStatusView.cpp new file mode 100644 index 000000000..de0efe281 --- /dev/null +++ b/src/PipelineStatusView.cpp @@ -0,0 +1,104 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * 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 . + */ + +#include "PipelineStatusView.h" + +#include +#include + +#include "libtomahawk/pipeline.h" + +using namespace Tomahawk; + + +PipelineStatusView::PipelineStatusView( AnimatedSplitter* parent ) + : AnimatedWidget( parent ) + , m_parent( parent ) +{ + setHiddenSize( QSize( 0, 0 ) ); + setLayout( new QVBoxLayout() ); + m_tree = new QTreeWidget( this ); + + layout()->setMargin( 0 ); + layout()->addWidget( m_tree ); + + QStringList headers; + headers << tr( "Searching For" ) << tr( "Pending" ); + m_tree->setHeaderLabels( headers ); + + m_tree->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored ); + m_tree->setColumnCount( 2 ); + m_tree->setColumnWidth( 0, 200 ); + m_tree->setColumnWidth( 1, 50 ); + + m_tree->header()->setStretchLastSection( true ); + m_tree->setRootIsDecorated( false ); + + m_tree->setFrameShape( QFrame::NoFrame ); + m_tree->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + new QTreeWidgetItem( m_tree ); + + connect( Pipeline::instance(), SIGNAL( resolving( Tomahawk::query_ptr ) ), SLOT( onPipelineUpdate( Tomahawk::query_ptr ) ) ); + connect( Pipeline::instance(), SIGNAL( idle() ), SLOT( onPipelineUpdate() ) ); + + onPipelineUpdate(); +} + + +void +PipelineStatusView::onPipelineUpdate( const query_ptr& query ) +{ + qDebug() << Q_FUNC_INFO; + + QTreeWidgetItem* ti = m_tree->invisibleRootItem()->child( 0 ); + + if ( Pipeline::instance()->activeQueryCount() && !query.isNull() ) + { + ti->setText( 0, QString( "%1 - %2" ).arg( query->artist() ).arg( query->track() ) ); + ti->setText( 1, QString( "%1" ).arg( Pipeline::instance()->activeQueryCount() + Pipeline::instance()->pendingQueryCount() ) ); + + if ( isHidden() ) + emit showWidget(); + } + else + { + ti->setText( 0, tr( "Idle" ) ); + ti->setText( 1, QString( "None" ) ); + + if ( !isHidden() ) + emit hideWidget(); + } +} + + +QSize +PipelineStatusView::sizeHint() const +{ + unsigned int y = 0; + y += m_tree->header()->height(); + y += m_tree->contentsMargins().top() + m_tree->contentsMargins().bottom(); + + if ( m_tree->invisibleRootItem()->childCount() ) + { + unsigned int rowheight = m_tree->sizeHintForRow( 0 ); + y += rowheight * m_tree->invisibleRootItem()->childCount() + 2; + } + + return QSize( 0, y ); +} diff --git a/src/PipelineStatusView.h b/src/PipelineStatusView.h new file mode 100644 index 000000000..df3a72542 --- /dev/null +++ b/src/PipelineStatusView.h @@ -0,0 +1,51 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * 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 . + */ + +#ifndef PIPELINESTATUSVIEW_H +#define PIPELINESTATUSVIEW_H + +#include +#include + +#include "typedefs.h" +#include "utils/animatedsplitter.h" + +class StreamConnection; + +class PipelineStatusView : public AnimatedWidget +{ +Q_OBJECT + +public: + explicit PipelineStatusView( AnimatedSplitter* parent ); + virtual ~PipelineStatusView() + { + qDebug() << Q_FUNC_INFO; + } + + QSize sizeHint() const; + +private slots: + void onPipelineUpdate( const Tomahawk::query_ptr& query = Tomahawk::query_ptr() ); + +private: + QTreeWidget* m_tree; + AnimatedSplitter* m_parent; +}; + +#endif // TRANSFERVIEW_H From b4e0674e2bf0ebc10c74ddf250653e670b044956 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 1 Jun 2011 17:41:44 -0400 Subject: [PATCH 34/36] SHow initial config dialog as sheet on osx, WIP --- src/settingsdialog.cpp | 61 +++++++++++++++++++++++++++++++++++------- src/settingsdialog.h | 4 ++- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index fc67ff18a..1a3ea88a6 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -382,7 +382,7 @@ SettingsDialog::onLastFmFinished() ui->pushButtonTestLastfmLogin->setText( tr( "Failed" ) ); ui->pushButtonTestLastfmLogin->setEnabled( true ); break; - + default: qDebug() << "Couldn't get last.fm auth result"; ui->pushButtonTestLastfmLogin->setText( tr( "Could not contact server" ) ); @@ -528,6 +528,15 @@ SettingsDialog::sipFactoryClicked( SipPluginFactory* factory ) SipPlugin* p = factory->createPlugin(); bool added = false; if( p->configWidget() ) { + +#ifdef Q_OS_MAC + // on osx a sheet needs to be non-modal + DelegateConfigWrapper* dialog = new DelegateConfigWrapper( p->configWidget(), QString("%1 Config" ).arg( p->friendlyName() ), this, Qt::Sheet ); + dialog->setProperty( "sipplugin", QVariant::fromValue< QObject* >( p ) ); + connect( dialog, SIGNAL( finished( int ) ), this, SLOT( sipCreateConfigClosed( int ) ) ); + + dialog->show(); +#else DelegateConfigWrapper dialog( p->configWidget(), QString("%1 Config" ).arg( p->friendlyName() ), this ); QWeakPointer< DelegateConfigWrapper > watcher( &dialog ); int ret = dialog.exec(); @@ -544,13 +553,44 @@ SettingsDialog::sipFactoryClicked( SipPluginFactory* factory ) // canceled, delete it added = false; } + + handleSipPluginAdded( p, added ); +#endif } else { // no config, so just add it added = true; TomahawkSettings::instance()->addSipPlugin( p->pluginId() ); SipHandler::instance()->addSipPlugin( p ); + + handleSipPluginAdded( p, added ); } +} + +void +SettingsDialog::sipCreateConfigClosed( int finished ) +{ + DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() ); + SipPlugin* p = qobject_cast< SipPlugin* >( dialog->property( "sipplugin" ).value< QObject* >() ); + Q_ASSERT( p ); + + bool added = false; + if( finished == QDialog::Accepted ) { + + p->saveConfig(); + TomahawkSettings::instance()->addSipPlugin( p->pluginId() ); + SipHandler::instance()->addSipPlugin( p ); + + added = true; + } + + handleSipPluginAdded( p, added ); +} + + +void +SettingsDialog::handleSipPluginAdded( SipPlugin* p, bool added ) +{ SipPluginFactory* f = SipHandler::instance()->factoryFromPlugin( p ); if( added && f && f->isUnique() ) { // remove from actions list @@ -570,6 +610,7 @@ SettingsDialog::sipFactoryClicked( SipPluginFactory* factory ) } } + void SettingsDialog::sipContextMenuRequest( const QPoint& p ) { @@ -625,9 +666,9 @@ ProxyDialog::ProxyDialog( QWidget *parent ) , ui( new Ui::ProxyDialog ) { ui->setupUi( this ); - + // ugly, I know, but... - + int i = 0; ui->typeBox->insertItem( i, "No Proxy", QNetworkProxy::NoProxy ); m_forwardMap[ QNetworkProxy::NoProxy ] = i; @@ -637,9 +678,9 @@ ProxyDialog::ProxyDialog( QWidget *parent ) m_forwardMap[ QNetworkProxy::Socks5Proxy ] = i; m_backwardMap[ i ] = QNetworkProxy::Socks5Proxy; i++; - + TomahawkSettings* s = TomahawkSettings::instance(); - + ui->typeBox->setCurrentIndex( m_forwardMap[s->proxyType()] ); ui->hostLineEdit->setText( s->proxyHost() ); ui->portSpinBox->setValue( s->proxyPort() ); @@ -681,18 +722,18 @@ ProxyDialog::proxyTypeChangedSlot( int index ) ui->passwordLineEdit->setEnabled( true ); ui->checkBoxUseProxyForDns->setEnabled( true ); ui->noHostLineEdit->setEnabled( true ); - } + } } void ProxyDialog::saveSettings() { qDebug() << Q_FUNC_INFO; - + //First set settings TomahawkSettings* s = TomahawkSettings::instance(); s->setProxyHost( ui->hostLineEdit->text() ); - + int port = ui->portSpinBox->value(); s->setProxyPort( port ); s->setProxyNoProxyHosts( ui->noHostLineEdit->text() ); @@ -700,10 +741,10 @@ ProxyDialog::saveSettings() s->setProxyPassword( ui->passwordLineEdit->text() ); s->setProxyType( ui->typeBox->itemData( ui->typeBox->currentIndex() ).toInt() ); s->setProxyDns( ui->checkBoxUseProxyForDns->checkState() == Qt::Checked ); - + if( s->proxyHost().isEmpty() ) return; - + TomahawkUtils::NetworkProxyFactory* proxyFactory = new TomahawkUtils::NetworkProxyFactory(); QNetworkProxy proxy( static_cast(s->proxyType()), s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() ); proxyFactory->setProxy( proxy ); diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 78e1f55e3..5cebb30a1 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -48,7 +48,7 @@ public: private slots: void proxyTypeChangedSlot( int index ); - + private: Ui::ProxyDialog* ui; QHash m_forwardMap; @@ -95,12 +95,14 @@ private slots: // dialog slots void resolverConfigClosed( int value ); void sipConfigClosed( int value ); + void sipCreateConfigClosed( int value ); void changePage( QListWidgetItem*, QListWidgetItem* ); private: void createIcons(); void setupSipButtons(); + void handleSipPluginAdded( SipPlugin* p, bool added ); Ui_StackedSettingsDialog* ui; From e130fc41ed66a2bfef578d568890a44586c98fc5 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 1 Jun 2011 20:29:54 -0400 Subject: [PATCH 35/36] resize dialog when it changes size, and hide/show otherwise osx goes CRAZY PANTS --- src/delegateconfigwrapper.h | 11 +++++++++++ src/sip/twitter/twitterconfigwidget.cpp | 2 ++ src/sip/twitter/twitterconfigwidget.h | 1 + 3 files changed, 14 insertions(+) diff --git a/src/delegateconfigwrapper.h b/src/delegateconfigwrapper.h index 463261deb..6dc40700a 100644 --- a/src/delegateconfigwrapper.h +++ b/src/delegateconfigwrapper.h @@ -47,6 +47,8 @@ public: setSizeGripEnabled( false ); setMinimumSize( sizeHint() ); setMaximumSize( sizeHint() ); // to remove the resize grip on osx this is the only way + + connect( conf, SIGNAL( sizeHintChanged() ), this, SLOT( updateSizeHint() ) ); #endif } public slots: @@ -72,6 +74,15 @@ public slots: m_widget->setVisible( false ); } + void updateSizeHint() { + hide(); + setSizeGripEnabled( false ); + setMinimumSize( sizeHint() ); + setMaximumSize( sizeHint() ); + + show(); + } + private: QWidget* m_widget; }; diff --git a/src/sip/twitter/twitterconfigwidget.cpp b/src/sip/twitter/twitterconfigwidget.cpp index 115147cd9..ddf5e89df 100644 --- a/src/sip/twitter/twitterconfigwidget.cpp +++ b/src/sip/twitter/twitterconfigwidget.cpp @@ -132,6 +132,7 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) m_plugin->connectPlugin( false ); emit twitterAuthed( true ); + emit sizeHintChanged(); } void @@ -161,6 +162,7 @@ TwitterConfigWidget::deauthenticateTwitter() ui->twitterTweetComboBox->setVisible( false ); emit twitterAuthed( false ); + emit sizeHintChanged(); } void diff --git a/src/sip/twitter/twitterconfigwidget.h b/src/sip/twitter/twitterconfigwidget.h index 5de30c7a7..d06c08737 100644 --- a/src/sip/twitter/twitterconfigwidget.h +++ b/src/sip/twitter/twitterconfigwidget.h @@ -45,6 +45,7 @@ public: signals: void twitterAuthed( bool authed ); + void sizeHintChanged(); private slots: void authDeauthTwitter(); void startPostGotTomahawkStatus(); From 59452f6163f2e98b22ac10a68aa068b7ed6514b3 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 2 Jun 2011 17:24:44 -0400 Subject: [PATCH 36/36] Try to avoid showing the window before placing it --- src/delegateconfigwrapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/delegateconfigwrapper.h b/src/delegateconfigwrapper.h index 6dc40700a..b7986ba88 100644 --- a/src/delegateconfigwrapper.h +++ b/src/delegateconfigwrapper.h @@ -28,7 +28,6 @@ class DelegateConfigWrapper : public QDialog public: DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent, Qt::WindowFlags flags = 0 ) : QDialog( parent, flags ), m_widget( conf ) { - m_widget->setVisible( true ); m_widget->setWindowFlags( Qt::Sheet ); setWindowTitle( title ); @@ -50,6 +49,7 @@ public: connect( conf, SIGNAL( sizeHintChanged() ), this, SLOT( updateSizeHint() ) ); #endif + m_widget->setVisible( true ); } public slots: void closed( QAbstractButton* b )