From 3262b43c6c865ff2fe9214fa10181d34e9b0908f Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Mon, 9 May 2011 17:15:42 +0900 Subject: [PATCH] Add XKB layouts --- Makefile.am | 7 + configure.ac | 53 ++++ data/ibus.schemas.in | 36 +++ ibus/Makefile.am | 26 ++ ibus/__init__.py | 2 + ibus/bus.py | 3 + ibus/interface/iibus.py | 3 + ibus/xkblayout.py.in | 225 ++++++++++++++ ibus/xkbxml.py.in | 413 ++++++++++++++++++++++++++ setup/Makefile.am | 1 + setup/enginecombobox.py | 7 +- setup/main.py | 3 + setup/setup.ui | 609 ++++++++++++++++++++++++++++++++++++++- setup/xkbsetup.py | 454 +++++++++++++++++++++++++++++ src/ibusfactory.c | 21 ++- src/ibusfactory.h | 5 +- ui/gtk/panel.py | 39 +++ xkb/Makefile.am | 104 +++++++ xkb/ibus-engine-xkb-main.c | 397 +++++++++++++++++++++++++ xkb/ibus-engine-xkb-main.h | 46 +++ xkb/ibus-xkb-main.c | 105 +++++++ xkb/xkblayout.xml.in | 16 + xkb/xkblayoutconfig.xml.in | 6 + xkb/xkblib.c | 303 +++++++++++++++++++ xkb/xkblib.h | 40 +++ xkb/xkbxml.c | 696 ++++++++++++++++++++++++++++++++++++++++++++ xkb/xkbxml.h | 189 ++++++++++++ 27 files changed, 3803 insertions(+), 6 deletions(-) create mode 100644 ibus/xkblayout.py.in create mode 100644 ibus/xkbxml.py.in create mode 100644 setup/xkbsetup.py create mode 100644 xkb/Makefile.am create mode 100644 xkb/ibus-engine-xkb-main.c create mode 100644 xkb/ibus-engine-xkb-main.h create mode 100644 xkb/ibus-xkb-main.c create mode 100644 xkb/xkblayout.xml.in create mode 100644 xkb/xkblayoutconfig.xml.in create mode 100644 xkb/xkblib.c create mode 100644 xkb/xkblib.h create mode 100644 xkb/xkbxml.c create mode 100644 xkb/xkbxml.h diff --git a/Makefile.am b/Makefile.am index 29c57e1..dbe0b6b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -42,6 +42,12 @@ DAEMON_DIRS = \ $(NULL) endif +if ENABLE_XKB +XKB_DIRS = \ + xkb \ + $(NULL) +endif + if ENABLE_MEMCONF MEMCONF_DIRS = \ memconf \ @@ -60,6 +66,7 @@ SUBDIRS = \ $(DAEMON_DIRS) \ $(PYTHON_DIRS) \ $(GCONF_DIRS) \ + $(XKB_DIRS) \ $(MEMCONF_DIRS) \ $(NULL) diff --git a/configure.ac b/configure.ac index 8da8c6e..b3cdb0b 100644 --- a/configure.ac +++ b/configure.ac @@ -185,6 +185,57 @@ else enable_xim="no (disabled, use --enable-xim to enable)" fi +AC_ARG_ENABLE(xkb, + AS_HELP_STRING([--disable-xkb], + [Do not build xkb]), + [enable_xkb=$enableval], + [enable_xkb=yes] +) + +AM_CONDITIONAL([ENABLE_XKB], [test x"$enable_xkb" = x"yes"]) +if test x"$enable_xkb" = x"yes"; then + PKG_CHECK_MODULES(X11, [ + x11 + ]) + PKG_CHECK_MODULES(XKB, + [xkbfile],, + [XKB_LIBS="-lxkbfile"] + ) + AC_DEFINE(HAVE_XKB, 1, [define to 1 if you have xkbfile]) +else + enable_xkb="no (disabled, use --enable-xkb to enable)" +fi + +# define XKB rules file +AC_ARG_WITH(xkb-rules-xml, + AS_HELP_STRING([--with-xkb-rules-xml[=$DIR/evdev.xml]], + [Set evdev.xml file path (default: /usr/share/X11/xkb/rules/evdev.xml)]), + XKB_RULES_XML_FILE=$with_xkb_rules_xml, + XKB_RULES_XML_FILE="/usr/share/X11/xkb/rules/evdev.xml" +) +AC_DEFINE_UNQUOTED(XKB_RULES_XML_FILE, "$XKB_RULES_XML_FILE", + [Define file path of evdev.xml]) +AC_SUBST(XKB_RULES_XML_FILE) + +# define XKB preload layouts +AC_ARG_WITH(xkb-preload-layouts, + AS_HELP_STRING([--with-xkb-preload-layouts[=layout,...]], + [Set preload xkb layouts (default: us,fr,de,...)]), + XKB_PRELOAD_LAYOUTS=$with_xkb_preload_layouts, + [XKB_PRELOAD_LAYOUTS=""\ +"us,us(chr),us(dvorak),ad,al,am,ara,az,ba,bd,be,bg,br,bt,by,"\ +"de,dk,ca,ch,cn(tib),cz,ee,epo,es,et,fi,fo,fr,"\ +"gb,ge,ge(dsb),ge(ru),ge(os),gh,gh(akan),gh(ewe),gh(fula),gh(ga),gh(hausa),"\ +"gn,gr,hu,hr,ie,ie(CloGaelach),il,"\ +"in,in(ben),in(guj),in(guru),in(jhelum),in(kan),in(mal),in(ori),in(tam),"\ +"in(tel),in(urd-phonetic),in(bolnagri),iq,iq(ku),ir,ir(ku),is,it,"\ +"kg,kh,kz,la,latam,lk,lk(tam_unicode),lt,lv,ma,ma(tifinagh),mal,mao,"\ +"me,mk,mm,mt,mv,ng,ng(hausa),ng,ng(igbo),ng(yoruba),nl,no,no(smi),np,"\ +"pk,pl,pl(csb),pt,ro,rs,ru,ru(cv),ru(kom),ru(sah),ru(tt),ru(xal),"\ +"se,si,sk,sy,sy(ku),th,tj,tr,ua,uz,vn"] +) +AC_SUBST(XKB_PRELOAD_LAYOUTS) + # GObject introspection GOBJECT_INTROSPECTION_CHECK([0.6.8]) @@ -430,6 +481,7 @@ gconf/Makefile gconf/gconf.xml.in bindings/Makefile bindings/vala/Makefile +xkb/Makefile ]) AC_OUTPUT @@ -445,6 +497,7 @@ Build options: Build gtk2 immodule $enable_gtk2 Build gtk3 immodule $enable_gtk3 Build XIM agent server $enable_xim + Build XKB $enable_xkb Build python modules $enable_python Build gconf modules $enable_gconf Build memconf modules $enable_memconf diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in index b75295e..7ca4899 100644 --- a/data/ibus.schemas.in +++ b/data/ibus.schemas.in @@ -190,6 +190,42 @@ + /schemas/desktop/ibus/general/system_keyboard_layout + /desktop/ibus/general/system_keyboard_layout + ibus + string + default + ibus + + Set system keyboard layout + Override default system keyboard layout. default is 'default' + + + + /schemas/desktop/ibus/general/system_keyboard_option + /desktop/ibus/general/system_keyboard_option + ibus + string + default + ibus + + Set system keyboard option + Override default system keyboard option. default is 'default' + + + + /schemas/desktop/ibus/general/xkb_latin_layouts + /desktop/ibus/general/xkb_latin_layouts + ibus + list + string + [ara,bg,cz,dev,gr,gur,in,jp(kana),mal,mkd,ru,ua] + + Latin layout which have no ASCII + us layout is appended to the latin layouts. variant is not needed. + + + /schemas/desktop/ibus/panel/use_custom_font /desktop/ibus/panel/use_custom_font ibus diff --git a/ibus/Makefile.am b/ibus/Makefile.am index c71df1b..508c98f 100644 --- a/ibus/Makefile.am +++ b/ibus/Makefile.am @@ -58,12 +58,38 @@ nodist_ibus_PYTHON = \ ibusdir = @pkgpythondir@ +xkblayout_py_in_files = \ + xkblayout.py.in \ + xkbxml.py.in \ + $(NULL) +xkblayout_py_DATA = $(xkblayout_py_in_files:.py.in=.py) +xkblayout_pydir = @pkgpythondir@ + +ibus_PYTHON += $(xkblayout_py_DATA) + +if ENABLE_XKB +XKB_COMMAND=\\\"$(libexecdir)/ibus-xkb\\\" +HAVE_XKB=True +else +XKB_COMMAND="None" +HAVE_XKB=False +endif + +%.py : %.py.in + @sed -e "s|\@XKB_COMMAND\@|$(XKB_COMMAND)|g" \ + -e "s|\@XKB_RULES_XML_FILE\@|$(XKB_RULES_XML_FILE)|g" \ + -e "s|\@HAVE_XKB\@|$(HAVE_XKB)|g" \ + -e "s|\@datadir\@|$(datadir)|g" \ + $< > $@ + EXTRA_DIST = \ _config.py.in \ + $(xkblayout_py_in_files) \ $(NULL) CLEANFILES = \ *.pyc \ + $(xkblayout_py_DATA) \ $(NULL) DISTCLEANFILES = \ diff --git a/ibus/__init__.py b/ibus/__init__.py index 7c8f8be..3c25605 100644 --- a/ibus/__init__.py +++ b/ibus/__init__.py @@ -41,4 +41,6 @@ from text import * from observedpath import * from enginedesc import * from component import * +from xkblayout import * +from xkbxml import * from _config import * diff --git a/ibus/bus.py b/ibus/bus.py index 5738fad..05ec49e 100644 --- a/ibus/bus.py +++ b/ibus/bus.py @@ -160,6 +160,9 @@ class Bus(object.Object): data = serializable.deserialize_object(data) return data + def get_use_sys_layout(self): + return self.__ibus.GetUseSysLayout(); + def introspect_ibus(self): return self.__ibus.Introspect() diff --git a/ibus/interface/iibus.py b/ibus/interface/iibus.py index 678d517..7de56fc 100644 --- a/ibus/interface/iibus.py +++ b/ibus/interface/iibus.py @@ -75,6 +75,9 @@ class IIBus(dbus.service.Object): @method(in_signature="v", out_signature="v") def Ping(self, data, dbusconn): pass + @method(out_signature="b") + def GetUseSysLayout(self, dbusconn): pass + @signal(signature="") def RegistryChanged(self): pass diff --git a/ibus/xkblayout.py.in b/ibus/xkblayout.py.in new file mode 100644 index 0000000..7685776 --- /dev/null +++ b/ibus/xkblayout.py.in @@ -0,0 +1,225 @@ +# vim:set et sts=4 sw=4: +# +# ibus - The Input Bus +# +# Copyright (c) 2010 Takao Fujiwara +# Copyright (c) 2007-2010 Peng Huang +# Copyright (c) 2007-2010 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA + +__all__ = ( + "XKBLayout", + ) + +import os, sys, time + +XKB_COMMAND = @XKB_COMMAND@ +XKB_SESSION_TIME_OUT = 30.0 + +class XKBLayout(): + def __init__(self, config = None, command=XKB_COMMAND): + self.__config = config + self.__command = command + self.__use_xkb = True + if self.__command == None: + self.__use_xkb = False + self.__default_layout = self.get_layout() + self.__default_model = self.get_model() + self.__default_option = self.get_option() + self.__time_lag_session_xkb_layout = True + self.__time_lag_session_xkb_option = True + self.__time_lag_session_xkb_timer = time.time() + self.__xkb_latin_layouts = [] + if config != None: + self.__xkb_latin_layouts = list(self.__config.get_value("general", + "xkb_latin_layouts", + [])) + + + def __get_model_from_layout(self, layout): + layout_array = layout.split(',') + option_array = [] + is_bracket = False + for i, l in enumerate(layout_array): + option_array.append("") + left_bracket = l.find('(') + right_bracket = l.find(')') + if left_bracket >= 0 and right_bracket > left_bracket: + is_bracket = True + layout_array[i] = l[:left_bracket] + option_array[i] = l[left_bracket + 1:right_bracket] + if is_bracket == False: + return (layout, "default") + layout = ','.join(layout_array) + option = ','.join(option_array) + return (layout, option) + + def use_xkb(self, enable): + if self.__command == None: + return + self.__use_xkb = enable + + def get_layout(self): + if not self.__use_xkb: + return None + exec_command = "%s %s" % (self.__command, "--get") + retval = None + for line in os.popen(exec_command).readlines(): + line = line.strip() + if line.startswith("layout: "): + retval = line[len("layout: "):] + break + return retval + + def get_model(self): + if not self.__use_xkb: + return None + exec_command = "%s %s" % (self.__command, "--get") + retval = None + for line in os.popen(exec_command).readlines(): + line = line.strip() + if line.startswith("model: "): + retval = line[len("model: "):] + break + return retval + + def get_option(self): + if not self.__use_xkb: + return None + exec_command = "%s %s" % (self.__command, "--get") + retval = None + for line in os.popen(exec_command).readlines(): + line = line.strip() + if line.startswith("option: "): + retval = line[len("option: "):] + break + return retval + + def set_layout(self, layout="default", model="default", option="default"): + if not self.__use_xkb: + return + if layout == None: + return + if self.__default_layout == None: + # Maybe opening display was failed in constructor. + self.reload_default_layout() + if self.__default_layout == None: + return + layout = str(layout) + # if set_default_layout() is not default, the default layout is + # pulled from the current XKB. But it's possible gnome-settings-daemon + # does not run yet. I added XKB_SESSION_TIME_OUT for the timer. + if self.__time_lag_session_xkb_layout == True: + self.__default_layout = self.get_layout() + self.__default_model = self.get_model() + if self.__time_lag_session_xkb_option == True: + self.__default_option = self.get_option() + if (self.__time_lag_session_xkb_layout == True or \ + self.__time_lag_session_xkb_option == True ) and \ + (self.__time_lag_session_xkb_timer - time.time() \ + > XKB_SESSION_TIME_OUT): + self.__time_lag_session_xkb_layout = False + self.__time_lag_session_xkb_option = False + if layout == "default": + layout = self.__default_layout + else: + self.__time_lag_session_xkb_layout = False + if model != None: + model = str(model) + if model == "default": + (layout, model) = self.__get_model_from_layout(layout) + if model == "default": + model = self.__default_model + else: + self.__time_lag_session_xkb_layout = False + if option != None: + option = str(option) + if option == "default": + option = self.__default_option + else: + self.__time_lag_session_xkb_option = False + need_us_layout = False + for latin_layout in self.__xkb_latin_layouts: + latin_layout = str(latin_layout) + if layout == latin_layout: + need_us_layout = True + break + if model != None and layout + '(' + model + ')' == latin_layout: + need_us_layout = True + break + if need_us_layout: + layout = layout + ",us" + if model != None: + model = model + "," + if layout == self.get_layout() and \ + model == self.get_model() and \ + option == self.get_option(): + return + args = [] + args.append(self.__command) + args.append(os.path.basename(self.__command)) + args.append("--layout") + args.append(layout) + if model != None: + args.append("--model") + args.append(model) + if option != None: + args.append("--option") + args.append(option) + pid = os.spawnl(os.P_NOWAIT, *args) + os.waitpid(pid, 0) + + def set_default_layout(self, layout="default", model="default"): + if not self.__use_xkb: + return + if layout == None: + print >> sys.stderr, "ibus.xkblayout: None layout" + return + if model == None: + print >> sys.stderr, "ibus.xkblayout: None model" + return + if layout == 'default': + self.__default_layout = self.get_layout() + self.__default_model = self.get_model() + else: + if model == 'default': + (layout, model) = self.__get_model_from_layout(layout) + self.__default_layout = layout + self.__time_lag_session_xkb_layout = False + if model == 'default': + self.__default_model = None + else: + self.__default_model = model + + def set_default_option(self, option="default"): + if not self.__use_xkb: + return + if option == None: + print >> sys.stderr, "ibus.xkblayout: None option" + return + if option == 'default': + self.__default_option = self.get_option() + else: + self.__default_option = option + self.__time_lag_session_xkb_option = False + + def reload_default_layout(self): + if not self.__use_xkb: + return + self.__default_layout = self.get_layout() + self.__default_model = self.get_model() + self.__default_option = self.get_option() diff --git a/ibus/xkbxml.py.in b/ibus/xkbxml.py.in new file mode 100644 index 0000000..d3d0ba2 --- /dev/null +++ b/ibus/xkbxml.py.in @@ -0,0 +1,413 @@ +# vim:set et sts=4 sw=4: +# +# ibus - The Input Bus +# +# Copyright (c) 2010 Takao Fujiwara +# Copyright (c) 2007-2010 Peng Huang +# Copyright (c) 2007-2010 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA + +__all__ = ( + "XKBConfigRegistry", + "XKBLayoutConfig", + ) + +import os +import string +import xml.sax as sax +import enginedesc +from xml.sax.saxutils import XMLFilterBase, XMLGenerator +from xml.sax._exceptions import SAXParseException +from cStringIO import StringIO + +try: + from glib import get_user_config_dir +except ImportError: + get_user_config_dir = lambda : None + +XKB_RULES_XML_FILE = "@XKB_RULES_XML_FILE@" + +class XKBConfigRegistryHandler(XMLFilterBase): + def __init__(self, parser=None, root='root'): + XMLFilterBase.__init__(self, parser) + self.__root = root + self.__current_node = root + self.__layoutlist_array = {} + self.__layoutlist = False + self.__layout = False + self.__layout_label = None + self.__layout_desc = {} + self.__layout_lang = {} + self.__variantlist = False + self.__variant = False + self.__variant_label = None + self.__variant_desc = {} + self.__optionlist_array = {} + self.__optionlist = False + self.__option_group_desc = {} + self.__option_desc = {} + self.__option = False + self.__option_label = None + self.__group = False + self.__group_label = None + + def __characters_layoutlist(self, text): + if not self.__layout: + return + if self.__variant: + if self.__current_node == "name": + self.__variant_label = text + if self.__layout_label != None and \ + self.__layout_label in self.__layoutlist_array: + self.__layoutlist_array[self.__layout_label].append(text) + elif self.__current_node == "description": + label = "%s(%s)" % (self.__layout_label, self.__variant_label) + self.__variant_desc[label] = text + elif self.__current_node == "iso639Id": + label = self.__layout_label + if label != None: + label = "%s(%s)" % (label, self.__variant_label) + else: + label = self.__variant_label + if label not in self.__layout_lang: + self.__layout_lang[label] = [] + self.__layout_lang[label].append(text) + else: + pass + else: + if self.__current_node == "name": + self.__layout_label = text + self.__layoutlist_array[self.__layout_label] = [] + elif self.__current_node == "description": + self.__layout_desc[self.__layout_label] = text + elif self.__current_node == "iso639Id": + if self.__layout_label not in self.__layout_lang: + self.__layout_lang[self.__layout_label] = [] + self.__layout_lang[self.__layout_label].append(text) + else: + pass + + def __characters_optionlist(self, text): + if not self.__group: + return + if self.__option: + if self.__current_node == "name": + self.__option_label = text + if self.__group_label != None and \ + self.__group_label in self.__optionlist_array: + self.__optionlist_array[self.__group_label].append(text) + elif self.__current_node == "description": + self.__option_desc[self.__option_label] = text + else: + pass + else: + if self.__current_node == "name": + self.__group_label = text + self.__optionlist_array[self.__group_label] = [] + elif self.__current_node == "description": + self.__option_group_desc[self.__group_label] = text + else: + pass + + def startElement(self, name, attrs): + self.__current_node = name + if name == "layoutList": + self.__layoutlist = True + elif name == "layout": + self.__layout = True + self.__layout_label = None + elif name == "variantList": + self.__variantlist = True + elif name == "variant": + self.__variant = True + self.__variant_label = None + elif name == "optionList": + self.__optionlist = True + elif name == "option": + self.__option = True + self.__option_label = None + elif name == "group": + self.__group = True + self.__group_label = None + + def endElement(self, name): + self.__current_node = self.__root + if name == "layoutList": + self.__layoutlist = False + elif name == "layout": + self.__layout = False + elif name == "variantList": + self.__variantlist = False + elif name == "variant": + self.__variant = False + elif name == "optionList": + self.__optionlist = False + elif name == "option": + self.__option = False + elif name == "group": + self.__group = False + + def characters(self, text): + if self.__current_node == self.__root: + return + if self.__layoutlist: + self.__characters_layoutlist(text) + elif self.__optionlist: + self.__characters_optionlist(text) + + def getLayoutList(self): + return self.__layoutlist_array + + def getLayoutDesc(self): + return self.__layout_desc + + def getLayoutLang(self): + return self.__layout_lang + + def getVariantDesc(self): + return self.__variant_desc + + def getOptionList(self): + return self.__optionlist_array + + def getOptionGroupDesc(self): + return self.__option_group_desc + + def getOptionDesc(self): + return self.__option_desc + +class XKBLayoutConfigHandler(XMLFilterBase): + def __init__(self, + parser=None, + downstream=None, + preload_layouts=None, + root='root'): + XMLFilterBase.__init__(self, parser) + self.__downstream = downstream + self.__preload_layouts = preload_layouts + self.__root = root + self.__current_node = root + self.__xkblayout = False + self.__config = False + + def startDocument(self): + if self.__downstream != None: + self.__downstream.startDocument() + + def endDocument(self): + if self.__downstream != None: + self.__downstream.endDocument() + + def startElement(self, name, attrs): + self.__current_node = name + if name == "xkblayout": + self.__xkblayout = True + if name == "config": + self.__config = True + if self.__downstream != None: + self.__downstream.startElement(name, {}) + + def endElement(self, name): + self.__current_node = self.__root + if name == "xkblayout": + self.__xkblayout = False + if name == "config": + self.__config = False + if self.__downstream != None: + self.__downstream.endElement(name) + + def characters(self, text): + if self.__current_node == self.__root: + return + if not self.__xkblayout or not self.__config: + return + if self.__current_node == "preload_layouts": + if self.__preload_layouts == None: + self.__preload_layouts = text.split(',') + self.__preload_layouts.sort() + if self.__downstream != None: + self.__downstream.characters(string.join(self.__preload_layouts, + ',')) + + def getPreloadLayouts(self): + return self.__preload_layouts + +class XKBConfigRegistry(): + def __init__(self, file_path=XKB_RULES_XML_FILE): + self.__handler = None + parser = sax.make_parser() + parser.setFeature(sax.handler.feature_namespaces, 0) + self.__handler = XKBConfigRegistryHandler(parser) + parser.setContentHandler(self.__handler) + f = file(file_path, 'r') + try: + parser.parse(f) + except SAXParseException: + print "ERROR: invalid file format", file_path + finally: + f.close() + + def get_layout_list(self): + return self.__handler.getLayoutList() + + def get_layout_desc(self): + return self.__handler.getLayoutDesc() + + def get_layout_lang(self): + return self.__handler.getLayoutLang() + + def get_variant_desc(self): + return self.__handler.getVariantDesc() + + def get_option_list(self): + return self.__handler.getOptionList() + + def get_option_group_desc(self): + return self.__handler.getOptionGroupDesc() + + def get_option_desc(self): + return self.__handler.getOptionDesc() + + @classmethod + def have_xkb(self): + return @HAVE_XKB@ + + @classmethod + def engine_desc_new(self, + lang, + layout, + layout_desc=None, + variant=None, + variant_desc=None): + if layout_desc != None and variant_desc != None: + longname = layout_desc + " - " + variant_desc + elif layout != None and variant != None: + longname = layout + " - " + variant + elif layout_desc != None: + longname = layout_desc + else: + longname = layout + if variant != None: + name = "xkb:layout:" + layout + ":" + variant + desc = "XKB " + layout + "(" + variant + ") keyboard layout" + engine_layout = layout + "(" + variant + ")" + else: + name = "xkb:layout:" + layout + desc = "XKB " + layout + " keyboard layout" + engine_layout = layout + + engine = enginedesc.EngineDesc(name, longname, desc, lang, + "LGPL2.1", + "Takao Fujiwara ", + "ibus-engine", + engine_layout) + return engine + +class XKBLayoutConfig(): + def __init__(self, + system_config="@datadir@/ibus/xkb/xkblayoutconfig.xml"): + self.__user_config = get_user_config_dir() + if self.__user_config == None: + self.__user_config = os.environ['HOME'] + "/.config" + self.__user_config = self.__user_config + \ + "/ibus/xkb/xkblayoutconfig.xml" + self.__system_config = system_config + self.__filter_handler = None + self.__load() + + def __load(self, downstream=None, preload_layouts=None): + parser = sax.make_parser() + parser.setFeature(sax.handler.feature_namespaces, 0) + self.__filter_handler = XKBLayoutConfigHandler(parser, + downstream, + preload_layouts) + parser.setContentHandler(self.__filter_handler) + f = None + if os.path.exists(self.__user_config): + f = file(self.__user_config) + elif os.path.exists(self.__system_config): + f = file(self.__system_config) + if f == None: + return + try: + parser.parse(f) + except SAXParseException: + print "ERROR: invalid file format", self.__user_config + finally: + f.close() + + def get_preload_layouts(self): + return self.__filter_handler.getPreloadLayouts() + + def save_preload_layouts(self, layouts): + if layouts == None: + if os.path.exists(self.__user_config): + os.unlink(self.__user_config) + return + parser = sax.make_parser() + parser.setFeature(sax.handler.feature_namespaces, 0) + result = StringIO() + downstream_handler = XMLGenerator(result, 'utf-8') + self.__load(downstream_handler, layouts) + contents = result.getvalue() + dir = os.path.dirname(self.__user_config) + if not os.path.exists(dir): + os.makedirs(dir, 0700) + f = open(self.__user_config, 'w') + f.write(contents) + f.close() + os.chmod(self.__user_config, 0600) + +def test(): + xkbconfig = XKBConfigRegistry() + layout_list = xkbconfig.get_layout_list() + layout_desc = xkbconfig.get_layout_desc() + layout_lang = xkbconfig.get_layout_lang() + variant_desc = xkbconfig.get_variant_desc() + for layout in layout_list.keys(): + if layout not in layout_lang: + print "layout name:", layout, "NO-LANG description:", layout_desc[layout] + continue + lang = layout_lang[layout] + print "layout name:", layout, "lang:", lang, "description:", layout_desc[layout] + for variant in layout_list[layout]: + label = "%s(%s)" % (layout, variant) + if label in layout_lang: + lang = layout_lang[label] + print " variant name:", variant, "lang:", lang, "description:", variant_desc[variant] + + option_list = xkbconfig.get_option_list() + option_group_desc = xkbconfig.get_option_group_desc() + option_desc = xkbconfig.get_option_desc() + for option_group in option_list.keys(): + print "option group name:", option_group, "description:", option_group_desc[option_group] + for option in option_list[option_group]: + print " option name:", option, "description:", option_desc[option] + +def test2(): + xkblayoutconfig = XKBLayoutConfig("../xkb/xkblayoutconfig.xml") + list = xkblayoutconfig.get_preload_layouts() + print list + if list == None: + list = [] + list.append("gb(test)") + list.sort() + #xkblayoutconfig.save_preload_layouts(list) + +if __name__ == "__main__": + test() + test2() diff --git a/setup/Makefile.am b/setup/Makefile.am index 9618d7f..48b1fed 100644 --- a/setup/Makefile.am +++ b/setup/Makefile.am @@ -28,6 +28,7 @@ ibussetup_PYTHON = \ enginetreeview.py \ engineabout.py \ keyboardshortcut.py \ + xkbsetup.py \ $(NULL) ibussetup_DATA = \ diff --git a/setup/enginecombobox.py b/setup/enginecombobox.py index 2fd8876..7383177 100644 --- a/setup/enginecombobox.py +++ b/setup/enginecombobox.py @@ -43,6 +43,7 @@ class EngineComboBox(gtk.ComboBox): self.connect("notify::active", self.__notify_active_cb) self.__model = None + self.__title = _("Select an input method") renderer = gtk.CellRendererPixbuf() renderer.set_property("xalign", 0) @@ -117,7 +118,7 @@ class EngineComboBox(gtk.ComboBox): renderer.set_property("weight", pango.WEIGHT_NORMAL) elif isinstance(engine, int): renderer.set_property("sensitive", True) - renderer.set_property("text", _("Select an input method")) + renderer.set_property("text", self.__title) renderer.set_property("weight", pango.WEIGHT_NORMAL) else: renderer.set_property("sensitive", True) @@ -140,5 +141,9 @@ class EngineComboBox(gtk.ComboBox): def get_active_engine(self): return self.get_property("active-engine") + def get_title(self): + return self.__title + def set_title(self, title): + self.__title = title diff --git a/setup/main.py b/setup/main.py index a22bb0c..7f4a040 100644 --- a/setup/main.py +++ b/setup/main.py @@ -37,6 +37,7 @@ from gtk import gdk from enginecombobox import EngineComboBox from enginetreeview import EngineTreeView from engineabout import EngineAbout +from xkbsetup import XKBSetup from i18n import DOMAINNAME, _, N_, init as i18n_init ( @@ -241,6 +242,8 @@ class Setup(object): self.__combobox.connect("notify::active-engine", self.__combobox_notify_active_engine_cb) self.__treeview.connect("notify", self.__treeview_notify_cb) + XKBSetup(self.__config, self.__builder) + def __combobox_notify_active_engine_cb(self, combobox, property): engine = self.__combobox.get_active_engine() button = self.__builder.get_object("button_engine_add") diff --git a/setup/setup.ui b/setup/setup.ui index 0a69df8..f1e6d0b 100644 --- a/setup/setup.ui +++ b/setup/setup.ui @@ -117,7 +117,6 @@ True - False The shortcut keys for switching to previous input method in the list 0 Previous input method: @@ -204,7 +203,6 @@ True - False True False @@ -216,7 +214,6 @@ ... True - False True False True @@ -825,6 +822,7 @@ You may use up/down buttons to change it.</i></small> True vertical 6 + True Use system keyboard layout @@ -840,6 +838,57 @@ You may use up/down buttons to change it.</i></small> 0 + + + True + 6 + + + True + System Keyboard Layout: + True + center + + + False + False + 0 + + + + + + True + True + False + + + False + False + 1 + + + + + False + False + 1 + + + + + Add or remove layouts in 'Select an input method' list + True + True + False + Add or remove keyboard layouts in all input method engnines + + + False + False + 2 + + @@ -1038,4 +1087,558 @@ Homepage: http://code.google.com/p/ibus + + Add or Remove Layouts + ibus-setup + + + vertical + True + 10 + 12 + + + True + 12 + 12 + + + True + 0 + none + + + True + 12 + 12 + + + True + vertical + 6 + + + True + True + 5 + 450 + 350 + automatic + automatic + out + + + True + none + + + True + vertical + + + + + + + 0 + + + + + + + + + True + <b>Keyboard Layout</b> + True + + + + + + + False + 0 + + + + + True + True + end + + + gtk-cancel + True + True + + + False + False + 0 + + + + + gtk-ok + True + True + True + + + False + False + 1 + + + + + False + False + end + 1 + + + + + + + System Keyboard Layout Setup + ibus-setup + + + vertical + True + 10 + 12 + + + True + 12 + 12 + + + True + 0 + none + + + True + 12 + 12 + + + True + vertical + 6 + + + True + + + True + vertical + 6 + + + True + + + False + 0 + + + + + 150 + True + True + automatic + automatic + in + + + True + True + 150 + + + + + False + 1 + + + + + 0 + + + + + True + vertical + 5 + start + + + gtk-add + True + False + True + True + Add the selected keyboard layout into the system keyboard layouts + True + + + False + False + 0 + + + + + gtk-remove + True + False + True + True + Remove the selected keyboard layout from the system keyboard layouts + True + + + False + False + 1 + + + + + gtk-go-up + True + False + True + True + Move up the selected keyboard layout in the system keyboard layouts list + True + + + False + False + 2 + + + + + gtk-go-down + True + False + True + True + Move down the selected keyboard layout in the system keyboard layouts list + True + + + False + False + 3 + + + + + Rese_t + True + False + True + True + Reset the system keyboard layouts list + True + + + False + False + 4 + + + + + 1 + + + + + 0 + + + + + True + 6 + + + True + gtk-info + 2 + + + False + 0 + + + + + True + 0 + True + + + 1 + + + + + 1 + + + + + + + + + True + <b>Keyboard Layout</b> + True + + + + + + + False + 0 + + + + + True + 12 + 12 + + + True + 0 + none + + + True + 12 + 12 + + + True + vertical + 6 + + + True + True + start + + + True + _Options... + True + True + + + False + False + 0 + + + + + False + False + 0 + + + + + + + + + True + <b>Keyboard Option</b> + True + + + + + + + False + 1 + + + + + True + True + end + + + gtk-cancel + True + True + + + False + False + 0 + + + + + gtk-ok + True + True + True + + + False + False + 1 + + + + + False + False + end + 1 + + + + + + + System Keyboard Option Setup + ibus-setup + + + vertical + True + 10 + 12 + + + True + 12 + 12 + + + True + 0 + none + + + True + 12 + 12 + + + True + vertical + 6 + + + Use the default keyboard option + True + True + False + Use the defualt XKB option + True + + + 0 + + + + + True + True + 5 + 450 + 350 + automatic + automatic + out + + + True + none + + + True + vertical + + + + + + + 1 + + + + + + + + + True + <b>Keyboard Option</b> + True + + + + + + + False + 0 + + + + + True + True + end + + + gtk-close + True + True + + + False + False + 0 + + + + + False + False + end + 1 + + + + + diff --git a/setup/xkbsetup.py b/setup/xkbsetup.py new file mode 100644 index 0000000..74d5212 --- /dev/null +++ b/setup/xkbsetup.py @@ -0,0 +1,454 @@ +# vim:set et sts=4 sw=4: +# +# ibus - The Input Bus +# +# Copyright (c) 2010 Takao Fujiwara +# Copyright (c) 2007-2010 Peng Huang +# Copyright (c) 2007-2010 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA + +import gettext +import gobject +import gtk +import ibus + +_ = lambda a : gettext.dgettext("ibus", a) +XKB_MAX_LAYOUTS = 4 + +class XKBSetup(gobject.GObject): + def __init__(self, config, builder): + super(XKBSetup, self).__init__() + + self.__config = config + self.__builder = builder + + # system keyboard layout setting + self.__button_system_keyboard_layout = self.__builder.get_object("button_system_keyboard_layout") + text = str(self.__config.get_value("general", "system_keyboard_layout", '')) + if text == 'default' or text == '': + text = _("Default") + self.__button_system_keyboard_layout.set_label(text) + if not self.__config.get_value("general", "use_system_keyboard_layout", True): + self.__button_system_keyboard_layout.set_sensitive(False) + self.__button_system_keyboard_layout.connect("clicked", self.__button_system_keyboard_layout_cb) + + # use system keyboard layout setting + button = self.__builder.get_object("checkbutton_use_sys_layout") + button.connect("toggled", lambda button: self.__button_system_keyboard_layout.set_sensitive(button.get_active())) + + self.__xkblayoutconfig = None + self.__preload_xkb_engines = [] + self.__other_xkb_engines = [] + self.__default_xkb_engine = None + if ibus.XKBConfigRegistry.have_xkb(): + self.__xkblayoutconfig = ibus.XKBLayoutConfig() + + # config layouts dialog + self.__init_config_layouts() + + # default system keyboard dialog + self.__init_system_keyboard() + + def __get_xkb_engines(self): + xkb_engines = [] + xkbconfig = ibus.XKBConfigRegistry() + layout_list = xkbconfig.get_layout_list() + layout_desc = xkbconfig.get_layout_desc() + layout_lang = xkbconfig.get_layout_lang() + variant_desc = xkbconfig.get_variant_desc() + for layout in layout_list.keys(): + langs = [] + if layout in layout_lang: + langs = layout_lang[layout] + for lang in langs: + engine = ibus.XKBConfigRegistry.engine_desc_new( + lang, + layout, + layout_desc[layout], + None, + None) + xkb_engines.append(engine) + for variant in layout_list[layout]: + label = "%s(%s)" % (layout, variant) + sub_langs = [] + if label in layout_lang: + sub_langs = layout_lang[label] + else: + sub_langs = langs + for lang in sub_langs: + engine = ibus.XKBConfigRegistry.engine_desc_new( + lang, + layout, + layout_desc[layout], + variant, + variant_desc[label]) + xkb_engines.append(engine) + return xkb_engines + + def __get_default_xkb_engine(self): + if self.__default_xkb_engine != None: + return self.__default_xkb_engine + self.__default_xkb_engine = ibus.XKBConfigRegistry.engine_desc_new( + "other", + "default", + _("Default"), + None, + None) + return self.__default_xkb_engine + + def __init_config_layouts(self): + if not ibus.XKBConfigRegistry.have_xkb(): + button = self.__builder.get_object("button_config_layouts") + button.hide() + return + + self.__dialog_config_layouts = self.__builder.get_object("dialog_config_layouts") + self.__button_config_layouts_cancel = self.__builder.get_object("button_config_layouts_cancel") + self.__button_config_layouts_cancel.connect("clicked", self.__button_config_layouts_cancel_cb) + self.__button_config_layouts_ok = self.__builder.get_object("button_config_layouts_ok") + self.__button_config_layouts_ok.connect("clicked", self.__button_config_layouts_ok_cb) + self.__vbox_all_keyboard_layouts = self.__builder.get_object("vbox_all_keyboard_layouts") + + xkb_engines = self.__get_xkb_engines() + if len(xkb_engines) > 0: + button = self.__builder.get_object("button_config_layouts") + button.connect("clicked", self.__button_config_layouts_cb) + button.set_sensitive(True) + + engine_dict = {} + for engine in xkb_engines: + if not engine.name.startswith("xkb:layout:"): + continue + lang = ibus.get_language_name(engine.language) + if lang not in engine_dict: + engine_dict[lang] = [] + engine_dict[lang].append(engine) + + keys = engine_dict.keys() + keys.sort() + if ibus.get_language_name("Other") in keys: + keys.remove(ibus.get_language_name("Other")) + keys += [ibus.get_language_name("Other")] + + preload_xkb_engines = self.__xkblayoutconfig.get_preload_layouts() + for lang in keys: + expander = gtk.Expander("") + self.__vbox_all_keyboard_layouts.pack_start(expander, True, True, 0) + expander.show() + label = expander.get_label_widget() + label.set_label(lang) + align = gtk.Alignment(0, 0, 1, 0) + align.set_padding(6, 0, 18, 0) + expander.add(align) + align.show() + vbox = gtk.VBox(False, 0) + align.add(vbox) + vbox.show() + + def cmp_engine(a, b): + if a.rank == b.rank: + return cmp(a.longname, b.longname) + return int(b.rank - a.rank) + engine_dict[lang].sort(cmp_engine) + + for engine in engine_dict[lang]: + sub_name = engine.name[len("xkb:layout:"):] + layout_list = sub_name.split(':') + if len(layout_list) > 1: + layout = "%s(%s)" % (layout_list[0], layout_list[1]) + else: + layout = layout_list[0] + has_preloaded = False + for preload_name in preload_xkb_engines: + preload_name = str(preload_name) + if len(preload_name) == 0: + continue + if layout == preload_name: + has_preloaded = True + break + + checkbutton = gtk.CheckButton(engine.longname) + checkbutton.set_data("layout", layout) + if has_preloaded: + checkbutton.set_active(True) + vbox.pack_start(checkbutton, False, True, 0) + checkbutton.show() + + def __init_system_keyboard_layout(self): + self.__dialog_system_keyboard_layout = self.__builder.get_object("dialog_system_keyboard_layout") + self.__button_system_keyboard_layout_cancel = self.__builder.get_object("button_system_keyboard_layout_cancel") + self.__button_system_keyboard_layout_cancel.connect("clicked", self.__button_system_keyboard_layout_cancel_cb) + self.__button_system_keyboard_layout_ok = self.__builder.get_object("button_system_keyboard_layout_ok") + self.__button_system_keyboard_layout_ok.connect("clicked", self.__button_system_keyboard_layout_ok_cb) + + # get xkb layouts + xkb_engines = self.__get_xkb_engines() + + self.__combobox_system_keyboard_layout = self.__builder.get_object("combobox_system_keyboard_layout_engines") + self.__combobox_system_keyboard_layout.set_engines(xkb_engines) + self.__combobox_system_keyboard_layout.set_title(_("Select keyboard layouts")) + self.__combobox_system_keyboard_layout.connect("notify::active-engine", self.__combobox_notify_active_system_keyboard_layout_cb) + self.__treeview_system_keyboard_layout = self.__builder.get_object("treeview_system_keyboard_layout_engines") + self.__treeview_system_keyboard_layout.connect("notify", self.__treeview_notify_system_keyboard_layout_cb) + column = self.__treeview_system_keyboard_layout.get_column(0) + column.set_title(_("Keyboard Layouts")) + button = self.__builder.get_object("button_system_keyboard_layout_engine_add") + button.connect("clicked", self.__button_system_keyboard_layout_add_cb) + button = self.__builder.get_object("button_system_keyboard_layout_engine_remove") + button.connect("clicked", self.__button_system_keyboard_layout_remove_cb) + button = self.__builder.get_object("button_system_keyboard_layout_engine_up") + button.connect("clicked", lambda *args:self.__treeview_system_keyboard_layout.move_up_engine()) + + button = self.__builder.get_object("button_system_keyboard_layout_engine_down") + button.connect("clicked", lambda *args:self.__treeview_system_keyboard_layout.move_down_engine()) + button = self.__builder.get_object("button_system_keyboard_layout_engine_reset") + button.connect("clicked", self.__button_system_keyboard_layout_reset_cb) + button_reset = button + text = str(self.__config.get_value("general", "system_keyboard_layout", '')) + if text == "default" or text == None: + engine = self.__get_default_xkb_engine() + self.__treeview_system_keyboard_layout.set_engines([engine]) + button_reset.set_sensitive(False) + else: + for layout in text.split(','): + layout_engine = None + for engine in xkb_engines: + if layout == engine.layout: + layout_engine = engine + break + if layout_engine != None: + self.__treeview_system_keyboard_layout.append_engine(layout_engine) + button_reset.set_sensitive(True) + label = self.__builder.get_object("label_system_keyboard_layout_engines") + label.set_markup(_("The system keyboard layouts " + "can be set less than or equal to %d.\n" + "You may use Up/Down buttons to change the order." + "") % XKB_MAX_LAYOUTS) + + def __init_system_keyboard_option(self): + self.__dialog_system_keyboard_option = self.__builder.get_object("dialog_system_keyboard_option") + self.__button_system_keyboard_option_close = self.__builder.get_object("button_system_keyboard_option_close") + self.__button_system_keyboard_option_close.connect( + "clicked", lambda button: self.__dialog_system_keyboard_option.hide()) + + button = self.__builder.get_object("button_system_keyboard_option_setup") + button.connect("clicked", self.__button_system_keyboard_option_cb) + self.__checkbutton_use_system_keyboard_option = self.__builder.get_object("checkbutton_use_system_keyboard_option") + self.__vbox_system_keyboard_options = self.__builder.get_object("vbox_system_keyboard_options") + option_array = [] + text = str(self.__config.get_value("general", "system_keyboard_option", '')) + if text == None or text == "default": + self.__checkbutton_use_system_keyboard_option.set_active(True) + self.__vbox_system_keyboard_options.set_sensitive(False) + else: + self.__checkbutton_use_system_keyboard_option.set_active(False) + self.__vbox_system_keyboard_options.set_sensitive(True) + option_array = text.split(',') + self.__checkbutton_use_system_keyboard_option.connect( + "toggled", lambda button: self.__vbox_system_keyboard_options.set_sensitive(not button.get_active())) + + xkbconfig = ibus.XKBConfigRegistry() + option_list = xkbconfig.get_option_list() + option_group_desc = xkbconfig.get_option_group_desc() + option_desc = xkbconfig.get_option_desc() + for option_group in option_list.keys(): + expander = gtk.Expander("") + self.__vbox_system_keyboard_options.pack_start(expander, True, True, 0) + expander.show() + checked = 0 + label = expander.get_label_widget() + label.set_label(option_group_desc[option_group]) + label.set_data("option_group", option_group) + expander.set_data("checked", checked) + align = gtk.Alignment(0, 0, 1, 0) + align.set_padding(6, 0, 18, 0) + expander.add(align) + align.show() + vbox = gtk.VBox(False, 0) + align.add(vbox) + vbox.show() + for option in option_list[option_group]: + checkbutton = gtk.CheckButton(option_desc[option]) + checkbutton.set_data("option", option) + if option in option_array: + checkbutton.set_active(True) + label.set_markup("" + + option_group_desc[option_group] + + "") + checked = checked + 1 + expander.set_data("checked", checked) + checkbutton.connect("toggled", + self.__checkbutton_system_keyboard_option_toggled_cb, + expander) + vbox.pack_start(checkbutton, False, True, 0) + checkbutton.show() + + def __init_system_keyboard(self): + if not ibus.XKBConfigRegistry.have_xkb(): + hbox = self.__builder.get_object("hbox_system_keyboard_layout") + hbox.hide() + return + + self.__init_system_keyboard_layout() + self.__init_system_keyboard_option() + + def __combobox_notify_active_system_keyboard_layout_cb(self, combobox, property): + engine = self.__combobox_system_keyboard_layout.get_active_engine() + button = self.__builder.get_object("button_system_keyboard_layout_engine_add") + engines = self.__treeview_system_keyboard_layout.get_engines() + button.set_sensitive(engine != None and \ + engine not in engines and \ + len(engines) < XKB_MAX_LAYOUTS) + + def __treeview_notify_system_keyboard_layout_cb(self, treeview, property): + if property.name != "active-engine" and property.name != "engines": + return + + engines = self.__treeview_system_keyboard_layout.get_engines() + engine = self.__treeview_system_keyboard_layout.get_active_engine() + + button = self.__builder.get_object("button_system_keyboard_layout_engine_remove") + button.set_sensitive(engine != None) + button = self.__builder.get_object("button_system_keyboard_layout_engine_up") + button.set_sensitive(engine not in engines[:1]) + button = self.__builder.get_object("button_system_keyboard_layout_engine_down") + button.set_sensitive(engine not in engines[-1:]) + + def __button_system_keyboard_layout_add_cb(self, button): + engines = self.__treeview_system_keyboard_layout.get_engines() + engine = self.__combobox_system_keyboard_layout.get_active_engine() + if len(engines) > 0 and engines[0].layout == "default": + self.__treeview_system_keyboard_layout.set_engines([engine]) + else: + self.__treeview_system_keyboard_layout.append_engine(engine) + button_reset = self.__builder.get_object("button_system_keyboard_layout_engine_reset") + button_reset.set_sensitive(True) + if len(self.__treeview_system_keyboard_layout.get_engines()) >= XKB_MAX_LAYOUTS: + button.set_sensitive(False) + + def __button_system_keyboard_layout_remove_cb(self, button): + self.__treeview_system_keyboard_layout.remove_engine() + if len(self.__treeview_system_keyboard_layout.get_engines()) < XKB_MAX_LAYOUTS: + button_add = self.__builder.get_object("button_system_keyboard_layout_engine_add") + button_add.set_sensitive(True) + button_reset = self.__builder.get_object("button_system_keyboard_layout_engine_reset") + button_reset.set_sensitive(True) + + def __button_system_keyboard_layout_reset_cb(self, button): + engine = self.__get_default_xkb_engine() + self.__treeview_system_keyboard_layout.set_engines([engine]) + button.set_sensitive(False) + + def __button_config_layouts_cb(self, button): + self.__dialog_config_layouts.run() + self.__dialog_config_layouts.hide() + + def __button_config_layouts_cancel_cb(self, button): + self.__dialog_config_layouts.hide() + + def __button_config_layouts_ok_cb(self, button): + self.__dialog_config_layouts.hide() + engine_list = [] + for expander in self.__vbox_all_keyboard_layouts.get_children(): + align = expander.get_children()[0] + vbox = align.get_children()[0] + for checkbutton in vbox.get_children(): + if checkbutton.get_active(): + engine_list.append(checkbutton.get_data("layout")) + if len(engine_list) == 0: + return + engine_list.sort() + self.__xkblayoutconfig.save_preload_layouts(engine_list) + message = _("Please restart IBus to reload your configuration.") + dlg = gtk.MessageDialog(type = gtk.MESSAGE_INFO, + buttons = gtk.BUTTONS_OK, + message_format = message) + dlg.run() + dlg.destroy() + + def __button_system_keyboard_layout_cb(self, button): + self.__dialog_system_keyboard_layout.run() + self.__dialog_system_keyboard_layout.hide() + + def __button_system_keyboard_layout_cancel_cb(self, button): + self.__dialog_system_keyboard_layout.hide() + + def __button_system_keyboard_layout_ok_cb(self, button): + self.__dialog_system_keyboard_layout.hide() + layout = "default" + for engine in self.__treeview_system_keyboard_layout.get_engines(): + if layout == "default": + layout = engine.layout + else: + layout = "%s,%s" % (layout, engine.layout) + if layout == None or layout == "": + layout = "default" + org_layout = str(self.__config.get_value("general", "system_keyboard_layout", None)) + if layout != org_layout: + self.__config.set_value("general", "system_keyboard_layout", layout) + if layout == "default": + layout = _("Default") + self.__button_system_keyboard_layout.set_label(layout) + option = "default" + if not self.__checkbutton_use_system_keyboard_option.get_active(): + for expander in self.__vbox_system_keyboard_options.get_children(): + align = expander.get_children()[0] + vbox = align.get_children()[0] + for checkbutton in vbox.get_children(): + if checkbutton.get_active(): + data = checkbutton.get_data("option") + if option == "default": + option = data + else: + option = "%s,%s" % (option, data) + if option == None or option == "": + option = "default" + if option != "default" and option.find(':') < 0: + message = _("The keyboard option cannot be chosen.") + dlg = gtk.MessageDialog(type = gtk.MESSAGE_INFO, + buttons = gtk.BUTTONS_OK, + message_format = message) + dlg.run() + dlg.destroy() + return + org_option = str(self.__config.get_value("general", "system_keyboard_option", None)) + if option != org_option: + self.__config.set_value("general", "system_keyboard_option", option) + message = _("Please restart IBus to reload your configuration.") + dlg = gtk.MessageDialog(type = gtk.MESSAGE_INFO, + buttons = gtk.BUTTONS_OK, + message_format = message) + dlg.run() + dlg.destroy() + + def __button_system_keyboard_option_cb(self, button): + self.__dialog_system_keyboard_option.run() + self.__dialog_system_keyboard_option.hide() + + def __checkbutton_system_keyboard_option_toggled_cb(self, button, user_data): + expander = user_data + checked = expander.get_data("checked") + label = expander.get_label_widget() + if button.get_active(): + checked = checked + 1 + label.set_markup("" + label.get_text() + "") + else: + checked = checked - 1 + if checked <= 0: + label.set_text(label.get_text()) + expander.set_data("checked", checked) + diff --git a/src/ibusfactory.c b/src/ibusfactory.c index 11d9a6d..7770216 100644 --- a/src/ibusfactory.c +++ b/src/ibusfactory.c @@ -21,6 +21,7 @@ */ #include "ibusfactory.h" #include "ibusengine.h" +#include "ibusmarshalers.h" #include "ibusshare.h" #include "ibusinternal.h" @@ -28,6 +29,7 @@ (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_FACTORY, IBusFactoryPrivate)) enum { + LOOKUP_ENGINE_NAME, LAST_SIGNAL, }; @@ -42,6 +44,8 @@ struct _IBusFactoryPrivate { GHashTable *engine_table; }; +static guint factory_signals[LAST_SIGNAL] = { 0 }; + /* functions prototype */ static void ibus_factory_destroy (IBusFactory *factory); static void ibus_factory_set_property (IBusFactory *engine, @@ -113,6 +117,17 @@ ibus_factory_class_init (IBusFactoryClass *class) ibus_service_class_add_interfaces (IBUS_SERVICE_CLASS (class), introspection_xml); g_type_class_add_private (class, sizeof (IBusFactoryPrivate)); + + factory_signals[LOOKUP_ENGINE_NAME] = + g_signal_new (I_("lookup-engine-name"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (IBusFactoryClass, lookup_engine_name), + NULL, NULL, + _ibus_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); } static void @@ -190,8 +205,12 @@ ibus_factory_service_method_call (IBusService *service, if (g_strcmp0 (method_name, "CreateEngine") == 0) { gchar *engine_name = NULL; + GType engine_type; + g_variant_get (parameters, "(&s)", &engine_name); - GType engine_type = (GType )g_hash_table_lookup (factory->priv->engine_table, engine_name); + g_signal_emit (factory, factory_signals[LOOKUP_ENGINE_NAME], + 0, engine_name); + engine_type = (GType) g_hash_table_lookup (factory->priv->engine_table, engine_name); if (engine_type == G_TYPE_INVALID) { gchar *error_message = g_strdup_printf ("Can not fond engine %s", engine_name); diff --git a/src/ibusfactory.h b/src/ibusfactory.h index 47c06e0..102081c 100644 --- a/src/ibusfactory.h +++ b/src/ibusfactory.h @@ -127,10 +127,13 @@ struct _IBusFactoryClass { IBusServiceClass parent; /* signals */ + void (* lookup_engine_name) + (IBusFactory *factory, + const gchar *engine_name); /*< private >*/ /* padding */ - gpointer pdummy[8]; + gpointer pdummy[7]; }; /** diff --git a/ui/gtk/panel.py b/ui/gtk/panel.py index 90be1d5..de64920 100644 --- a/ui/gtk/panel.py +++ b/ui/gtk/panel.py @@ -132,6 +132,22 @@ class Panel(ibus.PanelBase): self.__config_load_show_im_name() # self.__bus.request_name(ibus.panel.IBUS_SERVICE_PANEL, 0) + # init xkb + self.__xkblayout = ibus.XKBLayout(self.__config) + use_xkb = self.__config.get_value("general", "use_system_keyboard_layout", False) + if not use_xkb: + self.__xkblayout.use_xkb(use_xkb) + value = str(self.__config.get_value("general", "system_keyboard_layout", '')) + if value == '': + value = 'default' + if value != 'default': + self.__xkblayout.set_default_layout(value) + value = str(self.__config.get_value("general", "system_keyboard_option", '')) + if value == '': + value = 'default' + if value != 'default': + self.__xkblayout.set_default_option(value) + def set_cursor_location(self, x, y, w, h): self.__candidate_panel.set_cursor_location(x, y, w, h) @@ -226,14 +242,20 @@ class Panel(ibus.PanelBase): if not enabled: self.__set_im_icon(ICON_KEYBOARD) self.__set_im_name(None) + if self.__bus.get_use_sys_layout(): + self.__xkblayout.set_layout() else: engine = self.__focus_ic.get_engine() if engine: self.__set_im_icon(engine.icon) self.__set_im_name(engine.longname) + if self.__bus.get_use_sys_layout(): + self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine)) else: self.__set_im_icon(ICON_KEYBOARD) self.__set_im_name(None) + if self.__bus.get_use_sys_layout(): + self.__xkblayout.set_layout() self.__language_bar.focus_in() def focus_out(self, ic): @@ -243,6 +265,8 @@ class Panel(ibus.PanelBase): self.__language_bar.focus_out() self.__set_im_icon(ICON_KEYBOARD) self.__set_im_name(None) + if self.__bus.get_use_sys_layout(): + self.__xkblayout.set_layout() def state_changed(self): if not self.__focus_ic: @@ -255,14 +279,20 @@ class Panel(ibus.PanelBase): self.reset() self.__set_im_icon(ICON_KEYBOARD) self.__set_im_name(None) + if self.__bus.get_use_sys_layout(): + self.__xkblayout.set_layout() else: engine = self.__focus_ic.get_engine() if engine: self.__set_im_icon(engine.icon) self.__set_im_name(engine.longname) + if self.__bus.get_use_sys_layout(): + self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine)) else: self.__set_im_icon(ICON_KEYBOARD) self.__set_im_name(None) + if self.__bus.get_use_sys_layout(): + self.__xkblayout.set_layout() def reset(self): @@ -542,3 +572,12 @@ class Panel(ibus.PanelBase): flags=glib.SPAWN_DO_NOT_REAP_CHILD)[0] self.__setup_pid = pid glib.child_watch_add(self.__setup_pid, self.__child_watch_cb) + + def __engine_get_layout_wrapper(self, engine): + # This code is for the back compatibility. + # Should we remove the codes after all IM engines are changed + # to "default" layout? + if engine.name != None and engine.name.startswith("xkb:layout:"): + return engine.layout + else: + return "default" diff --git a/xkb/Makefile.am b/xkb/Makefile.am new file mode 100644 index 0000000..64b1fc8 --- /dev/null +++ b/xkb/Makefile.am @@ -0,0 +1,104 @@ +# vim:set noet ts=4: +# +# ibus - The Input Bus +# +# Copyright (C) 2010 Takao Fujiwara +# Copyright (c) 2007-2010 Peng Huang +# Copyright (c) 2007-2010 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA + +libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la + +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src \ + -DIBUS_LOCALEDIR=\"$(datadir)/locale\" \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + $(NULL) + +noinst_PROGRAMS = $(TESTS) +libexec_PROGRAMS = +EXTRA_DIST = +DISTCLEANFILES = + +if ENABLE_XKB +libexec_PROGRAMS += ibus-xkb +ibus_xkb_SOURCES = \ + ibus-xkb-main.c \ + xkblib.h \ + xkblib.c \ + $(NULL) +ibus_xkb_CFLAGS = \ + @XKB_CFLAGS@ \ + @X11_CFLAGS@ \ + @GLIB2_CFLAGS@ \ + $(NULL) +ibus_xkb_LDADD = \ + @XKB_LIBS@ \ + @X11_LIBS@ \ + @GLIB2_LIBS@ \ + $(libibus) \ + $(NULL) + +libexec_PROGRAMS += ibus-engine-xkb +ibus_engine_xkb_SOURCES = \ + ibus-engine-xkb-main.c \ + ibus-engine-xkb-main.h \ + xkbxml.c \ + xkbxml.h \ + $(NULL) +ibus_engine_xkb_CFLAGS = \ + @GLIB2_CFLAGS@ \ + @GOBJECT2_CFLAGS@ \ + @GCONF_CFLAGS@ \ + $(NULL) +ibus_engine_xkb_LDADD = \ + @GLIB2_LIBS@ \ + @GOBJECT2_LIBS@ \ + @GCONF_LIBS@ \ + $(libibus) \ + $(NULL) + +xkblayoutdir = $(datadir)/ibus/component +xkblayout_in_files = xkblayout.xml.in +xkblayout_DATA = $(xkblayout_in_files:.xml.in=.xml) + +xkblayoutconfigdir = $(datadir)/ibus/xkb +xkblayoutconfig_in_files = xkblayoutconfig.xml.in +xkblayoutconfig_DATA = $(xkblayoutconfig_in_files:.xml.in=.xml) + +%.xml : %.xml.in + @sed -e "s|\@libexecdir\@|$(libexecdir)|g" \ + -e "s|\@datadir\@|$(datadir)|g" \ + -e "s|\@XKB_PRELOAD_LAYOUTS\@|$(XKB_PRELOAD_LAYOUTS)|g" \ + $< > $@ + +INCLUDES += \ + -DXKBLAYOUTCONFIG_FILE=\""$(xkblayoutconfigdir)/$(xkblayoutconfig_DATA)"\" \ + $(NULL) + +EXTRA_DIST += \ + $(xkblayout_in_files) \ + $(xkblayoutconfig_in_files) \ + $(NULL) + +DISTCLEANFILES += \ + $(xkblayout_DATA) \ + $(xkblayoutconfig_DATA) \ + $(NULL) + +endif diff --git a/xkb/ibus-engine-xkb-main.c b/xkb/ibus-engine-xkb-main.c new file mode 100644 index 0000000..e1861e8 --- /dev/null +++ b/xkb/ibus-engine-xkb-main.c @@ -0,0 +1,397 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2010 Takao Fujiwara + * Copyright (C) 2008-2010 Peng Huang + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#ifdef ENABLE_NLS +#include +#endif + +#include "ibus-engine-xkb-main.h" +#include "xkbxml.h" + +#define IBUS_TYPE_XKB_ENGINE (ibus_xkb_engine_get_type ()) + +static IBusBus *bus = NULL; +static IBusFactory *factory = NULL; +static IBusEngineClass *parent_class = NULL; +static gboolean ibus = FALSE; +static gboolean xml = FALSE; + +static const GOptionEntry entries[] = +{ + { "ibus", 'i', 0, G_OPTION_ARG_NONE, &ibus, "component is executed by ibus", NULL }, + { "xml", 'x', 0, G_OPTION_ARG_NONE, &xml, "print component xml", NULL }, + { NULL }, +}; + +static GObject* +ibus_xkb_engine_constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + IBusXKBEngine *engine; + + engine = (IBusXKBEngine *) G_OBJECT_CLASS (parent_class)->constructor (type, + n_construct_params, + construct_params); + + return (GObject *) engine; +} + +static void +ibus_xkb_engine_destroy (IBusObject *object) +{ + IBUS_OBJECT_CLASS (parent_class)->destroy (object); +} + +static void +ibus_xkb_engine_enable (IBusEngine *engine) +{ + parent_class->enable (engine); +} + +static void +ibus_xkb_engine_disable (IBusEngine *engine) +{ + parent_class->disable (engine); +} + +static void +ibus_xkb_engine_focus_in (IBusEngine *engine) +{ + parent_class->focus_in (engine); +} + +static void +ibus_xkb_engine_focus_out (IBusEngine *engine) +{ + parent_class->focus_out (engine); +} + +static void +ibus_xkb_engine_class_init (IBusXKBEngineClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass); + + parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass); + object_class->constructor = ibus_xkb_engine_constructor; + ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_xkb_engine_destroy; + engine_class->enable = ibus_xkb_engine_enable; + engine_class->disable = ibus_xkb_engine_disable; + engine_class->focus_in = ibus_xkb_engine_focus_in; + engine_class->focus_out = ibus_xkb_engine_focus_out; + +} + +static void +ibus_xkb_engine_init (IBusXKBEngine *engine) +{ +} + +GType +ibus_xkb_engine_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (IBusXKBEngineClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) ibus_xkb_engine_class_init, + NULL, + NULL, + sizeof (IBusXKBEngine), + 0, + (GInstanceInitFunc) ibus_xkb_engine_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_ENGINE, + "IBusXKBEngine", + &type_info, + (GTypeFlags) 0); + } + + return type; +} + +static void +ibus_disconnected_cb (IBusBus *bus, + gpointer user_data) +{ + g_debug ("bus disconnected"); + ibus_quit (); +} + +static void +_factory_lookup_engine_name_cb (IBusFactory *factory, + const gchar *engine_name, + gpointer data) +{ + static GList *engine_list = NULL; + GList *list; + gboolean has_name = FALSE; + + g_return_if_fail (engine_name != NULL); + + if (g_strcmp0 (engine_name, "xkb:layout:us") == 0) { + return; + } + list = engine_list; + while (list) { + if (g_strcmp0 (list->data, engine_name) == 0) { + has_name = TRUE; + break; + } + list = list->next; + } + if (has_name) { + return; + } + + ibus_factory_add_engine (factory, engine_name, IBUS_TYPE_XKB_ENGINE); + engine_list = g_list_append (engine_list, (gpointer) g_strdup (engine_name)); +} + +static void +start_component (int argc, char **argv) +{ + IBusComponent *component; + + ibus_init (); + + bus = ibus_bus_new (); + g_signal_connect (bus, "disconnected", G_CALLBACK (ibus_disconnected_cb), NULL); + + component = ibus_component_new ("org.freedesktop.IBus.XKB", + "XKB Component", + VERSION, + "LGPL2.1", + "Takao Fujiwara ", + "http://code.google.com/p/ibus/", + "", + GETTEXT_PACKAGE); + ibus_component_add_engine (component, + ibus_xkb_engine_desc_new ("eng", + "us", + "USA", + NULL, + NULL)); + + factory = ibus_factory_new (ibus_bus_get_connection (bus)); + + ibus_factory_add_engine (factory, "xkb:layout:us", IBUS_TYPE_XKB_ENGINE); + + g_signal_connect (G_OBJECT (factory), "lookup-engine-name", + G_CALLBACK (_factory_lookup_engine_name_cb), + NULL); + if (ibus) { + ibus_bus_request_name (bus, "org.freedesktop.IBus.XKB", 0); + } + else { + ibus_bus_register_component (bus, component); + } + + g_object_unref (component); + + ibus_main (); +} + +static gboolean +is_included_engine_in_preload (const GList * preload_xkb_engines, + const gchar *layout, + const gchar *variant) +{ + const GList *list = preload_xkb_engines; + gchar *key = NULL; + gboolean retval = FALSE; + + g_return_val_if_fail (layout != NULL, FALSE); + + if (variant == NULL) { + key = g_strdup (layout); + } else { + key = g_strdup_printf ("%s(%s)", layout, variant); + } + while (list) { + if (list->data == NULL) { + continue; + } + if (g_strcmp0 ((const gchar *) list->data, + (const gchar *) key) == 0) { + retval = TRUE; + break; + } + list = list->next; + } + g_free (key); + return retval; +} + +static void +print_component () +{ + IBusXKBLayoutConfig *layout_config; + IBusXKBConfigRegistry *config_registry; + GHashTable *layout_list; + GHashTable *layout_lang; + GHashTable *layout_desc; + GHashTable *variant_desc; + IBusComponent *component; + IBusEngineDesc *engine; + const GList *preload_xkb_engines = NULL; + GList *keys; + GList *variants; + GList *langs; + gboolean is_preload; + gchar *layout_name; + const gchar *desc; + gchar *output; + GString *str; + +#ifdef XKBLAYOUTCONFIG_FILE + layout_config = ibus_xkb_layout_config_new (XKBLAYOUTCONFIG_FILE); + preload_xkb_engines = ibus_xkb_layout_config_get_preload_layouts (layout_config); +#endif + + config_registry = ibus_xkb_config_registry_new (); + layout_list = (GHashTable *) ibus_xkb_config_registry_get_layout_list (config_registry); + layout_lang = (GHashTable *) ibus_xkb_config_registry_get_layout_lang (config_registry); + layout_desc = (GHashTable *) ibus_xkb_config_registry_get_layout_desc (config_registry); + variant_desc = (GHashTable *) ibus_xkb_config_registry_get_variant_desc (config_registry); + component = ibus_xkb_component_new (); + for (keys = g_hash_table_get_keys (layout_list); keys; keys = keys->next) { + if (keys->data == NULL) { + continue; + } + desc = (const gchar *) g_hash_table_lookup (layout_desc, keys->data); + langs = (GList *) g_hash_table_lookup (layout_lang, keys->data); + for (;langs; langs = langs->next) { + if (langs->data == NULL) { + continue; + } + is_preload = FALSE; + if (!preload_xkb_engines) { + is_preload = TRUE; + } else { + is_preload = is_included_engine_in_preload (preload_xkb_engines, + (const gchar *) keys->data, + NULL); + } + if (is_preload) { + engine = ibus_xkb_engine_desc_new ((const gchar *) langs->data, + (const gchar *) keys->data, + desc, + NULL, + NULL); + ibus_component_add_engine (component, engine); + } + } + variants = (GList *) g_hash_table_lookup (layout_list, keys->data); + for (;variants; variants = variants->next) { + if (variants->data == NULL) { + continue; + } + layout_name = g_strdup_printf ("%s(%s)", (gchar *) keys->data, + (gchar *) variants->data); + langs = (GList *) g_hash_table_lookup (layout_lang, layout_name); + if (langs == NULL) { + g_free (layout_name); + layout_name = g_strdup ((gchar *) keys->data); + langs = (GList *) g_hash_table_lookup (layout_lang, layout_name); + } + g_free (layout_name); + for (;langs; langs = langs->next) { + if (langs->data == NULL) { + continue; + } + is_preload = FALSE; + if (!preload_xkb_engines) { + is_preload = TRUE; + } else { + is_preload = is_included_engine_in_preload (preload_xkb_engines, + (const gchar *) keys->data, + (const gchar *) variants->data); + } + if (is_preload) { + engine = ibus_xkb_engine_desc_new ((const gchar *) langs->data, + (const gchar *) keys->data, + desc, + (const gchar *) variants->data, + (const gchar *) g_hash_table_lookup (variant_desc, variants->data)); + ibus_component_add_engine (component, engine); + } + } + } + } + g_object_unref (G_OBJECT (config_registry)); +#ifdef XKBLAYOUTCONFIG_FILE + g_object_unref (G_OBJECT (layout_config)); +#endif + + str = g_string_new (NULL); + ibus_component_output_engines (component , str, 0); + g_object_unref (G_OBJECT (component)); + + output = g_string_free (str, FALSE); + g_print ("%s\n", output); + g_free (output); +} + +int +main (int argc, char **argv) +{ + GError *error = NULL; + GOptionContext *context; + +#ifdef ENABLE_NLS + setlocale (LC_ALL, ""); +#endif + + g_type_init (); + + context = g_option_context_new ("- ibus xkb engine component"); + + g_option_context_add_main_entries (context, entries, "ibus-xbl"); + + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_print ("Option parsing failed: %s\n", error->message); + exit (-1); + } + + if (xml) { + print_component (); + return 0; + } + start_component (argc, argv); + + return 0; +} diff --git a/xkb/ibus-engine-xkb-main.h b/xkb/ibus-engine-xkb-main.h new file mode 100644 index 0000000..8007631 --- /dev/null +++ b/xkb/ibus-engine-xkb-main.h @@ -0,0 +1,46 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2010 Takao Fujiwara + * Copyright (C) 2008-2010 Peng Huang + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __IBUS_ENGINE_XKB_MAIN_H_ +#define __IBUS_ENGINE_XKB_MAIN_H_ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +G_BEGIN_DECLS + +typedef struct _IBusXKBEngine IBusXKBEngine; +typedef struct _IBusXKBEngineClass IBusXKBEngineClass; + +struct _IBusXKBEngine { + IBusEngine engine; +}; + +struct _IBusXKBEngineClass { + IBusEngineClass parent; +}; + +G_END_DECLS +#endif diff --git a/xkb/ibus-xkb-main.c b/xkb/ibus-xkb-main.c new file mode 100644 index 0000000..9db7d0a --- /dev/null +++ b/xkb/ibus-xkb-main.c @@ -0,0 +1,105 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2010 Takao Fujiwara + * Copyright (C) 2008-2010 Peng Huang + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef ENABLE_NLS +#include +#endif + +#include "xkblib.h" + +static gboolean get_layout = FALSE; +static gchar *layout = NULL; +static gchar *model = NULL; +static gchar *option = NULL; + +static const GOptionEntry entries[] = +{ + { "get", 'g', 0, G_OPTION_ARG_NONE, &get_layout, N_("Get current xkb layout"), NULL }, + /* Translators: the "layout" should not be translated due to a variable. */ + { "layout", 'l', 0, G_OPTION_ARG_STRING, &layout, N_("Set xkb layout"), "layout" }, + { "model", 'm', 0, G_OPTION_ARG_STRING, &model, N_("Set xkb model"), "model" }, + { "option", 'o', 0, G_OPTION_ARG_STRING, &option, N_("Set xkb option"), "option" }, + { NULL }, +}; + +int +main (int argc, char *argv[]) +{ + GOptionContext *context; + GError *error = NULL; + Display *xdisplay; + +#ifdef ENABLE_NLS + setlocale (LC_ALL, ""); + + bindtextdomain (GETTEXT_PACKAGE, IBUS_LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +#endif + + context = g_option_context_new ("- ibus daemon"); + + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); + g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); + + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_printerr ("Option parsing failed: %s\n", error->message); + return -1; + } + + xdisplay = XOpenDisplay (NULL); + if (xdisplay == NULL) { + g_warning ("Could not open display"); + return -1; + } + ibus_xkb_init (xdisplay); + + if (layout) { + ibus_xkb_set_layout (layout, model, option); + } + if (get_layout) { + layout = ibus_xkb_get_current_layout (); + model = ibus_xkb_get_current_model (); + option = ibus_xkb_get_current_option (); + g_printf ("layout: %s\n" + "model: %s\n" + "option: %s\n", + layout ? layout : "", + model ? model : "", + option ? option : ""); + g_free (layout); + g_free (model); + g_free (option); + } + + ibus_xkb_finit (); + + return 0; +} diff --git a/xkb/xkblayout.xml.in b/xkb/xkblayout.xml.in new file mode 100644 index 0000000..0b5a4dc --- /dev/null +++ b/xkb/xkblayout.xml.in @@ -0,0 +1,16 @@ + + + org.freedesktop.IBus.XKB + XKB Component + @libexecdir@/ibus-engine-xkb --ibus + 0.0.0 + Takao Fujiwara <takao.fujiwara1@gmail.com> + LGPL2.1 + http://code.google.com/p/ibus/ + ibus + + @datadir@/ibus/xkb/xkblayoutconfig.xml + ~/.config/ibus/xkb/xkblayoutconfig.xml + + + diff --git a/xkb/xkblayoutconfig.xml.in b/xkb/xkblayoutconfig.xml.in new file mode 100644 index 0000000..b1212d1 --- /dev/null +++ b/xkb/xkblayoutconfig.xml.in @@ -0,0 +1,6 @@ + + + + @XKB_PRELOAD_LAYOUTS@ + + diff --git a/xkb/xkblib.c b/xkb/xkblib.c new file mode 100644 index 0000000..640f783 --- /dev/null +++ b/xkb/xkblib.c @@ -0,0 +1,303 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2010 Takao Fujiwara + * Copyright (C) 2008-2010 Peng Huang + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include /* for XKBrules.h */ +#include +#include +#include + +#include "xkblib.h" + +#ifndef XKB_RULES_XML_FILE +#define XKB_RULES_XML_FILE "/usr/share/X11/xkb/rules/evdev.xml" +#endif + +static gchar **default_layouts; +static gchar **default_models; +static gchar **default_options; +static int default_layout_group; + +static Display * +get_xdisplay (Display *xdisplay) +{ + static Display *saved_xdisplay = NULL; + if (xdisplay != NULL) { + saved_xdisplay = xdisplay; + } + return saved_xdisplay; +} + +static void +init_xkb_default_layouts (Display *xdisplay) +{ + XkbStateRec state; + Atom xkb_rules_name, type; + int format; + unsigned long l, nitems, bytes_after; + unsigned char *prop = NULL; + + xkb_rules_name = XInternAtom (xdisplay, "_XKB_RULES_NAMES", TRUE); + if (xkb_rules_name == None) { + g_warning ("Could not get XKB rules atom"); + return; + } + if (XGetWindowProperty (xdisplay, + XDefaultRootWindow (xdisplay), + xkb_rules_name, + 0, 1024, FALSE, XA_STRING, + &type, &format, &nitems, &bytes_after, &prop) != Success) { + g_warning ("Could not get X property"); + return; + } + if (nitems < 3) { + g_warning ("Could not get group layout from X property"); + return; + } + for (l = 0; l < 2; l++) { + prop += strlen ((const char *) prop) + 1; + } + if (prop == NULL || *prop == '\0') { + g_warning ("No layouts form X property"); + return; + } + default_layouts = g_strsplit ((gchar *) prop, ",", -1); + prop += strlen ((const char *) prop) + 1; + default_models = g_strsplit ((gchar *) prop, ",", -1); + prop += strlen ((const char *) prop) + 1; + default_options = g_strsplit ((gchar *) prop, ",", -1); + + if (XkbGetState (xdisplay, XkbUseCoreKbd, &state) != Success) { + g_warning ("Could not get state"); + return; + } + default_layout_group = state.group; +} + +static Bool +set_xkb_rules (Display *xdisplay, + const char *rules_file, const char *model, + const char *all_layouts, const char *all_variants, + const char *all_options) +{ + gchar *rules_path; + XkbRF_RulesPtr rules; + XkbRF_VarDefsRec rdefs; + XkbComponentNamesRec rnames; + XkbDescPtr xkb; + + rules_path = g_strdup ("./rules/evdev"); + rules = XkbRF_Load (rules_path, "C", TRUE, TRUE); + if (rules == NULL) { + g_return_val_if_fail (XKB_RULES_XML_FILE != NULL, FALSE); + + g_free (rules_path); + if (g_str_has_suffix (XKB_RULES_XML_FILE, ".xml")) { + rules_path = g_strndup (XKB_RULES_XML_FILE, + strlen (XKB_RULES_XML_FILE) - 4); + } else { + rules_path = g_strdup (XKB_RULES_XML_FILE); + } + rules = XkbRF_Load (rules_path, "C", TRUE, TRUE); + } + g_return_val_if_fail (rules != NULL, FALSE); + + memset (&rdefs, 0, sizeof (XkbRF_VarDefsRec)); + memset (&rnames, 0, sizeof (XkbComponentNamesRec)); + rdefs.model = model ? g_strdup (model) : NULL; + rdefs.layout = all_layouts ? g_strdup (all_layouts) : NULL; + rdefs.variant = all_variants ? g_strdup (all_variants) : NULL; + rdefs.options = all_options ? g_strdup (all_options) : NULL; + XkbRF_GetComponents (rules, &rdefs, &rnames); + xkb = XkbGetKeyboardByName (xdisplay, XkbUseCoreKbd, &rnames, + XkbGBN_AllComponentsMask, + XkbGBN_AllComponentsMask & + (~XkbGBN_GeometryMask), True); + if (!xkb) { + g_warning ("Cannot load new keyboard description."); + return FALSE; + } + XkbRF_SetNamesProp (xdisplay, rules_path, &rdefs); + g_free (rules_path); + g_free (rdefs.model); + g_free (rdefs.layout); + g_free (rdefs.variant); + g_free (rdefs.options); + + return TRUE; +} + +static Bool +update_xkb_properties (Display *xdisplay, + const char *rules_file, const char *model, + const char *all_layouts, const char *all_variants, + const char *all_options) +{ + int len; + char *pval; + char *next; + Atom rules_atom; + Window root_window; + + len = (rules_file ? strlen (rules_file) : 0); + len += (model ? strlen (model) : 0); + len += (all_layouts ? strlen (all_layouts) : 0); + len += (all_variants ? strlen (all_variants) : 0); + len += (all_options ? strlen (all_options) : 0); + + if (len < 1) { + return TRUE; + } + len += 5; /* trailing NULs */ + + rules_atom = XInternAtom (xdisplay, _XKB_RF_NAMES_PROP_ATOM, False); + root_window = XDefaultRootWindow (xdisplay); + pval = next = g_new0 (char, len + 1); + if (!pval) { + return TRUE; + } + + if (rules_file) { + strcpy (next, rules_file); + next += strlen (rules_file); + } + *next++ = '\0'; + if (model) { + strcpy (next, model); + next += strlen (model); + } + *next++ = '\0'; + if (all_layouts) { + strcpy (next, all_layouts); + next += strlen (all_layouts); + } + *next++ = '\0'; + if (all_variants) { + strcpy (next, all_variants); + next += strlen (all_variants); + } + *next++ = '\0'; + if (all_options) { + strcpy (next, all_options); + next += strlen (all_options); + } + *next++ = '\0'; + if ((next - pval) != len) { + g_free (pval); + return TRUE; + } + + XChangeProperty (xdisplay, root_window, + rules_atom, XA_STRING, 8, PropModeReplace, + (unsigned char *) pval, len); + XSync(xdisplay, False); + + return TRUE; +} + +void +ibus_xkb_init (Display *xdisplay) +{ + get_xdisplay (xdisplay); + init_xkb_default_layouts (xdisplay); +} + +void +ibus_xkb_finit (void) +{ + g_strfreev (default_layouts); + default_layouts = NULL; + g_strfreev (default_models); + default_models = NULL; + g_strfreev (default_options); + default_options = NULL; +} + +gchar * +ibus_xkb_get_current_layout (void) +{ + if (default_layouts == NULL) { + g_warning ("Your system seems not to support XKB."); + return NULL; + } + + return g_strjoinv (",", (gchar **) default_layouts); +} + +gchar * +ibus_xkb_get_current_model (void) +{ + if (default_models == NULL) { + return NULL; + } + + return g_strjoinv (",", (gchar **) default_models); +} + +gchar * +ibus_xkb_get_current_option (void) +{ + if (default_options == NULL) { + return NULL; + } + + return g_strjoinv (",", (gchar **) default_options); +} + +gboolean +ibus_xkb_set_layout (const char *layouts, + const char *variants, + const char *options) +{ + Display *xdisplay; + gboolean retval; + gchar *layouts_line; + + if (default_layouts == NULL) { + g_warning ("Your system seems not to support XKB."); + return NULL; + } + + if (layouts == NULL || g_strcmp0 (layouts, "default") == 0) { + layouts_line = g_strjoinv (",", (gchar **) default_layouts); + } else { + layouts_line = g_strdup (layouts); + } + + xdisplay = get_xdisplay (NULL); + retval = set_xkb_rules (xdisplay, + "evdev", "evdev", + layouts_line, variants, options); + update_xkb_properties (xdisplay, + "evdev", "evdev", + layouts_line, variants, options); + g_free (layouts_line); + + return retval; +} diff --git a/xkb/xkblib.h b/xkb/xkblib.h new file mode 100644 index 0000000..09d506d --- /dev/null +++ b/xkb/xkblib.h @@ -0,0 +1,40 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2010 Takao Fujiwara + * Copyright (C) 2008-2010 Peng Huang + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __XKBLIB_H_ +#define __XKBLIB_H_ + +#include + +G_BEGIN_DECLS + +void ibus_xkb_init (Display *xdisplay); +void ibus_xkb_finit (void); +gchar *ibus_xkb_get_current_layout (void); +gchar *ibus_xkb_get_current_model (void); +gchar *ibus_xkb_get_current_option (void); +gboolean ibus_xkb_set_layout (const char *layouts, + const char *variants, + const char *options); + +G_END_DECLS +#endif diff --git a/xkb/xkbxml.c b/xkb/xkbxml.c new file mode 100644 index 0000000..5e9885c --- /dev/null +++ b/xkb/xkbxml.c @@ -0,0 +1,696 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2010 Takao Fujiwara + * Copyright (C) 2008-2010 Peng Huang + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "xkbxml.h" +#include "ibus.h" + +#ifndef XKB_RULES_XML_FILE +#define XKB_RULES_XML_FILE "/usr/share/X11/xkb/rules/evdev.xml" +#endif + +#define IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryPrivate)) +#define IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_XKB_LAYOUT_CONFIG, IBusXKBLayoutConfigPrivate)) + +typedef struct _IBusXKBConfigRegistryPrivate IBusXKBConfigRegistryPrivate; +typedef struct _IBusXKBLayoutConfigPrivate IBusXKBLayoutConfigPrivate; + +enum { + PROP_0, + PROP_SYSTEM_CONFIG_FILE, +}; + +struct _IBusXKBConfigRegistryPrivate { + GHashTable *layout_list; + GHashTable *layout_lang; + GHashTable *layout_desc; + GHashTable *variant_desc; +}; + +struct _IBusXKBLayoutConfigPrivate { + gchar *system_config_file; + GList *preload_layouts; +}; + +/* functions prototype */ +static void ibus_xkb_config_registry_destroy + (IBusXKBConfigRegistry *xkb_config); +static void ibus_xkb_layout_config_destroy + (IBusXKBLayoutConfig *xkb_layout_config); + +G_DEFINE_TYPE (IBusXKBConfigRegistry, ibus_xkb_config_registry, IBUS_TYPE_OBJECT) +G_DEFINE_TYPE (IBusXKBLayoutConfig, ibus_xkb_layout_config, IBUS_TYPE_OBJECT) + +static void +parse_xkb_xml_languagelist_node (IBusXKBConfigRegistryPrivate *priv, + XMLNode *parent_node, + const gchar *layout_name) +{ + XMLNode *node = parent_node; + XMLNode *sub_node; + GList *p; + GList *lang_list = NULL; + + g_assert (node != NULL); + g_assert (layout_name != NULL); + for (p = node->sub_nodes; p; p = p->next) { + sub_node = (XMLNode *) p->data; + if (g_strcmp0 (sub_node->name, "iso639Id") == 0) { + lang_list = g_list_append (lang_list, + (gpointer) g_strdup (sub_node->text)); + continue; + } + } + if (lang_list == NULL) { + /* some nodes have no lang */ + return; + } + if (g_hash_table_lookup (priv->layout_lang, layout_name) != NULL) { + g_warning ("duplicated name %s exists", layout_name); + return; + } + g_hash_table_insert (priv->layout_lang, + (gpointer) g_strdup (layout_name), + (gpointer) lang_list); +} + +static const gchar * +parse_xkb_xml_configitem_node (IBusXKBConfigRegistryPrivate *priv, + XMLNode *parent_node) +{ + XMLNode *node = parent_node; + XMLNode *sub_node; + GList *p; + gchar *name = NULL; + gchar *description = NULL; + + g_assert (node != NULL); + for (p = node->sub_nodes; p; p = p->next) { + sub_node = (XMLNode *) p->data; + if (g_strcmp0 (sub_node->name, "name") == 0) { + name = sub_node->text; + continue; + } + if (g_strcmp0 (sub_node->name, "description") == 0) { + description = sub_node->text; + continue; + } + if (g_strcmp0 (sub_node->name, "languageList") == 0) { + if (name == NULL) { + g_warning ("layout name is NULL in node %s", node->name); + continue; + } + parse_xkb_xml_languagelist_node (priv, sub_node, name); + continue; + } + } + if (name == NULL) { + g_warning ("No name in layout node"); + return NULL; + } + if (g_hash_table_lookup (priv->layout_desc, name) != NULL) { + g_warning ("duplicated name %s exists", name); + return name; + } + g_hash_table_insert (priv->layout_desc, + (gpointer) g_strdup (name), + (gpointer) g_strdup (description)); + + return name; +} + +static const gchar * +parse_xkb_xml_variant_configitem_node (IBusXKBConfigRegistryPrivate *priv, + XMLNode *parent_node, + const gchar *layout_name) +{ + XMLNode *node = parent_node; + XMLNode *sub_node; + GList *p; + gchar *name = NULL; + gchar *description = NULL; + gchar *variant_lang_name = NULL; + + g_assert (node != NULL); + g_assert (layout_name != NULL); + for (p = node->sub_nodes; p; p = p->next) { + sub_node = (XMLNode *) p->data; + if (g_strcmp0 (sub_node->name, "name") == 0) { + name = sub_node->text; + continue; + } + if (g_strcmp0 (sub_node->name, "description") == 0) { + description = sub_node->text; + continue; + } + if (g_strcmp0 (sub_node->name, "languageList") == 0) { + if (name == NULL) { + g_warning ("layout name is NULL in node %s", node->name); + continue; + } + variant_lang_name = g_strdup_printf ("%s(%s)", layout_name, name); + parse_xkb_xml_languagelist_node (priv, sub_node, variant_lang_name); + g_free (variant_lang_name); + continue; + } + } + if (name == NULL) { + g_warning ("No name in layout node"); + return NULL; + } + if (g_hash_table_lookup (priv->variant_desc, name) != NULL) { + /* This is an expected case. */ + return name; + } + g_hash_table_insert (priv->variant_desc, + (gpointer) g_strdup (name), + (gpointer) g_strdup (description)); + return name; +} + +static const gchar * +parse_xkb_xml_variant_node (IBusXKBConfigRegistryPrivate *priv, + XMLNode *parent_node, + const gchar *layout_name) +{ + XMLNode *node = parent_node; + XMLNode *sub_node; + GList *p; + const gchar *variant_name = NULL; + + g_assert (node != NULL); + g_assert (layout_name != NULL); + for (p = node->sub_nodes; p; p = p->next) { + sub_node = (XMLNode *) p->data; + if (g_strcmp0 (sub_node->name, "configItem") == 0) { + variant_name = parse_xkb_xml_variant_configitem_node (priv, sub_node, layout_name); + continue; + } + } + return variant_name; +} + +static GList * +parse_xkb_xml_variantlist_node (IBusXKBConfigRegistryPrivate *priv, + XMLNode *parent_node, + const gchar *layout_name, + GList *variant_list) +{ + XMLNode *node = parent_node; + XMLNode *sub_node; + GList *p; + const gchar *variant_name = NULL; + + g_assert (node != NULL); + g_assert (layout_name != NULL); + for (p = node->sub_nodes; p; p = p->next) { + sub_node = (XMLNode *) p->data; + if (g_strcmp0 (sub_node->name, "variant") == 0) { + variant_name = parse_xkb_xml_variant_node (priv, sub_node, layout_name); + if (variant_name != NULL) { + variant_list = g_list_append (variant_list, + (gpointer) g_strdup (variant_name)); + } + continue; + } + } + return variant_list; +} + +static void +parse_xkb_xml_layout_node (IBusXKBConfigRegistryPrivate *priv, + XMLNode *parent_node) +{ + XMLNode *node = parent_node; + XMLNode *sub_node; + GList *p; + const gchar *name = NULL; + GList *variant_list = NULL; + + g_assert (node != NULL); + for (p = node->sub_nodes; p; p = p->next) { + sub_node = (XMLNode *) p->data; + if (g_strcmp0 (sub_node->name, "configItem") == 0) { + name = parse_xkb_xml_configitem_node (priv, sub_node); + continue; + } + if (g_strcmp0 (sub_node->name, "variantList") == 0) { + if (name == NULL) { + g_warning ("layout name is NULL in node %s", node->name); + continue; + } + variant_list = parse_xkb_xml_variantlist_node (priv, sub_node, + name, + variant_list); + continue; + } + } + if (g_hash_table_lookup (priv->layout_list, name) != NULL) { + g_warning ("duplicated name %s exists", name); + return; + } + g_hash_table_insert (priv->layout_list, + (gpointer) g_strdup (name), + (gpointer) variant_list); +} + +static void +parse_xkb_xml_top_node (IBusXKBConfigRegistryPrivate *priv, + XMLNode *parent_node) +{ + XMLNode *node = parent_node; + XMLNode *sub_node; + GList *p; + + g_assert (priv != NULL); + g_assert (node != NULL); + + if (g_strcmp0 (node->name, "xkbConfigRegistry") != 0) { + g_warning ("node has no xkbConfigRegistry name"); + return; + } + for (p = node->sub_nodes; p; p = p->next) { + sub_node = (XMLNode *) p->data; + if (g_strcmp0 (sub_node->name, "layoutList") == 0) { + break; + } + } + if (p == NULL) { + g_warning ("xkbConfigRegistry node has no layoutList node"); + return; + } + node = sub_node; + for (p = node->sub_nodes; p; p = p->next) { + sub_node = (XMLNode *) p->data; + if (g_strcmp0 (sub_node->name, "layout") == 0) { + parse_xkb_xml_layout_node (priv, sub_node); + continue; + } + } +} + +static void +free_lang_list (GList *list) +{ + GList *l = list; + while (l) { + g_free (l->data); + l->data = NULL; + l = l->next; + } + g_list_free (list); +} + +static void +parse_xkb_config_registry_file (IBusXKBConfigRegistryPrivate *priv, + const gchar *file) +{ + XMLNode *node; + + g_assert (file != NULL); + + priv->layout_list = g_hash_table_new_full (g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) free_lang_list); + priv->layout_desc = g_hash_table_new_full (g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + priv->layout_lang = g_hash_table_new_full (g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) free_lang_list); + priv->variant_desc = g_hash_table_new_full (g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + node = ibus_xml_parse_file (file); + parse_xkb_xml_top_node (priv, node); + ibus_xml_free (node); +} + +static GList * +parse_xkblayoutconfig_file (gchar *path) +{ + XMLNode *node = NULL; + XMLNode *sub_node; + XMLNode *sub_sub_node; + GList *p; + GList *retval = NULL; + gchar **array; + int i; + + node = ibus_xml_parse_file (path); + if (node == NULL) { + return NULL; + } + if (g_strcmp0 (node->name, "xkblayout") != 0) { + ibus_xml_free (node); + return NULL; + } + for (p = node->sub_nodes; p != NULL; p = p->next) { + sub_node = (XMLNode *) p->data; + if (g_strcmp0 (sub_node->name, "config") == 0) { + GList *pp; + for (pp = sub_node->sub_nodes; pp != NULL; pp = pp->next) { + sub_sub_node = (XMLNode *) pp->data; + if (g_strcmp0 (sub_sub_node->name, "preload_layouts") == 0) { + if (sub_sub_node->text != NULL) { + array = g_strsplit ((gchar *) sub_sub_node->text, + ",", -1); + for (i = 0; array[i]; i++) { + retval = g_list_append (retval, g_strdup (array[i])); + } + g_strfreev (array); + break; + } + } + } + } + if (retval != NULL) { + break; + } + } + + ibus_xml_free (node); + return retval; +} + +static void +parse_xkb_layout_config (IBusXKBLayoutConfigPrivate *priv) +{ + gchar *basename; + gchar *user_config; + GList *list = NULL; + + g_return_if_fail (priv->system_config_file != NULL); + + basename = g_path_get_basename (priv->system_config_file); + user_config = g_build_filename (g_get_user_config_dir (), + "ibus", "xkb", + basename, NULL); + g_free (basename); + list = parse_xkblayoutconfig_file (user_config); + g_free (user_config); + if (list) { + priv->preload_layouts = list; + return; + } + list = parse_xkblayoutconfig_file (priv->system_config_file); + priv->preload_layouts = list; +} + +static void +ibus_xkb_config_registry_init (IBusXKBConfigRegistry *xkb_config) +{ + IBusXKBConfigRegistryPrivate *priv; + const gchar *file = XKB_RULES_XML_FILE; + + priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config); + parse_xkb_config_registry_file (priv, file); +} + +static void +ibus_xkb_config_registry_destroy (IBusXKBConfigRegistry *xkb_config) +{ + IBusXKBConfigRegistryPrivate *priv; + + g_return_if_fail (xkb_config != NULL); + + priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config); + + g_hash_table_destroy (priv->layout_list); + priv->layout_list = NULL; + g_hash_table_destroy (priv->layout_lang); + priv->layout_lang= NULL; + g_hash_table_destroy (priv->layout_desc); + priv->layout_desc= NULL; + g_hash_table_destroy (priv->variant_desc); + priv->variant_desc = NULL; + + IBUS_OBJECT_CLASS(ibus_xkb_config_registry_parent_class)->destroy (IBUS_OBJECT (xkb_config)); +} + +static void +ibus_xkb_config_registry_class_init (IBusXKBConfigRegistryClass *klass) +{ + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (IBusXKBConfigRegistryPrivate)); + + ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_xkb_config_registry_destroy; +} + +static void +ibus_xkb_layout_config_init (IBusXKBLayoutConfig *xkb_layout_config) +{ + IBusXKBLayoutConfigPrivate *priv; + + priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config); + priv->system_config_file = NULL; + priv->preload_layouts = NULL; +} + +static GObject * +ibus_xkb_layout_config_constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *obj; + IBusXKBLayoutConfig *xkb_layout_config; + IBusXKBLayoutConfigPrivate *priv; + + obj = G_OBJECT_CLASS (ibus_xkb_layout_config_parent_class)->constructor (type, n_construct_params, construct_params); + xkb_layout_config = IBUS_XKB_LAYOUT_CONFIG (obj); + priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config); + parse_xkb_layout_config (priv); + + return obj; +} + +static void +ibus_xkb_layout_config_destroy (IBusXKBLayoutConfig *xkb_layout_config) +{ + IBusXKBLayoutConfigPrivate *priv; + + g_return_if_fail (xkb_layout_config != NULL); + + priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config); + + g_free (priv->system_config_file); + priv->system_config_file = NULL; + free_lang_list (priv->preload_layouts); + priv->preload_layouts = NULL; +} + +static void +ibus_xkb_layout_config_set_property (IBusXKBLayoutConfig *xkb_layout_config, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + IBusXKBLayoutConfigPrivate *priv; + + g_return_if_fail (xkb_layout_config != NULL); + priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config); + + switch (prop_id) { + case PROP_SYSTEM_CONFIG_FILE: + g_assert (priv->system_config_file == NULL); + priv->system_config_file = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (xkb_layout_config, prop_id, pspec); + } +} + +static void +ibus_xkb_layout_config_get_property (IBusXKBLayoutConfig *xkb_layout_config, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + IBusXKBLayoutConfigPrivate *priv; + + g_return_if_fail (xkb_layout_config != NULL); + priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config); + + switch (prop_id) { + case PROP_SYSTEM_CONFIG_FILE: + g_value_set_string (value, priv->system_config_file); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (xkb_layout_config, prop_id, pspec); + + } +} + +static void +ibus_xkb_layout_config_class_init (IBusXKBLayoutConfigClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (IBusXKBLayoutConfigPrivate)); + + gobject_class->constructor = ibus_xkb_layout_config_constructor; + gobject_class->set_property = (GObjectSetPropertyFunc) ibus_xkb_layout_config_set_property; + gobject_class->get_property = (GObjectGetPropertyFunc) ibus_xkb_layout_config_get_property; + ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_xkb_layout_config_destroy; + + /** + * IBusProxy:interface: + * + * The interface of the proxy object. + */ + g_object_class_install_property (gobject_class, + PROP_SYSTEM_CONFIG_FILE, + g_param_spec_string ("system_config_file", + "system_config_file", + "The system file of xkblayoutconfig", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +IBusXKBConfigRegistry * +ibus_xkb_config_registry_new (void) +{ + IBusXKBConfigRegistry *xkb_config; + + xkb_config = IBUS_XKB_CONFIG_REGISTRY (g_object_new (IBUS_TYPE_XKB_CONFIG_REGISTRY, NULL)); + return xkb_config; +} + +#define TABLE_FUNC(field_name) const GHashTable * \ +ibus_xkb_config_registry_get_##field_name (IBusXKBConfigRegistry *xkb_config) \ +{ \ + IBusXKBConfigRegistryPrivate *priv; \ + \ + g_return_val_if_fail (xkb_config != NULL, NULL); \ + priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config); \ + return priv->field_name; \ +} + +TABLE_FUNC (layout_list) +TABLE_FUNC (layout_lang) +TABLE_FUNC (layout_desc) +TABLE_FUNC (variant_desc) + +#undef TABLE_FUNC + +IBusComponent * +ibus_xkb_component_new (void) +{ + IBusComponent *component; + + component = ibus_component_new ("org.freedesktop.IBus.XKB", + "XKB Component", + VERSION, + "LGPL2.1", + "Takao Fujiwara ", + "http://code.google.com/p/ibus/", + LIBEXECDIR "/ibus-engine-xkb --ibus", + GETTEXT_PACKAGE); + + return component; +} + +IBusEngineDesc * +ibus_xkb_engine_desc_new (const gchar *lang, + const gchar *layout, + const gchar *layout_desc, + const gchar *variant, + const gchar *variant_desc) +{ + IBusEngineDesc *engine; + gchar *name = NULL; + gchar *longname = NULL; + gchar *desc = NULL; + gchar *engine_layout = NULL; + + g_return_val_if_fail (lang != NULL && layout != NULL, NULL); + + if (layout_desc && variant_desc) { + longname = g_strdup_printf ("%s - %s", layout_desc, variant_desc); + } else if (layout && variant) { + longname = g_strdup_printf ("%s - %s", layout, variant); + } else if (layout_desc) { + longname = g_strdup (layout_desc); + } else { + longname = g_strdup (layout); + } + if (variant) { + name = g_strdup_printf ("xkb:layout:%s:%s", layout, variant); + desc = g_strdup_printf ("XKB %s(%s) keyboard layout", layout, variant); + engine_layout = g_strdup_printf ("%s(%s)", layout, variant); + } else { + name = g_strdup_printf ("xkb:layout:%s", layout); + desc = g_strdup_printf ("XKB %s keyboard layout", layout); + engine_layout = g_strdup (layout); + } + + engine = ibus_engine_desc_new (name, + longname, + desc, + lang, + "LGPL2.1", + "Takao Fujiwara ", + "ibus-engine", + engine_layout); + + g_free (name); + g_free (longname); + g_free (desc); + g_free (engine_layout); + + return engine; +} + +IBusXKBLayoutConfig * +ibus_xkb_layout_config_new (const gchar *system_config_file) +{ + IBusXKBLayoutConfig *xkb_layout_config; + + xkb_layout_config = IBUS_XKB_LAYOUT_CONFIG (g_object_new (IBUS_TYPE_XKB_LAYOUT_CONFIG, + "system_config_file", + system_config_file, + NULL)); + return xkb_layout_config; +} + +const GList * +ibus_xkb_layout_config_get_preload_layouts (IBusXKBLayoutConfig *xkb_layout_config) +{ + IBusXKBLayoutConfigPrivate *priv; + + g_return_val_if_fail (xkb_layout_config != NULL, NULL); + priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config); + return (const GList *) priv->preload_layouts; +} diff --git a/xkb/xkbxml.h b/xkb/xkbxml.h new file mode 100644 index 0000000..f4858fa --- /dev/null +++ b/xkb/xkbxml.h @@ -0,0 +1,189 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2010 Takao Fujiwara + * Copyright (C) 2008-2010 Peng Huang + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __IBUS_XKB_H_ +#define __IBUS_XKB_H_ + +#include "ibus.h" + +/* + * Type macros. + */ +/* define IBusXKBConfigRegistry macros */ +#define IBUS_TYPE_XKB_CONFIG_REGISTRY \ + (ibus_xkb_config_registry_get_type ()) +#define IBUS_XKB_CONFIG_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistry)) +#define IBUS_XKB_CONFIG_REGISTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass)) +#define IBUS_IS_XKB_CONFIG_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY)) +#define IBUS_IS_XKB_CONFIG_REGISTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY)) +#define IBUS_XKB_CONFIG_REGISTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass)) + +/* define IBusXKBLayoutConfig macros */ +#define IBUS_TYPE_XKB_LAYOUT_CONFIG \ + (ibus_xkb_layout_config_get_type ()) +#define IBUS_XKB_LAYOUT_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_XKB_LAYOUT_CONFIG, IBusXKBLayoutConfig)) +#define IBUS_XKB_LAYOUT_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_XKB_LAYOUT_CONFIG, IBusXKBLayoutConfigClass)) +#define IBUS_IS_XKB_LAYOUT_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_XKB_LAYOUT_CONFIG)) +#define IBUS_IS_XKB_LAYOUT_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_XKB_LAYOUT_CONFIG)) +#define IBUS_XKB_LAYOUT_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_XKB_LAYOUT_CONFIG, IBusXKBLayoutConfigClass)) + +G_BEGIN_DECLS + +typedef struct _IBusXKBConfigRegistry IBusXKBConfigRegistry; +typedef struct _IBusXKBConfigRegistryClass IBusXKBConfigRegistryClass; +typedef struct _IBusXKBLayoutConfig IBusXKBLayoutConfig; +typedef struct _IBusXKBLayoutConfigClass IBusXKBLayoutConfigClass; + +struct _IBusXKBConfigRegistry { + IBusObject parent; +}; + +struct _IBusXKBConfigRegistryClass { + IBusObjectClass parent; + /* signals */ + /*< private >*/ + /* padding */ + gpointer pdummy[8]; +}; + +struct _IBusXKBLayoutConfig { + IBusObject parent; +}; + +struct _IBusXKBLayoutConfigClass { + IBusObjectClass parent; + /* signals */ + /*< private >*/ + /* padding */ + gpointer pdummy[8]; +}; + + +GType ibus_xkb_config_registry_get_type + (void); +/** + * ibus_xkb_config_registry_new: + * @returns: A newly allocated IBusXKBConfigRegistry + * + * New an IBusXKBConfigRegistry. + */ +IBusXKBConfigRegistry * + ibus_xkb_config_registry_new + (void); +/** + * ibus_xkb_config_registry_get_layout_list: + * @xkb_config: An IBusXKBConfigRegistry. + * @returns: A const GHashTable + * + * a const GHashTable + */ +const GHashTable * + ibus_xkb_config_registry_get_layout_list + (IBusXKBConfigRegistry *xkb_config); +/** + * ibus_xkb_config_registry_get_layout_lang: + * @xkb_config: An IBusXKBConfigRegistry. + * @returns: A const GHashTable + * + * a const GHashTable + */ +const GHashTable * + ibus_xkb_config_registry_get_layout_lang + (IBusXKBConfigRegistry *xkb_config); +/** + * ibus_xkb_config_registry_get_layout_desc: + * @xkb_config: An IBusXKBConfigRegistry. + * @returns: A const GHashTable + * + * a const GHashTable + */ +const GHashTable * + ibus_xkb_config_registry_get_layout_desc + (IBusXKBConfigRegistry *xkb_config); +/** + * ibus_xkb_config_registry_get_variant_desc: + * @xkb_config: An IBusXKBConfigRegistry. + * @returns: A const GHashTable + * + * a const GHashTable + */ +const GHashTable * + ibus_xkb_config_registry_get_variant_desc + (IBusXKBConfigRegistry *xkb_config); +/** + * ibus_xkb_component_new: + * @returns: A newly allocated IBusComponent. + * + * New an IBusComponent. + */ +IBusComponent *ibus_xkb_component_new (void); + +/** + * ibus_xkb_engine_desc_new: + * @lang: Language (e.g. zh, jp) supported by this input method engine. + * @layout: Keyboard layout + * @layout_desc: Keyboard layout description for engine description + * @variant: Keyboard variant + * @variant_desc: Keyboard variant description for engine description + * @returns: A newly allocated IBusEngineDesc. + * + * New a IBusEngineDesc. + */ +IBusEngineDesc *ibus_xkb_engine_desc_new (const gchar *lang, + const gchar *layout, + const gchar *layout_desc, + const gchar *variant, + const gchar *variant_desc); + +GType ibus_xkb_layout_config_get_type (void); + +/** + * ibus_xkb_layout_config_new: + * @returns: A newly allocated IBusXKBLayoutConfig + * + * New an IBusXKBLayoutConfig + */ +IBusXKBLayoutConfig * + ibus_xkb_layout_config_new (const gchar *system_config_file); + +/** + * ibus_xkb_layout_config_get_preload_layouts: + * @xkb_layout_config: An IBusXKBLayoutConfig. + * @returns: A const GList + * + * a const GList + */ +const GList * ibus_xkb_layout_config_get_preload_layouts + (IBusXKBLayoutConfig *xkb_layout_config); + +G_END_DECLS +#endif -- 1.7.4.4