Skip to content

Commit

Permalink
Add a view for libattica-provided GHNS resolvers
Browse files Browse the repository at this point in the history
  • Loading branch information
lfranchi committed Sep 11, 2011
1 parent cc2cc83 commit 3f457d4
Show file tree
Hide file tree
Showing 16 changed files with 774 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ macro_log_feature(Boost_FOUND "Boost" "Provides free peer-reviewed portable C++
macro_optional_find_package(QCA2)
macro_log_feature(QCA2_FOUND "QCA2" "Provides encryption and signing functions required for Grooveshark resolver" "http://delta.affinix.com/qca/" FALSE "" "")

macro_optional_find_package(LibAttica)
macro_log_feature(LIBATTICA_FOUND "libattica" "Provides support for automatic fetching and managing of resolvers from the tomahawk website" "https://projects.kde.org/projects/kdesupport/attica" FALSE "" "")

# required
#While we distribute our own liblastfm2, don't need to look for it
#macro_optional_find_package(LibLastFm 0.3.3)
Expand Down
63 changes: 63 additions & 0 deletions CMakeModules/FindLibAttica.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Try to find the Attica library
# Once done this will define
#
# LIBATTICA_FOUND Indicates that Attica was found
# LIBATTICA_LIBRARIES Libraries needed to use Attica
# LIBATTICA_LIBRARY_DIRS Paths needed for linking against Attica
# LIBATTICA_INCLUDE_DIR Path needed for finding Attica include files
#
# The minimum required version of LibAttica can be specified using the
# standard syntax, e.g. find_package(LibAttica 0.20)

# Copyright (c) 2009 Frederik Gladhorn <gladhorn@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.

# Support LIBATTICA_MIN_VERSION for compatibility:
IF(NOT LibAttica_FIND_VERSION)
SET(LibAttica_FIND_VERSION "${LIBATTICA_MIN_VERSION}")
ENDIF(NOT LibAttica_FIND_VERSION)

# the minimum version of LibAttica we require
IF(NOT LibAttica_FIND_VERSION)
SET(LibAttica_FIND_VERSION "0.1.0")
ENDIF(NOT LibAttica_FIND_VERSION)


IF (NOT WIN32)
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
FIND_PACKAGE(PkgConfig)
PKG_CHECK_MODULES(PC_LIBATTICA QUIET libattica)
SET(LIBATTICA_DEFINITIONS ${PC_ATTICA_CFLAGS_OTHER})
ENDIF (NOT WIN32)

FIND_PATH(LIBATTICA_INCLUDE_DIR attica/provider.h
HINTS
${PC_LIBATTICA_INCLUDEDIR}
${PC_LIBATTICA_INCLUDE_DIRS}
PATH_SUFFIXES attica
)

# Store the version number in the cache, so we don't have to search everytime:
IF(LIBATTICA_INCLUDE_DIR AND NOT LIBATTICA_VERSION)
FILE(READ ${LIBATTICA_INCLUDE_DIR}/attica/version.h LIBATTICA_VERSION_CONTENT)
STRING (REGEX MATCH "LIBATTICA_VERSION_STRING \".*\"\n" LIBATTICA_VERSION_MATCH "${LIBATTICA_VERSION_CONTENT}")
IF(LIBATTICA_VERSION_MATCH)
STRING(REGEX REPLACE "LIBATTICA_VERSION_STRING \"(.*)\"\n" "\\1" _LIBATTICA_VERSION ${LIBATTICA_VERSION_MATCH})
ENDIF(LIBATTICA_VERSION_MATCH)
SET(LIBATTICA_VERSION "${_LIBATTICA_VERSION}" CACHE STRING "Version number of LibAttica" FORCE)
ENDIF(LIBATTICA_INCLUDE_DIR AND NOT LIBATTICA_VERSION)


FIND_LIBRARY(LIBATTICA_LIBRARIES NAMES attica libattica
HINTS
${PC_LIBATTICA_LIBDIR}
${PC_LIBATTICA_LIBRARY_DIRS}
)

INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibAttica REQUIRED_VARS LIBATTICA_LIBRARIES LIBATTICA_INCLUDE_DIR
VERSION_VAR LIBATTICA_VERSION)

