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