2 * Copyright (C) 2007 Andriy Rysin (rysin@kde.org)
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include <QTextDocument> // for Qt::escape
27 #include <libxklavier/xklavier.h>
30 #include "kxkbconfig.h"
31 #include "xklavier_adaptor.h"
34 #define KDE_TRANSLATE 1
37 class XKlavierAdaptorPriv
{
39 QHash
<QString
, QString
> m_models
;
40 QHash
<QString
, QString
> m_layouts
;
41 QHash
<QString
, QList
<XkbVariant
>*> m_variants
;
42 QHash
<QString
, XkbOption
> m_options
;
43 QHash
<QString
, XkbOptionGroup
> m_optionGroups
;
45 static XklConfigRegistry
*config
;
48 XkbOptionGroup
* currGroup
;
52 XklConfigRegistry
*XKlavierAdaptorPriv::config
;
56 XKlavierAdaptor::XKlavierAdaptor(Display
* dpy
)
58 priv
= new XKlavierAdaptorPriv();
62 priv
->engine
= xkl_engine_get_instance(dpy
);
63 if (priv
->engine
== NULL
) {
64 kError() << "XKlavier engine cannot be initialized!" << endl
;
68 #if KDE_TRANSLATE == 1
69 // If we cannot get XKlavier's own i18n to work,
70 // we have to manually translate strings it reports using its catalog.
71 // This catalog has to be in the locale paths which KDE will search.
72 KGlobal::locale()->insertCatalog("xkeyboard-config");
73 // KDE's i18n expects messages to be well-formed XML,
74 // so use Qt::escape() to replace < with < etc.
75 // Actually we couldn't have done this just like that,
76 // as then an escaped message would not be found in the catalog;
77 // but, by a lucky fiat, xkeyboard-config too wants its messages as
78 // well-formed XML, so they come escaped exactly like this.
79 #define I18N_KDE(x) i18n(Qt::escape(QString::fromUtf8(x)).toUtf8())
83 QHash
<QString
, QString
> XKlavierAdaptor::getModels() { return priv
->m_models
; }
84 QHash
<QString
, QString
> XKlavierAdaptor::getLayouts() { return priv
->m_layouts
; }
85 QHash
<QString
, XkbOption
> XKlavierAdaptor::getOptions() { return priv
->m_options
; }
86 QHash
<QString
, XkbOptionGroup
> XKlavierAdaptor::getOptionGroups() { return priv
->m_optionGroups
; }
87 QHash
<QString
, QList
<XkbVariant
>*> XKlavierAdaptor::getVariants() { return priv
->m_variants
; }
91 static void processModel(XklConfigRegistry
*, const XklConfigItem
* configItem
, gpointer userData
)
93 QString model
= QString::fromUtf8(configItem
->name
);
94 #if KDE_TRANSLATE == 1
95 QString desc
= I18N_KDE(configItem
->description
);
97 QString desc
= QString::fromUtf8(configItem
->description
);
101 kDebug() << "model: " << model
<< " - " << desc
;
104 ((XKlavierAdaptorPriv
*)userData
)->m_models
.insert(model
, desc
);
108 static void processVariants(XklConfigRegistry
*, const XklConfigItem
* configItem
, gpointer userData
)
111 variant
.name
= QString::fromUtf8(configItem
->name
);
112 #if KDE_TRANSLATE == 1
113 variant
.description
= I18N_KDE(configItem
->description
);
115 variant
.description
= QString::fromUtf8(configItem
->description
);
117 QString layout
= ((XKlavierAdaptorPriv
*)userData
)->currLayout
;
120 kDebug() << "\tvariant: " << variant
.name
<< "-" << variant
.description
<< " (parent: " << layout
<< ")";
123 QList
<XkbVariant
>* vars
= ((XKlavierAdaptorPriv
*)userData
)->m_variants
[layout
];
124 vars
->append(variant
);
128 static void processLayout(XklConfigRegistry
*, const XklConfigItem
* configItem
, gpointer userData
)
130 QString layout
= QString::fromUtf8(configItem
->name
);
131 #if KDE_TRANSLATE == 1
132 QString desc
= I18N_KDE(configItem
->description
);
134 QString desc
= QString::fromUtf8(configItem
->description
);
138 kDebug() << "layout: " << layout
<< " - " << desc
;
140 ((XKlavierAdaptorPriv
*)userData
)->m_layouts
.insert(layout
, desc
);
141 ((XKlavierAdaptorPriv
*)userData
)->m_variants
.insert(layout
, new QList
<XkbVariant
>());
143 ((XKlavierAdaptorPriv
*)userData
)->currLayout
= layout
;
144 xkl_config_registry_foreach_layout_variant(XKlavierAdaptorPriv::config
,
145 configItem
->name
, processVariants
, userData
);
149 static void processOptions(XklConfigRegistry
*, const XklConfigItem
* configItem
, gpointer userData
)
153 option
.name
= QString::fromUtf8(configItem
->name
);
154 #if KDE_TRANSLATE == 1
155 option
.description
= I18N_KDE(configItem
->description
);
157 option
.description
= QString::fromUtf8(configItem
->description
);
159 option
.group
= ((XKlavierAdaptorPriv
*)userData
)->currGroup
;
162 kDebug() << "\toptions: " << option
.name
;
165 ((XKlavierAdaptorPriv
*)userData
)->m_options
.insert(option
.name
, option
);
169 static void processOptionGroup(XklConfigRegistry
*, const XklConfigItem
* configItem
, void *userData
)
171 XkbOptionGroup group
;
172 group
.name
= QString::fromUtf8(configItem
->name
);
173 #if KDE_TRANSLATE == 1
174 group
.description
= I18N_KDE(configItem
->description
);
176 group
.description
= QString::fromUtf8(configItem
->description
);
178 group
.exclusive
= ! GPOINTER_TO_INT (g_object_get_data (G_OBJECT (configItem
),
179 XCI_PROP_ALLOW_MULTIPLE_SELECTION
));
182 kDebug() << "group: " << group
.name
<< " - " << group
.description
;
185 ((XKlavierAdaptorPriv
*)userData
)->m_optionGroups
.insert(group
.name
, group
);
187 ((XKlavierAdaptorPriv
*)userData
)->currGroup
=
188 &((XKlavierAdaptorPriv
*)userData
)->m_optionGroups
[group
.name
];
189 xkl_config_registry_foreach_option(XKlavierAdaptorPriv::config
,
190 configItem
->name
, processOptions
, userData
);
194 static const int LOCALE_CATEGORY
= LC_ALL
;
196 void XKlavierAdaptor::loadXkbConfig(bool layoutsOnly
)
198 if( priv
->engine
== NULL
)
201 const char* currLocale
= setlocale(LOCALE_CATEGORY
, NULL
);
203 QString locale
= KGlobal::locale()->language();
204 if( locale
.indexOf('_') == -1 ) { // TODO: do we have to do this?
205 QString country
= KGlobal::locale()->country();
206 if( ! country
.isEmpty() ) {
208 locale
+= country
.toUpper();
211 // locale = "uk_UA"; // testing
214 kDebug() << "Setting LC_ALL for libxklavier: " << locale
;
216 const char* newLocale
= setlocale(LOCALE_CATEGORY
, locale
.toLatin1());
217 if( newLocale
== NULL
) {
218 kDebug() << "Setting locale " << locale
<< " failed - will use 'C' locale";
219 setlocale(LC_ALL
, "C");
222 kDebug() << "Xklavier initialized";
223 priv
->config
= xkl_config_registry_get_instance(priv
->engine
);
225 xkl_config_registry_load(priv
->config
);
227 void *userData
= priv
;
229 // xkl_config_registry_set_custom_charset(priv->config, "UTF-8");
231 xkl_config_registry_foreach_layout(priv
->config
, processLayout
, userData
);
233 if( ! layoutsOnly
) {
234 xkl_config_registry_foreach_model(priv
->config
, processModel
, userData
);
235 xkl_config_registry_foreach_option_group(priv
->config
, processOptionGroup
, userData
);
238 kDebug() << priv
->m_layouts
.count() << "total layouts" << priv
->m_models
.count() << "models";
240 setlocale(LOCALE_CATEGORY
, currLocale
);
242 g_object_unref(priv
->config
);
245 XKlavierAdaptor::~XKlavierAdaptor()
247 g_object_unref(priv
->engine
);
249 // kDebug() << "Finalizer";
253 XKlavierAdaptor::getGroupNames()
257 // kDebug() << "retrieving active layout from server...";
258 XklConfigRec configRec
;
259 xkl_config_rec_get_from_server(&configRec
, priv
->engine
);
261 for(int ii
=0; configRec
.layouts
[ii
] != NULL
&& ii
< GROUP_LIMIT
; ii
++) {
263 lu
.layout
= configRec
.layouts
[ii
];
264 lu
.variant
= configRec
.variants
[ii
];
265 xkbConfig
.layouts
<< lu
;
266 kDebug() << " layout nm:" << lu
.layout
<< "variant:" << lu
.variant
;
269 for(int ii
=0; configRec
.options
[ii
] != NULL
&& ii
< 15; ii
++) {
270 xkbConfig
.options
<< configRec
.options
[ii
];
271 kDebug() << " option:" << configRec
.options
[ii
];
274 // const char **gn = xkl_engine_get_groups_names(priv->engine);
275 // int gt = xkl_engine_get_num_groups(priv->engine);
277 // for (i = 0; i < gt; i++)
278 // kDebug() << "group:" << gn[i];
283 static XKlavierAdaptor
* instance
= NULL
;
286 XKlavierAdaptor::getInstance(Display
* dpy
)
288 if( instance
== NULL
) {
289 instance
= new XKlavierAdaptor(dpy
);
295 XKlavierAdaptor::startListening()
297 return xkl_engine_start_listen(priv
->engine
, XKLL_TRACK_KEYBOARD_STATE
);
301 XKlavierAdaptor::stopListening()
303 return xkl_engine_start_listen(priv
->engine
, XKLL_TRACK_KEYBOARD_STATE
);
307 XKlavierAdaptor::filterEvents(XEvent
* ev
)
309 return xkl_engine_filter_events(priv
->engine
, ev
);