File indexing completed on 2025-01-05 04:00:04

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2008-03-14
0007  * Description : User interface for searches
0008  *
0009  * SPDX-FileCopyrightText: 2008-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "ratingsearchutilities.h"
0016 
0017 // Qt includes
0018 
0019 #include <QLineEdit>
0020 #include <QLinearGradient>
0021 #include <QListView>
0022 #include <QMouseEvent>
0023 #include <QPainter>
0024 #include <QPen>
0025 #include <QStyle>
0026 #include <QStyleOption>
0027 #include <QVBoxLayout>
0028 #include <QApplication>
0029 
0030 // KDE includes
0031 
0032 #include <klocalizedstring.h>
0033 
0034 // Local includes
0035 
0036 #include "digikam_debug.h"
0037 #include "digikam_globals.h"
0038 
0039 namespace Digikam
0040 {
0041 
0042 RatingStarDrawer::RatingStarDrawer()
0043     : m_starPolygon    (RatingWidget::starPolygon()),
0044       m_starPolygonSize(QSize(15, 15))
0045 {
0046 }
0047 
0048 QRect RatingStarDrawer::drawStarPolygons(QPainter* painter, int numberOfStars) const
0049 {
0050     QRect    drawnRect(0, 0, 0, 0);
0051     QPolygon polygon(m_starPolygon);
0052 
0053     if (numberOfStars)
0054     {
0055         drawnRect.adjust(0, 0, 0, m_starPolygonSize.height());
0056     }
0057 
0058     for (int i = 0 ; i < numberOfStars ; ++i)
0059     {
0060         painter->drawPolygon(polygon, Qt::WindingFill);
0061         polygon.translate(m_starPolygonSize.width(), 0);
0062         drawnRect.adjust(0, 0, m_starPolygonSize.width(), 0);
0063     }
0064 
0065     return drawnRect;
0066 }
0067 
0068 // -------------------------------------------------------------------------
0069 
0070 RatingComboBoxDelegate::RatingComboBoxDelegate(QObject* const parent)
0071     : QItemDelegate(parent)
0072 {
0073 }
0074 
0075 QSize RatingComboBoxDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
0076 {
0077     QVariant value = index.data(Qt::DisplayRole);
0078 
0079 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0080 
0081     if (value.typeId() == QVariant::Int)
0082 
0083 #else
0084 
0085     if (value.type() == QVariant::Int)
0086 
0087 #endif
0088 
0089     {
0090         return QSize(RatingMax * (m_starPolygonSize.width() + 1), m_starPolygonSize.height());
0091     }
0092     else
0093     {
0094         return QItemDelegate::sizeHint(option, index);
0095     }
0096 }
0097 
0098 void RatingComboBoxDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
0099                                    const QModelIndex& index) const
0100 {
0101     QVariant value  = index.data(Qt::DisplayRole);
0102     bool selectable = index.flags() & Qt::ItemIsSelectable;
0103 
0104 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0105 
0106     if (value.typeId() == QVariant::Int)
0107 
0108 #else
0109 
0110     if (value.type() == QVariant::Int)
0111 
0112 #endif
0113 
0114     {
0115         painter->save();
0116         drawBackground(painter, option, index);
0117         drawDisplay(painter, option, option.rect, QString());
0118 
0119         // our custom painting
0120 
0121         drawRating(painter, option.rect, value.toInt(), selectable);
0122 
0123         drawFocus(painter, option, option.rect);
0124         painter->restore();
0125     }
0126     else
0127     {
0128         QItemDelegate::paint(painter, option, index);
0129     }
0130 }
0131 
0132 void RatingComboBoxDelegate::drawRating(QPainter* painter, const QRect& rect, int rating, bool selectable) const
0133 {
0134     painter->save();
0135 
0136     painter->setRenderHint(QPainter::Antialiasing, true);
0137 /*
0138     pen.setJoinStyle(Qt::MiterJoin);
0139 */
0140     painter->setPen(qApp->palette().color(QPalette::Text));
0141 
0142     if (!selectable)
0143     {
0144         painter->setOpacity(.1);
0145     }
0146 
0147     painter->setBrush(qApp->palette().color(QPalette::Link));
0148 
0149     // move painter while drawing polygons
0150 
0151     painter->translate(rect.topLeft());
0152     QRect drawRect = drawStarPolygons(painter, rating);
0153     painter->translate(drawRect.topRight());
0154 
0155     painter->setBrush(QBrush());
0156     drawStarPolygons(painter, RatingMax - rating);
0157 
0158     painter->restore();
0159 }
0160 
0161 // -------------------------------------------------------------------------
0162 
0163 RatingComboBoxModel::RatingComboBoxModel(QObject* const parent)
0164     : QAbstractListModel(parent)
0165 {
0166     for (int value = RatingComboBox::Null ; value <= RatingComboBox::Rating5 ; ++value)
0167     {
0168         m_entries << (RatingComboBox::RatingValue)value;
0169     }
0170 }
0171 
0172 int RatingComboBoxModel::rowCount(const QModelIndex& parent) const
0173 {
0174     if (parent.isValid())
0175     {
0176         return 0;
0177     }
0178 
0179     return m_entries.size();
0180 }
0181 
0182 QVariant RatingComboBoxModel::data(const QModelIndex& index, int role) const
0183 {
0184     if (index.isValid())
0185     {
0186         RatingComboBox::RatingValue value = (RatingComboBox::RatingValue)index.internalId();
0187 
0188         if      (role == Qt::DisplayRole)
0189         {
0190             return ratingValueToDisplay(value);
0191         }
0192         else if (role == RatingRole)
0193         {
0194             return (int)value;
0195         }
0196     }
0197 
0198     return QVariant();
0199 }
0200 
0201 QVariant RatingComboBoxModel::ratingValueToDisplay(RatingComboBox::RatingValue value) const
0202 {
0203     switch (value)
0204     {
0205         case RatingComboBox::Null:
0206         {
0207             return i18n("(No Value Selected)");
0208         }
0209 
0210         case RatingComboBox::NoRating:
0211         {
0212             return i18n("No Rating assigned");
0213         }
0214 
0215         case RatingComboBox::Rating0:
0216         case RatingComboBox::Rating1:
0217         case RatingComboBox::Rating2:
0218         case RatingComboBox::Rating3:
0219         case RatingComboBox::Rating4:
0220         case RatingComboBox::Rating5:
0221         {
0222             return (int)value;
0223         }
0224     }
0225 
0226     return QVariant();
0227 }
0228 
0229 QModelIndex RatingComboBoxModel::index(int row, int column, const QModelIndex& parent) const
0230 {
0231     if (parent.isValid() || (column != 0) || (row >= m_entries.size()))
0232     {
0233         return QModelIndex();
0234     }
0235 
0236     // third argument: RatingValue as internal data
0237 
0238     return createIndex(row, column, m_entries.at(row));
0239 }
0240 
0241 QModelIndex RatingComboBoxModel::indexForRatingValue(RatingComboBox::RatingValue value) const
0242 {
0243     int row = m_entries.indexOf(value);
0244 
0245     if (row != -1)
0246     {
0247         return createIndex(row, 0, value);
0248     }
0249 
0250     return QModelIndex();
0251 }
0252 
0253 // -------------------------------------------------------------------------
0254 
0255 RatingComboBoxWidget::RatingComboBoxWidget(QWidget* const parent)
0256     : RatingWidget(parent)
0257 {
0258     m_value = RatingComboBox::Null;
0259 
0260     // generate paint event on mouse enter/leave
0261 
0262     setAttribute(Qt::WA_Hover);
0263 
0264     // set lineedit-like background, also for cached pixmaps
0265 
0266     setBackgroundRole(QPalette::Base);
0267     regeneratePixmaps();
0268 
0269     connect(this, SIGNAL(signalRatingChanged(int)),
0270             this, SLOT(slotRatingChanged(int)));
0271 }
0272 
0273 RatingComboBox::RatingValue RatingComboBoxWidget::ratingValue() const
0274 {
0275     return m_value;
0276 }
0277 
0278 void RatingComboBoxWidget::setRatingValue(RatingComboBox::RatingValue value)
0279 {
0280     if (m_value == value)
0281     {
0282         return;
0283     }
0284 
0285     m_value = value;
0286 
0287     // sync with base class
0288 
0289     blockSignals(true);
0290 
0291     if (m_value >= RatingComboBox::Rating0)
0292     {
0293         setRating(value);
0294     }
0295     else
0296     {
0297         setRating(0);
0298     }
0299 
0300     blockSignals(false);
0301 
0302     update();
0303     Q_EMIT ratingValueChanged(m_value);
0304 }
0305 
0306 void RatingComboBoxWidget::slotRatingChanged(int rating)
0307 {
0308     RatingComboBox::RatingValue newValue = (RatingComboBox::RatingValue)rating;
0309 
0310     if (m_value != newValue)
0311     {
0312         m_value = newValue;
0313         Q_EMIT ratingValueChanged(m_value);
0314     }
0315 }
0316 
0317 void RatingComboBoxWidget::paintEvent(QPaintEvent* e)
0318 {
0319     if      (m_value >= RatingComboBox::Rating0)
0320     {
0321 /*
0322         qCDebug(DIGIKAM_GENERAL_LOG) << "m_value" << m_value << "defaulting paint to parent" << this;
0323 */
0324         RatingWidget::paintEvent(e);
0325     }
0326     else if (m_value == RatingComboBox::NoRating)
0327     {
0328         QPainter p(this);
0329 
0330         QPixmap pix = starPixmap();
0331         int width   = regPixmapWidth();
0332         p.drawPixmap(0, 0, pix);
0333 
0334         // draw red cross
0335 
0336         p.setPen(Qt::red);
0337         p.drawLine(0, 0, width, width);
0338         p.drawLine(0, width, width, 0);
0339     }
0340     else if (m_value == RatingComboBox::Null)
0341     {
0342         QPainter p(this);
0343 
0344         if (underMouse() && isEnabled())
0345         {
0346             QPixmap pix = starPixmap();
0347             int x       = 0;
0348 
0349             for (int i = 0 ; i < RatingMax ; ++i)
0350             {
0351                 p.drawPixmap(x, 0, pix);
0352                 x += regPixmapWidth();
0353             }
0354         }
0355         else
0356         {
0357             p.setRenderHint(QPainter::Antialiasing, true);
0358 /*
0359             pen.setJoinStyle(Qt::MiterJoin);
0360 */
0361             QColor foreground = palette().color(QPalette::Active, QPalette::WindowText);
0362             QColor background = palette().color(QPalette::Active, QPalette::Window);
0363             foreground.setAlphaF(foreground.alphaF() * 0.5);
0364             background.setAlphaF(background.alphaF() * 0.5);
0365             QColor foregroundEnd(foreground), backgroundEnd(background);
0366             foregroundEnd.setAlphaF(0);
0367             backgroundEnd.setAlphaF(0);
0368 
0369             QLinearGradient grad(QPointF(0, (double)rect().height() / 2), QPointF(width(), (double)rect().height() / 2));
0370             grad.setColorAt(0, foreground);
0371             grad.setColorAt(1, foregroundEnd);
0372             p.setPen(QPen(grad, 0));
0373 
0374             grad.setColorAt(0, background);
0375             grad.setColorAt(1, backgroundEnd);
0376             p.setBrush(grad);
0377 
0378             drawStarPolygons(&p, 5);
0379         }
0380     }
0381 }
0382 
0383 // -------------------------------------------------------------------------
0384 
0385 RatingComboBox::RatingComboBox(QWidget* const parent)
0386     : ModelIndexBasedComboBox(parent)
0387 {
0388     m_syncing = false;
0389 
0390     // create a custom model that contains the rating values
0391 
0392     m_model   = new RatingComboBoxModel(this);
0393     setModel(m_model);
0394 
0395     // set a custom delegate which draws rating stars
0396 
0397     RatingComboBoxDelegate* delegate = new RatingComboBoxDelegate(this);
0398     view()->setItemDelegate(delegate);
0399 
0400     // set a line edit that carries a RatingWidget
0401 
0402     ProxyLineEdit* lineEdit = new ProxyLineEdit;
0403     m_ratingWidget          = new RatingComboBoxWidget;
0404     lineEdit->setWidget(m_ratingWidget);
0405     setLineEdit(lineEdit);
0406 
0407     connect(view()->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
0408             this, SLOT(currentValueChanged(QModelIndex,QModelIndex)));
0409 
0410     connect(m_ratingWidget, SIGNAL(ratingValueChanged(int)),
0411             this, SLOT(ratingWidgetChanged(int)));
0412 }
0413 
0414 void RatingComboBox::setRatingValue(RatingComboBox::RatingValue value)
0415 {
0416     if      (value > Rating5)
0417     {
0418         value = Rating5;
0419     }
0420     else if (value < Null)
0421     {
0422         value = Null;
0423     }
0424 
0425     setCurrentIndex(m_model->indexForRatingValue(value));
0426 }
0427 
0428 RatingComboBox::RatingValue RatingComboBox::ratingValue() const
0429 {
0430     return (RatingValue)view()->currentIndex().data(RatingComboBoxModel::RatingRole).toInt();
0431 }
0432 
0433 void RatingComboBox::currentValueChanged(const QModelIndex& current, const QModelIndex&)
0434 {
0435     if (m_syncing)
0436     {
0437         return;
0438     }
0439 
0440     RatingValue value = (RatingValue)current.data(RatingComboBoxModel::RatingRole).toInt();
0441 
0442     m_syncing         = true;
0443     m_ratingWidget->setRatingValue(value);
0444     m_syncing         = false;
0445 
0446     Q_EMIT ratingValueChanged(value);
0447 }
0448 
0449 void RatingComboBox::ratingWidgetChanged(int rv)
0450 {
0451     if (m_syncing)
0452     {
0453         return;
0454     }
0455 
0456     RatingValue value = (RatingValue)rv;
0457     QModelIndex index = m_model->indexForRatingValue(value);
0458 
0459     m_syncing         = true;
0460     setCurrentIndex(index);
0461     m_syncing         = false;
0462 
0463     Q_EMIT ratingValueChanged(value);
0464 }
0465 
0466 } // namespace Digikam
0467 
0468 #include "moc_ratingsearchutilities.cpp"