MARK_AS_ADVANCED(LIBATTICA_INCLUDE_DIR LIBATTICA_LIBRARIES)
21 changes: 21 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
settingslistdelegate.cpp
resolversmodel.cpp
tomahawkwindow.cpp

GetNewStuffDialog.cpp
GetNewStuffDelegate.cpp
GetNewStuffModel.cpp
AtticaManager.cpp
)

SET( tomahawkHeaders ${tomahawkHeaders}
Expand Down Expand Up @@ -126,6 +131,11 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui}
resolversmodel.h
delegateconfigwrapper.h
tomahawkwindow.h

GetNewStuffDialog.h
GetNewStuffDelegate.h
GetNewStuffModel.h
AtticaManager.h
)

SET( tomahawkUI ${tomahawkUI}
Expand All @@ -135,6 +145,8 @@ SET( tomahawkUI ${tomahawkUI}
proxydialog.ui

audiocontrols.ui

GetNewStuffDialog.ui
)

INCLUDE_DIRECTORIES(
Expand Down Expand Up @@ -194,6 +206,10 @@ IF(QCA2_FOUND)
INCLUDE_DIRECTORIES( ${QCA2_INCLUDE_DIR} )
ENDIF(QCA2_FOUND)

IF(LIBATTICA_FOUND)
INCLUDE_DIRECTORIES( ${LIBATTICA_INCLUDE_DIR} )
ENDIF(LIBATTICA_FOUND)

kde4_add_app_icon( tomahawkSources "${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon-*.png" )
qt4_add_resources( RC_SRCS "../resources.qrc" )
qt4_wrap_cpp( tomahawkMoc ${tomahawkHeaders} )
Expand Down Expand Up @@ -238,6 +254,11 @@ IF(QCA2_FOUND)
SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${QCA2_LIBRARIES} )
ENDIF(QCA2_FOUND)

IF(LIBATTICA_FOUND)
SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${LIBATTICA_LIBRARIES} )
ENDIF(LIBATTICA_FOUND)


TARGET_LINK_LIBRARIES( tomahawk
${LINK_LIBRARIES}
${TOMAHAWK_LIBRARIES}
Expand Down
245 changes: 245 additions & 0 deletions src/GetNewStuffDelegate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/

#include "GetNewStuffDelegate.h"

#include "GetNewStuffModel.h"
#include "utils/tomahawkutils.h"

#include <QtGui/QPainter>
#include <QApplication>
#include <QMouseEvent>

#define PADDING 4

#ifdef Q_WS_MAC
#define SIZEHINT_HEIGHT 70
#else
#define SIZEHINT_HEIGHT 60
#endif

GetNewStuffDelegate::GetNewStuffDelegate( QObject* parent )
: QStyledItemDelegate ( parent )
, m_widestTextWidth( 0 )
{
m_defaultCover.load( RESPATH "images/sipplugin-online.png" );
m_ratingStarPositive.load( RESPATH "images/loved.png" );
m_ratingStarNegative.load( RESPATH "images/not-loved.png" );

m_ratingStarPositive = m_ratingStarPositive.scaled( 8, 8, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_ratingStarNegative = m_ratingStarNegative.scaled( 8, 8, Qt::KeepAspectRatio, Qt::SmoothTransformation );
const int w = SIZEHINT_HEIGHT - 2*PADDING;
m_defaultCover = m_defaultCover.scaled( w, w, Qt::KeepAspectRatio, Qt::SmoothTransformation );

// save the widest wifth
QFont f( QApplication::font() );
f.setPointSize( f.pointSize() - 1 );
QFontMetrics fm( f );
QStringList l = QStringList() << tr( "Installed" ) << tr( "Installing" ) << tr( "Failed" ) << tr( "Uninstalling" );
foreach ( const QString& str, l )
{
if ( fm.width( str ) > m_widestTextWidth )
m_widestTextWidth = fm.width( str );
}
}

void
GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index );

QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget );

