# vim:set et sts=4 sw=4 fileencoding=utf-8:
#
# ibus-hangul - The Hangul Engine For IBus
#
# Copyright (c) 2009-2011 Choe Hwanjin <choe.hwanjin@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import sys
import os

from os import environ, getuid, path
import locale
import gettext
import subprocess
import xml.dom.minidom

from gi.repository import GLib

# set_prgname before importing other modules to show the name in warning
# messages when import modules are failed. E.g. Gtk.
GLib.set_prgname('ibus-setup-hangul')

from gi.repository import Gtk
from gi.repository import Gdk
#from gi.repository import GdkPixbuf
from gi.repository import GdkX11
#from gi.repository import Pango
from gi.repository import IBus
import locale
import gettext

import config
from keycapturedialog import KeyCaptureDialog
from aboutdialog import AboutDialog
from layoutdialog import LayoutDialog
from autotextdialog import AutotextDialog

_ = lambda a : gettext.dgettext(config.gettext_package, a)
N_ = lambda a: a


def get_hangul_keyboard_list():
    from ctypes import CDLL, c_int, c_char_p
    libhangul = CDLL('libhangul.so.1')
    libhangul.hangul_ic_get_n_keyboards.argtypes = []
    libhangul.hangul_ic_get_n_keyboards.restype = c_int
    libhangul.hangul_ic_get_keyboard_id.argtypes = [c_int]
    libhangul.hangul_ic_get_keyboard_id.restype = c_char_p
    libhangul.hangul_ic_get_keyboard_name.argtypes = [c_int]
    libhangul.hangul_ic_get_keyboard_name.restype = c_char_p

    n = libhangul.hangul_ic_get_n_keyboards()
    list = []
    for i in range(n):
        id = libhangul.hangul_ic_get_keyboard_id(i).decode('UTF-8')
        name = libhangul.hangul_ic_get_keyboard_name(i).decode('UTF-8')
        list.append((id, name))
    return list


