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"