You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/kcontrol/input/touchpad.cpp

529 lines
18 KiB

/*******************************************************************************
tdecm_touchpad
A touchpad module for the TDE Control Centre
Copyright © 2024 Mavridis Philippe <mavridisf@gmail.com>
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
// TQt
#include <tqwhatsthis.h>
#include <tqtooltip.h>
#include <tqtabwidget.h>
#include <tqradiobutton.h>
#include <tqbuttongroup.h>
#include <tqcombobox.h>
#include <tqcheckbox.h>
#include <tqslider.h>
#include <tqlayout.h>
#include <tqlabel.h>
// TDE
#include <tdeglobal.h>
#include <kiconloader.h>
#include <tdeaboutdata.h>
#include <kdialog.h>
#include <kdebug.h>
// TouchpadConfig
#include "touchpad_settings.h"
#include "touchpad.h"
#include "touchpad.moc"
/******************************* TouchpadConfig *******************************/
TouchpadConfig::TouchpadConfig(TQWidget *parent, const char *name)
: TDECModule(parent, name),
m_error(nullptr)
{
TDEGlobal::iconLoader()->addAppDir("kcminput");
d_settings = new TouchpadSettings;
d_settings->apply();
if (!d_settings->supportedTouchpad())
{
TQString error_str;
if (!d_settings->foundTouchpad())
{
error_str = i18n(
"<qt><h1>Touchpad not found</h1>"
"Please check your system installation.</qt>"
);
}
else IF_DRIVER(None)
{
error_str = i18n(
"<qt><h1>Unsupported driver</h1>"
"<p>This module only supports the following drivers:"
"<p>Libinput, Synaptics</qt>"
);
}
else error_str = i18n("<qt><h1>Unknown error</h1></qt>");
m_error = new TQLabel(error_str, this);
m_error->setAlignment(TQt::AlignCenter);
new TQVBoxLayout(this);
layout()->add(m_error);
return;
}
initWidgets();
load();
kdDebug() << "managed touchpad: " << d_settings->touchpad().name
<< " (xid = " << d_settings->touchpad().id << ")" << endl;
TDEAboutData* about = new TDEAboutData(
"tdecm_touchpad",
I18N_NOOP("Touchpad"),
0, 0,
TDEAboutData::License_GPL,
I18N_NOOP("(c) 2024 Mavridis Philippe")
);
about->addAuthor("Mavridis Philippe", 0, 0);
setAboutData(about);
}
TouchpadConfig::~TouchpadConfig()
{
DEL(m_error)
DEL(d_settings);
}
void TouchpadConfig::initWidgets()
{
// Create containers
m_container = new TQTabWidget(this);
TQFrame *touchpadWidget = new TQFrame(this);
touchpadWidget->setMargin(0);
new TQVBoxLayout(touchpadWidget);
// Enable option
TQFrame *enableCheckBox = new TQFrame(touchpadWidget);
enableCheckBox->setSizePolicy(TQSizePolicy::Maximum, TQSizePolicy::Fixed);
m_enabled = new TQCheckBox(i18n("Enable touchpad"), enableCheckBox);
TQWhatsThis::add(m_enabled, i18n(
"This option determines whether the touchpad is enabled or disabled"
));
// Compute margin for idented checkboxes based on checkbox height
int lmargin = m_enabled->height() / 2;
// Align the Enable checkbox with the other options below
new TQHBoxLayout(enableCheckBox);
enableCheckBox->layout()->addItem(new TQSpacerItem(lmargin, lmargin, TQSizePolicy::Fixed));
enableCheckBox->layout()->add(m_enabled);
// Settings frame
TQFrame *settingsFrame = new TQFrame(touchpadWidget);
TQGridLayout *grid = new TQGridLayout(settingsFrame, 3, 2, KDialog::spacingHint());
connect(m_enabled, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed()));
connect(m_enabled, TQ_SIGNAL(toggled(bool)), settingsFrame, TQ_SLOT(setEnabled(bool)));
// Behaviour
m_behaviour = new TQGroupBox(2, TQt::Vertical, i18n("Behaviour"), settingsFrame);
m_offWhileTyping = new TQCheckBox(i18n("Disable touchpad while typing"), m_behaviour);
TQWhatsThis::add(m_offWhileTyping, i18n(
"If this option is checked, the touchpad is disabled while you are typing, so as "
"to prevent accidental cursor movement and clicks."
));
connect(m_offWhileTyping, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed()));
m_mbEmulation = new TQCheckBox(i18n("Middle button emulation"), m_behaviour);
TQWhatsThis::add(m_mbEmulation, i18n(
"If this option is enabled, a simultaneous left and right button click is "
"automatically transformed into a middle button click."
));
IF_DRIVER(LibInput)
{
connect(m_mbEmulation, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed()));
}
else
{
DISABLE_UNSUPPORTED_OPTION(m_mbEmulation);
}
// Speed
m_speed = new TQGroupBox(4, TQt::Vertical, i18n("Speed"), settingsFrame);
TQLabel *accelLabel = new TQLabel(i18n("Acceleration:"), m_speed);
m_accel = new TQSlider(-100, 100, 5, 0, TQt::Horizontal, m_speed);
TQWidget *accelSliderMarkBox = new TQWidget(m_speed);
new TQHBoxLayout(accelSliderMarkBox);
accelSliderMarkBox->layout()->setAutoAdd(true);
TQLabel *l;
l = new TQLabel(i18n("Slower"), accelSliderMarkBox);
l->setAlignment(TQt::AlignLeft);
l = new TQLabel(i18n("Normal"), accelSliderMarkBox);
l->setAlignment(TQt::AlignHCenter);
l = new TQLabel(i18n("Faster"), accelSliderMarkBox);
l->setAlignment(TQt::AlignRight);
l = nullptr;
m_accelAdaptive = new TQCheckBox(i18n("Use adaptive profile"), m_speed);
IF_DRIVER(LibInput)
{
connect(m_accel, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(changed()));
connect(m_accelAdaptive, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed()));
// check available profiles
TQValueList<bool> accelProfilesAvail = d_settings->getAccelProfilesAvailability();
if (!accelProfilesAvail.count() || accelProfilesAvail[0] == 0 || accelProfilesAvail[1] == 0)
{
m_accelAdaptive->setEnabled(false);
}
}
else
{
DISABLE_UNSUPPORTED_OPTION(m_speed)
}
// Tapping
m_tapping = new TQGroupBox(5, TQt::Vertical, i18n("Tapping"), settingsFrame);
m_tapClick = new TQCheckBox(i18n("Tap to click"), m_tapping);
TQWhatsThis::add(m_tapClick, i18n(
"If this option is checked, a tap on the touchpad is interpreted as a button click."
));
connect(m_tapClick, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed()));
connect(m_tapClick, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(updateWidgetStates()));
m_tapDrag = new TQCheckBox(i18n("Tap-and-drag"), m_tapping);
TQWhatsThis::add(m_tapDrag, i18n(
"Tap-and-drag is a tap which is immediately followed by a finger down and that finger "
"being held down emulates a button press. Moving the finger around can thus drag the "
"selected item on the screen."
));
connect(m_tapDrag, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed()));
connect(m_tapDrag, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(updateWidgetStates()));
m_tapDragLock = new TQCheckBox(i18n("Tap-and-drag lock"), m_tapping);
TQWhatsThis::add(m_tapDragLock, i18n(
"When enabled, lifting a finger while dragging will not immediately stop dragging."
));
IF_DRIVER(LibInput)
{
connect(m_tapDragLock, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed()));
}
else
{
DISABLE_UNSUPPORTED_OPTION(m_tapDragLock);
}
TQLabel *tapMappingLabel = new TQLabel(i18n("Two-finger tap:"), m_tapping);
m_tapMapping = new TQComboBox(m_tapping); // "lrm" and "lmr"
m_tapMapping->setSizePolicy(TQSizePolicy::Maximum, TQSizePolicy::Fixed);
m_tapMapping->insertItem(
TDEGlobal::iconLoader()->loadIcon("mouse3", TDEIcon::Small),
i18n("Right click (three-finger tap for middle click)"),
0);
m_tapMapping->insertItem(
TDEGlobal::iconLoader()->loadIcon("mouse2", TDEIcon::Small),
i18n("Middle click (three-finger tap for right click)"),
1);
connect(m_tapMapping, TQ_SIGNAL(activated(const TQString&)), this, TQ_SLOT(changed()));
// Scrolling options
m_scrolling = new TQGroupBox(4, TQt::Vertical, i18n("Scrolling options"), settingsFrame);
m_verScroll = new TQCheckBox(i18n("Vertical scrolling"), m_scrolling);
TQWhatsThis::add(m_verScroll, i18n(
"This option enables/disables the vertical scrolling gesture on the touchpad. "
"(The actual gesture depends on the selected scroll method.) "
"Unless the used driver is Synaptics, disabling vertical scrolling also disables "
"horizontal scrolling."
));
connect(m_verScroll, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed()));
connect(m_verScroll, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(updateWidgetStates()));
m_horScroll = new TQCheckBox(i18n("Horizontal scrolling"), m_scrolling);
TQWhatsThis::add(m_horScroll, i18n(
"This option enables/disables the horizontal scrolling gesture on the touchpad. "
"(The actual gesture depends on the selected scroll method.)"
));
connect(m_horScroll, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed()));
connect(m_horScroll, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(updateWidgetStates()));
m_naturalScroll = new TQCheckBox(i18n("Reverse scroll direction"), m_scrolling);
TQWhatsThis::add(m_naturalScroll, i18n(
"If this option is checked, the scrolling direction is reversed to resemble natural "
"movement of content. This feature is also called natural scrolling."
));
connect(m_naturalScroll, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed()));
m_naturalScrollDirections = new TQFrame(m_scrolling);
TQWhatsThis::add(m_naturalScrollDirections, i18n(
"This option allows you to select the scrolling directions to which reversed scrolling will be applied. "
"It is only available if the Synaptics driver is used."
));
TQGridLayout *nsdl = new TQGridLayout(m_naturalScrollDirections, 2, 2, KDialog::spacingHint());
m_horNaturalScroll = new TQCheckBox(i18n("Apply to horizontal scrolling"), m_naturalScrollDirections);
m_verNaturalScroll = new TQCheckBox(i18n("Apply to vertical scrolling"), m_naturalScrollDirections);
nsdl->addItem(new TQSpacerItem(lmargin, lmargin, TQSizePolicy::Fixed), 0, 0);
nsdl->addItem(new TQSpacerItem(lmargin, lmargin, TQSizePolicy::Fixed), 1, 0);
nsdl->addWidget(m_horNaturalScroll, 0, 1);
nsdl->addWidget(m_verNaturalScroll, 1, 1);
IF_DRIVER(Synaptics)
{
connect(m_horNaturalScroll, TQ_SIGNAL(toggled(bool)), TQ_SLOT(changed()));
connect(m_verNaturalScroll, TQ_SIGNAL(toggled(bool)), TQ_SLOT(changed()));
connect(m_naturalScroll, TQ_SIGNAL(toggled(bool)), TQ_SLOT(updateWidgetStates()));
}
else
{
// Not only disable, but also force checkboxes to be checked on
// so that the user knows that the natural scrolling option applies
// always to both directions
DISABLE_UNSUPPORTED_OPTION(m_naturalScrollDirections);
m_horNaturalScroll->setChecked(true);
m_verNaturalScroll->setChecked(true);
}
// Scrolling methods
m_scrollMethods = new TQButtonGroup(3, TQt::Vertical, i18n("Scrolling method"), settingsFrame);
TQWhatsThis::add(m_scrollMethods, i18n(
"Here you can select your preferred scrolling method. The two most common options are "
"two-finger scrolling and edge scrolling. Two-finger scrolling entails a movement with "
"two fingers vertically or horizontally upon the surface of the touchpad. Edge scrolling "
"on the other hand tracks movements with one finger along the right or bottom edge of "
"the touchpad."
));
connect(m_scrollMethods, TQ_SIGNAL(clicked(int)), this, TQ_SLOT(changed()));
TQStringList scrollMethodLabels;
scrollMethodLabels << i18n("Two-finger")
<< i18n("Edge");
IF_DRIVER(LibInput)
{
scrollMethodLabels << i18n("Button");
}
TQValueList<bool> scrollMethodAvail = d_settings->getScrollMethodsAvailability();
Q_ASSERT(scrollMethodLabels.count() == scrollMethodAvail.count());
for (int i = 0; i < scrollMethodLabels.count(); ++i)
{
TQRadioButton *rad = new TQRadioButton(scrollMethodLabels[i], m_scrollMethods);
rad->setEnabled(scrollMethodAvail[i]);
}
// Finalize layout
grid->addWidget(m_behaviour, 0, 0);
grid->addWidget(m_speed, 1, 0);
grid->addMultiCellWidget(m_scrolling, 0, 1, 1, 1);
grid->addWidget(m_scrollMethods, 2, 1);
grid->addWidget(m_tapping, 2, 0);
grid->addItem(new TQSpacerItem(10, 10));
// Synaptics deprecation warning
IF_DRIVER(Synaptics)
{
TQLabel *l = new TQLabel(i18n(
"<qt><b>Warning:</b> The Synaptics driver has been deprecated.</qt>"
), settingsFrame);
TQWhatsThis::add(l, i18n(
"<qt><p><b>The Synaptics driver is no longer in active development.</b>"
"<p>While Libinput is the preferred choice for handling input devices, "
"you might still have valid reasons to use the older Synaptics driver "
"in its place. Please bear in mind that you will probably not receive "
"updates and bug fixes from its upstream.</qt>"
));
grid->addMultiCellWidget(l, 3, 3, 0, 1);
}
touchpadWidget->layout()->add(enableCheckBox);
touchpadWidget->layout()->add(settingsFrame);
m_container->addTab(touchpadWidget, SmallIconSet("input-touchpad"), d_settings->touchpad().name);
new TQVBoxLayout(this, KDialog::marginHint());
layout()->add(m_container);
}
// We handle more complex UI cases here
void TouchpadConfig::updateWidgetStates()
{
if (!d_settings->foundTouchpad()) return;
// Scrolling related options
bool on;
IF_DRIVER(LibInput)
{
// To disable vertical scrolling under LibInput one has to disable scrolling entirely
// so we mirror this in the UI
on = m_verScroll->isChecked();
m_horScroll->setEnabled(on);
}
else
{
// In case we can control both horizontal and vertical scrolling separately, any UI
// changes should be triggered when both are disabled
on = m_verScroll->isChecked() || m_horScroll->isChecked();
// Only enable natural scroll directions options when not under LibInput
m_naturalScrollDirections->setEnabled(on && m_naturalScroll->isChecked());
}
m_naturalScroll->setEnabled(on);
m_scrollMethods->setEnabled(on);
// Tapping related options
m_tapDrag->setEnabled(m_tapClick->isChecked());
IF_DRIVER(LibInput)
{
m_tapDragLock->setEnabled(m_tapClick->isChecked() && m_tapDrag->isChecked());
}
}
void TouchpadConfig::defaults()
{
load(true);
}
void TouchpadConfig::load()
{
load(false);
}
void TouchpadConfig::load(bool useDefaults)
{
if (!d_settings->foundTouchpad()) return;
d_settings->load();
m_enabled->setChecked(d_settings->enabled);
// Behaviour
m_offWhileTyping->setChecked(d_settings->offWhileTyping);
IF_DRIVER(LibInput)
{
m_mbEmulation->setChecked(d_settings->midButtonEmulation);
}
// Speed
IF_DRIVER(LibInput)
{
m_accel->setValue(d_settings->accelSpeed);
m_accelAdaptive->setChecked(d_settings->accelProfile == 0);
}
// Tapping
m_tapClick->setChecked(d_settings->tapClick);
m_tapDrag->setChecked(d_settings->tapDrag);
IF_DRIVER(LibInput)
{
m_tapDragLock->setChecked(d_settings->tapDragLock);
}
m_tapMapping->setCurrentItem(d_settings->tapMapping);
// Scrolling options
m_horScroll->setChecked(d_settings->scrollDirections & TQt::Horizontal);
m_verScroll->setChecked(d_settings->scrollDirections & TQt::Vertical);
m_naturalScroll->setChecked(d_settings->naturalScroll);
IF_DRIVER(Synaptics)
{
m_naturalScrollDirections->setEnabled(d_settings->naturalScroll);
m_horNaturalScroll->setChecked(d_settings->naturalScrollDirections & TQt::Horizontal);
m_verNaturalScroll->setChecked(d_settings->naturalScrollDirections & TQt::Vertical);
}
IF_DRIVER(LibInput)
{
m_horScroll->setEnabled(m_verScroll->isOn());
m_naturalScroll->setEnabled(m_verScroll->isOn());
m_scrollMethods->setEnabled(m_verScroll->isOn());
}
// Scrolling method
m_scrollMethods->setButton(d_settings->scrollMethod);
}
void TouchpadConfig::save()
{
if (!d_settings->foundTouchpad()) return;
d_settings->enabled = m_enabled->isChecked();
// Behaviour
d_settings->offWhileTyping = m_offWhileTyping->isChecked();
IF_DRIVER(LibInput)
{
d_settings->midButtonEmulation = m_mbEmulation->isChecked();
}
// Speed
IF_DRIVER(LibInput)
{
d_settings->accelSpeed = m_accel->value();
d_settings->accelProfile = (m_accelAdaptive->isChecked() ? 0 : 1);
}
// Tapping
d_settings->tapClick = m_tapClick->isChecked();
d_settings->tapDrag = m_tapDrag->isChecked();
IF_DRIVER(LibInput)
{
d_settings->tapDragLock = m_tapDragLock->isChecked();
}
d_settings->tapMapping = m_tapMapping->currentItem();
// Scrolling options
int scrollDirections = 0;
if (m_horScroll->isChecked()) scrollDirections |= TQt::Horizontal;
if (m_verScroll->isChecked()) scrollDirections |= TQt::Vertical;
d_settings->scrollDirections = scrollDirections;
d_settings->naturalScroll = m_naturalScroll->isChecked();
int naturalScrollDirections = 0;
if (m_horNaturalScroll->isChecked()) naturalScrollDirections |= TQt::Horizontal;
if (m_verNaturalScroll->isChecked()) naturalScrollDirections |= TQt::Vertical;
d_settings->naturalScrollDirections = naturalScrollDirections;
// Scrolling method
d_settings->scrollMethod = m_scrollMethods->selectedId();
d_settings->save();
d_settings->apply();
}
Touchpad TouchpadConfig::touchpad()
{
return d_settings->touchpad();
}