* Copyright (C) 2003,2012 by Martin Koller *
* kollix@aon.at *
* This file is part of the KDE Control Center Module for Joysticks *
* *
* 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 2 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, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#include "joywidget.h"
#include "joydevice.h"
#include "poswidget.h"
#include "caldialog.h"
#include <QTableWidget>
#include <QLabel>
#include <QCheckBox>
#include <QTimer>
#include <QFontMetrics>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QHeaderView>
#include <KLocalizedString>
#include <kdialog.h>
#include <kmessagebox.h>
#include <kiconloader.h>
#include <kcombobox.h>
#include <kurlcompletion.h>
#include <kmessagewidget.h>
#include <stdio.h>
#include <kvbox.h>
#include <kdebug.h>
static QString PRESSED = I18N_NOOP("PRESSED");
class TableWidget : public QTableWidget
{
public:
TableWidget(int row, int col) : QTableWidget(row, col) {}
QSize sizeHint() const override
{
return QSize(150, 100);
}
};
JoyWidget::JoyWidget(QWidget *parent)
: QWidget(parent), idle(nullptr), joydev(nullptr)
{
QVBoxLayout *mainVbox = new QVBoxLayout(this);
mainVbox->setSpacing(KDialog::spacingHint());
mainVbox->setContentsMargins(0, 0, 0, 0);
{
messageBox = new KMessageWidget(this);
messageBox->setMessageType(KMessageWidget::Error);
messageBox->setCloseButtonVisible(false);
messageBox->hide();
mainVbox->addWidget(messageBox);
}
QHBoxLayout *devHbox = new QHBoxLayout;
devHbox->setSpacing(KDialog::spacingHint());
devHbox->addWidget(new QLabel(i18n("Device:")));
devHbox->addWidget(device = new KComboBox(true));
device->setInsertPolicy(QComboBox::NoInsert);
KUrlCompletion *kc = new KUrlCompletion(KUrlCompletion::FileCompletion);
device->setCompletionObject(kc);
device->setAutoDeleteCompletionObject(true);
connect(device, SIGNAL(activated(QString)), this, SLOT(deviceChanged(QString)));
connect(device, SIGNAL(returnPressed(QString)), this, SLOT(deviceChanged(QString)));
devHbox->setStretchFactor(device, 3);
QHBoxLayout *hbox = new QHBoxLayout;
hbox->setSpacing(KDialog::spacingHint());
mainVbox->addLayout(devHbox);
mainVbox->addLayout(hbox);
QVBoxLayout *vboxLeft = new QVBoxLayout;
vboxLeft->setSpacing(KDialog::spacingHint());
vboxLeft->addWidget(new QLabel(i18nc("Cue for deflection of the stick", "Position:")));
vboxLeft->addWidget(xyPos = new PosWidget);
vboxLeft->addWidget(trace = new QCheckBox(i18n("Show trace")));
connect(trace, &QAbstractButton::toggled, this, &JoyWidget::traceChanged);
QVBoxLayout *vboxMid = new QVBoxLayout;
vboxMid->setSpacing(KDialog::spacingHint());
QVBoxLayout *vboxRight = new QVBoxLayout;
vboxRight->setSpacing(KDialog::spacingHint());
QFontMetrics fm(font());
int colWidth = qMax(fm.width(PRESSED), fm.width(QStringLiteral("-32767"))) + 10;
vboxMid->addWidget(new QLabel(i18n("Buttons:")));
buttonTbl = new TableWidget(0, 1);
buttonTbl->setSelectionMode(QAbstractItemView::NoSelection);
buttonTbl->setEditTriggers(QAbstractItemView::NoEditTriggers);
buttonTbl->setHorizontalHeaderLabels(QStringList(i18n("State")));
buttonTbl->setSortingEnabled(false);
buttonTbl->horizontalHeader()->setSectionsClickable(false);
buttonTbl->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
buttonTbl->horizontalHeader()->resizeSection(0, colWidth);
buttonTbl->verticalHeader()->setSectionsClickable(false);
vboxMid->addWidget(buttonTbl);
vboxRight->addWidget(new QLabel(i18n("Axes:")));
axesTbl = new TableWidget(0, 1);
axesTbl->setSelectionMode(QAbstractItemView::NoSelection);
axesTbl->setEditTriggers(QAbstractItemView::NoEditTriggers);
axesTbl->setHorizontalHeaderLabels(QStringList(i18n("Value")));
axesTbl->setSortingEnabled(false);
axesTbl->horizontalHeader()->setSectionsClickable(false);
axesTbl->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
axesTbl->horizontalHeader()->resizeSection(0, colWidth);
axesTbl->verticalHeader()->setSectionsClickable(false);
vboxRight->addWidget(axesTbl);
hbox->addLayout(vboxLeft);
hbox->addLayout(vboxMid);
hbox->addLayout(vboxRight);
calibrate = new QPushButton(i18n("Calibrate"));
connect(calibrate, &QAbstractButton::clicked, this, &JoyWidget::calibrateDevice);
calibrate->setEnabled(false);
vboxLeft->addStretch();
vboxLeft->addWidget(calibrate);
idle = new QTimer(this);
connect(idle, &QTimer::timeout, this, &JoyWidget::checkDevice);
init();
}
JoyWidget::~JoyWidget()
{
delete joydev;
}
void JoyWidget::init()
{
int i;
bool first = true;
char dev[30];
device->clear();
buttonTbl->setRowCount(0);
axesTbl->setRowCount(0);
for (i = 0; i < 5; i++)
{
sprintf(dev, "/dev/js%d", i);
JoyDevice *joy = new JoyDevice(dev);
if ( joy->open() != JoyDevice::SUCCESS )
{
delete joy;
sprintf(dev, "/dev/input/js%d", i);
joy = new JoyDevice(dev);
if ( joy->open() != JoyDevice::SUCCESS )
{
delete joy;
continue;
}
}
device->addItem(QStringLiteral("%1 (%2)").arg(joy->text()).arg(joy->device()));
if ( first )
{
showDeviceProps(joy);
first = false;
}
else
delete joy;
}
if ( device->count() == 0 )
{
messageBox->show();
messageBox->setText(QStringLiteral("<qt>%1</qt>").arg(
i18n("No joystick device automatically found on this computer.<br />"
"Checks were done in /dev/js[0-4] and /dev/input/js[0-4]<br />"
"If you know that there is one attached, please enter the correct device file.")));
}
}
void JoyWidget::traceChanged(bool state)
{
xyPos->showTrace(state);
}
void JoyWidget::restoreCurrDev()
{
if ( !joydev )
{
device->setEditText(QLatin1String(""));
calibrate->setEnabled(false);
}
else
{
int index = device->findText(joydev->device(), Qt::MatchContains);
if ( index == -1 )
device->setEditText(joydev->device());
else
device->setEditText(device->itemText(index));
}
}
void JoyWidget::deviceChanged(const QString &dev)
{
int start, stop;
QString devName;
if ( (start = dev.indexOf(QLatin1String("/dev"))) == -1 )
{
KMessageBox::sorry(this,
i18n("The given device name is invalid (does not contain /dev).\n"
"Please select a device from the list or\n"
"enter a device file, like /dev/js0."), i18n("Unknown Device"));
restoreCurrDev();
return;
}
if ( (stop = dev.indexOf(QLatin1Char(')'), start)) != -1 )
devName = dev.mid(start, stop - start);
else
devName = dev.mid(start);
if ( joydev && (devName == joydev->device()) ) return;
JoyDevice *joy = new JoyDevice(devName);
JoyDevice::ErrorCode ret = joy->open();
if ( ret != JoyDevice::SUCCESS )
{
KMessageBox::error(this, joy->errText(ret), i18n("Device Error"));
delete joy;
restoreCurrDev();
return;
}
showDeviceProps(joy);
}
void JoyWidget::showDeviceProps(JoyDevice *joy)
{
joydev = joy;
buttonTbl->setRowCount(joydev->numButtons());
axesTbl->setRowCount(joydev->numAxes());
if ( joydev->numAxes() >= 2 )
{
axesTbl->setVerticalHeaderItem(0, new QTableWidgetItem(i18n("1(x)")));
axesTbl->setVerticalHeaderItem(1, new QTableWidgetItem(i18n("2(y)")));
}
calibrate->setEnabled(true);
idle->start(0);
buttonTbl->verticalHeader()->setFixedWidth(qMax(buttonTbl->verticalHeader()->width(),
axesTbl->verticalHeader()->width()));
axesTbl->verticalHeader()->setFixedWidth(buttonTbl->verticalHeader()->width());
*/
}
void JoyWidget::checkDevice()
{
if ( !joydev ) return;
JoyDevice::EventType type;
int number, value;
if ( !joydev->getEvent(type, number, value) )
return;
if ( type == JoyDevice::BUTTON )
{
if ( ! buttonTbl->item(number, 0) )
buttonTbl->setItem(number, 0, new QTableWidgetItem());
if ( value == 0 )
buttonTbl->item(number, 0)->setText(QStringLiteral("-"));
else
buttonTbl->item(number, 0)->setText(PRESSED);
}
if ( type == JoyDevice::AXIS )
{
if ( number == 0 )
xyPos->changeX(value);
if ( number == 1 )
xyPos->changeY(value);
if ( ! axesTbl->item(number, 0) )
axesTbl->setItem(number, 0, new QTableWidgetItem());
axesTbl->item(number, 0)->setText(QStringLiteral("%1").arg(int(value)));
}
}
void JoyWidget::calibrateDevice()
{
if ( !joydev ) return;
JoyDevice::ErrorCode ret = joydev->initCalibration();
if ( ret != JoyDevice::SUCCESS )
{
KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error"));
return;
}
if ( KMessageBox::messageBox(this, KMessageBox::Information,
i18n("<qt>Calibration is about to check the precision.<br /><br />"
"<b>Please move all axes to their center position and then "
"do not touch the joystick anymore.</b><br /><br />"
"Click OK to start the calibration.</qt>"),
i18n("Calibration"),
KStandardGuiItem::ok(), KStandardGuiItem::cancel()) != KMessageBox::Ok )
return;
idle->stop();
CalDialog dlg(this, joydev);
dlg.calibrate();
if ( dlg.result() == QDialog::Rejected )
joydev->restoreCorr();
idle->start(0);
}
void JoyWidget::resetCalibration()
{
if ( !joydev ) return;
JoyDevice::ErrorCode ret = joydev->restoreCorr();
if ( ret != JoyDevice::SUCCESS )
{
KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error"));
}
else
{
KMessageBox::information(this,
i18n("Restored all calibration values for joystick device %1.", joydev->device()),
i18n("Calibration Success"));
}
}