#!/usr/bin/python

import pygtk
pygtk.require('1.2')

import gtk, libglade, Xlib.keysymdef, re, os, sys, string

def MessageBox(text, yesFunc=None, yesArgs=None,
               noFunc=None, noArgs=None):
    """Function to report warnings and Yes/No choice messages as a Dialog"""
    
    warning = gtk.GtkDialog()
    warning.set_title("Xkeysw Config Warning")
    warning.set_modal(1)
    warning.set_position("center")
    message = gtk.GtkLabel(text)
    warning.vbox.pack_start(message, 1, 1, 0)
    message.set_line_wrap(1)
    message.set_justify("center")
    message.set_padding(20,15)
    message.show()
    if yesFunc:
        def callback(w, warning, func, args):
            if func:
                if args: func(args)
                else: func()
            warning.destroy()
            
        hButtonBox = gtk.GtkHButtonBox()
        warning.action_area.pack_start(hButtonBox, 1, 1, 0)
        hButtonBox.set_layout_default("spread")
        hButtonBox.set_spacing(10)
        yesButton = gtk.GtkButton("Yes")
        hButtonBox.add(yesButton)
        yesButton.signal_connect("clicked", callback,
                                 warning, yesFunc, yesArgs)
        yesButton.show()
        noButton = gtk.GtkButton("No")
        hButtonBox.add(noButton)
        noButton.signal_connect("clicked", callback,
                                warning, noFunc, noArgs)
        noButton.show()
        hButtonBox.show()
    else:
        button = gtk.GtkButton("   OK   ")
        warning.action_area.pack_start(button, 0, 0, 0)
        button.signal_connect("clicked", warning.destroy)
        button.show()
    warning.show()

