+/* $BEGIN_LICENSE
+
+This file is part of Minitube.
+Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
+
+Minitube is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Minitube is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Minitube. If not, see <http://www.gnu.org/licenses/>.
+
+$END_LICENSE */
+
#include "playlistitemdelegate.h"
#include "playlistmodel.h"
#include "fontutils.h"
#include "downloaditem.h"
-#include "utils.h"
+#include "iconutils.h"
#include "videodefinition.h"
#include "video.h"
+#include "datautils.h"
+
+const int PlaylistItemDelegate::THUMB_HEIGHT = 90;
+const int PlaylistItemDelegate::THUMB_WIDTH = 160;
+const int PlaylistItemDelegate::PADDING = 10;
+
+namespace {
-const qreal PlaylistItemDelegate::THUMB_HEIGHT = 90.0;
-const qreal PlaylistItemDelegate::THUMB_WIDTH = 120.0;
-const qreal PlaylistItemDelegate::PADDING = 10.0;
+void drawElidedText(QPainter *painter, const QRect &textBox, const int flags, const QString &text) {
+ QString elidedText = QFontMetrics(painter->font()).elidedText(text, Qt::ElideRight, textBox.width(), flags);
+ painter->drawText(textBox, 0, elidedText);
+}
-QRect lastAuthorRect;
-QHash<int, QRect> authorRects;
+}
PlaylistItemDelegate::PlaylistItemDelegate(QObject* parent, bool downloadInfo)
: QStyledItemDelegate(parent),
- downloadInfo(downloadInfo) {
+ downloadInfo(downloadInfo),
+ progressBar(0) {
+
boldFont.setBold(true);
smallerBoldFont = FontUtils::smallBold();
smallerFont = FontUtils::small();
} else createPlayIcon();
}
+PlaylistItemDelegate::~PlaylistItemDelegate() {
+ if (progressBar) delete progressBar;
+}
+
void PlaylistItemDelegate::createPlayIcon() {
- playIcon = QPixmap(THUMB_WIDTH, THUMB_HEIGHT);
+ qreal maxRatio = IconUtils::maxSupportedPixelRatio();
+ playIcon = QPixmap(THUMB_WIDTH * maxRatio, THUMB_HEIGHT * maxRatio);
+ playIcon.setDevicePixelRatio(maxRatio);
playIcon.fill(Qt::transparent);
- QPainter painter(&playIcon);
- QPolygon polygon;
- polygon << QPoint(PADDING*4, PADDING*2)
- << QPoint(THUMB_WIDTH-PADDING*4, THUMB_HEIGHT/2)
- << QPoint(PADDING*4, THUMB_HEIGHT-PADDING*2);
+
+ QPixmap tempPixmap(THUMB_WIDTH * maxRatio, THUMB_HEIGHT * maxRatio);
+ tempPixmap.setDevicePixelRatio(maxRatio);
+ tempPixmap.fill(Qt::transparent);
+ QPainter painter(&tempPixmap);
painter.setRenderHints(QPainter::Antialiasing, true);
+
+ const int hPadding = PADDING*6;
+ const int vPadding = PADDING*2;
+
+ QPolygon polygon;
+ polygon << QPoint(hPadding, vPadding)
+ << QPoint(THUMB_WIDTH-hPadding, THUMB_HEIGHT/2)
+ << QPoint(hPadding, THUMB_HEIGHT-vPadding);
painter.setBrush(Qt::white);
QPen pen;
pen.setColor(Qt::white);
pen.setCapStyle(Qt::RoundCap);
painter.setPen(pen);
painter.drawPolygon(polygon);
-}
+ painter.end();
-PlaylistItemDelegate::~PlaylistItemDelegate() { }
+ QPainter painter2(&playIcon);
+ painter2.setOpacity(.75);
+ painter2.drawPixmap(0, 0, tempPixmap);
+}
QSize PlaylistItemDelegate::sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const {
- return QSize( 256, THUMB_HEIGHT+1.0);
+ return QSize(THUMB_WIDTH, THUMB_HEIGHT + 1);
}
void PlaylistItemDelegate::paint( QPainter* painter,
- const QStyleOptionViewItem& option, const QModelIndex& index ) const {
+ const QStyleOptionViewItem& option, const QModelIndex& index ) const {
int itemType = index.data(ItemTypeRole).toInt();
if (itemType == ItemTypeVideo) {
}
void PlaylistItemDelegate::paintBody( QPainter* painter,
- const QStyleOptionViewItem& option,
- const QModelIndex& index ) const {
-
+ const QStyleOptionViewItem& option,
+ const QModelIndex& index ) const {
painter->save();
painter->translate( option.rect.topLeft() );
-
- QRectF line(0, 0, option.rect.width(), option.rect.height());
+ QRect line(0, 0, option.rect.width(), option.rect.height());
if (downloadInfo) line.setWidth(line.width() / 2);
- painter->setClipRect(line);
const bool isActive = index.data( ActiveTrackRole ).toBool();
const bool isSelected = option.state & QStyle::State_Selected;
+ // get the video metadata
+ const Video *video = index.data(VideoRole).value<VideoPointer>().data();
+
// draw the "current track" highlight underneath the text
- if (isActive && !isSelected) {
- paintActiveOverlay(painter, line.x(), line.y(), line.width(), line.height());
- }
+ if (isActive && !isSelected)
+ paintActiveOverlay(painter, option, line);
- // get the video metadata
- const VideoPointer videoPointer = index.data( VideoRole ).value<VideoPointer>();
- const Video *video = videoPointer.data();
+ // separator
+ painter->setPen(option.palette.color(QPalette::Midlight));
+ painter->drawLine(THUMB_WIDTH, THUMB_HEIGHT, option.rect.width(), THUMB_HEIGHT);
+ if (!video->thumbnail().isNull())
+ painter->setPen(Qt::black);
+ painter->drawLine(0, THUMB_HEIGHT, THUMB_WIDTH-1, THUMB_HEIGHT);
// thumb
- painter->drawPixmap(0, 0, THUMB_WIDTH, THUMB_HEIGHT, video->thumbnail());
+ painter->drawPixmap(0, 0, video->thumbnail());
// play icon overlayed on the thumb
if (isActive)
- paintPlayIcon(painter);
+ painter->drawPixmap(0, 0, playIcon);
// time
- drawTime(painter, video->formattedDuration(), line);
-
- if (isActive) painter->setFont(boldFont);
-
- // text color
- if (isSelected)
- painter->setPen(QPen(option.palette.brush(QPalette::HighlightedText), 0));
- else
- painter->setPen(QPen(option.palette.brush(QPalette::Text), 0));
-
- // title
- QString videoTitle = video->title();
- QRectF textBox = line.adjusted(PADDING+THUMB_WIDTH, PADDING, -2 * PADDING, -PADDING);
- textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
- painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
-
- painter->setFont(smallerFont);
-
- // published date
- QString publishedString = video->published().date().toString(Qt::DefaultLocaleShortDate);
- QSizeF stringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, publishedString ) );
- QPointF textLoc(PADDING+THUMB_WIDTH, PADDING*2 + textBox.height());
- QRectF publishedTextBox(textLoc , stringSize);
- painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString);
-
- // author
- bool authorHovered = false;
- bool authorPressed = false;
- const bool isHovered = index.data(HoveredItemRole).toBool();
- if (isHovered) {
- authorHovered = index.data(AuthorHoveredRole).toBool();
- authorPressed = index.data(AuthorPressedRole).toBool();
- }
+ if (video->duration() > 0)
+ drawTime(painter, video->formattedDuration(), line);
- painter->save();
- painter->setFont(smallerBoldFont);
- if (!isSelected) {
- if (authorHovered)
- painter->setPen(QPen(option.palette.brush(QPalette::Highlight), 0));
+ if (line.width() > THUMB_WIDTH + 60) {
+
+ // if (isActive) painter->setFont(boldFont);
+
+ // text color
+ if (isSelected)
+ painter->setPen(QPen(option.palette.highlightedText(), 0));
else
- painter->setPen(QPen(option.palette.brush(QPalette::Mid), 0));
- }
- QString authorString = video->author();
- textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
- stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) );
- QRectF authorTextBox(textLoc , stringSize);
- authorRects.insert(index.row(), authorTextBox.toRect());
- painter->drawText(authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString);
- painter->restore();
+ painter->setPen(QPen(option.palette.text(), 0));
+
+ // title
+ QString videoTitle = video->title();
+ QString v = videoTitle;
+ const int flags = Qt::AlignTop | Qt::TextWordWrap;
+ QRect textBox = line.adjusted(PADDING+THUMB_WIDTH, PADDING, 0, 0);
+ textBox = painter->boundingRect(textBox, flags, v);
+ while (textBox.height() > 55 && v.length() > 10) {
+ videoTitle.truncate(videoTitle.length() - 1);
+ v = videoTitle;
+ v = v.trimmed().append("...");
+ textBox = painter->boundingRect(textBox, flags, v);
+ }
+ painter->drawText(textBox, flags, v);
+
+ painter->setFont(smallerFont);
+
+ // published date
+ QString publishedString = DataUtils::formatDateTime(video->published());
+ QSize stringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, publishedString ) );
+ QPoint textLoc(PADDING+THUMB_WIDTH, PADDING*2 + textBox.height());
+ QRect publishedTextBox(textLoc , stringSize);
+ painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString);
+
+ // author
+ bool authorHovered = false;
+ bool authorPressed = false;
+ const bool isHovered = index.data(HoveredItemRole).toBool();
+ if (isHovered) {
+ authorHovered = index.data(AuthorHoveredRole).toBool();
+ authorPressed = index.data(AuthorPressedRole).toBool();
+ }
- // view count
- if (video->viewCount() >= 0) {
painter->save();
- QLocale locale;
- QString viewCountString = tr("%1 views").arg(locale.toString(video->viewCount()));
+ painter->setFont(smallerBoldFont);
+ if (!isSelected) {
+ if (authorHovered)
+ painter->setPen(QPen(option.palette.brush(QPalette::Highlight), 0));
+ else
+ painter->setOpacity(.5);
+ }
+ const QString &authorString = video->channelTitle();
textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
- stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, viewCountString ) );
- QRectF viewCountTextBox(textLoc , stringSize);
- painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, viewCountString);
+ stringSize = QSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) );
+ QRect authorTextBox(textLoc , stringSize);
+ authorRects.insert(index.row(), authorTextBox);
+ if (authorTextBox.right() > line.width()) authorTextBox.setRight(line.width());
+ drawElidedText(painter, authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString);
painter->restore();
- }
- if (downloadInfo) {
- painter->save();
- QString definitionString = VideoDefinition::getDefinitionName(video->getDefinitionCode());
- textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
- stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, definitionString ) );
- QRectF viewCountTextBox(textLoc , stringSize);
- painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, definitionString);
- painter->restore();
- }
+ // view count
+ if (video->viewCount() >= 0) {
+ QLocale locale;
+ QString viewCountString = tr("%1 views").arg(locale.toString(video->viewCount()));
+ textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
+ stringSize = QSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, viewCountString ) );
+ QRect viewCountTextBox(textLoc , stringSize);
+ if (viewCountTextBox.right() > line.width()) viewCountTextBox.setRight(line.width());
+ drawElidedText(painter, viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, viewCountString);
+ }
+
+ if (downloadInfo) {
+ const QString definitionString = VideoDefinition::getDefinitionFor(video->getDefinitionCode()).getName();
+ textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
+ stringSize = QSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, definitionString ) );
+ QRect viewCountTextBox(textLoc , stringSize);
+ painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, definitionString);
+ }
- /*
- QLinearGradient myGradient;
- QPen myPen;
- QFont myFont;
- QPointF baseline(authorTextBox.x(), authorTextBox.y() + authorTextBox.height());
- QPainterPath myPath;
- myPath.addText(baseline, boldFont, authorString);
- painter->setBrush(palette.color(QPalette::WindowText));
- painter->setPen(palette.color(QPalette::Dark));
- painter->setRenderHints (QPainter::Antialiasing, true);
- painter->drawPath(myPath);
- */
+ } else {
- // separator
- painter->setClipping(false);
- painter->setPen(option.palette.color(QPalette::Midlight));
- painter->drawLine(THUMB_WIDTH, THUMB_HEIGHT, option.rect.width(), THUMB_HEIGHT);
- if (!video->thumbnail().isNull())
- painter->setPen(Qt::black);
- painter->drawLine(0, THUMB_HEIGHT, THUMB_WIDTH-1, THUMB_HEIGHT);
+ const bool isHovered = index.data(HoveredItemRole).toBool();
+ if (!isActive && isHovered) {
+ painter->setFont(smallerFont);
+ painter->setPen(Qt::white);
+ QString videoTitle = video->title();
+ QString v = videoTitle;
+ const int flags = Qt::AlignTop | Qt::TextWordWrap;
+ QRect textBox(PADDING, PADDING, THUMB_WIDTH - PADDING*2, THUMB_HEIGHT - PADDING*2);
+ textBox = painter->boundingRect(textBox, flags, v);
+ while (textBox.height() > THUMB_HEIGHT && v.length() > 10) {
+ videoTitle.truncate(videoTitle.length() - 1);
+ v = videoTitle;
+ v = v.trimmed().append("...");
+ textBox = painter->boundingRect(textBox, flags, v);
+ }
+ painter->fillRect(QRect(0, 0, THUMB_WIDTH, textBox.height() + PADDING*2), QColor(0, 0, 0, 128));
+ painter->drawText(textBox, flags, v);
+ }
+
+ }
painter->restore();
}
-void PlaylistItemDelegate::paintActiveOverlay( QPainter *painter, qreal x, qreal y, qreal w, qreal h ) const {
-
- QPalette palette;
- QColor highlightColor = palette.color(QPalette::Highlight);
- QColor backgroundColor = palette.color(QPalette::Base);
- const float animation = 0.25;
- const int gradientRange = 16;
-
- QColor color2 = QColor::fromHsv(
- highlightColor.hue(),
- (int) (backgroundColor.saturation() * (1.0f - animation) + highlightColor.saturation() * animation),
- (int) (backgroundColor.value() * (1.0f - animation) + highlightColor.value() * animation)
- );
- QColor color1 = QColor::fromHsv(
- color2.hue(),
- qMax(color2.saturation() - gradientRange, 0),
- qMin(color2.value() + gradientRange, 255)
- );
- QRect rect((int) x, (int) y, (int) w, (int) h);
+void PlaylistItemDelegate::paintActiveOverlay(QPainter *painter, const QStyleOptionViewItem& option, const QRect &line) const {
painter->save();
- painter->setPen(Qt::NoPen);
- QLinearGradient linearGradient(0, 0, 0, rect.height());
- linearGradient.setColorAt(0.0, color1);
- linearGradient.setColorAt(1.0, color2);
- painter->setBrush(linearGradient);
- painter->drawRect(rect);
+ painter->setOpacity(.1);
+ painter->fillRect(line, option.palette.highlight());
painter->restore();
}
-void PlaylistItemDelegate::paintPlayIcon(QPainter *painter) const {
- painter->save();
- painter->setOpacity(.5);
- painter->drawPixmap(playIcon.rect(), playIcon);
- painter->restore();
-}
-
-void PlaylistItemDelegate::drawTime(QPainter *painter, QString time, QRectF line) const {
+void PlaylistItemDelegate::drawTime(QPainter *painter, const QString &time, const QRect &line) const {
static const int timePadding = 4;
- QRectF textBox = painter->boundingRect(line, Qt::AlignLeft | Qt::AlignTop, time);
+ QRect textBox = painter->boundingRect(line, Qt::AlignLeft | Qt::AlignTop, time);
// add padding
textBox.adjust(0, 0, timePadding, 0);
// move to bottom right corner of the thumb
}
void PlaylistItemDelegate::paintDownloadInfo( QPainter* painter,
- const QStyleOptionViewItem& option,
- const QModelIndex& index ) const {
+ const QStyleOptionViewItem& option,
+ const QModelIndex& index ) const {
// get the video metadata
const DownloadItemPointer downloadItemPointer = index.data(DownloadItemRole).value<DownloadItemPointer>();
QString eta = DownloadItem::formattedTime(downloadItem->remainingTime());
message = tr("%1 of %2 (%3) — %4").arg(
- downloaded,
- total,
- speed,
- eta
- );
+ downloaded,
+ total,
+ speed,
+ eta
+ );
} else if (status == Starting) {
message = tr("Preparing");
} else if (status == Failed) {
if (status != Finished && status != Failed && status != Idle) {
if (downloadButtonHovered) message = tr("Stop downloading");
painter->save();
- QIcon closeIcon = Utils::icon("window-close");
+ QIcon closeIcon = IconUtils::icon("window-close");
painter->drawPixmap(downloadButtonRect(line), closeIcon.pixmap(16, 16, iconMode));
painter->restore();
}
else if (status == Finished) {
if (downloadButtonHovered)
#ifdef APP_MAC
- message = tr("Show in %1").arg("Finder");
+ message = tr("Show in %1").arg("Finder");
#else
- message = tr("Open parent folder");
+ message = tr("Open parent folder");
#endif
painter->save();
- QIcon searchIcon = Utils::icon("system-search");
+ QIcon searchIcon = IconUtils::icon("system-search");
painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
painter->restore();
}
else if (status == Failed || status == Idle) {
if (downloadButtonHovered) message = tr("Restart downloading");
painter->save();
- QIcon searchIcon = Utils::icon("view-refresh");
+ QIcon searchIcon = IconUtils::icon("view-refresh");
painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
painter->restore();
}
- QRectF textBox = line.adjusted(PADDING, PADDING*2 + progressBar->sizeHint().height(), -2 * PADDING, -PADDING);
+ QRect textBox = line.adjusted(PADDING, PADDING*2 + progressBar->sizeHint().height(), -2 * PADDING, -PADDING);
textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
painter->restore();
-
}
-QRect PlaylistItemDelegate::downloadButtonRect(QRect line) const {
+QRect PlaylistItemDelegate::downloadButtonRect(const QRect &line) const {
return QRect(
- line.width() - PADDING*2 - 16,
- PADDING + progressBar->sizeHint().height() / 2 - 8,
- 16,
- 16);
+ line.width() - PADDING*2 - 16,
+ PADDING + progressBar->sizeHint().height() / 2 - 8,
+ 16,
+ 16);
}
QRect PlaylistItemDelegate::authorRect(const QModelIndex& index) const {