add more spacing
[personal-kdebase.git] / workspace / kcontrol / access / kaccess.cpp
blob76281991e5a17c480fbbeee46a12b8f1dba73fda
1 #include <unistd.h>
4 #include <QTimer>
5 #include <QPainter>
7 #include <QLabel>
8 #include <QtGui/QDesktopWidget>
9 #include <QGroupBox>
10 #include <QHBoxLayout>
11 #include <QVBoxLayout>
13 #include <KDialog>
14 #include <KMessageBox>
15 #include <KComboBox>
16 #include <KIconLoader>
17 #include <KDebug>
18 #include <KNotification>
19 #include <KConfig>
20 #include <KGlobal>
21 #include <KLocale>
22 #include <netwm.h>
23 #include <KShortcut>
24 #include <kwindowsystem.h>
25 #include <kkeyserver.h>
27 #include <X11/XKBlib.h>
28 #define XK_MISCELLANY
29 #define XK_XKB_KEYS
30 #include <X11/keysymdef.h>
33 #include "kaccess.moc"
34 #include <QX11Info>
35 #include <kvbox.h>
37 struct ModifierKey {
38 const unsigned int mask;
39 const KeySym keysym;
40 const char *name;
41 const char *lockedText;
42 const char *latchedText;
43 const char *unlatchedText;
46 static ModifierKey modifierKeys[] = {
47 { ShiftMask, 0, "Shift",
48 I18N_NOOP("The Shift key has been locked and is now active for all of the following keypresses."),
49 I18N_NOOP("The Shift key is now active."),
50 I18N_NOOP("The Shift key is now inactive.") },
51 { ControlMask, 0, "Control",
52 I18N_NOOP("The Control key has been locked and is now active for all of the following keypresses."),
53 I18N_NOOP("The Control key is now active."),
54 I18N_NOOP("The Control key is now inactive.") },
55 { 0, XK_Alt_L, "Alt",
56 I18N_NOOP("The Alt key has been locked and is now active for all of the following keypresses."),
57 I18N_NOOP("The Alt key is now active."),
58 I18N_NOOP("The Alt key is now inactive.") },
59 { 0, 0, "Win",
60 I18N_NOOP("The Win key has been locked and is now active for all of the following keypresses."),
61 I18N_NOOP("The Win key is now active."),
62 I18N_NOOP("The Win key is now inactive.") },
63 { 0, XK_Meta_L, "Meta",
64 I18N_NOOP("The Meta key has been locked and is now active for all of the following keypresses."),
65 I18N_NOOP("The Meta key is now active."),
66 I18N_NOOP("The Meta key is now inactive.") },
67 { 0, XK_Super_L, "Super",
68 I18N_NOOP("The Super key has been locked and is now active for all of the following keypresses."),
69 I18N_NOOP("The Super key is now active."),
70 I18N_NOOP("The Super key is now inactive.") },
71 { 0, XK_Hyper_L, "Hyper",
72 I18N_NOOP("The Hyper key has been locked and is now active for all of the following keypresses."),
73 I18N_NOOP("The Hyper key is now active."),
74 I18N_NOOP("The Hyper key is now inactive.") },
75 { 0, 0, "Alt Graph",
76 I18N_NOOP("The Alt Graph key has been locked and is now active for all of the following keypresses."),
77 I18N_NOOP("The Alt Graph key is now active."),
78 I18N_NOOP("The Alt Graph key is now inactive.") },
79 { 0, XK_Num_Lock, "Num Lock",
80 I18N_NOOP("The Num Lock key has been activated."),
81 "",
82 I18N_NOOP("The Num Lock key is now inactive.") },
83 { LockMask, 0, "Caps Lock",
84 I18N_NOOP("The Caps Lock key has been activated."),
85 "",
86 I18N_NOOP("The Caps Lock key is now inactive.") },
87 { 0, XK_Scroll_Lock, "Scroll Lock",
88 I18N_NOOP("The Scroll Lock key has been activated."),
89 "",
90 I18N_NOOP("The Scroll Lock key is now inactive.") },
91 { 0, 0, "", "", "", "" }
95 /********************************************************************/
98 KAccessApp::KAccessApp(bool allowStyles, bool GUIenabled)
99 : KUniqueApplication(allowStyles, GUIenabled),
100 overlay(0), _player(0)
102 _activeWindow = KWindowSystem::activeWindow();
103 connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(activeWindowChanged(WId)));
105 features = 0;
106 requestedFeatures = 0;
107 dialog = 0;
109 initMasks();
110 XkbStateRec state_return;
111 XkbGetState (QX11Info::display(), XkbUseCoreKbd, &state_return);
112 unsigned char latched = XkbStateMods (&state_return);
113 unsigned char locked = XkbModLocks (&state_return);
114 state = ((int)locked)<<8 | latched;
117 int KAccessApp::newInstance()
119 KGlobal::config()->reparseConfiguration();
120 readSettings();
121 return 0;
124 void KAccessApp::readSettings()
126 KSharedConfig::Ptr _config = KGlobal::config();
127 KConfigGroup cg(_config, "Bell");
129 // bell ---------------------------------------------------------------
130 _systemBell = cg.readEntry("SystemBell", true);
131 _artsBell = cg.readEntry("ArtsBell", false);
132 _currentPlayerSource = cg.readPathEntry("ArtsBellFile", QString());
133 _visibleBell = cg.readEntry("VisibleBell", false);
134 _visibleBellInvert = cg.readEntry("VisibleBellInvert", false);
135 _visibleBellColor = cg.readEntry("VisibleBellColor", QColor(Qt::red));
136 _visibleBellPause = cg.readEntry("VisibleBellPause", 500);
138 // select bell events if we need them
139 int state = (_artsBell || _visibleBell) ? XkbBellNotifyMask : 0;
140 XkbSelectEvents(QX11Info::display(), XkbUseCoreKbd, XkbBellNotifyMask, state);
142 // deactivate system bell if not needed
143 if (!_systemBell)
144 XkbChangeEnabledControls(QX11Info::display(), XkbUseCoreKbd, XkbAudibleBellMask, 0);
145 else
146 XkbChangeEnabledControls(QX11Info::display(), XkbUseCoreKbd, XkbAudibleBellMask, XkbAudibleBellMask);
148 // keyboard -------------------------------------------------------------
149 KConfigGroup keyboardGroup(_config,"Keyboard");
151 // get keyboard state
152 XkbDescPtr xkb = XkbGetMap(QX11Info::display(), 0, XkbUseCoreKbd);
153 if (!xkb)
154 return;
155 if (XkbGetControls(QX11Info::display(), XkbAllControlsMask, xkb) != Success)
156 return;
158 // sticky keys
159 if (keyboardGroup.readEntry("StickyKeys", false))
161 if (keyboardGroup.readEntry("StickyKeysLatch", true))
162 xkb->ctrls->ax_options |= XkbAX_LatchToLockMask;
163 else
164 xkb->ctrls->ax_options &= ~XkbAX_LatchToLockMask;
165 if (keyboardGroup.readEntry("StickyKeysAutoOff", false))
166 xkb->ctrls->ax_options |= XkbAX_TwoKeysMask;
167 else
168 xkb->ctrls->ax_options &= ~XkbAX_TwoKeysMask;
169 if (keyboardGroup.readEntry("StickyKeysBeep", false))
170 xkb->ctrls->ax_options |= XkbAX_StickyKeysFBMask;
171 else
172 xkb->ctrls->ax_options &= ~XkbAX_StickyKeysFBMask;
173 xkb->ctrls->enabled_ctrls |= XkbStickyKeysMask;
175 else
176 xkb->ctrls->enabled_ctrls &= ~XkbStickyKeysMask;
178 // toggle keys
179 if (keyboardGroup.readEntry("ToggleKeysBeep", false))
180 xkb->ctrls->ax_options |= XkbAX_IndicatorFBMask;
181 else
182 xkb->ctrls->ax_options &= ~XkbAX_IndicatorFBMask;
184 // slow keys
185 if (keyboardGroup.readEntry("SlowKeys", false)) {
186 if (keyboardGroup.readEntry("SlowKeysPressBeep", false))
187 xkb->ctrls->ax_options |= XkbAX_SKPressFBMask;
188 else
189 xkb->ctrls->ax_options &= ~XkbAX_SKPressFBMask;
190 if (keyboardGroup.readEntry("SlowKeysAcceptBeep", false))
191 xkb->ctrls->ax_options |= XkbAX_SKAcceptFBMask;
192 else
193 xkb->ctrls->ax_options &= ~XkbAX_SKAcceptFBMask;
194 if (keyboardGroup.readEntry("SlowKeysRejectBeep", false))
195 xkb->ctrls->ax_options |= XkbAX_SKRejectFBMask;
196 else
197 xkb->ctrls->ax_options &= ~XkbAX_SKRejectFBMask;
198 xkb->ctrls->enabled_ctrls |= XkbSlowKeysMask;
200 else
201 xkb->ctrls->enabled_ctrls &= ~XkbSlowKeysMask;
202 xkb->ctrls->slow_keys_delay = keyboardGroup.readEntry("SlowKeysDelay", 500);
204 // bounce keys
205 if (keyboardGroup.readEntry("BounceKeys", false)) {
206 if (keyboardGroup.readEntry("BounceKeysRejectBeep", false))
207 xkb->ctrls->ax_options |= XkbAX_BKRejectFBMask;
208 else
209 xkb->ctrls->ax_options &= ~XkbAX_BKRejectFBMask;
210 xkb->ctrls->enabled_ctrls |= XkbBounceKeysMask;
212 else
213 xkb->ctrls->enabled_ctrls &= ~XkbBounceKeysMask;
214 xkb->ctrls->debounce_delay = keyboardGroup.readEntry("BounceKeysDelay", 500);
216 // gestures for enabling the other features
217 _gestures = keyboardGroup.readEntry("Gestures", true);
218 if (_gestures)
219 xkb->ctrls->enabled_ctrls |= XkbAccessXKeysMask;
220 else
221 xkb->ctrls->enabled_ctrls &= ~XkbAccessXKeysMask;
223 // timeout
224 if (keyboardGroup.readEntry("AccessXTimeout", false))
226 xkb->ctrls->ax_timeout = keyboardGroup.readEntry("AccessXTimeoutDelay", 30)*60;
227 xkb->ctrls->axt_opts_mask = 0;
228 xkb->ctrls->axt_opts_values = 0;
229 xkb->ctrls->axt_ctrls_mask = XkbStickyKeysMask | XkbSlowKeysMask;
230 xkb->ctrls->axt_ctrls_values = 0;
231 xkb->ctrls->enabled_ctrls |= XkbAccessXTimeoutMask;
233 else
234 xkb->ctrls->enabled_ctrls &= ~XkbAccessXTimeoutMask;
236 // gestures for enabling the other features
237 if (keyboardGroup.readEntry("AccessXBeep", true))
238 xkb->ctrls->ax_options |= XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask;
239 else
240 xkb->ctrls->ax_options &= ~(XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask);
242 _gestureConfirmation = keyboardGroup.readEntry("GestureConfirmation", true);
244 _kNotifyModifiers = keyboardGroup.readEntry("kNotifyModifiers", false);
245 _kNotifyAccessX = keyboardGroup.readEntry("kNotifyAccessX", false);
247 // mouse-by-keyboard ----------------------------------------------
249 KConfigGroup mouseGroup(_config,"Mouse");
251 if (mouseGroup.readEntry("MouseKeys", false))
253 xkb->ctrls->mk_delay = mouseGroup.readEntry("MKDelay", 160);
255 // Default for initial velocity: 200 pixels/sec
256 int interval = mouseGroup.readEntry("MKInterval", 5);
257 xkb->ctrls->mk_interval = interval;
259 // Default time to reach maximum speed: 5000 msec
260 xkb->ctrls->mk_time_to_max = mouseGroup.readEntry("MKTimeToMax",
261 (5000+interval/2)/interval);
263 // Default maximum speed: 1000 pixels/sec
264 // (The old default maximum speed from KDE <= 3.4
265 // (100000 pixels/sec) was way too fast)
266 xkb->ctrls->mk_max_speed = mouseGroup.readEntry("MKMaxSpeed", interval);
268 xkb->ctrls->mk_curve = mouseGroup.readEntry("MKCurve", 0);
269 xkb->ctrls->mk_dflt_btn = mouseGroup.readEntry("MKDefaultButton", 0);
271 xkb->ctrls->enabled_ctrls |= XkbMouseKeysMask;
273 else
274 xkb->ctrls->enabled_ctrls &= ~XkbMouseKeysMask;
276 features = xkb->ctrls->enabled_ctrls & (XkbSlowKeysMask | XkbBounceKeysMask | XkbStickyKeysMask | XkbMouseKeysMask);
277 if (dialog == 0)
278 requestedFeatures = features;
279 // set state
280 XkbSetControls(QX11Info::display(), XkbControlsEnabledMask | XkbMouseKeysAccelMask | XkbStickyKeysMask | XkbSlowKeysMask | XkbBounceKeysMask | XkbAccessXKeysMask | XkbAccessXTimeoutMask, xkb);
282 // select AccessX events
283 XkbSelectEvents(QX11Info::display(), XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask);
285 if (!_artsBell && !_visibleBell && !_gestureConfirmation
286 && !_kNotifyModifiers && !_kNotifyAccessX) {
288 // We will exit, but the features need to stay configured
289 uint ctrls = XkbStickyKeysMask | XkbSlowKeysMask | XkbBounceKeysMask | XkbMouseKeysMask | XkbAudibleBellMask | XkbControlsNotifyMask;
290 uint values = xkb->ctrls->enabled_ctrls & ctrls;
291 XkbSetAutoResetControls(QX11Info::display(), ctrls, &ctrls, &values);
292 exit(0);
293 } else {
294 // reset them after program exit
295 uint ctrls = XkbStickyKeysMask | XkbSlowKeysMask | XkbBounceKeysMask | XkbMouseKeysMask | XkbAudibleBellMask | XkbControlsNotifyMask;
296 uint values = XkbAudibleBellMask;
297 XkbSetAutoResetControls(QX11Info::display(), ctrls, &ctrls, &values);
300 delete overlay;
301 overlay = 0;
304 static int maskToBit (int mask) {
305 for (int i = 0; i < 8; i++)
306 if (mask & (1 << i))
307 return i;
308 return -1;
311 void KAccessApp::initMasks() {
312 for (int i = 0; i < 8; i++)
313 keys [i] = -1;
314 state = 0;
316 for (int i = 0; strcmp (modifierKeys[i].name, "") != 0; i++) {
317 int mask = modifierKeys[i].mask;
318 if (mask == 0) {
319 if (modifierKeys[i].keysym != 0) {
320 mask = XkbKeysymToModifiers (QX11Info::display(), modifierKeys[i].keysym);
321 } else {
322 if (!strcmp(modifierKeys[i].name, "Win")) {
323 mask = KKeyServer::modXMeta();
324 } else {
325 mask = XkbKeysymToModifiers (QX11Info::display(), XK_Mode_switch)
326 | XkbKeysymToModifiers (QX11Info::display(), XK_ISO_Level3_Shift)
327 | XkbKeysymToModifiers (QX11Info::display(), XK_ISO_Level3_Latch)
328 | XkbKeysymToModifiers (QX11Info::display(), XK_ISO_Level3_Lock);
334 int bit = maskToBit (mask);
335 if (bit != -1 && keys[bit] == -1)
336 keys[bit] = i;
341 bool KAccessApp::x11EventFilter(XEvent *event)
343 // handle XKB events
344 if (event->type == xkb_opcode)
346 XkbAnyEvent *ev = (XkbAnyEvent*) event;
348 switch (ev->xkb_type) {
349 case XkbStateNotify:
350 xkbStateNotify();
351 break;
352 case XkbBellNotify:
353 xkbBellNotify((XkbBellNotifyEvent*)event);
354 break;
355 case XkbControlsNotify:
356 xkbControlsNotify((XkbControlsNotifyEvent*)event);
357 break;
359 return true;
362 // process other events as usual
363 return KApplication::x11EventFilter(event);
367 void VisualBell::paintEvent(QPaintEvent *event)
369 QWidget::paintEvent(event);
370 QTimer::singleShot(_pause, this, SLOT(hide()));
374 void KAccessApp::activeWindowChanged(WId wid)
376 _activeWindow = wid;
380 void KAccessApp::xkbStateNotify () {
381 XkbStateRec state_return;
382 XkbGetState (QX11Info::display(), XkbUseCoreKbd, &state_return);
383 unsigned char latched = XkbStateMods (&state_return);
384 unsigned char locked = XkbModLocks (&state_return);
385 int mods = ((int)locked)<<8 | latched;
387 if (state != mods) {
388 if (_kNotifyModifiers)
389 for (int i = 0; i < 8; i++) {
390 if (keys[i] != -1) {
391 if ( !strcmp(modifierKeys[keys[i]].latchedText, "")
392 && ( (((mods >> i) & 0x101) != 0) != (((state >> i) & 0x101) != 0) ))
394 if ((mods >> i) & 1) {
395 KNotification::event ("lockkey-locked", i18n(modifierKeys[keys[i]].lockedText));
397 else {
398 KNotification::event ("lockkey-unlocked", i18n(modifierKeys[keys[i]].unlatchedText));
401 else if (strcmp(modifierKeys[keys[i]].latchedText, "")
402 && ( ((mods >> i) & 0x101) != ((state >> i) & 0x101) ))
404 if ((mods >> i) & 0x100) {
405 KNotification::event ("modifierkey-locked", i18n(modifierKeys[keys[i]].lockedText));
407 else if ((mods >> i) & 1) {
408 KNotification::event ( "modifierkey-latched", i18n(modifierKeys[keys[i]].latchedText));
410 else {
411 KNotification::event ("modifierkey-unlatched", i18n(modifierKeys[keys[i]].unlatchedText));
416 state = mods;
420 void KAccessApp::xkbBellNotify(XkbBellNotifyEvent *event)
422 // bail out if we should not really ring
423 if (event->event_only)
424 return;
426 // flash the visible bell
427 if (_visibleBell)
429 // create overlay widget
430 if (!overlay)
431 overlay = new VisualBell(_visibleBellPause);
433 WId id = _activeWindow;
435 NETRect frame, window;
436 NETWinInfo net(QX11Info::display(), id, desktop()->winId(), 0);
438 net.kdeGeometry(frame, window);
440 overlay->setGeometry(window.pos.x, window.pos.y, window.size.width, window.size.height);
442 if (_visibleBellInvert)
444 QPixmap screen = QPixmap::grabWindow(id, 0, 0, window.size.width, window.size.height);
445 #ifdef __GNUC__
446 #warning is this the best way to invert a pixmap?
447 #endif
448 // QPixmap invert(window.size.width, window.size.height);
449 QImage i = screen.toImage();
450 i.invertPixels();
451 QPalette pal = overlay->palette();
452 pal.setBrush(overlay->backgroundRole(), QBrush(QPixmap::fromImage(i)));
453 overlay->setPalette(pal);
455 QPainter p(&invert);
456 p.setRasterOp(QPainter::NotCopyROP);
457 p.drawPixmap(0, 0, screen);
458 overlay->setBackgroundPixmap(invert);
461 else
463 QPalette pal = overlay->palette();
464 pal.setColor(overlay->backgroundRole(), _visibleBellColor);
465 overlay->setPalette(pal);
468 // flash the overlay widget
469 overlay->raise();
470 overlay->show();
471 flush();
474 // ask Phonon to ring a nice bell
475 if (_artsBell) {
476 if (!_player) { // as creating the player is expensive, delay the creation
477 _player = Phonon::createPlayer(Phonon::AccessibilityCategory);
478 _player->setParent(this);
479 _player->setCurrentSource(_currentPlayerSource);
481 _player->play();
485 QString mouseKeysShortcut (Display *display) {
486 // Calculate the keycode
487 KeySym sym = XK_MouseKeys_Enable;
488 KeyCode code = XKeysymToKeycode(display, sym);
489 if (code == 0) {
490 sym = XK_Pointer_EnableKeys;
491 code = XKeysymToKeycode(display, sym);
492 if (code == 0)
493 return ""; // No shortcut available?
496 // Calculate the modifiers by searching the keysym in the X keyboard mapping
497 XkbDescPtr xkbdesc = XkbGetMap(display, XkbKeyTypesMask | XkbKeySymsMask, XkbUseCoreKbd);
499 if (!xkbdesc)
500 return ""; // Failed to obtain the mapping from server
502 bool found = false;
503 unsigned char modifiers = 0;
504 int groups = XkbKeyNumGroups(xkbdesc, code);
505 for (int grp = 0; grp < groups && !found; grp++)
507 int levels = XkbKeyGroupWidth(xkbdesc, code, grp);
508 for (int level = 0; level < levels && !found; level++)
510 if (sym == XkbKeySymEntry(xkbdesc, code, level, grp))
512 // keysym found => determine modifiers
513 int typeIdx = xkbdesc->map->key_sym_map[code].kt_index[grp];
514 XkbKeyTypePtr type = &(xkbdesc->map->types[typeIdx]);
515 for (int i = 0; i < type->map_count && !found; i++)
517 if (type->map[i].active && (type->map[i].level == level))
519 modifiers = type->map[i].mods.mask;
520 found = true;
526 XkbFreeClientMap (xkbdesc, 0, true);
528 if (!found)
529 return ""; // Somehow the keycode -> keysym mapping is flawed
531 XEvent ev;
532 ev.xkey.display = display;
533 ev.xkey.keycode = code;
534 ev.xkey.state = 0;
535 int key;
536 KKeyServer::xEventToQt(&ev, &key);
537 QString keyname = QKeySequence(key).toString();
539 unsigned int AltMask = KKeyServer::modXAlt();
540 unsigned int WinMask = KKeyServer::modXMeta();
541 unsigned int NumMask = KKeyServer::modXNumLock();
542 unsigned int ScrollMask= KKeyServer::modXScrollLock();
544 unsigned int MetaMask = XkbKeysymToModifiers (display, XK_Meta_L);
545 unsigned int SuperMask = XkbKeysymToModifiers (display, XK_Super_L);
546 unsigned int HyperMask = XkbKeysymToModifiers (display, XK_Hyper_L);
547 unsigned int AltGrMask = XkbKeysymToModifiers (display, XK_Mode_switch)
548 | XkbKeysymToModifiers (display, XK_ISO_Level3_Shift)
549 | XkbKeysymToModifiers (display, XK_ISO_Level3_Latch)
550 | XkbKeysymToModifiers (display, XK_ISO_Level3_Lock);
552 unsigned int mods = ShiftMask | ControlMask | AltMask | WinMask
553 | LockMask | NumMask | ScrollMask;
555 AltGrMask &= ~mods;
556 MetaMask &= ~(mods | AltGrMask);
557 SuperMask &= ~(mods | AltGrMask | MetaMask);
558 HyperMask &= ~(mods | AltGrMask | MetaMask | SuperMask);
560 if ((modifiers & AltGrMask) != 0)
561 keyname = i18n("AltGraph") + '+' + keyname;
562 if ((modifiers & HyperMask) != 0)
563 keyname = i18n("Hyper") + '+' + keyname;
564 if ((modifiers & SuperMask) != 0)
565 keyname = i18n("Super") + '+' + keyname;
566 if ((modifiers & WinMask) != 0)
567 keyname = i18n("Meta") + '+' + keyname;
568 if ((modifiers & WinMask) != 0)
569 keyname = QKeySequence(Qt::META).toString() + '+' + keyname;
570 if ((modifiers & AltMask) != 0)
571 keyname = QKeySequence(Qt::ALT).toString() + '+' + keyname;
572 if ((modifiers & ControlMask) != 0)
573 keyname = QKeySequence(Qt::CTRL).toString() + '+' + keyname;
574 if ((modifiers & ShiftMask) != 0)
575 keyname = QKeySequence(Qt::SHIFT).toString() + '+' + keyname;
577 return keyname;
580 void KAccessApp::createDialogContents() {
581 if (dialog == 0) {
582 dialog = new KDialog( 0 );
583 dialog->setCaption( i18n("Warning") );
584 dialog->setButtons( KDialog::Yes | KDialog::No );
585 dialog->setButtonGuiItem( KDialog::Yes, KStandardGuiItem::yes() );
586 dialog->setButtonGuiItem( KDialog::No, KStandardGuiItem::no() );
587 dialog->setEscapeButton( KDialog::Close );
588 dialog->setObjectName( "AccessXWarning" );
589 dialog->setModal( true );
590 dialog->showButtonSeparator( true );
592 KVBox *topcontents = new KVBox (dialog);
593 topcontents->setSpacing(KDialog::spacingHint()*2);
594 #ifdef __GNUC__
595 #warning "kde4 fixme"
596 #endif
597 //topcontents->setMargin(KDialog::marginHint());
599 QWidget *contents = new QWidget(topcontents);
600 QHBoxLayout * lay = new QHBoxLayout(contents);
601 lay->setSpacing(KDialog::spacingHint());
603 QLabel *label1 = new QLabel( contents);
604 QPixmap pixmap = KIconLoader::global()->loadIcon("dialog-warning", KIconLoader::NoGroup, KIconLoader::SizeMedium, KIconLoader::DefaultState, QStringList(), 0, true);
605 if (pixmap.isNull())
606 pixmap = QMessageBox::standardIcon(QMessageBox::Warning);
607 label1->setPixmap(pixmap);
609 lay->addWidget( label1, 0, Qt::AlignCenter );
610 lay->addSpacing(KDialog::spacingHint());
612 QVBoxLayout * vlay = new QVBoxLayout();
613 lay->addItem( vlay );
615 featuresLabel = new QLabel( "", contents );
616 featuresLabel->setAlignment( Qt::AlignVCenter );
617 featuresLabel->setWordWrap( true );
618 vlay->addWidget( featuresLabel );
619 vlay->addStretch();
621 QHBoxLayout * hlay = new QHBoxLayout();
622 vlay->addItem(hlay);
624 QLabel *showModeLabel = new QLabel( i18n("&When a gesture was used:"), contents );
625 hlay->addWidget( showModeLabel );
627 showModeCombobox = new KComboBox (contents);
628 hlay->addWidget( showModeCombobox );
629 showModeLabel->setBuddy(showModeCombobox);
630 showModeCombobox->insertItem ( 0, i18n("Change Settings Without Asking"));
631 showModeCombobox->insertItem ( 1, i18n("Show This Confirmation Dialog"));
632 showModeCombobox->insertItem ( 2, i18n("Deactivate All AccessX Features & Gestures"));
633 showModeCombobox->setCurrentIndex (1);
635 dialog->setMainWidget(topcontents);
636 dialog->showButtonSeparator(false);
638 connect (dialog, SIGNAL(yesClicked()), this, SLOT(yesClicked()));
639 connect (dialog, SIGNAL(noClicked()), this, SLOT(noClicked()));
640 connect (dialog, SIGNAL(closeClicked()), this, SLOT(dialogClosed()));
644 void KAccessApp::xkbControlsNotify(XkbControlsNotifyEvent *event)
646 unsigned int newFeatures = event->enabled_ctrls & (XkbSlowKeysMask | XkbBounceKeysMask | XkbStickyKeysMask | XkbMouseKeysMask);
648 if (newFeatures != features) {
649 unsigned int enabled = newFeatures & ~features;
650 unsigned int disabled = features & ~newFeatures;
652 if (!_gestureConfirmation) {
653 requestedFeatures = enabled | (requestedFeatures & ~disabled);
654 notifyChanges();
655 features = newFeatures;
657 else {
658 // set the AccessX features back to what they were. We will
659 // apply the changes later if the user allows us to do that.
660 readSettings();
662 requestedFeatures = enabled | (requestedFeatures & ~disabled);
664 enabled = requestedFeatures & ~features;
665 disabled = features & ~requestedFeatures;
667 QStringList enabledFeatures;
668 QStringList disabledFeatures;
670 if (enabled & XkbStickyKeysMask)
671 enabledFeatures << i18n("Sticky keys");
672 else if (disabled & XkbStickyKeysMask)
673 disabledFeatures << i18n("Sticky keys");
675 if (enabled & XkbSlowKeysMask)
676 enabledFeatures << i18n("Slow keys");
677 else if (disabled & XkbSlowKeysMask)
678 disabledFeatures << i18n("Slow keys");
680 if (enabled & XkbBounceKeysMask)
681 enabledFeatures << i18n("Bounce keys");
682 else if (disabled & XkbBounceKeysMask)
683 disabledFeatures << i18n("Bounce keys");
685 if (enabled & XkbMouseKeysMask)
686 enabledFeatures << i18n("Mouse keys");
687 else if (disabled & XkbMouseKeysMask)
688 disabledFeatures << i18n("Mouse keys");
690 QString question;
691 switch (enabledFeatures.count()) {
692 case 0: switch (disabledFeatures.count()) {
693 case 1: question = i18n("Do you really want to deactivate \"%1\"?",
694 disabledFeatures[0]);
695 break;
696 case 2: question = i18n("Do you really want to deactivate \"%1\" and \"%2\"?",
697 disabledFeatures[0], disabledFeatures[1]);
698 break;
699 case 3: question = i18n("Do you really want to deactivate \"%1\", \"%2\" and \"%3\"?",
700 disabledFeatures[0], disabledFeatures[1],
701 disabledFeatures[2]);
702 break;
703 case 4: question = i18n("Do you really want to deactivate \"%1\", \"%2\", \"%3\" and \"%4\"?",
704 disabledFeatures[0], disabledFeatures[1],
705 disabledFeatures[2], disabledFeatures[3]);
706 break;
708 break;
709 case 1: switch (disabledFeatures.count()) {
710 case 0: question = i18n("Do you really want to activate \"%1\"?",
711 enabledFeatures[0]);
712 break;
713 case 1: question = i18n("Do you really want to activate \"%1\" and to deactivate \"%2\"?",
714 enabledFeatures[0], disabledFeatures[0]);
715 break;
716 case 2: question = i18n("Do you really want to activate \"%1\" and to deactivate \"%2\" and \"%3\"?",
717 enabledFeatures[0], disabledFeatures[0],
718 disabledFeatures[1]);
719 break;
720 case 3: question = i18n("Do you really want to activate \"%1\" and to deactivate \"%2\", \"%3\" and \"%4\"?",
721 enabledFeatures[0], disabledFeatures[0],
722 disabledFeatures[1], disabledFeatures[2]);
723 break;
725 break;
726 case 2: switch (disabledFeatures.count()) {
727 case 0: question = i18n("Do you really want to activate \"%1\" and \"%2\"?",
728 enabledFeatures[0], enabledFeatures[1]);
729 break;
730 case 1: question = i18n("Do you really want to activate \"%1\" and \"%2\" and to deactivate \"%3\"?",
731 enabledFeatures[0], enabledFeatures[1],
732 disabledFeatures[0]);
733 break;
734 case 2: question = i18n("Do you really want to activate \"%1\", and \"%2\" and to deactivate \"%3\" and \"%4\"?",
735 enabledFeatures[0], enabledFeatures[1],
736 enabledFeatures[0], disabledFeatures[1]);
737 break;
739 break;
740 case 3: switch (disabledFeatures.count()) {
741 case 0: question = i18n("Do you really want to activate \"%1\", \"%2\" and \"%3\"?",
742 enabledFeatures[0], enabledFeatures[1],
743 enabledFeatures[2]);
744 break;
745 case 1: question = i18n("Do you really want to activate \"%1\", \"%2\" and \"%3\" and to deactivate \"%4\"?",
746 enabledFeatures[0], enabledFeatures[1],
747 enabledFeatures[2], disabledFeatures[0]);
748 break;
750 break;
751 case 4: question = i18n("Do you really want to activate \"%1\", \"%2\", \"%3\" and \"%4\"?",
752 enabledFeatures[0], enabledFeatures[1],
753 enabledFeatures[2], enabledFeatures[3]);
754 break;
756 QString explanation;
757 if (enabledFeatures.count()+disabledFeatures.count() == 1) {
758 explanation = i18n("An application has requested to change this setting.");
760 if (_gestures) {
761 if ((enabled | disabled) == XkbSlowKeysMask)
762 explanation = i18n("You held down the Shift key for 8 seconds or an application has requested to change this setting.");
763 else if ((enabled | disabled) == XkbStickyKeysMask)
764 explanation = i18n("You pressed the Shift key 5 consecutive times or an application has requested to change this setting.");
765 else if ((enabled | disabled) == XkbMouseKeysMask) {
766 QString shortcut = mouseKeysShortcut(QX11Info::display());
767 if (!shortcut.isEmpty() && !shortcut.isNull())
768 explanation = i18n("You pressed %1 or an application has requested to change this setting.", shortcut);
772 else {
773 if (_gestures)
774 explanation = i18n("An application has requested to change these settings, or you used a combination of several keyboard gestures.");
775 else
776 explanation = i18n("An application has requested to change these settings.");
779 createDialogContents();
780 featuresLabel->setText ( question+"\n\n"+explanation
781 +" "+i18n("These AccessX settings are needed for some users with motion impairments and can be configured in the KDE Control Center. You can also turn them on and off with standardized keyboard gestures.\n\nIf you do not need them, you can select \"Deactivate all AccessX features and gestures\".") );
783 KWindowSystem::setState( dialog->winId(), NET::KeepAbove );
784 kapp->updateUserTimestamp();
785 dialog->show();
790 void KAccessApp::notifyChanges() {
791 if (!_kNotifyAccessX)
792 return;
794 unsigned int enabled = requestedFeatures & ~features;
795 unsigned int disabled = features & ~requestedFeatures;
797 if (enabled & XkbSlowKeysMask)
798 KNotification::event ("slowkeys", i18n("Slow keys has been enabled. From now on, you need to press each key for a certain length of time before it gets accepted."));
799 else if (disabled & XkbSlowKeysMask)
800 KNotification::event ("slowkeys", i18n("Slow keys has been disabled."));
802 if (enabled & XkbBounceKeysMask)
803 KNotification::event ("bouncekeys", i18n("Bounce keys has been enabled. From now on, each key will be blocked for a certain length of time after it was used."));
804 else if (disabled & XkbBounceKeysMask)
805 KNotification::event ("bouncekeys", i18n("Bounce keys has been disabled."));
807 if (enabled & XkbStickyKeysMask)
808 KNotification::event ("stickykeys", i18n("Sticky keys has been enabled. From now on, modifier keys will stay latched after you have released them."));
809 else if (disabled & XkbStickyKeysMask)
810 KNotification::event ("stickykeys", i18n("Sticky keys has been disabled."));
812 if (enabled & XkbMouseKeysMask)
813 KNotification::event ("mousekeys", i18n("Mouse keys has been enabled. From now on, you can use the number pad of your keyboard in order to control the mouse."));
814 else if (disabled & XkbMouseKeysMask)
815 KNotification::event ("mousekeys", i18n("Mouse keys has been disabled."));
818 void KAccessApp::applyChanges() {
819 notifyChanges();
820 unsigned int enabled = requestedFeatures & ~features;
821 unsigned int disabled = features & ~requestedFeatures;
823 KConfigGroup config(KGlobal::config(), "Keyboard");
825 if (enabled & XkbSlowKeysMask)
826 config.writeEntry("SlowKeys", true);
827 else if (disabled & XkbSlowKeysMask)
828 config.writeEntry("SlowKeys", false);
830 if (enabled & XkbBounceKeysMask)
831 config.writeEntry("BounceKeys", true);
832 else if (disabled & XkbBounceKeysMask)
833 config.writeEntry("BounceKeys", false);
835 if (enabled & XkbStickyKeysMask)
836 config.writeEntry("StickyKeys", true);
837 else if (disabled & XkbStickyKeysMask)
838 config.writeEntry("StickyKeys", false);
840 KConfigGroup mousegrp(KGlobal::config(),"Mouse");
842 if (enabled & XkbMouseKeysMask)
843 mousegrp.writeEntry("MouseKeys", true);
844 else if (disabled & XkbMouseKeysMask)
845 mousegrp.writeEntry("MouseKeys", false);
846 mousegrp.sync();
847 config.sync();
850 void KAccessApp::yesClicked() {
851 if (dialog != 0)
852 dialog->deleteLater();
853 dialog = 0;
855 KConfigGroup config(KGlobal::config(), "Keyboard");
856 switch (showModeCombobox->currentIndex()) {
857 case 0:
858 config.writeEntry("Gestures", true);
859 config.writeEntry("GestureConfirmation", false);
860 break;
861 default:
862 config.writeEntry("Gestures", true);
863 config.writeEntry("GestureConfirmation", true);
864 break;
865 case 2:
866 requestedFeatures = 0;
867 config.writeEntry("Gestures", false);
868 config.writeEntry("GestureConfirmation", true);
870 config.sync();
872 if (features != requestedFeatures) {
873 notifyChanges();
874 applyChanges();
876 readSettings();
879 void KAccessApp::noClicked() {
880 if (dialog != 0)
881 dialog->deleteLater();
882 dialog = 0;
883 requestedFeatures = features;
885 KConfigGroup config(KGlobal::config(), "Keyboard");
886 switch (showModeCombobox->currentIndex()) {
887 case 0:
888 config.writeEntry("Gestures", true);
889 config.writeEntry("GestureConfirmation", false);
890 break;
891 default:
892 config.writeEntry("Gestures", true);
893 config.writeEntry("GestureConfirmation", true);
894 break;
895 case 2:
896 requestedFeatures = 0;
897 config.writeEntry("Gestures", false);
898 config.writeEntry("GestureConfirmation", true);
900 config.sync();
902 if (features != requestedFeatures)
903 applyChanges();
904 readSettings();
907 void KAccessApp::dialogClosed() {
908 if (dialog != 0)
909 dialog->deleteLater();
910 dialog = 0;
912 requestedFeatures = features;
915 void KAccessApp::setXkbOpcode(int opcode) {
916 xkb_opcode = opcode;