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