1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
|
#!/usr/bin/python
# Terminator by Chris Jones <cmsj@tenshu.net>
# GPL v2 only
"""container.py - classes necessary to contain Terminal widgets"""
import gobject
import gtk
from factory import Factory
from config import Config
from util import dbg, err
from translation import _
from signalman import Signalman
# pylint: disable-msg=R0921
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 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 add(self, widget):
"""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"""
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)
dialog.show_all()
return(dialog)
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"""
maker = Factory()
terminals = {}
for child in self.get_offspring():
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, 'get_size'):
layout['size'] = self.get_size()
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')
# vim: set expandtab ts=4 sw=4:
|