]> git.sur5r.net Git - minitube/blob - src/playlistitemdelegate.cpp
Imported Upstream version 2.0
[minitube] / src / playlistitemdelegate.cpp
1 #include "playlistitemdelegate.h"
2 #include "playlistmodel.h"
3 #include "fontutils.h"
4 #include "downloaditem.h"
5 #include "utils.h"
6 #include "videodefinition.h"
7 #include "video.h"
8
9 const qreal PlaylistItemDelegate::THUMB_HEIGHT = 90.0;
10 const qreal PlaylistItemDelegate::THUMB_WIDTH = 160.0;
11 const qreal PlaylistItemDelegate::PADDING = 10.0;
12
13 QRect lastAuthorRect;
14 QHash<int, QRect> authorRects;
15
16 PlaylistItemDelegate::PlaylistItemDelegate(QObject* parent, bool downloadInfo)
17     : QStyledItemDelegate(parent),
18     downloadInfo(downloadInfo) {
19     boldFont.setBold(true);
20     smallerBoldFont = FontUtils::smallBold();
21     smallerFont = FontUtils::small();
22
23     if (downloadInfo) {
24         progressBar = new QProgressBar(qApp->activeWindow());
25         QPalette palette = progressBar->palette();
26         palette.setColor(QPalette::Window, Qt::transparent);
27         progressBar->setPalette(palette);
28         progressBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
29         progressBar->hide();
30     } else createPlayIcon();
31 }
32
33 void PlaylistItemDelegate::createPlayIcon() {
34     playIcon = QPixmap(THUMB_WIDTH, THUMB_HEIGHT);
35     playIcon.fill(Qt::transparent);
36     QPainter painter(&playIcon);
37     QPolygon polygon;
38     const int hPadding = PADDING*6;
39     const int vPadding = PADDING*2;
40     polygon << QPoint(hPadding, vPadding)
41             << QPoint(THUMB_WIDTH-hPadding, THUMB_HEIGHT/2)
42             << QPoint(hPadding, THUMB_HEIGHT-vPadding);
43     painter.setRenderHints(QPainter::Antialiasing, true);
44     painter.setBrush(Qt::white);
45     QPen pen;
46     pen.setColor(Qt::white);
47     pen.setWidth(PADDING);
48     pen.setJoinStyle(Qt::RoundJoin);
49     pen.setCapStyle(Qt::RoundCap);
50     painter.setPen(pen);
51     painter.drawPolygon(polygon);
52 }
53
54 PlaylistItemDelegate::~PlaylistItemDelegate() { }
55
56 QSize PlaylistItemDelegate::sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const {
57     return QSize( 256, THUMB_HEIGHT+1.0);
58 }
59
60 void PlaylistItemDelegate::paint( QPainter* painter,
61                                 const QStyleOptionViewItem& option, const QModelIndex& index ) const {
62
63     int itemType = index.data(ItemTypeRole).toInt();
64     if (itemType == ItemTypeVideo) {
65         QStyleOptionViewItemV4 opt = QStyleOptionViewItemV4(option);
66         initStyleOption(&opt, index);
67         opt.text = "";
68         opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
69         paintBody(painter, opt, index);
70     } else
71         QStyledItemDelegate::paint( painter, option, index );
72
73 }
74
75 void PlaylistItemDelegate::paintBody( QPainter* painter,
76                                     const QStyleOptionViewItem& option,
77                                     const QModelIndex& index ) const {
78
79     painter->save();
80     painter->translate( option.rect.topLeft() );
81
82
83     QRectF line(0, 0, option.rect.width(), option.rect.height());
84     if (downloadInfo) line.setWidth(line.width() / 2);
85     painter->setClipRect(line);
86
87     const bool isActive = index.data( ActiveTrackRole ).toBool();
88     const bool isSelected = option.state & QStyle::State_Selected;
89
90     // draw the "current track" highlight underneath the text
91     if (isActive && !isSelected) {
92         paintActiveOverlay(painter, line.x(), line.y(), line.width(), line.height());
93     }
94
95     // get the video metadata
96     const VideoPointer videoPointer = index.data( VideoRole ).value<VideoPointer>();
97     const Video *video = videoPointer.data();
98
99     // thumb
100     painter->drawPixmap(0, 0, video->thumbnail());
101
102     // play icon overlayed on the thumb
103     if (isActive)
104         paintPlayIcon(painter);
105
106     // time
107     drawTime(painter, video->formattedDuration(), line);
108
109     if (isActive) painter->setFont(boldFont);
110
111     // text color
112     if (isSelected)
113         painter->setPen(QPen(option.palette.brush(QPalette::HighlightedText), 0));
114     else
115         painter->setPen(QPen(option.palette.brush(QPalette::Text), 0));
116
117     // title
118     QString videoTitle = video->title();
119     QRectF textBox = line.adjusted(PADDING+THUMB_WIDTH, PADDING, -2 * PADDING, -PADDING);
120     textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
121     painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
122
123     painter->setFont(smallerFont);
124
125     // published date
126     QString publishedString = video->published().date().toString(Qt::DefaultLocaleShortDate);
127     QSizeF stringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, publishedString ) );
128     QPointF textLoc(PADDING+THUMB_WIDTH, PADDING*2 + textBox.height());
129     QRectF publishedTextBox(textLoc , stringSize);
130     painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString);
131
132     // author
133     bool authorHovered = false;
134     bool authorPressed = false;
135     const bool isHovered = index.data(HoveredItemRole).toBool();
136     if (isHovered) {
137         authorHovered = index.data(AuthorHoveredRole).toBool();
138         authorPressed = index.data(AuthorPressedRole).toBool();
139     }
140
141     painter->save();
142     painter->setFont(smallerBoldFont);
143     if (!isSelected) {
144         if (authorHovered)
145             painter->setPen(QPen(option.palette.brush(QPalette::Highlight), 0));
146         else
147             painter->setPen(QPen(option.palette.brush(QPalette::Mid), 0));
148     }
149     QString authorString = video->author();
150     textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
151     stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) );
152     QRectF authorTextBox(textLoc , stringSize);
153     authorRects.insert(index.row(), authorTextBox.toRect());
154     painter->drawText(authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString);
155     painter->restore();
156
157     // view count
158     if (video->viewCount() >= 0) {
159         painter->save();
160         QLocale locale;
161         QString viewCountString = tr("%1 views").arg(locale.toString(video->viewCount()));
162         textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
163         stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, viewCountString ) );
164         QRectF viewCountTextBox(textLoc , stringSize);
165         painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, viewCountString);
166         painter->restore();
167     }
168
169     if (downloadInfo) {
170         painter->save();
171         QString definitionString = VideoDefinition::getDefinitionName(video->getDefinitionCode());
172         textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
173         stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, definitionString ) );
174         QRectF viewCountTextBox(textLoc , stringSize);
175         painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, definitionString);
176         painter->restore();
177     }
178
179     /*
180     QLinearGradient myGradient;
181     QPen myPen;
182     QFont myFont;
183     QPointF baseline(authorTextBox.x(), authorTextBox.y() + authorTextBox.height());
184     QPainterPath myPath;
185     myPath.addText(baseline, boldFont, authorString);
186     painter->setBrush(palette.color(QPalette::WindowText));
187     painter->setPen(palette.color(QPalette::Dark));
188     painter->setRenderHints (QPainter::Antialiasing, true);
189     painter->drawPath(myPath);
190     */
191
192     // separator
193     painter->setClipping(false);
194     painter->setPen(option.palette.color(QPalette::Midlight));
195     painter->drawLine(THUMB_WIDTH, THUMB_HEIGHT, option.rect.width(), THUMB_HEIGHT);
196     if (!video->thumbnail().isNull())
197         painter->setPen(Qt::black);
198     painter->drawLine(0, THUMB_HEIGHT, THUMB_WIDTH-1, THUMB_HEIGHT);
199
200     painter->restore();
201
202     if (downloadInfo) paintDownloadInfo(painter, option, index);
203
204 }
205
206 void PlaylistItemDelegate::paintActiveOverlay( QPainter *painter, qreal x, qreal y, qreal w, qreal h ) const {
207
208     QPalette palette;
209     QColor highlightColor = palette.color(QPalette::Highlight);
210     QColor backgroundColor = palette.color(QPalette::Base);
211     const float animation = 0.25;
212     const int gradientRange = 16;
213
214     QColor color2 = QColor::fromHsv(
215             highlightColor.hue(),
216             (int) (backgroundColor.saturation() * (1.0f - animation) + highlightColor.saturation() * animation),
217             (int) (backgroundColor.value() * (1.0f - animation) + highlightColor.value() * animation)
218             );
219     QColor color1 = QColor::fromHsv(
220             color2.hue(),
221             qMax(color2.saturation() - gradientRange, 0),
222             qMin(color2.value() + gradientRange, 255)
223             );
224     QRect rect((int) x, (int) y, (int) w, (int) h);
225     painter->save();
226     painter->setPen(Qt::NoPen);
227     QLinearGradient linearGradient(0, 0, 0, rect.height());
228     linearGradient.setColorAt(0.0, color1);
229     linearGradient.setColorAt(1.0, color2);
230     painter->setBrush(linearGradient);
231     painter->drawRect(rect);
232     painter->restore();
233 }
234
235 void PlaylistItemDelegate::paintPlayIcon(QPainter *painter) const {
236     painter->save();
237     painter->setOpacity(.5);
238     painter->drawPixmap(playIcon.rect(), playIcon);
239     painter->restore();
240 }
241
242 void PlaylistItemDelegate::drawTime(QPainter *painter, QString time, QRectF line) const {
243     static const int timePadding = 4;
244     QRectF textBox = painter->boundingRect(line, Qt::AlignLeft | Qt::AlignTop, time);
245     // add padding
246     textBox.adjust(0, 0, timePadding, 0);
247     // move to bottom right corner of the thumb
248     textBox.translate(THUMB_WIDTH - textBox.width(), THUMB_HEIGHT - textBox.height());
249
250     painter->save();
251     painter->setPen(Qt::NoPen);
252     painter->setBrush(Qt::black);
253     painter->setOpacity(.5);
254     painter->drawRect(textBox);
255     painter->restore();
256
257     painter->save();
258     painter->setPen(Qt::white);
259     painter->drawText(textBox, Qt::AlignCenter, time);
260     painter->restore();
261 }
262
263 void PlaylistItemDelegate::paintDownloadInfo( QPainter* painter,
264                                             const QStyleOptionViewItem& option,
265                                             const QModelIndex& index ) const {
266
267     // get the video metadata
268     const DownloadItemPointer downloadItemPointer = index.data(DownloadItemRole).value<DownloadItemPointer>();
269     const DownloadItem *downloadItem = downloadItemPointer.data();
270
271     painter->save();
272
273     const QRect line(0, 0, option.rect.width() / 2, option.rect.height());
274
275     painter->translate(option.rect.topLeft());
276     painter->translate(line.width(), 0);
277
278     QString message;
279     DownloadItemStatus status = downloadItem->status();
280
281     if (status == Downloading) {
282         QString downloaded = DownloadItem::formattedFilesize(downloadItem->bytesReceived());
283         QString total = DownloadItem::formattedFilesize(downloadItem->bytesTotal());
284         QString speed = DownloadItem::formattedSpeed(downloadItem->currentSpeed());
285         QString eta = DownloadItem::formattedTime(downloadItem->remainingTime());
286
287         message = tr("%1 of %2 (%3) — %4").arg(
288                 downloaded,
289                 total,
290                 speed,
291                 eta
292                 );
293     } else if (status == Starting) {
294         message = tr("Preparing");
295     } else if (status == Failed) {
296         message = tr("Failed") + " — " + downloadItem->errorMessage();
297     } else if (status == Finished) {
298         message = tr("Completed");
299     } else if (status == Idle) {
300         message = tr("Stopped");
301     }
302
303     // progressBar->setPalette(option.palette);
304     if (status == Finished) {
305         progressBar->setValue(100);
306         progressBar->setEnabled(true);
307     } else if (status == Downloading) {
308         progressBar->setValue(downloadItem->currentPercent());
309         progressBar->setEnabled(true);
310     } else {
311         progressBar->setValue(0);
312         progressBar->setEnabled(false);
313     }
314
315     int progressBarWidth = line.width() - PADDING*4 - 16;
316     progressBar->setMaximumWidth(progressBarWidth);
317     progressBar->setMinimumWidth(progressBarWidth);
318     painter->save();
319     painter->translate(PADDING, PADDING);
320     progressBar->render(painter);
321     painter->restore();
322
323     bool downloadButtonHovered = false;
324     bool downloadButtonPressed = false;
325     const bool isHovered = index.data(HoveredItemRole).toBool();
326     if (isHovered) {
327         downloadButtonHovered = index.data(DownloadButtonHoveredRole).toBool();
328         downloadButtonPressed = index.data(DownloadButtonPressedRole).toBool();
329     }
330     QIcon::Mode iconMode;
331     if (downloadButtonPressed) iconMode = QIcon::Selected;
332     else if (downloadButtonHovered) iconMode = QIcon::Active;
333     else iconMode = QIcon::Normal;
334
335     if (status != Finished && status != Failed && status != Idle) {
336         if (downloadButtonHovered) message = tr("Stop downloading");
337         painter->save();
338         QIcon closeIcon = Utils::icon("window-close");
339         painter->drawPixmap(downloadButtonRect(line), closeIcon.pixmap(16, 16, iconMode));
340         painter->restore();
341     }
342
343     else if (status == Finished) {
344         if (downloadButtonHovered)
345 #ifdef APP_MAC
346         message = tr("Show in %1").arg("Finder");
347 #else
348         message = tr("Open parent folder");
349 #endif
350         painter->save();
351         QIcon searchIcon = Utils::icon("system-search");
352         painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
353         painter->restore();
354     }
355
356     else if (status == Failed || status == Idle) {
357         if (downloadButtonHovered) message = tr("Restart downloading");
358         painter->save();
359         QIcon searchIcon = Utils::icon("view-refresh");
360         painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
361         painter->restore();
362     }
363
364     QRectF textBox = line.adjusted(PADDING, PADDING*2 + progressBar->sizeHint().height(), -2 * PADDING, -PADDING);
365     textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
366     painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
367
368     painter->restore();
369
370 }
371
372 QRect PlaylistItemDelegate::downloadButtonRect(QRect line) const {
373     return QRect(
374             line.width() - PADDING*2 - 16,
375             PADDING + progressBar->sizeHint().height() / 2 - 8,
376             16,
377             16);
378 }
379
380 QRect PlaylistItemDelegate::authorRect(const QModelIndex& index) const {
381     return authorRects.value(index.row());
382 }