PageRenderTime 146ms CodeModel.GetById 40ms app.highlight 72ms RepoModel.GetById 29ms app.codeStats 0ms

/src/GetNewStuffDelegate.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 323 lines | 243 code | 49 blank | 31 comment | 26 complexity | 12a0a8d2d8d6126087086c50ee6cc9e2 MD5 | raw file
  1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2 *
  3 *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
  4 *
  5 *   Tomahawk is free software: you can redistribute it and/or modify
  6 *   it under the terms of the GNU General Public License as published by
  7 *   the Free Software Foundation, either version 3 of the License, or
  8 *   (at your option) any later version.
  9 *
 10 *   Tomahawk is distributed in the hope that it will be useful,
 11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 13 *   GNU General Public License for more details.
 14 *
 15 *   You should have received a copy of the GNU General Public License
 16 *   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
 17 */
 18
 19#include "GetNewStuffDelegate.h"
 20
 21#include "GetNewStuffModel.h"
 22#include "utils/tomahawkutils.h"
 23#include "utils/logger.h"
 24
 25#include <QtGui/QPainter>
 26#include <QApplication>
 27#include <QMouseEvent>
 28#include "AtticaManager.h"
 29
 30#define PADDING 4
 31#define PADDING_BETWEEN_STARS 2
 32#define STAR_SIZE 12
 33
 34#ifdef Q_WS_MAC
 35#define SIZEHINT_HEIGHT 70
 36#else
 37#define SIZEHINT_HEIGHT 60
 38#endif
 39
 40GetNewStuffDelegate::GetNewStuffDelegate( QObject* parent )
 41    : QStyledItemDelegate ( parent )
 42    , m_widestTextWidth( 0 )
 43    , m_hoveringOver( -1 )
 44{
 45    m_defaultCover.load( RESPATH "images/sipplugin-online.png" );
 46    m_ratingStarPositive.load( RESPATH "images/starred.png" );
 47    m_ratingStarNegative.load( RESPATH "images/star-unstarred.png" );
 48    m_onHoverStar.load( RESPATH "images/star-hover.png" );
 49
 50    m_ratingStarPositive = m_ratingStarPositive.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation  );
 51    m_ratingStarNegative = m_ratingStarNegative.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation  );
 52    m_onHoverStar = m_onHoverStar.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation  );
 53
 54    const int w = SIZEHINT_HEIGHT - 2*PADDING;
 55    m_defaultCover = m_defaultCover.scaled( w, w, Qt::KeepAspectRatio, Qt::SmoothTransformation );
 56
 57    // save the widest wifth
 58    QFont f( QApplication::font() );
 59    f.setPointSize( f.pointSize() - 1 );
 60    QFontMetrics fm( f );
 61    QStringList l = QStringList() << tr( "Installed" ) << tr( "Installing" ) << tr( "Failed" ) << tr( "Uninstalling" );
 62    foreach ( const QString& str, l )
 63    {
 64        if ( fm.width( str ) > m_widestTextWidth )
 65            m_widestTextWidth = fm.width( str );
 66    }
 67}
 68
 69void
 70GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
 71{
 72    QStyleOptionViewItemV4 opt = option;
 73    initStyleOption( &opt, index );
 74
 75    QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget );
 76
 77    painter->setRenderHint( QPainter::Antialiasing );
 78
 79    QFont titleFont = opt.font;
 80    titleFont.setBold( true );
 81    titleFont.setPointSize( titleFont.pointSize() + 2 );
 82    QFontMetrics titleMetrics( titleFont );
 83
 84    QFont authorFont = opt.font;
 85    authorFont.setItalic( true );
 86    authorFont.setPointSize( authorFont.pointSize() - 1 );
 87    QFontMetrics authorMetrics( authorFont );
 88
 89    QFont descFont = authorFont;
 90    descFont.setItalic( false );
 91    QFontMetrics descMetrics( descFont );
 92
 93    QFont installFont = opt.font;
 94    installFont.setPointSize( installFont.pointSize() - 1 );
 95    QFontMetrics installMetrics( descFont );
 96
 97    const int height = opt.rect.height();
 98    const int center = height / 2 + opt.rect.top();
 99
