Add GUI for editing keys. To call it, just right-click a button.

portnov [2008-08-27 16:04:47]
Add GUI for editing keys. To call it, just right-click a button.
Filename
qwerty.py
diff --git a/qwerty.py b/qwerty.py
index 3cbba9e..d0a99b0 100755
--- a/qwerty.py
+++ b/qwerty.py
@@ -4,6 +4,7 @@
 import sys
 import os
 import re
+import time

 from Xlib import X,display,Xatom
 import Xlib.protocol.event
@@ -21,6 +22,9 @@ SHIFT =   1
 CONTROL = 2
 ALT     = 4

+OK = True
+CANCEL = False
+
 PADDING = 4
 ICON_TOP_MARGIN = 35
 LABEL_LEFT, LABEL_TOP = 15,25
@@ -221,16 +225,33 @@ class SectionButton(SuperButton):
             cr.set_font_size(16)
             cr.show_text(self.label)

-
 class Section(object):
     def __init__(self,title):
         self.title = title
         self.keys = []
+        self.keydict = {}

 class Key(object):
     def __init__(self,key):
         self.key = key
         self.mods = []
+
+    def __repr__(self):
+        return "<Key %s: %s>" % (self.key, self.title)
+
+def dict_from_keylist(keys):
+        return dict([((k.key,tuple(k.mods)), k) for k in keys])
+
+def parse_mods(s):
+    r = []
+    ms = s.upper().split()
+    if 'SHIFT' in ms:
+        r.append(SHIFT)
+    if ('CONTROL' in ms) or ('CTRL' in ms):
+        r.append(CONTROL)
+    if 'ALT' in ms:
+        r.append(ALT)
+    return r

 def parse_config():
     global sections
@@ -246,17 +267,6 @@ def parse_config():
         if line.startswith(key):
             return line[len(key):].strip()

-    def parse_mods(s):
-        r = []
-        ms = s.upper().split()
-        if 'SHIFT' in ms:
-            r.append(SHIFT)
-        if ('CONTROL' in ms) or ('CTRL' in ms):
-            r.append(CONTROL)
-        if 'ALT' in ms:
-            r.append(ALT)
-        return r
-
     context = GLOBAL
     for line in open(config_file):
         line = line.strip()
@@ -344,6 +354,18 @@ def parse_config():
                 raise SyntaxError, "Unexpected 'Command' key in config"
             continue
     sections = dict([(s.order, s) for s in ts])
+    for s in sections.values():
+        s.keydict = dict_from_keylist(s.keys)
+
+def mods_str(ms):
+    r = ''
+    if SHIFT in ms:
+        r += 'Shift '
+    if CONTROL in ms:
+        r += 'Control '
+    if ALT in ms:
+        r += 'Alt'
+    return r

 def save_config():
     def write(key,value):
@@ -352,16 +374,6 @@ def save_config():
     def writeln():
         cf.write('\n')

-    def mods_str(ms):
-        r = ''
-        if SHIFT in ms:
-            r += 'Shift '
-        if CONTROL in ms:
-            r += 'Control '
-        if ALT in ms:
-            r += 'Alt'
-        return r
-
     global sections
     global defsection
     global speckeys
@@ -375,7 +387,7 @@ def save_config():
         write('Order', S.order)
         writeln()

-        for K in S.keys:
+        for K in S.keydict.values():
             write('Key', K.key)
             if K.mods:
                 write('Mods', mods_str(K.mods))
@@ -395,6 +407,104 @@ def save_config():

     cf.close()

