Merge remote-tracking branch 'upstream/master' into 681-Plugin-Submission-Plugin-Utility-KeyBinding-Mouseless-Keyboard-URL-Open

This commit is contained in:
Vishweshwar Saran Singh Deo 2023-08-26 20:47:10 +05:30
commit bd4e3b3470
5 changed files with 202 additions and 6 deletions

View File

@ -720,7 +720,20 @@ class ConfigBase(Borg):
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

View File

@ -69,12 +69,14 @@ class PluginRegistry(borg.Borg):
if not self.available_plugins:
self.available_plugins = {}
def load_plugins(self):
def load_plugins(self, force=False):
"""Load all plugins present in the plugins/ directory in our module"""
if self.done:
if self.done and (not force):
dbg('Already loaded')
return
dbg('loading plugins, force:(%s)' % force)
config = Config()
for plugindir in self.path:
@ -93,8 +95,8 @@ class PluginRegistry(borg.Borg):
try:
module = __import__(plugin[:-3], None, None, [''])
for item in getattr(module, 'AVAILABLE'):
func = getattr(module, item)
if item not in list(self.available_plugins.keys()):
func = getattr(module, item)
self.available_plugins[item] = func
if item not in config['enabled_plugins']:
@ -102,6 +104,14 @@ class PluginRegistry(borg.Borg):
continue
if item not in self.instances:
self.instances[item] = func()
elif force:
#instead of multiple copies of loaded
#plugin objects, unload where plugins
#can clean up and then re-init so there
#is one plugin object
self.instances[item].unload()
self.instances.pop(item, None)
self.instances[item] = func()
except Exception as ex:
err('PluginRegistry::load_plugins: Importing plugin %s \
failed: %s' % (plugin, ex))
@ -187,3 +197,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 vssdeo@gmail.com
"""
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)

View File

@ -18,6 +18,8 @@ from .terminator import Terminator
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))
@ -472,6 +474,15 @@ class PrefsEditor:
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
@ -1900,8 +1911,14 @@ class PrefsEditor:
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
@ -1944,6 +1961,16 @@ class PrefsEditor:
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):

View File

@ -156,6 +156,13 @@ class Terminal(Gtk.VBox):
dbg('composite_support: %s' % self.composite_support)
self.vte.show()
#force to load for new window/terminal use case loading plugin
#and connecting signals, note the line update_url_matches also
#calls load_plugins, but it won't reload since already loaded
self.load_plugins(force = True)
self.update_url_matches()
self.terminalbox = self.create_terminalbox()
@ -285,6 +292,10 @@ class Terminal(Gtk.VBox):
return(terminalbox)
def load_plugins(self, force = False):
registry = plugin.PluginRegistry()
registry.load_plugins(force)
def _add_regex(self, name, re):
match = -1
if regex.FLAGS_PCRE2:

View File

@ -593,7 +593,7 @@ class Terminator(Borg):
for term in self.get_target_terms(widget):
name = term.titlebar.get_custom_string() or term.get_window_title()
term.feed(name)
term.feed(name.encode())
def get_sibling_terms(self, widget):
termset = []