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 }