# Terminator by Chris Jones # GPL v2 only """custom_commands.py - Terminator Plugin to add custom command menu entries""" import sys import os # 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 from gi.repository import GObject import terminatorlib.plugin as plugin from terminatorlib.config import Config from terminatorlib.translation import _ from terminatorlib.util import get_config_dir, err, dbg, gerr (CC_COL_ENABLED, CC_COL_NAME, CC_COL_COMMAND) = list(range(0,3)) # Every plugin you want Terminator to load *must* be listed in 'AVAILABLE' AVAILABLE = ['CustomCommandsMenu'] class CustomCommandsMenu(plugin.MenuItem): """Add custom commands to the terminal menu""" capabilities = ['terminal_menu'] cmd_list = {} conf_file = os.path.join(get_config_dir(),"custom_commands") def __init__( self): config = Config() sections = config.plugin_get_config(self.__class__.__name__) if not isinstance(sections, dict): return noord_cmds = [] for part in sections: s = sections[part] if not ("name" in s and "command" in s): print("CustomCommandsMenu: Ignoring section %s" % s) continue name = s["name"] 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, 'command' : command } else: noord_cmds.append( {'enabled' : enabled, 'name' : name, 'command' : command } ) for cmd in noord_cmds: self.cmd_list[len(self.cmd_list)] = cmd def callback(self, menuitems, menu, terminal): """Add our menu items to the menu""" submenus = {} item = Gtk.MenuItem.new_with_mnemonic(_('_Custom Commands')) menuitems.append(item) submenu = Gtk.Menu() item.set_submenu(submenu) menuitem = Gtk.MenuItem.new_with_mnemonic(_('_Preferences')) menuitem.connect("activate", self.configure) submenu.append(menuitem) menuitem = Gtk.SeparatorMenuItem() submenu.append(menuitem) theme = Gtk.IconTheme.get_default() for command in [ self.cmd_list[key] for key in sorted(self.cmd_list.keys()) ] : if not command['enabled']: continue exe = command['command'].split(' ')[0] iconinfo = theme.choose_icon([exe], Gtk.IconSize.MENU, Gtk.IconLookupFlags.USE_BUILTIN) leaf_name = command['name'].split('/')[-1] 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 iconinfo: image = Gtk.Image() image.set_from_icon_name(exe, Gtk.IconSize.MENU) menuitem = Gtk.ImageMenuItem(leaf_name) menuitem.set_image(image) else: menuitem = Gtk.MenuItem(leaf_name) terminals = terminal.terminator.get_target_terms(terminal) menuitem.connect("activate", self._execute, {'terminals' : terminals, 'command' : command['command'] }) target_submenu.append(menuitem) def _save_config(self): config = Config() config.plugin_del_config(self.__class__.__name__) i = 0 for command in [ self.cmd_list[key] for key in sorted(self.cmd_list.keys()) ] : enabled = command['enabled'] name = command['name'] command = command['command'] item = {} item['enabled'] = enabled item['name'] = name item['command'] = command item['position'] = i config.plugin_set(self.__class__.__name__, name, item) i = i + 1 config.save() def _execute(self, widget, data): command = data['command'] if command[-1] != '\n': command = command + '\n' for terminal in data['terminals']: terminal.vte.feed_child(command.encode(terminal.vte.get_encoding())) def configure(self, widget, data = None): ui = {} dbox = Gtk.Dialog( _("Custom Commands Configuration"), None, Gtk.DialogFlags.MODAL, ( _("_Cancel"), Gtk.ResponseType.REJECT, _("_OK"), Gtk.ResponseType.ACCEPT ) ) dbox.set_transient_for(widget.get_toplevel()) icon_theme = Gtk.IconTheme.get_default() if icon_theme.lookup_icon('terminator-custom-commands', 48, 0): dbox.set_icon_name('terminator-custom-commands') else: dbg('Unable to load Terminator custom command icon') icon = dbox.render_icon(Gtk.STOCK_DIALOG_INFO, Gtk.IconSize.BUTTON) dbox.set_icon(icon) store = Gtk.ListStore(bool, str, str) 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() selection.set_mode(Gtk.SelectionMode.SINGLE) selection.connect("changed", self.on_selection_changed, ui) ui['treeview'] = treeview renderer = Gtk.CellRendererToggle() renderer.connect('toggled', self.on_toggled, ui) column = Gtk.TreeViewColumn(_("Enabled"), renderer, active=CC_COL_ENABLED) treeview.append_column(column) renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn(_("Name"), renderer, text=CC_COL_NAME) treeview.append_column(column) renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn(_("Command"), renderer, text=CC_COL_COMMAND) treeview.append_column(column) scroll_window = Gtk.ScrolledWindow() scroll_window.set_size_request(500, 250) scroll_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) scroll_window.add_with_viewport(treeview) hbox = Gtk.HBox() hbox.pack_start(scroll_window, True, True, 0) dbox.vbox.pack_start(hbox, True, True, 0) button_box = Gtk.VBox() button = Gtk.Button(_("Top")) button_box.pack_start(button, False, True, 0) button.connect("clicked", self.on_goto_top, ui) button.set_sensitive(False) ui['button_top'] = button button = Gtk.Button(_("Up")) button_box.pack_start(button, False, True, 0) button.connect("clicked", self.on_go_up, ui) button.set_sensitive(False) ui['button_up'] = button button = Gtk.Button(_("Down")) button_box.pack_start(button, False, True, 0) button.connect("clicked", self.on_go_down, ui) button.set_sensitive(False) ui['button_down'] = button button = Gtk.Button(_("Last")) button_box.pack_start(button, False, True, 0) button.connect("clicked", self.on_goto_last, ui) button.set_sensitive(False) ui['button_last'] = button button = Gtk.Button(_("New")) button_box.pack_start(button, False, True, 0) button.connect("clicked", self.on_new, ui) ui['button_new'] = button button = Gtk.Button(_("Edit")) button_box.pack_start(button, False, True, 0) button.set_sensitive(False) button.connect("clicked", self.on_edit, ui) ui['button_edit'] = button button = Gtk.Button(_("Delete")) button_box.pack_start(button, False, True, 0) button.connect("clicked", self.on_delete, ui) button.set_sensitive(False) ui['button_delete'] = button hbox.pack_start(button_box, False, True, 0) self.dbox = dbox dbox.show_all() res = dbox.run() if res == Gtk.ResponseType.ACCEPT: self.update_cmd_list(store) self._save_config() del(self.dbox) dbox.destroy() return def update_cmd_list(self, store): iter = store.get_iter_first() self.cmd_list = {} i=0 while iter: (enabled, name, command) = store.get(iter, CC_COL_ENABLED, CC_COL_NAME, CC_COL_COMMAND) self.cmd_list[i] = {'enabled' : enabled, 'name': name, 'command' : command} iter = store.iter_next(iter) i = i + 1 def on_toggled(self, widget, path, data): treeview = data['treeview'] store = treeview.get_model() iter = store.get_iter(path) (enabled, name, command) = store.get(iter, CC_COL_ENABLED, CC_COL_NAME, CC_COL_COMMAND ) store.set_value(iter, CC_COL_ENABLED, not enabled) def on_selection_changed(self,selection, data=None): treeview = selection.get_tree_view() (model, iter) = selection.get_selected() data['button_top'].set_sensitive(iter is not None) data['button_up'].set_sensitive(iter is not None) data['button_down'].set_sensitive(iter is not None) data['button_last'].set_sensitive(iter is not None) 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 = ""): dialog = Gtk.Dialog( _("New Command"), None, Gtk.DialogFlags.MODAL, ( _("_Cancel"), Gtk.ResponseType.REJECT, _("_OK"), Gtk.ResponseType.ACCEPT ) ) dialog.set_transient_for(self.dbox) table = Gtk.Table(3, 2) label = Gtk.Label(label=_("Enabled:")) 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:")) table.attach(label, 0, 1, 1, 2) name = Gtk.Entry() name.set_text(name_var) table.attach(name, 1, 2, 1, 2) label = Gtk.Label(label=_("Command:")) table.attach(label, 0, 1, 2, 3) command = Gtk.Entry() command.set_text(command_var) table.attach(command, 1, 2, 2, 3) dialog.vbox.pack_start(table, True, True, 0) dialog.show_all() return (dialog,enabled,name,command) def on_new(self, button, data): (dialog,enabled,name,command) = self._create_command_dialog() res = dialog.run() item = {} if res == Gtk.ResponseType.ACCEPT: item['enabled'] = enabled.get_active() item['name'] = name.get_text() item['command'] = command.get_text() if item['name'] == '' or item['command'] == '': err = Gtk.MessageDialog(dialog, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, _("You need to define a name and command") ) err.run() err.destroy() else: # we have a new command store = data['treeview'].get_model() iter = store.get_iter_first() name_exist = False while iter != None: if store.get_value(iter,CC_COL_NAME) == item['name']: name_exist = True break iter = store.iter_next(iter) if not name_exist: store.append((item['enabled'], item['name'], item['command'])) else: gerr(_("Name *%s* already exist") % item['name']) dialog.destroy() def on_goto_top(self, button, data): treeview = data['treeview'] selection = treeview.get_selection() (store, iter) = selection.get_selected() if not iter: return firstiter = store.get_iter_first() store.move_before(iter, firstiter) def on_go_up(self, button, data): treeview = data['treeview'] selection = treeview.get_selection() (store, iter) = selection.get_selected() if not iter: return tmpiter = store.get_iter_first() if(store.get_path(tmpiter) == store.get_path(iter)): return while tmpiter: next = store.iter_next(tmpiter) if(store.get_path(next) == store.get_path(iter)): store.swap(iter, tmpiter) break tmpiter = next def on_go_down(self, button, data): treeview = data['treeview'] selection = treeview.get_selection() (store, iter) = selection.get_selected() if not iter: return next = store.iter_next(iter) if next: store.swap(iter, next) def on_goto_last(self, button, data): treeview = data['treeview'] selection = treeview.get_selection() (store, iter) = selection.get_selected() if not iter: return lastiter = iter tmpiter = store.get_iter_first() while tmpiter: lastiter = tmpiter tmpiter = store.iter_next(tmpiter) store.move_after(iter, lastiter) def on_delete(self, button, data): treeview = data['treeview'] selection = treeview.get_selection() (store, iter) = selection.get_selected() if iter: store.remove(iter) return def on_edit(self, button, data): treeview = data['treeview'] selection = treeview.get_selection() (store, iter) = selection.get_selected() 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) ) res = dialog.run() item = {} if res == Gtk.ResponseType.ACCEPT: item['enabled'] = enabled.get_active() item['name'] = name.get_text() item['command'] = command.get_text() if item['name'] == '' or item['command'] == '': err = Gtk.MessageDialog(dialog, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, _("You need to define a name and command") ) err.run() err.destroy() else: tmpiter = store.get_iter_first() name_exist = False while tmpiter != None: if store.get_path(tmpiter) != store.get_path(iter) and store.get_value(tmpiter,CC_COL_NAME) == item['name']: name_exist = True break tmpiter = store.iter_next(tmpiter) if not name_exist: store.set(iter, CC_COL_ENABLED,item['enabled'], CC_COL_NAME, item['name'], CC_COL_COMMAND, item['command'] ) else: gerr(_("Name *%s* already exist") % item['name']) dialog.destroy() if __name__ == '__main__': c = CustomCommandsMenu() c.configure(None, None) Gtk.main()