def save(self): """Save the config to a file""" dbg('ConfigBase::save: saving config') parser = ConfigObj() parser.indent_type = ' ' for section_name in ['global_config', 'keybindings']: dbg('ConfigBase::save: Processing section: %s' % section_name) section = getattr(self, section_name) parser[section_name] = dict_diff(DEFAULTS[section_name], section) parser['profiles'] = {} for profile in self.profiles: dbg('ConfigBase::save: Processing profile: %s' % profile) parser['profiles'][profile] = dict_diff( DEFAULTS['profiles']['default'], self.profiles[profile]) parser['layouts'] = {} for layout in self.layouts: dbg('ConfigBase::save: Processing layout: %s' % layout) parser['layouts'][layout] = self.layouts[layout] parser['plugins'] = {} for plugin in self.plugins: dbg('ConfigBase::save: Processing plugin: %s' % plugin) parser['plugins'][plugin] = self.plugins[plugin] config_dir = get_config_dir() if not os.path.isdir(config_dir): os.makedirs(config_dir) try: parser.write(open(self.command_line_options.config, 'w')) except Exception, ex: err('ConfigBase::save: Unable to save config: %s' % ex)
def move_tab(self, widget, direction): """Handle a keyboard shortcut for moving tab positions""" maker = Factory() notebook = self.get_child() if not maker.isinstance(notebook, 'Notebook'): dbg('not in a notebook, refusing to move tab %s' % direction) return dbg('moving tab %s' % direction) numpages = notebook.get_n_pages() page = notebook.get_current_page() child = notebook.get_nth_page(page) if direction == 'left': if page == 0: page = numpages else: page = page - 1 elif direction == 'right': if page == numpages - 1: page = 0 else: page = page + 1 else: err('unknown direction: %s' % direction) return notebook.reorder_child(child, page)
def create_layout(self, layout): """Apply any config items from our layout""" if not layout.has_key("children"): err("layout describes no children: %s" % layout) return children = layout["children"] if len(children) != 1: # We're a Window, we can only have one child err("incorrect number of children for Window: %s" % layout) return child = children[children.keys()[0]] terminal = self.get_children()[0] dbg("Making a child of type: %s" % child["type"]) if child["type"] == "VPaned": self.split_axis(terminal, True) elif child["type"] == "HPaned": self.split_axis(terminal, False) elif child["type"] == "Notebook": self.tab_new() i = 2 while i < len(child["children"]): self.tab_new() i = i + 1 elif child["type"] == "Terminal": pass else: err("unknown child type: %s" % child["type"]) return self.get_children()[0].create_layout(child)
def tab_change(self, widget, num=None): """Change to a specific tab""" if num is None: err('must specify a tab to change to') maker = Factory() child = self.get_child() if not maker.isinstance(child, 'Notebook'): dbg('child is not a notebook, nothing to change to') return if num == -1: # Go to the next tab cur = child.get_current_page() pages = child.get_n_pages() if cur == pages - 1: num = 0 else: num = cur + 1 elif num == -2: # Go to the previous tab cur = child.get_current_page() if cur > 0: num = cur - 1 else: num = child.get_n_pages() - 1 child.set_current_page(num) # Work around strange bug in gtk-2.12.11 and pygtk-2.12.1 # Without it, the selection changes, but the displayed page doesn't # change child.set_current_page(child.get_current_page())
def input(self, data): if "type" in data: function_name = "process_" + data["type"] dbg("got {}".format(function_name)) for plugin in self.bot_plugins: plugin.register_jobs() plugin.do(function_name, data)
def on_window_state_changed(self, window, event): """Handle the state of the window changing""" self.isfullscreen = bool(event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN) self.ismaximised = bool(event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED) dbg("Window::on_window_state_changed: fullscreen=%s, maximised=%s" % (self.isfullscreen, self.ismaximised)) return False
def save(self): """Save the config to a file""" dbg("ConfigBase::save: saving config") parser = ConfigObj() parser.indent_type = " " for section_name in ["global_config", "keybindings"]: dbg("ConfigBase::save: Processing section: %s" % section_name) section = getattr(self, section_name) parser[section_name] = dict_diff(DEFAULTS[section_name], section) parser["profiles"] = {} for profile in self.profiles: dbg("ConfigBase::save: Processing profile: %s" % profile) parser["profiles"][profile] = dict_diff(DEFAULTS["profiles"]["default"], self.profiles[profile]) parser["layouts"] = {} for layout in self.layouts: dbg("ConfigBase::save: Processing layout: %s" % layout) parser["layouts"][layout] = self.layouts[layout] parser["plugins"] = {} for plugin in self.plugins: dbg("ConfigBase::save: Processing plugin: %s" % plugin) parser["plugins"][plugin] = self.plugins[plugin] config_dir = get_config_dir() if not os.path.isdir(config_dir): os.makedirs(config_dir) try: parser.write(open(self.command_line_options.config, "w")) except Exception, ex: err("ConfigBase::save: Unable to save config: %s" % ex)
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) terminal.connect('ungroup-all', self.ungroup_all)
def get_pid_cwd(): """Determine an appropriate cwd function for the OS we are running on""" func = lambda pid: None system = platform.system() if system == 'Linux': dbg('Using Linux get_pid_cwd') func = linux_get_pid_cwd elif system == 'FreeBSD': try: import freebsd func = freebsd.get_process_cwd dbg('Using FreeBSD get_pid_cwd') except (OSError, NotImplementedError, ImportError): dbg('FreeBSD version too old for get_pid_cwd') elif system == 'SunOS': dbg('Using SunOS get_pid_cwd') func = sunos_get_pid_cwd elif psutil_avail: func = psutil_cwd else: dbg('Unable to determine a get_pid_cwd for OS: %s' % system) return(func)
def create_layout(self, layout): """Apply any config items from our layout""" if not layout.has_key('children'): err('layout describes no children: %s' % layout) return children = layout['children'] if len(children) != 1: # We're a Window, we can only have one child err('incorrect number of children for Window: %s' % layout) return child = children[children.keys()[0]] terminal = self.get_children()[0] dbg('Making a child of type: %s' % child['type']) if child['type'] == 'VPaned': self.split_axis(terminal, True) elif child['type'] == 'HPaned': self.split_axis(terminal, False) elif child['type'] == 'Notebook': self.tab_new() i = 2 while i < len(child['children']): self.tab_new() i = i + 1 elif child['type'] == 'Terminal': pass else: err('unknown child type: %s' % child['type']) return self.get_children()[0].create_layout(child)
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, event)
def input(self, data): data = Message(data) function_name = "process_" + data.type.lower() dbg("got {}".format(function_name)) for plugin in self.bot_plugins: plugin.register_jobs() plugin.do(function_name, data)
def create_layout(self, layout): """Apply layout configuration""" if not layout.has_key('children'): err('layout specifies no children: %s' % layout) return children = layout['children'] if len(children) != 2: # Paned widgets can only have two children err('incorrect number of children for Paned: %s' % layout) return keys = [] # FIXME: This seems kinda ugly. All we want here is to know the order # of children based on child['order'] try: child_order_map = {} for child in children: key = children[child]['order'] child_order_map[key] = child map_keys = child_order_map.keys() map_keys.sort() for map_key in map_keys: keys.append(child_order_map[map_key]) except KeyError: # We've failed to figure out the order. At least give the terminals # in the wrong order keys = children.keys() num = 0 for child_key in keys: child = children[child_key] dbg('Making a child of type: %s' % child['type']) if child['type'] == 'Terminal': pass elif child['type'] == 'VPaned': if num == 0: terminal = self.get_child1() else: terminal = self.get_child2() self.split_axis(terminal, True) elif child['type'] == 'HPaned': if num == 0: terminal = self.get_child1() else: terminal = self.get_child2() self.split_axis(terminal, False) else: err('unknown child type: %s' % child['type']) num = num + 1 self.get_child1().create_layout(children[keys[0]]) self.get_child2().create_layout(children[keys[1]]) # Set the position with ratio. For some reason more reliable than by pos. if layout.has_key('ratio'): self.ratio = float(layout['ratio']) self.set_position_by_ratio()
def create_layout(self, layout): """Apply layout configuration""" def child_compare(a, b): order_a = children[a]['order'] order_b = children[b]['order'] if (order_a == order_b): return 0 if (order_a < order_b): return -1 if (order_a > order_b): return 1 if not layout.has_key('children'): err('layout specifies no children: %s' % layout) return children = layout['children'] if len(children) <= 1: #Notebooks should have two or more children err('incorrect number of children for Notebook: %s' % layout) return num = 0 keys = children.keys() keys.sort(child_compare) for child_key in keys: child = children[child_key] dbg('Making a child of type: %s' % child['type']) if child['type'] == 'Terminal': pass elif child['type'] == 'VPaned': page = self.get_nth_page(num) self.split_axis(page, True) elif child['type'] == 'HPaned': page = self.get_nth_page(num) self.split_axis(page, False) num = num + 1 num = 0 for child_key in keys: page = self.get_nth_page(num) if not page: # This page does not yet exist, so make it self.newtab(children[child_key]) page = self.get_nth_page(num) if layout.has_key('labels'): labeltext = layout['labels'][num] if labeltext and labeltext != "None": label = self.get_tab_label(page) label.set_custom_label(labeltext) page.create_layout(children[child_key]) num = num + 1 if layout.has_key('active_page'): self.set_current_page(int(layout['active_page'])) else: self.set_current_page(0)
def with_proxy(func): """Decorator function to connect to the session dbus bus""" dbg('dbus client call: %s' % func.func_name) def _exec(*args, **argd): bus = dbus.SessionBus() proxy = bus.get_object(BUS_NAME, BUS_PATH) func(proxy, *args, **argd) return _exec
def new_tab_cmdline(self, options=dbus.Dictionary()): """Create a new tab""" dbg('dbus method called: new_tab with parameters %s'%(options)) oldopts = self.terminator.config.options_get() oldopts.__dict__ = options self.terminator.config.options_set(oldopts) window = self.terminator.get_windows()[0] window.tab_new()
def remove_widget(self, widget): """Remove all signal handlers for a widget""" if not self.cnxids.has_key(widget): dbg("%s not registered" % widget) return signals = self.cnxids[widget].keys() for signal in signals: self.remove_signal(widget, signal)
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 relay(self, bot, relay_ins): for data in relay_ins: if "type" in data: function_name = "relay_" + data["type"] dbg("got {}".format(function_name)) for plugin in self.bot_plugins: plugin.register_jobs() plugin.do(function_name, bot, data)
def new_window(self, options=dbus.Dictionary()): """Create a new Window""" dbg('dbus method called: new_window with parameters %s'%(options)) oldopts = self.terminator.config.options_get() oldopts.__dict__ = options self.terminator.config.options_set(oldopts) self.terminator.create_layout(oldopts.layout) self.terminator.layout_done()
def new_window_cmdline(self, options=dbus.Dictionary()): """Create a new Window""" dbg('dbus method called: new_window with parameters %s'%(options)) oldopts = self.terminator.config.options_get() oldopts.__dict__ = options self.terminator.config.options_set(oldopts) self.terminator.create_layout(oldopts.layout) self.terminator.layout_done()
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 save_state(self): if self.doing_layout or self.doing_quit: dbg('~DISCARD SAVE_STATE at layout or quit phase.') return if self._layout_changed: self.save_yourself() # reread our state then force save else: self.config.save() # let config decide
def new_tab(self, options=dbus.Dictionary()): """Create a new tab""" dbg('dbus method called: new_tab with parameters %s'%(options)) oldopts = self.terminator.config.options_get() oldopts.__dict__ = options self.terminator.config.options_set(oldopts) window = self.terminator.get_windows()[0] window.tab_new()
def find_terminal_by_pane_id(self, pane_id): """Search our terminals for one matching the supplied pane_id""" dbg('searching self.terminals for: %s' % pane_id) for terminal in self.terminals: dbg('checking: %s (%s)' % (terminal.pane_id, terminal)) if terminal.pane_id == pane_id: return terminal return None
def remove_widget(self, widget): """Remove all signal handlers for a widget""" if not self.cnxids.has_key(widget): dbg('%s not registered' % widget) return signals = self.cnxids[widget].keys() for signal in signals: self.remove_signal(widget, signal)
def split_axis(self, widget, vertical=True, cwd=None, sibling=None, widgetfirst=True): """Split the axis of a terminal inside us""" dbg('called for widget: %s' % widget) order = None page_num = self.page_num(widget) if page_num == -1: err('Notebook::split_axis: %s not found in Notebook' % widget) return label = self.get_tab_label(widget) self.remove(widget) maker = Factory() if vertical: container = maker.make('vpaned') else: container = maker.make('hpaned') self.get_toplevel().set_pos_by_ratio = True if not sibling: sibling = maker.make('terminal') sibling.set_cwd(cwd) if self.config['always_split_with_profile']: sibling.force_set_profile(None, widget.get_profile()) sibling.spawn_child() if widget.group and self.config['split_to_group']: sibling.set_group(None, widget.group) elif self.config['always_split_with_profile']: sibling.force_set_profile(None, widget.get_profile()) self.insert_page(container, None, page_num) self.child_set_property(container, 'tab-expand', True) self.child_set_property(container, 'tab-fill', True) self.set_tab_reorderable(container, True) self.set_tab_label(container, label) self.show_all() order = [widget, sibling] if widgetfirst is False: order.reverse() for terminal in order: container.add(terminal) self.set_current_page(page_num) self.show_all() while Gtk.events_pending(): Gtk.main_iteration_do(False) self.get_toplevel().set_pos_by_ratio = False GObject.idle_add(terminal.ensure_visible_and_focussed)
def create_layout(self, layout): """Apply layout configuration""" if not layout.has_key('children'): err('layout specifies no children: %s' % layout) return children = layout['children'] if len(children) != 2: # Paned widgets can only have two children err('incorrect number of children for Paned: %s' % layout) return keys = [] # FIXME: This seems kinda ugly. All we want here is to know the order # of children based on child['order'] try: child_order_map = {} for child in children: key = children[child]['order'] child_order_map[key] = child map_keys = child_order_map.keys() map_keys.sort() for map_key in map_keys: keys.append(child_order_map[map_key]) except KeyError: # We've failed to figure out the order. At least give the terminals # in the wrong order keys = children.keys() num = 0 for child_key in keys: child = children[child_key] dbg('Making a child of type: %s' % child['type']) if child['type'] == 'Terminal': pass elif child['type'] == 'VPaned': if num == 0: terminal = self.get_child1() else: terminal = self.get_child2() self.split_axis(terminal, True) elif child['type'] == 'HPaned': if num == 0: terminal = self.get_child1() else: terminal = self.get_child2() self.split_axis(terminal, False) else: err('unknown child type: %s' % child['type']) num = num + 1 self.get_child1().create_layout(children[keys[0]]) self.get_child2().create_layout(children[keys[1]]) # Store the position for later if layout['position']: self.position = int(layout['position'])
def on_destroy_event(self, widget, data=None): """Handle window destruction""" dbg('destroying self') for terminal in self.get_visible_terminals(): terminal.close() self.cnxids.remove_all() self.terminator.deregister_window(self) self.destroy() del (self)
def connect_gsetting_callbacks(self): """Get system settings and create callbacks for changes""" dbg("GSetting connects for system changes") # Have to preserve these to self, or callbacks don't happen self.gsettings_interface=Gio.Settings.new('org.gnome.desktop.interface') self.gsettings_interface.connect("changed::font-name", self.on_gsettings_change_event) self.gsettings_interface.connect("changed::monospace-font-name", self.on_gsettings_change_event) self.gsettings_wm=Gio.Settings.new('org.gnome.desktop.wm.preferences') self.gsettings_wm.connect("changed::focus-mode", self.on_gsettings_change_event)
def get_plugins_by_capability(self, capability): """Return a list of plugins with a particular capability""" result = [] dbg('PluginRegistry::get_plugins_by_capability: searching %d plugins \ for %s' % (len(self.instances), capability)) for plugin in self.instances: if capability in self.instances[plugin].capabilities: result.append(self.instances[plugin]) return result
def get_desired_visibility(self): """Returns True if the titlebar is supposed to be visible. False if not""" if self.editing() == True or self.terminal.group: dbg('implicit desired visibility') return(True) else: dbg('configured visibility: %s' % self.config['show_titlebar']) return(self.config['show_titlebar'])
def page_num_descendant(self, widget): """Find the tabnum of the tab containing a widget at any level""" tabnum = self.page_num(widget) dbg("widget is direct child if not equal -1 - tabnum: %d" % tabnum) while tabnum == -1 and widget.get_parent(): widget = widget.get_parent() tabnum = self.page_num(widget) dbg("found tabnum containing widget: %d" % tabnum) return tabnum
def commit_layout_change(self, who, key, value): """Update, set dict as dirty""" if not self._curlayoutname: err('LAYOUT NOT SET') return dbg('~CHANGE for %s. SET %s to %s [layout:%s]' % (who, key, value, self._curlayoutname)) self.layouts[self._curlayoutname][who][key] = value self._dirty = True
def get_desired_visibility(self): """Returns True if the titlebar is supposed to be visible. False if not""" if self.editing() == True or self.terminal.group: dbg('implicit desired visibility') return (True) else: dbg('configured visibility: %s' % self.config['show_titlebar']) return (self.config['show_titlebar'])
def hoover(self): """Check that we still have a reason to exist""" if len(self.children) == 1: dbg('Paned::hoover: We only have one child, die') parent = self.get_parent() child = self.children[0] self.remove(child) parent.replace(self, child) del(self)
def on_destroy_event(self, widget, data=None): """Handle window destruction""" dbg('destroying self') for terminal in self.get_visible_terminals(): terminal.close() self.cnxids.remove_all() self.terminator.deregister_window(self) self.destroy() del(self)
def __init__(self, borgtype=None): """Class initialiser. Overwrite our class dictionary with the shared state. This makes us identical to every other instance of this class type.""" if borgtype is None: raise TypeError('Borg::__init__: You must pass a borgtype') if borgtype not in self.__shared_state: dbg('Borg::__init__: Preparing borg state for %s' % borgtype) self.__shared_state[borgtype] = {} self.__dict__ = self.__shared_state[borgtype]
def make(self, product, **kwargs): """Make the requested product""" try: func = getattr(self, 'make_%s' % product.lower()) except AttributeError: err('Factory::make: requested object does not exist: %s' % product) return (None) dbg('Factory::make: created a %s' % product) return (func(**kwargs))
def on_delete_event(self, window, event, data=None): """Handle a window close request""" maker = Factory() if maker.isinstance(self.get_child(), 'Terminal'): dbg('Window::on_delete_event: Only one child, closing is fine') return(False) elif maker.isinstance(self.get_child(), 'Container'): return(self.confirm_close(window, _('window'))) else: dbg('unknown child: %s' % self.get_child())
def ungroup_tab(self, widget): """Ungroup all terminals in the current tab""" maker = Factory() notebook = self.get_child() if not maker.isinstance(notebook, 'Notebook'): dbg('note in a notebook, refusing to ungroup tab') return self.set_groups(None, self.get_visible_terminals())
def on_gsettings_change_event(self, settings, key): """Handle a gsetting change event""" dbg('GSetting change event received. Invalidating caches') self.system_focus = None self.system_font = None self.system_mono_font = None # Need to trigger a reconfigure to change active terminals immediately if "Terminator" not in globals(): from terminator import Terminator Terminator().reconfigure()
def __init__(self, borgtype=None): """Class initialiser. Overwrite our class dictionary with the shared state. This makes us identical to every other instance of this class type.""" if borgtype is None: raise TypeError("Borg::__init__: You must pass a borgtype") if not self.__shared_state.has_key(borgtype): dbg("Borg::__init__: Preparing borg state for %s" % borgtype) self.__shared_state[borgtype] = {} self.__dict__ = self.__shared_state[borgtype]
def on_window_state_changed(self, window, event): """Handle the state of the window changing""" self.isfullscreen = bool(event.new_window_state & Gdk.WindowState.FULLSCREEN) self.ismaximised = bool(event.new_window_state & Gdk.WindowState.MAXIMIZED) dbg('Window::on_window_state_changed: fullscreen=%s, maximised=%s' \ % (self.isfullscreen, self.ismaximised)) return (False)
def die(self, *args): """Die at the hands of the session manager""" dbg('session manager asked us to die') # FIXME: Test this self.config.save() self.config.set_nosave(True) for w in self.windows: w.close() # last w will call Gtk.main_quit() return True
def update_visibility(self): """Make the titlebar be visible or not""" if not self.get_desired_visibility(): dbg('hiding titlebar') self.hide() self.label.hide() else: dbg('showing titlebar') self.show() self.label.show()
def detect_file_type(fn): if ftdc.is_ftdc_file_or_dir(fn): return 'ftdc' with open(fn) as f: for _ in range(10): try: json.loads(f.next()) return 'json' except Exception as e: util.dbg(e) return 'text'
def get_child_metadata(self, widget): """Fetch the relevant metadata for a widget which we'd need to recreate it when it's readded""" metadata = {} metadata['tabnum'] = self.page_num(widget) label = self.get_tab_label(widget) if not label: dbg('unable to find label for widget: %s' % widget) else: metadata['label'] = label.get_label() return metadata
def create_layout(self, layout): """Apply layout configuration""" def child_compare(a, b): order_a = children[a]['order'] order_b = children[b]['order'] if (order_a == order_b): return 0 if (order_a < order_b): return -1 if (order_a > order_b): return 1 if not layout.has_key('children'): err('layout specifies no children: %s' % layout) return children = layout['children'] if len(children) <= 1: #Notebooks should have two or more children err('incorrect number of children for Notebook: %s' % layout) return num = 0 keys = children.keys() keys.sort(child_compare) for child_key in keys: child = children[child_key] dbg('Making a child of type: %s' % child['type']) if child['type'] == 'Terminal': pass elif child['type'] == 'VPaned': page = self.get_nth_page(num) self.split_axis(page, True) elif child['type'] == 'HPaned': page = self.get_nth_page(num) self.split_axis(page, False) num = num + 1 num = 0 for child_key in keys: page = self.get_nth_page(num) if not page: # This page does not yet exist, so make it self.newtab(children[child_key]) page = self.get_nth_page(num) if layout.has_key('labels'): labeltext = layout['labels'][num] if labeltext and labeltext != "None": label = self.get_tab_label(page) label.set_custom_label(labeltext) page.create_layout(children[child_key]) num = num + 1
def set_real_transparency(self, value=True): """Enable RGBA if supported on the current screen""" if self.is_composited() == False: value = False screen = self.get_screen() if value: dbg('setting rgba visual') visual = screen.get_rgba_visual() if visual: self.set_visual(visual)
def launch_layout(self): """Launch the selected layout as new instance""" dbg('We have takeoff!') selection = self.layouttreeview.get_selection() (listmodel, rowiter) = selection.get_selected() if not rowiter: # Something is wrong, just jump to the first item in the list selection.select_iter(self.layouttreestore.get_iter_first()) (listmodel, rowiter) = selection.get_selected() layout = listmodel.get_value(rowiter, 0) dbg('Clicked for %s' % layout) spawn_new_terminator(self.terminator.origcwd, ['-u', '-l', layout])