/******************************************************************************* tdecm_touchpad A touchpad module for the TDE Control Centre Copyright © 2024 Mavridis Philippe 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 . *******************************************************************************/ // TQt #include #include #include #include #include #include #include #include #include #include // TDE #include #include #include #include #include // 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( "

Touchpad not found

" "Please check your system installation.
" ); } else IF_DRIVER(None) { error_str = i18n( "

Unsupported driver

" "

This module only supports the following drivers:" "

Libinput, Synaptics" ); } else error_str = i18n("

Unknown error

"); 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 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 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( "Warning: The Synaptics driver has been deprecated." ), settingsFrame); TQWhatsThis::add(l, i18n( "

The Synaptics driver is no longer in active development." "

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." )); 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(); }