/** * @RDE_HEADER@ * SpinBox, that corrects values to a valid value that fits minimum() + n * singleStep() = value * $Id$ * $HeadURL$ * $Author$ * * Created by Malte Diers DER LOSER, NLSE, 2018-05-07 */ #include "lts_multi_range_spinbox.h" #include #include #include namespace LtsWidgets { LtsMultiRangeSpinBox::LtsMultiRangeSpinBox(QWidget *parent) : QSpinBox(parent) { m_curVal = minimum(); } /*! * \brief LtsSpinBoxNew::validate * \param input * \param pos * \brief only used if enter pressed * \return */ QValidator::State LtsMultiRangeSpinBox::validate(QString &input, int &pos) const { // QIntValidator validator; // int minimumVal = minimum(); // int maximumVal = maximum(); // validator.setBottom(minimum()); // validator.setTop(maximum()); // QValidator::State state = validator.validate(input, pos); // if (state == QValidator::Invalid || // state == QValidator::Intermediate) // { // return state; // } // // Now we're sure, it's an int. Therefore toInt() will always be okay: // int value = input.toInt(); // value -= minimum(); // return (value % singleStep() == 0) ? QValidator::Acceptable : QValidator::Intermediate; return QValidator::Acceptable; } void LtsMultiRangeSpinBox::fixup(QString &str) const { // QSpinBox::fixup(str); // if (singleStep() == 1) // { // return; // } // bool bOk = false; // int value = str.toInt(&bOk); // if (!bOk) // { // return; // } // fixupValue(value); // str = QString::number(value); } QValidator::State LtsMultiRangeSpinBox::validateAll(QString &input, int &pos) { for(QPair range : qAsConst(m_rangesList)) { QIntValidator validator(range.first, range.second); QValidator::State valState = validator.validate(input, pos); if (valState == QValidator::Acceptable) { return valState; } } return QValidator::Invalid; //return validate(input, pos); } /*! * \brief LtsSpinBoxNew::setGaps * \param gaps * \author Niklas Schmidt */ QVector> LtsWidgets::LtsMultiRangeSpinBox::sortRanges(const QVector > &ranges) { QVector> rangesEditable = ranges; QVector> result; std::sort(rangesEditable.begin(), rangesEditable.end()); QVector>::iterator it = rangesEditable.begin(); QPair current = *(it)++; while (it != rangesEditable.end()) { if (current.second > it->first) current.second = std::max(current.second, it->second); else { result.push_back(current); current = *(it); } ++it; } result.push_back(current); return result; } void LtsMultiRangeSpinBox::setRanges(const QVector> &ranges) { if(ranges.empty()) return; m_rangesList = sortRanges(ranges); QSpinBox::setMaximum(m_rangesList.last().second); QSpinBox::setMinimum(m_rangesList.first().first); QStringList rangeStrings; rangeStrings.reserve(m_rangesList.size()); for (const QPair &range : qAsConst(m_rangesList)) rangeStrings << tr("%1-%2%3").arg(range.first).arg(range.second).arg(suffix()); setToolTip(tr("Ranges:\n%1").arg(rangeStrings.join(QStringLiteral(",\n")))); } void LtsMultiRangeSpinBox::setRange(int min, int max) { setRanges({{min,max}}); } void LtsMultiRangeSpinBox::setMinimum(int min) { auto ranges = m_rangesList; ranges.first().first = min; setRanges(ranges); } void LtsMultiRangeSpinBox::setMaximum(int max) { auto ranges = m_rangesList; ranges.last().second = max; setRanges(ranges); } int LtsMultiRangeSpinBox::fixupValue(int value) const { int nearestValue = INT_MAX; for(QPair range : m_rangesList) { if(std::abs(range.first - value) < std::abs(nearestValue - value)) nearestValue = range.first; if(std::abs(range.second - value) < std::abs(nearestValue - value)) nearestValue = range.second; } value = nearestValue; int buffer = value; int remainder = (value - minimum()) % singleStep(); if (2 * remainder > singleStep()) { value += singleStep(); } value -= remainder; if(buffer + singleStep() > value + singleStep()) { value+= singleStep(); } if (value > maximum()) { value -= singleStep(); } return value; } int LtsMultiRangeSpinBox::fixupValue(int value, bool higher) const { int nearestValue = INT_MAX; for(QPair range : m_rangesList) { if(std::abs(range.first - value) < std::abs(nearestValue - value)) { if(higher) { if(range.second < value) continue; } else if(range.second > value) continue; nearestValue = range.first; } if(std::abs(range.second - value) < std::abs(nearestValue - value)) { if(higher) { if(range.second < value) continue; } else if(range.second > value) continue; nearestValue = range.second; } } value = nearestValue; int curVal = value; int remainder = (value - minimum()) % singleStep(); if (2 * remainder > singleStep()) { value += singleStep(); } value -= remainder; if(curVal + singleStep() > value + singleStep()) { value+= singleStep(); } if (value > maximum()) { value -= singleStep(); } return value; } void LtsMultiRangeSpinBox::focusOutEvent(QFocusEvent *event) { QSpinBox::focusOutEvent(event); QString value = QString::number(this->value()); int length = value.length(); QValidator::State valState = validateAll(value, length); if(valState == QValidator::State::Invalid) { this->setValue(fixupValue(value.toInt())); } } void LtsMultiRangeSpinBox::wheelEvent(QWheelEvent *event) { QSpinBox::wheelEvent(event); QString valueStr = QString::number(this->value()); int length = valueStr.length(); QValidator::State valState = validateAll(valueStr, length); if(valState == QValidator::State::Invalid) { /*! Check if the new value is lower or higher the past value Example: Gap between 50 and 100. Value changed from 100 to 99 requested So the new value is lower so the SpinBox need to be corrected down to 49 !*/ if(this->value() < m_curVal) { m_curVal = fixupValue(this->value(), false); setValue(m_curVal); } else { m_curVal = fixupValue(this->value(), true); setValue(m_curVal); } } } void LtsMultiRangeSpinBox::mousePressEvent(QMouseEvent *event) { QSpinBox::mousePressEvent(event); QString valueStr = QString::number(this->value()); int length = valueStr.length(); QValidator::State valState = validateAll(valueStr, length); if(valState == QValidator::State::Invalid) { /*! Check if the new value is lower or higher the past value Example: Gap between 50 and 100. Value changed from 100 to 99 requested So the new value is lower so the SpinBox need to be corrected down to 49 !*/ if(this->value() < m_curVal) { m_curVal = fixupValue(this->value(), false); setValue(m_curVal); } else { m_curVal = fixupValue(this->value(), true); setValue(m_curVal); } } } void LtsMultiRangeSpinBox::keyPressEvent(QKeyEvent *event) { QSpinBox::keyPressEvent(event); QString value = QString::number(this->value()); int length = value.length(); QValidator::State valState = validateAll(value, length); if(valState == QValidator::State::Invalid) { setValue(fixupValue(value.toInt())); } } } // namespace LtsWidgets