class HangulSetup (object):
    def __init__(self):
        # Python's locale module doesn't provide all methods on some
        # operating systems like FreeBSD
        try:
            locale.bindtextdomain(config.gettext_package, config.localedir)
            locale.bind_textdomain_codeset(config.gettext_package, 'UTF-8')
        except AttributeError:
            pass
        gettext.bindtextdomain(config.gettext_package, config.localedir)
        gettext.bind_textdomain_codeset(config.gettext_package, 'UTF-8')

        # IBus.Bus() calls ibus_bus_new().
        # Gtk.Builder().add_from_file() also calls ibus_bus_new_async()
        # via ibus_im_context_new().
        # Then if IBus.Bus() is called after Gtk.Builder().add_from_file(),
        # the connection delay would be happened without an async
        # finish function.
        ibus_address = IBus.get_address()
        bus = None
        if ibus_address != None:
            bus = IBus.Bus(connect_async='True')

        ui_file = os.path.join(os.path.dirname(__file__), "setup.ui")
        self.__builder = Gtk.Builder()
        self.__builder.set_translation_domain(config.gettext_package)
        self.__builder.add_from_file(ui_file)

        # setup dialog
        self.__window = self.__builder.get_object("SetupDialog")

        parent_xid = 0
        parent_wmname = None
        parent_wmclass = None

        try:
            #gnome_control_center_xid = int(environ['GNOME_CONTROL_CENTER_XID'])
            parent_xid = int(environ['IBUS_SETUP_XID'])
            if parent_xid != 0:
                parent_wmname = 'ibus-setup'
                parent_wmclass = 'Ibus-setup'
        except:
            #gnome_control_center_xid = 0
            pass

        try:
            if parent_xid == 0:
                parent_xid = int(environ['GNOME_CONTROL_CENTER_XID'])
                if parent_xid != 0:
                    parent_wmname = 'gnome-conrol-center'
                    parent_wmclass = 'Gnome-conrol-center'
        except:
            pass

        #if gnome_control_center_xid != 0:
        if parent_xid != 0:
            def set_transient(obj, pspec):
                window = self.__window.get_window()
                if window == None:
                    return
                parent_window = GdkX11.X11Window.foreign_new_for_display(
                                                    Gdk.Display.get_default(),
                                                    #gnome_control_center_xid)
                                                    parent_xid)
                if parent_window != None:
                    window.set_transient_for(parent_window)
            #self.__window.set_wmclass('gnome-control-center', 'Gnome-control-center')
            self.__window.set_wmclass(parent_wmname, parent_wmclass)
            self.__window.set_modal(True)
            self.__window.set_type_hint(Gdk.WindowTypeHint.DIALOG)
            self.__window.connect('notify::window', set_transient)

        self.__window.show()

        if ibus_address == None:
            self.__builder.connect_signals(self)
            # self.__run_message_dialog needs self.__builder.
            message = _("IBus daemon is not running.\n"
                                "Hangul engine settings cannot be saved.")
            self.__run_message_dialog(message, None, Gtk.MessageType.ERROR)
            return

        if bus.is_connected():
            self.__init_bus_connected(bus)
        else:
            bus.connect('connected', self.__init_bus_connected)

    def __init_bus_connected (self, bus):
        self.__bus = bus
        self.__config = self.__bus.get_config()
        self.__config.connect("value-changed", self.on_value_changed, None)
        self.mask = 0

        # setup dialog icon
        # glade 'icon_name' property has a custom scaling and it seems
        # to be difficult to show the complicated small icon in metacity.
        # This can add the pixbuf without scaling.
        icon_file = path.join(config.pkgdatadir, "icons", "ibus-hangul.svg")
        if path.exists(icon_file):
            self.__window.set_icon_from_file(icon_file)
        else:
            icon_file = 'ibus-hangul'
            self.__window.set_icon_from_file(icon_file)

        self.__window.connect("response", self.on_response, None)

        # General tab
        self.general_tab()

        # hangul tab
        self.hangul_tab()

        # hanja tab
        self.hanja_tab()

        # vi tab
        self.vi_tab()

        # dictionary tab
        self.dictionary_tab()

        # autotext tab
        self.autotext_tab()

        # advanced tab
        self.advanced_tab()

        ok_button = self.__builder.get_object("button_cancel")
        ok_button.grab_focus()

    # page 0
    def general_tab(self):
        # hangul
        self.__hangul_keyboard_select = self.__builder.get_object("HangulSelectButton")
        self.__hangul_keyboard_select.connect("clicked", 
                                                                    self.on_keyboard_layout_select_clicked, 
                                                                    'keyboard_hangul')

        default = GLib.Variant.new_string(
                            config.default_value['engine/hangul']['keyboard_hangul'])
        current = self.__read(
                            config.GSettings['engine/hangul']['keyboard_hangul'], default)

        self.__hangul_keyboard_entry = self.__builder.get_object("HangulSelectEntry")

        #self.__hangul_keyboard_list = get_hangul_keyboard_list()
        pipe = subprocess.Popen([config.setuplibdir + '/hangul_keyboard_list'], 
                                                stdout = subprocess.PIPE)
        (stdoutdata, stderrdata) = pipe.communicate()
        # Python 3.0 uses the concepts of text and (binary) data instead of 
        #                                                       Unicode strings and 8-bit strings.
        if sys.version.startswith('3'):
            if type(stdoutdata) == bytes:
                stdoutdata = stdoutdata.decode(encoding='UTF-8')
        pipe_line_list = stdoutdata.split('\n')
        self.__hangul_keyboard_list = []
        for line in pipe_line_list:
            items = line.split('\t')
            if len(items) > 1:
                self.__hangul_keyboard_list.append((items[0], items[1])) # (key, name)

        self.hangul_key = current.get_string()
        for (key, name) in self.__hangul_keyboard_list:
            if key == self.hangul_key:
                self.__hangul_keyboard_entry.set_text(name)
                self.__hangul_keyboard_entry.set_tooltip_text(key)
                break

        # english
        self.__english_keyboard_select = self.__builder.get_object("EnglishSelectButton")
        self.__english_keyboard_select.connect("clicked", 
                                                                    self.on_keyboard_layout_select_clicked, 
                                                                    'keyboard_english')

        default = GLib.Variant.new_string(
                            config.default_value['engine/hangul']['keyboard_english'])
        current = self.__read(
                            config.GSettings['engine/hangul']['keyboard_english'], default)

        self.__english_keyboard_entry = self.__builder.get_object("EnglishSelectEntry")

        self.__english_keyboard_list = config.english_keyboard_list

        self.english_key = current.get_string()
        for (key, name) in self.__english_keyboard_list:
            if key == self.english_key:
                self.__english_keyboard_entry.set_text(_(name))
                self.__english_keyboard_entry.set_tooltip_text(key)
                break

        #language
        default = GLib.Variant.new_string(
                            config.default_value['engine/hangul']['keyboard_language'])
        current = self.__read(
                            config.GSettings['engine/hangul']['keyboard_language'], default)

        self.__initial_language = self.__builder.get_object("InitialLanguage")

        model = self.__initial_language.get_model()
        if model == None:
            model = Gtk.ListStore(str, str, int)
            for (name, key, index) in config.initial_language_list:
                model.append([_(name), key, index])

            self.__initial_language.set_model(model)
            renderer = Gtk.CellRendererText()
            self.__initial_language.pack_start(renderer, True)
            self.__initial_language.add_attribute(renderer, 
                                        "text", 
                                        config.list_model['keyboard_language']['name'])

        initial_language = current.get_string()
        for row in model:
            if row[config.list_model['keyboard_language']['id']] == initial_language:
                self.__initial_language.set_active(
                            row[config.list_model['keyboard_language']['index']])
                break

        self.__initial_language.connect('changed', 
                                        self.apply_sensitive_toggle, 
                                        'keyboard_language')

        #base xkb layout
        default = GLib.Variant.new_string(
                            config.default_value['engine/hangul']['keyboard_xkb'])
        current = self.__read(
                            config.GSettings['engine/hangul']['keyboard_xkb'], default)

        self.__xkb_layout = self.__builder.get_object("XkbLayout")

        model = self.__xkb_layout.get_model()
        if model == None:
            model = Gtk.ListStore(str, str, str, str, int)
            for (name, key, xkb, longname, index) in config.xkb_layout_list:
                model.append([_(name), key, xkb, longname, index])

            self.__xkb_layout.set_model(model)
            renderer = Gtk.CellRendererText()
            self.__xkb_layout.pack_start(renderer, True)
            self.__xkb_layout.add_attribute(renderer, 
                                        "text", 
                                        config.list_model['keyboard_xkb']['name'])

        xkb_layout = current.get_string()
        for row in model:
            if row[config.list_model['keyboard_xkb']['id']] == xkb_layout:
                self.__xkb_layout.set_active(
                                                row[config.list_model['keyboard_xkb']['index']])
                break

        self.__english_keyboard_entry.set_sensitive(xkb_layout == "hangul:us")
        self.__english_keyboard_select.set_sensitive(xkb_layout == "hangul:us")

        self.__update_engine_combo_list()

        self.__xkb_layout.connect('changed', 
                                        self.keyboard_xkb_layout_sensitive, 
                                        'keyboard_xkb')

        # flush interval time
        self.__flush_time_select = self.__builder.get_object("IntervalTime")

        default = GLib.Variant.new_uint32(
                            config.default_value['engine/hangul']['preedit_flush_time'])
        current = self.__read(
                            config.GSettings['engine/hangul']['preedit_flush_time'], default)

        flush_time = current.get_uint32()
        if flush_time < 100 or flush_time > 5000:
            flush_time = default.get_uint32()
        self.__flush_time_select.set_range(10, 5000)
        self.__flush_time_select.set_increments(10, 100)
        self.__flush_time_select.set_value(flush_time)
        self.__flush_time_select.set_tooltip_text(_("Primary button: 10 steps\n"
                                                                                "Middle button: 100 steps\n"
                                                                                "Minimum: 10\n"
                                                                                "Maximum: 5000"))

        self.__flush_time_select.connect("value-changed", 
                                                                    self.apply_sensitive_toggle, 
                                                                    'preedit_flush_time')
        # enable auto flush
        default = GLib.Variant.new_boolean(
                            config.default_value['engine/hangul']['enable_preedit_flush'])
        enable_flush_time = self.__read(
                            config.GSettings['engine/hangul']['enable_preedit_flush'], default)

        self.__enable_flush_time = self.__builder.get_object("IntervalTimeEnable")
        self.__enable_flush_time.set_active(enable_flush_time.get_boolean())

        self.__enable_flush_time.connect('toggled', 
                                        self.flush_use_sensitive, 
                                        'enable_preedit_flush')
        if enable_flush_time.get_boolean() == False:
            self.__builder.get_object('IntervalTimeLabel').set_sensitive(False)
            self.__builder.get_object('IntervalTimeSpinBox').set_sensitive(False)

    # page 1
    def hangul_tab(self):
        button = self.__builder.get_object("HangulKeyListAddButton")
        button.connect("clicked", 
                                        self.on_hotkey_add, 
                                        'keys_hangul')

        button = self.__builder.get_object("HangulKeyListRemoveButton")
        button.connect("clicked", 
                                        self.on_hotkey_remove, 
                                        'keys_hangul')

        default = GLib.Variant.new_strv(
                            config.default_value['engine/hangul']['keys_hangul'])
        keylist_str = self.__read(
                            config.GSettings['engine/hangul']['keys_hangul'], default)

        self.__hangul_key_list = self.__builder.get_object("HangulKeyList")

        model = self.__hangul_key_list.get_model()
        if model == None:
            model = Gtk.ListStore(str, int)

            self.__hangul_key_list.set_model(model)
            #self.__hanja_key_list.set_headers_visible(True)    #setup.ui

            column = Gtk.TreeViewColumn(_("Hangul _keys"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, 
                                        "text", 
                                        config.list_model['keys_hangul']['shortcut'])
            self.__hangul_key_list.append_column(column)

            self.__hangul_key_list_strv = keylist_str.get_strv()
            if self.__hangul_key_list_strv[0]:
                for shortcut in self.__hangul_key_list_strv:
                    model.append([shortcut,
                                    self.__hangul_key_list_strv.index(shortcut)])

    # page 2
    def hanja_tab(self):
        button = self.__builder.get_object("HanjaKeyListAddButton")
        button.connect("clicked", 
                                        self.on_hotkey_add, 
                                        'keys_hanja')

        button = self.__builder.get_object("HanjaKeyListRemoveButton")
        button.connect("clicked", 
                                        self.on_hotkey_remove, 
                                        'keys_hanja')

        default = GLib.Variant.new_strv(
                                config.default_value['engine/hangul']['keys_hanja'])
        keylist_str = self.__read(
                                config.GSettings['engine/hangul']['keys_hanja'], default)

        self.__hanja_key_list = self.__builder.get_object("HanjaKeyList")
        model = self.__hanja_key_list.get_model()
        if model == None:
            model = Gtk.ListStore(str, int)
            self.__hanja_key_list.set_model(model)
            self.__hanja_key_list.set_headers_visible(True)

            column = Gtk.TreeViewColumn(_("Hanja key_s"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, 
                                        "text", 
                                        config.list_model['keys_hanja']['shortcut'])
            self.__hanja_key_list.append_column(column)

            self.__hanja_key_list_strv = keylist_str.get_strv()
            if self.__hanja_key_list_strv[0]:
                for shortcut in self.__hanja_key_list_strv:
                    model.append([shortcut,
                                    self.__hanja_key_list_strv.index(shortcut)])

    # page 3
    def vi_tab(self):
        button = self.__builder.get_object("OnKeyListAddButton")
        button.connect("clicked", 
                                        self.on_hotkey_add, 
                                        'keys_on')
        button = self.__builder.get_object("OnKeyListRemoveButton")
        button.connect("clicked", 
                                        self.on_hotkey_remove, 
                                        'keys_on')

        button = self.__builder.get_object("OffKeyListAddButton")
        button.connect("clicked", 
                                        self.on_hotkey_add, 
                                        'keys_off')
        button = self.__builder.get_object("OffKeyListRemoveButton")
        button.connect("clicked", 
                                        self.on_hotkey_remove, 
                                        'keys_off')

        ## enable vi key ##
        default = GLib.Variant.new_boolean(
                            config.default_value['engine/hangul']['enable_keys_vieditor'])
        enable_vi_editor = self.__read(
                            config.GSettings['engine/hangul']['enable_keys_vieditor'], default)

        self.__enable_vi_editor = self.__builder.get_object("VieditorKeysEnable")
        self.__enable_vi_editor.set_active(enable_vi_editor.get_boolean())

        self.__enable_vi_editor.connect('toggled', 
                                        self.vi_editor_keys_sensitive, 
                                        'enable_keys_vieditor')

        self.__vi_key_box = self.__builder.get_object("ViKeyBox")
        self.__vi_key_box.set_sensitive(self.__enable_vi_editor.get_active())

        ## on keys ##
        default = GLib.Variant.new_strv(
                            config.default_value['engine/hangul']['keys_on'])
        keylist_str = self.__read(
                            config.GSettings['engine/hangul']['keys_on'], default)

        self.__on_key_list = self.__builder.get_object("OnKeyList")

        model = self.__on_key_list.get_model()
        if model == None:
            model = Gtk.ListStore(str, int)

            self.__on_key_list.set_model(model)
            #self.__on_key_list.set_headers_visible(True)    #setup.ui

            column = Gtk.TreeViewColumn(_("On _keys"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, 
                                        "text", 
                                        config.list_model['keys_on']['shortcut'])
            self.__on_key_list.append_column(column)

            self.__on_key_list_strv = keylist_str.get_strv()
            if self.__on_key_list_strv != []:## 기본값이 없다
                if self.__on_key_list_strv[0]:
                    for shortcut in self.__on_key_list_strv:
                        model.append([shortcut,
                                            self.__on_key_list_strv.index(shortcut)])

        ## off keys ##
        default = GLib.Variant.new_strv(
                            config.default_value['engine/hangul']['keys_off'])
        keylist_str = self.__read(
                            config.GSettings['engine/hangul']['keys_off'], default)

        self.__off_key_list = self.__builder.get_object("OffKeyList")

        model = self.__off_key_list.get_model()
        if model == None:
            model = Gtk.ListStore(str, int)

            self.__off_key_list.set_model(model)
            #self.__off_key_list.set_headers_visible(True)    #setup.ui

            column = Gtk.TreeViewColumn(_("Off key_s"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, 
                                        "text", 
                                        config.list_model['keys_off']['shortcut'])
            self.__off_key_list.append_column(column)

            self.__off_key_list_strv = keylist_str.get_strv()
            if self.__off_key_list_strv[0]:
                for shortcut in self.__off_key_list_strv:
                    model.append([shortcut,
                                        self.__off_key_list_strv.index(shortcut)])

    # page 4
    def dictionary_tab(self):
        button = self.__builder.get_object("DictionaryListDefaultButton")
        button.connect("clicked", 
                                        self.on_dictionary_default_clicked,
                                        'dictionary')

        button = self.__builder.get_object("DictionaryListViewButton")
        button.connect("clicked", self.on_dictionary_view_clicked)

        button = self.__builder.get_object("DictionaryListEditButton")
        button.connect("clicked", 
                                        self.on_dictionary_edit_clicked,
                                        'dictionary')

        self.__dictionary_list_scrolled = self.__builder.get_object("DictionaryListScrolledWindow")
        self.__dictionary_list_scrolled.set_min_content_height(115)

        self.__dictionary_list = self.__builder.get_object("DictionaryList")
        self.__dictionary_list.connect("button-press-event", 
                                        self.on_dictionary_click_event,
                                        'dictionary')
        self.__dictionary_list.connect("button-release-event", 
                                        self.on_dictionary_click_event,
                                        'dictionary')
        self.__dictionary_list.connect("key-release-event", 
                                        self.on_dictionary_key_release_event,
                                        'dictionary')

        # symbol
        default_dict = GLib.Variant.new_string(
                            config.default_value['engine/hangul']['dictionary_symbol'])
        symbol_dict = self.__read(
                            config.GSettings['engine/hangul']['dictionary_symbol'], default_dict)

        if symbol_dict != default_dict:
            if not path.isfile(symbol_dict.get_string()):
                self.__run_message_dialog(
                                _("The Symbol dictionary file does not exist:\n") +\
                                symbol_dict.get_string(),
                                None,
                                Gtk.MessageType.ERROR)
                symbol_dict = default_dict
                self.apply_sensitive_toggle (None, 'dictionary')

        # hanja
        default_dict = GLib.Variant.new_string(
                            config.default_value['engine/hangul']['dictionary_hanja'])
        hanja_dict = self.__read(
                            config.GSettings['engine/hangul']['dictionary_hanja'], default_dict)

        if hanja_dict != default_dict:
            if not path.isfile(hanja_dict.get_string()):
                self.__run_message_dialog(
                                _("The Hanja dictionary file does not exist:\n") +\
                                hanja_dict.get_string(),
                                None,
                                Gtk.MessageType.ERROR)
                hanja_dict = default_dict
                self.apply_sensitive_toggle (None, 'dictionary')

        # emoticon
        default_dict = GLib.Variant.new_string(
                            config.default_value['engine/hangul']['dictionary_emoticon'])
        emoticon_dict = self.__read(
                            config.GSettings['engine/hangul']['dictionary_emoticon'], default_dict)

        if emoticon_dict != default_dict:
            if not path.isfile(emoticon_dict.get_string()):
                self.__run_message_dialog(
                                _("The Emoticon dictionary file does not exist:\n") +\
                                emoticon_dict.get_string(),
                                None,
                                Gtk.MessageType.ERROR)
                emoticon_dict = default_dict
                self.apply_sensitive_toggle (None, 'dictionary')

        model = self.__dictionary_list.get_model()
        if model == None:
            model = Gtk.ListStore(str, str, str, str)  #type, type, filename, filepath
            model.append([_(config.dict_type['dictionary_symbol']['name']), 
                                config.dict_type['dictionary_symbol']['id'], 
                                path.basename(symbol_dict.get_string()), 
                                symbol_dict.get_string()])
            model.append([_(config.dict_type['dictionary_hanja']['name']), 
                                config.dict_type['dictionary_hanja']['id'], 
                                path.basename(hanja_dict.get_string()), 
                                hanja_dict.get_string()])
            model.append([_(config.dict_type['dictionary_emoticon']['name']), 
                                config.dict_type['dictionary_emoticon']['id'], 
                                path.basename(emoticon_dict.get_string()), 
                                emoticon_dict.get_string()])

            self.__dictionary_list.set_model(model)
            #self.__dictionary_list.set_headers_visible(True)   #setup.ui

            column = Gtk.TreeViewColumn(_("Key"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, 
                                        "text", 
                                        config.list_model['dictionary']['id'])
            column.set_visible(False)
            self.__dictionary_list.append_column(column)

            column = Gtk.TreeViewColumn(_("Diction_ary"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, 
                                        "text", 
                                        config.list_model['dictionary']['name'])
            self.__dictionary_list.append_column(column)

            column = Gtk.TreeViewColumn(_("Filename"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, 
                                        "text", 
                                        config.list_model['dictionary']['filename'])
            self.__dictionary_list.append_column(column)

            column = Gtk.TreeViewColumn(_("Filepath"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, 
                                        "text", 
                                        config.list_model['dictionary']['filepath'])
            column.set_visible(False)
            self.__dictionary_list.append_column(column)

        selected_dictionary = self.__dictionary_list.get_selection()
        selected_dictionary.connect_after('changed', 
                                        self.on_dictionary_selection_changed, 
                                        0)

    # page 5
    def autotext_tab(self):
        # autotext on
        default = GLib.Variant.new_boolean(
                            config.default_value['engine/hangul']['enable_mode_autotext'])
        enable_autotext_mode = self.__read(
                            config.GSettings['engine/hangul']['enable_mode_autotext'], default)

        self.__enable_autotext_mode = self.__builder.get_object("AutotextEnable")
        self.__enable_autotext_mode.set_active(enable_autotext_mode.get_boolean())

        self.__enable_autotext_mode.connect('toggled', 
                                        self.autotext_mode_sensitive, 
                                        'enable_mode_autotext')

        # autotext key
        self.__autotext_mode_key_select = self.__builder.get_object("AutotextKeyEntryButton")
        self.__autotext_mode_key_select.connect("clicked", 
                                                                    self.on_autotext_mode_key_select, 
                                                                    'keys-mode-autotext')

        default = GLib.Variant.new_int32(
                            config.default_value['engine/hangul']['keys-mode-autotext'])
        autotext_key_val = self.__read(
                            config.GSettings['engine/hangul']['keys-mode-autotext'], default)

        autotext_key_name = Gdk.keyval_name(autotext_key_val.get_int32())
        self.__autotext_mode_key_entry = self.__builder.get_object("AutotextKeyEntry")
        self.__autotext_mode_key_entry.set_text(autotext_key_name)
        self.__autotext_mode_key_entry.set_editable(False)

        self.__autotext_mode_key_entry.connect('changed', 
                                        self.apply_sensitive_toggle, 
                                        'keys-mode-autotext')

        # autotext tree
        self.__autotext_treeview = self.__builder.get_object("AutotextTreeView")
        self.__autotext_treeview.set_headers_visible(True)

        model = self.__autotext_treeview.get_model()
        if model == None:
            model = Gtk.ListStore(str, str, str)  #key, autotext, comment
            autotext_list = self.__get_autotext_list()
            try:
                for (key, autotext, comment) in autotext_list:
                    model.append([key, autotext, comment])
            except TypeError:
                pass
            self.__autotext_treeview.set_model(model)
            #self.__dictionary_list.set_headers_visible(True)   #setup.ui

            column = Gtk.TreeViewColumn(_("Key"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, "text", 0)
            self.__autotext_treeview.append_column(column)

            column = Gtk.TreeViewColumn(_("Autotext"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, "text", 1)
            self.__autotext_treeview.append_column(column)

            column = Gtk.TreeViewColumn(_("Comment"))
            renderer = Gtk.CellRendererText()
            column.pack_start(renderer, True)
            column.add_attribute(renderer, "text", 2)
            #column.set_visible(False)
            self.__autotext_treeview.append_column(column)

        # autotext button
        button = self.__builder.get_object("AutotextAddButton")
        button.connect("clicked", 
                                    self.on_autotext_add,
                                    'keys-mode-autotext')
        button = self.__builder.get_object("AutotextRemoveButton")
        button.connect("clicked", 
                                    self.on_autotext_remove,
                                    'keys-mode-autotext')

        self.__autotext_tree_changed = False
        if enable_autotext_mode.get_boolean() == False:
            self.__builder.get_object('AutotextKeyBox').set_sensitive(False)
            self.__builder.get_object('AutotextTreeViewBox').set_sensitive(False)

    # page 6
    def advanced_tab(self):
        # word commit
        default = GLib.Variant.new_boolean(
                            config.default_value['engine/hangul']['preedit_word_commit'])
        word_commit = self.__read(
                            config.GSettings['engine/hangul']['preedit_word_commit'], default)

        self.__word_commit = self.__builder.get_object("WordCommit")
        self.__word_commit.set_active(word_commit.get_boolean())

        self.__word_commit.connect('toggled', 
                                        self.apply_sensitive_toggle, 
                                        'preedit_word_commit')

        # auto reorder
        default = GLib.Variant.new_boolean(
                            config.default_value['engine/hangul']['preedit_auto_reorder'])
        auto_reorder = self.__read(
                            config.GSettings['engine/hangul']['preedit_auto_reorder'], default)

        self.__auto_reorder = self.__builder.get_object("AutoReorder")
        self.__auto_reorder.set_active(auto_reorder.get_boolean())

        self.__auto_reorder.connect('toggled', 
                                        self.apply_sensitive_toggle, 
                                        'preedit_auto_reorder')

        # disable latin mode
        default = GLib.Variant.new_boolean(
                            config.default_value['engine/hangul']['disable_latin_mode'])
        disable_latin_mode = self.__read(
                            config.GSettings['engine/hangul']['disable_latin_mode'], default)

        self.__disable_latin_mode = self.__builder.get_object("DisableLatin")
        self.__disable_latin_mode.set_active(disable_latin_mode.get_boolean())

        self.__disable_latin_mode.connect('toggled', 
                                        self.apply_sensitive_toggle, 
                                        'disable_latin_mode')
                                        
        # notebook 104 keyboard
        default = GLib.Variant.new_boolean(
                            config.default_value['engine/hangul']['enable_104_keyboard'])
        enable_104_keys = self.__read(
                            config.GSettings['engine/hangul']['enable_104_keyboard'], default)

        self.__enable_104_keys = self.__builder.get_object("Keyboard104Keys")
        self.__enable_104_keys.set_active(enable_104_keys.get_boolean())

        self.__enable_104_keys.connect('toggled', 
                                        self.apply_sensitive_toggle, 
                                        'enable_104_keyboard')

        # extended layout
        default = GLib.Variant.new_boolean(
                            config.default_value['engine/hangul']['enable_extended_layout'])
        enable_layout_extended = self.__read(
                            config.GSettings['engine/hangul']['enable_extended_layout'], default)

        self.__enable_layout_extended = self.__builder.get_object("ExtensionFeatureEnable")
        self.__enable_layout_extended.set_active(enable_layout_extended.get_boolean())

        self.__enable_layout_extended.connect('toggled', 
                                        self.apply_sensitive_toggle, 
                                        'enable_extended_layout')

        # galmadeuli feature
        default = GLib.Variant.new_boolean(
                            config.default_value['engine/hangul']['enable_galmadeuli_method'])
        enable_method_galmadeuli = self.__read(
                            config.GSettings['engine/hangul']['enable_galmadeuli_method'], default)

        self.__enable_method_galmadeuli = self.__builder.get_object("GalmadeuliFeatureEnable")
        self.__enable_method_galmadeuli.set_active(enable_method_galmadeuli.get_boolean())

        self.__enable_method_galmadeuli.connect('toggled', 
                                        self.apply_sensitive_toggle, 
                                        'enable_galmadeuli_method')
        # hidden flag
        self.hidden_flags_sensitive ();

    def hidden_flags_sensitive(self):
        # hidden flag
        default = GLib.Variant.new_byte(
                            config.default_value['engine/hangul']['hidden_flag'])
        hidden_flags_gvariant = self.__read(
                            config.GSettings['engine/hangul']['hidden_flag'], default)
        hidden_flags = GLib.Variant.get_byte(hidden_flags_gvariant)

        if hidden_flags & config.VALUE_MASK['hidden_flag']['HANGUL_KEYBOARD_FLAG_EXTENDED']:
            self.__builder.get_object('AdvancedExtensionFeature').set_sensitive(True)
        else:
            self.__builder.get_object('AdvancedExtensionFeature').set_sensitive(False)
        if hidden_flags & config.VALUE_MASK['hidden_flag']['HANGUL_KEYBOARD_FLAG_GALMADEULI']:
            self.__builder.get_object('AdvancedGalmadeuliFeature').set_sensitive(True)
        else:
            self.__builder.get_object('AdvancedGalmadeuliFeature').set_sensitive(False)

    def keyboard_xkb_layout_sensitive(self, widget, mask_key):
        model = self.__xkb_layout.get_model()
        row = self.__xkb_layout.get_active()
        xkb_layout = model[row][config.list_model['keyboard_xkb']['id']]

        self.__english_keyboard_entry.set_sensitive(xkb_layout == "hangul:us")
        self.__english_keyboard_select.set_sensitive(xkb_layout == "hangul:us")

        self.apply_sensitive_toggle (None, mask_key)

    def flush_use_sensitive(self, widget, mask_key):
        enable_flush_time = widget.get_active()
        self.__builder.get_object('IntervalTimeLabel').set_sensitive(enable_flush_time)
        self.__builder.get_object('IntervalTimeSpinBox').set_sensitive(enable_flush_time)
        self.apply_sensitive_toggle (None, mask_key)

    def vi_editor_keys_sensitive(self, widget, mask_key):
        self.__vi_key_box.set_sensitive(self.__enable_vi_editor.get_active())
        self.apply_sensitive_toggle (None, mask_key)

    def autotext_mode_sensitive(self, widget, mask_key):
        enable_autotext_mode = widget.get_active()
        self.__builder.get_object('AutotextKeyBox').set_sensitive(enable_autotext_mode)
        self.__builder.get_object('AutotextTreeViewBox').set_sensitive(enable_autotext_mode)
        self.apply_sensitive_toggle (None, mask_key)

    def apply_sensitive_toggle(self, widget, mask_key):
        found = False

        for entry in config.VALUE_MASK['engine/hangul']:
            if mask_key == entry:
                self.mask |= config.VALUE_MASK['engine/hangul'][mask_key]
                found = True
                break

        if found == False:
            for entry in config.VALUE_MASK['panel']:
                if mask_key == entry:
                    self.mask |= config.VALUE_MASK['panel'][mask_key]
                    found = True
                    break

        if found == True:
            self.__builder.get_object('button_apply').set_sensitive(True)

    def apply_general(self):
        # general
        if self.mask & config.VALUE_MASK['engine/hangul']['keyboard_hangul']:
            current = self.__hangul_keyboard_entry.get_text()
            for (key, name) in self.__hangul_keyboard_list:
                if _(name) == _(current):
                    self.hangul_key = key
                    break
            self.__write(config.GSettings['engine/hangul']['keyboard_hangul'], 
                                    GLib.Variant.new_string(self.hangul_key))

        if self.mask & config.VALUE_MASK['engine/hangul']['keyboard_english']:
            current = self.__english_keyboard_entry.get_text()
            for (key, name) in self.__english_keyboard_list:
                if _(name) == _(current):
                    self.english_key = key
                    break
            self.__write(config.GSettings['engine/hangul']['keyboard_english'], 
                                    GLib.Variant.new_string(self.english_key))

        if self.mask & config.VALUE_MASK['engine/hangul']['keyboard_language']:
            model = self.__initial_language.get_model()
            row = self.__initial_language.get_active()
            self.__write(config.GSettings['engine/hangul']['keyboard_language'], 
                            GLib.Variant.new_string(
                                model[row][config.list_model['keyboard_language']['id']]))

        if self.mask & config.VALUE_MASK['engine/hangul']['keyboard_xkb']:
            model = self.__xkb_layout.get_model()
            row = self.__xkb_layout.get_active()
            default = GLib.Variant.new_string(
                            model[row][config.list_model['keyboard_xkb']['id']])
            current = self.__read(
                            config.GSettings['engine/hangul']['keyboard_xkb'], default)
            self.__write(config.GSettings['engine/hangul']['keyboard_xkb'], default)
            if default != current:
                self.__save_engine()

        if self.mask & config.VALUE_MASK['engine/hangul']['preedit_flush_time']:
            flush_time = self.__flush_time_select.get_value_as_int()
            self.__write(config.GSettings['engine/hangul']['preedit_flush_time'], 
                            GLib.Variant.new_uint32(flush_time))

        if self.mask & config.VALUE_MASK['engine/hangul']['enable_preedit_flush']:
            enable_flush_time = self.__enable_flush_time.get_active()
            self.__write(config.GSettings['engine/hangul']['enable_preedit_flush'], 
                            GLib.Variant.new_boolean(enable_flush_time))

    def apply_hangul(self):
        #hangul
        if self.mask & config.VALUE_MASK['engine/hangul']['keys_hangul']:
            model = self.__hangul_key_list.get_model()
            string_list = []
            row = model.get_iter_first()
            while row:
                string_list.append(model.get_value(row, 
                            config.list_model['keys_hangul']['shortcut']))
                row = model.iter_next(row)
            if string_list != []:
                self.__write(config.GSettings['engine/hangul']['keys_hangul'], 
                            GLib.Variant.new_strv(string_list))
            else:
                self.__write(config.GSettings['engine/hangul']['keys_hangul'], 
                            GLib.Variant.new_strv(
                                config.default_value['engine/hangul']['keys_hangul']))

    def apply_hanja(self):
        #hanja
        if self.mask & config.VALUE_MASK['engine/hangul']['keys_hanja']:
            model = self.__hanja_key_list.get_model()
            string_list = []
            row = model.get_iter_first()
            while row:
                string_list.append(model.get_value(row, 
                                config.list_model['keys_hanja']['shortcut']))
                row = model.iter_next(row)
            if string_list != []:
                self.__write(config.GSettings['engine/hangul']['keys_hanja'], 
                            GLib.Variant.new_strv(string_list))
            else:
                self.__write(config.GSettings['engine/hangul']['keys_hanja'], 
                            GLib.Variant.new_strv(
                                config.default_value['engine/hangul']['keys_hanja']))

    def apply_vi(self):
        # vi
        if self.mask & config.VALUE_MASK['engine/hangul']['enable_keys_vieditor']:
            enable_vi_editor = self.__enable_vi_editor.get_active()
            self.__write(config.GSettings['engine/hangul']['enable_keys_vieditor'], 
                            GLib.Variant.new_boolean(enable_vi_editor))

        if self.mask & config.VALUE_MASK['engine/hangul']['keys_on']:
            model = self.__on_key_list.get_model()
            string_list = []
            row = model.get_iter_first()
            while row:
                string_list.append(model.get_value(row, 
                                config.list_model['keys_on']['shortcut']))
                row = model.iter_next(row)
            if string_list != []:
                self.__write(config.GSettings['engine/hangul']['keys_on'], 
                            GLib.Variant.new_strv(string_list))
            else:
                self.__write(config.GSettings['engine/hangul']['keys_on'], 
                            GLib.Variant.new_strv(
                                config.default_value['engine/hangul']['keys_on']))

        if self.mask & config.VALUE_MASK['engine/hangul']['keys_off']:
            model = self.__off_key_list.get_model()
            string_list = []
            row = model.get_iter_first()
            while row:
                string_list.append(model.get_value(row, 
                                config.list_model['keys_off']['shortcut']))
                row = model.iter_next(row)
            if string_list != []:
                self.__write(config.GSettings['engine/hangul']['keys_off'], 
                            GLib.Variant.new_strv(string_list))
            else:
                self.__write(config.GSettings['engine/hangul']['keys_off'], 
                            GLib.Variant.new_strv(
                                config.default_value['engine/hangul']['keys_off']))

    def apply_dictionary(self):
        #dict
        if self.mask & config.VALUE_MASK['engine/hangul']['dictionary']:
            model = self.__dictionary_list.get_model()
            row = model.get_iter_first()
            while row:
                if model.get_value(row, config.list_model['dictionary']['id']) == \
                        config.dict_type['dictionary_symbol']['id']:
                    dict_name = 'dictionary_symbol'
                elif model.get_value(row, config.list_model['dictionary']['id']) == \
                        config.dict_type['dictionary_hanja']['id']:
                    dict_name = 'dictionary_hanja'
                elif model.get_value(row, config.list_model['dictionary']['id']) == \
                        config.dict_type['dictionary_emoticon']['id']:
                    dict_name = 'dictionary_emoticon'
                else:
                    row = model.iter_next(row)
                    continue

                select_dict = model.get_value(row, config.list_model['dictionary']['filepath'])
                if path.isfile(select_dict):
                    if select_dict != config.default_value['engine/hangul'][dict_name] :
                        self.__write(config.GSettings['engine/hangul'][dict_name], 
                                            GLib.Variant.new_string(select_dict))

                row = model.iter_next(row)

    def apply_autotext(self):
        # autotext
        if self.mask & config.VALUE_MASK['engine/hangul']['enable_mode_autotext']:
            enable_autotext_mode = self.__enable_autotext_mode.get_active()
            self.__write(config.GSettings['engine/hangul']['enable_mode_autotext'], 
                            GLib.Variant.new_boolean(enable_autotext_mode))

        if self.mask & config.VALUE_MASK['engine/hangul']['keys-mode-autotext']:
            autotext_mode_key_name = self.__autotext_mode_key_entry.get_text()
            autotext_mode_key_val = Gdk.keyval_from_name(autotext_mode_key_name)
            self.__write(config.GSettings['engine/hangul']['keys-mode-autotext'], 
                            GLib.Variant.new_int32(autotext_mode_key_val))

        if self.__autotext_tree_changed:
            self.__autotext_tree_changed = False
            autotext_list = []
            model = self.__autotext_treeview.get_model()
            row = model.get_iter_first()
            while row:
                (key, autotext, comment) = model.get(row, 
                                                            config.list_model['keys-mode-autotext']['key'],
                                                            config.list_model['keys-mode-autotext']['autotext'],
                                                            config.list_model['keys-mode-autotext']['comment'])
                if key and autotext:
                    autotext_list.append((key, autotext, comment))
                row = model.iter_next(row)
            self.__set_autotext_list(autotext_list)

    def apply_advanced(self):
        #advanced
        if self.mask & config.VALUE_MASK['engine/hangul']['preedit_word_commit']:
            word_commit = self.__word_commit.get_active()
            self.__write(config.GSettings['engine/hangul']['preedit_word_commit'], 
                            GLib.Variant.new_boolean(word_commit))

        if self.mask & config.VALUE_MASK['engine/hangul']['preedit_auto_reorder']:
            auto_reorder = self.__auto_reorder.get_active()
            self.__write(config.GSettings['engine/hangul']['preedit_auto_reorder'], 
                            GLib.Variant.new_boolean(auto_reorder))

        if self.mask & config.VALUE_MASK['engine/hangul']['disable_latin_mode']:
            disable_latin_mode = self.__disable_latin_mode.get_active()
            self.__write(config.GSettings['engine/hangul']['disable_latin_mode'], 
                            GLib.Variant.new_boolean(disable_latin_mode))

        if self.mask & config.VALUE_MASK['engine/hangul']['enable_104_keyboard']:
            enable_104_keys = self.__enable_104_keys.get_active()
            self.__write(config.GSettings['engine/hangul']['enable_104_keyboard'], 
                            GLib.Variant.new_boolean(enable_104_keys))

        if self.mask & config.VALUE_MASK['engine/hangul']['enable_extended_layout']:
            enable_layout_extended = self.__enable_layout_extended.get_active()
            self.__write(config.GSettings['engine/hangul']['enable_extended_layout'], 
                            GLib.Variant.new_boolean(enable_layout_extended))

        if self.mask & config.VALUE_MASK['engine/hangul']['enable_galmadeuli_method']:
            enable_method_galmadeuli = self.__enable_method_galmadeuli.get_active()
            self.__write(config.GSettings['engine/hangul']['enable_galmadeuli_method'], 
                            GLib.Variant.new_boolean(enable_method_galmadeuli))

    def on_response_apply(self):
        # general
        self.apply_general()
        # hangul
        self.apply_hangul()
        # hanja
        self.apply_hanja()
        # vi
        self.apply_vi()
        # dict
        self.apply_dictionary()
        # autotext
        self.apply_autotext()
        # advanced
        self.apply_advanced()
        self.mask = 0
        
    def on_response(self, widget, response, data = None):
        # gtk+3.11: emit_stop_by_name have been deprecated
        if response == Gtk.ResponseType.OK:
            self.on_response_ok()
            widget.stop_emission_by_name("response")
        if response == Gtk.ResponseType.APPLY:
            self.on_response_apply()
            self.__builder.get_object('button_apply').set_sensitive(False)
            widget.stop_emission_by_name("response")
        if response == Gtk.ResponseType.HELP:
            self.on_response_about(widget)
            widget.stop_emission_by_name("response")
        if response == Gtk.ResponseType.CANCEL:
            self.on_response_cancel()
            widget.stop_emission_by_name("response")
        if response == Gtk.ResponseType.NONE:
            widget.stop_emission_by_name("response")

    def on_response_ok(self):
        if self.__builder.get_object('button_apply').get_state() == \
                    Gtk.StateType.INSENSITIVE:
            Gtk.main_quit()
            return True
        dialog = Gtk.MessageDialog(transient_for = self.__builder.get_object("SetupDialog"))
        buttons = (_("_Ok"), Gtk.ResponseType.OK)
        dialog.add_buttons(*buttons)
        dialog.set_markup('<big><b>%s</b></big>' % _("Confirmation"))
        dialog.format_secondary_text(
                _("You are about to close the setup dialog\n"
                    "The changes will be applied"))
        response = dialog.run()
        dialog.hide()
        if response == Gtk.ResponseType.OK:
            self.on_response_apply()
            Gtk.main_quit()
            return True

    def on_response_cancel(self):
        if self.__builder.get_object('button_apply').get_state() == \
                    Gtk.StateType.INSENSITIVE:
            Gtk.main_quit()
            return True
        dialog = Gtk.MessageDialog(transient_for = self.__builder.get_object("SetupDialog"))
        buttons = ((_("_Cancel"), Gtk.ResponseType.CANCEL,
                        _("_Ok"), Gtk.ResponseType.OK))
        dialog.add_buttons(*buttons)
        dialog.set_markup('<big><b>%s</b></big>' % _("Notice!"))
        dialog.format_secondary_text(
                _("You are about to close the setup dialog\n"
                    "without saving your changes\n"
                    "is that OK?"))
        response = dialog.run()
        dialog.hide()
        if response == Gtk.ResponseType.OK:
            Gtk.main_quit()
            return True

    def on_response_about(self, widget, data = None):
        dialog = AboutDialog(widget)

    def on_hotkey_add(self, widget, mask_key):
        dialog = KeyCaptureDialog(_("Select Hotkey"), 
                                        self.__builder.get_object("SetupDialog"),
                                        self.__enable_104_keys.get_active())
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            key_str = dialog.get_key_string()
            if len(key_str) > 0:
                if mask_key == 'keys_hangul':
                    model = self.__hangul_key_list.get_model()
                elif mask_key == 'keys_hanja':
                    model = self.__hanja_key_list.get_model()
                elif mask_key == 'keys_on':
                    model = self.__on_key_list.get_model()
                elif mask_key == 'keys_off':
                    model = self.__off_key_list.get_model()
                else:
                    return
                # insert
                index = 0
                row = model.get_iter_first()
                while row:
                    string = model.get_value(row, 
                                    config.list_model[mask_key]['shortcut'])
                    if string == key_str:
                        model.remove(row)
                        break
                    index = index + 1
                    row = model.iter_next(row)

                model.append([key_str, index])
                self.apply_sensitive_toggle (None, mask_key)
        dialog.destroy()

    def on_hotkey_remove(self, widget, mask_key):
        if mask_key == 'keys_hangul':
            selection = self.__hangul_key_list.get_selection()
        elif mask_key == 'keys_hanja':
            selection = self.__hanja_key_list.get_selection()
        elif mask_key == 'keys_on':
            selection = self.__on_key_list.get_selection()
        elif mask_key == 'keys_off':
            selection = self.__off_key_list.get_selection()
        else:
            return
        (model, row) = selection.get_selected()
        if model and row:
            model.remove(row)
            self.apply_sensitive_toggle (None, mask_key)

    def variant_to_value(self, section, name, variant):
        if type(variant) != GLib.Variant:
            return variant
        type_string = variant.get_type_string()
        # b:boolean / y:guchar / d:double
        # v:variant / a:array / m:maybe / ():tuple / {}:dictionary
        if type_string == 's':
            return variant.get_string()
        elif type_string == 'i':
            return variant.get_int32()
        elif type_string == 'u':
            return variant.get_uint32()
        elif type_string == 'b':
            return variant.get_boolean()
        elif type_string == 'as':
            return variant.get_strv()
        else:
            value = config.default_value[section][name]
        return value

    def on_value_changed(self, ibusconfig, section, name, value, data):
        # 키값을 가져오지 못할 때 왜 value 는 () 가 되는가
        if section == "engine/hangul":
            if name.startswith('keyboard'):
                if name.endswith('hangul'):
                    self.hangul_key = self.variant_to_value(section, name, value)
                    for (key, name) in self.__hangul_keyboard_list:
                        if key == self.hangul_key:
                            self.__hangul_keyboard_entry.set_text(name)
                            self.__hangul_keyboard_entry.set_tooltip_text(key)
                            break
                elif name.endswith('english'):
                    self.english_key = self.variant_to_value(section, name, value)
                    for (key, name) in self.__english_keyboard_list:
                        if key == self.english_key:
                            self.__english_keyboard_entry.set_text(_(name))
                            self.__english_keyboard_entry.set_tooltip_text(key)
                            break
                elif name.endswith('language'):
                    initial_language = self.variant_to_value(section, name, value)
                    model = self.__initial_language.get_model()
                    for row in model:
                        if row[config.list_model['keyboard_language']['id']] == initial_language:
                            self.__initial_language.set_active(
                                        row[config.list_model['keyboard_language']['index']])
                            break
                elif name.endswith('xkb'):
                    xkb_layout = self.variant_to_value(section, name, value)
                    model = self.__xkb_layout.get_model()
                    for row in model:
                        if row[config.list_model['keyboard_xkb']['id']] == xkb_layout:
                            self.__xkb_layout.set_active(
                                        row[config.list_model['keyboard_xkb']['index']])
                            break

                    self.__english_keyboard_entry.set_sensitive(xkb_layout == "hangul:us")
                    self.__english_keyboard_select.set_sensitive(xkb_layout == "hangul:us")
            elif name.startswith('keys'):
                if name.endswith('hangul'):
                    self.__hangul_key_list_strv = self.variant_to_value(section, name, value)
                    if self.__hangul_key_list_strv[0]:
                        model = self.__hangul_key_list.get_model()
                        model.clear()
                        for shortcut in self.__hangul_key_list_strv:
                            model.append([shortcut,
                                                self.__hangul_key_list_strv.index(shortcut)])
                elif name.endswith('hanja'):
                    self.__hanja_key_list_strv = self.variant_to_value(section, name, value)
                    if self.__hanja_key_list_strv[0]:
                        model = self.__hanja_key_list.get_model()
                        model.clear()
                        for shortcut in self.__hanja_key_list_strv:
                            model.append([shortcut,
                                                self.__hanja_key_list_strv.index(shortcut)])
                elif name.endswith('on'):
                    self.__on_key_list_strv = self.variant_to_value(section, name, value)
                    if self.__on_key_list_strv[0]:
                        model = self.__on_key_list.get_model()
                        model.clear()
                        for shortcut in self.__on_key_list_strv:
                            model.append([shortcut,
                                                self.__on_key_list_strv.index(shortcut)])
                elif name.endswith('off'):
                    self.__off_key_list_strv = self.variant_to_value(section, name, value)
                    if self.__off_key_list_strv[0]:
                        model = self.__off_key_list.get_model()
                        model.clear()
                        for shortcut in self.__off_key_list_strv:
                            model.append([shortcut,
                                                self.__off_key_list_strv.index(shortcut)])
                elif name.endswith('autotext'):
                    autotext_key_val = self.variant_to_value(section, name, value)
                    autotext_key_name = Gdk.keyval_name(autotext_key_val)
                    self.__autotext_mode_key_entry.set_text(autotext_key_name)
            elif name.startswith('preedit'):
                if name.endswith('commit'):
                    word_commit = self.variant_to_value(section, name, value)
                    self.__word_commit.set_active(word_commit)
                elif name.endswith('reorder'):
                    auto_reorder = self.variant_to_value(section, name, value)
                    self.__auto_reorder.set_active(auto_reorder)
                elif name.endswith('time'):
                    flush_time = self.variant_to_value(section, name, value)
                    self.__flush_time_select.set_value(flush_time)
            elif name.startswith('disable'):
                if name.endswith('mode'):
                    word_commit = self.variant_to_value(section, name, value)
                    self.__word_commit.set_active(word_commit)
            elif name.startswith('dictionary'):
                if name.endswith('symbol'):
                    dictionary_symbol = self.variant_to_value(section, name, value)
                    model = self.__dictionary_list.get_model()
                    row = model.get_iter_first()
                    while row:
                        dict_type = model.get_value(row, config.list_model['dictionary']['id'])
                        if dict_type == config.dict_type['dictionary_symbol']['id']:
                            if not path.isfile(dictionary_symbol):
                                if dictionary_symbol != '' :
                                    self.__run_message_dialog(
                                                _("The Symbol dictionary file does not exist:\n") +\
                                                value.get_string(),
                                                None,
                                                Gtk.MessageType.ERROR)
                                dictionary_symbol = config.default_value[section][name]
                            model.set(row, 
                                config.list_model['dictionary']['filename'], path.basename(dictionary_symbol), 
                                config.list_model['dictionary']['filepath'], dictionary_symbol)
                            if dictionary_symbol == config.default_value[section][name] :
                                self.__builder.get_object('DictionaryListDefaultButton').set_sensitive(False)
                            break
                        row = model.iter_next(row)
                elif name.endswith('emoticon'):
                    dictionary_emoticon = self.variant_to_value(section, name, value)
                    model = self.__dictionary_list.get_model()
                    row = model.get_iter_first()
                    while row:
                        dict_type = model.get_value(row, config.list_model['dictionary']['id'])
                        if dict_type == config.dict_type['dictionary_emoticon']['id']:
                            if not path.isfile(dictionary_emoticon):
                                if dictionary_emoticon != '' :
                                    self.__run_message_dialog(
                                                _("The Emoticon dictionary file does not exist:\n") +\
                                                dictionary_emoticon,
                                                None,
                                                Gtk.MessageType.ERROR)
                                dictionary_emoticon = config.default_value[section][name]
                            model.set(row, 
                                config.list_model['dictionary']['filename'], path.basename(dictionary_emoticon), 
                                config.list_model['dictionary']['filepath'], dictionary_emoticon)
                            if dictionary_emoticon == config.default_value[section][name] :
                                self.__builder.get_object('DictionaryListDefaultButton').set_sensitive(False)
                            break
                        row = model.iter_next(row)
                elif name.endswith('hanja'):
                    dictionary_hanja = self.variant_to_value(section, name, value)
                    model = self.__dictionary_list.get_model()
                    row = model.get_iter_first()
                    while row:
                        dict_type = model.get_value(row, config.list_model['dictionary']['id'])
                        if dict_type == config.dict_type['dictionary_hanja']['id']:
                            if not path.isfile(dictionary_hanja):
                                if dictionary_hanja != '' :
                                    self.__run_message_dialog(
                                                _("The Hanja dictionary file does not exist:\n") +\
                                                dictionary_hanja,
                                                None,
                                                Gtk.MessageType.ERROR)
                                dictionary_hanja = config.default_value[section][name]
                            model.set(row, 
                                    config.list_model['dictionary']['filename'], path.basename(dictionary_hanja), 
                                    config.list_model['dictionary']['filepath'], dictionary_hanja)
                            if dictionary_hanja == config.default_value[section][name] :
                                self.__builder.get_object('DictionaryListDefaultButton').set_sensitive(False)
                            break
                        row = model.iter_next(row)
            elif name.startswith('enable'):
                if name.endswith('keyboard'):
                    enable_104_keys = self.variant_to_value(section, name, value)
                    self.__enable_104_keys.set_active(enable_104_keys)
                elif name.endswith('autotext'):
                    enable_autotext_mode = self.variant_to_value(section, name, value)
                    self.__enable_autotext_mode.set_active(enable_autotext_mode)
                    self.__builder.get_object('AutotextKeyBox').set_sensitive(enable_autotext_mode)
                    self.__builder.get_object('AutotextTreeViewBox').set_sensitive(enable_autotext_mode)
                elif name.endswith('vieditor'):
                    enable_vi_editor = self.variant_to_value(section, name, value)
                    self.__enable_vi_editor.set_active(enable_vi_editor)
                    self.__vi_key_box.set_sensitive(enable_vi_editor)
                elif name.endswith('flush'):
                    enable_flush_time = self.variant_to_value(section, name, value)
                    self.__enable_flush_time.set_active(enable_flush_time)
                    self.__builder.get_object('IntervalTimeLabel').set_sensitive(enable_flush_time)
                    self.__builder.get_object('IntervalTimeSpinBox').set_sensitive(enable_flush_time)
                elif name.endswith('extended'):
                    enable_layout_extended = self.variant_to_value(section, name, value)
                    self.__enable_layout_extended.set_active(enable_layout_extended)
                elif name.endswith('galmadeuli'):
                    enable_method_galmadeuli = self.variant_to_value(section, name, value)
                    self.__enable_method_galmadeuli.set_active(enable_method_galmadeuli)
            elif name.startswith('hidden'):
                if name.endswith('flag'):
                    # hidden flag
                    self.hidden_flags_sensitive ();
        self.__builder.get_object('button_apply').set_sensitive(False)

    def on_dictionary_file_selected(self, file_chooser, text_view):
        buffer = Gtk.TextBuffer()
        dict_file = file_chooser.get_filename()
        if dict_file != None:
            buffer.set_text (dict_file)
        text_view.set_buffer(buffer)

    def on_dictionary_selection_changed(self, widget, index):
        set_sensitive = lambda a, b: self.__builder.get_object(a).set_sensitive(b)
        model, row = widget.get_selected()
        dict_type = model.get_value(row, config.list_model['dictionary']['id'])
        dict_path = model.get_value(row, config.list_model['dictionary']['filepath'])
        if dict_path == config.default_value['engine/hangul']['dictionary_' + dict_type] :
            flag = False
        else :
            flag = True
        target = [['DictionaryListDefaultButton'], 
                        ['DictionaryListViewButton', 'DictionaryListEditButton'],]
        for name in target[index]:
            set_sensitive(name, flag)

    def on_dictionary_key_release_event(self, widget, event, mask_key):
        if event.hardware_keycode in [36, 65]: # enter, space_bar
            self.on_dictionary_edit_clicked(None, mask_key)

    def on_dictionary_click_event(self, widget, event, mask_key):
        if event.type == Gdk.EventType._2BUTTON_PRESS:
            widget.dc = True
        elif event.type == Gdk.EventType.BUTTON_RELEASE:
            if hasattr(widget, 'dc') and widget.dc:
                self.on_dictionary_edit_clicked(None, mask_key)
                widget.dc = False

    def on_dictionary_edit_clicked(self, widget, mask_key):
        dialog = Gtk.Dialog(transient_for=self.__builder.get_object("SetupDialog"))
        buttons=(_("_Cancel"), Gtk.ResponseType.CANCEL, 
                    _("_Ok"), Gtk.ResponseType.OK)
        dialog.add_buttons(*buttons)
        dialog.set_title(_("Dictionary File"))
        label = Gtk.Label()
        label.set_use_markup(True)
        label.set_markup('<big><b>%s</b></big>' % 
                                    _("Selected dictionary"))

        dict_file = None
        tree_view = self.__builder.get_object('DictionaryList')
        model, row = tree_view.get_selection().get_selected()
        if model and row:
            dict_file = model.get_value(row, config.list_model['dictionary']['filepath'])
        buffer = Gtk.TextBuffer()
        if dict_file != None:
            buffer.set_text (dict_file)
        text_view = Gtk.TextView.new_with_buffer(buffer)
        text_view.set_editable(False)
        # Gtk.WrapMode.NONE, Gtk.WrapMode.CHAR, Gtk.WrapMode.WORD or Gtk.WrapMode.WORD_CHAR.
        text_view.set_wrap_mode(Gtk.WrapMode.CHAR)
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.set_min_content_width(350)
        scrolled_window.add(text_view)

        file_chooser = Gtk.FileChooserButton()
        file_chooser.set_title(_("Choose a dictionary file"))
        file_chooser.set_show_hidden (True)
        if not dict_file:
            dict_file = None
        if dict_file != None:
            file_chooser.set_filename(dict_file)
        else:
            file_chooser.unselect_all()
        file_chooser.connect("file-set", self.on_dictionary_file_selected, text_view)

        parent_box = dialog.vbox
        parent_box.add(label)
        parent_box.add(scrolled_window)
        parent_box.add(file_chooser)
        parent_box.set_spacing(6)

        label.show()
        scrolled_window.show_all()
        file_chooser.show()

        response = dialog.run()
        dialog.destroy()
        if response == Gtk.ResponseType.OK:
            dict_file = file_chooser.get_filename()
            if dict_file == None or \
                    not dict_file or \
                    not path.isfile(dict_file):
                self.on_dictionary_default_clicked(None, mask_key)
            else:
                model.set(row, 
                    config.list_model['dictionary']['filename'], path.basename(dict_file), 
                    config.list_model['dictionary']['filepath'], dict_file)
                self.on_dictionary_selection_changed(tree_view.get_selection(), 0)
                self.apply_sensitive_toggle (None, mask_key)

    def __search_and_mark(self, buffer, text, start, end, onetime, forward):
        if forward:
            try:
                match = start.forward_search(text, 0, end)
            except RuntimeError:
                sys.setrecursionlimit(10000)
                match = start.forward_search(text, 0, end)
        else:
            try:
                match = start.backward_search(text, 0, end)
            except RuntimeError:
                sys.setrecursionlimit(10000)
                match = start.backward_search(text, 0, end)
        if match == None:
            return False
        match_start, match_end = match
        if onetime:
            buffer.place_cursor(match_start)
            buffer.select_range(match_start, match_end)
            return True
        buffer.apply_tag(buffer.tag_found, match_start, match_end)
        self.__search_and_mark(buffer, text, match_end, end, onetime, forward)
        return True

    def __filter_search(self, entry, onetime, forward):
        text = entry.get_text()
        self.__filter_timeout_id = 0

        text_view = entry.text_view
        buffer = text_view.get_buffer()
        start = buffer.get_start_iter()
        if onetime:
            bounds = buffer.get_selection_bounds()
            if len(bounds) != 0:
                start, end = bounds
                if forward:
                    start = end
        end = buffer.get_end_iter()
        if not forward:
            end = buffer.get_start_iter()
        if not onetime:
            buffer.remove_all_tags(start, end)
        if text == '':
            return
        found = self.__search_and_mark(buffer, text, start, end, onetime, forward)
        if not found and onetime and forward:
            end = start
            start = buffer.get_start_iter()
            self.__search_and_mark(buffer, text, start, end, onetime, forward)

    def __do_filter(self, entry):
        self.__filter_search(entry, False, True)
        return False

    def __filter_changed(self, entry):
        if self.__filter_timeout_id != 0:
            return
        self.__filter_timeout_id = GLib.timeout_add(150,
                                                                            self.__do_filter,
                                                                            entry)

    def __filter_key_release_event(self, entry, event):
        pressed, keyval = event.get_keyval()
        if keyval == IBus.KEY_Return:
            forward = True
            if event.get_state() & Gdk.ModifierType.SHIFT_MASK:
                forward = False
            self.__filter_search(entry, True, forward)
            text_view = entry.text_view
            buffer = text_view.get_buffer()
            text_view.scroll_to_mark(buffer.get_insert(),
                                                        0.25, False, 0.0, 0.0)
        return False

    def on_dictionary_view_clicked(self, widget):
        dict_file = None
        tree_view = self.__builder.get_object('DictionaryList')
        model, row = tree_view.get_selection().get_selected()
        if not row:
            self.__run_message_dialog(_("Your file is not good."), 
                                                    None,
                                                    Gtk.MessageType.ERROR)
            return
        dict_file = model.get_value(row, 
                                    config.list_model['dictionary']['filepath'])

        if dict_file == None or not dict_file:
            entry = model.get_value(row, config.list_model['dictionary']['id'])
            if entry == config.dict_type['dictionary_symbol']['id']:
                dict_file = config.default_value['dict']['dictionary_symbol']
            elif entry == config.dict_type['dictionary_hanja']['id']:
                dict_file = config.default_value['dict']['dictionary_hanja']
            elif entry == config.dict_type['dictionary_emoticon']['id']:
                dict_file = config.default_value['dict']['dictionary_emoticon']

        if not path.isfile(dict_file):
            self.__run_message_dialog(_("Your file does not exist: ") + dict_file, 
                                                        None,
                                                        Gtk.MessageType.ERROR)
            return

        lines = '';
        # in Python 3.0, Removed file. Use open()
        try:
            file_open = file(dict_file)
        except NameError:   # python3
            file_open = open(dict_file)
        for i, line in enumerate(file_open):
            lines = lines + line

        dialog = Gtk.Dialog(transient_for=self.__builder.get_object("SetupDialog"))
        buttons=(_("_Ok"), Gtk.ResponseType.OK)
        dialog.add_buttons(*buttons)
        dialog.set_title(_("View Dictionary File"))
        buffer = Gtk.TextBuffer()
        buffer.set_text(lines)
        buffer.place_cursor(buffer.get_start_iter())
        buffer.tag_found = buffer.create_tag('found', background = 'yellow')
        text_view = Gtk.TextView.new_with_buffer(buffer)
        text_view.set_editable(False)
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.add(text_view)
        scrolled_window.set_min_content_height(450)
        parent_box = dialog.vbox
        parent_box.add(scrolled_window)
        scrolled_window.show_all()
        dialog.set_default_size(500, 500)

        self.__filter_timeout_id = 0

        filter_entry = Gtk.SearchEntry(hexpand = True,
                                                            margin_left = 6,
                                                            margin_right = 6,
                                                            margin_top = 6,
                                                            margin_bottom = 6)
        filter_entry.text_view = text_view
        filter_entry.connect('search-changed', self.__filter_changed)
        filter_entry.connect('key-release-event',
                                                self.__filter_key_release_event)
        parent_box.add(filter_entry)
        filter_entry.show_all()
        filter_entry.grab_focus()

        scrolled_window.show_all()
        dialog.run()
        dialog.destroy()

    def on_dictionary_default_clicked(self, widget, mask_key):
        tree_view = self.__builder.get_object('DictionaryList')
        model, row = tree_view.get_selection().get_selected()
        entry = model.get_value(row, config.list_model['dictionary']['id'])
        dict_path = config.default_value['engine/hangul']["dictionary_" + entry]
        model.set(row, 
                config.list_model['dictionary']['filename'], path.basename (dict_path), 
                config.list_model['dictionary']['filepath'], dict_path)
        self.on_dictionary_selection_changed(tree_view.get_selection(), 0)
        self.apply_sensitive_toggle (None, mask_key)

    def on_keyboard_layout_select_clicked(self, widget, mask_key):
        if mask_key == 'keyboard_hangul':
            current_layout_id = self.hangul_key
            layout_list = self.__hangul_keyboard_list
        elif mask_key == 'keyboard_english':
            current_layout_id = self.english_key
            layout_list = self.__english_keyboard_list
        else:
            return

        dialog = LayoutDialog(transient_for=self.__window, \
                                            current_layout_id=current_layout_id, \
                                            layout_list=layout_list)

        response = dialog.run()
        if response != Gtk.ResponseType.APPLY:
            dialog.destroy()
            return

        self.apply_sensitive_toggle (None, mask_key)
        (key, name) = dialog.get_selected_layout()
        dialog.destroy()

        if key != None:
            if mask_key == 'keyboard_hangul':
                self.__hangul_keyboard_entry.set_text(name)
                self.__hangul_keyboard_entry.set_tooltip_text(key)
            elif mask_key == 'keyboard_english':
                self.__english_keyboard_entry.set_text(name)
                self.__english_keyboard_entry.set_tooltip_text(key)

    def on_autotext_mode_key_select(self, widget, mask_key):
        dialog = KeyCaptureDialog(_("Select an autotext mode key"), 
                                        self.__builder.get_object("SetupDialog"))
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            key_str = dialog.get_key_string()
            if len(key_str) > 0:
                self.__autotext_mode_key_entry.set_text(key_str)
                self.apply_sensitive_toggle (None, mask_key)
        dialog.destroy()

    def on_autotext_remove(self, widget, mask_key):
        selection = self.__autotext_treeview.get_selection()
        (model, row) = selection.get_selected()
        if model and row:
            model.remove(row)
            self.__autotext_tree_changed = True;
            self.apply_sensitive_toggle (None, mask_key)

    def on_autotext_add(self, widget, mask_key):
        dialog = AutotextDialog(self.__builder.get_object("SetupDialog"))
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            (key, autotext, comment) = dialog.get_autotext()
            if len(key) > 0 and len(autotext) > 0:
                model = self.__autotext_treeview.get_model()
                row = model.get_iter_first()
                while row:
                    string = model.get_value(row, 
                                    config.list_model['keys-mode-autotext']['key'])
                    if string == key:
                        model.remove(row)
                        break
                    row = model.iter_next(row)
                model.append([key, autotext, comment])
                self.__autotext_tree_changed = True;
                self.apply_sensitive_toggle (None, mask_key)
        dialog.destroy()

    def __run_message_dialog(self, message, message_sec = None, type=Gtk.MessageType.INFO):
        dialog = Gtk.MessageDialog(transient_for=self.__builder.get_object("SetupDialog"),
                                message_type=type)
        buttons=(_("_Ok"), Gtk.ResponseType.OK)
        dialog.add_buttons(*buttons)
        dialog.set_markup(message)
        if message_sec == None:
            message_sec = ""
        dialog.format_secondary_markup(message_sec)
        dialog.run()
        dialog.destroy()

    def __get_userhome(self):
        if 'HOME' not in environ:
            import pwd
            userhome = pwd.getpwuid(getuid()).pw_dir
        else:
            userhome = environ['HOME']
        userhome = userhome.rstrip('/')
        return userhome

    def __get_autotext_file(self):
        autotext_file = path.join(self.__get_userhome(), '.config',
                                'ibus-hangul', 'autotext.txt')
        return autotext_file

    def __get_autotext_list(self):
        autotext_file = self.__get_autotext_file()
        if not path.exists(autotext_file):
            return None
        autotext_list = []
        # in Python 3.0, Removed file. Use open()
        try:
            file_open = file(autotext_file)
        except NameError:   # python3
            file_open = open(autotext_file)
        for i, line in enumerate(file_open):
            if line.startswith('\n') or line.startswith('\r') or \
                        line.startswith('\0') or line.startswith('#') or None:
                continue
            (key, autotext, comment) = line.replace('\n', '').split(':')
            if key != None and autotext != None:
                autotext_list.append((key, autotext, comment))
        return autotext_list

    def __set_autotext_list(self, autotext_list):
        if autotext_list == None:
            return None

        autotext_file = self.__get_autotext_file()
        autotext_file_dir = path.dirname(autotext_file)

        lines = []
        line = '# Autotext list\n\n'
        lines.append(line)
        try:
            for (key, autotext, comment) in autotext_list:
                if key == None or autotext == None:
                    continue
                line = key + ':' + autotext +':' + comment +'\n'
                lines.append(line)
            lines.sort()
        except TypeError:
            pass

        if not path.exists(autotext_file_dir):
            try:
                original_umask = os.umask(0)
                os.makedirs(autotext_file_dir, 0o700) # python3 octal
            finally:
                os.umask(original_umask)
        try:
            file_open = open(autotext_file, 'w')
        except TypeError:   #python3
            import codecs
            file_open = codecs.open(autotext_file, 'w', 'utf-8')
        file_open.writelines(lines)
        file_open.close()
        os.chmod(autotext_file, 0o600) # python3 octal

        self.__run_message_dialog(_('Autotext file is changed\n'),
                                                    _('Must be set at least two autotexts'))
        # engine.c 에서 autotext.txt 를 다시 읽도록 enable_autotext_mode 를 건드려준다.
        enable_autotext_mode = self.__enable_autotext_mode.get_active()
        self.__write(config.GSettings['engine/hangul']['enable_mode_autotext'], 
                            GLib.Variant.new_boolean(enable_autotext_mode))
        
    def __resync_engine_file(self):
        user_config = path.join(self.__get_userhome(), '.config',
                                'ibus-hangul', 'engines.xml')
        system_config = path.join(config.pkgdatadir, 'engine', 'default.xml')
        if not path.exists(user_config):
            return
        if not path.exists(system_config):
            os.unlink(user_config)
            return

        # path.getmtime depends on the build time rather than install time.
        def __get_engine_file_version(engine_file):
            version_str = ''
            longname_str = ''
            dom = xml.dom.minidom.parse(engine_file)
            elements = dom.getElementsByTagName('version')
            nodes = []
            if len(elements) > 0:
                nodes = elements[0].childNodes
            if len(nodes) > 0:
                version_str = nodes[0].data
            try:
                if type(version_str) == unicode:
                    version_str = str(version_str)
            except NameError:   # python3
                None
            if version_str != '':
                version_str = version_str.strip()
            nodes = dom.getElementsByTagName('longname')[0].childNodes
            if len(nodes) > 0:
                longname_str = nodes[0].data
            try:
                if type(longname_str) == unicode:
                    longname_str = str(longname_str)
            except NameError:   # python3
                None
            if longname_str != '':
                longname_str = longname_str.strip()
            return (version_str, longname_str)

        (user_config_version, user_config_longname) = __get_engine_file_version(user_config)
        (system_config_version, system_config_longname) = __get_engine_file_version(system_config)
        if system_config_version > user_config_version or \
                    system_config_longname != user_config_longname:
            import shutil
            shutil.copyfile(system_config, user_config)

    def __get_engine_file(self):
        user_config = path.join(self.__get_userhome(), '.config',
                                'ibus-hangul', 'engines.xml')
        system_config = path.join(config.pkgdatadir, 'engine', 'default.xml')
        engine_file = None
        for config_file in [user_config, system_config]:
            if path.exists(config_file):
                engine_file = config_file
                break
        if engine_file == None:
            self.__run_message_dialog(
                                _("The engine xml file does not exist: ") + system_config,
                                None,
                                Gtk.MessageType.ERROR)
            return None
        return engine_file

    def __get_xkb_layout(self):
        xkb_layout = ''
        layout = ''
        variant = ''
        option = ''
        engine_file = self.__get_engine_file()
        if engine_file == None:
            return None

        dom = xml.dom.minidom.parse(engine_file)
        nodes = dom.getElementsByTagName('layout')[0].childNodes
        if len(nodes) > 0:
            layout = nodes[0].data
        try:
            if type(layout) == unicode:
                layout = str(layout)
        except NameError:   # python3
            None
        if layout != '':
            xkb_layout = layout.strip()
        nodes = dom.getElementsByTagName('layout_variant')[0].childNodes
        if len(nodes) > 0:
            variant = nodes[0].data
        try:
            if type(variant) == unicode:
                variant = str(variant)
        except NameError:   # python3
            None
        if variant != '':
            xkb_layout += '(' + variant.strip() + ')'
        nodes = dom.getElementsByTagName('layout_option')[0].childNodes
        if len(nodes) > 0:
            option = nodes[0].data
        try:
            if type(option) == unicode:
                option = str(option)
        except NameError:   # python3
            None
        if option != '':
            xkb_layout += '[' + option.strip() + ']'
        return xkb_layout

    def __parse_engine(self, xkb_layout):
        layout = None
        variant = None
        option = None

        length = xkb_layout.find('(')
        if length >= 0:
            if layout == None:
                layout = xkb_layout[0:length]
            xkb_layout = xkb_layout[length + 1:]
            length = xkb_layout.find(')')
            if length > 0:
                variant = xkb_layout[0:length]
                xkb_layout = xkb_layout[length + 1:]
            else:
                self.__run_message_dialog(
                                _("Invalid xkb layout: ") + xkb_layout,
                                None,
                                Gtk.MessageType.ERROR)
                return ('', '', '')
        length = xkb_layout.find('[')
        if length >= 0:
            if layout == None:
                layout = xkb_layout[0:length]
            xkb_layout = xkb_layout[length + 1:]
            xkb_layout = xkb_layout.find(']')
            if length > 0:
                option = xkb_layout[0:length]
                xkb_layout = xkb_layout[length + 1:]
            else:
                self.__run_message_dialog(
                                _("Invalid xkb layout: ") + xkb_layout,
                                None,
                                Gtk.MessageType.ERROR)
                return ('', '', '')

        if layout == None:
            layout = xkb_layout
        if layout == None:
            layout = ''
        if variant == None:
            variant = ''
        if option == None:
            option = ''
        return (layout, variant, option)

    def __save_engine(self):
        engine_file = self.__get_engine_file()
        if engine_file == None:
            return None

        model = self.__xkb_layout.get_model()
        row_index = self.__xkb_layout.get_active()
        xkb_layout = model[row_index][config.list_model['keyboard_xkb']['xkb']]
        
        (layout, variant, option) = self.__parse_engine(xkb_layout)
        ## layout:us, variant:dvorak, option:None 은 동작이 되다안되다 하는구만...
        ## layout:us(dvorak), variant:None, option:None 로 강제설정한다.
        layout = xkb_layout
        variant = ''
        option = ''

        dom = xml.dom.minidom.parse(engine_file)
        nodes = dom.getElementsByTagName('layout')[0].childNodes
        if len(nodes) == 0:
            nodes.append(dom.createTextNode(layout))
        else:
            nodes[0].data = layout
        nodes = dom.getElementsByTagName('layout_variant')[0].childNodes
        if len(nodes) == 0:
            nodes.append(dom.createTextNode(variant))
        else:
            nodes[0].data = variant
        nodes = dom.getElementsByTagName('layout_option')[0].childNodes
        if len(nodes) == 0:
            nodes.append(dom.createTextNode(option))
        else:
            nodes[0].data = option
        nodes = dom.getElementsByTagName('symbol')[0].childNodes
        # unicode will causes UnicodeEncodeError in write stream.
        try:
            if len(nodes) > 0 and type(nodes[0].data) == unicode:
                nodes[0].data = nodes[0].data.encode('utf-8')
        except NameError:   # python3
            None

        user_config = path.join(self.__get_userhome(), '.config',
                                'ibus-hangul', 'engines.xml')
        user_dir = path.dirname(user_config)
        if not path.exists(user_dir):
            try:
                original_umask = os.umask(0)
                os.makedirs(user_dir, 0o700) # python3 octal
            finally:
                os.umask(original_umask)
        try:
            f = open(user_config, 'w')
        except TypeError:   #python3
            import codecs
            f = codecs.open(user_config, 'w', 'utf-8')
        dom.writexml(f, '', '', '', 'utf-8')
        f.close()
        os.chmod(user_config, 0o600) # python3 octal
        self.__run_message_dialog(_('Xkb base keyboard layout is changed\n'),
                                    #_('Please restart ibus to reload the layout.\n'
                                    #'Type <span size="large" weight="bold">ibus restart</span>'
                                    #' into the terminal\n'
                                    #' and press <span size="large" weight="bold">enter</span>') )
                                    _("IBus will be restarted to reload the layout"))
        argv = ['ibus', 'restart']
        GLib.spawn_async (argv, flags=GLib.SPAWN_SEARCH_PATH)

    def __update_engine_combo_list(self):
        self.__resync_engine_file()
        xkb_layout = self.__get_xkb_layout()
        if xkb_layout == None:
            return
        if xkb_layout == '' or xkb_layout == 'us':
            xkb_layout = 'us(basic)'

        xkb_layout_list = []
        for (name, key, xkb, longname, index) in config.xkb_layout_list:
                xkb_layout_list.append(xkb)

        if xkb_layout != None and not xkb_layout in xkb_layout_list:
            xkb_layout = 'us(basic)'

        index = -1
        if xkb_layout != None:
            index = xkb_layout_list.index(xkb_layout)

        model = self.__xkb_layout.get_model
        if model == None:
            model = Gtk.ListStore(str, str, str, str, int)
            for (name, key, xkb, longname, index) in config.xkb_layout_list:
                model.append([_(name), key, xkb, longname, index])

        row_index = self.__xkb_layout.get_active()
        if index >= 0 and index != row_index:
            self.__xkb_layout.set_active(index)
            default = GLib.Variant.new_string(  \
                            config.xkb_layout_list[index]   \
                                                            [config.list_model['keyboard_xkb']['id']])
            self.__write(config.GSettings['engine/hangul']['keyboard_xkb'], default)

    def __read(self, name, v):
        value = self.__config.get_value("engine/hangul", name)
        if value is None:
            return v
        return value

    def __write(self, name, v):
        return self.__config.set_value("engine/hangul", name, v)

    def run(self):
        Gtk.main()

if __name__ == "__main__":
    HangulSetup().run()