painter->setRenderHint( QPainter::Antialiasing );

QFont titleFont = opt.font;
titleFont.setBold( true );
titleFont.setPointSize( titleFont.pointSize() + 2 );
QFontMetrics titleMetrics( titleFont );

QFont authorFont = opt.font;
authorFont.setItalic( true );
authorFont.setPointSize( authorFont.pointSize() - 1 );
QFontMetrics authorMetrics( authorFont );

QFont descFont = authorFont;
descFont.setItalic( false );
QFontMetrics descMetrics( descFont );

QFont installFont = opt.font;
installFont.setPointSize( installFont.pointSize() - 1 );
QFontMetrics installMetrics( descFont );

const int height = opt.rect.height();
const int center = height / 2 + opt.rect.top();

// Pixmap
QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >();
const int pixmapWidth = height - 2*PADDING;
QRect pixmapRect( PADDING, PADDING + opt.rect.top(), pixmapWidth, pixmapWidth );
if ( p.isNull() ) // default image... TODO
p = m_defaultCover;
else
p = p.scaled( pixmapRect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );

painter->drawPixmap( pixmapRect, p );

// Go from right edge now, stars, install button, and downloaded info

// install / status button
GetNewStuffModel::States state = static_cast< GetNewStuffModel::States >( index.data( GetNewStuffModel::StateRole ).toInt() );
QString actionText;
switch( state )
{
case GetNewStuffModel::Uninstalled:
actionText = tr( "Install" );
break;
case GetNewStuffModel::Installing:
actionText = tr( "Installing" );
break;
case GetNewStuffModel::Failed:
actionText = tr( "Failed" );
break;
case GetNewStuffModel::Installed:
actionText = tr( "Uninstall" );
break;
}

const int btnWidth = m_widestTextWidth + 7;
const int leftEdge = opt.rect.width() - PADDING - btnWidth - 3;
const QRect btnRect( leftEdge, center - ( installMetrics.height() + 4 ) / 2, btnWidth, installMetrics.height() + 4 );
m_cachedButtonRects[ QPair<int, int>(index.row(), index.column()) ] = btnRect;

QPen saved = painter->pen();
painter->setPen( opt.palette.color( QPalette::Active, QPalette::AlternateBase ) );

QPainterPath btnPath;
const int radius = 3;
//btnPath.addRoundedRect( btnRect, 3, 3 );
// draw top half gradient
const int btnCenter = btnRect.bottom() - ( btnRect.height() / 2 );
btnPath.moveTo( btnRect.left(), btnCenter );
btnPath.lineTo( btnRect.left(), btnRect.top() + radius );
btnPath.quadTo( QPoint( btnRect.topLeft() ), QPoint( btnRect.left() + radius, btnRect.top() ) );
btnPath.lineTo( btnRect.right() - radius, btnRect.top() );
btnPath.quadTo( QPoint( btnRect.topRight() ), QPoint( btnRect.right(), btnRect.top() + radius ) );
btnPath.lineTo( btnRect.right(),btnCenter );
btnPath.lineTo( btnRect.left(), btnCenter );

QLinearGradient g;
g.setColorAt( 0, QColor(54, 127, 211) );
g.setColorAt( 0.5, QColor(43, 104, 182) );
//painter->setPen( bg.darker() );
painter->fillPath( btnPath, g );
//painter->drawPath( btnPath );

btnPath = QPainterPath();
btnPath.moveTo( btnRect.left(), btnCenter );
btnPath.lineTo( btnRect.left(), btnRect.bottom() - radius );
btnPath.quadTo( QPoint( btnRect.bottomLeft() ), QPoint( btnRect.left() + radius, btnRect.bottom() ) );
btnPath.lineTo( btnRect.right() - radius, btnRect.bottom() );
btnPath.quadTo( QPoint( btnRect.bottomRight() ), QPoint( btnRect.right(), btnRect.bottom() - radius ) );
btnPath.lineTo( btnRect.right(), btnCenter );
btnPath.lineTo( btnRect.left(), btnCenter );

