File indexing completed on 2024-05-12 05:09:49

0001 /***************************************************************************
0002     Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or         *
0008  *   modify it under the terms of the GNU General Public License as        *
0009  *   published by the Free Software Foundation; either version 2 of        *
0010  *   the License or (at your option) version 3 or any later version        *
0011  *   accepted by the membership of KDE e.V. (or its successor approved     *
0012  *   by the membership of KDE e.V.), which shall act as a proxy            *
0013  *   defined in Section 14 of version 3 of the license.                    *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0022  *                                                                         *
0023  ***************************************************************************/
0024 
0025 #include "ratingwidget.h"
0026 #include "../field.h"
0027 #include "../utils/string_utils.h"
0028 #include "../tellico_debug.h"
0029 
0030 #include <QHash>
0031 #include <QBoxLayout>
0032 #include <QLabel>
0033 #include <QMouseEvent>
0034 #include <QToolButton>
0035 
0036 namespace {
0037   static const int RATING_WIDGET_MAX_ICONS = 10; // same as in Field::convertOldRating()
0038   static const int RATING_WIDGET_MAX_STAR_SIZE = 24;
0039 }
0040 
0041 using Tellico::GUI::RatingWidget;
0042 
0043 RatingWidget::RatingWidget(Tellico::Data::FieldPtr field_, QWidget* parent_)
0044     : QWidget(parent_), m_field(field_), m_currIndex(-1) {
0045   QHBoxLayout* layout = new QHBoxLayout(this);
0046   layout->setMargin(0);
0047   layout->setSpacing(0);
0048 
0049   m_pixOn = QIcon(QLatin1String(":/icons/star_on")).pixmap(QSize(18, 18));
0050   m_pixOff = QIcon(QLatin1String(":/icons/star_off")).pixmap(QSize(18, 18));
0051 
0052   m_widgets.reserve(RATING_WIDGET_MAX_ICONS);
0053   // find maximum width and height
0054   int w = qMax(RATING_WIDGET_MAX_STAR_SIZE, qMax(m_pixOn.width(), m_pixOff.width()));
0055   int h = qMax(RATING_WIDGET_MAX_STAR_SIZE, qMax(m_pixOn.height(), m_pixOff.height()));
0056   for(int i = 0; i < RATING_WIDGET_MAX_ICONS; ++i) {
0057     QLabel* l = new QLabel(this);
0058     l->setFixedSize(w, h);
0059     m_widgets.append(l);
0060     layout->addWidget(l);
0061   }
0062 
0063   m_clearButton = new QToolButton(this);
0064   if(layoutDirection() == Qt::LeftToRight) {
0065     m_clearButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-locationbar-rtl")));
0066   } else {
0067     m_clearButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-locationbar-ltr")));
0068   }
0069   connect(m_clearButton, &QAbstractButton::clicked, this, &RatingWidget::clearClicked);
0070   layout->addWidget(m_clearButton);
0071 
0072   // to keep the widget from resizing when the clear button is shown/hidden
0073   // have a fixed width spacer to swap with
0074   const int mw = m_clearButton->minimumSizeHint().width();
0075   m_clearButton->setFixedWidth(mw);
0076   m_clearSpacer = new QSpacerItem(mw, mw, QSizePolicy::Fixed, QSizePolicy::Fixed);
0077 
0078   init();
0079 }
0080 
0081 void RatingWidget::init() {
0082   updateBounds();
0083   m_total = qMin(m_max, static_cast<int>(m_widgets.count()));
0084   int i = 0;
0085   for( ; i < m_total; ++i) {
0086     m_widgets.at(i)->setPixmap(m_pixOff);
0087   }
0088   setUpdatesEnabled(false);
0089 
0090   QBoxLayout* l = ::qobject_cast<QBoxLayout*>(layout());
0091   // move the clear button to right after the last star
0092   l->removeWidget(m_clearButton);
0093   l->insertWidget(i, m_clearButton);
0094   l->removeItem(m_clearSpacer);
0095   l->insertSpacerItem(i+1, m_clearSpacer);
0096   m_clearButton->hide();
0097 
0098   setUpdatesEnabled(true);
0099 
0100   for( ; i < m_widgets.count(); ++i) {
0101     m_widgets.at(i)->setPixmap(QPixmap());
0102   }
0103   update();
0104 }
0105 
0106 void RatingWidget::updateBounds() {
0107   bool ok; // not used;
0108   m_min = Tellico::toUInt(m_field->property(QStringLiteral("minimum")), &ok);
0109   m_max = Tellico::toUInt(m_field->property(QStringLiteral("maximum")), &ok);
0110   if(m_max > RATING_WIDGET_MAX_ICONS) {
0111     myDebug() << "max is too high: " << m_max;
0112     m_max = RATING_WIDGET_MAX_ICONS;
0113   }
0114   if(m_min < 1) {
0115     m_min = 1;
0116   }
0117 }
0118 
0119 void RatingWidget::update() {
0120   int i = 0;
0121   for( ; i <= m_currIndex; ++i) {
0122     m_widgets.at(i)->setPixmap(m_pixOn);
0123   }
0124   for( ; i < m_total; ++i) {
0125     m_widgets.at(i)->setPixmap(m_pixOff);
0126   }
0127 
0128   QWidget::update();
0129 }
0130 
0131 void RatingWidget::mousePressEvent(QMouseEvent* event_) {
0132   // only react to left button
0133   if(event_->button() != Qt::LeftButton) {
0134     return;
0135   }
0136 
0137   int idx;
0138   QWidget* child = childAt(event_->pos());
0139   if(child) {
0140     idx = m_widgets.indexOf(static_cast<QLabel*>(child));
0141     // if the widget is clicked beyond the maximum value, clear it
0142     // remember total and min are values, but index is zero-based!
0143     if(idx > m_total-1) {
0144       idx = -1;
0145     } else if(idx < m_min-1) {
0146       idx = m_min-1; // limit to minimum, remember index is zero-based
0147     }
0148   } else {
0149     idx = -1;
0150   }
0151   if(m_currIndex != idx) {
0152     m_currIndex = idx;
0153     update();
0154     emit signalModified();
0155   }
0156 }
0157 
0158 void RatingWidget::enterEvent(QEvent* event_) {
0159   Q_UNUSED(event_);
0160   setUpdatesEnabled(false);
0161   m_clearButton->show();
0162   QBoxLayout* l = ::qobject_cast<QBoxLayout*>(layout());
0163   l->removeItem(m_clearSpacer);
0164   setUpdatesEnabled(true);
0165 }
0166 
0167 void RatingWidget::leaveEvent(QEvent* event_) {
0168   Q_UNUSED(event_);
0169   setUpdatesEnabled(false);
0170   m_clearButton->hide();
0171   QBoxLayout* l = ::qobject_cast<QBoxLayout*>(layout());
0172   l->insertSpacerItem(m_total+1, m_clearSpacer);
0173   setUpdatesEnabled(true);
0174 }
0175 
0176 void RatingWidget::clear() {
0177   m_currIndex = -1;
0178   update();
0179 }
0180 
0181 QString RatingWidget::text() const {
0182   // index is index of the list, which is zero-based. Add 1!
0183   return m_currIndex == -1 ? QString() : QString::number(m_currIndex+1);
0184 }
0185 
0186 void RatingWidget::setText(const QString& text_) {
0187   bool ok;
0188   // text is value, subtract one to get index
0189   m_currIndex = Tellico::toUInt(text_, &ok)-1;
0190   if(ok) {
0191     if(m_currIndex > m_total-1) {
0192       m_currIndex = -1;
0193     } else if(m_currIndex < m_min-1) {
0194       myWarning() << "RatingWidget:: Trying to set value to" << text_ << "but min is" << m_min;
0195       m_currIndex = -1; // trying to set a value outside the bounds results in no value
0196     }
0197   } else {
0198     m_currIndex = -1;
0199   }
0200   update();
0201 }
0202 
0203 void RatingWidget::updateField(Tellico::Data::FieldPtr field_) {
0204   const QString value = text();
0205   m_field = field_;
0206   init();
0207   setText(value);
0208 }
0209 
0210 void RatingWidget::clearClicked() {
0211   if(m_currIndex != -1) {
0212     m_currIndex = -1;
0213     update();
0214     emit signalModified();
0215   }
0216 }