class XKeyswConfig:
    def __init__(self, file):
        self.file = file
        
        gladefile = "xkeysw-config.glade"
        gladefullpath = None
        for path in [".", "/usr/share/xkeysw"]:
            if os.path.exists( os.path.join(path, gladefile) ):
                gladefullpath = os.path.join(path, gladefile)
        if gladefullpath:
            self.wtree = libglade.GladeXML( gladefullpath )
        else:
            raise NameError, "Glade file " + gladefile + " is not found."

        nameFuncMap = {}
        for key in dir(self.__class__):
            nameFuncMap[key] = getattr(self, key)
        self.wtree.signal_autoconnect(nameFuncMap)

        self.wfilerc = self.wtree.get_widget("FileRC")
        self.wtreerc = self.wtree.get_widget("TreeRC")
        self.wlayoutconf = self.wtree.get_widget("LayoutConfig")
        self.wlname = self.wtree.get_widget("lname")
        self.wlprefix = self.wtree.get_widget("lprefix")
        self.wprefix = self.wtree.get_widget("prefix")
        self.wfile = self.wtree.get_widget("File")
        self.wlfile_combo = self.wtree.get_widget("lfileCombo")
        self.wselectxmm = self.wtree.get_widget("SelectXMM")
        self.whotkeys = self.wtree.get_widget("hotkeys")
        self.whotkey = self.wtree.get_widget("hotkey")
        self.wtarget_combo = self.wtree.get_widget("layoutCombo")
        self.wnewkey_combo = self.wtree.get_widget("newkeyCombo")
        
        self.kmap = {}
        xk_match = re.compile("^XK_.*")
        for keymap in Xlib.keysymdef.__all__:
            glob = {}
            loc = {}
            exec "from Xlib.keysymdef import " + keymap in glob,loc
            dict = loc[keymap].__dict__
            for key in dict.keys():
                if xk_match.match( key ):
                    val = dict[key]
                    self.kmap[val] = key

        # * Config represents the configuration we are working on. It is a list of
        #   layouts.
        # * Each layout is a dictionary with keywords "layout", "prefix", "xmmfile",
        #   and "hotkeys". All except "hotkeys" have a string associated with them.
        #    - Value of "layout" is layout name.
        #    - Value of "prefix" is layout prefix.
        #    - Value of "xmmfie" is a name of the XMM file.
        #    - Value of "hotkeys" is a list of hotkeys switching this layout into another
        # * Each hotkey is another dictionary with keywords "keysym", "newsym",
        #   and "layout".
        #    - Value of "keysym" is a Keysym switching the layout.
        #    - Value of "newsym" is a Keysym which will be used instead of "keysym"
        #    - Value of "layout" is a new layout this hotkey will switch to
        self.Config = []

        # GlobaAssign has association between a Keysym and a layout where it is
        # assigned to switch into layout GLOBAL. This hotkey should be unique
        # in the whole configuration.
        self.GlobalAssign = {}

        # OtherAssign has association between a Keysym and a list of layouts where
        # this key is used as a hotkey switching to layout other than GLOBAL.
        self.OtherAssign = {}

        # Index of the currently edited layout. -1 if this is brand new layout.
        self.layoutIndex = -1

        # isConfigEdited and isLayoutEdited keep track of updates.
        self.isConfigEdited = 0
        self.isLayoutEdited = 0

        # pkgpath is a list of directories xkeysw uses to look for XMM files when
        # they do not have complete path to them.
        self.pkgpath = [".", os.getenv("HOME"), "/usr/share/xkeysw"]
        if os.getenv("XKEYSW_DATAPATH"):
            self.pkgpath.insert(1, os.getenv("XKEYSW_DATAPATH"))

        self.wtree.get_widget("MainWindow").show()

    def on_MainWindow_show(self, obj):
        self.wfilerc.set_text(self.file)        
        self.on_LoadRC_clicked()

    def file_ok_sel(self, w, entry):
        file = self.filew.get_filename()
        if os.path.isfile(file):
            entry.set_text( file )
            self.filew.destroy()
        else:
            MessageBox("You should select a file and not a directory.\n")

    def select_file(self, title, entry):
        """Create a FileSelection Dialog with specified title and
        an Entry widget reference to put the result into."""
        
        self.filew = gtk.GtkFileSelection(title)
        # Connect the ok_button to file_ok_sel method
        self.filew.ok_button.connect("clicked", self.file_ok_sel, entry)
        # Connect the cancel_button to destroy the widget
        self.filew.cancel_button.connect_object("clicked", self.filew.destroy,
                                                self.filew)
        self.filew.set_filename(entry.get_text())
        self.filew.set_modal(1)
        self.filew.show()
    
    def on_SelectRC_clicked(self, obj):
        self.select_file("Xkeysw Resource File Selection", self.wfilerc)
        
    def on_SelectXMM_clicked(self, obj):
        self.select_file("XMM File Selection", self.wlfile_combo.entry)

    def insertNewLayout(self, index, layout):
        """Insert new layout into an existing Tree representing configuration.
        Negative index means that layout should be appended. Otherwise, it
        should replace existing index Tree child with this new layout."""
        
        wlayout = gtk.GtkTreeItem("layout " + layout["layout"])
        if index >= 0:
            subtrees = self.wtreerc.children()
            self.wtreerc.remove_items(subtrees[index:index+1])
            self.wtreerc.insert(wlayout, index)
            self.wtreerc.select_item(index)
        else:
            self.wtreerc.append(wlayout)
        wlayout.show()

        wtree = gtk.GtkTree()
        wlayout.set_subtree(wtree)
        wlayout.expand()
        if layout.has_key("prefix") and len(layout["prefix"]) > 0:
            item = gtk.GtkTreeItem("prefix = \"" + layout["prefix"] + "\"")
            wtree.append(item)
            item.show()
        if layout.has_key("xmmfile") and len(layout["xmmfile"]) > 0:
            item = gtk.GtkTreeItem("xmmfile = \"" + layout["xmmfile"] + "\"")
            wtree.append(item)
            item.show()
        if layout.has_key("hotkeys"):
            for hotkey in layout["hotkeys"]:
                str = "hotkey = " + hotkey["keysym"]
                if hotkey.has_key("newsym") and len(hotkey["newsym"]) > 0:
                    str = str + "(" + hotkey["newsym"] + ")"
                str = str + " " + hotkey["layout"]
                item = gtk.GtkTreeItem(str)
                wtree.append(item)
                item.show()
        wtree.signal_connect("select_child", self.select_parent, wlayout)
        wtree.show()
        
    def select_parent(self, w, obj, parent):
        self.wtreerc.select_child(parent)

    def updateConfigEdited(self, update):
        """Callback for passing into MessageBox.
        Parameter update is a list: first element is integer and second is function."""
        
        self.isConfigEdited = update[0]
        func = update[1]
        func()

    def LoadingRCFile(self, filename = None):
        """Loading Resource File and saving its representation into self.Config."""
        
        new_config = []
        if filename:
            try:
                file = open(filename, "r")
            except IOError, (errno, strerror):
                MessageBox(filename + ": I/O error(" + str(errno) + "): " +
                           strerror + "\n")
                return

            layout = {"layout": "DEFAULT"}
            hotkeys = []
            hotkey = {}
            linenum = 0
            key_token = None
            key = None
            filestr = file.readline()
            re1 = re.compile("(\".*\")")
            re2 = re.compile("[ \t\n;:=,<>]+")
            while filestr:
                linenum = linenum + 1
                if filestr[-1:] == "\n":
                    filestr = filestr[:-1]
                comment = string.find(filestr, "#")
                if comment >= 0:
                    filestr = filestr[:comment]
                alltokens = re1.split(filestr)
                tokens = []
                for token in alltokens:
                    if token[:1] == token[-1:] == "\"":
                        tokens.append(token[1:-1])
                    else:
                        tokens.extend(re2.split(token))
                def nonempty(str): return len(str) > 0
                tokens = filter(nonempty, tokens)
                while len(tokens) > 0:
                    if not key_token:
                        key_token = tokens.pop(0)
                        if len(tokens) == 0:
                            break
                    if key_token == "prefix" or key_token == "xmmfile":
                        layout[key_token] = tokens.pop(0)
                        key_token = None
                        continue
                    if key_token == "hotkey":
                        if not key:
                            key = tokens.pop(0)
                            if len(tokens) == 0:
                                break
                        hotkey["layout"] = tokens.pop(0)
                        sep = string.find(key, "(")
                        if sep >=0 and key[-1:] == ")":
                            hotkey["keysym"] = key[:sep]
                            hotkey["newsym"] = key[sep+1:-1]
                        else:
                            hotkey["keysym"] = key
                        hotkeys.append(hotkey.copy())
                        key = None
                        hotkey = {}
                        key_token = None
                        continue
                    if key_token == "layout":
                        if len(hotkeys) > 0:
                            layout["hotkeys"] = hotkeys[:]
                        new_config.append(layout.copy())
                        layout = {}
                        layout["layout"] = tokens.pop(0)
                        hotkeys = []
                        key_token = None
                        continue
                    file.close()
                    message = "File " + filename + " does not look like an " + \
                              "xkeysw resource to me on line " + str(linenum) + \
                              ".\n"
                    if len(self.Config) > 0:
                        MessageBox(message)
                    else:
                        MessageBox(message + "Continue with an empty configuration?\n",
                                   self.LoadingRCFile, None, self.Exiting)
                    return
                
                filestr = file.readline()
                
            if len(hotkeys) > 0:
                layout["hotkeys"] = hotkeys
            new_config.append(layout.copy())
            file.close()
            
            # Collect all assignments to GLOBAL layout;
            self.GlobalAssign = {}
            self.OtherAssign = {}
            for layout in new_config:
                if layout.has_key("hotkeys"):
                    for key in layout["hotkeys"]:
                        if key["layout"] == "GLOBAL":
                            if not self.GlobalAssign.has_key(key["keysym"]):
                                self.GlobalAssign[key["keysym"]] = layout["layout"]

            # Delete all dublicated assignments in layouts and to GLOBAL layout
            for layout in new_config:
                if layout.has_key("hotkeys"):
                    LocalAssign = {}
                    for key in layout["hotkeys"]:
                        if LocalAssign.has_key(key["keysym"]):
                            MessageBox("Hotkey " + key["keysym"] +
                                       " present twice in layout " +
                                       layout["layout"] + ". Second " +
                                       "occurance is deleted.\n");
                            layout["hotkeys"].remove(key)
                            continue
                        if (self.GlobalAssign.has_key(key["keysym"]) and
                            self.GlobalAssign[key["keysym"]] != layout["layout"]):
                            MessageBox("Hotkey " + key["keysym"] + " was " +
                                       "assigned to layout GLOBAL in layout " +
                                       self.GlobalAssign[key["keysym"]] +
                                       ". Second occurance in layout " +
                                       layout["layout"] + " is deleted.\n")
                            layout["hotkeys"].remove(key)
                        if not self.GlobalAssign.has_key(key["keysym"]):
                            if self.OtherAssign.has_key(key["keysym"]):
                                self.OtherAssign[key["keysym"]].append(layout["layout"]);
                            else:
                                self.OtherAssign[key["keysym"]] = [layout["layout"]];

        else: # No file specified
            new_config = [{"layout": "DEFAULT"}]

        self.wtreerc.remove_items(self.wtreerc.children())
        self.Config = new_config[:]

        for layout in self.Config:
            self.insertNewLayout(-1, layout)
        self.wtreerc.show()
        self.isConfigEdited = 0

    def on_LoadRC_clicked(self, obj=None):
        if self.isConfigEdited:
            if self.isConfigEdited == 2:
                self.isConfigEdited = 1
            else:
                MessageBox("Updated configuration was not saved. " +
                           "Discard changes?\n", self.updateConfigEdited,
                           [2, self.on_LoadRC_clicked])
                return

        filename = self.wfilerc.get_text()

        if not os.path.exists(filename):
            noFunc = None
            if len(self.Config) == 0:
                noFunc = self.Exiting
            MessageBox("File " + filename + " does not exists. " +
                       "Continue with an empty configuration?",
                       self.LoadingRCFile, None, noFunc)
            return

        if os.path.isdir(filename):
            MessageBox(filename + " should be a file and not a directory.\n")
            return
        
        self.LoadingRCFile(filename)

    def SavingRCFile(self, filename):
        """Saving Resource File into the file."""
        
        try:
            file = open(filename, "w")
        except IOError, (errno, strerror):
            MessageBox(filename + ": I/O error(" + str(errno) + "): " + strerror + "\n")
            return

        file.write("# XKeysw resource file generated by xkeysw-config\n")
        file.write("# layout <layoutname>\n")
        file.write("# prefix \"<string>\"\n")
        file.write("# xmmfile <filename>\n")
        file.write("# hotkey KeySym <layoutname>\n\n")

        for layout in self.Config:
            if layout["layout"] != "DEFAULT":
                file.write("layout " + layout["layout"] + "\n")
                if layout.has_key("prefix") and len(layout["prefix"]) > 0:
                    file.write("\tprefix = \"" + layout["prefix"] + "\"\n")
                if layout.has_key("xmmfile") and len(layout["xmmfile"]) > 0:
                    file.write("\txmmfile = \"" + layout["xmmfile"] + "\"\n")
            if layout.has_key("hotkeys"):
                for hotkey in layout["hotkeys"]:
                    file.write("\thotkey = " + hotkey["keysym"])
                    if hotkey.has_key("newsym") and len(hotkey["newsym"]) > 0:
                        file.write("(" + hotkey["newsym"] + ")")
                    file.write(" " + hotkey["layout"] + "\n")
            file.write("\n");

        file.close()
        self.isConfigEdited = 0

    def on_SaveRC_clicked(self, obj=None):
        if not self.isConfigEdited:
            MessageBox("Configuration was not changed. Save anyway?\n",
                       self.updateConfigEdited, [3, self.on_SaveRC_clicked])
            return
        else:
            if self.isConfigEdited == 3:
                self.isConfigEdited = 0

        filename = self.wfilerc.get_text()

        if os.path.exists(filename):
            MessageBox("File " + filename + " exists. Overwite?\n",
                       self.SavingRCFile, filename)
            return

        self.SavingRCFile(filename)

    def Exiting(self):
        gtk.mainquit()        
        
    def on_Exit_clicked(self, obj):
        if self.isConfigEdited:
            MessageBox("Changes to the configuration were not saved. " +
                       "Exit anyway?\n", self.Exiting)
            return

        self.Exiting()

    def showLayoutConfig(self, index):
        """Preparation of the Layout window before showing it for editing.
        Parameter index is an index of layout in self.Config.
        If it is less than zero this is fresh new layout."""
        
        self.layoutIndex = index
        
        # Setup the list of XMM files.
        filehash = {}
        for dir in self.pkgpath:
            if os.path.exists(dir):
                for file in os.listdir(dir):
                    if re.match(".*\.xmm$", file):
                        filehash[file] = 1
        filelist = filehash.keys()
        if len(filelist) > 0:
            filelist.sort()
            self.wlfile_combo.set_popdown_strings(filelist)

        # Setup the list of defined Layouts
        layouts = ["GLOBAL"]
        for layout in self.Config:
            layouts.append(layout["layout"])
        self.wtarget_combo.set_popdown_strings(layouts)

        #Setup entries for new hotkeys
        self.whotkey.set_text("")
        self.wnewkey_combo.set_popdown_strings(["", "NoSymbol"])
        self.wnewkey_combo.entry.set_text("")

        if index >= 0:
            curLayout = self.Config[index]
        else:
            curLayout = {}
            
        if curLayout.has_key("layout"):
            self.wlname.set_text(curLayout["layout"])
        else:
            self.wlname.set_text("")
        if curLayout.has_key("prefix"):
            self.wlprefix.set_text(curLayout["prefix"])
        else:
            self.wlprefix.set_text("")
        if curLayout.has_key("xmmfile"):
            self.wlfile_combo.entry.set_text(curLayout["xmmfile"])
        else:
            self.wlfile_combo.entry.set_text("")

        self.whotkeys.clear()
        if curLayout.has_key("hotkeys"):
            for hotkey in curLayout["hotkeys"]:
                entry = [hotkey["keysym"], "", hotkey["layout"]]
                if hotkey.has_key("newsym"):
                    entry[1] = hotkey["newsym"]
                self.whotkeys.append(entry)

        if curLayout.has_key("layout") and curLayout["layout"] == "DEFAULT":
            self.wlfile_combo.hide()
            self.wselectxmm.hide()
            self.wfile.hide()
            self.wlname.set_editable(0)
            self.wprefix.hide()
            self.wlprefix.hide()
        else:
            self.wlfile_combo.show()
            self.wselectxmm.show()
            self.wfile.show()
            self.wlname.set_editable(1)
            self.wprefix.show()
            self.wlprefix.show()

        self.isLayoutEdited = 0
        self.wlayoutconf.show()
        
    def on_AddConfig_clicked(self, obj):
        self.showLayoutConfig(-1)
        
    def on_EditConfig_clicked(self, obj):
        items = self.wtreerc.get_selection()
        if len(items) > 0:
            self.showLayoutConfig(self.wtreerc.child_position(items[0]))
        
    def on_RemoveConfig_clicked(self, obj):
        items = self.wtreerc.get_selection()
        if len(items) > 0 and self.wtreerc.child_position(items[0]) > 0:
            index = self.wtreerc.child_position(items[0])
            
            if self.Config[index].has_key("hotkeys"):
                for key in self.Config[index]["hotkeys"]:
                    if key["layout"] == "GLOBAL":
                        del self.GlobalAssign[key["keysym"]]
                    else:
                        for layout in self.OtherAssign[key["keysym"]]:
                            if layout == self.Config[index]["layout"]:
                                self.OtherAssign[key["keysym"]].remove(layout)
                                break
                        if len(self.OtherAssign[key["keysym"]]) == 0:
                            del self.OtherAssign[key["keysym"]]

            self.Config[index:index+1] = []
            self.wtreerc.remove_item(items[0])
            self.isConfigEdited = 1
        
    def on_LayoutOK_clicked(self, obj):
        """Verify resulting layout on correctness and insert into self.Config."""
        
        if self.layoutIndex >= 0:
            old_layout = self.Config[self.layoutIndex]
            if ((old_layout.has_key("layout") and
                 self.wlname.get_text() != old_layout["layout"]) or
                (old_layout.has_key("xmmfile") and
                 self.wlfile_combo.entry.get_text() != old_layout["xmmfile"]) or
                (old_layout.has_key("prefix") and
                 self.wlprefix.get_text() != old_layout["prefix"])):
                self.isLayoutEdited = 1
        else:
            if (len(self.wlname.get_text()) > 0 or
                len(self.wlfile_combo.entry.get_text()) > 0 or
                len(self.wlprefix.get_text()) > 0):
                self.isLayoutEdited = 1

        if self.isLayoutEdited and len(self.wlname.get_text()) > 0:
            curLayout = {}
            curLayout["layout"] = self.wlname.get_text()

            for index in range(len(self.Config)):
                if index == self.layoutIndex:
                    continue
                if self.Config[index]["layout"] == curLayout["layout"]:
                    MessageBox("Layout " + curLayout["layout"] +
                               " already present in the configuration. " +
                               "Please, change the name.\n")
                    return

            if curLayout["layout"] != "DEFAULT":
                curLayout["xmmfile"] = self.wlfile_combo.entry.get_text()
                curLayout["prefix"] = self.wlprefix.get_text()

            if( self.layoutIndex >= 0 and
                self.Config[self.layoutIndex].has_key("hotkeys") ):
                for key in self.Config[self.layoutIndex]["hotkeys"]:
                    if key["layout"] == "GLOBAL":
                        del self.GlobalAssign[key["keysym"]]
                    else:
                        for layout in self.OtherAssign[key["keysym"]]:
                            if layout == self.Config[self.layoutIndex]["layout"]:
                                self.OtherAssign[key["keysym"]].remove(layout)
                                break
                        if len(self.OtherAssign[key["keysym"]]) == 0:
                            del self.OtherAssign[key["keysym"]]

            curLayout["hotkeys"] = []
            for index in range(self.whotkeys.rows):
                new_hotkey = {}
                new_hotkey["keysym"] = self.whotkeys.get_text(index,0)
                new_hotkey["newsym"] = self.whotkeys.get_text(index,1)
                new_hotkey["layout"] = self.whotkeys.get_text(index,2)
                if self.GlobalAssign.has_key(new_hotkey["keysym"]):
                    MessageBox("Hotkey " + new_hotkey["keysym"] + " is " +
                               "already used for GLOBAL layout in layout " +
                               self.GlobalAssign[new_hotkey["keysym"]] +
                               ". Assignment in " + curLayout["layout"] +
                               " will be deleted.\n")
                    continue
                if new_hotkey["layout"] == "GLOBAL":
                    self.GlobalAssign[new_hotkey["keysym"]] = curLayout["layout"]
                else:
                    if self.OtherAssign.has_key(new_hotkey["keysym"]):
                        self.OtherAssign[new_hotkey["keysym"]].append(curLayout["layout"])
                    else:
                        self.OtherAssign[new_hotkey["keysym"]] = [curLayout["layout"]]
                curLayout["hotkeys"].append(new_hotkey.copy())

            if self.layoutIndex >= 0:
                self.Config[self.layoutIndex] = curLayout.copy()
            else:
                self.Config.append(curLayout.copy())

            self.insertNewLayout(self.layoutIndex, curLayout)
            self.isConfigEdited = 1
            
        self.wlayoutconf.hide()
        
    def on_LayoutCancel_clicked(self, obj):
        self.wlayoutconf.hide()
        
    def insertNewKey(self, index):
        """Insert new key into the list of hotkeys. If index less than zero this is
        a fresh new key. Otherwise, update index key in the list."""
        
        new_hotkey = self.whotkey.get_text()
        new_layout = self.wtarget_combo.entry.get_text()
        if len(new_hotkey) > 0 and len(new_layout) > 0:
            entry = [new_hotkey,
                     self.wnewkey_combo.entry.get_text(),
                     new_layout]
            
            for i in range(self.whotkeys.rows):
                if i == index:
                    continue
                if self.whotkeys.get_text(i,0) == new_hotkey:
                    MessageBox("Hotkey " + new_hotkey +
                               " already exists in this layout.\n")
                    return

            if self.layoutIndex >= 0:
                old_layout = self.Config[self.layoutIndex]["layout"]
            if (self.GlobalAssign.has_key(new_hotkey) and
                (self.layoutIndex < 0 or
                 self.GlobalAssign[new_hotkey] != old_layout)):
                MessageBox("Hotkey " + new_hotkey + " already is used in " +
                           self.GlobalAssign[new_hotkey] +
                           " layout to switch to GLOBAL.\n")
                return
            if new_layout == "GLOBAL" and self.OtherAssign.has_key(new_hotkey):
                for layout in self.OtherAssign[new_hotkey]:
                    if self.layoutIndex < 0 or layout != old_layout:
                        MessageBox("Hotkey " + new_hotkey + " already is " +
                                   "used in some other layout and cannot " +
                                   "be used to switch to layout GLOBAL.\n")
                        return
                
            if index >= 0:
                self.whotkeys.remove(index)
                self.whotkeys.insert(index, entry)
                self.whotkeys.select_row(index,0)
            else:
                self.whotkeys.append(entry)
            self.isLayoutEdited = 1

    def on_AddKey_clicked(self, obj):
        self.insertNewKey(-1)
        
    def on_UpdateKey_clicked(self, obj):
        if len(self.whotkeys.selection) > 0:
            index = self.whotkeys.selection[0]
            if index >= 0:
                self.insertNewKey(index)
        
    def on_DelKey_clicked(self, obj):
        if len(self.whotkeys.selection) > 0:
            index = self.whotkeys.selection[0]
            if index >= 0:
                self.whotkeys.remove(index)
                self.isLayoutEdited = 1
            
    def on_hotkeys_select_row(self,obj,r,c,data):
        self.whotkey.set_text(self.whotkeys.get_text(r,0))
        self.wnewkey_combo.entry.set_text(self.whotkeys.get_text(r,1))
        self.wtarget_combo.entry.set_text(self.whotkeys.get_text(r,2))

    def on_hotkey_key_press_event( self, obj, key ):
        modifiers = {
            "Shift": 1,
            "Lock": 2,
            "Control": 4,
            "Mod1": 8,
            "Mod2": 16,
            "Mod3": 32,
            "Mod4": 64,
            "Mod5": 128,
            "Button1": 256,
            "Button2": 512,
            "Button3": 1024,
            "Button4": 2048,
            "Button5": 4096
            }
        modstr = ""
        for mod in modifiers.keys():
            if (modifiers[mod] & key.state):
                modstr = modstr + mod + "+"
        keysym = self.kmap[key.keyval]
        self.whotkey.set_text(modstr + keysym[3:])

    def on_newsym_key_press_event( self, obj, key ):
        keysym = self.kmap[key.keyval]
        self.wnewkey_combo.entry.set_text(keysym[3:])

if len(sys.argv) > 1:
    fname = sys.argv[1]
else:
    fname = os.path.join(os.getenv("HOME"), ".xkeyswrc")

try:
    l = XKeyswConfig(fname)
    gtk.mainloop ()
except NameError, message:
    print sys.argv[0] + ":", message
