1
/***************************************************************************
2
* Copyright (C) 2003 by Martin Koller *
4
* This file is part of the KDE Control Center Module for Joysticks *
6
* This program is free software; you can redistribute it and/or modify *
7
* it under the terms of the GNU General Public License as published by *
8
* the Free Software Foundation; either version 2 of the License, or *
9
* (at your option) any later version. *
11
* This program is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU General Public License for more details. *
16
* You should have received a copy of the GNU General Public License *
17
* along with this program; if not, write to the *
18
* Free Software Foundation, Inc., *
19
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
20
***************************************************************************/
21
#include "joywidget.h"
22
#include "joydevice.h"
23
#include "poswidget.h"
24
#include "caldialog.h"
26
#include <QtGui/QTableWidget>
30
#include <QFontMetrics>
31
#include <QPushButton>
32
#include <QHBoxLayout>
33
#include <QVBoxLayout>
34
#include <QHeaderView>
38
#include <kmessagebox.h>
39
#include <kiconloader.h>
40
#include <kcombobox.h>
41
#include <kurlcompletion.h>
46
//--------------------------------------------------------------
47
static QString PRESSED = I18N_NOOP("PRESSED");
48
//--------------------------------------------------------------
50
class TableWidget : public QTableWidget
53
TableWidget(int row, int col) : QTableWidget(row, col) {}
55
virtual QSize sizeHint() const
57
return QSize(150, 100); // return a smaller size than the Qt default(256, 192)
61
//--------------------------------------------------------------
63
JoyWidget::JoyWidget(QWidget *parent)
64
: QWidget(parent), idle(0), joydev(0)
66
QVBoxLayout *mainVbox = new QVBoxLayout(this);
67
mainVbox->setSpacing(KDialog::spacingHint());
68
mainVbox->setMargin(0);
70
// create area to show an icon + message if no joystick was detected
72
messageBox = new QFrame(this);
73
messageBox->setFrameStyle(QFrame::StyledPanel);
74
QHBoxLayout *box = new QHBoxLayout(messageBox);
75
box->setSpacing(KDialog::spacingHint());
77
QLabel *icon = new QLabel(messageBox);
78
icon->setPixmap(KIconLoader::global()->loadIcon("dialog-warning", KIconLoader::NoGroup,
79
KIconLoader::SizeMedium, KIconLoader::DefaultState, QStringList(), 0, true));
80
icon->setFixedSize(icon->sizeHint());
81
message = new QLabel(messageBox);
84
box->addWidget(message);
88
mainVbox->addWidget(messageBox);
91
QHBoxLayout *devHbox = new QHBoxLayout;
92
devHbox->setSpacing(KDialog::spacingHint());
93
devHbox->addWidget(new QLabel(i18n("Device:")));
94
devHbox->addWidget(device = new KComboBox(true));
96
device->setInsertPolicy(QComboBox::NoInsert);
97
KUrlCompletion *kc = new KUrlCompletion(KUrlCompletion::FileCompletion);
98
device->setCompletionObject(kc);
99
device->setAutoDeleteCompletionObject(true);
100
connect(device, SIGNAL(activated(const QString &)), this, SLOT(deviceChanged(const QString &)));
101
connect(device, SIGNAL(returnPressed(const QString &)), this, SLOT(deviceChanged(const QString &)));
102
devHbox->setStretchFactor(device, 3);
104
QHBoxLayout *hbox = new QHBoxLayout;
105
hbox->setSpacing(KDialog::spacingHint());
107
mainVbox->addLayout(devHbox);
108
mainVbox->addLayout(hbox);
110
QVBoxLayout *vboxLeft = new QVBoxLayout;
111
vboxLeft->setSpacing(KDialog::spacingHint());
112
vboxLeft->addWidget(new QLabel(i18nc("Cue for deflection of the stick", "Position:")));
113
vboxLeft->addWidget(xyPos = new PosWidget);
115
vboxLeft->addWidget(trace = new QCheckBox(i18n("Show trace")));
116
connect(trace, SIGNAL(toggled(bool)), this, SLOT(traceChanged(bool)));
118
QVBoxLayout *vboxMid = new QVBoxLayout;
119
vboxMid->setSpacing(KDialog::spacingHint());
121
QVBoxLayout *vboxRight = new QVBoxLayout;
122
vboxRight->setSpacing(KDialog::spacingHint());
124
// calculate the column width we need
125
QFontMetrics fm(font());
126
int colWidth = qMax(fm.width(PRESSED), fm.width("-32767")) + 10; // -32767 largest string
128
vboxMid->addWidget(new QLabel(i18n("Buttons:")));
129
buttonTbl = new TableWidget(0, 1);
130
buttonTbl->setSelectionMode(QAbstractItemView::NoSelection);
131
buttonTbl->setEditTriggers(QAbstractItemView::NoEditTriggers);
132
buttonTbl->setHorizontalHeaderLabels(QStringList(i18n("State")));
133
buttonTbl->setSortingEnabled(false);
134
buttonTbl->horizontalHeader()->setClickable(false);
135
buttonTbl->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
136
buttonTbl->horizontalHeader()->resizeSection(0, colWidth);
137
buttonTbl->verticalHeader()->setClickable(false);
138
vboxMid->addWidget(buttonTbl);
140
vboxRight->addWidget(new QLabel(i18n("Axes:")));
141
axesTbl = new TableWidget(0, 1);
142
axesTbl->setSelectionMode(QAbstractItemView::NoSelection);
143
axesTbl->setEditTriggers(QAbstractItemView::NoEditTriggers);
144
axesTbl->setHorizontalHeaderLabels(QStringList(i18n("Value")));
145
axesTbl->setSortingEnabled(false);
146
axesTbl->horizontalHeader()->setClickable(false);
147
axesTbl->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
148
axesTbl->horizontalHeader()->resizeSection(0, colWidth);
149
axesTbl->verticalHeader()->setClickable(false);
150
vboxRight->addWidget(axesTbl);
152
hbox->addLayout(vboxLeft);
153
hbox->addLayout(vboxMid);
154
hbox->addLayout(vboxRight);
157
calibrate = new QPushButton(i18n("Calibrate"));
158
connect(calibrate, SIGNAL(clicked()), this, SLOT(calibrateDevice()));
159
calibrate->setEnabled(false);
161
vboxLeft->addStretch();
162
vboxLeft->addWidget(calibrate);
164
// set up a timer for idle processing of joystick events
165
idle = new QTimer(this);
166
connect(idle, SIGNAL(timeout()), this, SLOT(checkDevice()));
168
// check which devicefiles we have
172
//--------------------------------------------------------------
174
JoyWidget::~JoyWidget()
179
//--------------------------------------------------------------
181
void JoyWidget::init()
183
// check which devicefiles we have
189
buttonTbl->setRowCount(0);
190
axesTbl->setRowCount(0);
192
for (i = 0; i < 5; i++) // check the first 5 devices
194
sprintf(dev, "/dev/js%d", i); // first look in /dev
195
JoyDevice *joy = new JoyDevice(dev);
197
if ( joy->open() != JoyDevice::SUCCESS )
200
sprintf(dev, "/dev/input/js%d", i); // then look in /dev/input
201
joy = new JoyDevice(dev);
203
if ( joy->open() != JoyDevice::SUCCESS )
206
continue; // try next number
212
device->addItem(QString("%1 (%2)").arg(joy->text()).arg(joy->device()));
214
// display values for first device
217
showDeviceProps(joy); // this sets the joy object into this->joydev
224
/* KDE 4: Remove this check(and i18n) when all KCM wrappers properly test modules */
225
if ( device->count() == 0 )
228
message->setWordWrap(true);
229
message->setText(QString("<qt><b>%1</b></qt>").arg(
230
i18n("No joystick device automatically found on this computer.<br />"
231
"Checks were done in /dev/js[0-4] and /dev/input/js[0-4]<br />"
232
"If you know that there is one attached, please enter the correct device file.")));
236
//--------------------------------------------------------------
238
void JoyWidget::traceChanged(bool state)
240
xyPos->showTrace(state);
243
//--------------------------------------------------------------
245
void JoyWidget::restoreCurrDev()
247
if ( !joydev ) // no device open
249
device->setEditText("");
250
calibrate->setEnabled(false);
254
// try to find the current open device in the combobox list
255
int index = device->findText(joydev->device(), Qt::MatchContains);
257
if ( index == -1 ) // the current open device is one the user entered (not in the list)
258
device->setEditText(joydev->device());
260
device->setEditText(device->itemText(index));
264
//--------------------------------------------------------------
266
void JoyWidget::deviceChanged(const QString &dev)
268
// find "/dev" in given string
272
if ( (start = dev.indexOf("/dev")) == -1 )
274
KMessageBox::sorry(this,
275
i18n("The given device name is invalid (does not contain /dev).\n"
276
"Please select a device from the list or\n"
277
"enter a device file, like /dev/js0."), i18n("Unknown Device"));
283
if ( (stop = dev.indexOf(")", start)) != -1 ) // seems to be text selected from our list
284
devName = dev.mid(start, stop - start);
286
devName = dev.mid(start);
288
if ( joydev && (devName == joydev->device()) ) return; // user selected the current device; ignore it
290
JoyDevice *joy = new JoyDevice(devName);
291
JoyDevice::ErrorCode ret = joy->open();
293
if ( ret != JoyDevice::SUCCESS )
295
KMessageBox::error(this, joy->errText(ret), i18n("Device Error"));
302
showDeviceProps(joy);
305
//--------------------------------------------------------------
307
void JoyWidget::showDeviceProps(JoyDevice *joy)
311
buttonTbl->setRowCount(joydev->numButtons());
313
axesTbl->setRowCount(joydev->numAxes());
314
if ( joydev->numAxes() >= 2 )
316
axesTbl->setVerticalHeaderItem(0, new QTableWidgetItem(i18n("1(x)")));
317
axesTbl->setVerticalHeaderItem(1, new QTableWidgetItem(i18n("2(y)")));
320
calibrate->setEnabled(true);
323
// make both tables use the same space for header; this looks nicer
324
// TODO: Don't know how to do this in Qt4; the following does no longer work
325
// Probably by setting a sizeHint for every single header item ?
327
buttonTbl->verticalHeader()->setFixedWidth(qMax(buttonTbl->verticalHeader()->width(),
328
axesTbl->verticalHeader()->width()));
329
axesTbl->verticalHeader()->setFixedWidth(buttonTbl->verticalHeader()->width());
333
//--------------------------------------------------------------
335
void JoyWidget::checkDevice()
337
if ( !joydev ) return; // no open device yet
339
JoyDevice::EventType type;
342
if ( !joydev->getEvent(type, number, value) )
345
if ( type == JoyDevice::BUTTON )
347
if ( ! buttonTbl->item(number, 0) )
348
buttonTbl->setItem(number, 0, new QTableWidgetItem());
350
if ( value == 0 ) // button release
351
buttonTbl->item(number, 0)->setText("-");
353
buttonTbl->item(number, 0)->setText(PRESSED);
356
if ( type == JoyDevice::AXIS )
358
if ( number == 0 ) // x-axis
359
xyPos->changeX(value);
361
if ( number == 1 ) // y-axis
362
xyPos->changeY(value);
364
if ( ! axesTbl->item(number, 0) )
365
axesTbl->setItem(number, 0, new QTableWidgetItem());
367
axesTbl->item(number, 0)->setText(QString("%1").arg(int(value)));
371
//--------------------------------------------------------------
373
void JoyWidget::calibrateDevice()
375
if ( !joydev ) return; // just to be save
377
JoyDevice::ErrorCode ret = joydev->initCalibration();
379
if ( ret != JoyDevice::SUCCESS )
381
KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error"));
385
if ( KMessageBox::messageBox(this, KMessageBox::Information,
386
i18n("<qt>Calibration is about to check the precision.<br /><br />"
387
"<b>Please move all axes to their center position and then "
388
"do not touch the joystick anymore.</b><br /><br />"
389
"Click OK to start the calibration.</qt>"),
391
KStandardGuiItem::ok(), KStandardGuiItem::cancel()) != KMessageBox::Ok )
394
idle->stop(); // stop the joystick event getting; this must be done inside the calibrate dialog
396
CalDialog dlg(this, joydev);
399
// user canceled somewhere during calibration, therefore the device is in a bad state
400
if ( dlg.result() == QDialog::Rejected )
401
joydev->restoreCorr();
403
idle->start(0); // continue with event getting
406
//--------------------------------------------------------------
408
void JoyWidget::resetCalibration()
410
if ( !joydev ) return; // just to be save
412
JoyDevice::ErrorCode ret = joydev->restoreCorr();
414
if ( ret != JoyDevice::SUCCESS )
416
KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error"));
420
KMessageBox::information(this,
421
i18n("Restored all calibration values for joystick device %1.", joydev->device()),
422
i18n("Calibration Success"));
426
//--------------------------------------------------------------
428
#include "joywidget.moc"