Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kcontrol / hardware / joystick / joywidget.cpp
blob3adb6ae3932879048296787d4262c377dfd9b2f8
1 /***************************************************************************
2 * Copyright (C) 2003 by Martin Koller *
3 * m.koller@surfeu.at *
4 * This file is part of the KDE Control Center Module for Joysticks *
5 * *
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. *
10 * *
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. *
15 * *
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>
27 #include <QLabel>
28 #include <QCheckBox>
29 #include <QTimer>
30 #include <QFontMetrics>
31 #include <QPushButton>
32 #include <QHBoxLayout>
33 #include <QVBoxLayout>
34 #include <QHeaderView>
36 #include <klocale.h>
37 #include <kdialog.h>
38 #include <kmessagebox.h>
39 #include <kiconloader.h>
40 #include <kcombobox.h>
41 #include <kurlcompletion.h>
43 #include <stdio.h>
44 #include <kvbox.h>
45 #include <kdebug.h>
46 //--------------------------------------------------------------
47 static QString PRESSED = I18N_NOOP("PRESSED");
48 //--------------------------------------------------------------
50 class TableWidget : public QTableWidget
52 public:
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);
83 box->addWidget(icon);
84 box->addWidget(message);
86 messageBox->hide();
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(i18n("Position:")));
113 vboxLeft->addWidget(xyPos = new PosWidget);
115 mainVbox->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);
156 // calibrate button
157 calibrate = new QPushButton(i18n("Calibrate"));
158 connect(calibrate, SIGNAL(clicked()), this, SLOT(calibrateDevice()));
159 calibrate->setEnabled(false);
161 mainVbox->addWidget(calibrate);
162 mainVbox->addStretch();
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
169 init();
172 //--------------------------------------------------------------
174 JoyWidget::~JoyWidget()
176 delete joydev;
179 //--------------------------------------------------------------
181 void JoyWidget::init()
183 // check which devicefiles we have
184 int i;
185 bool first = true;
186 char dev[30];
188 device->clear();
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 )
199 delete joy;
200 sprintf(dev, "/dev/input/js%d", i); // then look in /dev/input
201 joy = new JoyDevice(dev);
203 if ( joy->open() != JoyDevice::SUCCESS )
205 delete joy;
206 continue; // try next number
210 // we found one
212 device->addItem(QString("%1 (%2)").arg(joy->text()).arg(joy->device()));
214 // display values for first device
215 if ( first )
217 showDeviceProps(joy); // this sets the joy object into this->joydev
218 first = false;
220 else
221 delete joy;
224 /* KDE 4: Remove this check(and i18n) when all KCM wrappers properly test modules */
225 if ( device->count() == 0 )
227 messageBox->show();
228 message->setText(QString("<qt><b>%1</b></qt>").arg(
229 i18n("No joystick device automatically found on this computer.<br />"
230 "Checks were done in /dev/js[0-4] and /dev/input/js[0-4]<br />"
231 "If you know that there is one attached, please enter the correct device file.")));
235 //--------------------------------------------------------------
237 void JoyWidget::traceChanged(bool state)
239 xyPos->showTrace(state);
242 //--------------------------------------------------------------
244 void JoyWidget::restoreCurrDev()
246 if ( !joydev ) // no device open
248 device->setEditText("");
249 calibrate->setEnabled(false);
251 else
253 // try to find the current open device in the combobox list
254 int index = device->findText(joydev->device(), Qt::MatchContains);
256 if ( index == -1 ) // the current open device is one the user entered (not in the list)
257 device->setEditText(joydev->device());
258 else
259 device->setEditText(device->itemText(index));
263 //--------------------------------------------------------------
265 void JoyWidget::deviceChanged(const QString &dev)
267 // find "/dev" in given string
268 int start, stop;
269 QString devName;
271 if ( (start = dev.indexOf("/dev")) == -1 )
273 KMessageBox::sorry(this,
274 i18n("The given device name is invalid (does not contain /dev).\n"
275 "Please select a device from the list or\n"
276 "enter a device file, like /dev/js0."), i18n("Unknown Device"));
278 restoreCurrDev();
279 return;
282 if ( (stop = dev.indexOf(")", start)) != -1 ) // seems to be text selected from our list
283 devName = dev.mid(start, stop - start);
284 else
285 devName = dev.mid(start);
287 if ( joydev && (devName == joydev->device()) ) return; // user selected the current device; ignore it
289 JoyDevice *joy = new JoyDevice(devName);
290 JoyDevice::ErrorCode ret = joy->open();
292 if ( ret != JoyDevice::SUCCESS )
294 KMessageBox::error(this, joy->errText(ret), i18n("Device Error"));
296 delete joy;
297 restoreCurrDev();
298 return;
301 showDeviceProps(joy);
304 //--------------------------------------------------------------
306 void JoyWidget::showDeviceProps(JoyDevice *joy)
308 joydev = joy;
310 buttonTbl->setRowCount(joydev->numButtons());
312 axesTbl->setRowCount(joydev->numAxes());
313 if ( joydev->numAxes() >= 2 )
315 axesTbl->setVerticalHeaderItem(0, new QTableWidgetItem(i18n("1(x)")));
316 axesTbl->setVerticalHeaderItem(1, new QTableWidgetItem(i18n("2(y)")));
319 calibrate->setEnabled(true);
320 idle->start(0);
322 // make both tables use the same space for header; this looks nicer
323 // TODO: Don't know how to do this in Qt4; the following does no longer work
324 // Probably by setting a sizeHint for every single header item ?
326 buttonTbl->verticalHeader()->setFixedWidth(qMax(buttonTbl->verticalHeader()->width(),
327 axesTbl->verticalHeader()->width()));
328 axesTbl->verticalHeader()->setFixedWidth(buttonTbl->verticalHeader()->width());
332 //--------------------------------------------------------------
334 void JoyWidget::checkDevice()
336 if ( !joydev ) return; // no open device yet
338 JoyDevice::EventType type;
339 int number, value;
341 if ( !joydev->getEvent(type, number, value) )
342 return;
344 if ( type == JoyDevice::BUTTON )
346 if ( ! buttonTbl->item(number, 0) )
347 buttonTbl->setItem(number, 0, new QTableWidgetItem());
349 if ( value == 0 ) // button release
350 buttonTbl->item(number, 0)->setText("-");
351 else
352 buttonTbl->item(number, 0)->setText(PRESSED);
355 if ( type == JoyDevice::AXIS )
357 if ( number == 0 ) // x-axis
358 xyPos->changeX(value);
360 if ( number == 1 ) // y-axis
361 xyPos->changeY(value);
363 if ( ! axesTbl->item(number, 0) )
364 axesTbl->setItem(number, 0, new QTableWidgetItem());
366 axesTbl->item(number, 0)->setText(QString("%1").arg(int(value)));
370 //--------------------------------------------------------------
372 void JoyWidget::calibrateDevice()
374 if ( !joydev ) return; // just to be save
376 JoyDevice::ErrorCode ret = joydev->initCalibration();
378 if ( ret != JoyDevice::SUCCESS )
380 KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error"));
381 return;
384 if ( KMessageBox::messageBox(this, KMessageBox::Information,
385 i18n("<qt>Calibration is about to check the precision.<br /><br />"
386 "<b>Please move all axes to their center position and then "
387 "do not touch the joystick anymore.</b><br /><br />"
388 "Click OK to start the calibration.</qt>"),
389 i18n("Calibration"),
390 KStandardGuiItem::ok(), KStandardGuiItem::cancel()) != KMessageBox::Ok )
391 return;
393 idle->stop(); // stop the joystick event getting; this must be done inside the calibrate dialog
395 CalDialog dlg(this, joydev);
396 dlg.calibrate();
398 // user canceled somewhere during calibration, therefore the device is in a bad state
399 if ( dlg.result() == QDialog::Rejected )
400 joydev->restoreCorr();
402 idle->start(0); // continue with event getting
405 //--------------------------------------------------------------
407 void JoyWidget::resetCalibration()
409 if ( !joydev ) return; // just to be save
411 JoyDevice::ErrorCode ret = joydev->restoreCorr();
413 if ( ret != JoyDevice::SUCCESS )
415 KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error"));
417 else
419 KMessageBox::information(this,
420 i18n("Restored all calibration values for joystick device %1.", joydev->device()),
421 i18n("Calibration Success"));
425 //--------------------------------------------------------------
427 #include "joywidget.moc"