from pyfbsdk import *
from pyfbsdk_additions import *
import os
import os.path
import ConfigParser
import mbutils
def UIValue(key, mods):
if key:
return "%s : %s" % (mods, key)
else:
return ""
class Mapping(object):
def __init__(self,action,key="", mods="", time = "DN"):
self.action = action
self.mods = self.edit_mods = mods
self.key = self.edit_key = key
self.time = time
def FileValue(self):
if self.key:
return "{%s:%s*%s}" % (self.mods,self.key,self.time)
else:
return ""
def UIValue(self):
return UIValue(self.key,self.mods)
def MappingsToShortcut(mappings):
shortcut = ""
for i, mapping in enumerate(mappings):
value = mapping.FileValue()
if value:
if shortcut:
shortcut += "|"
shortcut += value
return shortcut
MOD_TO_ID = { "NONE" : 0,"SHFT" : 1,"CTRL" : 2,"ALT" : 4,"ALCT" : 6,"ALSH" : 5,"CTSH" : 3,"ALCTSH" : 7 }
KEY_TO_ID = { "NONE" : -1,
"ESC" : 0x1b, "TAB" : 0x09, "CAPS" : 0x14, "BKSP" : 0x08, "LBR" : 0xdb, "RBR" : 0xdd, "SEMI" : 0xba, "ENTR" : 0x0d,
"SPC" : 0x20, "PRNT" : 0x2c, "SCRL" : 0x91, "PAUS" : 0x13, "INS" : 0x2d, "HOME" : 0x24, "PGUP" : 0x21, "DEL" : 0x2e,
"END" : 0x1b, "PGDN" : 0x1b, "UP" : 0x1b, "LEFT" : 0x1b, "DOWN" : 0x1b, "RGHT" : 0x1b,
"F1" : 0x70,"F2" : 0x71 ,"F3" : 0x72, "F4" : 0x73, "F5" : 0x74, "F6" : 0x75, "F7" : 0x76, "F8" : 0x77, "F9" : 0x78,"F10" : 0x79 ,"F11" : 0x7a, "F12" : 0x7b,
"NUML" : 0x90, "NMUL" : 0x6a, "NADD" : 0x6b, "NDIV" : 0x6f, "NSUB" : 0x6d,"NDEC" : 0x6e ,"N0" : 0x60, "N1" : 0x61, "N2" : 0x62, "N3" : 0x63, "N4" : 0x64, "N5" : 0x65, "N6" : 0x66,"N7" : 0x67 ,"N8" : 0x68, "N9" : 0x69,
"'" : 0xde, "," : 0xbc, "-" : 0xbd, "/" : 0xbf,"=" : 0xbb ,"." : 0xbe, "\\" : 0xdc, "`" : 0xc0,
"0" : 48, "1" : 49, "2" : 50, "3" : 51, "4" : 52,"5" : 53 ,"6" : 54, "7" : 55, "8" : 56, "9" : 59,
"A" : 65, "B" : 66,"C" : 67, "D" : 68, "E" : 69,"F" : 70 , "G" : 71, "H" : 72,"I" : 73,
"J" : 74, "K" : 75,"L" : 76, "M" : 77, "N" : 78,"O" : 79, "P" : 80, "Q" : 81,"R" : 82 ,
"S" : 83, "T" : 84,"U" : 85, "V" : 86, "W" : 87,"X" : 88, "Y" : 89, "Z" : 90
}
ID_TO_KEY = {}
for key, i in KEY_TO_ID.iteritems():
ID_TO_KEY[i] = key
ID_TO_MOD = {}
for key, i in MOD_TO_ID.iteritems():
ID_TO_MOD[i] = key
class KeyboardMapper(object):
def GetMappingFromShortcut(self,key,mods,mappings = None):
if not mappings:
mappings = self.row_to_mapping
for mapping in mappings:
if key and key == mapping.key and mapping.mods == mods:
return mapping
return None
def OnShortcut(self,control,event):
if event.InputType == FBInputType.kFBKeyPressRaw and event.Key != -1 and self.edit_mapping:
self.edit_mapping.edit_key = ID_TO_KEY[event.Key]
self.edit_mapping.edit_mods = ID_TO_MOD[event.KeyState]
shortcut = UIValue(self.edit_mapping.edit_key,self.edit_mapping.edit_mods)
self.shortcut_edit.Text = shortcut
self.conflict_mapping = self.GetMappingFromShortcut(self.edit_mapping.edit_key,self.edit_mapping.edit_mods)
if self.conflict_mapping:
self.conflict_edit.Text = self.conflict_mapping.action
else:
self.conflict_edit.Text = ""
def UpdateEditMapping(self,row):
print "selcted", row
if self.shortcut_spread.GetRow(row).RowSelected:
mapping = self.row_to_mapping[row]
self.action_edit.Text = mapping.action
self.shortcut_edit.Text = mapping.UIValue()
self.edit_mapping = mapping
global foin
foin = mapping
else:
print "none"
self.edit_mapping = None
self.action_edit.Text = ""
self.shortcut_edit.Text = ""
self.conflict_mapping = None
self.conflict_edit.Text = ""
def RowClicked(self,control,event):
self.UpdateEditMapping(event.Row)
def OnKeyboardChange(self,control,event):
if self.row_to_mapping:
self.shortcut_spread.Clear()
self.shortcut_spread.ColumnAdd("Shortcut")
self.mapping_to_row = {}
self.row_to_mapping = []
self.action_to_mappings = {}
config = mbutils.OpenConfigFile(self.keyboard_files[self.file_list.ItemIndex])
rowref = 0
items = config.items("Actions")
items.sort(key = lambda pair : pair[0] )
for action, shortcut in items:
mappings = self.ParseShortcut(action, shortcut)
for i, mapping in enumerate(mappings):
if i > 0:
self.shortcut_spread.RowAdd("", rowref)
else:
self.shortcut_spread.RowAdd(action, rowref)
self.row_to_mapping.append(mapping)
self.mapping_to_row[mapping] = rowref
self.shortcut_spread.SetCellValue(rowref, 0, mapping.UIValue())
rowref += 1
def ParseShortcut(self,action, desc_string):
l = []
desclist = desc_string.split("|")
for desc in desclist:
if desc:
keystr = desc.strip("{}")
mod_keytime = keystr.split(":")
key_time = mod_keytime[1].split("*")
mapping = Mapping(action,key_time[0],mod_keytime[0],key_time[1])
else:
mapping = Mapping(action)
l.append(mapping)
self.action_to_mappings[action] = l
return l
def Assign(self,control,event):
if not self.edit_mapping:
return
action_mappings = self.action_to_mappings[self.edit_mapping.action]
if self.GetMappingFromShortcut(self.edit_mapping.edit_key,self.edit_mapping.edit_mods,action_mappings):
return
self.edit_mapping.key = self.edit_mapping.edit_key
self.edit_mapping.mods = self.edit_mapping.edit_mods
self.shortcut_spread.SetCellValue(self.mapping_to_row[self.edit_mapping], 0, self.edit_mapping.UIValue())
self.WriteEditMapping()
def WriteEditMapping(self,update_conflict = True):
keyboard_dir, keyboard_file = os.path.split(self.keyboard_files[self.file_list.ItemIndex])
config = FBConfigFile(keyboard_file,keyboard_dir)
config.Set("Actions",self.edit_mapping.action,MappingsToShortcut(self.action_to_mappings[self.edit_mapping.action]))
if self.conflict_mapping:
self.conflict_mapping.key = ""
self.conflict_mapping.mods = ""
if update_conflict:
self.shortcut_spread.SetCellValue(self.mapping_to_row[self.conflict_mapping], 0, "")
config.Set("Actions",self.conflict_mapping.action,MappingsToShortcut(self.action_to_mappings[self.conflict_mapping.action]))
self.conflict_edit.Text = ""
def Remove(self,control,event):
if not self.edit_mapping:
return
row = self.mapping_to_row[self.edit_mapping]
self.edit_mapping.key = ""
self.edit_mapping.mods = ""
self.shortcut_edit.Text = ""
self.WriteEditMapping(False)
self.OnKeyboardChange(None, None)
if row > len(self.row_to_mapping):
row = len(self.row_to_mapping) - 1
self.shortcut_spread.GetRow(row).RowSelected = True
self.UpdateEditMapping(row)
def Add(self,control,event):
if not self.edit_mapping:
return
action_mappings = self.action_to_mappings[self.edit_mapping.action]
if self.GetMappingFromShortcut(self.edit_mapping.edit_key,self.edit_mapping.edit_mods,action_mappings):
return
row = self.mapping_to_row[action_mappings[0]] + len(action_mappings)
mapping = Mapping(self.edit_mapping.action,self.edit_mapping.edit_key,self.edit_mapping.edit_mods)
self.edit_mapping = mapping
action_mappings.append(mapping)
self.WriteEditMapping(True)
self.OnKeyboardChange(None, None)
if row > len(self.row_to_mapping):
row = len(self.row_to_mapping) - 1
self.shortcut_spread.GetRow(row).RowSelected = True
self.UpdateEditMapping(row)
def Show(self):
self.popup.Show()
del self.popup
def ClosePopup(self,control, event):
self.popup.Close(True)
def __init__(self):
self.popup = FBPopup()
self.popup.Caption = "Keyboard Mapper"
self.popup.Modal = True
x = FBAddRegionParam(0,FBAttachType.kFBAttachLeft,"")
y = FBAddRegionParam(0,FBAttachType.kFBAttachTop,"")
w = FBAddRegionParam(0,FBAttachType.kFBAttachRight,"")
h = FBAddRegionParam(0,FBAttachType.kFBAttachBottom,"")
self.popup.AddRegion("main","main", x,y,w,h)
self.popup.Left = 300
self.popup.Top = 300
self.popup.Width = 650
self.popup.Height = 500
grid = GridLayout()
self.popup.SetControl("main",grid)
row = 0
widgetHeight = 25
buttonWidth = 90
l = FBLabel()
l.Caption = "Keyboard files"
grid.Add(l, row, 0)
grid.SetRowHeight(row, widgetHeight)
row += 1
self.file_list = FBList()
self.file_list.OnChange.Add(self.OnKeyboardChange)
self.file_list.Style = FBListStyle.kFBDropDownList
grid.Add(self.file_list, row, 0)
grid.SetRowHeight(row, widgetHeight)
row += 1
l = FBLabel()
l.Caption = "Shortcut list"
grid.Add(l, row, 0)
grid.SetRowHeight(row, widgetHeight)
row += 1
self.shortcut_spread = FBSpread()
self.shortcut_spread.Caption = "Action Name"
self.shortcut_spread.ColumnAdd("Shortcut")
self.shortcut_spread.OnRowClick.Add(self.RowClicked)
grid.AddRange(self.shortcut_spread, row, row, 0, 2)
grid.SetRowRatio(row, 1.0)
row += 1
l = FBLabel()
l.Caption = "Action Name"
grid.Add(l, row, 0)
l = FBLabel()
l.Caption = "Shortcut"
grid.Add(l, row, 2)
grid.SetRowHeight(row, widgetHeight)
row += 1
self.action_edit = FBEdit()
self.action_edit.ReadOnly = True
grid.Add(self.action_edit, row, 0)
self.input_layout = FBLayout()
x = FBAddRegionParam(5,FBAttachType.kFBAttachLeft,"")
y = FBAddRegionParam(5,FBAttachType.kFBAttachTop,"")
w = FBAddRegionParam(-5,FBAttachType.kFBAttachRight,"")
h = FBAddRegionParam(-5,FBAttachType.kFBAttachBottom,"")
self.input_layout.AddRegion("Border","Click here and type a shortcut", x, y, w, h)
self.input_layout.SetBorder("Border",FBBorderStyle.kFBEmbossBorder,True, False,2,2,90,0)
self.input_layout.OnInput.Add(self.OnShortcut)
grid.Add(self.input_layout, row, 1)
self.shortcut_edit = FBEdit()
self.shortcut_edit.ReadOnly = True
grid.Add(self.shortcut_edit, row, 2)
grid.SetRowHeight(row, widgetHeight)
row += 1
hbox = HBoxLayout()
b = FBButton()
b.Caption = "Replace shortcut"
b.OnClick.Add(self.Assign)
hbox.Add(b, buttonWidth)
b = FBButton()
b.Caption = "Add shortcut"
b.OnClick.Add(self.Add)
hbox.Add(b, buttonWidth)
b = FBButton()
b.Caption = "Remove shortcut"
b.OnClick.Add(self.Remove)
hbox.Add(b, buttonWidth)
grid.AddRange(hbox, row, row, 0, 1)
grid.SetRowHeight(row, widgetHeight)
row += 1
l = FBLabel()
l.Caption = "Shortcut already assigned to action"
grid.AddRange(l, row, row, 0,2)
grid.SetRowHeight(row, widgetHeight)
row += 1
self.conflict_edit = FBEdit()
self.conflict_edit.ReadOnly = True
grid.Add(self.conflict_edit, row, 0)
grid.SetRowHeight(row, widgetHeight)
row += 1
hbox = HBoxLayout()
b = FBButton()
b.Caption = "Close"
b.OnClick.Add(self.ClosePopup)
hbox.Add(b, buttonWidth)
l = FBLabel()
l.Caption = 'Reset Settings->Keyboard configuration->"Your Keyboard" for changes to take place.'
l.Style = FBTextStyle.kFBTextStyleBold
hbox.Add(l, 600)
grid.AddRange(hbox, row, row, 0, 2)
grid.SetRowHeight(row, widgetHeight)
row += 1
self.action_to_mappings = {}
self.mapping_to_row = {}
self.row_to_mapping = []
self.edit_mapping = None
self.conflict_mapping = None
self.keyboard_files = []
keyboard_folder = os.path.join(mbutils.GetConfigPath(),"Keyboard")
for f in os.listdir(keyboard_folder):
self.keyboard_files.append(os.path.join(keyboard_folder,f))
self.file_list.Items.append(f)
self.keyboard_files.append(os.path.join(mbutils.GetConfigPath(),"Python", "PythonKeyboard.txt"))
self.file_list.Items.append("PythonKeyboard.txt")
self.OnKeyboardChange(None, None)
def PopKeyboardMapper(control,event):
mapper = KeyboardMapper()
mapper.Show()
del mapper
def PopulateLayout(tool):
x = FBAddRegionParam(0,FBAttachType.kFBAttachLeft,"")
y = FBAddRegionParam(0,FBAttachType.kFBAttachTop,"")
w = FBAddRegionParam(0,FBAttachType.kFBAttachRight,"")
h = FBAddRegionParam(0,FBAttachType.kFBAttachBottom,"")
vbox = VBoxLayout()
tool.AddRegion("main","main", x, y, w, h)
tool.SetControl("main",vbox)
b = FBButton()
b.Caption = "Keyboard mapper"
vbox.Add(b, 35)
b.OnClick.Add(PopKeyboardMapper)
def CreateTool():
tool = CreateUniqueTool("Keyboard Utilities")
tool.StartSizeX = 200
tool.StartSizeY = 100
PopulateLayout(tool)
ShowTool(tool)
CreateTool()