File indexing completed on 2024-04-28 04:21:21

0001 /* SPDX-FileCopyrightText: 2003-2020 The KPhotoAlbum Development Team
0002 
0003    SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 #include "Delegate.h"
0006 
0007 #include "CellGeometry.h"
0008 #include "ThumbnailModel.h"
0009 #include "ThumbnailWidget.h"
0010 
0011 #include <DB/ImageDB.h>
0012 #include <kpabase/SettingsData.h>
0013 
0014 #include <KLocalizedString>
0015 #include <QPainter>
0016 ThumbnailView::Delegate::Delegate(ThumbnailFactory *factory, QObject *parent)
0017     : QStyledItemDelegate(parent)
0018     , ThumbnailComponent(factory)
0019 {
0020 }
0021 
0022 void ThumbnailView::Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0023 {
0024     paintCellBackground(painter, option.rect);
0025     if (widget()->isGridResizing())
0026         return;
0027 
0028     if (index.data(Qt::DecorationRole).value<QPixmap>().isNull())
0029         return;
0030 
0031     paintCellPixmap(painter, option, index);
0032     paintCellText(painter, option, index);
0033 }
0034 
0035 void ThumbnailView::Delegate::paintCellBackground(QPainter *painter, const QRect &rect) const
0036 {
0037     // we used to paint the cell background here even though it is the same color as the widget background.
0038     // -> now we only paint the grid here if needed
0039     if (widget()->isGridResizing() || Settings::SettingsData::instance()->thumbnailDisplayGrid()) {
0040         painter->setPen(widget()->palette().shadow().color());
0041         // left and right of frame
0042         painter->drawLine(rect.right(), rect.top(), rect.right(), rect.bottom());
0043 
0044         // bottom line
0045         painter->drawLine(rect.left(), rect.bottom(), rect.right(), rect.bottom());
0046     }
0047 }
0048 
0049 void ThumbnailView::Delegate::paintCellPixmap(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0050 {
0051     const QPixmap pixmap = index.data(Qt::DecorationRole).value<QPixmap>();
0052 
0053     const QRect pixmapRect = cellGeometryInfo()->iconGeometry(pixmap).translated(option.rect.topLeft());
0054     paintBoundingRect(painter, pixmapRect, index);
0055     painter->drawPixmap(pixmapRect, pixmap);
0056     paintVideoInfo(painter, pixmapRect, index);
0057     paintDropIndicator(painter, option.rect, index);
0058     paintStackedIndicator(painter, pixmapRect, index);
0059 
0060     // Paint transparent pixels over the widget for selection.
0061     const QItemSelectionModel *selectionModel = widget()->selectionModel();
0062     QColor selectionColor = widget()->palette().highlight().color();
0063     selectionColor.setAlpha(127);
0064     if (selectionModel->isSelected(index))
0065         painter->fillRect(option.rect, selectionColor);
0066     else if (selectionModel->hasSelection() && selectionModel->currentIndex() == index)
0067         painter->fillRect(option.rect, selectionColor);
0068 }
0069 
0070 void ThumbnailView::Delegate::paintVideoInfo(QPainter *painter, const QRect &pixmapRect, const QModelIndex &index) const
0071 {
0072     const auto fileName = model()->imageAt(index.row());
0073     const DB::ImageInfoPtr imageInfo = DB::ImageDB::instance()->info(fileName);
0074     if (!imageInfo || imageInfo->mediaType() != DB::Video)
0075         return;
0076 
0077     const QString text = videoLengthText(imageInfo);
0078     const QRect metricsRect = painter->fontMetrics().boundingRect(text);
0079 
0080     const int margin = 3;
0081     const QRect textRect = QRect(pixmapRect.right() - metricsRect.width() - 2 * margin,
0082                                  pixmapRect.bottom() - metricsRect.height() - margin,
0083                                  metricsRect.width() + margin, metricsRect.height());
0084     const QRect backgroundRect = textRect.adjusted(-margin, -margin, margin, margin);
0085 
0086     if (backgroundRect.width() > pixmapRect.width() / 2) {
0087         // Don't show the time if the box would fill more than half the thumbnail
0088         return;
0089     }
0090 
0091     painter->save();
0092     QColor bgColor = widget()->palette().shadow().color();
0093     bgColor.setAlpha(128);
0094     painter->fillRect(backgroundRect, QBrush(bgColor));
0095     painter->setPen(widget()->palette().brightText().color());
0096     painter->drawText(textRect, Qt::TextDontClip, text);
0097     painter->restore();
0098 }
0099 
0100 void ThumbnailView::Delegate::paintCellText(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0101 {
0102     // Optimization based on result from KCacheGrind
0103     if (!Settings::SettingsData::instance()->displayLabels() && !Settings::SettingsData::instance()->displayCategories())
0104         return;
0105 
0106     DB::FileName fileName = model()->imageAt(index.row());
0107     if (fileName.isNull())
0108         return;
0109 
0110     QString title = index.data(Qt::DisplayRole).value<QString>();
0111     QRect rect = cellGeometryInfo()->cellTextGeometry();
0112     painter->setPen(widget()->palette().text().color());
0113 
0114     // Qt::TextWordWrap just in case, if the text's width is wider than the cell's width
0115     painter->drawText(rect.translated(option.rect.topLeft()), Qt::AlignCenter | Qt::TextWordWrap, title);
0116 }
0117 
0118 QSize ThumbnailView::Delegate::sizeHint(const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const
0119 {
0120     return cellGeometryInfo()->cellSize();
0121 }
0122 
0123 /**
0124    This will paint the pixels around the thumbnail, which gives it a 3D
0125    effect, and also which indicates current image and selection state.
0126    The colors are fetched from looking at the Gwenview. I tried to see if I
0127    could figure out from the code how it was drawn, but failed at doing so.
0128 */
0129 void ThumbnailView::Delegate::paintBoundingRect(QPainter *painter, const QRect &pixmapRect, const QModelIndex &index) const
0130 {
0131     QRect rect = pixmapRect;
0132     rect.adjust(-5, -5, 4, 4);
0133     for (int i = 4; i >= 0; --i) {
0134         QColor color;
0135         if (widget()->selectionModel()->currentIndex() == index) {
0136             // a factor of 100 means same brightness, 200 = half the brightness
0137             static int factors[5] = { 123, 74, 72, 70, 129 };
0138             color = widget()->palette().highlight().color().darker(factors[i]);
0139         } else if (widget()->selectionModel()->isSelected(index)) {
0140             // a factor of 100 means same brightness, 200 = half the brightness
0141             static int factors[5] = { 177, 107, 104, 100, 185 };
0142             color = widget()->palette().highlight().color().darker(factors[i]);
0143         } else {
0144             // Originally I just painted the outline using drawRect, but that turned out to be a huge bottleneck.
0145             // The code was therefore converted to fillRect, which was much faster.
0146             // This code was complicted from that, as I previously drew the
0147             // rects from insite out, but with fillRect that doesn't work,
0148             // and I thefore had to rewrite the code to draw the rects from
0149             // outside in.
0150             // I now had to calculate the destination color myself, rather
0151             // than rely on drawing with a transparent color on top of the
0152             // background.
0153             // 12 Aug. 2010 17:38 -- Jesper K. Pedersen
0154             const QColor foreground = widget()->palette().shadow().color();
0155             const QColor background = widget()->palette().base().color();
0156 
0157             double alpha = (0.5 - 0.1 * i);
0158             double inverseAlpha = 1 - alpha;
0159 
0160             color = QColor(int(foreground.red() * alpha + background.red() * inverseAlpha),
0161                            int(foreground.green() * alpha + background.green() * inverseAlpha),
0162                            int(foreground.blue() * alpha + background.blue() * inverseAlpha));
0163         }
0164 
0165         QPen pen(color);
0166         painter->setPen(pen);
0167         painter->fillRect(rect, QBrush(color));
0168         rect.adjust(1, 1, -1, -1);
0169     }
0170 }
0171 
0172 static DB::StackID getStackId(const DB::FileName &fileName)
0173 {
0174     return DB::ImageDB::instance()->info(fileName)->stackId();
0175 }
0176 
0177 void ThumbnailView::Delegate::paintStackedIndicator(QPainter *painter, const QRect &pixmapRect, const QModelIndex &index) const
0178 {
0179     const auto fileName = model()->imageAt(index.row());
0180     const DB::ImageInfoPtr imageInfo = DB::ImageDB::instance()->info(fileName);
0181     if (!imageInfo || !imageInfo->isStacked())
0182         return;
0183 
0184     const QRect cellRect = widget()->visualRect(index);
0185 
0186     // Calculate the three points for the bottom most/right most lines
0187     int leftX = cellRect.left();
0188     int rightX = cellRect.right() + 5; // 5 for the 3D effect
0189 
0190     if (isFirst(index.row()))
0191         leftX = pixmapRect.left() + pixmapRect.width() / 2;
0192 
0193     if (isLast(index.row()))
0194         rightX = pixmapRect.right();
0195 
0196     QPoint bottomLeftPoint(leftX, pixmapRect.bottom());
0197     QPoint bottomRightPoint(rightX, pixmapRect.bottom());
0198     QPoint topPoint = isLast(index.row()) ? QPoint(rightX, pixmapRect.top() + pixmapRect.height() / 2) : QPoint();
0199 
0200     // Paint the lines.
0201     painter->save();
0202     const QColor lineColor = widget()->palette().shadow().color();
0203     const QColor alternateColor = widget()->palette().brightText().color();
0204     for (int i = 0; i < 8; ++i) {
0205         painter->setPen(QPen(i % 2 == 0 ? lineColor : alternateColor));
0206 
0207         painter->drawLine(bottomLeftPoint, bottomRightPoint);
0208         if (topPoint != QPoint()) {
0209             painter->drawLine(bottomRightPoint, topPoint);
0210             topPoint -= QPoint(1, 1);
0211         }
0212 
0213         bottomLeftPoint -= QPoint(isFirst(index.row()) ? 1 : 0, 1);
0214         bottomRightPoint -= QPoint(isLast(index.row()) ? 1 : 0, 1);
0215     }
0216     painter->restore();
0217 }
0218 
0219 bool ThumbnailView::Delegate::isFirst(int row) const
0220 {
0221     const DB::StackID curId = getStackId(model()->imageAt(row));
0222 
0223     return !model()->isItemInExpandedStack(curId) || row == 0 || getStackId(model()->imageAt(row - 1)) != curId;
0224 }
0225 
0226 bool ThumbnailView::Delegate::isLast(int row) const
0227 {
0228     const DB::StackID curId = getStackId(model()->imageAt(row));
0229 
0230     return !model()->isItemInExpandedStack(curId) || row == model()->imageCount() - 1 || getStackId(model()->imageAt(row + 1)) != curId;
0231 }
0232 
0233 QString ThumbnailView::Delegate::videoLengthText(const DB::ImageInfoPtr &imageInfo) const
0234 {
0235     const int length = imageInfo->videoLength();
0236     if (length < 0)
0237         return i18nc("No video length could be determined, so we just display 'video' instead of the video length.", "video");
0238 
0239     const int hours = length / 60 / 60;
0240     const int minutes = (length / 60) % 60;
0241     const int secs = length % 60;
0242 
0243     QString res;
0244     if (hours > 0)
0245         res = QString::number(hours) + QLatin1String(":");
0246 
0247     if (minutes < 10 && hours > 0)
0248         res += QLatin1String("0");
0249 
0250     res += QString::number(minutes);
0251     res += QLatin1String(":");
0252 
0253     if (secs < 10)
0254         res += QLatin1String("0");
0255 
0256     res += QString::number(secs);
0257 
0258     return res;
0259 }
0260 
0261 void ThumbnailView::Delegate::paintDropIndicator(QPainter *painter, const QRect &rect, const QModelIndex &index) const
0262 {
0263     const DB::FileName fileName = model()->imageAt(index.row());
0264 
0265     if (model()->leftDropItem() == fileName)
0266         painter->fillRect(rect.left(), rect.top(), 3, rect.height(), QBrush(Qt::red));
0267 
0268     else if (model()->rightDropItem() == fileName)
0269         painter->fillRect(rect.right() - 2, rect.top(), 3, rect.height(), QBrush(Qt::red));
0270 }
0271 
0272 // vi:expandtab:tabstop=4 shiftwidth=4: