Add openbox-keyconf from Sidux rev 783
[lxde/lxadmin.git] / src / openbox-keyconf / openbox-keyconf
1 #!/usr/bin/python
2 # -*- coding: UTF-8 -*-
3 #**************************************************************************
4 #                                                                         *
5 #   Copyright (c) 2010 by Elfriede Apfelkuchen                            *
6 #                                                                         *
7 #   This program is free software: you can redistribute it and/or modify  *
8 #   it under the terms of the GNU General Public License as published by  *
9 #   the Free Software Foundation, either version 3 of the License, or     *
10 #   (at your option) any later version.                                   *
11 #                                                                         *
12 #   This program is distributed in the hope that it will be useful,       *
13 #   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15 #   GNU General Public License for more details.                          *
16 #                                                                         *
17 #   You should have received a copy of the GNU General Public License     *
18 #   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
19 #                                                                         *
20 #**************************************************************************
21
22 helptext="""M = [Mod3]
23 W = [Windows]
24 S = [Shift]
25 A = [Alt]
26 C = [Control]
27
28 F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12
29 Escape
30 Return
31 Menu
32 Tab
33 Pause
34 space
35 Print
36 Caps_Lock
37 Scroll_Lock
38 Num_Lock
39 Insert
40 Delete
41 BackSpace
42 Home  = [Pos1]
43 End   = [End]
44 Prior = [PageUp]
45 Next  = [PageDown]
46 asciicircum =  [^]
47 less        =  [<]
48 greater     =  [>]
49 minus       =  [-]
50 plus        =  [+]
51 comma       =  [,]
52 period      =  [.]
53 numbersign  =  [#]
54 acute       =  [´]
55
56 Left,Right,Up,Down
57 _____________________
58
59 [Keypad/Numberblock]
60
61 KP_0, KP_1, KP_2, KP_3, KP_4, KP_5, KP_6, KP_7, KP_8, KP_9
62 KP_Divide   = [/]
63 KP_Multiply = [*]
64 KP_Subtract = [-]
65 KP_Add      = [+]
66 KP_Enter
67 KP_Begin  = [5]
68 KP_Home   = [7]
69 KP_End    = [1]
70 KP_Prior  = [9] [PageUp]
71 KP_Next   = [3] [PageDown]
72 KP_Up     = [8] [cursor up]     
73 KP_Down   = [2] [cursor down]
74 KP_Left   = [4] [cursor left]
75 KP_Right  = [6] [cursor right]
76
77 for more keycodes start "xev" in a terminal"""
78
79
80
81
82 import locale
83 locale.setlocale(locale.LC_ALL, '')           #set locale from 'LANG'
84 Xcodec=locale.getpreferredencoding(False)
85 import os
86 import sys
87 import time
88 import string
89 import codecs
90 import pygtk
91 pygtk.require('2.0')
92 import gtk
93 import pango
94 import gobject
95 import gc
96
97 configfile=".config/openbox/lxde-rc.xml"
98 mybuffer=None
99 helpwindow=None
100 I18N={}
101
102 def i18n(text):
103     if I18N_ready:
104        if I18N.has_key(text): return I18N[text]
105        else: return text
106     else: return text
107
108 def load_i18n(filename):
109      #lädt Internationalisierung.moo file
110      ready=False
111      lang=os.getenv('LANG')
112      if not lang: return ready
113      lang2='/usr/share/locale/'+lang[0:2] +'/LC_MESSAGES/'+filename
114      lang3='/usr/share/locale/'+lang[0:5] +'/LC_MESSAGES/'+filename
115      lang='no file'
116      if   os.access(lang3,os.R_OK): lang=lang3
117      elif os.access(lang2,os.R_OK): lang=lang2
118      if lang != 'no file':
119         f=codecs.open(lang,'r','utf_8')
120         if f:
121            lang=f.readlines()
122            f.close()
123            ready=True
124            for z in lang:
125                s=z.replace('\n','').split('=')
126                if s[0]=='': continue
127                I18N[s[0]]=s[1]
128      return ready
129
130
131 def create_help_view(data=None):
132     global helpwindow,mybuffer
133     if mybuffer!=None: 
134        helpwindow.show()
135        helpwindow.present()
136        return
137     helpwindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
138     helpwindow.set_title(i18n('Help: keyboard codes'))
139     helpwindow.set_size_request(600,400)
140     helpwindow.set_position(gtk.WIN_POS_CENTER_ALWAYS)
141     helpwindow.connect("destroy", destroy_help_view)
142     view=gtk.TextView()
143     view.set_editable(False)
144     view.set_cursor_visible(False)
145     view.set_pixels_above_lines(0)
146     view.set_pixels_below_lines(0)
147     view.set_pixels_inside_wrap(0)
148     view.set_wrap_mode(gtk.WRAP_NONE)
149     font_desc = pango.FontDescription('Monospace 12')
150     if font_desc:
151        view.modify_font(font_desc)
152     sw = gtk.ScrolledWindow()
153     sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
154     sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
155     sw.add(view)
156     mybuffer=view.get_buffer()
157     mybuffer.set_text(helptext)
158     helpwindow.add(sw) 
159     helpwindow.show_all()
160     
161
162 def destroy_help_view(widget, data=None):
163     global helpwindow,mybuffer
164     helpwindow.destroy()
165     helpwindow=None
166     mybuffer=None
167
168
169
170 class Tool:
171
172     def delete_event(self, widget, event, data=None):
173         # If you return FALSE in the "delete_event" signal handler,
174         # GTK will emit the "destroy" signal. Returning TRUE means
175         # you don't want the window to be destroyed.
176         # This is useful for popping up 'are you sure you want to quit?'
177         # type dialogs.
178         # Change FALSE to TRUE and the main window will not be destroyed
179         # with a "delete_event".
180         windowsize=self.window.get_size()
181         windowpos=self.window.get_position()
182         filenamen=home+'/.openbox-keyconf'
183         f=file(filenamen,'w')
184         if f:
185            f.write('x=%i\n'      % windowpos[0])
186            f.write('y=%i\n'      % windowpos[1])
187            f.write('width=%i\n'  % windowsize[0])
188            f.write('height=%i\n' % windowsize[1])
189            f.close()
190         return False
191
192
193     def destroy(self, widget, data=None):
194         #wird beim beenden ausgeführt
195         gtk.main_quit()
196
197
198     def goodbye(self,data=None):
199         #Cancel Button is clicked
200         if not self.delete_event(self.window,data):
201            gtk.main_quit()
202
203
204     def get_pathnumber(self,keycode):
205         # return pathnumber of a given keycode
206         for i in range(self.count_list):
207             if self.liststore[i][0]==keycode: break
208         if self.liststore[i][0]!=keycode: return -1  
209         if self.liststore[i][3]=='add':   return -1
210         return i
211
212
213     def save(self,data=None):
214         #Apply button is clicked
215         filename=home+'/'+configfile
216         text=''
217         try:
218            f=codecs.open(filename,'r','utf_8')
219            text=f.read()
220            config=text.split('\n')
221            f.close()
222         except:
223            print "can't read",filename 
224         for i in range(self.count_list):
225             a=self.liststore[i][1]
226             b=self.liststore[i][2]
227             c=self.liststore[i][3]
228             d=self.liststore[i][0]
229             if c!='orginal':
230                if c=='delete': print c,d 
231                else: print a,b
232         gc.collect()
233
234         f=codecs.open(filename,'w','utf_8')
235         found=False
236         cutting=False 
237         for line in config:
238             if '<keyboard>' in line:
239                found=True
240                f.write(line+'\n')
241                continue
242             if '</keyboard>' in line:
243                found=False
244                # add keybinding code
245                for i in range(self.count_list): 
246                    s=self.liststore[i][3]
247                    if s=='add':
248                       f.write('    <keybind key="'+self.liststore[i][1]+'">\n')
249                       a=self.liststore[i][2]
250                       a=a.split('command=')
251                       b=a[1].split(',)')
252                       f.write('      <action name="Execute">\n')
253                       f.write('        <command>'+b[0]+'</command>\n')
254                       f.write('      </action>\n')
255                       f.write('    </keybind>\n')
256                f.write(line+'\n')
257                continue
258             if not found:
259                f.write(line+'\n')
260                continue
261             if cutting:
262                if '</keybind>' in line: cutting=False
263                continue    
264             if '<keybind ' in line: 
265                lin=line.split('"')
266                p=self.get_pathnumber(lin[1])
267                if p==-1:
268                   f.write(line+'\n')
269                   continue
270                s=self.liststore[p][3]
271                if s=='orginal':
272                   f.write(line+'\n')
273                   continue
274                if s=='delete':
275                   cutting=True
276                   continue 
277                if s=='name':
278                   f.write('    <keybind key="'+self.liststore[p][1]+'">\n')
279                   continue
280                if s=='execute':
281                   f.write('    <keybind key="'+self.liststore[p][1]+'">\n')
282                   a=self.liststore[p][2]
283                   a=a.split('command=')
284                   b=a[1].split(',)')
285                   f.write('      <action name="Execute">\n')
286                   f.write('        <command>'+b[0]+'</command>\n')
287                   f.write('      </action>\n')
288                   f.write('    </keybind>\n')
289                   cutting=True
290                   continue
291             f.write(line+'\n')
292         f.flush()
293         f.close()
294         os.system('openbox --reconfigure')
295         self.goodbye()
296
297
298     def mouse(self, widget, event, data=None):
299         if event.button==1:
300            # a keybinding in the treeview is clicked
301            pathinfo=self.treeview.get_path_at_pos(int(event.x),int(event.y)) 
302            if pathinfo==None: return False
303            pathnr=pathinfo[0][0]  
304            name=self.liststore[pathnr][1]
305            action=self.liststore[pathnr][2]
306            self.lock=True
307            self.cb_exec.set_active(False)
308            self.execute.set_sensitive(0)
309            if 'command=' in action:
310               a1=action.split('command=')
311               a2=a1[1].split(',)')
312               self.execute.set_text(a2[0])
313               self.hbox.show()
314            else: self.hbox.hide()
315            self.path=pathnr
316            self.keys.set_text(name)
317            self.action.set_text(action)
318            self.lock=False
319    
320     
321     def row_activate(self, treeview, pathnr, column): 
322            # a keybinding is selected by doubleclick or keyboard
323            name=self.liststore[pathnr][1]
324            action=self.liststore[pathnr][2]
325            self.lock=True
326            self.cb_exec.set_active(False)
327            self.execute.set_sensitive(0)
328            if 'command=' in action:
329               a1=action.split('command=')
330               a2=a1[1].split(',)')
331               self.execute.set_text(a2[0])
332               self.hbox.show()
333            else: self.hbox.hide()
334            self.path=pathnr
335            self.keys.set_text(name)
336            self.action.set_text(action)
337            self.lock=False
338
339
340     def keys_changed(self,data):
341         if self.lock: return 
342         if self.path==-1: return  
343         a=self.keys.get_text() 
344         a=a.strip()
345         self.liststore[self.path][1]=a
346         b=self.liststore[self.path][3]
347         if b=='orginal': self.liststore[self.path][3]='name'
348
349
350     def execute_entry_changed(self,data):
351         if self.lock: return 
352         if self.path==-1: return  
353         a=self.execute.get_text() 
354         a=a.strip()
355         b='Execute(command='+a+',)'
356         self.liststore[self.path][2]=b
357         self.action.set_text(b)
358         
359
360     def add_new_keybinding(self,data=None):
361         self.count_list+=1
362         self.liststore.append(['F12','F12','Execute(command='+i18n("NameOfProgram")+',)','add'])     
363
364
365     def delete_keybinding(self,data=None):
366         if self.lock: return  
367         if self.path==-1: return  
368         self.hbox.hide()
369         self.liststore[self.path][1]=""
370         self.liststore[self.path][2]=""
371         self.liststore[self.path][3]="delete"
372         self.action.set_text('')
373         self.keys.set_text('')
374
375
376     def toggle_execute_entry(self,data=None):
377         if self.lock: return 
378         if self.path!=-1: b=self.liststore[self.path][3]
379         s=self.cb_exec.get_active()
380         if s: 
381            self.execute.set_sensitive(1)
382            if self.path!=-1:
383               if (b=='orginal')or(b=='name'):
384                   self.liststore[self.path][3]='execute'
385         else: 
386            self.execute.set_sensitive(0)
387            if self.path!=-1:
388               if (b=='execute'):
389                   self.liststore[self.path][3]='name'
390
391
392     def __init__(self):
393         namen=home+'/.openbox-keyconf'
394         if not os.access(namen,os.R_OK):
395            f=file(namen,'w')
396            f.write('x=0\n')
397            f.write('y=0\n')
398            f.write('width=800\n')
399            f.write('height=400\n')
400            f.close()
401         f=file(namen,'r')
402         s=f.readline()
403         while s !='':
404            s=s.replace('\n','')
405            z=s.split('=')
406            if z[0]=='x':       self.pos_x=int(z[1])
407            if z[0]=='y':       self.pos_y=int(z[1])
408            if z[0]=='width':   self.width=int(z[1])
409            if z[0]=='height':  self.height=int(z[1])
410            s=f.readline()
411         f.close()
412
413         window = gtk.Window(gtk.WINDOW_TOPLEVEL)
414         self.window=window
415         vbox = gtk.VBox(False, 0)
416         window.set_border_width(5)
417         window.add(vbox)
418
419         # When the window is given the "delete_event" signal (this is given
420         # by the window manager, usually by the "close" option, or on the
421         # titlebar), we ask it to call the delete_event () function
422         # as defined above. The data passed to the callback
423         # function is NULL and is ignored in the callback function.
424         window.connect("delete_event", self.delete_event)
425     
426         # Here we connect the "destroy" event to a signal handler.  
427         # This event occurs when we call gtk_widget_destroy() on the window,
428         # or if we return FALSE in the "delete_event" callback.
429         window.connect("destroy", self.destroy)
430     
431         # Sets the border width of the window.
432         window.set_title(i18n('openbox keyboard bindings'))
433         window.set_size_request(400,200)
434         window.resize(self.width, self.height)
435         window.move(self.pos_x,self.pos_y)
436
437         self.liststore=gtk.ListStore(str,str,str,str)
438         self.treeview=gtk.TreeView()
439         self.treeview.set_headers_visible(True)
440         self.treeview.set_model(self.liststore) 
441         self.treeview.set_property('rules-hint',True)
442         self.treeview.set_property('enable-grid-lines',True)
443         self.treeview.connect('row-activated', self.row_activate)
444         self.treeview.set_events(self.treeview.get_events() | gtk.gdk.BUTTON_PRESS_MASK)
445         self.treeview.connect("button-press-event", self.mouse)
446         self.cell1 = gtk.CellRendererText()
447         self.cell2 = gtk.CellRendererText()
448         self.spalte1 = gtk.TreeViewColumn(i18n('Key'))
449         self.spalte2 = gtk.TreeViewColumn(i18n('Action'))
450         self.treeview.append_column(self.spalte1)
451         self.treeview.append_column(self.spalte2)
452         self.spalte1.pack_start(self.cell1,True)
453         self.spalte2.pack_start(self.cell2,True)
454         self.spalte1.set_attributes(self.cell1, text=1)
455         self.spalte2.set_attributes(self.cell2, text=2)
456         sw = gtk.ScrolledWindow()
457         sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
458         sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
459         sw.add(self.treeview)
460         filename=home+'/'+configfile
461         self.count_list=0
462         try:
463            f=codecs.open(filename,'r','utf_8')
464            line=f.readline()
465            found=False
466            data='frog'
467            action=False
468            name=''
469            while True: 
470                  line=f.readline()
471                  if not line: break
472                  if '<keyboard>' in line:
473                     found=True
474                     continue
475                  if '</keyboard>' in line: 
476                     found=False
477                  if not found: continue
478                  lin=line.replace('\n','')
479                  if '<keybind ' in lin:
480                     lin2=lin.split('"') 
481                     name=lin2[1]
482                     action=False 
483                     data=''
484                     continue
485                  if '</keybind>' in lin:                 
486                     if action: data+=')'
487                     if name!='': 
488                        self.liststore.append([name,name,data,'orginal'])
489                        self.count_list+=1
490                     name=''
491                     continue
492                  if name!='':
493                     if '</action>' in lin: continue
494                     if '<action ' in lin:   
495                        if action: data+='), ' 
496                        action=True
497                        lin2=lin.split('"') 
498                        data=data+lin2[1]+'('
499                        continue
500                     if '<startupnotify>' in lin:
501                        continue 
502                     if '</startupnotify>' in lin:
503                        continue 
504                     if ('<' in lin) and ('>' in lin):
505                        lin2=lin.split('<')
506                        lin3=lin2[1].split('>')
507                        data=data+lin3[0]+'='+lin3[1]+','
508                  continue
509            f.close()
510         except:
511            pass
512         gc.collect()
513         self.lock=True
514         self.path=-1
515         hbox = gtk.HBox(False, 5)
516         self.keys=gtk.Entry(max=40)
517         self.keys.connect("changed",self.keys_changed)
518         self.action=gtk.Label('')
519         hbox.pack_start(self.keys,False,False,0)
520         hbox.pack_start(self.action,True,True,0)
521         vbox.pack_start(hbox, False,False, 0)
522         self.hbox = gtk.HBox(False, 5)
523         self.cb_exec=gtk.CheckButton(i18n("Edit command")+" =")
524         self.cb_exec.connect_object("toggled",self.toggle_execute_entry,None)
525         self.execute=gtk.Entry(max=0) 
526         self.execute.connect("changed",self.execute_entry_changed)
527         self.hbox.pack_start(self.cb_exec,False,False,0)
528         self.hbox.pack_start(self.execute,True,True,0)
529         vbox.pack_start(self.hbox, False,False, 0)
530         vbox.pack_start(sw, True, True, 0)
531         hbox = gtk.HBox(False, 5)
532         button1=gtk.Button(stock='gtk-apply')
533         hiddenlabel=gtk.Label(' ')
534         button2=gtk.Button(stock='gtk-cancel')
535         button3=gtk.Button(stock='gtk-add')
536         button4=gtk.Button(stock='gtk-delete')
537         button5=gtk.Button(stock='gtk-help')
538         hbox.pack_start(button1,False,False,0)
539         hbox.pack_start(button3,False,False,0)
540         hbox.pack_start(button4,False,False,0)
541         hbox.pack_start(button5,False,False,0)
542         hbox.pack_start(hiddenlabel,True,True,0)
543         hbox.pack_start(button2,False,False,0)
544         button1.connect_object("clicked", self.save,None)
545         button2.connect_object("clicked", self.goodbye,None)
546         button3.connect_object("clicked", self.add_new_keybinding,None)
547         button4.connect_object("clicked", self.delete_keybinding,None)
548         button5.connect("clicked", create_help_view)
549         vbox.pack_end(hbox, False, False, 0)
550         window.show_all() 
551         self.hbox.hide()       
552
553
554     def main(self):
555         # All PyGTK applications must have a gtk.main(). Control ends here
556         # and waits for an event to occur (like a key press or mouse event).
557         gtk.main()
558
559
560
561 # If the program is run directly or passed as an argument to the python
562 # interpreter then create a HelloWorld instance and show it
563 if __name__ == "__main__":
564      home=os.getenv("HOME")
565      codec1=Xcodec.lower().replace('iso-','iso').replace('-','_')
566      if 'ansi' in codec1: codec1='iso8859_1'  #Ansi is shit...
567      I18N_ready = load_i18n('openbox-keyconf.moo')
568      Elfriede = Tool()
569      Elfriede.main()
570
571
572