not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kcontrol / kxkb / kxkbcore.cpp
blob895091fb815861ecc0573b3a35c256809fedd32b
1 /*
2 Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
3 Copyright (C) 2006, Andriy Rysin <rysin@kde.org>. Derived from an
4 original by Matthias H�zer-Klpfel released under the QPL.
5 This file is part of the KDE project
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
22 DESCRIPTION
24 KDE Keyboard Tool. Manages XKB keyboard mappings.
26 #include <QDesktopWidget>
27 #include <QProcess>
29 #include <kglobal.h>
30 #include <kglobalaccel.h>
31 #include <klocale.h>
32 #include <kwindowsystem.h>
33 #include <kstandarddirs.h>
34 #include <kaction.h>
35 #include <kdebug.h>
36 #include <kconfig.h>
37 #include <ktoolinvocation.h>
38 #include <kglobalsettings.h>
39 #include <kactioncollection.h>
40 #include <kapplication.h>
42 #include "x11helper.h"
43 #include "extension.h"
44 #include "rules.h"
45 #ifdef HAVE_XKLAVIER
46 #include "xklavier_adaptor.h"
47 #endif
48 #include "kxkbconfig.h"
49 #include "layoutmap.h"
50 #include "kxkbwidget.h"
52 #include "kxkbcore.h"
54 #include "kxkbcore.moc"
58 This is dummy class just to handle x11 events
60 class DummyWidget: public QWidget
62 KxkbCore* kxkb;
63 public:
64 DummyWidget(KxkbCore* kxkb_):
65 kxkb(kxkb_)
66 { }
67 protected:
68 bool x11Event( XEvent * e) { return kxkb->x11EventFilter(e); }
72 KxkbCore::KxkbCore(int mode):
73 m_mode(mode),
74 m_currentLayout(0),
75 m_eventsHandled(false),
76 m_error(false),
77 m_layoutOwnerMap(NULL),
78 m_rules(NULL),
79 m_kxkbWidget(NULL),
80 actionCollection(NULL),
81 m_dummyWidget(NULL)
83 m_status = 0;
85 m_extension = new XKBExtension();
86 if( !m_extension->init() ) {
87 kError() << "XKB initialization failed, exiting..." << endl;
88 m_status = -2;
89 return;
92 X11Helper::registerForNewDeviceEvent(QX11Info::display());
94 m_layoutOwnerMap = new LayoutMap(m_kxkbConfig);
97 KxkbCore::~KxkbCore()
99 cleanup();
100 delete actionCollection;
101 delete m_kxkbWidget;
102 delete m_rules;
103 delete m_extension;
104 delete m_layoutOwnerMap;
107 int KxkbCore::newInstance()
109 if( m_status == 0 && settingsRead() ) {
110 initReactions();
112 initSwitchingPolicy();
113 m_layoutOwnerMap->reset();
115 initTray();
116 layoutApply();
117 return 0;
120 return -1;
124 void KxkbCore::initReactions()
126 if( ! m_eventsHandled ) {
127 m_dummyWidget = new DummyWidget(this);
128 KApplication::kApplication()->installX11EventFilter(m_dummyWidget);
129 #ifdef HAVE_XKLAVIER
130 XKlavierAdaptor::getInstance(QX11Info::display())->startListening();
131 #endif
132 m_eventsHandled = true;
135 initKDEShortcut();
138 void KxkbCore::cleanup()
140 kDebug() << "cleaning up";
141 if( m_dummyWidget != NULL ) {
142 #ifdef HAVE_XKLAVIER
143 XKlavierAdaptor::getInstance(QX11Info::display())->stopListening();
144 #endif
145 KApplication::kApplication()->removeX11EventFilter(m_dummyWidget);
146 delete m_dummyWidget;
147 m_dummyWidget = NULL;
148 m_eventsHandled = false;
150 stopKDEShortcut();
153 void KxkbCore::initKDEShortcut()
155 if( m_mode == KXKB_MAIN && !m_kxkbConfig.m_indicatorOnly ) { // TODO: should component react to kde shortcut?
156 if( actionCollection == NULL ) {
157 actionCollection = new KActionCollection( this );
158 KAction* a = NULL;
159 #include "kxkbbindings.cpp"
160 connect(a, SIGNAL(triggered()), this, SLOT(toggled()));
161 connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(settingsChanged(int)));
163 KAction* kAction = static_cast<KAction*>(actionCollection->action(0));
164 kDebug() << "kde shortcut" << kAction->globalShortcut().toString();
166 else {
167 stopKDEShortcut();
171 const KShortcut* KxkbCore::getKDEShortcut() {
172 if( actionCollection == NULL )
173 return NULL;
174 KAction* kAction = static_cast<KAction*>(actionCollection->action(0));
175 if (kAction == NULL)
176 return NULL;
178 return &kAction->globalShortcut();
181 void KxkbCore::stopKDEShortcut()
183 if( actionCollection != NULL ) {
184 KAction* kAction = static_cast<KAction*>(actionCollection->action(0));
185 disconnect(kAction, SIGNAL(triggered()), this, SLOT(toggled()));
186 disconnect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(settingsChanged(int)));
187 actionCollection->clear();
188 delete actionCollection;
189 actionCollection = NULL;
193 void KxkbCore::settingsChanged(int category)
195 if ( category == KGlobalSettings::SETTINGS_SHORTCUTS) {
196 // TODO: can we do it more efficient or recreating action collection is the only way?
197 stopKDEShortcut();
198 initKDEShortcut();
202 bool KxkbCore::settingsRead()
204 m_kxkbConfig.load( KxkbConfig::LOAD_ACTIVE_OPTIONS );
206 if ( m_kxkbConfig.m_useKxkb == false ) {
207 kWarning() << "Kxkb is disabled, exiting...";
208 m_status = -1;
209 return false;
212 if( m_rules == NULL )
213 m_rules = new XkbRules(false);
215 if( m_mode == KXKB_MAIN && ! m_kxkbConfig.m_indicatorOnly ) {
216 m_currentLayout = m_kxkbConfig.getDefaultLayout();
217 initLayoutGroups();
219 else {
220 // for component or indicator we don't need owner map
221 m_kxkbConfig.m_switchingPolicy = SWITCH_POLICY_GLOBAL;
222 updateGroupsFromServer();
225 if( m_kxkbConfig.m_layouts.count() == 1 ) {
226 if( m_kxkbConfig.m_showSingle == false ) {
227 kWarning() << "Kxkb is hidden for single layout";
228 // m_status = -1;
229 // return false;
233 return true;
236 void KxkbCore::initSwitchingPolicy()
238 disconnect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(windowChanged(WId)));
239 disconnect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), this, SLOT(desktopChanged(int)));
241 if( m_kxkbConfig.m_switchingPolicy != SWITCH_POLICY_GLOBAL
242 && m_mode == KXKB_MAIN && !m_kxkbConfig.m_indicatorOnly ) {
243 QDesktopWidget desktopWidget;
244 if( desktopWidget.numScreens() > 1 && desktopWidget.isVirtualDesktop() == false ) {
245 kWarning() << "With non-virtual desktop only global switching policy supported on non-primary screens" ;
246 //TODO: find out how to handle that
249 if( m_kxkbConfig.m_switchingPolicy == SWITCH_POLICY_DESKTOP ) {
250 connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), this, SLOT(desktopChanged(int)));
252 else {
253 connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(windowChanged(WId)));
258 void KxkbCore::initLayoutGroups()
260 QStringList layouts;
261 QStringList variants;
262 for(int ii=0; ii<(int)m_kxkbConfig.m_layouts.count(); ii++) {
263 LayoutUnit& layoutUnit = m_kxkbConfig.m_layouts[ii];
264 layouts << layoutUnit.layout;
265 variants << layoutUnit.variant;
267 m_extension->setLayoutGroups(m_kxkbConfig.m_model, layouts, variants,
268 m_kxkbConfig.m_options, m_kxkbConfig.m_resetOldOptions);
271 void KxkbCore::setWidget(KxkbWidget* kxkbWidget)
273 if( m_status < 0 ) {
274 kError() << "kxkb did not initialize - ignoring set widget" << endl;
275 return;
278 if( m_kxkbWidget != NULL ) {
279 kDebug() << "destroying old kxkb widget";
280 disconnect(m_kxkbWidget, SIGNAL(menuTriggered(QAction*)), this, SLOT(iconMenuTriggered(QAction*)));
281 disconnect(m_kxkbWidget, SIGNAL(iconToggled()), this, SLOT(toggled()));
282 delete m_kxkbWidget;
285 m_kxkbWidget = kxkbWidget;
286 if( m_kxkbWidget != NULL ) {
287 connect(m_kxkbWidget, SIGNAL(menuTriggered(QAction*)), this, SLOT(iconMenuTriggered(QAction*)));
288 connect(m_kxkbWidget, SIGNAL(iconToggled()), this, SLOT(toggled()));
290 if( m_rules != NULL ) // settings already read
291 initTray();
295 void KxkbCore::initTray()
297 if( m_kxkbWidget != NULL ) {
298 bool visible = m_kxkbConfig.m_layouts.count() > 1 || m_kxkbConfig.m_showSingle;
299 kDebug() << "initing tray, visible:" << visible;
301 m_kxkbWidget->setShowFlag(m_kxkbConfig.m_showFlag);
302 m_kxkbWidget->initLayoutList(m_kxkbConfig.m_layouts, *m_rules);
303 m_kxkbWidget->setCurrentLayout(m_kxkbConfig.m_layouts[m_currentLayout]);
304 m_kxkbWidget->setVisible( visible );
308 // This function activates the keyboard layout specified by the
309 // configuration members (m_currentLayout)
310 void KxkbCore::layoutApply()
312 setLayout(m_currentLayout);
315 // DBUS
316 bool KxkbCore::setLayout(const QString& layoutPair)
318 const LayoutUnit layoutUnitKey(layoutPair);
319 if( m_kxkbConfig.m_layouts.contains(layoutUnitKey) ) {
320 int ind = m_kxkbConfig.m_layouts.indexOf(layoutUnitKey);
321 return setLayout( ind );
323 return false;
327 // Activates the keyboard layout specified by layout
328 bool KxkbCore::setLayout(int layout)
330 bool res = m_extension->setGroup(layout);
332 updateIndicator(layout, res);
334 return res;
337 void KxkbCore::updateIndicator(int layout, int res)
339 if( layout >= GROUP_LIMIT || layout >= m_kxkbConfig.m_layouts.count() ) {
340 m_error = true;
342 if( m_kxkbWidget ) {
343 LayoutUnit lu( i18n("Group %1", layout+1), "" );
344 lu.setDisplayName( QString("%1").arg(layout+1) );
345 m_kxkbWidget->setCurrentLayout(lu);
347 kWarning() << "group is out of my range, seems like old style groups are used";
348 return;
351 m_error = ( res > 0 );
353 if( res ) {
354 m_currentLayout = layout;
355 m_layoutOwnerMap->ownerChanged();
356 m_layoutOwnerMap->setCurrentLayout(layout);
359 if( m_kxkbWidget ) {
360 const LayoutUnit& lu = m_kxkbConfig.m_layouts[layout];
362 if( res )
363 m_kxkbWidget->setCurrentLayout(lu);
364 else
365 m_kxkbWidget->setError(lu.toPair());
369 void KxkbCore::toggled()
371 if( m_kxkbConfig.m_layouts.count() <= 1 )
372 return;
374 int layout = m_layoutOwnerMap->getNextLayout();
375 setLayout(layout);
378 void KxkbCore::iconMenuTriggered(QAction* action)
380 int id = action->data().toInt();
382 if( KxkbWidget::START_MENU_ID <= id
383 && id < KxkbWidget::START_MENU_ID + (int)m_kxkbConfig.m_layouts.count() )
385 if( m_kxkbConfig.m_layouts.count() <= 1 )
386 return;
388 int layout = id - KxkbWidget::START_MENU_ID;
389 m_layoutOwnerMap->setCurrentLayout( layout );
390 setLayout( layout );
392 else if (id == KxkbWidget::CONFIG_MENU_ID)
394 QStringList lst;
395 lst << "keyboard_layout";
396 QProcess::startDetached("kcmshell4", lst);
400 void KxkbCore::desktopChanged(int desktop)
402 kDebug() << "desktop changed" << desktop;
403 windowChanged(-1);
406 // TODO: we also have to handle deleted windows
407 void KxkbCore::windowChanged(WId winId)
409 if( m_kxkbConfig.m_switchingPolicy == SWITCH_POLICY_GLOBAL ) { // should not happen actually
410 kDebug() << "windowChanged() signal in GLOBAL switching policy";
411 return;
414 kDebug() << "active window changed new WinId: " << winId;
416 if( m_kxkbConfig.m_switchingPolicy == SWITCH_POLICY_GLOBAL
417 || winId != X11Helper::UNKNOWN_WINDOW_ID ) {
419 m_layoutOwnerMap->ownerChanged();
420 int layoutState = m_layoutOwnerMap->getCurrentLayout();
422 if( layoutState != m_currentLayout ) {
423 setLayout(layoutState);
430 bool KxkbCore::x11EventFilter ( XEvent * event )
432 #ifdef HAVE_XKLAVIER
433 XKlavierAdaptor::getInstance(QX11Info::display())->filterEvents(event);
434 #endif
436 if( m_extension->isXkbEvent(event) ) {
437 // qApp->x11ProcessEvent ( event );
439 if( XKBExtension::isGroupSwitchEvent(event) ) {
440 // group changed
441 int group = m_extension->getGroup();
442 if( group != m_currentLayout || m_error ) {
443 kDebug() << "got event: group changed to " << group;
444 updateIndicator(group, 1);
447 else if( XKBExtension::isLayoutSwitchEvent(event) ) {
448 kDebug() << "got event: layouts changed";
449 updateGroupsFromServer();
451 else {
452 // kDebug() << "other xkb event: ";// + ((XkbEvent*)event)->any.xkb_type;
455 else {
456 if( X11Helper::isNewDeviceEvent(event) ) {
457 initLayoutGroups();
459 // else kDebug() << "other x11 event, type" << event->type;
461 return false;
465 KxkbCore::updateGroupsFromServer()
467 kDebug() << "updating groups from server";
469 #ifdef HAVE_XKLAVIER
470 XkbConfig xkbConfig = XKlavierAdaptor::getInstance(QX11Info::display())->getGroupNames();
471 #else
472 XkbConfig xkbConfig = X11Helper::getGroupNames(QX11Info::display());
473 #endif
475 int group = m_extension->getGroup();
476 kDebug() << " active group" << group;
478 const QList<LayoutUnit>& lus = xkbConfig.layouts;
479 if( lus.count() > 0 ) {
480 if( lus != m_kxkbConfig.m_layouts ) {
481 if( group >= m_kxkbConfig.m_layouts.count() )
482 group = 0;
483 m_currentLayout = group;
484 m_kxkbConfig.setConfiguredLayouts(xkbConfig);
485 m_layoutOwnerMap->reset();
486 initTray();
488 else {
489 kDebug() << " no change in layouts";
491 updateIndicator(group, 1);
493 else {
494 kWarning() << " failed to get layouts from server";
495 if( m_currentLayout != group && group < m_kxkbConfig.m_layouts.count() ) {
496 kDebug() << " ...tryin to set at least group" << group;
497 updateIndicator(group, 1);
500 // kDebug() << "updating layouts from server is not implemented w/out libxklavier";
502 return 0;