File indexing completed on 2024-05-12 17:07:15

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