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;