3 This file is part of Minitube.
4 Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
6 Minitube is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 Minitube is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Minitube. If not, see <http://www.gnu.org/licenses/>.
21 #include "playlistitemdelegate.h"
22 #include "playlistmodel.h"
23 #include "fontutils.h"
24 #include "downloaditem.h"
25 #include "iconutils.h"
26 #include "videodefinition.h"
29 const int PlaylistItemDelegate::THUMB_HEIGHT = 90;
30 const int PlaylistItemDelegate::THUMB_WIDTH = 160;
31 const int PlaylistItemDelegate::PADDING = 10;
33 PlaylistItemDelegate::PlaylistItemDelegate(QObject* parent, bool downloadInfo)
34 : QStyledItemDelegate(parent),
35 downloadInfo(downloadInfo),
38 boldFont.setBold(true);
39 smallerBoldFont = FontUtils::smallBold();
40 smallerFont = FontUtils::small();
43 progressBar = new QProgressBar(qApp->activeWindow());
44 QPalette palette = progressBar->palette();
45 palette.setColor(QPalette::Window, Qt::transparent);
46 progressBar->setPalette(palette);
47 progressBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
49 } else createPlayIcon();
52 PlaylistItemDelegate::~PlaylistItemDelegate() {
53 if (progressBar) delete progressBar;
56 void PlaylistItemDelegate::createPlayIcon() {
57 playIcon = QPixmap(THUMB_WIDTH, THUMB_HEIGHT);
58 playIcon.fill(Qt::transparent);
60 QPixmap tempPixmap(THUMB_WIDTH, THUMB_HEIGHT);
61 tempPixmap.fill(Qt::transparent);
62 QPainter painter(&tempPixmap);
63 painter.setRenderHints(QPainter::Antialiasing, true);
65 const int hPadding = PADDING*6;
66 const int vPadding = PADDING*2;
69 polygon << QPoint(hPadding, vPadding)
70 << QPoint(THUMB_WIDTH-hPadding, THUMB_HEIGHT/2)
71 << QPoint(hPadding, THUMB_HEIGHT-vPadding);
72 painter.setBrush(Qt::white);
74 pen.setColor(Qt::white);
75 pen.setWidth(PADDING);
76 pen.setJoinStyle(Qt::RoundJoin);
77 pen.setCapStyle(Qt::RoundCap);
79 painter.drawPolygon(polygon);
82 QPainter painter2(&playIcon);
83 painter2.setOpacity(.75);
84 painter2.drawPixmap(0, 0, tempPixmap);
87 QSize PlaylistItemDelegate::sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const {
88 return QSize(THUMB_WIDTH, THUMB_HEIGHT + 1);
91 void PlaylistItemDelegate::paint( QPainter* painter,
92 const QStyleOptionViewItem& option, const QModelIndex& index ) const {
94 int itemType = index.data(ItemTypeRole).toInt();
95 if (itemType == ItemTypeVideo) {
96 QStyleOptionViewItemV4 opt = QStyleOptionViewItemV4(option);
97 initStyleOption(&opt, index);
99 opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
100 paintBody(painter, opt, index);
102 QStyledItemDelegate::paint( painter, option, index );
106 void PlaylistItemDelegate::paintBody( QPainter* painter,
107 const QStyleOptionViewItem& option,
108 const QModelIndex& index ) const {
110 painter->translate( option.rect.topLeft() );
112 QRect line(0, 0, option.rect.width(), option.rect.height());
113 if (downloadInfo) line.setWidth(line.width() / 2);
115 const bool isActive = index.data( ActiveTrackRole ).toBool();
116 const bool isSelected = option.state & QStyle::State_Selected;
118 // draw the "current track" highlight underneath the text
119 if (isActive && !isSelected)
120 paintActiveOverlay(painter, line);
122 // get the video metadata
123 const VideoPointer videoPointer = index.data( VideoRole ).value<VideoPointer>();
124 const Video *video = videoPointer.data();
127 painter->drawPixmap(0, 0, video->thumbnail());
129 // play icon overlayed on the thumb
131 painter->drawPixmap(playIcon.rect(), playIcon);
134 if (video->duration() > 0)
135 drawTime(painter, video->formattedDuration(), line);
138 painter->setPen(option.palette.color(QPalette::Midlight));
139 painter->drawLine(THUMB_WIDTH, THUMB_HEIGHT, option.rect.width(), THUMB_HEIGHT);
140 if (!video->thumbnail().isNull())
141 painter->setPen(Qt::black);
142 painter->drawLine(0, THUMB_HEIGHT, THUMB_WIDTH-1, THUMB_HEIGHT);
144 if (line.width() > THUMB_WIDTH + 60) {
146 // if (isActive) painter->setFont(boldFont);
150 painter->setPen(QPen(option.palette.highlightedText(), 0));
152 painter->setPen(QPen(option.palette.text(), 0));
155 QString videoTitle = video->title();
156 QString v = videoTitle;
157 const int flags = Qt::AlignTop | Qt::TextWordWrap;
158 QRect textBox = line.adjusted(PADDING+THUMB_WIDTH, PADDING, 0, 0);
159 textBox = painter->boundingRect(textBox, flags, v);
160 while (textBox.height() > 55 && v.length() > 10) {
161 videoTitle.truncate(videoTitle.length() - 1);
163 v = v.trimmed().append("...");
164 textBox = painter->boundingRect(textBox, flags, v);
166 painter->drawText(textBox, flags, v);
168 painter->setFont(smallerFont);
171 QString publishedString = video->published().date().toString(Qt::DefaultLocaleShortDate);
172 QSize stringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, publishedString ) );
173 QPoint textLoc(PADDING+THUMB_WIDTH, PADDING*2 + textBox.height());
174 QRect publishedTextBox(textLoc , stringSize);
175 painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString);
177 if (line.width() > publishedTextBox.x() + publishedTextBox.width()*2) {
180 bool authorHovered = false;
181 bool authorPressed = false;
182 const bool isHovered = index.data(HoveredItemRole).toBool();
184 authorHovered = index.data(AuthorHoveredRole).toBool();
185 authorPressed = index.data(AuthorPressedRole).toBool();
189 painter->setFont(smallerBoldFont);
192 painter->setPen(QPen(option.palette.brush(QPalette::Highlight), 0));
194 painter->setOpacity(.5);
196 QString authorString = video->channelTitle();
197 textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
198 stringSize = QSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) );
199 QRect authorTextBox(textLoc , stringSize);
200 authorRects.insert(index.row(), authorTextBox);
201 painter->drawText(authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString);
204 if (line.width() > authorTextBox.x() + 50) {
207 if (video->viewCount() >= 0) {
209 QString viewCountString = tr("%1 views").arg(locale.toString(video->viewCount()));
210 textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
211 stringSize = QSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, viewCountString ) );
212 QRect viewCountTextBox(textLoc , stringSize);
213 painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, viewCountString);
217 const QString definitionString = VideoDefinition::getDefinitionFor(video->getDefinitionCode()).getName();
218 textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
219 stringSize = QSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, definitionString ) );
220 QRect viewCountTextBox(textLoc , stringSize);
221 painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, definitionString);
230 const bool isHovered = index.data(HoveredItemRole).toBool();
231 if (!isActive && isHovered) {
232 painter->setFont(smallerFont);
233 painter->setPen(Qt::white);
234 QString videoTitle = video->title();
235 QString v = videoTitle;
236 const int flags = Qt::AlignTop | Qt::TextWordWrap;
237 QRect textBox(PADDING, PADDING, THUMB_WIDTH - PADDING*2, THUMB_HEIGHT - PADDING*2);
238 textBox = painter->boundingRect(textBox, flags, v);
239 while (textBox.height() > THUMB_HEIGHT && v.length() > 10) {
240 videoTitle.truncate(videoTitle.length() - 1);
242 v = v.trimmed().append("...");
243 textBox = painter->boundingRect(textBox, flags, v);
245 painter->fillRect(QRect(0, 0, THUMB_WIDTH, textBox.height() + PADDING*2), QColor(0, 0, 0, 128));
246 painter->drawText(textBox, flags, v);
253 if (downloadInfo) paintDownloadInfo(painter, option, index);
257 void PlaylistItemDelegate::paintActiveOverlay(QPainter *painter, const QRect &line) const {
258 static QLinearGradient linearGradient;
259 static bool initialized = false;
263 QColor highlightColor = palette.color(QPalette::Highlight);
264 QColor backgroundColor = palette.color(QPalette::Base);
265 const float animation = 0.4;
266 const int gradientRange = 16;
268 QColor color2 = QColor::fromHsv(
269 highlightColor.hue(),
270 (int) (backgroundColor.saturation() * (1.0f - animation) + highlightColor.saturation() * animation),
271 (int) (backgroundColor.value() * (1.0f - animation) + highlightColor.value() * animation)
273 QColor color1 = QColor::fromHsv(
275 qMax(color2.saturation() - gradientRange, 0),
276 qMin(color2.value() + gradientRange, 255)
279 linearGradient = QLinearGradient(0, 0, 0, THUMB_HEIGHT);
280 linearGradient.setColorAt(0.0, color1);
281 linearGradient.setColorAt(1.0, color2);
285 painter->fillRect(line, linearGradient);
288 void PlaylistItemDelegate::drawTime(QPainter *painter, const QString &time, const QRect &line) const {
289 static const int timePadding = 4;
290 QRect textBox = painter->boundingRect(line, Qt::AlignLeft | Qt::AlignTop, time);
292 textBox.adjust(0, 0, timePadding, 0);
293 // move to bottom right corner of the thumb
294 textBox.translate(THUMB_WIDTH - textBox.width(), THUMB_HEIGHT - textBox.height());
297 painter->setPen(Qt::NoPen);
298 painter->setBrush(Qt::black);
299 painter->setOpacity(.5);
300 painter->drawRect(textBox);
304 painter->setPen(Qt::white);
305 painter->drawText(textBox, Qt::AlignCenter, time);
309 void PlaylistItemDelegate::paintDownloadInfo( QPainter* painter,
310 const QStyleOptionViewItem& option,
311 const QModelIndex& index ) const {
313 // get the video metadata
314 const DownloadItemPointer downloadItemPointer = index.data(DownloadItemRole).value<DownloadItemPointer>();
315 const DownloadItem *downloadItem = downloadItemPointer.data();
319 const QRect line(0, 0, option.rect.width() / 2, option.rect.height());
321 painter->translate(option.rect.topLeft());
322 painter->translate(line.width(), 0);
325 DownloadItemStatus status = downloadItem->status();
327 if (status == Downloading) {
328 QString downloaded = DownloadItem::formattedFilesize(downloadItem->bytesReceived());
329 QString total = DownloadItem::formattedFilesize(downloadItem->bytesTotal());
330 QString speed = DownloadItem::formattedSpeed(downloadItem->currentSpeed());
331 QString eta = DownloadItem::formattedTime(downloadItem->remainingTime());
333 message = tr("%1 of %2 (%3) — %4").arg(
339 } else if (status == Starting) {
340 message = tr("Preparing");
341 } else if (status == Failed) {
342 message = tr("Failed") + " — " + downloadItem->errorMessage();
343 } else if (status == Finished) {
344 message = tr("Completed");
345 } else if (status == Idle) {
346 message = tr("Stopped");
349 // progressBar->setPalette(option.palette);
350 if (status == Finished) {
351 progressBar->setValue(100);
352 progressBar->setEnabled(true);
353 } else if (status == Downloading) {
354 progressBar->setValue(downloadItem->currentPercent());
355 progressBar->setEnabled(true);
357 progressBar->setValue(0);
358 progressBar->setEnabled(false);
361 int progressBarWidth = line.width() - PADDING*4 - 16;
362 progressBar->setMaximumWidth(progressBarWidth);
363 progressBar->setMinimumWidth(progressBarWidth);
365 painter->translate(PADDING, PADDING);
366 progressBar->render(painter);
369 bool downloadButtonHovered = false;
370 bool downloadButtonPressed = false;
371 const bool isHovered = index.data(HoveredItemRole).toBool();
373 downloadButtonHovered = index.data(DownloadButtonHoveredRole).toBool();
374 downloadButtonPressed = index.data(DownloadButtonPressedRole).toBool();
376 QIcon::Mode iconMode;
377 if (downloadButtonPressed) iconMode = QIcon::Selected;
378 else if (downloadButtonHovered) iconMode = QIcon::Active;
379 else iconMode = QIcon::Normal;
381 if (status != Finished && status != Failed && status != Idle) {
382 if (downloadButtonHovered) message = tr("Stop downloading");
384 QIcon closeIcon = IconUtils::icon("window-close");
385 painter->drawPixmap(downloadButtonRect(line), closeIcon.pixmap(16, 16, iconMode));
389 else if (status == Finished) {
390 if (downloadButtonHovered)
392 message = tr("Show in %1").arg("Finder");
394 message = tr("Open parent folder");
397 QIcon searchIcon = IconUtils::icon("system-search");
398 painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
402 else if (status == Failed || status == Idle) {
403 if (downloadButtonHovered) message = tr("Restart downloading");
405 QIcon searchIcon = IconUtils::icon("view-refresh");
406 painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
410 QRect textBox = line.adjusted(PADDING, PADDING*2 + progressBar->sizeHint().height(), -2 * PADDING, -PADDING);
411 textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
412 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
417 QRect PlaylistItemDelegate::downloadButtonRect(const QRect &line) const {
419 line.width() - PADDING*2 - 16,
420 PADDING + progressBar->sizeHint().height() / 2 - 8,
425 QRect PlaylistItemDelegate::authorRect(const QModelIndex& index) const {
426 return authorRects.value(index.row());