g.setColorAt( 0, QColor(34, 85, 159) );
g.setColorAt( 0.5, QColor(35, 79, 147) );
painter->fillPath( btnPath, g );

painter->setFont( installFont );
painter->drawText( btnRect, Qt::AlignCenter, actionText );

painter->setPen( saved );
// rating stars
int rating = index.data( GetNewStuffModel::RatingRole ).toInt();
const int paddingBetweenStars = 2;
const int ratingWidth = 5 * ( m_ratingStarPositive.width() + paddingBetweenStars );
const int ratingY = ( btnRect.y() - ( btnRect.top() - opt.rect.y() ) / 2 ) - m_ratingStarNegative.height() / 2;
int runningEdge = ( btnRect.right() - btnRect.width() / 2 ) - ratingWidth / 2;
for ( int i = 1; i < 6; i++ )
{
QRect r( runningEdge, btnRect.top() - m_ratingStarPositive.height() - PADDING, m_ratingStarPositive.width(), m_ratingStarPositive.height() );
if ( i <= rating ) // positive star
painter->drawPixmap( r, m_ratingStarPositive );
else
painter->drawPixmap( r, m_ratingStarNegative );
runningEdge += m_ratingStarPositive.width() + paddingBetweenStars;
}

// downloaded num times, underneath button
QString count = tr( "%1 downloads" ).arg( index.data( GetNewStuffModel::DownloadCounterRole ).toInt() );
const QRect countRect( btnRect.left(), btnRect.bottom() + PADDING, btnRect.width(), opt.rect.bottom() - PADDING - btnRect.bottom() );
QFont countFont = descFont;
countFont.setPointSize( countFont.pointSize() - 2 );
countFont.setBold( true );
painter->setFont( countFont );
painter->drawText( countRect, Qt::AlignCenter | Qt::TextWordWrap, count );

// author and version
QString author = index.data( GetNewStuffModel::AuthorRole ).toString();
const int authorWidth = authorMetrics.width( author );
const int topTextLine = opt.rect.top() + PADDING;
const QRect authorRect( btnRect.x() - 3*PADDING - authorWidth, topTextLine, authorWidth + 6, authorMetrics.height() );
painter->setFont( authorFont );
painter->drawText( authorRect, Qt::AlignCenter, author );

const QRect versionRect = authorRect.translated( 0, authorRect.height() );
QString version = index.data( GetNewStuffModel::VersionRole ).toString();
painter->drawText( versionRect, Qt::AlignCenter, version );

// title
QString title = index.data( Qt::DisplayRole ).toString();
const int rightTitleEdge = authorRect.left() - PADDING;
const int leftTitleEdge = pixmapRect.right() + PADDING;
const QRect textRect( leftTitleEdge, topTextLine, rightTitleEdge - leftTitleEdge, versionRect.bottom() - opt.rect.top() - PADDING );
painter->setFont( titleFont );
painter->drawText( textRect, Qt::AlignVCenter | Qt::AlignLeft, title );

// description
QString desc = index.data( GetNewStuffModel::DescriptionRole ).toString();
const int descWidth = btnRect.left() - leftTitleEdge - PADDING;
const QRect descRect( leftTitleEdge, versionRect.bottom(), descWidth, opt.rect.bottom() - versionRect.bottom() + PADDING );
painter->setFont( descFont );
painter->drawText( descRect, Qt::AlignLeft | Qt::TextWordWrap, desc );
}


QSize
GetNewStuffDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
return QSize( 200, SIZEHINT_HEIGHT );
}

bool
GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
{
if ( event->type() == QEvent::MouseButtonRelease && m_cachedButtonRects.contains( QPair<int, int>( index.row(), index.column() ) ) )
{
QRect rect = m_cachedButtonRects[ QPair<int, int>( index.row(), index.column() ) ];
QMouseEvent* me = static_cast< QMouseEvent* >( event );

if ( rect.contains( me->pos() ) )
{
model->setData( index, true );

return true;
}
}
return false;
}
Loading

0 comments on commit 3f457d4

Please sign in to comment.