+class LabeledEntry(gtk.HBox):
+    def __init__(self,label):
+        gtk.HBox.__init__(self)
+        self.set_spacing(5)
+        lbl = gtk.Label(label+": ")
+        self.entry = gtk.Entry()
+        self.pack_start(lbl, False)
+        self.pack_start(self.entry, True)
+
+    def get_text(self):
+        return self.entry.get_text()
+
+    def set_text(self,text):
+        self.entry.set_text(text)
+
+class KeyEditDialog(object):
+    def __init__(self):
+        self.return_code = CANCEL
+        self.exit = False
+        self.callback = None
+
+        self.window = gtk.Window()
+        self.window.set_title('Edit key')
+        self.window.connect('destroy', self.on_cancel)
+        self.window.set_border_width(5)
+
+        self.ed_key   = LabeledEntry('Key')
+        self.ed_mods  = LabeledEntry('Mods')
+        self.ed_title = LabeledEntry('Title')
+        self.ed_desc  = LabeledEntry('Description')
+        self.ed_icon  = LabeledEntry('Icon')
+        self.ed_class = LabeledEntry('Class')
+        self.ed_command = LabeledEntry('Command')
+
+        vb = gtk.VBox()
+        vb.set_spacing(3)
+        for box in [self.ed_key, self.ed_mods, self.ed_title, self.ed_desc, self.ed_icon, self.ed_class, self.ed_command]:
+            vb.pack_start(box, False)
+        ok = gtk.Button(stock=gtk.STOCK_OK)
+        ok.connect('clicked', self.on_ok)
+        cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
+        cancel.connect('clicked', self.on_cancel)
+        okbox = gtk.HBox()
+        okbox.set_spacing(5)
+        okbox.pack_end(cancel, False)
+        okbox.pack_end(ok, False)
+        main = gtk.VBox()
+        main.pack_start(vb, True)
+        main.pack_start(okbox, False)
+        self.window.add(main)
+        self.window.set_size_request(400,300)
+
+    def show(self,G):
+        self.window.show_all()
+        self.G = G
+
+    def hide(self):
+        self.exit = True
+        self.window.hide()
+        K = self.get_key()
+        if not K:
+            return
+        self.G.S.keydict[K.key,tuple(K.mods)] = K
+        self.G.show_section()
+
+    def set_key(self,K):
+        self.ed_key.set_text(K.key)
+        self.ed_mods.set_text(mods_str(K.mods))
+        self.ed_title.set_text(K.title)
+        if hasattr(K,'desc'):
+            self.ed_desc.set_text(K.desc)
+        self.ed_icon.set_text(K.icon)
+        if hasattr(K,'cls'):
+            self.ed_class.set_text(K.cls)
+        self.ed_command.set_text(K.command)
+
+    def get_key(self):
+        K = Key(self.ed_key.get_text())
+        K.mods = parse_mods(self.ed_mods.get_text())
+        K.title = self.ed_title.get_text()
+        if not K.title:
+            return None
+        d = self.ed_desc.get_text()
+        if d:
+            K.desc = d
+        K.icon = self.ed_icon.get_text()
+        d = self.ed_class.get_text()
+        if d:
+            K.cls = d
+        K.command = self.ed_command.get_text()
+        return K
+
+    def on_ok(self,widget):
+        self.return_code = OK
+        self.hide()
+
+    def on_cancel(self,widget):
+        self.hide()

 class GUI(object):
     def __init__(self):
@@ -491,7 +601,6 @@ class GUI(object):
             for cls in self.convert_class(clss):
                 self.by_class[cls] = w

-
     def fill_taskbar(self):
         def rm(widget):
             self.taskbar.remove(widget)
@@ -567,7 +676,6 @@ class GUI(object):
         return self.key_in(kv,names)

     def key_released(self,widget,event):
-
         if self.key_in_mods(event.keyval):
             mods = self.translate_mods(event)

@@ -614,9 +722,15 @@ class GUI(object):
         elif k in ALPHABET.lower():
             self.do_button(k, mods)

+    def Key_by_key(self,key,mods=[]):
+        ks = self.S.keydict
+        try:
+            return ks[key, tuple(mods)]
+        except KeyError:
+            return None
+
     def key_exec(self,key,mods):
-        ks = dict([((k.key,tuple(k.mods)), k) for k in self.S.keys])
-        cmd = ks[key,tuple(mods)].command
+        cmd = self.Key_by_key(key,mods).command
         os.system(cmd + " &")

     def do_button(self,key, mods):
@@ -650,7 +764,20 @@ class GUI(object):
         else:
             os.system(key.command + ' &')

+    def edit_key(self,K,key=''):
+        ed = KeyEditDialog()
+        if K:
+            ed.set_key(K)
+        else:
+            ed.ed_key.set_text(key)
+        ed.show(self)
+
     def on_button(self,widget,event=None,mods=[]):
+        if event and event.button == 3:
+            K = self.Key_by_key(widget.key, mods)
+            self.edit_key(K, widget.key)
+            return
+
         if widget.running:
             self.activate_window(widget.cls)
         else:
@@ -666,9 +793,14 @@ class GUI(object):
         for b in self.buttons.values():
             b.queue_draw()

-    def show_section(self,k,mods):
+    def show_section(self,k=None,mods=None):
         global sections
         global defsection
+
+        if not k:
+            k = (self.S.order+1) % 10
+        if not mods:
+            mods = []

         self.collect_windows()

@@ -688,7 +820,7 @@ class GUI(object):
             btn = self.buttons[b]
             btn.clear()

-        for K in self.S.keys:
+        for K in self.S.keydict.values():
             if K.mods != mods:
                 continue
             k = K.key
ViewGit