File indexing completed on 2025-01-19 03:56:15

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-07-15
0007  * Description : Item delegate for image versions list view
0008  *
0009  * SPDX-FileCopyrightText: 2010-2011 by Martin Klapetek <martin dot klapetek at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "versionsdelegate.h"
0016 
0017 // Qt includes
0018 
0019 #include <QApplication>
0020 #include <QPainter>
0021 #include <QPropertyAnimation>
0022 #include <QStyle>
0023 #include <QStyleOptionViewItem>
0024 
0025 // KDE includes
0026 
0027 #include <klocalizedstring.h>
0028 
0029 // Local includes
0030 
0031 #include "digikam_debug.h"
0032 #include "itemdelegate.h"
0033 #include "itemhistorygraphmodel.h"
0034 #include "itemversionsmodel.h"
0035 #include "thumbnailloadthread.h"
0036 #include "dcategorydrawer.h"
0037 #include "dlayoutbox.h"
0038 #include "dworkingpixmap.h"
0039 
0040 namespace Digikam
0041 {
0042 
0043 class Q_DECL_HIDDEN VersionsDelegate::Private
0044 {
0045 public:
0046 
0047     explicit Private()
0048         : categoryExtraSpacing  (6),
0049           filterItemExtraSpacing(4),
0050           animationState        (0),
0051           animation             (nullptr),
0052           workingPixmap         (nullptr),
0053           categoryDrawer        (nullptr),
0054           thumbnailSize         (64),
0055           thumbsWaitingFor      (0),
0056           inSizeHint            (false)
0057     {
0058     }
0059 
0060     const int                     categoryExtraSpacing;
0061     const int                     filterItemExtraSpacing;
0062 
0063     int                           animationState;
0064     QPropertyAnimation*           animation;
0065     DWorkingPixmap*               workingPixmap;
0066     DCategoryDrawer*              categoryDrawer;
0067     int                           thumbnailSize;
0068 
0069     int                           thumbsWaitingFor;
0070     bool                          inSizeHint;
0071 
0072 public:
0073 
0074     inline const QWidget* widget(const QStyleOptionViewItem& option)
0075     {
0076         if (const QStyleOptionViewItem* v3 = qstyleoption_cast<const QStyleOptionViewItem*>(&option))
0077         {
0078             return v3->widget;
0079         }
0080 
0081         return nullptr;
0082     }
0083 
0084     inline const QStyle* style(const QStyleOptionViewItem& option)
0085     {
0086         const QWidget* w = widget(option);
0087 
0088         return (w ? w->style() : QApplication::style());
0089     }
0090 };
0091 
0092 VersionsDelegate::VersionsDelegate(QObject* const parent)
0093     : QStyledItemDelegate(parent),
0094       d                  (new Private)
0095 {
0096     d->workingPixmap  = new DWorkingPixmap(this);
0097     d->categoryDrawer = new DCategoryDrawer(nullptr);
0098     d->animation      = new QPropertyAnimation(this, "animationState", this);
0099     d->animation->setStartValue(0);
0100     d->animation->setEndValue(d->workingPixmap->frameCount() - 1);
0101     d->animation->setDuration(100 * d->workingPixmap->frameCount());
0102     d->animation->setLoopCount(-1);
0103 }
0104 
0105 VersionsDelegate::~VersionsDelegate()
0106 {
0107     delete d->categoryDrawer;
0108     delete d;
0109 }
0110 
0111 int VersionsDelegate::animationState() const
0112 {
0113     return d->animationState;
0114 }
0115 
0116 void VersionsDelegate::setAnimationState(int animationState)
0117 {
0118     if (d->animationState == animationState)
0119     {
0120         return;
0121     }
0122 
0123     d->animationState = animationState;
0124     Q_EMIT animationStateChanged();
0125 }
0126 
0127 void VersionsDelegate::setThumbnailSize(int size) const
0128 {
0129     d->thumbnailSize = size;
0130 }
0131 
0132 int VersionsDelegate::thumbnailSize() const
0133 {
0134     return d->thumbnailSize;
0135 }
0136 
0137 void VersionsDelegate::beginPainting()
0138 {
0139     d->thumbsWaitingFor = 0;
0140 }
0141 
0142 void VersionsDelegate::finishPainting()
0143 {
0144 /*
0145     qCDebug(DIGIKAM_GENERAL_LOG) << "painting finished" << d->thumbsWaitingFor;
0146 */
0147     if (d->thumbsWaitingFor)
0148     {
0149         d->animation->start();
0150     }
0151     else
0152     {
0153         d->animation->stop();
0154     }
0155 }
0156 
0157 QSize VersionsDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
0158 {
0159     if      (index.data(ItemHistoryGraphModel::IsImageItemRole).toBool())
0160     {
0161         d->inSizeHint = true;
0162         QSize size    = QStyledItemDelegate::sizeHint(option, index);
0163         d->inSizeHint = false;
0164 
0165         return size;
0166     }
0167     else if (index.data(ItemHistoryGraphModel::IsFilterActionItemRole).toBool())
0168     {
0169         QSize size = QStyledItemDelegate::sizeHint(option, index);
0170         size      += QSize(0, d->filterItemExtraSpacing);
0171 
0172         return size;
0173     }
0174     else if (index.data(ItemHistoryGraphModel::IsCategoryItemRole).toBool())
0175     {
0176         int height = d->categoryDrawer->categoryHeight(index, option) + d->categoryExtraSpacing;
0177         QSize size = QStyledItemDelegate::sizeHint(option, index);
0178 
0179         return size.expandedTo(QSize(0, height));
0180     }
0181     else if (index.data(ItemHistoryGraphModel::IsSeparatorItemRole).toBool())
0182     {
0183         //int pm = d->style(option)->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, d->widget(option));
0184         int pm = d->style(option)->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, nullptr, d->widget(option));
0185         //int spacing = d->style(option)->pixelMetric(QStyle::PM_LayoutVerticalSpacing, &option);
0186 
0187         return QSize(1, pm);
0188     }
0189     else
0190     {
0191         return QStyledItemDelegate::sizeHint(option, index);
0192     }
0193 }
0194 
0195 void VersionsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
0196 {
0197     if      (index.data(ItemHistoryGraphModel::IsCategoryItemRole).toBool())
0198     {
0199         QStyleOption opt = option;
0200         opt.rect.adjust(d->categoryExtraSpacing / 2, d->categoryExtraSpacing / 2, - d->categoryExtraSpacing / 2, 0);
0201 
0202         // purpose of sortRole is unclear, give Qt::DisplayRole
0203 
0204         d->categoryDrawer->drawCategory(index, Qt::DisplayRole, opt, painter);
0205     }
0206     else if (index.data(ItemHistoryGraphModel::IsSeparatorItemRole).toBool())
0207     {
0208         d->style(option)->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &option, painter, d->widget(option));
0209     }
0210     else
0211     {
0212         QStyledItemDelegate::paint(painter, option, index);
0213         return;
0214 /*
0215         if (index.data(ItemHistoryGraphModel::IsSubjectImageRole).toBool())
0216         {
0217             // draw 1px border
0218             QPen oldPen = painter->pen();
0219             QPen pen(option.palette.windowText(), 0);
0220             painter->setPen(pen);
0221             painter->drawRect(option.rect);
0222             painter->setPen(oldPen);
0223         }
0224 */
0225     }
0226 }
0227 
0228 void VersionsDelegate::initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const
0229 {
0230     QStyledItemDelegate::initStyleOption(option, index);
0231 
0232     // Don't show the separator-like focus indicator
0233 
0234     option->state &= ~QStyle::State_HasFocus;
0235 
0236     if (!index.data(ItemHistoryGraphModel::IsImageItemRole).toBool())
0237     {
0238         return;
0239     }
0240 /*
0241     if (index.data(ItemHistoryGraphModel::IsSubjectImageRole).toBool())
0242     {
0243         option->font.setWeight(QFont::Bold);
0244     }
0245 */
0246     option->font.setWeight(QFont::Bold);
0247 
0248     if (QStyleOptionViewItem* const v4 = qstyleoption_cast<QStyleOptionViewItem*>(option))
0249     {
0250         v4->features |= QStyleOptionViewItem::HasDecoration;
0251 
0252         if (d->inSizeHint)
0253         {
0254             v4->decorationSize = QSize(d->thumbnailSize, d->thumbnailSize);
0255         }
0256         else
0257         {
0258             QPixmap pix = ItemDelegate::retrieveThumbnailPixmap(index, d->thumbnailSize);
0259 
0260             if (pix.isNull())
0261             {
0262                 pix = d->workingPixmap->frameAt(d->animationState);
0263                 d->thumbsWaitingFor++;
0264             }
0265 
0266             v4->icon           = QIcon(pix);
0267             v4->decorationSize = pix.size();
0268         }
0269     }
0270 }
0271 
0272 /*
0273 void VersionsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
0274 {
0275     painter->save();
0276     painter->setRenderHint(QPainter::Antialiasing, true);
0277     QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter);
0278     //qCDebug(DIGIKAM_GENERAL_LOG) << QApplication::style()->subElementRect(QStyle::SE_ItemViewItemDecoration, &option, 0);
0279 
0280     if (dynamic_cast<const ItemVersionsModel*>(index.model())->paintTree())
0281     {
0282         const_cast<QStyleOptionViewItem&>(option).rect.setLeft(option.rect.left() + (index.data(Qt::UserRole).toInt() * 16));
0283     }
0284 
0285     QRect thumbRect(option.rect.left()+4, option.rect.top()+8, 64, 48);
0286     painter->setPen(Qt::black);
0287     painter->drawRect(thumbRect);
0288 
0289     QPixmap thumbnail;
0290 
0291     if (ThumbnailLoadThread::defaultIconViewThread()->find(index.data(Qt::DisplayRole).toString(), thumbnail))
0292     {
0293         if (!thumbnail.isNull())
0294         {
0295             thumbnail = thumbnail.scaled(64, 48, Qt::KeepAspectRatio);
0296             const_cast<VersionsDelegate*>(this)->d->thumbsPainted++;
0297         }
0298         else
0299         {
0300             //if the thumbnail pixmap is null, display an error icon instead
0301             thumbnail = BarIcon("task-reject");
0302         }
0303 
0304         if (d->thumbsPainted == index.model()->rowCount())
0305         {
0306             // the timer can be stopped after last thumbnail is drawn,
0307             // but it needs to be delayed a little, so that all thumbs
0308             // have enough time to get painted correctly
0309             delayedAnimationTimerStop();
0310 
0311         }
0312     }
0313     else
0314     {
0315         //when the thumbnail is not loaded yet, start the animation
0316         d->workingWidget->toggleTimer(true);
0317 
0318         connect(d->workingWidget, SIGNAL(animationStep()),
0319                 dynamic_cast<const ItemVersionsModel*>(index.model()), SLOT(slotAnimationStep()));
0320 
0321         thumbnail = QPixmap::grabWidget(d->workingWidget);
0322     }
0323 
0324     painter->drawPixmap(thumbRect.left()+(32-(int)(thumbnail.width()/2)), thumbRect.top()+(24-(int)(thumbnail.height()/2)), thumbnail);
0325 
0326     QRect textRect = option.rect;
0327     textRect.setLeft(textRect.left() + 72);
0328     QUrl path(index.data(Qt::DisplayRole).toString());
0329 
0330     if (index.row() == 0 && index.model()->rowCount() > 1)
0331     {
0332         painter->drawText(textRect, Qt::AlignVCenter, i18n("%1 (Original)", path.fileName()));
0333     }
0334     else if (index.row() == 0 && index.model()->rowCount() == 1)
0335     {
0336         painter->drawText(textRect, Qt::AlignVCenter, i18n("This is the original image"));
0337     }
0338     else
0339     {
0340         painter->drawText(textRect, Qt::AlignVCenter, path.fileName());
0341     }
0342 
0343     painter->restore();
0344 }
0345 */
0346 
0347 } // namespace Digikam
0348 
0349 #include "moc_versionsdelegate.cpp"