예제 #1
0
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)
        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)
        secondary = Gtk.Label(
            label=_('This %s has several terminals open. Closing \
the %s will also close all terminals within it.') % (reqtype, reqtype))
        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')
예제 #2
0
class Container(object):
    """Base class for Terminator Containers"""

    terminator = None
    immutable = None
    children = None
    config = None
    config_section = None
    signals = None
    signalman = None
    # All containers should have caption too.
    # They may pass it to their children
    caption = ''

    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"""
        widget.zombie = True
        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()
        #self.terminator.layout_changed(self)
        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.set_dirty()

        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 relayout(self, layout):
        """relayout children from config"""
        maker = Factory()
        mytype = maker.type(self)
        wujatam = {}
        if not mytype:
            err('unable to detemine own type. %s' % self)
            return

        if self.config_section and layout.has_key(self.config_section):
            # just relayout, likely after config edit
            wujatam = layout[self.config_section]
        else:
            err('unable to get own config for relayout. %s' % self)
            return

        paned = ''
        if mytype == 'Window':
            self.set_custom_title(wujatam['title'])
            self.set_custom_fixed(wujatam['titlefixed'])
        elif mytype == 'Notebook':
            pass  # Notebook now has own relayout
        elif mytype == 'HPaned' or mytype == 'VPaned':
            self.caption = wujatam['caption']
            if not self.caption:
                self.caption = 'NC'  # must have
            paned = self.caption
        else:
            raise NotImplementedError

        for child in self.get_children():
            if paned:
                child.inPaned = True
                child.caption = paned  # prefseditor needs it
            if hasattr(child, 'relayout'):
                child.relayout(layout)

    ## FIXME base class is not a good place to know about specific
    ## all children behavior. its good for our consecutive keys of parent
    ## containers though. So it'll stay here (ohir).
    def describe_layout(self, parent, global_layout):
        """Describe our current layout"""
        layout = {}
        tit = 'NO_title'
        capt = 'NO_caption'
        maker = Factory()
        mytype = maker.type(self)
        if not mytype:
            err('unable to detemine own type. %s' % self)
            return ({})

        paned = False
        children = self.get_children()
        if mytype == 'VPaned' or mytype == 'HPaned':
            for child in children:
                if hasattr(child, 'zombie') and child.zombie:
                    dbg('~ZOMBIE %s spotted in %s' %
                        (child.config_section, self.config_section))
                    return False
            paned = True

        name = "%s%s" % (self.config.name_next(), mytype[0].lower())
        self.config_section = name
        dbg('~DESCRIBE  [%s:%s of %s]' % (mytype, name, parent))

        if mytype == 'Window':
            layout['title'] = self.get_custom_title()
            layout['titlefixed'] = self.get_custom_fixed()
            if self.uuid == self.terminator.last_active_window:
                layout['last_active_window'] = True
        elif hasattr(self, 'title'):
            layout['title'] = self.title

        if hasattr(self, 'caption'):
            if paned and not self.caption:
                self.caption = 'NC'
            layout['caption'] = self.caption

        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, 'inPaned'):
            layout['_inPaned'] = self.inPaned

        layout['type'] = mytype
        layout['parent'] = parent
        global_layout[name] = layout

        for child in children:
            if not child:
                # This kicks spouriously with pane drags.
                # dbg('~DRAG: %s lost child in: %s' % (self.config_section, children))
                continue
            if paned:
                child.caption = self.caption
            child.inPaned = paned
            child.describe_layout(name, global_layout)

        return (True)

    def create_layout(self, layout):
        """Apply settings for our layout"""
        raise NotImplementedError('create_layout')
예제 #3
0
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')
예제 #4
0
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)
        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.RESPONSE_ACCEPT

        dialog = gtk.Dialog(_("Close?"), window, gtk.DIALOG_MODAL)
        dialog.set_has_separator(False)
        dialog.set_resizable(False)

        dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT)
        c_all = dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_ACCEPT)
        c_all.get_children()[0].get_children()[0].get_children()[1].set_label(_("Close _Terminals"))

        primary = gtk.Label(_("<big><b>Close multiple terminals?</b></big>"))
        primary.set_use_markup(True)
        primary.set_alignment(0, 0.5)
        secondary = gtk.Label(
            _(
                "This %s has several terminals open. Closing \
the %s will also close all terminals within it."
            )
            % (reqtype, reqtype)
        )
        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.ICON_SIZE_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)

        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")