def prepare_attributes(self): """Initialise anything that isn't already""" if not self.windows: self.windows = [] if not self.launcher_windows: self.launcher_windows = [] if not self.terminals: self.terminals = [] if not self.groups: self.groups = [] if not self.config: self.config = Config() if self.groupsend == None: self.groupsend = self.groupsend_type[ self.config['broadcast_default']] if not self.keybindings: self.keybindings = Keybindings() self.keybindings.configure(self.config['keybindings']) if not self.style_providers: self.style_providers = [] if not self.doing_layout: self.doing_layout = False if not self.pid_cwd: self.pid_cwd = get_pid_cwd() if self.gnome_client is None: self.attempt_gnome_client() self.connect_signals()
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 __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 (s.has_key("name") and s.has_key("command")): print "CustomCommandsMenu: Ignoring section %s" % s continue name = s["name"] command = s["command"] enabled = s["enabled"] and s["enabled"] or False if s.has_key("position"): 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 __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 ("regexp" in s and "command" in s): dbg("Ignoring section %s" % s) continue regexp = s["regexp"] command = s["command"] enabled = s["enabled"] and s["enabled"] or False if "position" in s: self.cmd_list[int(s["position"])] = { 'enabled': enabled, 'regexp': regexp, 'command': command } else: noord_cmds.append({ 'enabled': enabled, 'regexp': regexp, 'command': command }) for cmd in noord_cmds: self.cmd_list[len(self.cmd_list)] = cmd self._load_configured_handlers()
def _load_configured_handlers(self): """Forge an URLhandler plugin and hide it in the available ones.""" me = sys.modules[__name__] # Current module. config = Config() for key, handler in [(key, self.cmd_list[key]) for key in sorted(self.cmd_list.keys())]: # Forge a hidden/managed plugin # (names starting with an underscore will not be displayed in the preference/plugins window). rcom_name = "_RunCmdOnMatch_{}".format( key) # key is just the index # Make a handler class. RCOM = MetaRCOM(rcom_name, handler["regexp"], handler["command"]) # Instanciate the class. setattr(me, rcom_name, RCOM) if rcom_name not in AVAILABLE: AVAILABLE.append(rcom_name) dbg("add {} to the list of URL handlers: '{}' -> '{}'".format( rcom_name, RCOM.match, RCOM.cmd)) if handler['enabled'] and rcom_name not in config[ "enabled_plugins"]: config["enabled_plugins"].append(rcom_name) config.save()
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(f'CustomCommandsMenu: Ignoring section {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 __init__(self): self.config = Config().plugin_get_config(self.__class__.__name__) self.triggers = {} self.watches = {} self.load_triggers() self.update_watches() self.dialog_in_process = set() keepassDb = '' if 'keepassDb' in self.config: keepassDb = self.config['keepassDb'] self.triggersCommand = TriggersCommand(keepassDb)
def __init__(self): config = Config() sections = config.plugin_get_config(self.__class__.__name__) if not isinstance(sections, dict): return for part in sections: s = sections[part] if not (s.has_key("name") and s.has_key("command")): print "SSHMenu: Ignoring section %s" % s continue name = s["name"] command = s["command"] self.cmd_list.append({'name': name, 'command': command})
def __init__(self): super(TerminalExporter, self).__init__() self.config = Config() self.plugin_config = parse_plugin_config(self.config) self.logging_terminals = {} self.scrollback_lines = self.config['scrollback_lines'] dbg('using config: %s' % self.plugin_config)
def __init__(self): self.config = Config().plugin_get_config(self.__class__.__name__) self.watches = {} self.images = self.load_images() dbg(self.images) global_config = Terminator().config # Create ssh profiles self.ssh_profiles = {} for f in glob.glob(self.images): dbg(f) name = os.path.splitext(os.path.basename(f))[0] self.ssh_profiles[name] = f global_config.add_profile(name) profile = global_config.base.profiles[name] profile["background_darkness"] = 0.88 profile["background_image"] = f profile["background_type"] = "image" dbg(repr(self.ssh_profiles)) for v in global_config.list_profiles(): dbg(repr(v)) self.failback_profile = self.get_failback() self.last_profile = self.failback_profile self.load_patterns() self.update_watches()
def __init__(self): plugin.MenuItem.__init__(self) self.config = Config() colorize_config = self.config.plugin_get_config( self.__class__.__name__) if not colorize_config: colorize_config = self.presets self.color_set = [] counter = 0 if colorize_config: while colorize_config.get('color' + str(counter)): self.color_set.append( colorize_config.get('color' + str(counter))) counter += 1 dbg(self.color_set)
def __init__(self): plugin.MenuItem.__init__(self) self.config = Config() self.pluginConfig = parsePluginConfig(self.config) self.loggingTerminals = {} self.scrollbackLines = self.config['scrollback_lines'] dbg("using config: %s" % self.pluginConfig)
def __init__(self): """Class initialiser""" GObject.GObject.__init__(self) self.config = Config() self.get_style_context().add_class("terminator-terminal-searchbar") # Search text self.entry = Gtk.Entry() self.entry.set_activates_default(True) self.entry.show() self.entry.connect('activate', self.do_search) self.entry.connect('key-press-event', self.search_keypress) # Label label = Gtk.Label(label=_('Search:')) label.show() # Close Button close = Gtk.Button() close.set_relief(Gtk.ReliefStyle.NONE) close.set_focus_on_click(False) icon = Gtk.Image() icon.set_from_stock(Gtk.STOCK_CLOSE, Gtk.IconSize.MENU) close.add(icon) close.set_name('terminator-search-close-button') if hasattr(close, 'set_tooltip_text'): close.set_tooltip_text(_('Close Search bar')) close.connect('clicked', self.end_search) close.show_all() # Next Button self.next = Gtk.Button(label=_('Next')) self.next.show() self.next.set_sensitive(False) self.next.connect('clicked', self.next_search) # Previous Button self.prev = Gtk.Button(label=_('Prev')) self.prev.show() self.prev.set_sensitive(False) self.prev.connect('clicked', self.prev_search) # Wrap checkbox self.wrap = Gtk.CheckButton(label=_('Wrap')) self.wrap.show() self.wrap.set_sensitive(True) self.wrap.connect('toggled', self.wrap_toggled) self.pack_start(label, False, True, 0) self.pack_start(self.entry, True, True, 0) self.pack_start(self.prev, False, False, 0) self.pack_start(self.next, False, False, 0) self.pack_start(self.wrap, False, False, 0) self.pack_end(close, False, False, 0) self.hide() self.set_no_show_all(True)
def callback(self, menuitems, menu, terminal): self.config = Config() self.terminal = terminal self.profiles = self.terminal.config.list_profiles() target = random.randrange(0, len(self.profiles)) currentProfile = self.profiles[target] widget = self.terminal.get_vte() self.terminal.force_set_profile(widget, currentProfile)
def _save_config(self): config = Config() i = 0 length = len(self.cmd_list) while i < length: enabled = self.cmd_list[i]['enabled'] name = self.cmd_list[i]['name'] command = self.cmd_list[i]['command'] item = {} item['enabled'] = enabled item['name'] = name item['command'] = command config.plugin_set(self.__class__.__name__, name, item) config.save() i = i + 1
def __init__( self): config = Config() sections = config.plugin_get_config(self.__class__.__name__) if not isinstance(sections, dict): return for part in sections: s = sections[part] if not (s.has_key("name") and s.has_key("command")): print "CustomCommandsMenu: Ignoring section %s" % s continue name = s["name"] command = s["command"] enabled = s["enabled"] and s["enabled"] or False self.cmd_list.append( {'enabled' : enabled, 'name' : name, 'command' : command } )
def __init__(self): self.config = Config().plugin_get_config(self.__class__.__name__) self.watches = {} self.prompt_minlen = int(self.get_prompt_minlen()) self.line_minlen = int(self.get_line_minlen()) self.failback_profile = self.get_failback() self.last_profile = self.failback_profile self.load_patterns() self.load_profile_mappings() self.update_watches()
def __init__(self): self.config = Config() if self.config.plugin_get_config(self.plugin_name) is None: for key, value in CONFIG_DEFAULTS.iteritems(): self.config.plugin_set(self.plugin_name, key, value) self.config.save() self.match = self.config.plugin_get(self.plugin_name, "match") self.jira_url = self.config.plugin_get(self.plugin_name, "jira_url")
def __init__(self): self.config = Config() if not isinstance( self.config.plugin_get_config( self.__class__.__name__), dict): self.config.plugin_set_config(self.__class__.__name__, {}) self.config.save() self._authorization = dict(Authorization='Bearer ' + TOKEN) self._base_api_url = 'https://api.online.net/api/v1' self._servers_infos_file_name = '/tmp/online_servers_infos'
def __init__( self): self.cmd_list = [] self.cur_input = '' config = Config() sections = config.plugin_get_config(self.__class__.__name__) if not isinstance(sections, dict): return for part in sections: s = sections[part] if not (s.has_key("ip") and s.has_key("user")): print "SSH Configuration: Ignoring section %s" % s continue ip = s["ip"] user = s["user"] port = s["port"] last_time = s["last_time"] passwd = s["passwd"] self.cmd_list.append({ 'ip' : ip, 'user' : user, 'port' : port,'last_time':last_time ,'passwd':passwd })
def __init__(self): plugin.MenuItem.__init__(self) self.config = Config() colorize_config = self.config.plugin_get_config(self.__class__.__name__) if not colorize_config: colorize_config = self.presets self.color_set = [] counter = 0 if colorize_config: while colorize_config.get('color' + str(counter)): self.color_set.append(colorize_config.get('color' + str(counter))) counter += 1 print self.color_set
def __init__(self): self.terminator = Terminator() self.terminator.register_launcher_window(self) self.config = Config() self.config.base.reload() self.builder = Gtk.Builder() try: # Figure out where our library is on-disk so we can open our UI (head, _tail) = os.path.split(Config.__file__) librarypath = os.path.join(head, 'layoutlauncher.glade') gladefile = open(librarypath, 'r') gladedata = gladefile.read() except Exception as ex: print("Failed to find layoutlauncher.glade") print(ex) return self.builder.add_from_string(gladedata) self.window = self.builder.get_object('layoutlauncherwin') icon_theme = Gtk.IconTheme.get_default() if icon_theme.lookup_icon('terminator-layout', 48, 0): self.window.set_icon_name('terminator-layout') else: dbg('Unable to load Terminator layout launcher icon') icon = self.window.render_icon(Gtk.STOCK_DIALOG_INFO, Gtk.IconSize.BUTTON) self.window.set_icon(icon) self.builder.connect_signals(self) self.window.connect('destroy', self.on_destroy_event) self.window.show_all() self.layouttreeview = self.builder.get_object('layoutlist') self.layouttreestore = self.builder.get_object('layoutstore') self.update_layouts()
def __init__(self, title, notebook): """Class initialiser""" GObject.GObject.__init__(self) self.notebook = notebook self.terminator = Terminator() self.config = Config() self.label = EditableLabel(title) self.update_angle() self.pack_start(self.label, True, True, 0) self.update_button() self.show_all()
def _save_config(self): log_debug("_save_config") config = Config() config.plugin_del_config(self.__class__.__name__) i = 0 for ssh_conf in self.cmd_list : ip = ssh_conf['ip'] user = ssh_conf['user'] port = ssh_conf['port'] last_time = ssh_conf['last_time'] passwd = ssh_conf['passwd'] item = { 'ip': ip, 'user': user, 'port': port, 'last_time': last_time, 'passwd': passwd} ssh_key = user+"@"+ip config.plugin_set(self.__class__.__name__, ssh_key, item) i = i + 1 config.save()
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 _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'] regexp = command['regexp'] command = command['command'] item = {} item['enabled'] = enabled item['regexp'] = regexp item['command'] = command item['position'] = i config.plugin_set(self.__class__.__name__, regexp, item) i = i + 1 config.save() self._load_configured_handlers()
def _save_config(self): config = Config() i = 0 length = len(self.cmd_list) while i < length: name = self.cmd_list[i]['name'] command = self.cmd_list[i]['command'] item = {} item['name'] = name item['command'] = command config.plugin_set(self.__class__.__name__, name, item) config.save() i = i + 1
def load_plugins(self, testing=False): """Load all plugins present in the plugins/ directory in our module""" if self.done: dbg('PluginRegistry::load_plugins: Already loaded') return config = Config() for plugindir in self.path: sys.path.insert(0, plugindir) try: files = os.listdir(plugindir) except OSError: sys.path.remove(plugindir) continue for plugin in files: if plugin == '__init__.py': continue pluginpath = os.path.join(plugindir, plugin) if os.path.isfile(pluginpath) and plugin[-3:] == '.py': dbg('PluginRegistry::load_plugins: Importing plugin %s' % plugin) try: module = __import__(plugin[:-3], None, None, ['']) for item in getattr(module, 'AVAILABLE'): if item not in self.available_plugins.keys(): func = getattr(module, item) self.available_plugins[item] = func if not testing and item not in config[ 'enabled_plugins']: dbg('plugin %s not enabled, skipping' % item) continue if item not in self.instances: self.instances[item] = func() except Exception as ex: err('PluginRegistry::load_plugins: Importing plugin %s \ failed: %s' % (plugin, ex)) self.done = True
class JiraUrlPlugin(plugin.URLHandler): capabilties = ["url_handler"] handler_name = "jira_ticket" config = None plugin_name = "JiraUrlPlugin" def __init__(self): self.config = Config() if self.config.plugin_get_config(self.plugin_name) is None: for key, value in CONFIG_DEFAULTS.iteritems(): self.config.plugin_set(self.plugin_name, key, value) self.config.save() self.match = self.config.plugin_get(self.plugin_name, "match") self.jira_url = self.config.plugin_get(self.plugin_name, "jira_url") def callback(self, url): if not (self.match or self.jira_url): return for item in re.findall(self.match, url): return("%s/%s" % (self.jira_url, item))
class ClonedSplittingMenu(plugin.MenuItem): capabilities = ['terminal_menu'] config = Config() maker = Factory() def __init__(self): myconfig = self.config.plugin_get_config(self.__class__.__name__) # Now extract valid data from sections{} def callback(self, menuitems, menu, terminal): mhor = gtk.MenuItem('Clone Horizontally') mvert = gtk.MenuItem('Clone Vertically') mhor.connect('activate', self.clone_session, True, terminal) mvert.connect('activate', self.clone_session, False, terminal) menuitems.append(mhor) menuitems.append(mvert) def clone_session(self, menuitem, is_horizontal, terminal): container = self.get_terminal_container(terminal) if container: self.register_signals(container, terminal) cmd = self.get_terminal_cmd(terminal) cwd = terminal.terminator.pid_cwd(terminal.pid) sibling = ClonableTerminal() sibling.set_cwd(cwd) sibling.spawn_child_with_command(cmd) terminal.emit( 'split-horiz-clone' if is_horizontal else 'split-vert-clone', cwd, sibling) else: terminal.emit('split-horiz' if is_horizontal else 'split-vert', cwd) def get_terminal_container(self, terminal, container=None): terminator = Terminator() if not container: for window in terminator.windows: owner = self.get_terminal_container(terminal, window) if owner: return owner else: for child in container.get_children(): if isinstance(child, Terminal) and child == terminal: return container if isinstance(child, Container): owner = self.get_terminal_container(terminal, child) if owner: return owner def register_signals(self, container, terminal): container.signals.append({ 'name': 'split-horiz-clone', 'flags': gobject.SIGNAL_RUN_LAST, 'return_type': gobject.TYPE_NONE, 'param_types': (gobject.TYPE_STRING, gobject.TYPE_OBJECT) }) container.signals.append({ 'name': 'split-vert-clone', 'flags': gobject.SIGNAL_RUN_LAST, 'return_type': gobject.TYPE_NONE, 'param_types': (gobject.TYPE_STRING, gobject.TYPE_OBJECT) }) container.register_signals(terminal) container.connect_child(terminal, 'split-horiz-clone', self.split_horiz) container.connect_child(terminal, 'split-vert-clone', self.split_vert) def split_horiz(self, terminal, cwd=None, sibling=None): container = self.get_terminal_container(terminal) return (container.split_axis(terminal, True, cwd, sibling)) def split_vert(self, terminal, cwd=None, sibling=None): container = self.get_terminal_container(terminal) return (container.split_axis(terminal, False, cwd, sibling)) def get_terminal_cmd(self, terminal): raw = subprocess.Popen( ['ps', '--no-headers', '-p', str(terminal.pid), '-o', 'command'], stdout=subprocess.PIPE) ps_line = subprocess.check_output(['head', '-1'], stdin=raw.stdout).strip() if ps_line and ps_line.strip().startswith('ssh'): return ps_line.strip() raw = subprocess.Popen([ 'ps', '--no-headers', '--ppid', str(terminal.pid), '-o', 'command' ], stdout=subprocess.PIPE) ps_lines = subprocess.check_output( ['head', '-100'], stdin=raw.stdout).strip().split('\n') for ps_line in ps_lines: if ps_line.strip().startswith('ssh'): return ps_line.strip() def log(self, name, obj): with open('/tmp/log', 'a') as f: f.write('%s:' % name) f.write(str(obj)) f.write(': done\n')
import terminatorlib.plugin as plugin from terminatorlib.translation import _ from terminatorlib.util import err, dbg from terminatorlib.version import APP_NAME try: gi.require_version('Notify', '0.7') from gi.repository import Notify # Every plugin you want Terminator to load *must* be listed in 'AVAILABLE' # This is inside this try so we only make the plugin available if pynotify # is present on this computer. AVAILABLE = ['ActivityWatch', 'InactivityWatch'] except (ImportError, ValueError): err('ActivityWatch plugin unavailable as we cannot import Notify') config = Config() inactive_period = float( config.plugin_get('InactivityWatch', 'inactive_period', 10.0)) watch_interval = int( config.plugin_get('InactivityWatch', 'watch_interval', 5000)) hush_period = float(config.plugin_get('ActivityWatch', 'hush_period', 10.0)) class ActivityWatch(plugin.MenuItem): """Add custom commands to the terminal menu""" capabilities = ['terminal_menu'] watches = None last_notifies = None timers = None def __init__(self):
def parse_options(): """Parse the command line options""" usage = "usage: %prog [options]" is_x_terminal_emulator = os.path.basename( sys.argv[0]) == 'x-terminal-emulator' parser = OptionParser(usage) parser.add_option('-v', '--version', action='store_true', dest='version', help=_('Display program version')) parser.add_option('-m', '--maximise', action='store_true', dest='maximise', help=_('Maximize the window')) parser.add_option('-f', '--fullscreen', action='store_true', dest='fullscreen', help=_('Make the window fill the screen')) parser.add_option('-b', '--borderless', action='store_true', dest='borderless', help=_('Disable window borders')) parser.add_option('-H', '--hidden', action='store_true', dest='hidden', help=_('Hide the window at startup')) parser.add_option('-T', '--title', dest='forcedtitle', help=_('Specify a title for the window')) parser.add_option('--geometry', dest='geometry', type='string', help=_( 'Set the preferred size and position of the window' '(see X man page)')) if not is_x_terminal_emulator: parser.add_option( '-e', '--command', dest='command', help=_('Specify a command to execute inside the terminal')) else: parser.add_option( '--command', dest='command', help=_('Specify a command to execute inside the terminal')) parser.add_option( '-e', '--execute2', dest='execute', action='callback', callback=execute_cb, help=_('Use the rest of the command line as a command to ' 'execute inside the terminal, and its arguments')) parser.add_option('-g', '--config', dest='config', help=_('Specify a config file')) parser.add_option( '-x', '--execute', dest='execute', action='callback', callback=execute_cb, help=_('Use the rest of the command line as a command to execute ' 'inside the terminal, and its arguments')) parser.add_option('--working-directory', metavar='DIR', dest='working_directory', help=_('Set the working directory')) parser.add_option('-i', '--icon', dest='forcedicon', help=_('Set a custom \ icon for the window (by file or name)')) parser.add_option( '-r', '--role', dest='role', help=_('Set a custom WM_WINDOW_ROLE property on the window')) parser.add_option('-l', '--layout', dest='layout', help=_('Launch with the given layout')) parser.add_option('-s', '--select-layout', action='store_true', dest='select', help=_('Select a layout from a list')) parser.add_option('-p', '--profile', dest='profile', help=_('Use a different profile as the default')) parser.add_option('-u', '--no-dbus', action='store_true', dest='nodbus', help=_('Disable DBus')) parser.add_option( '-d', '--debug', action='count', dest='debug', help=_('Enable debugging information (twice for debug server)')) parser.add_option( '--debug-classes', action='store', dest='debug_classes', help=_('Comma separated list of classes to limit debugging to')) parser.add_option( '--debug-methods', action='store', dest='debug_methods', help=_('Comma separated list of methods to limit debugging to')) parser.add_option( '--new-tab', action='store_true', dest='new_tab', help=_('If Terminator is already running, just open a new tab')) for item in [ '--sm-client-id', '--sm-config-prefix', '--screen', '-n', '--no-gconf' ]: parser.add_option(item, dest='dummy', action='store', help=SUPPRESS_HELP) global options (options, args) = parser.parse_args() if len(args) != 0: parser.error('Additional unexpected arguments found: %s' % args) if options.version: print('%s %s' % (APP_NAME, APP_VERSION)) sys.exit(0) if options.debug_classes or options.debug_methods: if not options.debug > 0: options.debug = 1 if options.debug: terminatorlib.util.DEBUG = True if options.debug > 1: terminatorlib.util.DEBUGFILES = True if options.debug_classes: classes = options.debug_classes.split(',') for item in classes: terminatorlib.util.DEBUGCLASSES.append(item.strip()) if options.debug_methods: methods = options.debug_methods.split(',') for item in methods: terminatorlib.util.DEBUGMETHODS.append(item.strip()) if options.working_directory: if os.path.exists(os.path.expanduser(options.working_directory)): options.working_directory = os.path.expanduser( options.working_directory) os.chdir(options.working_directory) else: err('OptionParse::parse_options: %s does not exist' % options.working_directory) options.working_directory = '' if options.layout is None: options.layout = 'default' configobj = Config() if options.profile and options.profile not in configobj.list_profiles(): options.profile = None configobj.options_set(options) if terminatorlib.util.DEBUG == True: dbg('OptionParse::parse_options: command line options: %s' % options) return options
import terminatorlib.plugin as plugin from terminatorlib.translation import _ from terminatorlib.util import err, dbg from terminatorlib.version import APP_NAME try: gi.require_version('Notify', '0.7') from gi.repository import Notify # Every plugin you want Terminator to load *must* be listed in 'AVAILABLE' # This is inside this try so we only make the plugin available if pynotify # is present on this computer. AVAILABLE = ['ActivityWatch', 'InactivityWatch'] except ImportError: err(_('ActivityWatch plugin unavailable: please install python-notify')) config = Config() inactive_period = float(config.plugin_get('InactivityWatch', 'inactive_period', 10.0)) watch_interval = int(config.plugin_get('InactivityWatch', 'watch_interval', 5000)) hush_period = float(config.plugin_get('ActivityWatch', 'hush_period', 10.0)) class ActivityWatch(plugin.MenuItem): """Add custom commands to the terminal menu""" capabilities = ['terminal_menu'] watches = None last_notifies = None timers = None def __init__(self):
class Online_Servers(plugin.MenuItem): capabilities = ['terminal_menu'] config_file = os.path.join(get_config_dir(), 'online_Servers') def __init__(self): self.config = Config() if not isinstance( self.config.plugin_get_config( self.__class__.__name__), dict): self.config.plugin_set_config(self.__class__.__name__, {}) self.config.save() self._authorization = dict(Authorization='Bearer ' + TOKEN) self._base_api_url = 'https://api.online.net/api/v1' self._servers_infos_file_name = '/tmp/online_servers_infos' def callback(self, menuitems, menu, terminal): item = gtk.MenuItem('Serveurs online.net') menuitems.append(item) submenu = gtk.Menu() item.set_submenu(submenu) menuitem = gtk.MenuItem('Préferences') menuitem.connect('activate', self.settings) submenu.append(menuitem) submenu.append(gtk.SeparatorMenuItem()) for server in self._load_servers_infos(): config = self.config.plugin_get_config(self.__class__.__name__) if server['hostname'] in config: port = config[server['hostname']]['port'] if config[server['hostname']]['port'] != '' else '22' user = config[server['hostname']]['user'] + '@' if config[server['hostname']]['user'] != '' else '' if config[server['hostname']]['iface'] == 'private': ip = server['ippriv'] else: ip = server['ippub'] else: port = 22 user = '' ip = server['ippub'] menuitem = gtk.MenuItem(server['hostname']) menuitem.connect( 'activate', self._execute, {'terminal' : terminal, 'command' : 'ssh -p {} {}{}'.format( port, user, ip)}) submenu.append(menuitem) menuitems.append(submenu) def settings(self, widget): dbox = gtk.Dialog( 'Configuration', None, gtk.DIALOG_MODAL, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT)) servers_list = gtk.ListStore(str) def serverList(widget=None, overwrite=False): servers_list.clear() for server in self._load_servers_infos(overwrite=overwrite): servers_list.append([server['hostname']]) serverList() tree = gtk.TreeView(servers_list) selection = tree.get_selection() selection.set_mode(gtk.SELECTION_SINGLE) renderer = gtk.CellRendererText() column = gtk.TreeViewColumn("Serveurs", renderer, text=0) tree.append_column(column) button_box = gtk.VBox() button_edit = gtk.Button('Modifier', stock=gtk.STOCK_EDIT) button_edit.set_sensitive(False) button_edit.connect('clicked', self._edit, tree) selection.connect("changed", self._on_selection_changed, button_edit) button_refresh = gtk.Button('Rafraichir') button_refresh.connect('clicked', serverList, True) HBox = gtk.HBox() HBox.pack_start(tree) button_box.pack_start(button_edit, False, False) button_box.pack_start(button_refresh, False, False) HBox.pack_start(button_box) dbox.vbox.pack_start(HBox) dbox.show_all() if dbox.run() == gtk.RESPONSE_REJECT: dbox.destroy() return def _on_selection_changed(self, selection, data=None): data.set_sensitive(True) return def _edit(self, button, treeview): selection = treeview.get_selection() (store, iter) = selection.get_selected() selected_value = store.get_value(iter, 0) config = self.config.plugin_get_config( self.__class__.__name__) in_config = False if selected_value not in config else True dialog = gtk.Dialog( 'Paramètres liés au serveur', None, gtk.DIALOG_MODAL, ( gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT ) ) table = gtk.Table(1, 3) label = gtk.Label('Interface:') table.attach(label, 0, 1, 0, 1) r1 = gtk.RadioButton(None, label='Privée') r2 = gtk.RadioButton(r1, label='Publique') if in_config: if config[selected_value]['iface'] == 'private': r1.set_active(1) else: r2.set_active(1) else: r2.set_active(1) table.attach(r1, 1, 2, 0, 1) table.attach(r2, 2, 3, 0, 1) dialog.vbox.pack_start(table) table = gtk.Table(1, 2) label = gtk.Label('Utilisateur:') table.attach(label, 0, 1, 1, 2) user = gtk.Entry() if in_config: user.set_text(config[selected_value]['user']) else: user.set_text('') table.attach(user, 1, 2, 1, 2) label = gtk.Label('Port:') table.attach(label, 0, 1, 2, 3) port = gtk.Entry() if in_config: port.set_text(config[selected_value]['port']) else: port.set_text('') table.attach(port, 1, 2, 2, 3) dialog.vbox.pack_start(table) dialog.show_all() data = dict() run = dialog.run() if run == gtk.RESPONSE_REJECT: dialog.destroy() if run == gtk.RESPONSE_ACCEPT: if r1.get_active(): data['iface'] = 'private' else: data['iface'] = 'public' data['user'] = user.get_text() data['port'] = port.get_text() if in_config: self.config.plugin_set_config( self.__class__.__name__, {}) self.config.save() config[selected_value] = data else: config.update({selected_value: data}) self.config.plugin_set_config( self.__class__.__name__, config) self.config.save() dialog.destroy() def _execute(self, widget, data): data['terminal'].vte.feed_child(data['command'] + '\n') @property def _servers_list(self): url = self._base_api_url + '/server' servers = requests.get( url, headers=self._authorization) servers = [server.strip(url) for server in loads(servers.text)] return servers @property def _servers_infos(self): servers = list() servers_list = self._servers_list for i, server in enumerate(servers_list): request = requests.get( self._base_api_url + '/server/' + server, headers=self._authorization) servers.append(loads(request.text)) for i, server in enumerate(list(servers)): servers[i] = dict( hostname=server['hostname'], ippub=server['network']['ip'][0] if server['network']['ip'] != [] else '', ippriv=server['network']['private'][0] if server['network']['private'] != [] else '') return servers def _save_servers_infos(self): infos = self._servers_infos pickle.dump(infos, open(self._servers_infos_file_name, 'wb+')) return infos def _load_servers_infos(self, overwrite=False): if os.path.isfile(self._servers_infos_file_name) and not overwrite: servers = pickle.load(open(self._servers_infos_file_name, 'rb')) else: servers = self._save_servers_infos() return servers
def __init__(self, terminal): """Class initialiser""" self.terminal = terminal self.terminator = Terminator() self.config = Config()
class TerminalPopupMenu(object): """Class implementing the Terminal context menu""" terminal = None terminator = None config = None def __init__(self, terminal): """Class initialiser""" self.terminal = terminal self.terminator = Terminator() self.config = Config() def show(self, widget, event=None): """Display the context menu""" terminal = self.terminal menu = Gtk.Menu() self.popup_menu = menu url = None button = None time = None self.config.set_profile(terminal.get_profile()) if event: url = terminal.vte.match_check_event(event) button = event.button time = event.time else: time = 0 button = 3 if url and url[0]: dbg("URL matches id: %d" % url[1]) if not url[1] in terminal.matches.values(): err("Unknown URL match id: %d" % url[1]) dbg("Available matches: %s" % terminal.matches) nameopen = None namecopy = None if url[1] == terminal.matches['email']: nameopen = _('_Send email to...') namecopy = _('_Copy email address') elif url[1] == terminal.matches['voip']: nameopen = _('Ca_ll VoIP address') namecopy = _('_Copy VoIP address') elif url[1] in terminal.matches.values(): # This is a plugin match for pluginname in terminal.matches: if terminal.matches[pluginname] == url[1]: break dbg("Found match ID (%d) in terminal.matches plugin %s" % (url[1], pluginname)) registry = PluginRegistry() registry.load_plugins() plugins = registry.get_plugins_by_capability('url_handler') for urlplugin in plugins: if urlplugin.handler_name == pluginname: dbg("Identified matching plugin: %s" % urlplugin.handler_name) nameopen = _(urlplugin.nameopen) namecopy = _(urlplugin.namecopy) break if not nameopen: nameopen = _('_Open link') if not namecopy: namecopy = _('_Copy address') icon = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU) item = Gtk.ImageMenuItem.new_with_mnemonic(nameopen) item.set_property('image', icon) item.connect('activate', lambda x: terminal.open_url(url, True)) menu.append(item) item = Gtk.MenuItem.new_with_mnemonic(namecopy) item.connect( 'activate', lambda x: terminal.clipboard.set_text( terminal.prepare_url(url), len(terminal.prepare_url(url)))) menu.append(item) menu.append(Gtk.SeparatorMenuItem()) item = Gtk.ImageMenuItem.new_with_mnemonic(_('_Copy')) item.connect('activate', lambda x: terminal.vte.copy_clipboard()) item.set_sensitive(terminal.vte.get_has_selection()) menu.append(item) item = Gtk.ImageMenuItem.new_with_mnemonic(_('_Paste')) item.connect('activate', lambda x: terminal.paste_clipboard()) menu.append(item) menu.append(Gtk.SeparatorMenuItem()) if not terminal.is_zoomed(): item = Gtk.ImageMenuItem.new_with_mnemonic( _('Split H_orizontally')) image = Gtk.Image() image.set_from_icon_name(APP_NAME + '_horiz', Gtk.IconSize.MENU) item.set_image(image) if hasattr(item, 'set_always_show_image'): item.set_always_show_image(True) item.connect( 'activate', lambda x: terminal.emit('split-horiz', self.terminal.get_cwd())) menu.append(item) item = Gtk.ImageMenuItem.new_with_mnemonic(_('Split V_ertically')) image = Gtk.Image() image.set_from_icon_name(APP_NAME + '_vert', Gtk.IconSize.MENU) item.set_image(image) if hasattr(item, 'set_always_show_image'): item.set_always_show_image(True) item.connect( 'activate', lambda x: terminal.emit('split-vert', self.terminal.get_cwd())) menu.append(item) item = Gtk.MenuItem.new_with_mnemonic(_('Open _Tab')) item.connect('activate', lambda x: terminal.emit('tab-new', False, terminal)) menu.append(item) if self.terminator.debug_address is not None: item = Gtk.MenuItem.new_with_mnemonic(_('Open _Debug Tab')) item.connect( 'activate', lambda x: terminal.emit('tab-new', True, terminal)) menu.append(item) menu.append(Gtk.SeparatorMenuItem()) item = Gtk.ImageMenuItem.new_with_mnemonic(_('_Close')) item.connect('activate', lambda x: terminal.close()) menu.append(item) menu.append(Gtk.SeparatorMenuItem()) if not terminal.is_zoomed(): sensitive = not terminal.get_toplevel() == terminal.get_parent() item = Gtk.MenuItem.new_with_mnemonic(_('_Zoom terminal')) item.connect('activate', terminal.zoom) item.set_sensitive(sensitive) menu.append(item) item = Gtk.MenuItem.new_with_mnemonic(_('Ma_ximize terminal')) item.connect('activate', terminal.maximise) item.set_sensitive(sensitive) menu.append(item) menu.append(Gtk.SeparatorMenuItem()) else: item = Gtk.MenuItem.new_with_mnemonic(_('_Restore all terminals')) item.connect('activate', terminal.unzoom) menu.append(item) menu.append(Gtk.SeparatorMenuItem()) if self.config['show_titlebar'] == False: item = Gtk.MenuItem.new_with_mnemonic(_('Grouping')) submenu = self.terminal.populate_group_menu() submenu.show_all() item.set_submenu(submenu) menu.append(item) menu.append(Gtk.SeparatorMenuItem()) item = Gtk.CheckMenuItem.new_with_mnemonic(_('Show _scrollbar')) item.set_active(terminal.scrollbar.get_property('visible')) item.connect('toggled', lambda x: terminal.do_scrollbar_toggle()) menu.append(item) if hasattr(Gtk, 'Builder'): # VERIFY FOR GTK3: is this ever false? item = Gtk.MenuItem.new_with_mnemonic(_('_Preferences')) item.connect('activate', lambda x: PrefsEditor(self.terminal)) menu.append(item) profilelist = sorted(self.config.list_profiles(), key=string.lower) if len(profilelist) > 1: item = Gtk.MenuItem.new_with_mnemonic(_('Profiles')) submenu = Gtk.Menu() item.set_submenu(submenu) menu.append(item) current = terminal.get_profile() group = None for profile in profilelist: profile_label = profile if profile_label == 'default': profile_label = profile.capitalize() item = Gtk.RadioMenuItem(profile_label, group) if profile == current: item.set_active(True) item.connect('activate', terminal.force_set_profile, profile) submenu.append(item) self.add_encoding_items(menu) try: menuitems = [] registry = PluginRegistry() registry.load_plugins() plugins = registry.get_plugins_by_capability('terminal_menu') for menuplugin in plugins: menuplugin.callback(menuitems, menu, terminal) if len(menuitems) > 0: menu.append(Gtk.SeparatorMenuItem()) for menuitem in menuitems: menu.append(menuitem) except Exception as ex: err('TerminalPopupMenu::show: %s' % ex) menu.show_all() menu.popup(None, None, None, None, button, time) return True def add_encoding_items(self, menu): """Add the encoding list to the menu""" terminal = self.terminal active_encodings = terminal.config['active_encodings'] item = Gtk.MenuItem.new_with_mnemonic(_("Encodings")) menu.append(item) submenu = Gtk.Menu() item.set_submenu(submenu) encodings = TerminatorEncoding().get_list() encodings.sort(lambda x, y: cmp(x[2].lower(), y[2].lower())) current_encoding = terminal.vte.get_encoding() group = None if current_encoding not in active_encodings: active_encodings.insert(0, _(current_encoding)) for encoding in active_encodings: if encoding == terminal.default_encoding: extratext = " (%s)" % _("Default") elif encoding == current_encoding and \ terminal.custom_encoding == True: extratext = " (%s)" % _("User defined") else: extratext = "" radioitem = Gtk.RadioMenuItem(_(encoding) + extratext, group) if encoding == current_encoding: radioitem.set_active(True) if group is None: group = radioitem radioitem.connect('activate', terminal.on_encoding_change, encoding) submenu.append(radioitem) item = Gtk.MenuItem.new_with_mnemonic(_("Other Encodings")) submenu.append(item) #second level submenu = Gtk.Menu() item.set_submenu(submenu) group = None for encoding in encodings: if encoding[1] in active_encodings: continue if encoding[1] is None: label = "%s %s" % (encoding[2], terminal.vte.get_encoding()) else: label = "%s %s" % (encoding[2], encoding[1]) radioitem = Gtk.RadioMenuItem(label, group) if group is None: group = radioitem if encoding[1] == current_encoding: radioitem.set_active(True) radioitem.connect('activate', terminal.on_encoding_change, encoding[1]) submenu.append(radioitem)
class Terminator(Borg): """master object for the application""" windows = None launcher_windows = None windowtitle = None terminals = None groups = None config = None keybindings = None style_providers = None last_focused_term = None origcwd = None dbus_path = None dbus_name = None pid_cwd = None gnome_client = None debug_address = None ibus_running = None doing_layout = None layoutname = None last_active_window = None prelayout_windows = None groupsend = None groupsend_type = {'all': 0, 'group': 1, 'off': 2} cur_gtk_theme_name = None gtk_settings = None def __init__(self): """Class initialiser""" Borg.__init__(self, self.__class__.__name__) self.prepare_attributes() def prepare_attributes(self): """Initialise anything that isn't already""" if not self.windows: self.windows = [] if not self.launcher_windows: self.launcher_windows = [] if not self.terminals: self.terminals = [] if not self.groups: self.groups = [] if not self.config: self.config = Config() if self.groupsend == None: self.groupsend = self.groupsend_type[ self.config['broadcast_default']] if not self.keybindings: self.keybindings = Keybindings() self.keybindings.configure(self.config['keybindings']) if not self.style_providers: self.style_providers = [] if not self.doing_layout: self.doing_layout = False if not self.pid_cwd: self.pid_cwd = get_pid_cwd() if self.gnome_client is None: self.attempt_gnome_client() self.connect_signals() def connect_signals(self): """Connect all the gtk signals""" self.gtk_settings = Gtk.Settings().get_default() self.gtk_settings.connect('notify::gtk-theme-name', self.on_gtk_theme_name_notify) self.cur_gtk_theme_name = self.gtk_settings.get_property( 'gtk-theme-name') def set_origcwd(self, cwd): """Store the original cwd our process inherits""" if cwd == '/': cwd = os.path.expanduser('~') os.chdir(cwd) self.origcwd = cwd def set_dbus_data(self, dbus_service): """Store the DBus bus details, if they are available""" if dbus_service: self.dbus_name = dbus_service.bus_name.get_name() self.dbus_path = dbus_service.bus_path def attempt_gnome_client(self): """Attempt to find a GNOME Session to register with""" try: from gi.repository import Gnome self.gnome_program = Gnome.init(APP_NAME, APP_VERSION) # VERIFY FOR GTK3 self.gnome_client = Gnome.Ui.master_client() # VERIFY FOR GTK3 self.gnome_client.connect_to_session_manager() self.gnome_client.connect('save-yourself', self.save_yourself) self.gnome_client.connect('die', self.die) dbg('GNOME session support enabled and registered') except (ImportError, AttributeError): self.gnome_client = False dbg('GNOME session support not available') def save_yourself(self, *args): """Save as much state as possible for the session manager""" dbg('preparing session manager state') # FIXME: Implement this def die(self, *args): """Die at the hands of the session manager""" dbg('session manager asked us to die') # FIXME: Implement this def get_windows(self): """Return a list of windows""" return self.windows def register_window(self, window): """Register a new window widget""" if window not in self.windows: dbg('Terminator::register_window: registering %s:%s' % (id(window), type(window))) self.windows.append(window) def deregister_window(self, window): """de-register a window widget""" dbg('Terminator::deregister_window: de-registering %s:%s' % (id(window), type(window))) if window in self.windows: self.windows.remove(window) else: err('%s is not in registered window list' % window) if len(self.windows) == 0: # We have no windows left, we should exit dbg('no windows remain, quitting') Gtk.main_quit() def register_launcher_window(self, window): """Register a new launcher window widget""" if window not in self.launcher_windows: dbg('Terminator::register_launcher_window: registering %s:%s' % (id(window), type(window))) self.launcher_windows.append(window) def deregister_launcher_window(self, window): """de-register a launcher window widget""" dbg('Terminator::deregister_launcher_window: de-registering %s:%s' % (id(window), type(window))) if window in self.launcher_windows: self.launcher_windows.remove(window) else: err('%s is not in registered window list' % window) if len(self.launcher_windows) == 0 and len(self.windows) == 0: # We have no windows left, we should exit dbg('no windows remain, quitting') Gtk.main_quit() def register_terminal(self, terminal): """Register a new terminal widget""" if terminal not in self.terminals: dbg('Terminator::register_terminal: registering %s:%s' % (id(terminal), type(terminal))) self.terminals.append(terminal) def deregister_terminal(self, terminal): """De-register a terminal widget""" dbg('Terminator::deregister_terminal: de-registering %s:%s' % (id(terminal), type(terminal))) self.terminals.remove(terminal) if len(self.terminals) == 0: dbg('no terminals remain, destroying all windows') for window in self.windows: window.destroy() else: dbg('Terminator::deregister_terminal: %d terminals remain' % len(self.terminals)) def find_terminal_by_uuid(self, uuid): """Search our terminals for one matching the supplied UUID""" dbg('searching self.terminals for: %s' % uuid) for terminal in self.terminals: dbg('checking: %s (%s)' % (terminal.uuid.urn, terminal)) if terminal.uuid.urn == uuid: return terminal return None def find_window_by_uuid(self, uuid): """Search our terminals for one matching the supplied UUID""" dbg('searching self.terminals for: %s' % uuid) for window in self.windows: 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): """Create a window with a Terminal in it""" maker = Factory() window = maker.make('Window') terminal = maker.make('Terminal') if cwd: terminal.set_cwd(cwd) if profile and self.config['always_split_with_profile']: terminal.force_set_profile(None, profile) window.add(terminal) window.show(True) terminal.spawn_child() return (window, terminal) def create_layout(self, layoutname): """Create all the parts necessary to satisfy the specified layout""" layout = None objects = {} self.doing_layout = True self.last_active_window = None self.prelayout_windows = self.windows[:] layout = copy.deepcopy(self.config.layout_get_config(layoutname)) if not layout: # User specified a non-existent layout. default to one Terminal err('layout %s not defined' % layout) self.new_window() return # Wind the flat objects into a hierarchy hierarchy = {} count = 0 # Loop over the layout until we have consumed it, or hit 1000 loops. # This is a stupid artificial limit, but it's safe. while len(layout) > 0 and count < 1000: count = count + 1 if count == 1000: err('hit maximum loop boundary. THIS IS VERY LIKELY A BUG') for obj in tuple(layout.keys()): if layout[obj]['type'].lower() == 'window': hierarchy[obj] = {} hierarchy[obj]['type'] = 'Window' hierarchy[obj]['children'] = {} # Copy any additional keys for objkey in layout[obj].keys(): if layout[obj][ objkey] != '' and objkey not in hierarchy[obj]: hierarchy[obj][objkey] = layout[obj][objkey] objects[obj] = hierarchy[obj] del layout[obj] else: # Now examine children to see if their parents exist yet if 'parent' not in layout[obj]: err('Invalid object: %s' % obj) del layout[obj] continue if layout[obj]['parent'] in objects: # Our parent has been created, add ourselves childobj = {} childobj['type'] = layout[obj]['type'] childobj['children'] = {} # Copy over any additional object keys for objkey in layout[obj].keys(): if objkey not in childobj: childobj[objkey] = layout[obj][objkey] objects[layout[obj] ['parent']]['children'][obj] = childobj objects[obj] = childobj del layout[obj] layout = hierarchy for windef in layout: if layout[windef]['type'] != 'Window': err('invalid layout format. %s' % layout) raise ValueError dbg('Creating a window') window, terminal = self.new_window() if 'position' in layout[windef]: parts = layout[windef]['position'].split(':') if len(parts) == 2: window.move(int(parts[0]), int(parts[1])) if 'size' in layout[windef]: parts = layout[windef]['size'] winx = int(parts[0]) winy = int(parts[1]) if winx > 1 and winy > 1: window.resize(winx, winy) if 'title' in layout[windef]: window.title.force_title(layout[windef]['title']) if 'maximised' in layout[windef]: if layout[windef]['maximised'] == 'True': window.ismaximised = True else: window.ismaximised = False window.set_maximised(window.ismaximised) if 'fullscreen' in layout[windef]: if layout[windef]['fullscreen'] == 'True': window.isfullscreen = True else: window.isfullscreen = False window.set_fullscreen(window.isfullscreen) window.create_layout(layout[windef]) self.layoutname = layoutname def layout_done(self): """Layout operations have finished, record that fact""" self.doing_layout = False maker = Factory() window_last_active_term_mapping = {} for window in self.windows: if window.is_child_notebook(): source = window.get_toplevel().get_children()[0] else: source = window window_last_active_term_mapping[window] = copy.copy( source.last_active_term) for terminal in self.terminals: if not terminal.pid: terminal.spawn_child() for window in self.windows: if window.is_child_notebook(): # For windows with a notebook notebook = window.get_toplevel().get_children()[0] # Cycle through pages by number for page in range(0, notebook.get_n_pages()): # Try and get the entry in the previously saved mapping mapping = window_last_active_term_mapping[window] page_last_active_term = mapping.get( notebook.get_nth_page(page), None) if page_last_active_term is None: # Couldn't find entry, so we find the first child of type Terminal children = notebook.get_nth_page(page).get_children() for page_last_active_term in children: if maker.isinstance(page_last_active_term, 'Terminal'): page_last_active_term = page_last_active_term.uuid break else: err('Should never reach here!') page_last_active_term = None if page_last_active_term is None: # Bail on this tab as we're having no luck here, continue with the next continue # Set the notebook entry, then ensure Terminal is visible and focussed urn = page_last_active_term.urn notebook.last_active_term[notebook.get_nth_page( page)] = page_last_active_term if urn: term = self.find_terminal_by_uuid(urn) if term: term.ensure_visible_and_focussed() else: # For windows without a notebook ensure Terminal is visible and focussed 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() # Build list of new windows using prelayout list new_win_list = [] for window in self.windows: if window not in self.prelayout_windows: new_win_list.append(window) # Make sure all new windows get bumped to the top for window in new_win_list: window.show() window.grab_focus() try: t = GdkX11.x11_get_server_time(window.get_window()) except (TypeError, AttributeError): t = 0 window.get_window().focus(t) # Awful workaround to be sure that the last focused window is actually the one focused. # Don't ask, don't tell policy on this. Even this is not 100% if self.last_active_window: window = self.find_window_by_uuid(self.last_active_window.urn) count = 0 while count < 1000 and Gtk.events_pending(): count += 1 Gtk.main_iteration_do(False) window.show() window.grab_focus() try: t = GdkX11.x11_get_server_time(window.get_window()) except (TypeError, AttributeError): t = 0 window.get_window().focus(t) self.prelayout_windows = None def on_gtk_theme_name_notify(self, settings, prop): """Reconfigure if the gtk theme name changes""" new_gtk_theme_name = settings.get_property(prop.name) if new_gtk_theme_name != self.cur_gtk_theme_name: self.cur_gtk_theme_name = new_gtk_theme_name self.reconfigure() def reconfigure(self): """Update configuration for the whole application""" if self.style_providers != []: for style_provider in self.style_providers: Gtk.StyleContext.remove_provider_for_screen( Gdk.Screen.get_default(), style_provider) self.style_providers = [] # Force the window background to be transparent for newer versions of # GTK3. We then have to fix all the widget backgrounds because the # widgets theming may not render it's own background. css = """ .terminator-terminal-window { background-color: alpha(@theme_bg_color,0); } .terminator-terminal-window .notebook.header, .terminator-terminal-window notebook header { background-color: @theme_bg_color; } .terminator-terminal-window .pane-separator { background-color: @theme_bg_color; } .terminator-terminal-window .terminator-terminal-searchbar { background-color: @theme_bg_color; } """ # Fix several themes that put a borders, corners, or backgrounds around # viewports, making the titlebar look bad. css += """ .terminator-terminal-window GtkViewport, .terminator-terminal-window viewport { border-width: 0px; border-radius: 0px; background-color: transparent; } """ # Add per profile snippets for setting the background of the HBox template = """ .terminator-profile-%s { background-color: alpha(%s, %s); } """ profiles = self.config.base.profiles for profile in profiles.keys(): if profiles[profile]['use_theme_colors']: # Create a dummy window/vte and realise it so it has correct # values to read from tmp_win = Gtk.Window() tmp_vte = Vte.Terminal() tmp_win.add(tmp_vte) tmp_win.realize() bgcolor = tmp_vte.get_style_context().get_background_color( Gtk.StateType.NORMAL) bgcolor = "#{0:02x}{1:02x}{2:02x}".format( int(bgcolor.red * 255), int(bgcolor.green * 255), int(bgcolor.blue * 255)) tmp_win.remove(tmp_vte) del tmp_vte del tmp_win else: bgcolor = Gdk.RGBA() bgcolor = profiles[profile]['background_color'] if profiles[profile]['background_type'] == 'transparent': bgalpha = profiles[profile]['background_darkness'] else: bgalpha = "1" munged_profile = "".join( [c if c.isalnum() else "-" for c in profile]) css += template % (munged_profile, bgcolor, bgalpha) style_provider = Gtk.CssProvider() style_provider.load_from_data(css.encode('utf8')) self.style_providers.append(style_provider) # Attempt to load some theme specific stylistic tweaks for appearances usr_theme_dir = os.path.expanduser('~/.local/share/themes') (head, _tail) = os.path.split(borg.__file__) app_theme_dir = os.path.join(head, 'themes') theme_name = self.gtk_settings.get_property('gtk-theme-name') theme_part_list = ['terminator.css'] if self.config[ 'extra_styling']: # checkbox_style - needs adding to prefs theme_part_list.append('terminator_styling.css') for theme_part_file in theme_part_list: for theme_dir in [usr_theme_dir, app_theme_dir]: path_to_theme_specific_css = os.path.join( theme_dir, theme_name, 'gtk-3.0/apps', theme_part_file) if os.path.isfile(path_to_theme_specific_css): style_provider = Gtk.CssProvider() style_provider.connect('parsing-error', self.on_css_parsing_error) try: style_provider.load_from_path( path_to_theme_specific_css) except GError: # Hmmm. Should we try to provide GTK version specific files here on failure? gtk_version_string = '.'.join([ str(Gtk.get_major_version()), str(Gtk.get_minor_version()), str(Gtk.get_micro_version()) ]) err('Error(s) loading css from %s into Gtk %s' % (path_to_theme_specific_css, gtk_version_string)) self.style_providers.append(style_provider) break # Size the GtkPaned splitter handle size. if self.config['handle_size'] in range(0, 21): css = '''.terminator-terminal-window GtkPaned, .terminator-terminal-window paned {{ min-width: {0}px; min-height: {0}px; }} '''.format(self.config['handle_size']) style_provider = Gtk.CssProvider() style_provider.load_from_data(css.encode()) self.style_providers.append(style_provider) # Apply the providers, incrementing priority so they don't cancel out # each other for idx in range(0, len(self.style_providers)): Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), self.style_providers[idx], Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + idx) # Cause all the terminals to reconfigure for terminal in self.terminals: terminal.reconfigure() # Reparse our keybindings self.keybindings.configure(self.config['keybindings']) # Update tab position if appropriate maker = Factory() for window in self.windows: child = window.get_child() if maker.isinstance(child, 'Notebook'): child.configure() def on_css_parsing_error(self, provider, section, error, user_data=None): """Report CSS parsing issues""" file_path = section.get_file().get_path() line_no = section.get_end_line() + 1 col_no = section.get_end_position() + 1 err('%s, at line %d, column %d, of file %s' % (error.message, line_no, col_no, file_path)) def create_group(self, name): """Create a new group""" if name not in self.groups: dbg('Terminator::create_group: registering group %s' % name) self.groups.append(name) def closegroupedterms(self, group): """Close all terminals in a group""" for terminal in self.terminals[:]: if terminal.group == group: terminal.close() def group_hoover(self): """Clean out unused groups""" if self.config['autoclean_groups']: inuse = [] todestroy = [] for terminal in self.terminals: if terminal.group: if not terminal.group in inuse: inuse.append(terminal.group) for group in self.groups: if not group in inuse: todestroy.append(group) dbg('Terminator::group_hoover: %d groups, hoovering %d' % (len(self.groups), len(todestroy))) for group in todestroy: self.groups.remove(group) def group_emit(self, terminal, group, type, event): """Emit to each terminal in a group""" dbg('Terminator::group_emit: emitting a keystroke for group %s' % group) for term in self.terminals: if term != terminal and term.group == group: term.vte.emit(type, eventkey2gdkevent(event)) def all_emit(self, terminal, type, event): """Emit to all terminals""" for term in self.terminals: if term != terminal: term.vte.emit(type, eventkey2gdkevent(event)) def do_enumerate(self, widget, pad): """Insert the number of each terminal in a group, into that terminal""" if pad: numstr = '%0' + str(len(str(len(self.terminals)))) + 'd' else: numstr = '%d' terminals = [] for window in self.windows: containers, win_terminals = enumerate_descendants(window) terminals.extend(win_terminals) for term in self.get_target_terms(widget): idx = terminals.index(term) term.feed(numstr % (idx + 1)) def get_sibling_terms(self, widget): termset = [] for term in self.terminals: if term.group == widget.group: termset.append(term) return termset def get_target_terms(self, widget): """Get the terminals we should currently be broadcasting to""" if self.groupsend == self.groupsend_type['all']: return self.terminals elif self.groupsend == self.groupsend_type['group']: if widget.group != None: return self.get_sibling_terms(widget) return [widget] 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(): return terminal return None def focus_changed(self, widget): """We just moved focus to a new terminal""" for terminal in self.terminals: terminal.titlebar.update(widget) return def focus_left(self, widget): self.last_focused_term = widget def describe_layout(self): """Describe our current layout""" layout = {} count = 0 for window in self.windows: parent = '' count = window.describe_layout(count, parent, layout, 0) return layout
def __init__(self): """Class initialiser""" self.children = [] self.signals = [] self.cnxids = Signalman() self.config = Config()
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA import gtk import terminatorlib.plugin as plugin from terminatorlib.translation import _ from os.path import isfile from glib import timeout_add_seconds import vte from terminatorlib.config import Config config = Config( ) base = config.base profile = config.get_profile( ) idx = 0 import sys import gobject from gobject import GObject sys.path.append('.') from setter import Viewer import os class Fn( ): def __init__( self,profile,term=None ): self.profile = profile
class Colorize(plugin.MenuItem): """ Add custom command to the terminal menu""" capabilities = ['terminal_menu'] config = None color_set = None ratio = 0.7 presets = { 'color0' : { 'name' : ' blue', 'title_transmit_bg_color': '#0076C9' }, 'color1' : { 'name' : 'purple', 'title_transmit_bg_color': '#B20DAC' }, 'color2' : { 'name' : 'yellow', 'title_transmit_bg_color': '#EAF12A' }, 'color3' : { 'name' : 'green', 'title_transmit_bg_color': '#50B20D' }, 'color4' : { 'name' : 'cyan', 'title_transmit_bg_color': '#2DF2C1' } } def __init__(self): plugin.MenuItem.__init__(self) self.config = Config() colorize_config = self.config.plugin_get_config(self.__class__.__name__) if not colorize_config: colorize_config = self.presets self.color_set = [] counter = 0 if colorize_config: while colorize_config.get('color' + str(counter)): self.color_set.append(colorize_config.get('color' + str(counter))) counter += 1 print self.color_set def callback(self, menuitems, menu, terminal): """ Add save menu item to log 'content'the menu""" vte_terminal = terminal.get_vte() change_color_item = gtk.MenuItem(_('Change color')) change_color_item.connect("activate", self.change_color, terminal) change_color_item.set_has_tooltip(True) change_color_item.set_tooltip_text("Change titlebar color of this terminal") menuitems.append(change_color_item) # sub menu pick_color_menu = gtk.Menu() counter = 1 for color in self.color_set: if color.get('name'): suffix = color['name'] else: suffix = str(counter) color_item = gtk.MenuItem(_('Color') + ' ' + suffix) color_item.connect("activate", self.pick_color, terminal, counter - 1) color_item.set_has_tooltip(True) color_item.set_tooltip_text("Set this color for current terminal") accel_group = gtk.AccelGroup() color_item.add_accelerator("activate", accel_group, ord(str(counter)), gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK, gtk.ACCEL_VISIBLE) pick_color_menu.append(color_item) counter += 1 item = gtk.MenuItem(_('Pick color')) item.set_submenu(pick_color_menu) menuitems.append(item) def get_terminal_container(self, terminal, container=None): terminator = Terminator() if not container: for window in terminator.windows: owner = self.get_terminal_container(terminal, window) if owner: return owner else: for child in container.get_children(): if isinstance(child, Terminal) and child == terminal: return container if isinstance(child, Container): owner = self.get_terminal_container(terminal, child) if owner: return owner def register_signals(self, container, terminal): container.signals.append({ 'name': 'pick-first-color', 'flags': gobject.SIGNAL_RUN_LAST, 'return_type': gobject.TYPE_NONE, 'param_types': None }) # container.signals.append({ # 'name': 'split-vert-clone', # 'flags': gobject.SIGNAL_RUN_LAST, # 'return_type': gobject.TYPE_NONE, # 'param_types': None # }) container.register_signals(terminal) container.connect_child(terminal, 'pick-first-color', self.pick_first_color) # container.connect_child(terminal, 'split-vert-clone', self.split_vert) def pick_first_color(self, terminal): self.pick_color(None, terminal, 1) def pick_color(self, _widget, terminal, index): print 'index', index self.set_titlebar_color(terminal, gtk.gdk.Color(self.color_set[index]['title_transmit_bg_color'])) def change_color(self, _widget, Terminal): """ Handle menu item callback by saving text to a file""" color_dialog = gtk.ColorSelectionDialog("Pick new terminal's titlebar color") color_sel = color_dialog.colorsel # set previous colors previous_color = gtk.gdk.color_parse(Terminal.config['title_transmit_bg_color']) color_sel.set_previous_color(previous_color) color_sel.set_current_color(previous_color) color_sel.set_has_palette(True) response = color_dialog.run() if response == gtk.RESPONSE_OK: self.set_titlebar_color(Terminal, color_sel.get_current_color()) color_dialog.destroy() def get_inactive_color(self, transmit_color): return gtk.gdk.Color(transmit_color.red_float * self.ratio, transmit_color.green_float * self.ratio, transmit_color.blue_float * self.ratio) def get_font_color(self, bg_color): lightness = (((bg_color.red_float * 299) + (bg_color.green_float * 587) + (bg_color.blue_float * 114)) /1000) new_fg_color = gtk.gdk.Color(0, 0, 0) if lightness < 0.5: new_fg_color = gtk.gdk.Color(65535, 65535, 65535) return new_fg_color def set_titlebar_color(self, Terminal, color): new_transmit_bg_color = color new_inactive_bg_color = self.get_inactive_color(new_transmit_bg_color) new_transmit_fg_color = self.get_font_color(new_transmit_bg_color) new_inactive_fg_color = self.get_font_color(new_inactive_bg_color) new_color_config = { 'title_transmit_bg_color': new_transmit_bg_color.to_string(), 'title_inactive_bg_color': new_inactive_bg_color.to_string(), 'title_transmit_fg_color': new_transmit_fg_color.to_string(), 'title_inactive_fg_color': new_inactive_fg_color.to_string() } new_config = ColorizeConfig(Terminal.titlebar.config, new_color_config) Terminal.titlebar.config = new_config
class Container(object): """Base class for Terminator Containers""" terminator = None immutable = None children = None config = None signals = None signalman = None def __init__(self): """Class initialiser""" self.children = [] self.signals = [] self.cnxids = Signalman() self.config = Config() def register_signals(self, widget): """Register gobject signals in a way that avoids multiple inheritance""" existing = GObject.signal_list_names(widget) for signal in self.signals: if signal['name'] in existing: dbg('Container:: skipping signal %s for %s, already exists' % (signal['name'], widget)) else: dbg('Container:: registering signal for %s on %s' % (signal['name'], widget)) try: GObject.signal_new(signal['name'], widget, signal['flags'], signal['return_type'], signal['param_types']) except RuntimeError: err('Container:: registering signal for %s on %s failed' % (signal['name'], widget)) def connect_child(self, widget, signal, handler, *args): """Register the requested signal and record its connection ID""" self.cnxids.new(widget, signal, handler, *args) return def disconnect_child(self, widget): """De-register the signals for a child""" self.cnxids.remove_widget(widget) def get_offspring(self): """Return a list of direct child widgets, if any""" return (self.children) def get_child_metadata(self, widget): """Return metadata that would be useful to recreate ourselves after our child is .remove()d and .add()ed""" return None def split_horiz(self, widget, cwd=None): """Split this container horizontally""" return (self.split_axis(widget, True, cwd)) def split_vert(self, widget, cwd=None): """Split this container vertically""" return (self.split_axis(widget, False, cwd)) def split_axis(self, widget, vertical=True, cwd=None, sibling=None, siblinglast=None): """Default axis splitter. This should be implemented by subclasses""" raise NotImplementedError('split_axis') def rotate(self, widget, clockwise): """Rotate children in this container""" raise NotImplementedError('rotate') def add(self, widget, metadata=None): """Add a widget to the container""" raise NotImplementedError('add') def remove(self, widget): """Remove a widget from the container""" raise NotImplementedError('remove') def replace(self, oldwidget, newwidget): """Replace the child oldwidget with newwidget. This is the bare minimum required for this operation. Containers should override it if they have more complex requirements""" if not oldwidget in self.get_children(): err('%s is not a child of %s' % (oldwidget, self)) return self.remove(oldwidget) self.add(newwidget) def hoover(self): """Ensure we still have a reason to exist""" raise NotImplementedError('hoover') def get_children(self): """Return an ordered list of the children of this Container""" raise NotImplementedError('get_children') def closeterm(self, widget): """Handle the closure of a terminal""" try: if self.get_property('term_zoomed'): # We're zoomed, so unzoom and then start closing again dbg('Container::closeterm: terminal zoomed, unzooming') self.unzoom(widget) widget.close() return (True) except TypeError: pass if not self.remove(widget): dbg('Container::closeterm: self.remove() failed for %s' % widget) return (False) self.terminator.deregister_terminal(widget) widget.close() self.terminator.group_hoover() return (True) def resizeterm(self, widget, keyname): """Handle a keyboard event requesting a terminal resize""" raise NotImplementedError('resizeterm') def toggle_zoom(self, widget, fontscale=False): """Toggle the existing zoom state""" try: if self.get_property('term_zoomed'): self.unzoom(widget) else: self.zoom(widget, fontscale) except TypeError: err('Container::toggle_zoom: %s is unable to handle zooming, for \ %s' % (self, widget)) def zoom(self, widget, fontscale=False): """Zoom a terminal""" raise NotImplementedError('zoom') def unzoom(self, widget): """Unzoom a terminal""" raise NotImplementedError('unzoom') def construct_confirm_close(self, window, reqtype): """Create a confirmation dialog for closing things""" # skip this dialog if applicable if self.config['suppress_multiple_term_dialog']: return Gtk.ResponseType.ACCEPT 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')) primary = Gtk.Label( label=_('<big><b>Close multiple terminals?</b></big>')) 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.set_line_wrap(True) labels = Gtk.VBox() labels.pack_start(primary, False, False, 6) labels.pack_start(secondary, False, False, 6) image = Gtk.Image.new_from_stock(Gtk.STOCK_DIALOG_WARNING, Gtk.IconSize.DIALOG) image.set_alignment(0.5, 0) box = Gtk.HBox() box.pack_start(image, False, False, 6) box.pack_start(labels, False, False, 6) dialog.vbox.pack_start(box, False, False, 12) checkbox = Gtk.CheckButton(_("Do not show this message next time")) dialog.vbox.pack_end(checkbox, True, True, 0) dialog.show_all() result = dialog.run() # set configuration self.config.base.reload() self.config['suppress_multiple_term_dialog'] = checkbox.get_active() self.config.save() dialog.destroy() return (result) def propagate_title_change(self, widget, title): """Pass a title change up the widget stack""" maker = Factory() parent = self.get_parent() title = widget.get_window_title() if maker.isinstance(self, 'Notebook'): self.update_tab_label_text(widget, title) elif maker.isinstance(self, 'Window'): self.title.set_title(widget, title) if maker.isinstance(parent, 'Container'): parent.propagate_title_change(widget, title) def get_visible_terminals(self): """Walk the widget tree to find all of the visible terminals. That is, any terminals which are not hidden in another Notebook pane""" if not hasattr(self, 'cached_maker'): self.cached_maker = Factory() maker = self.cached_maker terminals = {} for child in self.get_offspring(): if not child: continue if maker.isinstance(child, 'Terminal'): terminals[child] = child.get_allocation() elif maker.isinstance(child, 'Container'): terminals.update(child.get_visible_terminals()) else: err('Unknown child type %s' % type(child)) return (terminals) def describe_layout(self, count, parent, global_layout, child_order): """Describe our current layout""" layout = {} maker = Factory() mytype = maker.type(self) if not mytype: err('unable to detemine own type. %s' % self) return ({}) layout['type'] = mytype layout['parent'] = parent layout['order'] = child_order if hasattr(self, 'get_position'): position = self.get_position() if hasattr(position, '__iter__'): position = ':'.join([str(x) for x in position]) layout['position'] = position if hasattr(self, 'ismaximised'): layout['maximised'] = self.ismaximised if hasattr(self, 'isfullscreen'): layout['fullscreen'] = self.isfullscreen if hasattr(self, 'ratio'): layout['ratio'] = self.ratio if hasattr(self, 'get_size'): layout['size'] = self.get_size() if hasattr(self, 'title'): layout['title'] = self.title.text if mytype == 'Notebook': labels = [] last_active_term = [] for tabnum in xrange(0, self.get_n_pages()): page = self.get_nth_page(tabnum) label = self.get_tab_label(page) labels.append(label.get_custom_label()) last_active_term.append( self.last_active_term[self.get_nth_page(tabnum)]) layout['labels'] = labels layout['last_active_term'] = last_active_term layout['active_page'] = self.get_current_page() else: if hasattr( self, 'last_active_term') and self.last_active_term is not None: layout['last_active_term'] = self.last_active_term if mytype == 'Window': if self.uuid == self.terminator.last_active_window: layout['last_active_window'] = True else: layout['last_active_window'] = False name = 'child%d' % count count = count + 1 global_layout[name] = layout child_order = 0 for child in self.get_children(): if hasattr(child, 'describe_layout'): count = child.describe_layout(count, name, global_layout, child_order) child_order = child_order + 1 return (count) def create_layout(self, layout): """Apply settings for our layout""" raise NotImplementedError('create_layout')
#!/usr/bin/env python import os import sys from terminatorlib.config import Config from terminatorlib.util import get_config_dir class Opts(object): pass opts = Opts() opts.config = os.path.join(get_config_dir(), 'config') config = Config() config.options_set(opts) # misc options config['word_chars'] = '-A-Za-z0-9,./?%&_~' config['scrollback_lines'] = 500000 config['scroll_on_output'] = False config['use_system_font'] = False config['font'] = 'Ubuntu Mono 9' config.save()