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: