/src/GetNewStuffDelegate.cpp

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