""" - With min changes to main code base, implementing mouse free url handling. - Using shortcuts to browse URL, next / previous, clear search. Selected URL is copied to clipboard. - Vishweshwar Saran Singh Deo vssdeo@gmail.com """ import gi gi.require_version('Vte', '2.91') # vte-0.38 (gnome-3.14) from gi.repository import Vte from terminatorlib.terminator import Terminator from terminatorlib.config import Config import terminatorlib.plugin as plugin from terminatorlib.plugin import KeyBindUtil from terminatorlib.util import get_config_dir, err, dbg, gerr from terminatorlib import regex import re AVAILABLE = ['MouseFreeURLHandler'] PluginUrlActFindNext = "plugin_url_find_next" PluginUrlActFindPrev = "plugin_url_find_prev" PluginUrlActEsc = "plugin_url_esc" PluginUrlActLaunch = "plugin_url_launch" PluginUrlFindNext = "Plugin Url Find Next" PluginUrlFindPrev = "Plugin Url Find Prev" PluginUrlEsc = "Plugin Url Esc" PluginUrlLaunch = "Plugin Url Launch" class MouseFreeURLHandler(plugin.Plugin): capabilities = ['MouseFreeHandler'] handler_name = 'MouseFreeHandler' nameopen = None namecopy = None match = None flag_http_on = False config = Config() keyb = KeyBindUtil(config) matches = [] matches_ptr = -1 vte = None cur_term = None #basic pattern searchtext = "https?\:\/\/[^\s]+[\/\w]" def __init__(self): self.connect_signals() self.keyb.bindkey_check_config( [PluginUrlFindNext , PluginUrlActFindNext, "j"]) self.keyb.bindkey_check_config( [PluginUrlFindPrev , PluginUrlActFindPrev, "k"]) self.keyb.bindkey_check_config( [PluginUrlEsc , PluginUrlActEsc, "Escape"]) self.keyb.bindkey_check_config( [PluginUrlLaunch, PluginUrlActLaunch, "Return"]) def connect_signals(self): for term in Terminator().terminals: dbg("signal connect term:%s" % term) term.connect('focus-in', self.on_focus_in) self.windows = Terminator().get_windows() for window in self.windows: window.connect('key-press-event', self.on_keypress) def unload(self): dbg("unloading") for term in Terminator().terminals: try: term.disconnect_by_func(self.on_focus_in) except: dbg("no connected signals") for window in self.windows: try: window.disconnect_by_func(self.on_keypress) except: dbg("no connected signals") self.keyb.unbindkey( [PluginUrlFindNext , PluginUrlActFindNext, "j"]) self.keyb.unbindkey( [PluginUrlFindPrev , PluginUrlActFindPrev, "k"]) self.keyb.unbindkey( [PluginUrlEsc , PluginUrlActEsc, "Escape"]) self.keyb.unbindkey( [PluginUrlLaunch, PluginUrlActLaunch, "Return"]) def extract(self): #can we do extract more efficiently col, row = self.vte.get_cursor_position() (txt, attr) = self.vte.get_text_range_format( Vte.Format.TEXT, 0, 0, row, col) self.matches = re.findall(self.searchtext, txt) self.matches_ptr = len(self.matches)-1 def get_selected_url(self): if len(self.matches): dbg("found selected URL (%s %s)" % (self.matches_ptr, self.matches[self.matches_ptr])) return self.matches[self.matches_ptr] dbg("selected URL (%s %s)" % (self.matches_ptr, "not found")) return None def get_focussed_terminal(self): """iterate over all the terminals to find which, if any, has focus""" for terminal in Terminator().terminals: if terminal.get_vte().has_focus(): return(terminal) return(None) def on_focus_in(self, widget, event = None): dbg("focus-in clear url search buffer widget: %s" % widget) self.cur_term = self.get_focussed_terminal() self.vte = self.cur_term.get_vte() self.clear_search() def on_keypress(self, widget, event): act = self.keyb.keyaction(event) dbg("keyaction: (%s) (%s)" % (str(act), event.keyval)) if act == PluginUrlActFindNext: if not self.flag_http_on: dbg("search URL on") self.search() self.extract() #so when we start search last item be selected self.vte.search_find_previous() self.get_selected_url() # dbg url print self.vte.copy_clipboard() return True else: self.vte.search_find_next() if (self.matches_ptr < len(self.matches)-1): self.matches_ptr += 1 else: self.matches_ptr = 0 self.vte.copy_clipboard() self.get_selected_url() # dbg url print return True if act == PluginUrlActFindPrev: if not self.flag_http_on: self.search() self.extract() self.vte.search_find_previous() self.get_selected_url() # dbg url print self.vte.copy_clipboard() return True else: self.vte.search_find_previous() if self.matches_ptr > 0: self.matches_ptr -= 1 elif len(self.matches): self.matches_ptr = len(self.matches)-1 self.vte.copy_clipboard() self.get_selected_url() # dbg url print return True if act == PluginUrlActEsc: self.clear_search() return if act == PluginUrlActLaunch: url = self.get_selected_url() if url: self.cur_term.open_url(url, prepare=False) return #TODO: use case for KeyBindUtil #So this is capturing key as if user presses return #then the current selection would be cleared in case he types #more commands or text in terminal has more urls now. So next #time search should restart with complete text #For KeyBindUtil if we register then keybinding will #be shown in Preferences->Keybindings and if any other plugin #wants to listen to same key code it will throw error since in #UI binding has to be unique. May be we can have keybinds #hidden from UI which plugins can use internally if event.keyval == 65293: # self.clear_search() return def clear_search(self): self.matches = [] self.flag_http_on = False self.matches_ptr = -1 if self.vte: self.vte.search_set_regex(None, 0) dbg("search URL off") self.vte.unselect_all() def search(self): dbg("searching text") self.flag_http_on = True self.vte.search_set_wrap_around(True) regex_flags_pcre2 = (regex.FLAGS_PCRE2 | regex.PCRE2_CASELESS) searchre = Vte.Regex.new_for_search(self.searchtext, len(self.searchtext), regex_flags_pcre2) self.vte.search_set_regex(searchre, 0)