100    // Pixmap
101    QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >();
102    const int pixmapWidth = height - 2*PADDING;
103    QRect pixmapRect( PADDING, PADDING + opt.rect.top(), pixmapWidth, pixmapWidth );
104    if ( p.isNull() ) // default image... TODO
105        p = m_defaultCover;
106    else
107        p = p.scaled( pixmapRect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
108
109    painter->drawPixmap( pixmapRect, p );
110
111    // Go from right edge now, stars, install button, and downloaded info
112
113    // install / status button
114    AtticaManager::ResolverState state = static_cast< AtticaManager::ResolverState >( index.data( GetNewStuffModel::StateRole ).toInt() );
115    QString actionText;
116    switch( state )
117    {
118        case AtticaManager::Uninstalled:
119            actionText = tr( "Install" );
120            break;
121        case AtticaManager::Installing:
122            actionText = tr( "Installing" );
123            break;
124        case AtticaManager::Upgrading:
125            actionText = tr( "Upgrading" );
126            break;
127        case AtticaManager::Failed:
128            actionText = tr( "Failed" );
129            break;
130        case AtticaManager::Installed:
131            actionText = tr( "Uninstall" );
132            break;
133        case AtticaManager::NeedsUpgrade:
134            actionText = tr( "Upgrade" );
135            break;
136    }
137
138    const int btnWidth = m_widestTextWidth + 7;
139    const int leftEdge = opt.rect.width() - PADDING - btnWidth - 3;
140    const QRect btnRect( leftEdge, center - ( installMetrics.height() + 4 ) / 2, btnWidth, installMetrics.height() + 4 );
141    m_cachedButtonRects[ QPair<int, int>(index.row(), index.column()) ] = btnRect;
142
143    QPen saved = painter->pen();
144    painter->setPen( opt.palette.color( QPalette::Active, QPalette::AlternateBase ) );
145
146    QPainterPath btnPath;
147    const int radius = 3;
148    //btnPath.addRoundedRect( btnRect, 3, 3 );
149    // draw top half gradient
150    const int btnCenter = btnRect.bottom() - ( btnRect.height() / 2 );
151    btnPath.moveTo( btnRect.left(), btnCenter );
152    btnPath.lineTo( btnRect.left(), btnRect.top() + radius );
153    btnPath.quadTo( QPoint( btnRect.topLeft() ), QPoint( btnRect.left() + radius, btnRect.top() ) );
154    btnPath.lineTo( btnRect.right() - radius, btnRect.top() );
155    btnPath.quadTo( QPoint( btnRect.topRight() ), QPoint( btnRect.right(), btnRect.top() + radius ) );
156    btnPath.lineTo( btnRect.right(),btnCenter );
157    btnPath.lineTo( btnRect.left(), btnCenter );
158
159    QLinearGradient g;
160    g.setColorAt( 0, QColor(54, 127, 211) );
161    g.setColorAt( 0.5, QColor(43, 104, 182) );
162    //painter->setPen( bg.darker() );
163    painter->fillPath( btnPath, g );
164    //painter->drawPath( btnPath );
165
166    btnPath = QPainterPath();
167    btnPath.moveTo( btnRect.left(), btnCenter );
168    btnPath.lineTo( btnRect.left(), btnRect.bottom() - radius );
169    btnPath.quadTo( QPoint( btnRect.bottomLeft() ), QPoint( btnRect.left() + radius, btnRect.bottom() ) );
170    btnPath.lineTo( btnRect.right() - radius, btnRect.bottom() );
171    btnPath.quadTo( QPoint( btnRect.bottomRight() ), QPoint( btnRect.right(), btnRect.bottom() - radius ) );
172    btnPath.lineTo( btnRect.right(), btnCenter );
173    btnPath.lineTo( btnRect.left(), btnCenter );
174
175    g.setColorAt( 0, QColor(34, 85, 159) );
176    g.setColorAt( 0.5, QColor(35, 79, 147) );
177    painter->fillPath( btnPath, g );
178
179    painter->setFont( installFont );
180    painter->drawText( btnRect, Qt::AlignCenter, actionText );
181
182    painter->setPen( saved );
183
184    // rating stars
185    int rating = index.data( GetNewStuffModel::RatingRole ).toInt();
186    const int ratingWidth = 5 * ( m_ratingStarPositive.width() + PADDING_BETWEEN_STARS );
187    int runningEdge = ( btnRect.right() - btnRect.width() / 2 ) - ratingWidth / 2;
188    for ( int i = 1; i < 6; i++ )
189    {
190        QRect r( runningEdge, btnRect.top() - m_ratingStarPositive.height() - PADDING, m_ratingStarPositive.width(), m_ratingStarPositive.height() );
191        if ( i == 1 )
192            m_cachedStarRects[ QPair<int, int>(index.row(), index.column()) ] = r;
193
194        const bool userHasRated = index.data( GetNewStuffModel::UserHasRatedRole ).toBool();
195        if ( !userHasRated && // Show on-hover animation if the user hasn't rated it yet, and is hovering over it
196             m_hoveringOver > -1 &&
197             m_hoveringItem == index )
198        {
199            if ( i <= m_hoveringOver ) // positive star
200                painter->drawPixmap( r, m_onHoverStar );
201            else
202                painter->drawPixmap( r, m_ratingStarNegative );
203        }
204        else
205        {
206            if ( i <= rating ) // positive or rated star
207            {
208                if ( userHasRated )
209                    painter->drawPixmap( r, m_onHoverStar );
210                else
211                    painter->drawPixmap( r, m_ratingStarPositive );
212            }
213            else
214                painter->drawPixmap( r, m_ratingStarNegative );
215        }
216        runningEdge += m_ratingStarPositive.width() + PADDING_BETWEEN_STARS;
217    }
218
219    // downloaded num times, underneath button
220    QString count = tr( "%1 downloads" ).arg( index.data( GetNewStuffModel::DownloadCounterRole ).toInt() );
221    const QRect countRect( btnRect.left(), btnRect.bottom() + PADDING, btnRect.width(), opt.rect.bottom() - PADDING - btnRect.bottom() );
222    QFont countFont = descFont;
223    countFont.setPointSize( countFont.pointSize() - 2 );
224    countFont.setBold( true );
225    painter->setFont( countFont );
226    painter->drawText( countRect, Qt::AlignCenter | Qt::TextWordWrap, count );
227
228    // author and version
229    QString author = index.data( GetNewStuffModel::AuthorRole ).toString();
230    const int authorWidth = authorMetrics.width( author );
231    const int topTextLine = opt.rect.top() + PADDING;
232    const QRect authorRect( btnRect.x() - 3*PADDING - authorWidth, topTextLine, authorWidth + 6, authorMetrics.height() );
233    painter->setFont( authorFont );
234    painter->drawText( authorRect, Qt::AlignCenter, author );
235
236    const QRect versionRect = authorRect.translated( 0, authorRect.height() );
237    QString version = index.data( GetNewStuffModel::VersionRole ).toString();
238    painter->drawText( versionRect, Qt::AlignCenter, version );
239
240    // title
241    QString title = index.data( Qt::DisplayRole ).toString();
242    const int rightTitleEdge = authorRect.left() - PADDING;
243    const int leftTitleEdge = pixmapRect.right() + PADDING;
244    const QRect textRect( leftTitleEdge, topTextLine, rightTitleEdge - leftTitleEdge, versionRect.bottom() - opt.rect.top() - PADDING );
245    painter->setFont( titleFont );
246    painter->drawText( textRect, Qt::AlignVCenter | Qt::AlignLeft, title );
247
248    // description
249    QString desc = index.data( GetNewStuffModel::DescriptionRole ).toString();
250    const int descWidth = btnRect.left() - leftTitleEdge - PADDING;
251    const QRect descRect( leftTitleEdge, versionRect.bottom(), descWidth, opt.rect.bottom() - versionRect.bottom() + PADDING );
252    painter->setFont( descFont );
253    painter->drawText( descRect, Qt::AlignLeft | Qt::TextWordWrap, desc );
254}
255
256
257QSize
258GetNewStuffDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
259{
260    Q_UNUSED( option );
261    Q_UNUSED( index );
262    return QSize( 200, SIZEHINT_HEIGHT );
263}
264
265bool
266GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
267{
268    Q_UNUSED( option );
269
270    if ( event->type() != QEvent::MouseButtonRelease &&
271         event->type() != QEvent::MouseMove )
272        return false;
273
274    if ( event->type() == QEvent::MouseButtonRelease && m_cachedButtonRects.contains( QPair<int, int>( index.row(), index.column() ) ) )
275    {
276        QRect rect = m_cachedButtonRects[ QPair<int, int>( index.row(), index.column() ) ];
277        QMouseEvent* me = static_cast< QMouseEvent* >( event );
278
279        if ( rect.contains( me->pos() ) )
280        {
281            model->setData( index, true );
282
283            return true;
284        }
285    }
286
287    if ( m_cachedStarRects.contains( QPair<int, int>( index.row(), index.column() ) ) )
288    {
289        QRect fullStars = m_cachedStarRects[ QPair<int, int>( index.row(), index.column() ) ];
290        const int starsWidth = 5 * ( m_ratingStarPositive.width() + PADDING_BETWEEN_STARS );
291        fullStars.setWidth( starsWidth );
292
293        QMouseEvent* me = static_cast< QMouseEvent* >( event );
294
295        if ( fullStars.contains( me->pos() ) )
296        {
297            const int eachStar = starsWidth / 5;
298            const int clickOffset = me->pos().x() - fullStars.x();
299            const int whichStar = (clickOffset / eachStar) + 1;
300
301            if ( event->type() == QEvent::MouseButtonRelease )
302            {
303                model->setData( index, whichStar, GetNewStuffModel::RatingRole );
304            }
305            else if ( event->type() == QEvent::MouseMove )
306            {
307                // 0-indexed
308                m_hoveringOver = whichStar;
309                m_hoveringItem = index;
310            }
311
312            return true;
313        }
314    }
315
316    if ( m_hoveringOver > -1 )
317    {
318        emit update( m_hoveringItem );
319        m_hoveringOver = -1;
320        m_hoveringItem = QPersistentModelIndex();
321    }
322    return false;
323}