File indexing completed on 2024-05-12 05:35:43

0001 /*
0002     kcmmisc.cpp
0003 
0004     SPDX-FileCopyrightText: 1997 Patrick Dowler <dowler@morgul.fsh.uvic.ca>
0005 
0006     Layout management, cleanups:
0007     SPDX-FileCopyrightText: 1999 Dirk A. Mueller <dmuell@gmx.net>
0008 
0009     SPDX-FileCopyrightText: 2021 Cyril Rossi <cyril.rossi@enioka.com>
0010 
0011     SPDX-License-Identifier: GPL-2.0-or-later
0012 */
0013 
0014 #include "kcmmisc.h"
0015 #include "keyboardmiscsettings.h"
0016 #include "ui_kcmmiscwidget.h"
0017 
0018 #include <QButtonGroup>
0019 #include <QCheckBox>
0020 #include <QDoubleSpinBox>
0021 #include <QSpinBox>
0022 #include <QWhatsThis>
0023 #include <QtGui/private/qtx11extras_p.h>
0024 
0025 #include <KConfig>
0026 #include <KConfigGroup>
0027 #include <KLocalizedString>
0028 #include <KSharedConfig>
0029 
0030 #include <X11/Xlib.h>
0031 #include <cmath>
0032 
0033 namespace
0034 {
0035 bool hasAccentSupport()
0036 {
0037     static bool isPlasmaIM = (qgetenv("QT_IM_MODULE") == "plasmaim");
0038     return isPlasmaIM;
0039 }
0040 }
0041 
0042 KCMiscKeyboardWidget::KCMiscKeyboardWidget(QWidget *parent, KeyboardMiscSettings *settings)
0043     : QWidget(parent)
0044     , ui(*new Ui_KeyboardConfigWidget)
0045     , m_settings(settings)
0046 {
0047     ui.setupUi(this);
0048 
0049     ui.kcfg_repeatDelay->setSingleStep(50);
0050     ui.kcfg_repeatRate->setSingleStep(5);
0051 
0052     sliderMax = (int)floor(0.5 + 2 * (log(5000.0L) - log(100.0L)) / (log(5000.0L) - log(4999.0L)));
0053     ui.delaySlider->setRange(0, sliderMax);
0054     ui.delaySlider->setSingleStep(sliderMax / 100);
0055     ui.delaySlider->setPageStep(sliderMax / 10);
0056     ui.delaySlider->setTickInterval(sliderMax / 10);
0057 
0058     ui.rateSlider->setRange(20, 10000);
0059     ui.rateSlider->setSingleStep(30);
0060     ui.rateSlider->setPageStep(500);
0061     ui.rateSlider->setTickInterval(498);
0062 
0063     connect(ui.kcfg_repeatDelay, SIGNAL(valueChanged(int)), this, SLOT(delaySpinboxChanged(int)));
0064     connect(ui.delaySlider, &QAbstractSlider::valueChanged, this, &KCMiscKeyboardWidget::delaySliderChanged);
0065     connect(ui.kcfg_repeatRate, SIGNAL(valueChanged(double)), this, SLOT(rateSpinboxChanged(double)));
0066     connect(ui.rateSlider, &QAbstractSlider::valueChanged, this, &KCMiscKeyboardWidget::rateSliderChanged);
0067 
0068     _numlockButtonGroup = new QButtonGroup(ui.numlockButtonGroup);
0069     _numlockButtonGroup->addButton(ui.radioButton1, 0);
0070     _numlockButtonGroup->addButton(ui.radioButton2, 1);
0071     _numlockButtonGroup->addButton(ui.radioButton3, 2);
0072 
0073     connect(_numlockButtonGroup, &QButtonGroup::idClicked, this, qOverload<>(&KCMiscKeyboardWidget::changed));
0074 
0075     _keyboardRepeatButtonGroup = new QButtonGroup(ui.repeatFormLayout);
0076     if (hasAccentSupport()) {
0077         _keyboardRepeatButtonGroup->addButton(ui.accentMenuRadioButton, 0);
0078     } else {
0079         ui.accentMenuRadioButton->setVisible(false);
0080     }
0081     _keyboardRepeatButtonGroup->addButton(ui.repeatRadioButton, 1);
0082     _keyboardRepeatButtonGroup->addButton(ui.nothingRadioButton, 2);
0083 
0084     connect(_keyboardRepeatButtonGroup, &QButtonGroup::idClicked, this, qOverload<>(&KCMiscKeyboardWidget::changed));
0085     connect(_keyboardRepeatButtonGroup, &QButtonGroup::idClicked, this, &KCMiscKeyboardWidget::keyboardRepeatStateChanged);
0086 
0087     connect(_numlockButtonGroup, qOverload<QAbstractButton *>(&QButtonGroup::buttonClicked), this, &KCMiscKeyboardWidget::updateUiDefaultIndicator);
0088     connect(_keyboardRepeatButtonGroup, qOverload<QAbstractButton *>(&QButtonGroup::buttonClicked), this, &KCMiscKeyboardWidget::updateUiDefaultIndicator);
0089 }
0090 
0091 KCMiscKeyboardWidget::~KCMiscKeyboardWidget()
0092 {
0093     delete &ui;
0094 }
0095 
0096 // set the slider and LCD values
0097 void KCMiscKeyboardWidget::setRepeat(KeyBehaviour keyboardRepeat, int delay_, double rate_)
0098 {
0099     _keyboardRepeatButtonGroup->button(keyboardRepeat)->click();
0100 
0101     delaySpinboxChanged(delay_);
0102     rateSpinboxChanged(rate_);
0103 }
0104 
0105 TriState TriStateHelper::getTriState(const QButtonGroup *group)
0106 {
0107     int selected = group->checkedId();
0108     return selected < 0 ? STATE_UNCHANGED : getTriState(selected);
0109 }
0110 
0111 void TriStateHelper::setTriState(QButtonGroup *group, TriState state)
0112 {
0113     group->button(getInt(state))->click();
0114 }
0115 
0116 void KCMiscKeyboardWidget::load()
0117 {
0118     // need to read as string to support old "true/false" parameter
0119     QString key = m_settings->keyboardRepeat();
0120     if (key == QLatin1String("true") || key == TriStateHelper::getString(STATE_ON) || key == QLatin1String("accent")) {
0121         keyboardRepeat = KeyBehaviour::AccentMenu;
0122     } else if (key == QLatin1String("false") || key == TriStateHelper::getString(STATE_OFF) || key == QLatin1String("nothing")) {
0123         keyboardRepeat = KeyBehaviour::DoNothing;
0124     } else if (key == QLatin1String("repeat")) {
0125         keyboardRepeat = KeyBehaviour::RepeatKey;
0126     }
0127     setRepeat(keyboardRepeat, m_settings->repeatDelay(), m_settings->repeatRate());
0128 
0129     numlockState = TriStateHelper::getTriState(m_settings->numLock());
0130     TriStateHelper::setTriState(_numlockButtonGroup, numlockState);
0131 }
0132 
0133 void KCMiscKeyboardWidget::save()
0134 {
0135     numlockState = TriStateHelper::getTriState(_numlockButtonGroup);
0136     keyboardRepeat = KeyBehaviour(_keyboardRepeatButtonGroup->checkedId());
0137 
0138     m_settings->setKeyboardRepeat(keybehaviourNames[keyboardRepeat]);
0139     m_settings->setNumLock(TriStateHelper::getInt(numlockState));
0140 }
0141 
0142 void KCMiscKeyboardWidget::defaults()
0143 {
0144     setRepeat(defaultValueKeyboardRepeat(), m_settings->defaultRepeatDelayValue(), m_settings->defaultRepeatRateValue());
0145     TriStateHelper::setTriState(_numlockButtonGroup, static_cast<TriState>(m_settings->defaultNumLockValue()));
0146     Q_EMIT changed(true);
0147 }
0148 
0149 QString KCMiscKeyboardWidget::quickHelp() const
0150 {
0151     return QString();
0152 
0153     /* "<h1>Keyboard</h1> This module allows you to choose options"
0154        " for the way in which your keyboard works. The actual effect of"
0155        " setting these options depends upon the features provided by your"
0156        " keyboard hardware and the X server on which Plasma is running.<p>"
0157        " For example, you may find that changing the key click volume"
0158        " has no effect because this feature is not available on your system." */
0159 }
0160 
0161 bool KCMiscKeyboardWidget::isSaveNeeded() const
0162 {
0163     return m_settings->keyboardRepeat() != keybehaviourNames[KeyBehaviour(_keyboardRepeatButtonGroup->checkedId())]
0164         || m_settings->numLock() != TriStateHelper::getInt(TriStateHelper::getTriState(_numlockButtonGroup));
0165 }
0166 
0167 bool KCMiscKeyboardWidget::isDefault() const
0168 {
0169     return defaultValueKeyboardRepeat() == KeyBehaviour(_keyboardRepeatButtonGroup->checkedId())
0170         && m_settings->defaultNumLockValue() == TriStateHelper::getInt(TriStateHelper::getTriState(_numlockButtonGroup));
0171 }
0172 
0173 void KCMiscKeyboardWidget::setDefaultIndicator(bool visible)
0174 {
0175     m_highlightVisible = visible;
0176     updateUiDefaultIndicator();
0177 }
0178 
0179 void KCMiscKeyboardWidget::updateUiDefaultIndicator()
0180 {
0181     const auto isNumLockDefault = m_settings->defaultNumLockValue() == TriStateHelper::getInt(TriStateHelper::getTriState(_numlockButtonGroup));
0182     for (auto button : _numlockButtonGroup->buttons()) {
0183         setDefaultIndicatorVisible(button, m_highlightVisible && !isNumLockDefault && _numlockButtonGroup->checkedButton() == button);
0184     }
0185 
0186     const auto isKeyboardRepeatDefault = defaultValueKeyboardRepeat() == KeyBehaviour(_keyboardRepeatButtonGroup->checkedId());
0187     for (auto button : _keyboardRepeatButtonGroup->buttons()) {
0188         setDefaultIndicatorVisible(button, m_highlightVisible && !isKeyboardRepeatDefault && _keyboardRepeatButtonGroup->checkedButton() == button);
0189     }
0190 
0191     setDefaultIndicatorVisible(ui.delaySlider, m_highlightVisible && ui.kcfg_repeatDelay->value() != m_settings->defaultRepeatDelayValue());
0192     setDefaultIndicatorVisible(ui.rateSlider, m_highlightVisible && ui.kcfg_repeatRate->value() != m_settings->defaultRepeatRateValue());
0193 }
0194 
0195 void KCMiscKeyboardWidget::delaySliderChanged(int value)
0196 {
0197     double alpha = sliderMax / (log(5000.0L) - log(100.0L));
0198     double linearValue = exp(value / alpha + log(100.0L));
0199 
0200     ui.kcfg_repeatDelay->setValue((int)floor(0.5 + linearValue));
0201     updateUiDefaultIndicator();
0202 
0203     Q_EMIT changed(true);
0204 }
0205 
0206 void KCMiscKeyboardWidget::delaySpinboxChanged(int value)
0207 {
0208     double alpha = sliderMax / (log(5000.0L) - log(100.0L));
0209     double logVal = alpha * (log((double)value) - log(100.0L));
0210 
0211     ui.delaySlider->setValue((int)floor(0.5 + logVal));
0212 
0213     Q_EMIT changed(true);
0214 }
0215 
0216 void KCMiscKeyboardWidget::rateSliderChanged(int value)
0217 {
0218     ui.kcfg_repeatRate->setValue(value / 100.0);
0219     updateUiDefaultIndicator();
0220 
0221     Q_EMIT changed(true);
0222 }
0223 
0224 void KCMiscKeyboardWidget::rateSpinboxChanged(double value)
0225 {
0226     ui.rateSlider->setValue((int)(value * 100));
0227     Q_EMIT changed(true);
0228 }
0229 
0230 void KCMiscKeyboardWidget::changed()
0231 {
0232     Q_EMIT changed(true);
0233 }
0234 
0235 void KCMiscKeyboardWidget::keyboardRepeatStateChanged(int selection)
0236 {
0237     ui.keyboardRepeatParamsGroupBox->setVisible(selection == KeyBehaviour::RepeatKey);
0238     changed();
0239 }
0240 
0241 void KCMiscKeyboardWidget::setDefaultIndicatorVisible(QWidget *widget, bool visible)
0242 {
0243     widget->setProperty("_kde_highlight_neutral", visible);
0244     widget->update();
0245 }
0246 
0247 KeyBehaviour KCMiscKeyboardWidget::defaultValueKeyboardRepeat() const
0248 {
0249     if (m_settings->defaultKeyboardRepeatValue() == keybehaviourNames[KeyBehaviour::AccentMenu] && !hasAccentSupport()) {
0250         return KeyBehaviour::RepeatKey;
0251     }
0252 
0253     const auto keys = keybehaviourNames.keys();
0254     auto defaultRepeat = std::find_if(keys.constBegin(), keys.constEnd(), [=, this](const KeyBehaviour &key) {
0255         return keybehaviourNames[key] == m_settings->defaultKeyboardRepeatValue();
0256     });
0257 
0258     return *defaultRepeat;
0259 }