File indexing completed on 2025-02-16 04:40:44

0001 /***************************************************************************
0002  *   SPDX-License-Identifier: GPL-2.0-or-later
0003  *                                                                         *
0004  *   SPDX-FileCopyrightText: 2004-2019 Thomas Fischer <fischer@unix-ag.uni-kl.de>
0005  *                                                                         *
0006  *   This program is free software; you can redistribute it and/or modify  *
0007  *   it under the terms of the GNU General Public License as published by  *
0008  *   the Free Software Foundation; either version 2 of the License, or     *
0009  *   (at your option) any later version.                                   *
0010  *                                                                         *
0011  *   This program is distributed in the hope that it will be useful,       *
0012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0014  *   GNU General Public License for more details.                          *
0015  *                                                                         *
0016  *   You should have received a copy of the GNU General Public License     *
0017  *   along with this program; if not, see <https://www.gnu.org/licenses/>. *
0018  ***************************************************************************/
0019 
0020 #include "rangewidget.h"
0021 
0022 #include <QComboBox>
0023 #include <QLineEdit>
0024 #include <QBoxLayout>
0025 #include <QStringListModel>
0026 #include <QLabel>
0027 
0028 #include <KLocalizedString>
0029 
0030 class RangeWidget::Private
0031 {
0032 public:
0033     enum TextAlternative {LowerAlternativ, UpperAlternative};
0034 
0035     const QStringList values;
0036     int lowerValue, upperValue;
0037     QComboBox *lowerComboBox, *upperComboBox;
0038 
0039     Private(const QStringList &_values, RangeWidget *parent)
0040             : values(_values), lowerValue(0), upperValue(_values.size() - 1)
0041     {
0042         Q_UNUSED(parent)
0043 
0044         QBoxLayout *layout = new QHBoxLayout(parent);
0045         layout->setContentsMargins(0, 0, 0, 0);
0046 
0047         lowerComboBox = new QComboBox(parent);
0048         layout->addWidget(lowerComboBox, 1, Qt::AlignCenter);
0049         lowerComboBox->setModel(new QStringListModel(lowerComboBox));
0050 
0051         QLabel *label = new QLabel(QChar(0x22ef), parent);
0052         layout->addWidget(label, 0, Qt::AlignCenter);
0053 
0054         upperComboBox = new QComboBox(parent);
0055         layout->addWidget(upperComboBox, 1, Qt::AlignCenter);
0056         upperComboBox->setModel(new QStringListModel(upperComboBox));
0057 
0058         layout->addStretch(100); ///< left-align this widget's child widgets
0059 
0060         adjustComboBoxes();
0061 
0062         connect(lowerComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), parent, &RangeWidget::lowerComboBoxChanged);
0063         connect(upperComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), parent, &RangeWidget::upperComboBoxChanged);
0064     }
0065 
0066     QStringList stringListRange(const QStringList &originalList, int min, int max, const TextAlternative textAlternative) {
0067         if (originalList.isEmpty()) return QStringList();
0068 
0069         QStringList result;
0070         for (int i = qMin(originalList.size() - 1, qMin(max, qMax(0, min))); i <= qMin(originalList.size() - 1, qMax(min, qMax(0, max))); ++i) {
0071             const QStringList alternatives = originalList[i].split(QStringLiteral("|"));
0072             Q_ASSERT_X(alternatives.size() >= 1 && alternatives.size() <= 2, "RangeWidget::Private::stringListRange", "Either one or two alternatives must be given");
0073             const int alternativeIndex = alternatives.size() == 1 || textAlternative == LowerAlternativ ? 0 : 1;
0074             if (!alternatives[alternativeIndex].isEmpty())
0075                 result.append(alternatives[alternativeIndex]);
0076         }
0077 
0078         return result;
0079     }
0080 
0081     void adjustComboBoxes() {
0082         const int minimum = 0, maximum = values.size() - 1;
0083         Q_ASSERT_X(minimum <= lowerValue, "RangeWidget::Private::adjustSpinBoxes", "minimum<=lowerValue");
0084         Q_ASSERT_X(lowerValue <= upperValue, "RangeWidget::Private::adjustSpinBoxes", "lowerValue<=upperValue");
0085         Q_ASSERT_X(upperValue <= maximum, "RangeWidget::Private::adjustSpinBoxes", "upperValue<=maximum");
0086 
0087         /// Disable signals being emitted when the combo boxes get updated
0088         QSignalBlocker lowerComboBoxSignalBlocker(lowerComboBox), upperComboBoxSignalBlocker(upperComboBox);
0089 
0090         /// Compute a temporary QStringList containing only values from minimum to current upper value
0091         const QStringList lowerValues = stringListRange(values, minimum, upperValue, LowerAlternativ);
0092         qobject_cast<QStringListModel *>(lowerComboBox->model())->setStringList(lowerValues);
0093         lowerComboBox->setCurrentIndex(lowerValue);
0094 
0095         /// Compute a temporary QStringList containing only values from current lower value to maximum
0096         const QStringList upperValues = stringListRange(values, lowerValue, maximum, UpperAlternative);
0097         qobject_cast<QStringListModel *>(upperComboBox->model())->setStringList(upperValues);
0098         upperComboBox->setCurrentIndex(upperValue - lowerValue);
0099     }
0100 };
0101 
0102 RangeWidget::RangeWidget(const QStringList &values, QWidget *parent)
0103         : QWidget(parent), d(new Private(values, this))
0104 {
0105     /// nothing
0106 }
0107 
0108 RangeWidget::~RangeWidget()
0109 {
0110     delete d;
0111 }
0112 
0113 int RangeWidget::maximum() const
0114 {
0115     return d->values.size() - 1;
0116 }
0117 
0118 void RangeWidget::setLowerValue(int newLowerValue)
0119 {
0120     newLowerValue = qMin(qMax(qMin(newLowerValue, d->values.size() - 1), 0), d->upperValue);
0121     if (newLowerValue != d->lowerValue) {
0122         d->lowerValue = newLowerValue;
0123         Q_EMIT lowerValueChanged(d->lowerValue);
0124         d->adjustComboBoxes();
0125     }
0126 }
0127 
0128 int RangeWidget::lowerValue() const
0129 {
0130     return d->lowerValue;
0131 }
0132 
0133 void RangeWidget::setUpperValue(int newUpperValue)
0134 {
0135     newUpperValue = qMax(qMax(qMin(newUpperValue, d->values.size() - 1), 0), d->lowerValue);
0136     if (newUpperValue != d->upperValue) {
0137         d->upperValue = newUpperValue;
0138         Q_EMIT upperValueChanged(d->upperValue);
0139         d->adjustComboBoxes();
0140     }
0141 }
0142 
0143 int RangeWidget::upperValue() const
0144 {
0145     return d->upperValue;
0146 }
0147 
0148 void RangeWidget::lowerComboBoxChanged(int spinboxLowerValue)
0149 {
0150     const int newLowerValue = spinboxLowerValue;
0151     if (newLowerValue != d->lowerValue) {
0152         d->lowerValue = newLowerValue;
0153         Q_EMIT lowerValueChanged(d->lowerValue);
0154         d->adjustComboBoxes();
0155     }
0156 }
0157 
0158 void RangeWidget::upperComboBoxChanged(int spinboxUpperValue)
0159 {
0160     const int newUpperValue = spinboxUpperValue + d->lowerValue;
0161     if (newUpperValue != d->upperValue) {
0162         d->upperValue = newUpperValue;
0163         Q_EMIT upperValueChanged(d->upperValue);
0164         d->adjustComboBoxes();
0165     }
0166 }