diff --git a/ChangeLog b/ChangeLog index fa612aac7..3db2b7612 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ +Version 0.4.0: + Version 0.3.3: * Automatically load Super Collection tracks when no official release information is available. diff --git a/data/images/process-stop.png b/data/images/process-stop.png new file mode 100644 index 000000000..738d6f9b8 Binary files /dev/null and b/data/images/process-stop.png differ diff --git a/resources.qrc b/resources.qrc index 134cccd5d..4bc424c60 100644 --- a/resources.qrc +++ b/resources.qrc @@ -135,5 +135,6 @@ data/images/rdio.png data/images/lastfm-icon.png data/sql/dbmigrate-27_to_28.sql + data/images/process-stop.png diff --git a/src/CMakeLists.osx.cmake b/src/CMakeLists.osx.cmake index 9239931eb..f7a502f47 100644 --- a/src/CMakeLists.osx.cmake +++ b/src/CMakeLists.osx.cmake @@ -58,4 +58,7 @@ if (APPLE) FILE(COPY ${CMAKE_SOURCE_DIR}/admin/mac/sparkle_pub.pem DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/Resources") + FILE(COPY /usr/bin/SetFile DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/MacOS") + FILE(COPY /usr/bin/GetFileInfo DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/MacOS") + endif (APPLE) diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 3ddce4215..6affaf09b 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -38,6 +38,7 @@ set( libGuiSources jobview/PipelineStatusItem.cpp jobview/TransferStatusItem.cpp jobview/LatchedStatusItem.cpp + jobview/ErrorStatusMessage.cpp infobar/infobar.cpp @@ -272,6 +273,7 @@ set( libGuiHeaders jobview/PipelineStatusItem.h jobview/TransferStatusItem.h jobview/LatchedStatusItem.h + jobview/ErrorStatusMessage.h thirdparty/Qocoa/qsearchfield.h ) diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index 5d4be8afc..f19f662f0 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -590,10 +590,10 @@ AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState ) if ( newState == Phonon::ErrorState ) { + stop(); + tLog() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType(); emit error( UnknownError ); - - stop(); return; } if ( newState == Phonon::PlayingState ) diff --git a/src/libtomahawk/dropjob.cpp b/src/libtomahawk/dropjob.cpp index 6c0ed2f9f..f8a4adcef 100644 --- a/src/libtomahawk/dropjob.cpp +++ b/src/libtomahawk/dropjob.cpp @@ -35,6 +35,8 @@ #include "utils/xspfloader.h" #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" +#include "jobview/ErrorStatusMessage.h" + #ifdef QCA2_FOUND #include "utils/groovesharkparser.h" #endif //QCA2_FOUND @@ -699,10 +701,18 @@ DropJob::removeDuplicates() { bool contains = false; foreach( const Tomahawk::query_ptr &tmpItem, list ) + { if ( item->album() == tmpItem->album() && item->artist() == tmpItem->artist() && item->track() == tmpItem->track() ) + { + if ( item->playable() && !tmpItem->playable() ) + list.replace( list.indexOf( tmpItem ), item ); + contains = true; + break; + } + } if ( !contains ) list.append( item ); } diff --git a/src/libtomahawk/dropjob.h b/src/libtomahawk/dropjob.h index 71ca32787..08077808b 100644 --- a/src/libtomahawk/dropjob.h +++ b/src/libtomahawk/dropjob.h @@ -23,6 +23,8 @@ #include "query.h" #include "infosystem/infosystem.h" +#include "utils/xspfloader.h" + #include #include #include diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp index b2d5b7a32..f55456a65 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp @@ -451,8 +451,15 @@ LastFmPlugin::notInCacheSlot( QHash criteria, Tomahawk::InfoSy QString artistName = criteria["artist"]; QString albumName = criteria["album"]; - QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b"; - QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); + QUrl imgurl( "http://ws.audioscrobbler.com/2.0/" ); + imgurl.addQueryItem( "method", "album.imageredirect" ); + imgurl.addQueryItem( "artist", artistName ); + imgurl.addQueryItem( "album", albumName ); + imgurl.addQueryItem( "autocorrect", QString::number( 1 ) ); + imgurl.addQueryItem( "size", "large" ); + imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" ); + + QNetworkRequest req( imgurl ); QNetworkReply* reply = TomahawkUtils::nam()->get( req ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); @@ -464,8 +471,14 @@ LastFmPlugin::notInCacheSlot( QHash criteria, Tomahawk::InfoSy { QString artistName = criteria["artist"]; - QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b"; - QNetworkRequest req( imgurl.arg( artistName ) ); + QUrl imgurl( "http://ws.audioscrobbler.com/2.0/" ); + imgurl.addQueryItem( "method", "artist.imageredirect" ); + imgurl.addQueryItem( "artist", artistName ); + imgurl.addQueryItem( "autocorrect", QString::number( 1 ) ); + imgurl.addQueryItem( "size", "large" ); + imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" ); + + QNetworkRequest req( imgurl ); QNetworkReply* reply = TomahawkUtils::nam()->get( req ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); diff --git a/src/libtomahawk/jobview/ErrorStatusMessage.cpp b/src/libtomahawk/jobview/ErrorStatusMessage.cpp new file mode 100644 index 000000000..38f1082a1 --- /dev/null +++ b/src/libtomahawk/jobview/ErrorStatusMessage.cpp @@ -0,0 +1,56 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi + * + * 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 "ErrorStatusMessage.h" + +#include "utils/tomahawkutils.h" + +#include + +QPixmap* ErrorStatusMessage::s_pixmap = 0; + +ErrorStatusMessage::ErrorStatusMessage( const QString& message, int timeoutSecs ) + : JobStatusItem() + , m_message( message ) +{ + m_timer = new QTimer( this ); + m_timer->setInterval( timeoutSecs * 1000 ); + m_timer->setSingleShot( true ); + + connect( m_timer, SIGNAL( timeout() ), this, SIGNAL( finished() ) ); + + if ( !s_pixmap ) + s_pixmap = new QPixmap( RESPATH "images/process-stop.png" ); + + m_timer->start(); +} + + +QPixmap +ErrorStatusMessage::icon() const +{ + Q_ASSERT( s_pixmap ); + return *s_pixmap; +} + + +QString +ErrorStatusMessage::mainText() const +{ + return m_message; +} diff --git a/src/libtomahawk/jobview/ErrorStatusMessage.h b/src/libtomahawk/jobview/ErrorStatusMessage.h new file mode 100644 index 000000000..8eb23a93f --- /dev/null +++ b/src/libtomahawk/jobview/ErrorStatusMessage.h @@ -0,0 +1,48 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi + * + * 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 ERRORSTATUSMESSAGE_H +#define ERRORSTATUSMESSAGE_H + +#include "JobStatusItem.h" +#include "dllmacro.h" + +class QTimer; +class QPixmap; + +class DLLEXPORT ErrorStatusMessage : public JobStatusItem +{ + Q_OBJECT +public: + explicit ErrorStatusMessage( const QString& errorMessage, int defaultTimeoutSecs = 8 ); + + QString type() const { return "errormessage"; } + QString rightColumnText() const { return QString(); } + + QPixmap icon() const; + QString mainText() const; + + bool allowMultiLine() const { return true; } +private: + QString m_message; + QTimer* m_timer; + + static QPixmap* s_pixmap; +}; + +#endif // ERRORSTATUSMESSAGE_H diff --git a/src/libtomahawk/jobview/JobStatusDelegate.cpp b/src/libtomahawk/jobview/JobStatusDelegate.cpp index c279687ee..9c686c15d 100644 --- a/src/libtomahawk/jobview/JobStatusDelegate.cpp +++ b/src/libtomahawk/jobview/JobStatusDelegate.cpp @@ -23,14 +23,16 @@ #include #include +#include #define ROW_HEIGHT 20 #define ICON_PADDING 1 #define PADDING 2 JobStatusDelegate::JobStatusDelegate( QObject* parent ) : QStyledItemDelegate ( parent ) + , m_parentView( qobject_cast< QListView* >( parent ) ) { - + Q_ASSERT( m_parentView ); } JobStatusDelegate::~JobStatusDelegate() @@ -45,6 +47,7 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, QStyleOptionViewItemV4 opt = option; initStyleOption( &opt, index ); QFontMetrics fm( opt.font ); + const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool(); opt.state &= ~QStyle::State_MouseOver; QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget ); @@ -52,7 +55,9 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, // painter->drawLine( opt.rect.topLeft(), opt.rect.topRight() ); painter->setRenderHint( QPainter::Antialiasing ); - const QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING ); + QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING ); + if ( allowMultiLine ) + iconRect.moveTop( opt.rect.top() + opt.rect.height() / 2 - iconRect.height() / 2); QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >(); p = p.scaledToHeight( iconRect.height(), Qt::SmoothTransformation ); painter->drawPixmap( iconRect, p ); @@ -71,15 +76,34 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const int mainW = rightEdge - 3*PADDING - iconRect.right(); QString mainText = index.data( Qt::DisplayRole ).toString(); - mainText = fm.elidedText( mainText, Qt::ElideRight, mainW ); - painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), Qt::AlignLeft | Qt::AlignVCenter, mainText ); + QTextOption to( Qt::AlignLeft | Qt::AlignVCenter ); + if ( !allowMultiLine ) + mainText = fm.elidedText( mainText, Qt::ElideRight, mainW ); + else + to.setWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere ); + painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), mainText, to ); } QSize JobStatusDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const { -// return QStyledItemDelegate::sizeHint( option, index ); - const int w = QStyledItemDelegate::sizeHint ( option, index ).width(); - return QSize( w, ROW_HEIGHT ); + const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool(); + + if ( !allowMultiLine ) + return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), ROW_HEIGHT ); + else if ( m_cachedMultiLineHeights.contains( index ) ) + return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), m_cachedMultiLineHeights[ index ] ); + + // Don't elide, but stretch across as many rows as required + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, index ); + + const QString text = index.data( Qt::DisplayRole ).toString(); + const int leftEdge = ICON_PADDING + ROW_HEIGHT + 2*PADDING; + const QRect rect = opt.fontMetrics.boundingRect( leftEdge, opt.rect.top(), m_parentView->width() - leftEdge, 200, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, text ); + + m_cachedMultiLineHeights.insert( index, rect.height() + 4*PADDING ); + + return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), rect.height() + 4*PADDING ); } diff --git a/src/libtomahawk/jobview/JobStatusDelegate.h b/src/libtomahawk/jobview/JobStatusDelegate.h index fc2752350..08a355db4 100644 --- a/src/libtomahawk/jobview/JobStatusDelegate.h +++ b/src/libtomahawk/jobview/JobStatusDelegate.h @@ -22,6 +22,7 @@ #include class QPainter; +class QListView; class JobStatusDelegate : public QStyledItemDelegate { @@ -33,6 +34,10 @@ public: virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + +private: + mutable QHash< QPersistentModelIndex, int > m_cachedMultiLineHeights; + QListView* m_parentView; }; #endif // JOBSTATUSDELEGATE_H diff --git a/src/libtomahawk/jobview/JobStatusItem.h b/src/libtomahawk/jobview/JobStatusItem.h index 77f0dbb6e..8e9a94c58 100644 --- a/src/libtomahawk/jobview/JobStatusItem.h +++ b/src/libtomahawk/jobview/JobStatusItem.h @@ -53,6 +53,7 @@ public: * and a count will be shown instead. */ virtual bool collapseItem() const { return false; } + virtual bool allowMultiLine() const { return false; } signals: /// Ask for an update diff --git a/src/libtomahawk/jobview/JobStatusModel.cpp b/src/libtomahawk/jobview/JobStatusModel.cpp index a50c8a4e3..fa73af0a1 100644 --- a/src/libtomahawk/jobview/JobStatusModel.cpp +++ b/src/libtomahawk/jobview/JobStatusModel.cpp @@ -100,6 +100,8 @@ JobStatusModel::data( const QModelIndex& index, int role ) const else return item->rightColumnText(); } + case AllowMultiLineRole: + return item->allowMultiLine(); } return QVariant(); diff --git a/src/libtomahawk/jobview/JobStatusModel.h b/src/libtomahawk/jobview/JobStatusModel.h index c3611cfd3..539189337 100644 --- a/src/libtomahawk/jobview/JobStatusModel.h +++ b/src/libtomahawk/jobview/JobStatusModel.h @@ -31,7 +31,8 @@ public: enum JobRoles { // DecorationRole is icon // DisplayRole is main col - RightColumnRole = Qt::UserRole + 1 + RightColumnRole = Qt::UserRole + 1, + AllowMultiLineRole = Qt::UserRole + 2 }; explicit JobStatusModel( QObject* parent = 0 ); diff --git a/src/libtomahawk/jobview/JobStatusView.cpp b/src/libtomahawk/jobview/JobStatusView.cpp index fa121a1f0..4b10f09e2 100644 --- a/src/libtomahawk/jobview/JobStatusView.cpp +++ b/src/libtomahawk/jobview/JobStatusView.cpp @@ -40,6 +40,7 @@ JobStatusView* JobStatusView::s_instance = 0; JobStatusView::JobStatusView( AnimatedSplitter* parent ) : AnimatedWidget( parent ) , m_parent( parent ) + , m_cachedHeight( -1 ) { s_instance = this; @@ -56,9 +57,7 @@ JobStatusView::JobStatusView( AnimatedSplitter* parent ) m_view->setFrameShape( QFrame::NoFrame ); m_view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - -// new QTreeWidgetItem( m_tree ); - m_view->setUniformItemSizes( true ); + m_view->setUniformItemSizes( false ); #ifndef Q_WS_WIN QFont f = font(); @@ -86,12 +85,14 @@ JobStatusView::setModel( JobStatusModel* m ) connect( m_view->model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( checkCount() ) ); connect( m_view->model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( checkCount() ) ); + connect( m_view->model(), SIGNAL( modelReset() ), this, SLOT( checkCount() ) ); } void JobStatusView::checkCount() { + m_cachedHeight = -1; if ( m_view->model()->rowCount() == 0 && !isHidden() ) emit hideWidget(); else @@ -102,15 +103,21 @@ JobStatusView::checkCount() QSize JobStatusView::sizeHint() const { + if ( m_cachedHeight >= 0 ) + return QSize( 0, m_cachedHeight ); + unsigned int y = 0; -// y += m_tree->header()->height(); y += m_view->contentsMargins().top() + m_view->contentsMargins().bottom(); if ( m_view->model()->rowCount() ) { - unsigned int rowheight = m_view->sizeHintForRow( 0 ); - y += rowheight * m_view->model()->rowCount() + 2; + for ( int i = 0; i < m_view->model()->rowCount(); i++ ) + { + y += m_view->sizeHintForRow( i ); + } + y += 2; // some padding } + m_cachedHeight = y; return QSize( 0, y ); } diff --git a/src/libtomahawk/jobview/JobStatusView.h b/src/libtomahawk/jobview/JobStatusView.h index 1cbc5a7c2..5d07c16b7 100644 --- a/src/libtomahawk/jobview/JobStatusView.h +++ b/src/libtomahawk/jobview/JobStatusView.h @@ -56,6 +56,7 @@ private: QListView* m_view; JobStatusModel* m_model; AnimatedSplitter* m_parent; + mutable int m_cachedHeight; static JobStatusView* s_instance; }; diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp index cc3a7b28b..43073bf68 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp @@ -181,6 +181,7 @@ Tomahawk::EchonestControl::updateWidgets() input->hide(); m_match = QWeakPointer< QWidget >( match ); m_input = QWeakPointer< QWidget >( input ); + m_data.first = m_currentType; } else if( selectedType() == "Artist Description" ) { m_currentType = Echonest::DynamicPlaylist::Description; @@ -199,6 +200,7 @@ Tomahawk::EchonestControl::updateWidgets() input->hide(); m_match = QWeakPointer< QWidget >( match ); m_input = QWeakPointer< QWidget >( input ); + m_data.first = m_currentType; } else if( selectedType() == "User Radio" ) { m_currentType = Echonest::DynamicPlaylist::SourceCatalog; @@ -246,6 +248,7 @@ Tomahawk::EchonestControl::updateWidgets() input->hide(); m_match = QWeakPointer< QWidget >( match ); m_input = QWeakPointer< QWidget >( input ); + m_data.first = m_currentType; } else if( selectedType() == "Variety" ) { m_currentType = Echonest::DynamicPlaylist::Variety; @@ -266,6 +269,7 @@ Tomahawk::EchonestControl::updateWidgets() input->hide(); m_match = QWeakPointer< QWidget >( match ); m_input = QWeakPointer< QWidget >( input ); + m_data.first = m_currentType; } else if( selectedType() == "Adventurousness" ) { m_currentType = Echonest::DynamicPlaylist::Adventurousness; @@ -287,6 +291,7 @@ Tomahawk::EchonestControl::updateWidgets() input->hide(); m_match = QWeakPointer< QWidget >( match ); m_input = QWeakPointer< QWidget >( input ); + m_data.first = m_currentType; } else if( selectedType() == "Tempo" ) { m_currentType = Echonest::DynamicPlaylist::MinTempo; diff --git a/src/libtomahawk/utils/groovesharkparser.cpp b/src/libtomahawk/utils/groovesharkparser.cpp index dcc28e620..ec088cc08 100644 --- a/src/libtomahawk/utils/groovesharkparser.cpp +++ b/src/libtomahawk/utils/groovesharkparser.cpp @@ -27,6 +27,7 @@ #include "dropjob.h" #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" +#include "jobview/ErrorStatusMessage.h" #include "dropjobnotifier.h" #include "viewmanager.h" @@ -198,6 +199,7 @@ GroovesharkParser::groovesharkLookupFinished() } else { + JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching Grooveshark information from the network!" ) ) ); tLog() << "Error in network request to grooveshark for track decoding:" << r->errorString(); } diff --git a/src/libtomahawk/utils/itunesparser.cpp b/src/libtomahawk/utils/itunesparser.cpp index 1afb1cfaf..f4f97060d 100644 --- a/src/libtomahawk/utils/itunesparser.cpp +++ b/src/libtomahawk/utils/itunesparser.cpp @@ -25,6 +25,7 @@ #include "sourcelist.h" #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" +#include "jobview/ErrorStatusMessage.h" #include @@ -166,6 +167,7 @@ ItunesParser::itunesResponseLookupFinished() } else { + JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching iTunes information from the network!" ) ) ); tLog() << "Error in network request to Itunes for track decoding:" << r->errorString(); } diff --git a/src/libtomahawk/utils/rdioparser.cpp b/src/libtomahawk/utils/rdioparser.cpp index a0621f23c..3d8416177 100644 --- a/src/libtomahawk/utils/rdioparser.cpp +++ b/src/libtomahawk/utils/rdioparser.cpp @@ -26,6 +26,7 @@ #include "dropjob.h" #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" +#include "jobview/ErrorStatusMessage.h" #include "dropjobnotifier.h" #include "viewmanager.h" #include "sourcelist.h" @@ -189,6 +190,7 @@ RdioParser::rdioReturned() } else { + JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching Rdio information from the network!" ) ) ); tLog() << "Error in network request to Rdio for track decoding:" << r->errorString(); } diff --git a/src/libtomahawk/utils/shortenedlinkparser.cpp b/src/libtomahawk/utils/shortenedlinkparser.cpp index 6d89aba69..f11646607 100644 --- a/src/libtomahawk/utils/shortenedlinkparser.cpp +++ b/src/libtomahawk/utils/shortenedlinkparser.cpp @@ -22,6 +22,9 @@ #include "utils/logger.h" #include "utils/tomahawkutils.h" #include "query.h" +#include "jobview/ErrorStatusMessage.h" +#include "jobview/JobStatusModel.h" +#include "jobview/JobStatusView.h" #include @@ -78,6 +81,9 @@ ShortenedLinkParser::lookupFinished() QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() ); Q_ASSERT( r ); + if ( r->error() != QNetworkReply::NoError ) + JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Network error parsing shortened link!" ) ) ); + QVariant redir = r->attribute( QNetworkRequest::RedirectionTargetAttribute ); if ( redir.isValid() && !redir.toUrl().isEmpty() ) { diff --git a/src/libtomahawk/utils/spotifyparser.cpp b/src/libtomahawk/utils/spotifyparser.cpp index cba486f2d..49c86081a 100644 --- a/src/libtomahawk/utils/spotifyparser.cpp +++ b/src/libtomahawk/utils/spotifyparser.cpp @@ -26,6 +26,7 @@ #include "dropjob.h" #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" +#include "jobview/ErrorStatusMessage.h" #include "dropjobnotifier.h" #include "viewmanager.h" @@ -220,6 +221,7 @@ SpotifyParser::spotifyBrowseFinished() } else { + JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching Spotify information from the network!" ) ) ); tLog() << "Error in network request to Spotify for track decoding:" << r->errorString(); } diff --git a/src/libtomahawk/utils/xspfloader.cpp b/src/libtomahawk/utils/xspfloader.cpp index 570213c41..29fdbfe67 100644 --- a/src/libtomahawk/utils/xspfloader.cpp +++ b/src/libtomahawk/utils/xspfloader.cpp @@ -1,7 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Leo Franchi + * Copyright 2011-2012, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,12 @@ #include "utils/tomahawkutils.h" #include "utils/logger.h" +#ifndef ENABLE_HEADLESS +#include "jobview/JobStatusView.h" +#include "jobview/JobStatusModel.h" +#include "jobview/ErrorStatusMessage.h" +#endif + #include "sourcelist.h" #include "playlist.h" #include @@ -33,6 +39,22 @@ using namespace Tomahawk; +QString +XSPFLoader::errorToString( XSPFErrorCode error ) +{ + switch ( error ) + { + case ParseError: + return tr( "Failed to parse contents of XSPF playlist" ); + case InvalidTrackError: + return tr( "Some playlist entries were found without artist and track name, they will be omitted"); + case FetchError: + return tr( "Failed to fetch the desired playlist from the network, or the desired file does not exist" ); + default: + return QString(); + } +} + XSPFLoader::XSPFLoader( bool autoCreate, bool autoUpdate, QObject *parent ) : QObject( parent ) , m_autoCreate( autoCreate ) @@ -98,6 +120,9 @@ void XSPFLoader::reportError() { emit error( FetchError ); +#ifndef ENABLE_HEADLESS + JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorToString( FetchError) ) ); +#endif deleteLater(); } diff --git a/src/libtomahawk/utils/xspfloader.h b/src/libtomahawk/utils/xspfloader.h index 055e76dbf..eee77de96 100644 --- a/src/libtomahawk/utils/xspfloader.h +++ b/src/libtomahawk/utils/xspfloader.h @@ -1,7 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Leo Franchi + * Copyright 2011-2012, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -49,6 +49,8 @@ public: void setOverrideTitle( const QString& newTitle ); void setAutoResolveTracks( bool autoResolve ) { m_autoResolve = autoResolve; } + static QString errorToString( XSPFErrorCode error ); + signals: void error( XSPFLoader::XSPFErrorCode error ); void ok( const Tomahawk::playlist_ptr& ); diff --git a/src/libtomahawk/widgets/checkdirtree.cpp b/src/libtomahawk/widgets/checkdirtree.cpp index ee4bdc86f..b5d1e4a7a 100644 --- a/src/libtomahawk/widgets/checkdirtree.cpp +++ b/src/libtomahawk/widgets/checkdirtree.cpp @@ -22,6 +22,7 @@ #include "utils/logger.h" #include "tomahawksettings.h" +#include #include static QString s_macVolumePath = "/Volumes"; @@ -31,9 +32,12 @@ CheckDirModel::CheckDirModel( QWidget* parent ) , m_shownVolumes( false ) { #ifdef Q_WS_MAC + m_setFilePath = QString( "%1/SetFile" ) .arg( QCoreApplication::applicationDirPath() ); + m_getFileInfoPath = QString( "%1/GetFileInfo" ).arg( QCoreApplication::applicationDirPath() ); + QProcess* checkVolumeVisible = new QProcess( this ); connect( checkVolumeVisible, SIGNAL( readyReadStandardOutput() ), this, SLOT( getFileInfoResult() ) ); - checkVolumeVisible->start( "GetFileInfo", QStringList() << "-aV" << s_macVolumePath ); + checkVolumeVisible->start( m_getFileInfoPath, QStringList() << "-aV" << s_macVolumePath ); #endif } @@ -42,7 +46,7 @@ CheckDirModel::~CheckDirModel() #ifdef Q_WS_MAC // reset to previous state if ( m_shownVolumes ) - QProcess::startDetached( QString( "SetFile -a V %1" ).arg( s_macVolumePath ) ); + QProcess::startDetached( QString( "%1 -a V %2" ).arg( m_setFilePath).arg( s_macVolumePath ) ); #endif } @@ -60,7 +64,7 @@ CheckDirModel::getFileInfoResult() // Remove the hidden flag for the /Volumnes folder so all mount points are visible in the default (Q)FileSystemModel QProcess* p = new QProcess( this ); connect( p, SIGNAL( finished( int, QProcess::ExitStatus ) ), this, SLOT( volumeShowFinished() ) ); - p->start( QString( "SetFile -a v %1" ).arg( s_macVolumePath ) ); + p->start( QString( "%1 -a v %2" ).arg( m_setFilePath ).arg( s_macVolumePath ) ); m_shownVolumes = true; } diff --git a/src/libtomahawk/widgets/checkdirtree.h b/src/libtomahawk/widgets/checkdirtree.h index 3c8119534..f064a5c0d 100644 --- a/src/libtomahawk/widgets/checkdirtree.h +++ b/src/libtomahawk/widgets/checkdirtree.h @@ -51,6 +51,8 @@ private: QHash m_checkTable; bool m_shownVolumes; + QString m_setFilePath; + QString m_getFileInfoPath; }; diff --git a/src/libtomahawk/widgets/searchwidget.cpp b/src/libtomahawk/widgets/searchwidget.cpp index c7a547333..e8efdfbcc 100644 --- a/src/libtomahawk/widgets/searchwidget.cpp +++ b/src/libtomahawk/widgets/searchwidget.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2012 Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -112,6 +113,20 @@ SearchWidget::changeEvent( QEvent* e ) } +Tomahawk::playlistinterface_ptr +SearchWidget::playlistInterface() const +{ + return ui->resultsView->playlistInterface(); +} + + +bool +SearchWidget::jumpToCurrentTrack() +{ + return ui->resultsView->jumpToCurrentTrack(); +} + + void SearchWidget::onResultsFound( const QList& results ) { diff --git a/src/libtomahawk/widgets/searchwidget.h b/src/libtomahawk/widgets/searchwidget.h index b16f3611f..2da9eeb0d 100644 --- a/src/libtomahawk/widgets/searchwidget.h +++ b/src/libtomahawk/widgets/searchwidget.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2012 Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,7 +47,7 @@ public: ~SearchWidget(); virtual QWidget* widget() { return this; } - virtual Tomahawk::playlistinterface_ptr playlistInterface() const { return Tomahawk::playlistinterface_ptr(); } + virtual Tomahawk::playlistinterface_ptr playlistInterface() const; virtual bool isTemporaryPage() const { return true; } virtual QString title() const { return QString( tr( "Search: %1" ) ).arg( m_search ); } @@ -55,7 +56,7 @@ public: virtual bool showStatsBar() const { return false; } - virtual bool jumpToCurrentTrack() { return false; } + virtual bool jumpToCurrentTrack(); protected: void changeEvent( QEvent* e ); diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 7363978ae..c5194ff26 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -319,11 +319,14 @@ TomahawkApp::~TomahawkApp() delete AtticaManager::instance(); #endif - delete Pipeline::instance(); - if ( !m_database.isNull() ) delete m_database.data(); + delete Pipeline::instance(); + + if ( !m_infoSystem.isNull() ) + delete m_infoSystem.data(); + tLog() << "Finished shutdown."; } diff --git a/src/tomahawktrayicon.cpp b/src/tomahawktrayicon.cpp index 6a58a4e6f..b0673be5f 100644 --- a/src/tomahawktrayicon.cpp +++ b/src/tomahawktrayicon.cpp @@ -58,6 +58,8 @@ TomahawkTrayIcon::TomahawkTrayIcon( QObject* parent ) m_showWindowAction = m_contextMenu->addAction( tr( "Hide Tomahawk Window" ) ); m_showWindowAction->setData( true ); connect( m_showWindowAction, SIGNAL( triggered() ), this, SLOT( showWindow() ) ); + + connect( m_contextMenu, SIGNAL( aboutToShow() ), this, SLOT( menuAboutToShow() ) ); #endif m_contextMenu->addSeparator(); @@ -118,6 +120,16 @@ TomahawkTrayIcon::showWindow() } +void +TomahawkTrayIcon::menuAboutToShow() +{ + // When using Cmd-H on mac to hide a window, it is an OS-level hide that is different from QWidget::hide(). + // Qt returns isVisible() == true for windows that are hidden with Cmd-H, which is weird. isActiveWindow() returns + // the proper information though. + setShowHideWindow( APP->mainWindow()->isActiveWindow() ); +} + + void TomahawkTrayIcon::setResult( const Tomahawk::result_ptr& result ) { diff --git a/src/tomahawktrayicon.h b/src/tomahawktrayicon.h index 3e5de54e1..be984ae43 100644 --- a/src/tomahawktrayicon.h +++ b/src/tomahawktrayicon.h @@ -46,6 +46,7 @@ private slots: void enablePlay(); void enablePause(); + void menuAboutToShow(); private: void refreshToolTip(); ~TomahawkTrayIcon(); diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 54b9fefb3..8d8c086b8 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -1,7 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Leo Franchi + * Copyright 2010-2012, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,6 +58,8 @@ #include "tomahawksettings.h" #include "sourcelist.h" #include "jobview/JobStatusView.h" +#include "jobview/JobStatusModel.h" +#include "jobview/ErrorStatusMessage.h" #include "tomahawktrayicon.h" #include "scanmanager.h" #include "tomahawkapp.h" @@ -80,6 +82,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) , m_searchWidget( 0 ) , m_audioControls( new AudioControls( this ) ) , m_trayIcon( new TomahawkTrayIcon( this ) ) + , m_audioRetryCounter( 0 ) { setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) ); @@ -570,11 +573,17 @@ TomahawkWindow::onXSPFError( XSPFLoader::XSPFErrorCode error ) void TomahawkWindow::onAudioEngineError( AudioEngine::AudioErrorCode /* error */ ) { + QString msg; #ifdef Q_WS_X11 - QMessageBox::warning( this, tr( "Playback Error" ), tr( "Sorry, there is a problem accessing your audio device. Make sure you have a suitable Phonon backend and required plugins installed." ), QMessageBox::Ok ); + msg = tr( "Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed." ); #else - QMessageBox::warning( this, tr( "Playback Error" ), tr( "Sorry, there is a problem accessing your audio device." ), QMessageBox::Ok ); + msg = tr( "Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped." ); #endif + JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( msg, 15 ) ); + + if ( m_audioRetryCounter < 3 ) + AudioEngine::instance()->play(); + m_audioRetryCounter++; } @@ -654,6 +663,7 @@ TomahawkWindow::playlistCreateDialogFinished( int ret ) void TomahawkWindow::audioStarted() { + m_audioRetryCounter = 0; ui->actionPlay->setText( tr( "Pause" ) ); } diff --git a/src/tomahawkwindow.h b/src/tomahawkwindow.h index 9c8401832..adc3d98f0 100644 --- a/src/tomahawkwindow.h +++ b/src/tomahawkwindow.h @@ -1,7 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Leo Franchi + * Copyright 2010-2012, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -145,6 +145,7 @@ private: Tomahawk::result_ptr m_currentTrack; QString m_windowTitle; + int m_audioRetryCounter; }; #endif // TOMAHAWKWINDOW_H diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m index 8ba4adb8f..851ea0385 100644 --- a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m +++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m @@ -88,11 +88,11 @@ } -(void)printBacktrace; { - int x; - for(x = 3; x < frameCount; x++) { - if(frameStrings[x] == NULL) { break; } - printf("%s\n", frameStrings[x]); - } + int x; + for(x = 3; x < frameCount; x++) { + if(frameStrings[x] == NULL) { break; } + printf("%s\n", frameStrings[x]); + } } @end diff --git a/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h index f33ad7040..aa974d238 100644 --- a/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h +++ b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h @@ -31,4 +31,13 @@ -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event; @end -extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey; \ No newline at end of file +#ifdef __cplusplus +extern "C" { +#endif + +extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey; +extern NSString *kIgnoreMediaKeysDefaultsKey; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m index a349f5922..665edc27f 100644 --- a/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m +++ b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m @@ -28,6 +28,9 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv [self startWatchingAppSwitching]; singleton = self; _mediaKeyAppList = [NSMutableArray new]; + _tapThreadRL=nil; + _eventPort=nil; + _eventPortSource=nil; return self; } -(void)dealloc; @@ -58,6 +61,9 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv } -(void)startWatchingMediaKeys;{ + // Prevent having multiple mediaKeys threads + [self stopWatchingMediaKeys]; + [self setShouldInterceptMediaKeyEvents:YES]; // Add an event tap to intercept the system defined media key events @@ -78,6 +84,22 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv -(void)stopWatchingMediaKeys; { // TODO: Shut down thread, remove event tap port and source + + if(_tapThreadRL){ + CFRunLoopStop(_tapThreadRL); + _tapThreadRL=nil; + } + + if(_eventPort){ + CFMachPortInvalidate(_eventPort); + CFRelease(_eventPort); + _eventPort=nil; + } + + if(_eventPortSource){ + CFRelease(_eventPortSource); + _eventPortSource=nil; + } } #pragma mark - @@ -90,7 +112,9 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv 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*/; + return + ![[NSUserDefaults standardUserDefaults] boolForKey:kIgnoreMediaKeysDefaultsKey] + && floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/; #endif } @@ -108,6 +132,14 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv @"com.apple.Aperture", @"com.plexsquared.Plex", @"com.soundcloud.desktop", + @"org.niltsh.MPlayerX", + @"com.ilabs.PandorasHelper", + @"com.mahasoftware.pandabar", + @"com.bitcartel.pandorajam", + @"org.clementine-player.clementine", + @"fm.last.Last.fm", + @"com.beatport.BeatportPro", + @"com.Timenut.SongKey", @"com.macromedia.fireworks", // the tap messes up their mouse input nil ]; @@ -213,6 +245,8 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv #pragma mark Task switching callbacks NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey = @"SPApplicationsNeedingMediaKeys"; +NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys"; + -(void)mediaKeyAppListChanged;