1 /*
2     kcmmisc.cpp
3 
4     SPDX-FileCopyrightText: 1997 Patrick Dowler <dowler@morgul.fsh.uvic.ca>
5 
6     Layout management, cleanups:
7     SPDX-FileCopyrightText: 1999 Dirk A. Mueller <dmuell@gmx.net>
8 
9     SPDX-License-Identifier: GPL-2.0-or-later
10 */
11 
12 #include "kcmmisc.h"
13 #include "ui_kcmmiscwidget.h"
14 
15 #include <QButtonGroup>
16 #include <QCheckBox>
17 #include <QDoubleSpinBox>
18 #include <QSpinBox>
19 #include <QWhatsThis>
20 #include <QX11Info>
21 
22 #include <KConfig>
23 #include <KConfigGroup>
24 #include <KLocalizedString>
25 #include <KSharedConfig>
26 
27 #include <X11/Xlib.h>
28 #include <cmath>
29 
30 namespace
31 {
hasAccentSupport()32 bool hasAccentSupport()
33 {
34     static bool isPlasmaIM = (qgetenv("QT_IM_MODULE") == "plasmaim");
35     return isPlasmaIM;
36 }
37 }
38 
KCMiscKeyboardWidget(QWidget * parent)39 KCMiscKeyboardWidget::KCMiscKeyboardWidget(QWidget *parent)
40     : QWidget(parent)
41     , ui(*new Ui_KeyboardConfigWidget)
42 {
43     ui.setupUi(this);
44 
45     ui.delay->setRange(100, 5000);
46     ui.delay->setSingleStep(50);
47     ui.rate->setRange(0.2, 100);
48     ui.rate->setSingleStep(5);
49 
50     sliderMax = (int)floor(0.5 + 2 * (log(5000.0L) - log(100.0L)) / (log(5000.0L) - log(4999.0L)));
51     ui.delaySlider->setRange(0, sliderMax);
52     ui.delaySlider->setSingleStep(sliderMax / 100);
53     ui.delaySlider->setPageStep(sliderMax / 10);
54     ui.delaySlider->setTickInterval(sliderMax / 10);
55 
56     ui.rateSlider->setRange(20, 10000);
57     ui.rateSlider->setSingleStep(30);
58     ui.rateSlider->setPageStep(500);
59     ui.rateSlider->setTickInterval(498);
60 
61     connect(ui.delay, SIGNAL(valueChanged(int)), this, SLOT(delaySpinboxChanged(int)));
62     connect(ui.delaySlider, &QAbstractSlider::valueChanged, this, &KCMiscKeyboardWidget::delaySliderChanged);
63     connect(ui.rate, SIGNAL(valueChanged(double)), this, SLOT(rateSpinboxChanged(double)));
64     connect(ui.rateSlider, &QAbstractSlider::valueChanged, this, &KCMiscKeyboardWidget::rateSliderChanged);
65 
66     _numlockButtonGroup = new QButtonGroup(ui.numlockButtonGroup);
67     _numlockButtonGroup->addButton(ui.radioButton1, 0);
68     _numlockButtonGroup->addButton(ui.radioButton2, 1);
69     _numlockButtonGroup->addButton(ui.radioButton3, 2);
70 
71     connect(_numlockButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(changed()));
72 
73     _keyboardRepeatButtonGroup = new QButtonGroup(ui.repeatFormLayout);
74     if (hasAccentSupport()) {
75         _keyboardRepeatButtonGroup->addButton(ui.accentMenuRadioButton, 0);
76     } else {
77         ui.accentMenuRadioButton->setVisible(false);
78     }
79     _keyboardRepeatButtonGroup->addButton(ui.repeatRadioButton, 1);
80     _keyboardRepeatButtonGroup->addButton(ui.nothingRadioButton, 2);
81 
82     connect(_keyboardRepeatButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(changed()));
83     connect(_keyboardRepeatButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(keyboardRepeatStateChanged(int)));
84 }
85 
~KCMiscKeyboardWidget()86 KCMiscKeyboardWidget::~KCMiscKeyboardWidget()
87 {
88     delete &ui;
89 }
90 
91 // set the slider and LCD values
setRepeat(KeyBehaviour keyboardRepeat,int delay_,double rate_)92 void KCMiscKeyboardWidget::setRepeat(KeyBehaviour keyboardRepeat, int delay_, double rate_)
93 {
94     if (keyboardRepeat == KeyBehaviour::AccentMenu && !hasAccentSupport()) {
95         keyboardRepeat = KeyBehaviour::RepeatKey;
96     }
97     _keyboardRepeatButtonGroup->button(keyboardRepeat)->click();
98     ui.delay->setValue(delay_);
99     ui.rate->setValue(rate_);
100     delaySpinboxChanged(delay_);
101     rateSpinboxChanged(rate_);
102 }
103 
getTriState(const QButtonGroup * group)104 TriState TriStateHelper::getTriState(const QButtonGroup *group)
105 {
106     int selected = group->checkedId();
107     return selected < 0 ? STATE_UNCHANGED : getTriState(selected);
108 }
109 
setTriState(QButtonGroup * group,TriState state)110 void TriStateHelper::setTriState(QButtonGroup *group, TriState state)
111 {
112     group->button(getInt(state))->click();
113 }
114 
load()115 void KCMiscKeyboardWidget::load()
116 {
117     KConfigGroup config(KSharedConfig::openConfig(QStringLiteral("kcminputrc"), KConfig::NoGlobals), "Keyboard");
118 
119     ui.delay->blockSignals(true);
120     ui.rate->blockSignals(true);
121 
122     // need to read as string to support old "true/false" parameter
123     QString key = config.readEntry("KeyRepeat", TriStateHelper::getString(STATE_ON));
124     if (key == QLatin1String("true") || key == TriStateHelper::getString(STATE_ON) || key == QLatin1String("accent")) {
125         keyboardRepeat = KeyBehaviour::AccentMenu;
126     } else if (key == QLatin1String("false") || key == TriStateHelper::getString(STATE_OFF) || key == QLatin1String("nothing")) {
127         keyboardRepeat = KeyBehaviour::DoNothing;
128     } else if (key == QLatin1String("repeat")) {
129         keyboardRepeat = KeyBehaviour::RepeatKey;
130     }
131 
132     //  keyboardRepeat = (key ? AutoRepeatModeOn : AutoRepeatModeOff);
133     int delay = config.readEntry("RepeatDelay", DEFAULT_REPEAT_DELAY);
134     double rate = config.readEntry("RepeatRate", DEFAULT_REPEAT_RATE);
135     setRepeat(keyboardRepeat, delay, rate);
136 
137     //  setRepeat(kbd.global_auto_repeat, ui.delay->value(), ui.rate->value());
138 
139     numlockState = TriStateHelper::getTriState(config.readEntry("NumLock", TriStateHelper::getInt(STATE_UNCHANGED)));
140     TriStateHelper::setTriState(_numlockButtonGroup, numlockState);
141 
142     ui.delay->blockSignals(false);
143     ui.rate->blockSignals(false);
144 }
145 
save()146 void KCMiscKeyboardWidget::save()
147 {
148     KConfigGroup config(KSharedConfig::openConfig(QStringLiteral("kcminputrc"), KConfig::NoGlobals), "Keyboard");
149 
150     keyboardRepeat = KeyBehaviour(_keyboardRepeatButtonGroup->checkedId());
151     numlockState = TriStateHelper::getTriState(_numlockButtonGroup);
152 
153     config.writeEntry("KeyRepeat", keybehaviourNames[KeyBehaviour(_keyboardRepeatButtonGroup->checkedId())], KConfig::Notify);
154     config.writeEntry("RepeatRate", ui.rate->value(), KConfig::Notify);
155     config.writeEntry("RepeatDelay", ui.delay->value(), KConfig::Notify);
156     config.writeEntry("NumLock", TriStateHelper::getInt(numlockState));
157     config.sync();
158 }
159 
defaults()160 void KCMiscKeyboardWidget::defaults()
161 {
162     setRepeat(KeyBehaviour::AccentMenu, DEFAULT_REPEAT_DELAY, DEFAULT_REPEAT_RATE);
163     TriStateHelper::setTriState(_numlockButtonGroup, STATE_UNCHANGED);
164     Q_EMIT changed(true);
165 }
166 
quickHelp() const167 QString KCMiscKeyboardWidget::quickHelp() const
168 {
169     return QString();
170 
171     /* "<h1>Keyboard</h1> This module allows you to choose options"
172        " for the way in which your keyboard works. The actual effect of"
173        " setting these options depends upon the features provided by your"
174        " keyboard hardware and the X server on which Plasma is running.<p>"
175        " For example, you may find that changing the key click volume"
176        " has no effect because this feature is not available on your system." */
177 }
178 
delaySliderChanged(int value)179 void KCMiscKeyboardWidget::delaySliderChanged(int value)
180 {
181     double alpha = sliderMax / (log(5000.0L) - log(100.0L));
182     double linearValue = exp(value / alpha + log(100.0L));
183 
184     ui.delay->setValue((int)floor(0.5 + linearValue));
185 
186     Q_EMIT changed(true);
187 }
188 
delaySpinboxChanged(int value)189 void KCMiscKeyboardWidget::delaySpinboxChanged(int value)
190 {
191     double alpha = sliderMax / (log(5000.0L) - log(100.0L));
192     double logVal = alpha * (log((double)value) - log(100.0L));
193 
194     ui.delaySlider->setValue((int)floor(0.5 + logVal));
195 
196     Q_EMIT changed(true);
197 }
198 
rateSliderChanged(int value)199 void KCMiscKeyboardWidget::rateSliderChanged(int value)
200 {
201     ui.rate->setValue(value / 100.0);
202 
203     Q_EMIT changed(true);
204 }
205 
rateSpinboxChanged(double value)206 void KCMiscKeyboardWidget::rateSpinboxChanged(double value)
207 {
208     ui.rateSlider->setValue((int)(value * 100));
209 
210     Q_EMIT changed(true);
211 }
212 
changed()213 void KCMiscKeyboardWidget::changed()
214 {
215     Q_EMIT changed(true);
216 }
217 
keyboardRepeatStateChanged(int selection)218 void KCMiscKeyboardWidget::keyboardRepeatStateChanged(int selection)
219 {
220     ui.keyboardRepeatParamsGroupBox->setVisible(selection == KeyBehaviour::RepeatKey);
221     changed();
222 }
223