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