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