Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

805 keyboard shortcut support for plugins code decoupling #806

15 changes: 14 additions & 1 deletion terminatorlib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,20 @@ def save(self):
for section_name in ['global_config', 'keybindings']:
dbg('Processing section: %s' % section_name)
section = getattr(self, section_name)
parser[section_name] = dict_diff(DEFAULTS[section_name], section)
if section_name == 'keybindings':
from terminatorlib.plugin import KeyBindUtil
# for plugin KeyBindUtil assist in plugin_util
keybindutil = KeyBindUtil();
keyb_keys = keybindutil.get_all_act_to_keys()
# we only need keys as a reference so to match them
# against new values
keyb_keys = dict.fromkeys(keyb_keys, "")

default_merged_section = {**keyb_keys, **DEFAULTS[section_name]}
merged_section = {**keyb_keys, **section}
parser[section_name] = dict_diff(default_merged_section, merged_section)
else:
parser[section_name] = dict_diff(DEFAULTS[section_name], section)

from .configjson import JSON_PROFILE_NAME, JSON_LAYOUT_NAME

Expand Down
135 changes: 135 additions & 0 deletions terminatorlib/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,138 @@ class MenuItem(Plugin):
def callback(self, menuitems, menu, terminal):
"""Callback to transform the enclosed URL"""
raise NotImplementedError


"""
-Basic plugin util for key-press handling, has all mapping to be used
in layout keybindings

Vishweshwar Saran Singh Deo [email protected]
"""

from gi.repository import Gtk, Gdk
from terminatorlib.keybindings import Keybindings, KeymapError

PLUGIN_UTIL_DESC = 0
PLUGIN_UTIL_ACT = 1
PLUGIN_UTIL_KEYS = 2

class KeyBindUtil:

keybindings = Keybindings()

map_key_to_act = {}
map_act_to_keys = {}
map_act_to_desc = {}

def __init__(self, config=None):
self.config = config

#Example
# bind
# first param is desc, second is action str
# self.keyb.bindkey([PluginUrlFindNext , PluginUrlActFindNext, "<Alt>j"])
#
# get action name
# act = self.keyb.keyaction(event)

# if act == "url_find_next":


#check map key_val_mask -> action
def _check_keybind_change(self, key):
act = key[PLUGIN_UTIL_ACT]
for key_val_mask in self.map_key_to_act:
old_act = self.map_key_to_act[key_val_mask]
if act == old_act:
return key_val_mask
return None

#check in config before binding
def bindkey_check_config(self, key):
if not self.config:
raise Warning("bindkey_check_config called without config init")

actstr = key[PLUGIN_UTIL_ACT]
kbsect = self.config.base.get_item('keybindings')
keystr = kbsect[actstr] if actstr in kbsect else ""
dbg("bindkey_check_config:action (%s) key str:(%s)" % (actstr, keystr))
if len(keystr):
key[PLUGIN_UTIL_KEYS] = keystr
dbg("found new Action->KeyVal in config: (%s, %s)"
% (actstr, keystr));
self.bindkey(key)

def bindkey(self, key):
(keyval, mask) = self.keybindings._parsebinding(key[PLUGIN_UTIL_KEYS])
keyval = Gdk.keyval_to_lower(keyval)
mask = Gdk.ModifierType(mask)

ret = (keyval, mask)
dbg("bindkey: (%s) (%s)" % (key[PLUGIN_UTIL_KEYS], str(ret)))

#remove if any old key_val_mask
old_key_val_mask = self._check_keybind_change(key)
if old_key_val_mask:
dbg("found old key binding, removing: (%s)" % str(old_key_val_mask))
del self.map_key_to_act[old_key_val_mask]

#map key-val-mask to action, used to ease key-press management
self.map_key_to_act[ret] = key[PLUGIN_UTIL_ACT]


#map action to key-combo-str, used in preferences->keybinding
self.map_act_to_keys[key[PLUGIN_UTIL_ACT]] = key[PLUGIN_UTIL_KEYS]
#map action to key-combo description, in used preferences->keybinding
self.map_act_to_desc[key[PLUGIN_UTIL_ACT]] = key[PLUGIN_UTIL_DESC]

def unbindkey(self, key):

