From de48b07328df73fa6c5b0c1fcdf565fa56739ea9 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Sun, 7 May 2023 05:15:53 +0000 Subject: [PATCH 01/26] remove deprecated pytest-runner See https://github.com/pytest-dev/pytest-runner/#deprecation-notice. We can call pytest directly instead. --- .github/workflows/python.yml | 6 ++---- setup.py | 4 ---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 2889b70f..9e499b7d 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -41,13 +41,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -e . - python setup.py develop + pip install -e '.[test]' - name: Compile all scripts run: python -m compileall -f terminatorlib/ tests/ remotinator terminator - name: Run tests run: | - pip install -e '.[test]' - xvfb-run -a python setup.py test + xvfb-run -a pytest diff --git a/setup.py b/setup.py index d706b7f4..c49a6f93 100755 --- a/setup.py +++ b/setup.py @@ -219,9 +219,6 @@ setup(name=APP_NAME, 'terminatorlib', 'terminatorlib.plugins', ], - setup_requires=[ - 'pytest-runner', - ], install_requires=[ 'pycairo', 'configobj', @@ -229,7 +226,6 @@ setup(name=APP_NAME, 'pygobject', 'psutil', ], - tests_require=test_deps, extras_require={'test': test_deps}, package_data={'terminatorlib': ['preferences.glade', 'layoutlauncher.glade']}, cmdclass={'build': BuildData, 'install_data': InstallData, 'uninstall': Uninstall}, From 2fe3ad8b08bdd449c873453b4fd432b5caee3288 Mon Sep 17 00:00:00 2001 From: Vishweshwar Saran Singh Deo Date: Mon, 4 Sep 2023 17:21:37 +0530 Subject: [PATCH 02/26] [bug 802] - Ability to undo or restore changes to the preferences #802 - added basic config restore option from preferences window - changes to glade made in addition to addition of destory signal - minor cleaup in config to have a single function return confilg filename via get_config_filename --- terminatorlib/config.py | 36 +++++++++++++++++++++++++++------ terminatorlib/preferences.glade | 20 ++++++++++++++++-- terminatorlib/prefseditor.py | 11 ++++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/terminatorlib/config.py b/terminatorlib/config.py index 4fbb907d..2e555467 100644 --- a/terminatorlib/config.py +++ b/terminatorlib/config.py @@ -622,12 +622,7 @@ class ConfigBase(Borg): dbg('config already loaded') return - if self.command_line_options and self.command_line_options.config: - filename = self.command_line_options.config - else: - filename = os.path.join(get_config_dir(), 'config') - if not os.path.exists(filename): - filename = os.path.join(get_system_config_dir(), 'config') + filename = self.get_config_filename() dbg('looking for config file: %s' % filename) try: # @@ -706,6 +701,35 @@ class ConfigBase(Borg): self.loaded = True + def get_config_filename(self): + filename = '' + if self.command_line_options and self.command_line_options.config: + filename = self.command_line_options.config + else: + filename = os.path.join(get_config_dir(), 'config') + if not os.path.exists(filename): + filename = os.path.join(get_system_config_dir(), 'config') + + return filename + + def save_config_with_suffix(self, suffix): + filename = self.get_config_filename() + #save the current config, to revert any changes make in preferences + cur_loaded_file = filename + suffix + shutil.copy2(filename, cur_loaded_file) + + def restore_config_with_suffix(self, suffix): + filename = self.get_config_filename() + cur_loaded_file = filename + suffix + dbg("restoring from file:%s to file:%s" % (cur_loaded_file, filename)) + shutil.copy2(cur_loaded_file, filename) + + def remove_config_with_suffix(self, suffix): + filename = self.get_config_filename() + cur_loaded_file = filename + suffix + if os.path.exists(cur_loaded_file): + os.remove(cur_loaded_file) + def reload(self): """Force a reload of the base config""" self.loaded = False diff --git a/terminatorlib/preferences.glade b/terminatorlib/preferences.glade index e0e8a2a5..83a90847 100644 --- a/terminatorlib/preferences.glade +++ b/terminatorlib/preferences.glade @@ -424,6 +424,7 @@ Terminator Preferences 640 400 + True @@ -1241,7 +1242,7 @@ - + True False @@ -4410,6 +4411,21 @@ Much of the behavior of Terminator is based on GNOME Terminal, and we are adding 0 + + + Discard Changes + False + True + True + True + + + + True + True + 1 + + gtk-close @@ -4423,7 +4439,7 @@ Much of the behavior of Terminator is based on GNOME Terminal, and we are adding True True - 1 + 2 diff --git a/terminatorlib/prefseditor.py b/terminatorlib/prefseditor.py index d5933c4b..72c14110 100755 --- a/terminatorlib/prefseditor.py +++ b/terminatorlib/prefseditor.py @@ -235,14 +235,25 @@ class PrefsEditor: nb = guiget('notebook1') nb.set_current_page(cur_page) + self.config.base.save_config_with_suffix('_cur') + + def on_destroy_event(self, _widget): + self.config.base.remove_config_with_suffix('_cur') + def on_closebutton_clicked(self, _button): """Close the window""" + self.config.base.remove_config_with_suffix('_cur') terminator = Terminator() terminator.reconfigure() self.window.destroy() self.calling_window.preventHide = False del(self) + def on_restoreconfigbutton_clicked(self, _button): + """restore config to load time""" + self.config.base.restore_config_with_suffix('_cur') + self.on_closebutton_clicked(_button) + def set_values(self): """Update the preferences window with all the configuration from Config()""" From 6fc6d7f65817ce2832ee2d6db8e9708a492f6cce Mon Sep 17 00:00:00 2001 From: Vishweshwar Saran Singh Deo Date: Mon, 11 Sep 2023 18:30:02 +0530 Subject: [PATCH 03/26] [bug 802] 802-Ability-to-undo-or-restore-changes-to-the-preferences #802 - fixed the errors in case of missing configs - restore file is stored in writable path --- terminatorlib/config.py | 60 ++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/terminatorlib/config.py b/terminatorlib/config.py index 2e555467..97583570 100644 --- a/terminatorlib/config.py +++ b/terminatorlib/config.py @@ -713,22 +713,55 @@ class ConfigBase(Borg): return filename def save_config_with_suffix(self, suffix): - filename = self.get_config_filename() - #save the current config, to revert any changes make in preferences - cur_loaded_file = filename + suffix - shutil.copy2(filename, cur_loaded_file) + try: + filename = self.get_config_filename() + + #save the current config, to revert any changes make in preferences + #save the current config to config_dir path which is at least writable + cfg_filename = os.path.join(get_config_dir(), 'config') + cur_loaded_file = cfg_filename + suffix + + if os.path.exists(filename) and cur_loaded_file: + dbg('copy file:%s to' \ + ' file:%s' % (filename, cur_loaded_file)) + shutil.copy2(filename, cur_loaded_file) + elif cur_loaded_file: + open(cur_loaded_file, 'a').close() + else: + err('ConfigBase:: Unable to get filename to save') + except Exception as ex: + err('ConfigBase::save_config_with_suffix' \ + ' Unable to save config: %s' % ex) def restore_config_with_suffix(self, suffix): - filename = self.get_config_filename() - cur_loaded_file = filename + suffix - dbg("restoring from file:%s to file:%s" % (cur_loaded_file, filename)) - shutil.copy2(cur_loaded_file, filename) + try: + filename = self.get_config_filename() + + cfg_filename = os.path.join(get_config_dir(), 'config') + cur_loaded_file = cfg_filename + suffix + if os.path.exists(cur_loaded_file): + if not os.access(filename, os.W_OK): + dbg('path:%s not writable' \ + ' restoring to path:%s' % (filename,cfg_filename)) + filename = cfg_filename + + dbg('restore from file:%s to file:%s' + % (cur_loaded_file, filename)) + shutil.copy2(cur_loaded_file, filename) + except Exception as ex: + err('ConfigBase::restore_config_with_suffix' \ + ' Unable to restore config: %s' % ex) def remove_config_with_suffix(self, suffix): - filename = self.get_config_filename() - cur_loaded_file = filename + suffix - if os.path.exists(cur_loaded_file): - os.remove(cur_loaded_file) + try: + cfg_filename = os.path.join(get_config_dir(), 'config') + cur_loaded_file = cfg_filename + suffix + if os.path.exists(cur_loaded_file): + dbg('remove file:%s' % (cur_loaded_file)) + os.remove(cur_loaded_file) + except Exception as ex: + err('ConfigBase::remove_config_with_suffix' \ + ' Unable to remove config: %s' % ex) def reload(self): """Force a reload of the base config""" @@ -795,7 +828,8 @@ class ConfigBase(Borg): open(filename, 'a').close() backup_file = filename + '~' - shutil.copy2(filename, backup_file) + if os.path.exists(filename): + shutil.copy2(filename, backup_file) with open(filename, 'wb') as fh: parser.write(fh) From b935eda987d2ae4bce0b412884b9d574f2021e29 Mon Sep 17 00:00:00 2001 From: Julien Dusser Date: Sun, 10 Sep 2023 17:02:38 +0200 Subject: [PATCH 04/26] [bug 827] fix --new-tab and --toggle-visibility Commit 2e1dd1f add a 'if' between a 'if' and 'else' breaking logic. --- terminator | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/terminator b/terminator index 21eaec7d..4603b5b0 100755 --- a/terminator +++ b/terminator @@ -111,11 +111,9 @@ if __name__ == '__main__': elif OPTIONS.toggle_visibility: dbg('requesting to toggle windows visibility') ipc.toggle_visibility_cmdline(optionslist) - - if OPTIONS.reload: + elif OPTIONS.reload: dbg('requesting to reload configuration for all windows') ipc.reload_configuration() - elif OPTIONS.unhide: print('requesting to unhide windows') ipc.unhide_cmdline(optionslist) From 86337ad326b9fc2fa24b80e6922d6bc5a0985ba3 Mon Sep 17 00:00:00 2001 From: Vishweshwar Saran Singh Deo Date: Sun, 8 Oct 2023 12:40:19 +0530 Subject: [PATCH 05/26] [bug 706] 706-Favorites-Bookmarks-Plugin #706 - modified custom command plugin to have bookmarks - added keybindings for directly adding or launch the custom command menu - made name parsing of the custom command optional --- terminatorlib/plugins/custom_commands.py | 247 +++++++++++++++++++---- 1 file changed, 211 insertions(+), 36 deletions(-) diff --git a/terminatorlib/plugins/custom_commands.py b/terminatorlib/plugins/custom_commands.py index e624358e..a6534eeb 100644 --- a/terminatorlib/plugins/custom_commands.py +++ b/terminatorlib/plugins/custom_commands.py @@ -1,8 +1,15 @@ # Terminator by Chris Jones # GPL v2 only +# +# -added keybinding, bookmark functionality +# -made name parsing to menu, optional +# - Vishweshwar Saran Singh Deo vssdeo@gmail.com +# TODO: tags + """custom_commands.py - Terminator Plugin to add custom command menu entries""" import sys import os +import time # Fix imports when testing this file directly if __name__ == '__main__': @@ -14,8 +21,17 @@ import terminatorlib.plugin as plugin from terminatorlib.config import Config from terminatorlib.translation import _ from terminatorlib.util import get_config_dir, err, dbg, gerr +from terminatorlib.terminator import Terminator -(CC_COL_ENABLED, CC_COL_NAME, CC_COL_COMMAND) = list(range(0,3)) +from terminatorlib.plugin import KeyBindUtil + +(CC_COL_ENABLED, CC_COL_NAME, CC_COL_NAME_PARSE, CC_COL_COMMAND) = list(range(0,4)) + +PluginActAdd = "plugin_add" +PluginActBmk = "plugin_bmk" + +PluginAddDesc = "Plugin Add Bookmark" +PluginBmkDesc = "Plugin Open Bookmark Preferences" # Every plugin you want Terminator to load *must* be listed in 'AVAILABLE' AVAILABLE = ['CustomCommandsMenu'] @@ -25,10 +41,27 @@ class CustomCommandsMenu(plugin.MenuItem): capabilities = ['terminal_menu'] cmd_list = {} conf_file = os.path.join(get_config_dir(),"custom_commands") + keyb = None def __init__( self): + + # In prev code dbox is needed if _create_command_dialog func is called + # after configure func where dbox is init. In case we call + # _create_command_dialog without calling configure func, like in quick + # bookmark add then we need to check. + self.dbox = None + config = Config() sections = config.plugin_get_config(self.__class__.__name__) + + self.connect_signals() + self.keyb = KeyBindUtil(config) + self.keyb.bindkey_check_config( + [PluginAddDesc , PluginActAdd, "b"]) + + self.keyb.bindkey_check_config( + [PluginBmkDesc , PluginActBmk, "b"]) + if not isinstance(sections, dict): return noord_cmds = [] @@ -38,23 +71,108 @@ class CustomCommandsMenu(plugin.MenuItem): print("CustomCommandsMenu: Ignoring section %s" % s) continue name = s["name"] + name_parse = s.get("name_parse", "True") command = s["command"] enabled = s["enabled"] and s["enabled"] or False if "position" in s: self.cmd_list[int(s["position"])] = {'enabled' : enabled, 'name' : name, + 'name_parse' : name_parse, 'command' : command } else: noord_cmds.append( {'enabled' : enabled, 'name' : name, + 'name_parse' : name_parse, 'command' : command } ) for cmd in noord_cmds: self.cmd_list[len(self.cmd_list)] = cmd + + def unload(self): + dbg("unloading") + for window in self.windows: + try: + window.disconnect_by_func(self.on_keypress) + except: + dbg("no connected signals") + + self.keyb.unbindkey( + [PluginAddDesc , PluginActAdd, "b"]) + self.keyb.unbindkey( + [PluginBmkDesc , PluginActBmk, "b"]) + + def connect_signals(self): + self.windows = Terminator().get_windows() + for window in self.windows: + window.connect('key-press-event', self.on_keypress) + + def get_last_exe_cmd(self): + cur_win = Terminator().last_focused_term.get_toplevel() + + #TODO: there has to be a better way to get the last command executed + focus_term = cur_win.get_focussed_terminal() + tmp_file = os.path.join(os.sep, 'tmp', 'term_cmd') + command = 'fc -n -l -1 -1 > ' + tmp_file + '; #bookmark last cmd\n' + focus_term.vte.feed_child(str(command).encode("utf-8")) + + fsz = 0 + count = 0 + while not (count == 2 or fsz): + time.sleep(0.1) + if os.path.exists(tmp_file): + fsz = os.path.getsize(tmp_file) + count += 1 + + last_cmd = None + try: + with open(tmp_file, 'r') as file: + last_cmd = file.read() + file.close() + except Exception as ex: + err('Unable to open ‘%s’ ex: (%s)' % (tmp_file, ex)) + + if os.path.exists(tmp_file): + os.remove(tmp_file) + + if last_cmd: + last_cmd = last_cmd.rstrip() + dbg('last exec cmd: (%s)' % last_cmd) + return last_cmd + + def get_last_exe_cmd_dialog_vars(self): + last_exe_cmd = self.get_last_exe_cmd() + + dialog_vars = { 'enabled' : True, + 'name' : last_exe_cmd, + 'name_parse': False, + 'command' : last_exe_cmd } + return dialog_vars + + + def on_keypress(self, widget, event): + act = self.keyb.keyaction(event) + dbg("keyaction: (%s) (%s)" % (str(act), event.keyval)) + + if act == PluginActAdd: + dbg("add bookmark") + + dialog_vars = self.get_last_exe_cmd_dialog_vars() + self.on_new(None, {'dialog_vars' : dialog_vars }) + self.update_cmd_list(self.store) + self._save_config() + return True + + if act == PluginActBmk: + dbg("open bookmark preferences") + cur_win = Terminator().last_focused_term.get_toplevel() + self.configure(cur_win) + return True + + def callback(self, menuitems, menu, terminal): """Add our menu items to the menu""" submenus = {} @@ -81,16 +199,20 @@ class CustomCommandsMenu(plugin.MenuItem): branch_names = command['name'].split('/')[:-1] target_submenu = submenu parent_submenu = submenu - for idx in range(len(branch_names)): - lookup_name = '/'.join(branch_names[0:idx+1]) - target_submenu = submenus.get(lookup_name, None) - if not target_submenu: - item = Gtk.MenuItem(_(branch_names[idx])) - parent_submenu.append(item) - target_submenu = Gtk.Menu() - item.set_submenu(target_submenu) - submenus[lookup_name] = target_submenu - parent_submenu = target_submenu + if not command['name_parse']: + leaf_name = command['name'] + branch_names = '' + else: + for idx in range(len(branch_names)): + lookup_name = '/'.join(branch_names[0:idx+1]) + target_submenu = submenus.get(lookup_name, None) + if not target_submenu: + item = Gtk.MenuItem(_(branch_names[idx])) + parent_submenu.append(item) + target_submenu = Gtk.Menu() + item.set_submenu(target_submenu) + submenus[lookup_name] = target_submenu + parent_submenu = target_submenu if iconinfo: image = Gtk.Image() image.set_from_icon_name(exe, Gtk.IconSize.MENU) @@ -109,11 +231,13 @@ class CustomCommandsMenu(plugin.MenuItem): for command in [ self.cmd_list[key] for key in sorted(self.cmd_list.keys()) ] : enabled = command['enabled'] name = command['name'] + name_parse = command['name_parse'] command = command['command'] item = {} item['enabled'] = enabled item['name'] = name + item['name_parse'] = name_parse item['command'] = command item['position'] = i @@ -128,6 +252,13 @@ class CustomCommandsMenu(plugin.MenuItem): for terminal in data['terminals']: terminal.vte.feed_child(command.encode()) + def setup_store(self): + self.store = Gtk.ListStore(bool, str, bool, str) + for command in [ self.cmd_list[key] for key in sorted(self.cmd_list.keys()) ]: + self.store.append([command['enabled'], command['name'], + command['name_parse'], command['command']]) + return self.store + def configure(self, widget, data = None): ui = {} dbox = Gtk.Dialog( @@ -149,11 +280,8 @@ class CustomCommandsMenu(plugin.MenuItem): icon = dbox.render_icon(Gtk.STOCK_DIALOG_INFO, Gtk.IconSize.BUTTON) dbox.set_icon(icon) - store = Gtk.ListStore(bool, str, str) + store = self.setup_store() - for command in [ self.cmd_list[key] for key in sorted(self.cmd_list.keys()) ]: - store.append([command['enabled'], command['name'], command['command']]) - treeview = Gtk.TreeView(store) #treeview.connect("cursor-changed", self.on_cursor_changed, ui) selection = treeview.get_selection() @@ -226,7 +354,11 @@ class CustomCommandsMenu(plugin.MenuItem): button.set_sensitive(False) ui['button_delete'] = button - + button = Gtk.Button(_("Bookmark Last Cmd")) + button_box.pack_start(button, False, True, 0) + ui['dialog_vars'] = self.get_last_exe_cmd_dialog_vars() + button.connect("clicked", self.on_new, ui) + ui['button_save_last_cmd'] = button hbox.pack_start(button_box, False, True, 0) self.dbox = dbox @@ -237,6 +369,7 @@ class CustomCommandsMenu(plugin.MenuItem): self._save_config() del(self.dbox) dbox.destroy() + self.dbox = None return @@ -245,12 +378,14 @@ class CustomCommandsMenu(plugin.MenuItem): self.cmd_list = {} i=0 while iter: - (enabled, name, command) = store.get(iter, + (enabled, name, name_parse, command) = store.get(iter, CC_COL_ENABLED, CC_COL_NAME, + CC_COL_NAME_PARSE, CC_COL_COMMAND) self.cmd_list[i] = {'enabled' : enabled, 'name': name, + 'name_parse' : name_parse, 'command' : command} iter = store.iter_next(iter) i = i + 1 @@ -278,7 +413,8 @@ class CustomCommandsMenu(plugin.MenuItem): data['button_edit'].set_sensitive(iter is not None) data['button_delete'].set_sensitive(iter is not None) - def _create_command_dialog(self, enabled_var = False, name_var = "", command_var = ""): + def _create_command_dialog(self, enabled_var = False, name_var = "", + name_parse_var = "", command_var = ""): dialog = Gtk.Dialog( _("New Command"), None, @@ -288,38 +424,72 @@ class CustomCommandsMenu(plugin.MenuItem): _("_OK"), Gtk.ResponseType.ACCEPT ) ) - dialog.set_transient_for(self.dbox) - table = Gtk.Table(3, 2) + # dbox is init in configure function, in case we want to + # create dialog directly + if self.dbox: + dialog.set_transient_for(self.dbox) + + table = Gtk.Table(4, 2) + table.set_row_spacings(5) + table.set_col_spacings(5) label = Gtk.Label(label=_("Enabled:")) + label.set_alignment(0, 0) table.attach(label, 0, 1, 0, 1) enabled = Gtk.CheckButton() enabled.set_active(enabled_var) table.attach(enabled, 1, 2, 0, 1) - label = Gtk.Label(label=_("Name:")) + label = Gtk.Label(label=_("Parse Name into SubMenu's:")) + label.set_alignment(0, 0) table.attach(label, 0, 1, 1, 2) + name_parse = Gtk.CheckButton() + name_parse.set_active(name_parse_var) + table.attach(name_parse, 1, 2, 1, 2) + + label = Gtk.Label(label=_("Name:")) + table.attach(label, 0, 1, 2, 3) name = Gtk.Entry() name.set_text(name_var) - table.attach(name, 1, 2, 1, 2) + table.attach(name, 1, 2, 2, 3) label = Gtk.Label(label=_("Command:")) - table.attach(label, 0, 1, 2, 3) + table.attach(label, 0, 1, 3, 4) command = Gtk.TextView() command.get_buffer().set_text(command_var) - table.attach(command, 1, 2, 2, 3) + table.attach(command, 1, 2, 3, 4) - dialog.vbox.pack_start(table, True, True, 0) + dialog.vbox.pack_start(table, True, True, 10) dialog.show_all() - return (dialog,enabled,name,command) + return (dialog,enabled,name,name_parse,command) def on_new(self, button, data): - (dialog,enabled,name,command) = self._create_command_dialog() + + #default values can be passed to dialogue window if required + enabled_var = '' + name_var = '' + name_parse_var= '' + command_var = '' + + if data and 'dialog_vars' in data: + dialog_vars = data.get('dialog_vars', {}) + enabled_var = dialog_vars.get('enabled', True) + name_var = dialog_vars.get('name', '') + name_parse_var= dialog_vars.get('name_parse', False) + command_var = dialog_vars.get('command', '') + + (dialog,enabled,name,name_parse,command) = self._create_command_dialog( + enabled_var = enabled_var, + name_var = name_var, + name_parse_var = name_parse_var, + command_var = command_var) + res = dialog.run() item = {} if res == Gtk.ResponseType.ACCEPT: item['enabled'] = enabled.get_active() item['name'] = name.get_text() + item['name_parse'] = name_parse.get_active() item['command'] = command.get_buffer().get_text(command.get_buffer().get_start_iter(), command.get_buffer().get_end_iter(), True) if item['name'] == '' or item['command'] == '': err = Gtk.MessageDialog(dialog, @@ -332,7 +502,9 @@ class CustomCommandsMenu(plugin.MenuItem): err.destroy() else: # we have a new command - store = data['treeview'].get_model() + store = data['treeview'].get_model() if 'treeview' in data else None + if not store: + store = self.setup_store() iter = store.get_iter_first() name_exist = False while iter != None: @@ -341,7 +513,8 @@ class CustomCommandsMenu(plugin.MenuItem): break iter = store.iter_next(iter) if not name_exist: - store.append((item['enabled'], item['name'], item['command'])) + store.append((item['enabled'], item['name'], + item['name_parse'], item['command'])) else: gerr(_("Name *%s* already exist") % item['name']) dialog.destroy() @@ -420,16 +593,17 @@ class CustomCommandsMenu(plugin.MenuItem): if not iter: return - (dialog,enabled,name,command) = self._create_command_dialog( - enabled_var = store.get_value(iter, CC_COL_ENABLED), - name_var = store.get_value(iter, CC_COL_NAME), - command_var = store.get_value(iter, CC_COL_COMMAND) - ) + (dialog,enabled,name,name_parse,command) = self._create_command_dialog( + enabled_var = store.get_value(iter, CC_COL_ENABLED), + name_var = store.get_value(iter, CC_COL_NAME), + name_parse_var = store.get_value(iter, CC_COL_NAME_PARSE), + command_var = store.get_value(iter, CC_COL_COMMAND)) res = dialog.run() item = {} if res == Gtk.ResponseType.ACCEPT: item['enabled'] = enabled.get_active() item['name'] = name.get_text() + item['name_parse'] = name_parse.get_active() item['command'] = command.get_buffer().get_text(command.get_buffer().get_start_iter(), command.get_buffer().get_end_iter(), True) if item['name'] == '' or item['command'] == '': err = Gtk.MessageDialog(dialog, @@ -452,14 +626,15 @@ class CustomCommandsMenu(plugin.MenuItem): store.set(iter, CC_COL_ENABLED,item['enabled'], CC_COL_NAME, item['name'], + CC_COL_NAME_PARSE, item['name_parse'], CC_COL_COMMAND, item['command'] ) else: gerr(_("Name *%s* already exist") % item['name']) dialog.destroy() - - + + if __name__ == '__main__': c = CustomCommandsMenu() c.configure(None, None) From 8911723518befa50e4031e623bd6e4d806a9d341 Mon Sep 17 00:00:00 2001 From: Vulcalien Date: Tue, 10 Oct 2023 15:47:39 +0200 Subject: [PATCH 06/26] Window: always ask confirmation to close, even with only one terminal --- terminatorlib/window.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 9cf7f688..3b9d88fa 100644 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -280,22 +280,14 @@ class Window(Container, Gtk.Window): def on_delete_event(self, window, event, data=None): """Handle a window close request""" maker = Factory() - if maker.isinstance(self.get_child(), 'Terminal'): - if self.is_zoomed(): - return(self.confirm_close(window, _('window'))) - else: - dbg('Only one child, closing is fine') - return(False) - elif maker.isinstance(self.get_child(), 'Container'): - return(self.confirm_close(window, _('window'))) + + if (maker.isinstance(self.get_child(), 'Terminal') or + maker.isinstance(self.get_child(), 'Container')): + confirm_close = self.construct_confirm_close(window, _('window')) + return (confirm_close != Gtk.ResponseType.ACCEPT) else: dbg('unknown child: %s' % self.get_child()) - - def confirm_close(self, window, type): - """Display a confirmation dialog when the user is closing multiple - terminals in one window""" - - return(not (self.construct_confirm_close(window, type) == Gtk.ResponseType.ACCEPT)) + return False # close anyway def on_destroy_event(self, widget, data=None): """Handle window destruction""" From 8c0c7ceb4fb6d3817824d78ed1c42a82ca9baade Mon Sep 17 00:00:00 2001 From: Vulcalien Date: Tue, 10 Oct 2023 17:03:21 +0200 Subject: [PATCH 07/26] Add a setting to specify when to ask to confirm before closing 'ask_before_closing' replaces 'suppress_multiple_term_dialog'. The function 'Container.construct_confirm_close' was significantly modified. The terminator_config manpage has been updated. --- doc/terminator_config.5 | 15 +++++------ doc/terminator_config.adoc | 10 +++---- terminatorlib/config.py | 2 +- terminatorlib/container.py | 54 +++++++++++++++++++++++++++----------- terminatorlib/notebook.py | 30 ++++++++++----------- terminatorlib/window.py | 9 ++++--- 6 files changed, 70 insertions(+), 50 deletions(-) diff --git a/doc/terminator_config.5 b/doc/terminator_config.5 index 04b1e084..4af2ca9b 100644 --- a/doc/terminator_config.5 +++ b/doc/terminator_config.5 @@ -1,13 +1,13 @@ '\" t .\" Title: terminator_config .\" Author: [see the "AUTHOR(S)" section] -.\" Generator: Asciidoctor 2.0.18 -.\" Date: 2023-04-22 +.\" Generator: Asciidoctor 2.0.16 +.\" Date: 2023-10-10 .\" Manual: Manual for Terminator .\" Source: Terminator .\" Language: English .\" -.TH "TERMINATOR_CONFIG" "5" "2023-04-22" "Terminator" "Manual for Terminator" +.TH "TERMINATOR_CONFIG" "5" "2023-10-10" "Terminator" "Manual for Terminator" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 @@ -130,12 +130,11 @@ If set to True, the window will resize in step with font sizes. Default value: \fBFalse\fP .RE .sp -\fBsuppress_multiple_term_dialog\fP = \fIboolean\fP +\fBask_before_closing\fP = \fIstring\fP .RS 4 -If set to True, Terminator will ask for confirmation when closing -multiple terminals. -.br -Default value: \fBFalse\fP +Specify when to ask for confirmation before closing a window or a tab. +Can be any of: \*(Aqalways\*(Aq, \*(Aqmultiple_terminals\*(Aq, \*(Aqnever\*(Aq. +Default value: \fBmultiple_terminals\fP .RE .sp \fBborderless\fP = \fIboolean\fP diff --git a/doc/terminator_config.adoc b/doc/terminator_config.adoc index bcca88ff..d0b89fe8 100644 --- a/doc/terminator_config.adoc +++ b/doc/terminator_config.adoc @@ -2,7 +2,7 @@ :doctype: manpage :manmanual: Manual for Terminator :mansource: Terminator -:revdate: 2023-04-22 +:revdate: 2023-10-10 :docdate: {revdate} == NAME @@ -90,10 +90,10 @@ Default value: *False* If set to True, the window will resize in step with font sizes. + Default value: *False* -*suppress_multiple_term_dialog* = _boolean_:: -If set to True, Terminator will ask for confirmation when closing -multiple terminals. + -Default value: *False* +*ask_before_closing* = _string_:: +Specify when to ask for confirmation before closing a window or a tab. +Can be any of: 'always', 'multiple_terminals', 'never'. +Default value: *multiple_terminals* // --- Window appearance --- diff --git a/terminatorlib/config.py b/terminatorlib/config.py index 4fbb907d..45350611 100644 --- a/terminatorlib/config.py +++ b/terminatorlib/config.py @@ -105,7 +105,7 @@ DEFAULTS = { 'enabled_plugins' : ['LaunchpadBugURLHandler', 'LaunchpadCodeURLHandler', 'APTURLHandler'], - 'suppress_multiple_term_dialog': False, + 'ask_before_closing' : 'multiple_terminals', 'always_split_with_profile': False, 'putty_paste_style' : False, 'putty_paste_style_source_clipboard': False, diff --git a/terminatorlib/container.py b/terminatorlib/container.py index ba7ab8d3..14192779 100644 --- a/terminatorlib/container.py +++ b/terminatorlib/container.py @@ -152,33 +152,54 @@ class Container(object): """Unzoom a terminal""" raise NotImplementedError('unzoom') - def construct_confirm_close(self, window, reqtype): + def construct_confirm_close(self, window, child): """Create a confirmation dialog for closing things""" - + maker = Factory() + + has_multiple_terms = False + if not maker.isinstance(child, 'Terminal'): + has_multiple_terms = True + elif maker.isinstance(self, 'Window'): + has_multiple_terms = self.is_zoomed() + # skip this dialog if applicable - if self.config['suppress_multiple_term_dialog']: + if self.config['ask_before_closing'] == 'never': return Gtk.ResponseType.ACCEPT - + elif self.config['ask_before_closing'] == 'multiple_terminals': + if not has_multiple_terms: + return Gtk.ResponseType.ACCEPT + + # text + confirm_button_text = (_('Close _Terminals') if has_multiple_terms + else _('Close _Terminal')) + big_label_text = (_('Close multiple terminals?') if has_multiple_terms + else _('Close terminal?')) + + if not has_multiple_terms: + description_text = _('Do you really wish to close this terminal?') + elif maker.isinstance(self, 'Window'): + description_text = _('This window has several terminals open. Closing \ +the window will also close all terminals within it.') + elif maker.isinstance(self, 'Notebook'): + description_text = _('This tab has several terminals open. Closing \ +the tab will also close all terminals within it.') + else: + description_text = '' + + # dialog GUI dialog = Gtk.Dialog(_('Close?'), window, Gtk.DialogFlags.MODAL) dialog.set_resizable(False) dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT) c_all = dialog.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.ACCEPT) c_all.get_children()[0].get_children()[0].get_children()[1].set_label( - _('Close _Terminals')) + confirm_button_text) - primary = Gtk.Label(label=_('Close multiple terminals?')) + primary = Gtk.Label(label=_('' + big_label_text + '')) primary.set_use_markup(True) primary.set_alignment(0, 0.5) - if reqtype == 'window': - label_text = _('This window has several terminals open. Closing \ -the window will also close all terminals within it.') - elif reqtype == 'tab': - label_text = _('This tab has several terminals open. Closing \ -the tab will also close all terminals within it.') - else: - label_text = '' - secondary = Gtk.Label(label=label_text) + + secondary = Gtk.Label(label=description_text) secondary.set_line_wrap(True) labels = Gtk.VBox() @@ -203,7 +224,8 @@ the tab will also close all terminals within it.') # set configuration self.config.base.reload() - self.config['suppress_multiple_term_dialog'] = checkbox.get_active() + if checkbox.get_active(): + self.config['ask_before_closing'] = 'never' self.config.save() dialog.destroy() diff --git a/terminatorlib/notebook.py b/terminatorlib/notebook.py index eb630b17..de67c2ac 100644 --- a/terminatorlib/notebook.py +++ b/terminatorlib/notebook.py @@ -379,35 +379,33 @@ class Notebook(Container, Gtk.Notebook): maker = Factory() child = nb.get_nth_page(tabnum) + confirm_close = self.construct_confirm_close(self.window, child) + if confirm_close != Gtk.ResponseType.ACCEPT: + dbg('user cancelled request') + return + if maker.isinstance(child, 'Terminal'): dbg('child is a single Terminal') + del nb.last_active_term[child] child.close() # FIXME: We only do this del and return here to avoid removing the # page below, which child.close() implicitly does del(label) - return elif maker.isinstance(child, 'Container'): dbg('child is a Container') - result = self.construct_confirm_close(self.window, _('tab')) - if result == Gtk.ResponseType.ACCEPT: - containers = None - objects = None - containers, objects = enumerate_descendants(child) + containers = None + objects = None + containers, objects = enumerate_descendants(child) - while len(objects) > 0: - descendant = objects.pop() - descendant.close() - while Gtk.events_pending(): - Gtk.main_iteration() - return - else: - dbg('user cancelled request') - return + while len(objects) > 0: + descendant = objects.pop() + descendant.close() + while Gtk.events_pending(): + Gtk.main_iteration() else: err('Notebook::closetab: child is unknown type %s' % child) - return def resizeterm(self, widget, keyname): """Handle a keyboard event requesting a terminal resize""" diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 3b9d88fa..69e999f9 100644 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -281,12 +281,13 @@ class Window(Container, Gtk.Window): """Handle a window close request""" maker = Factory() - if (maker.isinstance(self.get_child(), 'Terminal') or - maker.isinstance(self.get_child(), 'Container')): - confirm_close = self.construct_confirm_close(window, _('window')) + child = self.get_child() + if (maker.isinstance(child, 'Terminal') or + maker.isinstance(child, 'Container')): + confirm_close = self.construct_confirm_close(window, child) return (confirm_close != Gtk.ResponseType.ACCEPT) else: - dbg('unknown child: %s' % self.get_child()) + dbg('unknown child: %s' % child) return False # close anyway def on_destroy_event(self, widget, data=None): From a5ddcbe2556aa32055664aafc8fae6ff4e5e280c Mon Sep 17 00:00:00 2001 From: Vishweshwar Saran Singh Deo Date: Tue, 17 Oct 2023 01:11:21 +0530 Subject: [PATCH 08/26] [bug 706] 706-Favorites-Bookmarks-Plugin #706 - the dialog window taking command for bookmark via shortcut keybinding will have focus on OK Button for faster interaction - fixed a transient window issue for loading pref window via shortcut and context menu - filed new command text area was getting filled with last command - fixed list store init --- terminatorlib/plugins/custom_commands.py | 26 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/terminatorlib/plugins/custom_commands.py b/terminatorlib/plugins/custom_commands.py index a6534eeb..99d45ac5 100644 --- a/terminatorlib/plugins/custom_commands.py +++ b/terminatorlib/plugins/custom_commands.py @@ -160,6 +160,7 @@ class CustomCommandsMenu(plugin.MenuItem): if act == PluginActAdd: dbg("add bookmark") + self.setup_store() dialog_vars = self.get_last_exe_cmd_dialog_vars() self.on_new(None, {'dialog_vars' : dialog_vars }) self.update_cmd_list(self.store) @@ -167,13 +168,13 @@ class CustomCommandsMenu(plugin.MenuItem): return True if act == PluginActBmk: - dbg("open bookmark preferences") - cur_win = Terminator().last_focused_term.get_toplevel() - self.configure(cur_win) + dbg("open custom command preferences") + self.configure(None) return True def callback(self, menuitems, menu, terminal): + """Add our menu items to the menu""" submenus = {} item = Gtk.MenuItem.new_with_mnemonic(_('_Custom Commands')) @@ -270,7 +271,8 @@ class CustomCommandsMenu(plugin.MenuItem): _("_OK"), Gtk.ResponseType.ACCEPT ) ) - dbox.set_transient_for(widget.get_toplevel()) + if widget: + dbox.set_transient_for(widget.get_toplevel()) icon_theme = Gtk.IconTheme.get_default() if icon_theme.lookup_icon('terminator-custom-commands', 48, 0): @@ -356,8 +358,7 @@ class CustomCommandsMenu(plugin.MenuItem): button = Gtk.Button(_("Bookmark Last Cmd")) button_box.pack_start(button, False, True, 0) - ui['dialog_vars'] = self.get_last_exe_cmd_dialog_vars() - button.connect("clicked", self.on_new, ui) + button.connect("clicked", self.on_last_exe_cmd, ui) ui['button_save_last_cmd'] = button hbox.pack_start(button_box, False, True, 0) @@ -424,8 +425,16 @@ class CustomCommandsMenu(plugin.MenuItem): _("_OK"), Gtk.ResponseType.ACCEPT ) ) + + #since we call this via shortcut keybinding + #lets focus on OK button + buttonbox = dialog.get_action_area() + buttons = buttonbox.get_children() + dialog.set_focus(buttons[1]) + # dbox is init in configure function, in case we want to # create dialog directly + if self.dbox: dialog.set_transient_for(self.dbox) @@ -463,6 +472,11 @@ class CustomCommandsMenu(plugin.MenuItem): dialog.show_all() return (dialog,enabled,name,name_parse,command) + def on_last_exe_cmd(self, button, data): + new_data = data.copy() + new_data['dialog_vars'] = self.get_last_exe_cmd_dialog_vars() + self.on_new(button, new_data) + def on_new(self, button, data): #default values can be passed to dialogue window if required From 9000327973121d4c6b33b5a6e5f900c31654e55a Mon Sep 17 00:00:00 2001 From: Vishweshwar Saran Singh Deo Date: Tue, 17 Oct 2023 19:49:57 +0530 Subject: [PATCH 09/26] [bug 843] 843-Plugin-SaveLastSessionLayout-not-saving-layout-when-user-logs-out-or-shutdown-or-restart #843 - fixed Plugin SaveLastSessionLayout not saving layout when user logs out or shutdown or restart - signals added --- terminatorlib/plugins/save_last_session_layout.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/terminatorlib/plugins/save_last_session_layout.py b/terminatorlib/plugins/save_last_session_layout.py index 12a360ed..1d4e37c2 100644 --- a/terminatorlib/plugins/save_last_session_layout.py +++ b/terminatorlib/plugins/save_last_session_layout.py @@ -1,4 +1,5 @@ import os +import signal import sys # Fix imports when testing this file directly @@ -44,9 +45,21 @@ class SaveLastSessionLayout(plugin.Plugin): r = config.add_layout("SaveLastSessionLayout", current_layout) config.save() return True + + def signal_handler(self,signum, frame): + + signame = signal.Signals(signum).name + dbg('signal handler called:signal %s (%s)' % + (signame, signum)) + self.save_session_layout() def connect_signals(self): dbg("SaveLastSessionLayout connect_signals") + + signal.signal(signal.SIGTERM, self.signal_handler) + signal.signal(signal.SIGCHLD, self.signal_handler) + signal.signal(signal.SIGHUP, self.signal_handler) + n = 0 for term in Terminator().terminals: dbg("SaveLastSessionLayout connect_signals to term num:(%d)" % n) From ff45920874714bfd68ca0c2c9190f185e49a8f2b Mon Sep 17 00:00:00 2001 From: Vishweshwar Saran Singh Deo Date: Fri, 20 Oct 2023 23:50:25 +0530 Subject: [PATCH 10/26] [bug 835] 835-crash-after-unzooming-a-single-terminal-inside-a-tab #835 -removed previous code to start fresh -added event type for tab-change since other way of identifying zoomed widget was not simple and clear -emit tab-change is done at a single point now in notebook.py --- terminatorlib/notebook.py | 6 ++---- terminatorlib/terminal.py | 26 +++++++++++++------------- terminatorlib/terminator.py | 10 ++-------- terminatorlib/window.py | 8 +++++++- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/terminatorlib/notebook.py b/terminatorlib/notebook.py index eb630b17..358c82d4 100644 --- a/terminatorlib/notebook.py +++ b/terminatorlib/notebook.py @@ -343,10 +343,6 @@ class Notebook(Container, Gtk.Notebook): self.set_current_page(tabpos) self.show_all() if maker.isinstance(term_widget, 'Terminal'): - #notify plugins of tab-change - dbg("emit tab-change for tabpos: %s " % tabpos) - term_widget.emit('tab-change', tabpos) - self.set_current_page(tabpos) widget.grab_focus() def wrapcloseterm(self, widget): @@ -528,6 +524,8 @@ class Notebook(Container, Gtk.Notebook): # if we can't find a last active term we must be starting up if term is not None: GObject.idle_add(term.ensure_visible_and_focussed) + dbg('emit tab-change type: targe-plugin for page:%s' % page_num) + term.emit('tab-change', page_num, 'target-plugin') return True def on_scroll_event(self, notebook, event): diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index 7ed248b0..e19b14f3 100644 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -68,7 +68,7 @@ class Terminal(Gtk.VBox): 'navigate': (GObject.SignalFlags.RUN_LAST, None, (GObject.TYPE_STRING,)), 'tab-change': (GObject.SignalFlags.RUN_LAST, None, - (GObject.TYPE_INT,)), + (GObject.TYPE_INT,GObject.TYPE_STRING,)), 'group-all': (GObject.SignalFlags.RUN_LAST, None, ()), 'group-all-toggle': (GObject.SignalFlags.RUN_LAST, None, ()), 'move-tab': (GObject.SignalFlags.RUN_LAST, None, @@ -1985,40 +1985,40 @@ class Terminal(Gtk.VBox): self.zoom() def key_next_tab(self): - self.emit('tab-change', -1) + self.emit('tab-change', -1, None) def key_prev_tab(self): - self.emit('tab-change', -2) + self.emit('tab-change', -2, None) def key_switch_to_tab_1(self): - self.emit('tab-change', 0) + self.emit('tab-change', 0, None) def key_switch_to_tab_2(self): - self.emit('tab-change', 1) + self.emit('tab-change', 1, None) def key_switch_to_tab_3(self): - self.emit('tab-change', 2) + self.emit('tab-change', 2, None) def key_switch_to_tab_4(self): - self.emit('tab-change', 3) + self.emit('tab-change', 3, None) def key_switch_to_tab_5(self): - self.emit('tab-change', 4) + self.emit('tab-change', 4, None) def key_switch_to_tab_6(self): - self.emit('tab-change', 5) + self.emit('tab-change', 5, None) def key_switch_to_tab_7(self): - self.emit('tab-change', 6) + self.emit('tab-change', 6, None) def key_switch_to_tab_8(self): - self.emit('tab-change', 7) + self.emit('tab-change', 7, None) def key_switch_to_tab_9(self): - self.emit('tab-change', 8) + self.emit('tab-change', 8, None) def key_switch_to_tab_10(self): - self.emit('tab-change', 9) + self.emit('tab-change', 9, None) def key_reset(self): self.vte.reset (True, False) diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index aede3336..d133667d 100644 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -186,13 +186,6 @@ class Terminator(Borg): for terminal in self.terminals: dbg('checking: %s (%s)' % (terminal.uuid.urn, terminal)) if terminal.uuid.urn == uuid: - if terminal.get_toplevel().is_child_notebook(): - topchild = terminal.get_toplevel().get_child() - current_page = topchild.get_current_page() - #we need to emit signal for plugin and retain same page - dbg("current_page for tab-change-signal:%s" % current_page) - terminal.emit('tab-change', current_page) - return terminal return None @@ -203,6 +196,7 @@ class Terminator(Borg): dbg('checking: %s (%s)' % (window.uuid.urn, window)) if window.uuid.urn == uuid: return window + return None def new_window(self, cwd=None, profile=None): @@ -217,7 +211,7 @@ class Terminator(Borg): window.add(terminal) window.show(True) terminal.spawn_child() - terminal.emit('tab-change', 0) + terminal.emit('tab-change', 0, None) return(window, terminal) diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 9cf7f688..c76976d0 100644 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -738,8 +738,14 @@ class Window(Container, Gtk.Window): def disable_geometry_hints(self): self.set_geometry_hints(None, None, 0) - def tab_change(self, widget, num=None): + def tab_change(self, widget, num=None, event_type=None): """Change to a specific tab""" + + #we return if event_type is set by a component + #we only handle default + if event_type: + return + if self.is_zoomed(): self.unzoom() From c7c9fd0d4b1da29c71f8079b5661c4f308385196 Mon Sep 17 00:00:00 2001 From: Vishweshwar Saran Singh Deo Date: Tue, 24 Oct 2023 10:14:19 +0530 Subject: [PATCH 11/26] [bug 835] 835-crash-after-unzooming-a-single-terminal-inside-a-tab #835 - removing the tab-change event dependency and having a simpler solution with focus-in --- terminatorlib/notebook.py | 2 -- terminatorlib/terminal.py | 26 +++++++++++++------------- terminatorlib/terminator.py | 3 +-- terminatorlib/window.py | 8 +------- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/terminatorlib/notebook.py b/terminatorlib/notebook.py index 358c82d4..9cb6bd48 100644 --- a/terminatorlib/notebook.py +++ b/terminatorlib/notebook.py @@ -524,8 +524,6 @@ class Notebook(Container, Gtk.Notebook): # if we can't find a last active term we must be starting up if term is not None: GObject.idle_add(term.ensure_visible_and_focussed) - dbg('emit tab-change type: targe-plugin for page:%s' % page_num) - term.emit('tab-change', page_num, 'target-plugin') return True def on_scroll_event(self, notebook, event): diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index e19b14f3..7ed248b0 100644 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -68,7 +68,7 @@ class Terminal(Gtk.VBox): 'navigate': (GObject.SignalFlags.RUN_LAST, None, (GObject.TYPE_STRING,)), 'tab-change': (GObject.SignalFlags.RUN_LAST, None, - (GObject.TYPE_INT,GObject.TYPE_STRING,)), + (GObject.TYPE_INT,)), 'group-all': (GObject.SignalFlags.RUN_LAST, None, ()), 'group-all-toggle': (GObject.SignalFlags.RUN_LAST, None, ()), 'move-tab': (GObject.SignalFlags.RUN_LAST, None, @@ -1985,40 +1985,40 @@ class Terminal(Gtk.VBox): self.zoom() def key_next_tab(self): - self.emit('tab-change', -1, None) + self.emit('tab-change', -1) def key_prev_tab(self): - self.emit('tab-change', -2, None) + self.emit('tab-change', -2) def key_switch_to_tab_1(self): - self.emit('tab-change', 0, None) + self.emit('tab-change', 0) def key_switch_to_tab_2(self): - self.emit('tab-change', 1, None) + self.emit('tab-change', 1) def key_switch_to_tab_3(self): - self.emit('tab-change', 2, None) + self.emit('tab-change', 2) def key_switch_to_tab_4(self): - self.emit('tab-change', 3, None) + self.emit('tab-change', 3) def key_switch_to_tab_5(self): - self.emit('tab-change', 4, None) + self.emit('tab-change', 4) def key_switch_to_tab_6(self): - self.emit('tab-change', 5, None) + self.emit('tab-change', 5) def key_switch_to_tab_7(self): - self.emit('tab-change', 6, None) + self.emit('tab-change', 6) def key_switch_to_tab_8(self): - self.emit('tab-change', 7, None) + self.emit('tab-change', 7) def key_switch_to_tab_9(self): - self.emit('tab-change', 8, None) + self.emit('tab-change', 8) def key_switch_to_tab_10(self): - self.emit('tab-change', 9, None) + self.emit('tab-change', 9) def key_reset(self): self.vte.reset (True, False) diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index d133667d..86aa0999 100644 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -196,7 +196,6 @@ class Terminator(Borg): dbg('checking: %s (%s)' % (window.uuid.urn, window)) if window.uuid.urn == uuid: return window - return None def new_window(self, cwd=None, profile=None): @@ -211,7 +210,7 @@ class Terminator(Borg): window.add(terminal) window.show(True) terminal.spawn_child() - terminal.emit('tab-change', 0, None) + terminal.emit('tab-change', 0) return(window, terminal) diff --git a/terminatorlib/window.py b/terminatorlib/window.py index c76976d0..9cf7f688 100644 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -738,14 +738,8 @@ class Window(Container, Gtk.Window): def disable_geometry_hints(self): self.set_geometry_hints(None, None, 0) - def tab_change(self, widget, num=None, event_type=None): + def tab_change(self, widget, num=None): """Change to a specific tab""" - - #we return if event_type is set by a component - #we only handle default - if event_type: - return - if self.is_zoomed(): self.unzoom() From b052da7efc8e75fd7bb7322b3cb4246a21a40afc Mon Sep 17 00:00:00 2001 From: Vishweshwar Saran Singh Deo Date: Tue, 24 Oct 2023 11:34:03 +0530 Subject: [PATCH 12/26] [bug 846] 846-mouseless-keyboard-url-open-fails-to-extract-text-and-clear-search-between-commands #846 - this includes the changes in #835 since its required to decouple - made plugin dependent on focus-in and removed tab-change - plugin is now decoupled from main terminator code --- .../plugins/mousefree_url_handler.py | 70 ++++++++++++------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/terminatorlib/plugins/mousefree_url_handler.py b/terminatorlib/plugins/mousefree_url_handler.py index 2b7da543..e69dd40d 100644 --- a/terminatorlib/plugins/mousefree_url_handler.py +++ b/terminatorlib/plugins/mousefree_url_handler.py @@ -48,6 +48,8 @@ class MouseFreeURLHandler(plugin.Plugin): keyb = KeyBindUtil(config) matches = [] matches_ptr = -1 + vte = None + cur_term = None #basic pattern searchtext = "https?\:\/\/[^\s]+[\/\w]" @@ -64,16 +66,9 @@ class MouseFreeURLHandler(plugin.Plugin): [PluginUrlLaunch, PluginUrlActLaunch, "Return"]) def connect_signals(self): - #this is not giving list off all connected terminals in window - dbg("direct terminals: %s" % Terminator().terminals) - - #get list of all terminals indirectly - terms = Terminator().terminals[0] - dbg("in-direct get terminals: %s" % terms.terminator.terminals) - - for term in terms.terminator.terminals: + for term in Terminator().terminals: dbg("signal connect term:%s" % term) - term.connect('tab-change', self.on_focus_in) + term.connect('focus-in', self.on_focus_in) self.windows = Terminator().get_windows() for window in self.windows: @@ -81,9 +76,7 @@ class MouseFreeURLHandler(plugin.Plugin): def unload(self): dbg("unloading") - #disconnect all signals and events - terms = Terminator().terminals[0] - for term in terms.terminator.terminals: + for term in Terminator().terminals: try: term.disconnect_by_func(self.on_focus_in) except: @@ -107,23 +100,30 @@ class MouseFreeURLHandler(plugin.Plugin): def extract(self): #can we do extract more efficiently col, row = self.vte.get_cursor_position() - (txt, attr) = self.vte.get_text_range(0,0,row, col) + (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_term(self): - return Terminator().last_focused_term - def get_selected_url(self): if len(self.matches): - dbg("found selected URL (%s %s %s)" % - (self.matches_ptr, self.matches[self.matches_ptr], self)) + 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 on_focus_in(self, widget, event): - dbg("focus-in clear url search buffer: %s" % self) + 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): @@ -160,8 +160,8 @@ class MouseFreeURLHandler(plugin.Plugin): self.get_selected_url() # dbg url print self.vte.copy_clipboard() return True - - self.vte.search_find_previous() + else: + self.vte.search_find_previous() if self.matches_ptr > 0: self.matches_ptr -= 1 @@ -174,26 +174,44 @@ class MouseFreeURLHandler(plugin.Plugin): if act == PluginUrlActEsc: self.clear_search() + return if act == PluginUrlActLaunch: url = self.get_selected_url() if url: - self.get_term().open_url(url, prepare=False) + 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.get_term(): - self.vte = self.get_term().get_vte() + + 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 = self.get_term().get_vte() self.vte.search_set_wrap_around(True) regex_flags_pcre2 = (regex.FLAGS_PCRE2 | regex.PCRE2_CASELESS) From 03e5769bd175447aa586735bc5d7ad5094216eae Mon Sep 17 00:00:00 2001 From: Vishweshwar Saran Singh Deo Date: Thu, 26 Oct 2023 23:38:59 +0530 Subject: [PATCH 13/26] [bug 852] - 852-terminator_py_get_focussed_terminal_always_returns_none - added get_vte().has_focus() for a valid focussed terminal to return --- terminatorlib/terminator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index aede3336..1889cab6 100644 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -614,7 +614,7 @@ class Terminator(Borg): def get_focussed_terminal(self): """iterate over all the terminals to find which, if any, has focus""" for terminal in self.terminals: - if terminal.has_focus(): + if terminal.get_vte().has_focus(): return(terminal) return(None) From 3e7145034a7f1c5535b820e90bfc54f97e9ceaef Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:24:25 +0000 Subject: [PATCH 14/26] Translate po/terminator.pot in ru 100% translated source file: 'po/terminator.pot' on 'ru'. --- po/ru.po | 341 +++++++++++++++++++------------------------------------ 1 file changed, 114 insertions(+), 227 deletions(-) diff --git a/po/ru.po b/po/ru.po index 6ccdae4e..4473f3d0 100644 --- a/po/ru.po +++ b/po/ru.po @@ -2,27 +2,25 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# # Translators: # Gnome Terminator , 2020 -# +# Mariya Shikunova , 2023 +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-01-22 00:51+0100\n" +"POT-Creation-Date: 2022-10-19 09:29-0400\n" "PO-Revision-Date: 2020-04-22 08:11+0000\n" -"Last-Translator: Gnome Terminator , 2020\n" -"Language-Team: Russian (https://www.transifex.com/terminator/teams/109338/" -"ru/)\n" -"Language: ru\n" +"Last-Translator: Mariya Shikunova , 2023\n" +"Language-Team: Russian (https://app.transifex.com/terminator/teams/109338/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" -"%100>=11 && n%100<=14)? 2 : 3);\n" +"Language: ru\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" #. Command uuid req. Description #: ../remotinator.py:39 @@ -47,7 +45,7 @@ msgstr "Вывести список всех терминалов" #: ../remotinator.py:44 msgid "Get the uuid of the current focused terminal" -msgstr "" +msgstr "Вывести UUID текущего терминала" #: ../remotinator.py:45 msgid "Get the UUID of a parent window" @@ -67,23 +65,23 @@ msgstr "Вывести заголовок родительской вкладк #: ../remotinator.py:49 msgid "Set the title of a parent tab" -msgstr "" +msgstr "Указать заголовок родительской вкладки" #: ../remotinator.py:50 msgid "Set the background image" -msgstr "" +msgstr "Указать фоновое изображение" #: ../remotinator.py:51 msgid "Set the background image for all terminals" -msgstr "" +msgstr "Указать фоновое изображение для всех терминалов" #: ../remotinator.py:52 msgid "Switch current terminal profile" -msgstr "" +msgstr "Переключиться на профиль текущего терминала" #: ../remotinator.py:53 msgid "Switch profile of all currently running terminals" -msgstr "" +msgstr "Переключиться на профиль всех текущих запущенных терминалов" #: ../remotinator.py:70 #, python-format @@ -112,23 +110,24 @@ msgstr "" #: ../remotinator.py:80 msgid "Profile name to switch to" -msgstr "" +msgstr "Имя профиля для переключения" #: ../remotinator.py:83 msgid "File to pass to command" -msgstr "" +msgstr "Файл для передачи команде" #: ../remotinator.py:86 msgid "Command to run in new terminal" -msgstr "" +msgstr "Команда для выполнения в новом терминале" #: ../remotinator.py:89 msgid "Tab name to set. Only used with \"set_tab_title\" command." msgstr "" +"Имя вкладки для установки. Используется только с командой «set_tab_title»." #: ../remotinator.py:92 msgid "Tab name to set." -msgstr "" +msgstr "Имя вкладки для установки." #: ../data/terminator.desktop.in.h:1 ../data/terminator.appdata.xml.in.h:1 #: ../terminatorlib/plugins/activitywatch.py:83 @@ -150,9 +149,14 @@ msgstr "Технологии будущего для терминалов" msgid "" "A power-user tool for arranging terminals. It is inspired by programs such " "as gnome-multi-term, quadkonsole, etc. in that the main focus is arranging " -"terminals in grids (tabs is the most common default method, which Terminator " -"also supports)." +"terminals in grids (tabs is the most common default method, which Terminator" +" also supports)." msgstr "" +"Удобный инструмент для настройки терминалов. На его создание вдохновили " +"такие программы, как gnome-multi-term и quadkonsole, в которых основное " +"внимание уделяется размещению терминалов в виде сетки (наиболее " +"распространённый метод по умолчанию — вкладки, который также поддерживается " +"в Terminator)" #: ../data/terminator.appdata.xml.in.h:5 msgid "" @@ -161,6 +165,10 @@ msgid "" "out in different directions with useful features for sysadmins and other " "users." msgstr "" +"Большая часть поведения Terminator основана на GNOME Terminal, и со временем" +" в приложение добавляется всё больше функций из него, однако также есть " +"планы по расширению Terminator в разных направлениях с помощью полезных " +"функций для системных администраторов и других пользователей." #: ../data/terminator.appdata.xml.in.h:6 msgid "Some highlights:" @@ -180,7 +188,7 @@ msgstr "Перетаскивание и изменение порядка тер #: ../data/terminator.appdata.xml.in.h:10 msgid "Lots of keyboard shortcuts" -msgstr "Множество сочетаний клавиш быстрого доступа" +msgstr "Множество комбинаций клавиш быстрого доступа" #: ../data/terminator.appdata.xml.in.h:11 msgid "Save multiple layouts and profiles via GUI preferences editor" @@ -225,7 +233,7 @@ msgid "" "This window has several terminals open. Closing the window will also close " "all terminals within it." msgstr "" -"Это окно имеет несколько запущенных терминалов. Закрытие окна повлечет за " +"Это окно имеет несколько запущенных терминалов. Закрытие окна повлечёт за " "собой так же и их закрытие." #: ../terminatorlib/container.py:178 @@ -233,8 +241,8 @@ msgid "" "This tab has several terminals open. Closing the tab will also close all " "terminals within it." msgstr "" -"Эта вкладка имеет несколько запущенных терминалов. Ее закрытие повлечет за " -"собой так же и их закрытие." +"Эта вкладка имеет несколько запущенных терминалов. Её закрытие повлечёт за " +"собой также и их закрытие." #: ../terminatorlib/container.py:198 msgid "Do not show this message next time" @@ -299,8 +307,8 @@ msgid "" "Use the rest of the command line as a command to execute inside the " "terminal, and its arguments" msgstr "" -"Использовать для выполнения в терминале остаток командной строки как команду " -"и её аргументы" +"Использовать для выполнения в терминале остаток командной строки как команду" +" и её аргументы" #: ../terminatorlib/optionparse.py:69 msgid "Specify a config file" @@ -308,7 +316,7 @@ msgstr "Укажите файл конфигурации" #: ../terminatorlib/optionparse.py:71 msgid "Specify a partial config json file" -msgstr "" +msgstr "Укажите частичный json-файл конфигурации" #: ../terminatorlib/optionparse.py:76 msgid "Set the working directory" @@ -316,7 +324,8 @@ msgstr "Установить рабочий каталог" #: ../terminatorlib/optionparse.py:77 msgid "Set a custom icon for the window (by file or name)" -msgstr "Установить пользовательский значок для этого окна (по файлу или имени)" +msgstr "" +"Установить пользовательский значок для этого окна (по файлу или имени)" #: ../terminatorlib/optionparse.py:80 msgid "Set a custom WM_WINDOW_ROLE property on the window" @@ -352,19 +361,19 @@ msgstr "Разделенный запятыми список методов дл #: ../terminatorlib/optionparse.py:96 msgid "If Terminator is already running, just open a new tab" -msgstr "Если Терминатор уже запущен, просто откройте новую вкладку" +msgstr "Если Terminator уже запущен, просто откройте новую вкладку" #: ../terminatorlib/optionparse.py:98 msgid "If Terminator is already running, just unhide all hidden windows" -msgstr "" +msgstr "Если Terminator уже запущен, раскройте все скрытые окна" #: ../terminatorlib/optionparse.py:100 msgid "List all profiles" -msgstr "" +msgstr "Вывести список всех профилей" #: ../terminatorlib/optionparse.py:102 msgid "List all layouts" -msgstr "" +msgstr "Вывести список всех компоновок" #: ../terminatorlib/plugins/activitywatch.py:54 msgid "Watch for _activity" @@ -482,7 +491,7 @@ msgstr "Название *%s* уже существует" #: ../terminatorlib/plugins/dir_open.py:26 msgid "Open current directory" -msgstr "" +msgstr "Открыть текущий каталог" #: ../terminatorlib/plugins/logger.py:21 #: ../terminatorlib/plugins/terminalshot.py:21 @@ -552,15 +561,15 @@ msgstr "Оставить терминал открытым" #: ../terminatorlib/preferences.glade.h:11 msgid "Black on light yellow" -msgstr "Черный на светло-жёлтом" +msgstr "Чёрный на светло-жёлтом" #: ../terminatorlib/preferences.glade.h:12 msgid "Black on white" -msgstr "Черный на белом" +msgstr "Чёрный на белом" #: ../terminatorlib/preferences.glade.h:13 msgid "Gray on black" -msgstr "серый на чёрном" +msgstr "Серый на чёрном" #: ../terminatorlib/preferences.glade.h:14 msgid "Green on black" @@ -572,7 +581,7 @@ msgstr "Белый на чёрном" #: ../terminatorlib/preferences.glade.h:16 msgid "Orange on black" -msgstr "Оранжевый на черном" +msgstr "Оранжевый на чёрном" #: ../terminatorlib/preferences.glade.h:17 msgid "Ambience" @@ -592,7 +601,7 @@ msgstr "Gruvbox светлая" #: ../terminatorlib/preferences.glade.h:21 msgid "Gruvbox dark" -msgstr "Gruvbox темная" +msgstr "Gruvbox тёмная" #: ../terminatorlib/preferences.glade.h:22 msgid "Custom" @@ -616,7 +625,7 @@ msgstr "Используемый в GNOME по умолчанию" #: ../terminatorlib/preferences.glade.h:27 msgid "Click to focus" -msgstr "Активизация при щелчке мышью" +msgstr "Фокус по щелчку" #: ../terminatorlib/preferences.glade.h:28 msgid "Follow mouse pointer" @@ -684,7 +693,7 @@ msgstr "Полноэкранный режим" #: ../terminatorlib/preferences.glade.h:45 msgid "Terminator Preferences" -msgstr "Терминатор Параметры" +msgstr "Параметры Terminator" #: ../terminatorlib/preferences.glade.h:46 msgid "Behavior" @@ -716,7 +725,7 @@ msgstr "Подсказка геометрии окна" #: ../terminatorlib/preferences.glade.h:53 msgid "DBus server" -msgstr "DBus сервер" +msgstr "Сервер DBus" #: ../terminatorlib/preferences.glade.h:54 msgid "Mouse focus:" @@ -728,11 +737,11 @@ msgstr "Режим трансляции нажатий клавиш" #: ../terminatorlib/preferences.glade.h:56 msgid "PuTTY style paste:" -msgstr "" +msgstr "Стиль вставки PuTTY:" #: ../terminatorlib/preferences.glade.h:57 msgid "Smart copy" -msgstr "\"Умное\" копирование" +msgstr "«Умное» копирование" #: ../terminatorlib/preferences.glade.h:58 msgid "Re-use profiles for new terminals" @@ -744,23 +753,23 @@ msgstr "Использовать пользовательский обработ #: ../terminatorlib/preferences.glade.h:60 msgid "PRIMARY" -msgstr "" +msgstr "PRIMARY" #: ../terminatorlib/preferences.glade.h:61 msgid "Clipboard" -msgstr "" +msgstr "Буфер обмена" #: ../terminatorlib/preferences.glade.h:62 msgid "Clear selection on copy" -msgstr "" +msgstr "Отменить выделение при копировании" #: ../terminatorlib/preferences.glade.h:63 msgid "Open links with a single click (instead of Ctrl-left click)" -msgstr "" +msgstr "Открывать ссылки щелчком мыши (вместо Ctrl+ЛКМ)" #: ../terminatorlib/preferences.glade.h:64 msgid "Disable mouse paste" -msgstr "" +msgstr "Отключить вставку мышью" #: ../terminatorlib/preferences.glade.h:65 msgid "Custom URL handler:" @@ -788,11 +797,11 @@ msgstr "Дополнительные стили (зависит от темы)" #: ../terminatorlib/preferences.glade.h:71 msgid "Cell Height:" -msgstr "" +msgstr "Высота ячейки:" #: ../terminatorlib/preferences.glade.h:72 msgid "Cell Width:" -msgstr "" +msgstr "Ширина ячейки:" #: ../terminatorlib/preferences.glade.h:73 msgid "Tab position:" @@ -808,7 +817,7 @@ msgstr "Кнопки переключения вкладок" #: ../terminatorlib/preferences.glade.h:76 msgid "Title bar at bottom (Require restart)" -msgstr "" +msgstr "Панель заголовка внизу (требуется перезапуск)" #: ../terminatorlib/preferences.glade.h:77 msgid "Global" @@ -844,11 +853,11 @@ msgstr "Копирование на выбор" #: ../terminatorlib/preferences.glade.h:85 msgid "Disable Ctrl+mousewheel zoom" -msgstr "" +msgstr "Отключить масштабирование через Ctrl+колёсико мыши" #: ../terminatorlib/preferences.glade.h:86 msgid "Select-by-_word characters:" -msgstr "Выбор _слов по символам:" +msgstr "Символы, выделяемые по _слову:" #: ../terminatorlib/preferences.glade.h:87 msgid "Cursor" @@ -864,11 +873,11 @@ msgstr "Мерцание" #: ../terminatorlib/preferences.glade.h:90 msgid "Use default colors" -msgstr "" +msgstr "Цвет по умолчанию" #: ../terminatorlib/preferences.glade.h:91 msgid "Foreground:" -msgstr "" +msgstr "Передний план:" #: ../terminatorlib/preferences.glade.h:92 msgid "Background:" @@ -928,11 +937,11 @@ msgstr "Встроенные с_хемы:" #: ../terminatorlib/preferences.glade.h:107 msgid "_Foreground:" -msgstr "" +msgstr "_Передний план" #: ../terminatorlib/preferences.glade.h:108 msgid "_Background:" -msgstr "" +msgstr "_Задний план:" #: ../terminatorlib/preferences.glade.h:109 msgid "Palette" @@ -948,7 +957,7 @@ msgstr "Цветовая _палитра:" #: ../terminatorlib/preferences.glade.h:112 msgid "Show b_old text in bright colors" -msgstr "" +msgstr "Показывать п_олужирный текст в светлых цветах" #: ../terminatorlib/preferences.glade.h:113 msgid "Colors" @@ -964,19 +973,19 @@ msgstr "_Прозрачный фон" #: ../terminatorlib/preferences.glade.h:116 msgid "Background Image" -msgstr "" +msgstr "Фоновое изображение" #: ../terminatorlib/preferences.glade.h:117 msgid "Background Image File:" -msgstr "" +msgstr "Файл фонового изображения" #: ../terminatorlib/preferences.glade.h:118 msgid "Choose file" -msgstr "" +msgstr "Выберите файл" #: ../terminatorlib/preferences.glade.h:119 msgid "S_hade background:" -msgstr "" +msgstr "З_атенение фона:" #: ../terminatorlib/preferences.glade.h:120 msgid "None" @@ -1022,12 +1031,12 @@ msgstr "Прокрутка" msgid "" "Note: These options may cause some applications to behave " "incorrectly. They are only here to allow you to work around certain " -"applications and operating systems that expect different terminal behavior." +"applications and operating systems that expect different terminal " +"behavior." msgstr "" "Замечание: Эти параметры могут вызвать некорректную работу " "некоторых приложений. Они представлены только для того, чтобы позволить " -"работать с некоторыми приложениями и операционными ситемами, ожидающими " +"работать с некоторыми приложениями и операционными системами, ожидающими " "другого поведения терминала. " #: ../terminatorlib/preferences.glade.h:131 @@ -1072,7 +1081,7 @@ msgstr "Укажите шрифт заголовка" #: ../terminatorlib/preferences.glade.h:141 msgid "Titlebar" -msgstr "" +msgstr "Заголовок окна" #: ../terminatorlib/preferences.glade.h:142 #: ../terminatorlib/terminal_popup_menu.py:204 @@ -1113,11 +1122,11 @@ msgstr "Комбинации клавиш" #: ../terminatorlib/preferences.glade.h:153 msgid "Plugin" -msgstr "Надстройка" +msgstr "Модуль" #: ../terminatorlib/preferences.glade.h:154 msgid "This plugin has no configuration options" -msgstr "Этот плагин не имеет параметров конфигурации" +msgstr "Этот модуль не имеет параметров конфигурации" #: ../terminatorlib/preferences.glade.h:155 msgid "Plugins" @@ -1125,32 +1134,17 @@ msgstr "Модули" #: ../terminatorlib/preferences.glade.h:158 msgid "Version: 2.1.1" -msgstr "" +msgstr "Версия: 2.1.1" #: ../terminatorlib/preferences.glade.h:159 msgid "" -"The goal of this project is to produce a useful tool for arranging " -"terminals. It is inspired by programs such as gnome-multi-term, quadkonsole, " -"etc. in that the main focus is arranging terminals in grids (tabs is the " -"most common default method, which Terminator also supports).\n" +"The goal of this project is to produce a useful tool for arranging terminals. It is inspired by programs such as gnome-multi-term, quadkonsole, etc. in that the main focus is arranging terminals in grids (tabs is the most common default method, which Terminator also supports).\n" "\n" -"Much of the behavior of Terminator is based on GNOME Terminal, and we are " -"adding more features from that as time goes by, but we also want to extend " -"out in different directions with useful features for sysadmins and other " -"users. If you have any suggestions, please file wishlist bugs! (see left for " -"the Development link)" +"Much of the behavior of Terminator is based on GNOME Terminal, and we are adding more features from that as time goes by, but we also want to extend out in different directions with useful features for sysadmins and other users. If you have any suggestions, please file wishlist bugs! (see left for the Development link)" msgstr "" -"Задачей данного проекта является создание удобного инструмента для " -"совмещения терминалов. Вдохновленный такими программами как gnome-multi-" -"term, quadkonsole и подобных, он прежде всего нацелен собирать терминалы в " -"сетки (которые в свою очередь могут разноситься по вкладкам, которые, кстати " -"Terminator так же поддерживает).\n" +"Задачей данного проекта является создание удобного инструмента для совмещения терминалов. Вдохновлённый такими программами как gnome-multi-term, quadkonsole и подобных, он прежде всего нацелен собирать терминалы в сетки (которые в свою очередь могут разноситься по вкладкам, которые, кстати Terminator так же поддерживает).\n" "\n" -"Большая часть функционала заимствована из GNOME Terminal и мы со временем " -"добавляем больше разных плюшек оттуда. Но хотелось бы как-то еще расширить " -"его возможности для сисадминов и прочих пользователей. Если у вас есть какие-" -"либо предложения, пожалуйста озвучьте их на багтрекере (wishlist bugs)! (см. " -"сайт разработчиков)" +"Большая часть функционала заимствована из GNOME Terminal и мы со временем добавляем больше разных плюшек оттуда. Но хотелось бы как-то еще расширить его возможности для сисадминов и прочих пользователей. Если у вас есть какие-либо предложения, пожалуйста озвучьте их на багтрекере (wishlist bugs)! (см. сайт разработчиков)" #: ../terminatorlib/preferences.glade.h:162 msgid "The Manual" @@ -1159,9 +1153,10 @@ msgstr "Руководство" #: ../terminatorlib/preferences.glade.h:163 msgid "" "Development\n" -"Bugs / " -"Enhancements" +"Bugs / Enhancements" msgstr "" +"Разработка\n" +"Ошибки / Улучшения" #: ../terminatorlib/preferences.glade.h:165 msgid "About" @@ -1181,15 +1176,15 @@ msgstr "Восстановить размер шрифта" #: ../terminatorlib/prefseditor.py:107 msgid "Increase font size on all terminals" -msgstr "" +msgstr "Увеличить размер шрифта во всех терминалах" #: ../terminatorlib/prefseditor.py:108 msgid "Decrease font size on all terminals" -msgstr "" +msgstr "Уменьшить размер шрифта во всех терминалах" #: ../terminatorlib/prefseditor.py:109 msgid "Restore original font size on all terminals" -msgstr "" +msgstr "Восстановить исходный размер шрифта во всех терминалах" #: ../terminatorlib/prefseditor.py:110 msgid "Create a new tab" @@ -1249,7 +1244,7 @@ msgstr "Вставить из буфера обмена" #: ../terminatorlib/prefseditor.py:126 msgid "Paste primary selection" -msgstr "" +msgstr "Вставить первоначальный выделенный фрагмент" #: ../terminatorlib/prefseditor.py:127 msgid "Show/Hide the scrollbar" @@ -1385,7 +1380,7 @@ msgstr "Показать/Скрыть окно" #: ../terminatorlib/prefseditor.py:160 msgid "Create new group" -msgstr "" +msgstr "Создать новую группу" #: ../terminatorlib/prefseditor.py:161 msgid "Group all terminals" @@ -1393,7 +1388,7 @@ msgstr "Группировать все терминалы" #: ../terminatorlib/prefseditor.py:162 msgid "Group/Ungroup all terminals" -msgstr "Группировать/разрознить все терминалы" +msgstr "Группировать/разделить все терминалы" #: ../terminatorlib/prefseditor.py:163 msgid "Ungroup all terminals" @@ -1401,15 +1396,15 @@ msgstr "Разгруппировать все терминалы" #: ../terminatorlib/prefseditor.py:164 msgid "Group terminals in window" -msgstr "" +msgstr "Группировать терминалы в окне" #: ../terminatorlib/prefseditor.py:165 msgid "Group/Ungroup terminals in window" -msgstr "" +msgstr "Группировать/разделить терминалы в окне" #: ../terminatorlib/prefseditor.py:166 msgid "Ungroup terminals in window" -msgstr "" +msgstr "Разгруппировать терминалы в окне" #: ../terminatorlib/prefseditor.py:167 msgid "Group terminals in tab" @@ -1417,7 +1412,7 @@ msgstr "Группировать терминалы во вкладке" #: ../terminatorlib/prefseditor.py:168 msgid "Group/Ungroup terminals in tab" -msgstr "Группировать/разрознить терминалы во вкладке" +msgstr "Группировать/разделить терминалы во вкладке" #: ../terminatorlib/prefseditor.py:169 msgid "Ungroup terminals in tab" @@ -1429,7 +1424,7 @@ msgstr "Создать новое окно" #: ../terminatorlib/prefseditor.py:171 msgid "Spawn a new Terminator process" -msgstr "Создать новый процесс Terminator'а" +msgstr "Создать новый процесс Terminator" #: ../terminatorlib/prefseditor.py:172 msgid "Don't broadcast key presses" @@ -1449,7 +1444,7 @@ msgstr "Вставить номер терминала" #: ../terminatorlib/prefseditor.py:176 msgid "Insert padded terminal number" -msgstr "Вставить номер терминала" +msgstr "Вставить дополненный номер терминала" #: ../terminatorlib/prefseditor.py:177 msgid "Edit window title" @@ -1477,17 +1472,17 @@ msgstr "Переключиться на предыдущий профиль" #: ../terminatorlib/prefseditor.py:183 msgid "Open the Preferences window" -msgstr "" +msgstr "Открыть окно параметров" #: ../terminatorlib/prefseditor.py:184 msgid "Open the manual" msgstr "Открыть руководство" -#: ../terminatorlib/prefseditor.py:1370 +#: ../terminatorlib/prefseditor.py:1366 msgid "New Profile" msgstr "Создать профиль" -#: ../terminatorlib/prefseditor.py:1413 ../terminatorlib/prefseditor.py:1418 +#: ../terminatorlib/prefseditor.py:1409 ../terminatorlib/prefseditor.py:1414 msgid "New Layout" msgstr "Новое расположение" @@ -1534,7 +1529,7 @@ msgstr "Вст_авить" #: ../terminatorlib/terminal_popup_menu.py:112 msgid "Set W_indow Title" -msgstr "" +msgstr "Указать название о_кна" #: ../terminatorlib/terminal_popup_menu.py:117 msgid "Split H_orizontally" @@ -1574,7 +1569,7 @@ msgstr "Группирование" #: ../terminatorlib/terminal_popup_menu.py:186 msgid "Relaunch Command" -msgstr "" +msgstr "Перезапустить команду" #: ../terminatorlib/terminal_popup_menu.py:191 msgid "Show _scrollbar" @@ -1582,7 +1577,7 @@ msgstr "Показать полосу прокрутки" #: ../terminatorlib/terminal_popup_menu.py:248 msgid "_Layouts..." -msgstr "" +msgstr "_Шаблоны..." #: ../terminatorlib/terminal.py:481 msgid "N_ew group..." @@ -1599,11 +1594,11 @@ msgstr "Удалить группу %s" #: ../terminatorlib/terminal.py:512 msgid "G_roup all in window" -msgstr "" +msgstr "С_группировать всё в окне" #: ../terminatorlib/terminal.py:517 msgid "Ungro_up all in window" -msgstr "" +msgstr "Раз_группировать всё в окне" #: ../terminatorlib/terminal.py:522 msgid "G_roup all in tab" @@ -1611,7 +1606,7 @@ msgstr "С_группировать всё во вкладке" #: ../terminatorlib/terminal.py:527 msgid "Ungro_up all in tab" -msgstr "Раз_рознить терминалы во вкладке" +msgstr "Раз_группировать терминалы во вкладке" #: ../terminatorlib/terminal.py:532 msgid "Remove all groups" @@ -1650,25 +1645,25 @@ msgstr "_Добавить номер терминала" msgid "Insert _padded terminal number" msgstr "Вставить _номер терминала" -#: ../terminatorlib/terminal.py:1490 +#: ../terminatorlib/terminal.py:1492 msgid "Unable to find a shell" msgstr "Не удается найти оболочку (shell)" -#: ../terminatorlib/terminal.py:1521 +#: ../terminatorlib/terminal.py:1546 msgid "Unable to start shell:" msgstr "Не удается запустить оболочку:" -#: ../terminatorlib/terminal.py:1975 +#: ../terminatorlib/terminal.py:2000 msgid "Rename Window" msgstr "Переименование окна" -#: ../terminatorlib/terminal.py:1983 +#: ../terminatorlib/terminal.py:2008 msgid "Enter a new title for the Terminator window..." msgstr "Введите новое название для окна Terminator..." #: ../terminatorlib/titlebar.py:112 msgid "[INACTIVE: Right-Click for Relaunch option] " -msgstr "" +msgstr "[НЕАКТИВНО: Щёлкните правой кнопкой мыши для функции перезапуска] " #: ../terminatorlib/titlebar.py:258 msgid "Alpha" @@ -1773,117 +1768,9 @@ msgstr "окно" #: ../terminatorlib/window.py:773 #, python-format msgid "Window group %s" -msgstr "" +msgstr "Группа окон %s" #: ../terminatorlib/window.py:799 #, python-format msgid "Tab %d" msgstr "Вкладка %d" - -#~ msgid "Current Locale" -#~ msgstr "Текущая языковая настройка" - -#~ msgid "Western" -#~ msgstr "Западная" - -#~ msgid "Central European" -#~ msgstr "Центрально-европейская" - -#~ msgid "South European" -#~ msgstr "Южно-европейская" - -#~ msgid "Baltic" -#~ msgstr "Балтийская" - -#~ msgid "Cyrillic" -#~ msgstr "Кириллица" - -#~ msgid "Arabic" -#~ msgstr "Арабская" - -#~ msgid "Greek" -#~ msgstr "Греческая" - -#~ msgid "Hebrew Visual" -#~ msgstr "Иврит (Визуальный)" - -#~ msgid "Hebrew" -#~ msgstr "Иврит" - -#~ msgid "Turkish" -#~ msgstr "Турецкая" - -#~ msgid "Nordic" -#~ msgstr "Скандинавская" - -#~ msgid "Celtic" -#~ msgstr "Кельтская" - -#~ msgid "Romanian" -#~ msgstr "Румынская" - -#~ msgid "Unicode" -#~ msgstr "Юникод" - -#~ msgid "Armenian" -#~ msgstr "Армянская" - -#~ msgid "Chinese Traditional" -#~ msgstr "Традиционная китайская" - -#~ msgid "Cyrillic/Russian" -#~ msgstr "Кириллица/Русская" - -#~ msgid "Japanese" -#~ msgstr "Японская" - -#~ msgid "Korean" -#~ msgstr "Корейская" - -#~ msgid "Chinese Simplified" -#~ msgstr "Упрощенная китайская" - -#~ msgid "Georgian" -#~ msgstr "Грузинская" - -#~ msgid "Cyrillic/Ukrainian" -#~ msgstr "Кириллица/Украина" - -#~ msgid "Croatian" -#~ msgstr "Хорватская" - -#~ msgid "Hindi" -#~ msgstr "Хинди" - -#~ msgid "Persian" -#~ msgstr "Персидская" - -#~ msgid "Gujarati" -#~ msgstr "Гуарати (Индия)" - -#~ msgid "Gurmukhi" -#~ msgstr "Гармукхи" - -#~ msgid "Icelandic" -#~ msgstr "Исладнский" - -#~ msgid "Vietnamese" -#~ msgstr "Вьетнамский" - -#~ msgid "Thai" -#~ msgstr "Тайский" - -#~ msgid "Encoding:" -#~ msgstr "Кодировка:" - -#~ msgid "Encodings" -#~ msgstr "Кодировки" - -#~ msgid "Default" -#~ msgstr "По умолчанию" - -#~ msgid "User defined" -#~ msgstr "Пользовательский" - -#~ msgid "Other Encodings" -#~ msgstr "Другие кодировки" From a28c5db7cfcea6d039c45a96b35ccbe7caa27c3d Mon Sep 17 00:00:00 2001 From: atomsforpeace <1306402+AtomsForPeace@users.noreply.github.com> Date: Fri, 26 Jan 2024 15:17:41 +0100 Subject: [PATCH 15/26] add missing @with_proxy, fixes #850 --- terminatorlib/ipc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/terminatorlib/ipc.py b/terminatorlib/ipc.py index 310a2325..a296c6ef 100644 --- a/terminatorlib/ipc.py +++ b/terminatorlib/ipc.py @@ -359,6 +359,7 @@ def new_tab_cmdline(session, options): def toggle_visibility_cmdline(session,options): session.toggle_visibility_cmdline(options) +@with_proxy def reload_configuration(session): """Call the dbus method to reload configuration for all windows""" session.reload_configuration() From 6651b30fdeb09030fcb77b248ccf503ab4bdca9a Mon Sep 17 00:00:00 2001 From: nautics889 Date: Fri, 26 Jan 2024 20:20:19 +0200 Subject: [PATCH 16/26] fix: Remove unused imports in several modules #752 Removed unused imports in: * terminator * terminatorlib/plugins/dir_open.py * terminatorlib/terminal.py * terminatorlib/terminal_popup_menu.py * terminatorlib/terminator.py * tests/test_signalman.py --- terminator | 3 +-- terminatorlib/plugins/dir_open.py | 2 -- terminatorlib/terminal.py | 2 +- terminatorlib/terminal_popup_menu.py | 1 - terminatorlib/terminator.py | 1 - tests/test_signalman.py | 1 - 6 files changed, 2 insertions(+), 8 deletions(-) diff --git a/terminator b/terminator index 21eaec7d..5be73d53 100755 --- a/terminator +++ b/terminator @@ -21,8 +21,7 @@ import sys import os -import psutil -import pwd + try: ORIGCWD = os.getcwd() except OSError: diff --git a/terminatorlib/plugins/dir_open.py b/terminatorlib/plugins/dir_open.py index 051c04d0..9e90086a 100644 --- a/terminatorlib/plugins/dir_open.py +++ b/terminatorlib/plugins/dir_open.py @@ -1,7 +1,5 @@ from gi.repository import Gtk -from terminatorlib.config import Config -from terminatorlib.terminal import Terminal from terminatorlib.translation import _ import terminatorlib.plugin as plugin diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index 7ed248b0..7d58f9d8 100644 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -15,7 +15,7 @@ try: except ImportError: from urllib import unquote as urlunquote -from .util import dbg, err, spawn_new_terminator, make_uuid, manual_lookup, display_manager +from .util import dbg, err, spawn_new_terminator, make_uuid, manual_lookup from . import util from .config import Config from .cwd import get_pid_cwd diff --git a/terminatorlib/terminal_popup_menu.py b/terminatorlib/terminal_popup_menu.py index dc7f95f7..d23406b7 100644 --- a/terminatorlib/terminal_popup_menu.py +++ b/terminatorlib/terminal_popup_menu.py @@ -12,7 +12,6 @@ from .util import err, dbg, spawn_new_terminator from .config import Config from .prefseditor import PrefsEditor from . import plugin -from .layoutlauncher import LayoutLauncher class TerminalPopupMenu(object): """Class implementing the Terminal context menu""" diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index aede3336..4da976f7 100644 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -17,7 +17,6 @@ from .config import Config from .keybindings import Keybindings from .util import dbg, err, enumerate_descendants from .factory import Factory -from .version import APP_NAME, APP_VERSION from .translation import _ try: diff --git a/tests/test_signalman.py b/tests/test_signalman.py index 0044e11d..c10a6ddd 100755 --- a/tests/test_signalman.py +++ b/tests/test_signalman.py @@ -30,7 +30,6 @@ False """ -import os import sys, os.path sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) From d36f148a26fed4492c70eaa385d6aff761af9d46 Mon Sep 17 00:00:00 2001 From: mark doerr Date: Sat, 3 Feb 2024 12:26:15 +0100 Subject: [PATCH 17/26] feat: plugin - save current user layout --- .../plugins/save_user_session_layout.py | 69 +++++++++++++++++++ terminatorlib/version.py | 2 +- 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 terminatorlib/plugins/save_user_session_layout.py diff --git a/terminatorlib/plugins/save_user_session_layout.py b/terminatorlib/plugins/save_user_session_layout.py new file mode 100644 index 00000000..a640a4cd --- /dev/null +++ b/terminatorlib/plugins/save_user_session_layout.py @@ -0,0 +1,69 @@ +import os +import sys + +# Fix imports when testing this file directly +if __name__ == '__main__': + sys.path.append( os.path.join(os.path.dirname(__file__), "../..")) + +from gi.repository import Gtk,Vte + +from terminatorlib.config import Config +import terminatorlib.plugin as plugin +from terminatorlib.translation import _ +from terminatorlib.util import get_config_dir, err, dbg, gerr +from terminatorlib.terminator import Terminator +from terminatorlib import util + + +# AVAILABLE must contain a list of all the classes that you want exposed +AVAILABLE = ['SaveUserSessionLayout'] + +class SaveUserSessionLayout(plugin.MenuItem): + capabilities = ['terminal_menu', 'session'] + + config = None + conf_file = os.path.join(get_config_dir(),"save_last_session_cwd") + conf_sessions = [] + emit_close_count = 0 + + vte_version = Vte.get_minor_version() + + def __init__(self): + dbg("SaveUserSessionLayout Init") + plugin.MenuItem.__init__(self) + + def callback(self, menuitems, menu, terminal): + """ Add save menu item to the menu""" + vte_terminal = terminal.get_vte() + item = Gtk.MenuItem.new_with_mnemonic(_('Save _UserSessionLayout')) + item.connect("activate", self.save_all_session_layouts, terminal) + menuitems.append(item) + + def save_all_session_layouts(self, menuitem, terminal): + for term in Terminator().terminals: + self.save_session_layout("", "") + + #not used, but capability can be used to load automatically + def load_session_layout(self, debugtab=False, widget=None, cwd=None, metadata=None, profile=None): + dbg("SaveUserSessionLayout load layout") + terminator = Terminator() + util.spawn_new_terminator(terminator.origcwd, ['-u', '-l', 'SaveUserSessionLayout']) + + def save_session_layout(self, debugtab=False, widget=None, cwd=None, metadata=None, profile=None): + + config = Config() + terminator = Terminator() + current_layout = terminator.describe_layout(save_cwd = True) + dbg("SaveUserSessionLayout: save layout(%s)" % current_layout) + res = config.replace_layout("SaveUserSessionLayout", current_layout) + if (not res): + r = config.add_layout("SaveUserSessionLayout", current_layout) + config.save() + return True + + + def close(self, term, event, arg1 = None): + if (self.emit_close_count == 0): + self.emit_close_count = self.emit_close_count + 1 + self.save_session_layout("", "") + diff --git a/terminatorlib/version.py b/terminatorlib/version.py index e0aa5fcb..d9e093e2 100644 --- a/terminatorlib/version.py +++ b/terminatorlib/version.py @@ -20,4 +20,4 @@ TerminatorVersion supplies our version number. """ APP_NAME = 'terminator' -APP_VERSION = '2.1.3' +APP_VERSION = '2.1.4' From 4519b3e6319c9b7338596a601dd4c812b6c58092 Mon Sep 17 00:00:00 2001 From: Vishweshwar Saran Singh Deo Date: Mon, 5 Feb 2024 22:35:26 +0530 Subject: [PATCH 18/26] [bug 760] working directory feature is broken - as per the bug the layout does not get updated when "Save" button in layout is pressed - it does get updated if the window is closed by pressing top x close icon. - on pressing Save, it seems that the prefseditor takes this from current_layout = terminator.describe_layout() and saves it in config - whereas the current changes are done per key stroke and config is updated - this patch copies the parameters like directory and command when Save is press and on_layoutrefreshbutton_clicked() is called - Hence working dir and command are copied when Save is pressed using uuid to match terminals. - If there is a command registered then the terminal runs the command and exits. so one is not able to see the results. --- terminatorlib/config.py | 28 ++++++++++++++++++++++++++++ terminatorlib/prefseditor.py | 10 +++++++++- terminatorlib/terminal.py | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/terminatorlib/config.py b/terminatorlib/config.py index 97583570..404a506e 100644 --- a/terminatorlib/config.py +++ b/terminatorlib/config.py @@ -499,6 +499,34 @@ class Config(object): """Set a layout""" return(self.base.set_layout(layout, tree)) + def copy_layout_item(self, src_layout, dst_layout, item): + items = {} + for child in src_layout: + section = src_layout[child] + sec_type = section.get('type', None) + if sec_type != 'Terminal': + continue + + cp_item = section.get(item, None) + uuid = str(section.get('uuid', None)) + if cp_item: + items[uuid] = cp_item + + dbg("items to be copied:%s" % items) + for child in dst_layout: + section = dst_layout[child] + sec_type = section.get('type', None) + if sec_type != 'Terminal': + continue + + uuid = str(section.get('uuid', None)) + update_item = items.get(uuid, None) + if uuid and update_item: + dbg("update layout item:(%s) with value:(%s)" + % (item, update_item)) + section[item] = update_item + + class ConfigBase(Borg): """Class to provide access to our user configuration""" loaded = None diff --git a/terminatorlib/prefseditor.py b/terminatorlib/prefseditor.py index 72c14110..e8b7a613 100755 --- a/terminatorlib/prefseditor.py +++ b/terminatorlib/prefseditor.py @@ -1594,6 +1594,14 @@ class PrefsEditor: (model, rowiter) = selected.get_selected() name = model.get_value(rowiter, 0) + config_layout = self.config.base.get_layout(name) + dbg("layout from terminator:(%s)" % current_layout) + dbg("layout from config:(%s)" % config_layout) + + self.config.copy_layout_item(config_layout, current_layout, 'directory') + self.config.copy_layout_item(config_layout, current_layout, 'command') + dbg("updated layout from terminator:(%s)" % current_layout) + if self.config.replace_layout(name, current_layout): treeview.set_cursor(model.get_path(rowiter), column=treeview.get_column(0), start_editing=False) self.config.save() @@ -2163,7 +2171,7 @@ class LayoutEditor: def on_layout_profile_workingdir_activate(self, widget): """A new working directory has been entered for this item""" - workdir = widget.get_text() + workdir = os.path.expanduser(widget.get_text()) layout = self.config.layout_get_config(self.layout_name) layout[self.layout_item]['directory'] = workdir self.config.save() diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index 7d58f9d8..3c82deda 100644 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -1507,7 +1507,7 @@ class Terminal(Gtk.VBox): def set_cwd(self, cwd=None): """Set our cwd""" if cwd is not None: - self.cwd = cwd + self.cwd = os.path.expanduser(cwd) def held_open(self, widget=None, respawn=False, debugserver=False): self.is_held_open = True From 305f6b28818616fbca01e558a7ceb62550835d10 Mon Sep 17 00:00:00 2001 From: Matt Rose Date: Thu, 8 Feb 2024 16:02:41 -0500 Subject: [PATCH 19/26] GUI designed --- terminatorlib/preferences.glade | 64 ++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/terminatorlib/preferences.glade b/terminatorlib/preferences.glade index 83a90847..53e6e32f 100644 --- a/terminatorlib/preferences.glade +++ b/terminatorlib/preferences.glade @@ -1,7 +1,24 @@ - + + + + + + + + + Never + + + Multiple Terminals + + + Always + + + @@ -473,7 +490,7 @@ 36 True - + True False @@ -524,7 +541,7 @@ 0 - 1 + 2 2 @@ -541,7 +558,7 @@ 0 - 2 + 3 2 @@ -558,7 +575,7 @@ 0 - 3 + 4 2 @@ -575,7 +592,7 @@ 0 - 4 + 5 2 @@ -592,7 +609,7 @@ 0 - 5 + 6 2 @@ -609,7 +626,7 @@ 0 - 6 + 7 2 @@ -626,10 +643,39 @@ 0 - 7 + 8 2 + + + True + False + AskBeforeCloseListStore + 1 + + + + 0 + + + + + 1 + 1 + + + + + True + False + Ask Before Closing: + + + 0 + 1 + + True From aa03c235b6ae9b6a74701371c679da5c76900b42 Mon Sep 17 00:00:00 2001 From: Matt Rose Date: Thu, 8 Feb 2024 16:24:22 -0500 Subject: [PATCH 20/26] wired up preferences GUI --- terminatorlib/preferences.glade | 1 + terminatorlib/prefseditor.py | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/terminatorlib/preferences.glade b/terminatorlib/preferences.glade index 53e6e32f..fbfb4c76 100644 --- a/terminatorlib/preferences.glade +++ b/terminatorlib/preferences.glade @@ -653,6 +653,7 @@ False AskBeforeCloseListStore 1 + diff --git a/terminatorlib/prefseditor.py b/terminatorlib/prefseditor.py index 72c14110..8aac701d 100755 --- a/terminatorlib/prefseditor.py +++ b/terminatorlib/prefseditor.py @@ -308,6 +308,18 @@ class PrefsEditor: active = 0 widget = guiget('winstatecombo') widget.set_active(active) + # Ask Before Closing + option = self.config['ask_before_closing'] + if option == 'never': + active = 0 + elif option == 'multiple_terminals': + active = 1 + elif option == 'always': + active = 2 + else: + active = 1 + widget = guiget('askbeforeclose') + widget.set_active(active) # Window borders widget = guiget('winbordercheck') widget.set_active(not self.config['borderless']) @@ -1497,7 +1509,20 @@ class PrefsEditor: value = 'normal' self.config['window_state'] = value self.config.save() - + def on_askbeforeclose_changed(self, widget): + """Ask Before Close changed""" + selected = widget.get_active() + if selected == 0: + value = 'Never' + elif selected == 1: + value = 'Multiple Terminals' + elif selected == 2: + value = 'Always' + else: + value = 'Multiple Terminals' + configval = value.lower().replace(" ","_") + self.config['ask_before_closing'] = configval + self.config.save() # helper function, not a signal def addprofile(self, name, toclone): """Add a profile""" From 46f956613ff633f8da7633b48050a438f8c06531 Mon Sep 17 00:00:00 2001 From: Rick Calixte <10281587+rcalixte@users.noreply.github.com> Date: Tue, 6 Feb 2024 14:11:25 -0500 Subject: [PATCH 21/26] window.py: Fix window group toggle keybind The current conditional always evaluates to False, resulting in the ungroup_win() function never being called --- terminatorlib/window.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 69e999f9..d0a15b4e 100644 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -768,7 +768,7 @@ class Window(Container, Gtk.Window): # change child.set_current_page(child.get_current_page()) - def set_groups(self, new_group, term_list): + def set_groups(self, new_group, term_list): """Set terminals in term_list to new_group""" for terminal in term_list: terminal.set_group(None, new_group) @@ -802,7 +802,7 @@ class Window(Container, Gtk.Window): def group_win_toggle(self, widget): """Toggle grouping to all windows in the current window""" - if widget.group == 'Window': + if widget.group: self.ungroup_win(widget) else: self.group_win(widget) From 36b3602292fc1321dbd03aaff6215ec4a232d7f0 Mon Sep 17 00:00:00 2001 From: Matt Rose Date: Thu, 15 Feb 2024 21:44:14 -0500 Subject: [PATCH 22/26] fix syntax warnings Got the following Warnings when running under python 3.12.1 home/mattrose/Code/terminator/terminatorlib/terminal.py:324: SyntaxWarning: invalid escape sequence '\[' hostchars = "-A-Za-z0-9:\[\]" /home/mattrose/Code/terminator/terminatorlib/terminal.py:348: SyntaxWarning: invalid escape sequence '\.' "(www|ftp)[" + hostchars + "]*\.[" + hostchars + /home/mattrose/Code/terminator/terminatorlib/terminal.py:354: SyntaxWarning: invalid escape sequence '\.' "[a-zA-Z0-9-]*\.[a-zA-Z0-9][a-zA-Z0-9-]+" + /home/mattrose/Code/terminator/terminatorlib/terminal.py:359: SyntaxWarning: invalid escape sequence '\^' """news:[-A-Z\^_a-z{|}~!"#$%&'()*+,./0-9;:=?`]+@""" + To fix this, I changed the strings to raw strings to pass to the regex --- terminatorlib/terminal.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index 3c82deda..c34b7ab6 100644 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -321,7 +321,7 @@ class Terminal(Gtk.VBox): """Update the regexps used to match URLs""" userchars = "-A-Za-z0-9" passchars = "-A-Za-z0-9,?;.:/!%$^*&~\"#'" - hostchars = "-A-Za-z0-9:\[\]" + hostchars = r"-A-Za-z0-9:\[\]" pathchars = "-A-Za-z0-9_$.+!*(),;:@&=?/~#%'" schemes = "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:|ssh:)" user = "[" + userchars + "]+(:[" + passchars + "]+)?" @@ -345,18 +345,18 @@ class Terminal(Gtk.VBox): self._add_regex('voip', re) re = (lboundry + - "(www|ftp)[" + hostchars + "]*\.[" + hostchars + + "(www|ftp)[" + hostchars + r"]*\.[" + hostchars + ".]+(:[0-9]+)?(" + urlpath + ")?" + rboundry + "/?") self._add_regex('addr_only', re) re = (lboundry + "(mailto:)?[a-zA-Z0-9][a-zA-Z0-9.+-]*@[a-zA-Z0-9]" + - "[a-zA-Z0-9-]*\.[a-zA-Z0-9][a-zA-Z0-9-]+" + + r"[a-zA-Z0-9-]*\.[a-zA-Z0-9][a-zA-Z0-9-]+" + "[.a-zA-Z0-9-]*" + rboundry) self._add_regex('email', re) re = (lboundry + - """news:[-A-Z\^_a-z{|}~!"#$%&'()*+,./0-9;:=?`]+@""" + + r"""news:[-A-Z\^_a-z{|}~!"#$%&'()*+,./0-9;:=?`]+@""" + "[-A-Za-z0-9.]+(:[0-9]+)?" + rboundry) self._add_regex('nntp', re) From 7f581fde86cb8d8b5014fa6a2e929f7d73e3dbb7 Mon Sep 17 00:00:00 2001 From: Matt Rose Date: Thu, 15 Feb 2024 22:16:33 -0500 Subject: [PATCH 23/26] fix traceback in layout_done Got this while testing layouts. Easy fix Traceback (most recent call last): File "/home/mattrose/Code/terminator/./terminator", line 137, in TERMINATOR.layout_done() File "/home/mattrose/Code/terminator/terminatorlib/terminator.py", line 341, in layout_done term.ensure_visible_and_focussed() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'ensure_visible_and_focussed' --- terminatorlib/terminator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index 4da976f7..8b5ac0d9 100644 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -338,7 +338,8 @@ class Terminator(Borg): # For windows without a notebook ensure Terminal is visible and focused if window_last_active_term_mapping[window]: term = self.find_terminal_by_uuid(window_last_active_term_mapping[window].urn) - term.ensure_visible_and_focussed() + if term: + term.ensure_visible_and_focussed() # Build list of new windows using prelayout list new_win_list = [] From 5d0904b619bf852008227cd7e2e34e0c06dbb7db Mon Sep 17 00:00:00 2001 From: mark doerr Date: Fri, 16 Feb 2024 19:36:41 +0100 Subject: [PATCH 24/26] fix: version re-set to 2.1.3 --- terminatorlib/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminatorlib/version.py b/terminatorlib/version.py index d9e093e2..e0aa5fcb 100644 --- a/terminatorlib/version.py +++ b/terminatorlib/version.py @@ -20,4 +20,4 @@ TerminatorVersion supplies our version number. """ APP_NAME = 'terminator' -APP_VERSION = '2.1.4' +APP_VERSION = '2.1.3' From 4e6e2937713766a29918a8f327bf285cbc8954f7 Mon Sep 17 00:00:00 2001 From: Matt Rose Date: Mon, 11 Mar 2024 15:17:51 -0400 Subject: [PATCH 25/26] Properly parse file:/// URIs Previously, file URIs were lumped in with full uris that would be used for HTTP, FTP, etc. This caused file:/// uri parser to ignore any file with a root dir that had a character that was not a valid hostname character to be ignored. --- terminatorlib/terminal.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index c34b7ab6..2aaf58d7 100644 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -300,6 +300,7 @@ class Terminal(Gtk.VBox): registry.load_plugins(force) def _add_regex(self, name, re): + dbg(f"adding regex: {re}") match = -1 if regex.FLAGS_PCRE2: try: @@ -323,19 +324,23 @@ class Terminal(Gtk.VBox): passchars = "-A-Za-z0-9,?;.:/!%$^*&~\"#'" hostchars = r"-A-Za-z0-9:\[\]" pathchars = "-A-Za-z0-9_$.+!*(),;:@&=?/~#%'" - schemes = "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:|ssh:)" + schemes = "(news:|telnet:|nntp:|https?:|ftps?:|webcal:|ssh:)" user = "[" + userchars + "]+(:[" + passchars + "]+)?" urlpath = "/[" + pathchars + "]*[^]'.}>) \t\r\n,\\\"]" lboundry = "\\b" rboundry = "\\b" + re = (lboundry + "file:/" + "//?(:[0-9]+)?(" + urlpath + ")" + + rboundry + "/?") + self._add_regex('file', re) + re = (lboundry + schemes + "//(" + user + "@)?[" + hostchars +".]+(:[0-9]+)?(" + urlpath + ")?" + rboundry + "/?") self._add_regex('full_uri', re) - if self.matches['full_uri'] == -1: + if self.matches['full_uri'] == -1 or self.matches['file'] == -1: err ('Terminal::update_url_matches: Failed adding URL matches') else: re = (lboundry + From 4062cddb1f27aad946a1fe363f3dcf219a34a044 Mon Sep 17 00:00:00 2001 From: Gleb Popov Date: Mon, 25 Mar 2024 22:22:57 +0300 Subject: [PATCH 26/26] Install manpages to share/man on FreeBSD --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c49a6f93..d0906ba9 100755 --- a/setup.py +++ b/setup.py @@ -175,7 +175,7 @@ class InstallData(install_data): return data_files -if platform.system() in ['FreeBSD', 'OpenBSD']: +if platform.system() in ['OpenBSD']: man_dir = 'man' else: man_dir = 'share/man'