# Suppose user changed the key-combo and its diff from
# what the plugin had set by default, we need to get
# current key-combo
act = key[PLUGIN_UTIL_ACT]
act_keys = self.map_act_to_keys[act]

(keyval, mask) = self.keybindings._parsebinding(act_keys)
keyval = Gdk.keyval_to_lower(keyval)
mask = Gdk.ModifierType(mask)

ret = (keyval, mask)
dbg("unbindkey: (%s) (%s)" % (key[PLUGIN_UTIL_KEYS], str(ret)))

# FIXME keys should always be there, can also use .pop(key, None)
# lets do it after testing
del self.map_key_to_act[ret]
del self.map_act_to_keys[key[PLUGIN_UTIL_ACT]]
del self.map_act_to_desc[key[PLUGIN_UTIL_ACT]]


def keyaction(self, event):
#FIXME MOD2 mask comes in the event, remove
event.state &= ~Gdk.ModifierType.MOD2_MASK

keyval = Gdk.keyval_to_lower(event.keyval)
ret = (keyval, event.state)
dbg("keyaction: (%s)" % str(ret))
return self.map_key_to_act.get(ret, None)

def get_act_to_keys(self, key):
return self.map_act_to_keys.get(key)

def get_all_act_to_keys(self):
return self.map_act_to_keys

def get_all_act_to_desc(self):
return self.map_act_to_desc

def get_act_to_desc(self, act):
return self.map_act_to_desc.get(act)

#get action to key binding from config
def get_act_to_keys_config(self, act):
if not self.config:
raise Warning("get_keyvalmask_for_act called without config init")

keybindings = self.config["keybindings"]
return keybindings.get(act)
29 changes: 28 additions & 1 deletion terminatorlib/prefseditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from .plugin import PluginRegistry
from .version import APP_NAME

from .plugin import KeyBindUtil

def get_color_string(widcol):
return('#%02x%02x%02x' % (widcol.red>>8, widcol.green>>8, widcol.blue>>8))

Expand Down Expand Up @@ -472,6 +474,15 @@ def on_search_refilter(widget):
liststore = widget.get_model()
liststore.set_sort_column_id(0, Gtk.SortType.ASCENDING)
keybindings = self.config['keybindings']

keybindutil = KeyBindUtil()
plugin_keyb_act = keybindutil.get_all_act_to_keys()
plugin_keyb_desc = keybindutil.get_all_act_to_desc()
#merge give preference to main bindings over plugin
keybindings = {**plugin_keyb_act, **keybindings}
self.keybindingnames = {**plugin_keyb_desc, **self.keybindingnames}
#dbg("appended actions %s names %s" % (keybindings, self.keybindingnames))

for keybinding in keybindings:
keyval = 0
mask = 0
Expand Down Expand Up @@ -1900,8 +1911,14 @@ def on_cellrenderer_accel_edited(self, liststore, path, key, mods, _code):
current_binding = liststore.get_value(liststore.get_iter(path), 0)
parsed_accel = Gtk.accelerator_parse(accel)

keybindutil = KeyBindUtil()
keybindings = self.config["keybindings"]
#merge give preference to main bindings over plugin
plugin_keyb_act = keybindutil.get_all_act_to_keys()
keybindings = {**plugin_keyb_act, **keybindings}

duplicate_bindings = []
for conf_binding, conf_accel in self.config["keybindings"].items():
for conf_binding, conf_accel in keybindings.items():
if conf_accel is None:
continue

Expand Down Expand Up @@ -1944,6 +1961,16 @@ def on_cellrenderer_accel_edited(self, liststore, path, key, mods, _code):
binding = liststore.get_value(liststore.get_iter(path), 0)
accel = Gtk.accelerator_name(key, mods)
self.config['keybindings'][binding] = accel

plugin_keyb_desc = keybindutil.get_act_to_desc(binding)
if plugin_keyb_desc:
dbg("modifying plugin binding: %s, %s, %s" %
(plugin_keyb_desc, binding, accel))
keybindutil.bindkey([plugin_keyb_desc, binding, accel])
else:
dbg("skipping: %s" % binding)
pass

self.config.save()

def on_cellrenderer_accel_cleared(self, liststore, path):
Expand Down