2
# Terminator.util - misc utility functions
3
# Copyright (C) 2006-2010 cmsj@tenshu.net
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation, version 2 only.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Terminator.util - misc utility functions
19
>>> a = {'foo': 'bar', 'baz': 'bjonk'}
20
>>> b = {'foo': 'far', 'baz': 'bjonk'}
32
# set this to true to enable debugging output
34
# set this to true to additionally list filenames in debugging
36
# list of classes to show debugging for. empty list means show all classes
38
# list of methods to show debugging for. empty list means show all methods
42
"""Print a message if debugging is enabled"""
44
stackitem = inspect.stack()[1]
45
parent_frame = stackitem[0]
46
method = parent_frame.f_code.co_name
47
names, varargs, keywords, local_vars = inspect.getargvalues(parent_frame)
50
classname = local_vars[self_name].__class__.__name__
55
filename = parent_frame.f_code.co_filename
56
extra = " (%s:%s)" % (filename, line)
59
if DEBUGCLASSES != [] and classname not in DEBUGCLASSES:
61
if DEBUGMETHODS != [] and method not in DEBUGMETHODS:
63
print >> sys.stderr, "%s::%s: %s%s" % (classname, method, log, extra)
66
"""Print an error message"""
67
print >> sys.stderr, log
69
def gerr(message = None):
70
"""Display a graphical error. This should only be used for serious
71
errors as it will halt execution"""
73
dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
74
gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message)
77
def has_ancestor(widget, wtype):
78
"""Walk up the family tree of widget to see if any ancestors are of type"""
80
widget = widget.get_parent()
81
if isinstance(widget, wtype):
85
def get_top_window(widget):
86
"""Return the Window instance a widget belongs to"""
87
parent = widget.get_parent()
90
parent = widget.get_parent()
93
def path_lookup(command):
94
'''Find a command in our path'''
95
if os.path.isabs(command):
96
if os.path.isfile(command):
100
elif command[:2] == './' and os.path.isfile(command):
101
dbg('path_lookup: Relative filename %s found in cwd' % command)
105
paths = os.environ['PATH'].split(':')
106
if len(paths[0]) == 0:
108
except (ValueError, NameError):
109
dbg('path_lookup: PATH not set in environment, using fallbacks')
110
paths = ['/usr/local/bin', '/usr/bin', '/bin']
112
dbg('path_lookup: Using %d paths: %s' % (len(paths), paths))
115
target = os.path.join(path, command)
116
if os.path.isfile(target):
117
dbg('path_lookup: found %s' % target)
120
dbg('path_lookup: Unable to locate %s' % command)
123
"""Find an appropriate shell for the user"""
124
shells = [os.getenv('SHELL'), pwd.getpwuid(os.getuid())[6], 'bash',
125
'zsh', 'tcsh', 'ksh', 'csh', 'sh']
130
elif os.path.isfile(shell):
133
rshell = path_lookup(shell)
134
if rshell is not None:
135
dbg('shell_lookup: Found %s at %s' % (shell, rshell))
137
dbg('shell_lookup: Unable to locate a shell')
139
def widget_pixbuf(widget, maxsize=None):
140
"""Generate a pixbuf of a widget"""
141
if gtk.gtk_version < (2, 14):
144
pixmap = widget.get_snapshot()
145
(width, height) = pixmap.get_size()
146
pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height)
147
pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, width,
150
longest = max(width, height)
152
if maxsize is not None:
153
factor = float(maxsize) / float(longest)
155
if not maxsize or (width * factor) > width or (height * factor) > height:
158
scaledpixbuf = pixbuf.scale_simple(int(width * factor), int(height * factor), gtk.gdk.INTERP_BILINEAR)
162
def get_config_dir():
163
"""Expand all the messy nonsense for finding where ~/.config/terminator
166
configdir = os.environ['XDG_CONFIG_HOME']
168
configdir = os.path.join(os.path.expanduser('~'), '.config')
170
return(os.path.join(configdir, 'terminator'))
172
def dict_diff(reference, working):
173
"""Examine the values in the supplied working set and return a new dict
174
that only contains those values which are different from those in the
175
reference dictionary"""
179
for key in reference:
180
if reference[key] != working[key]:
181
result[key] = working[key]
185
# Helper functions for directional navigation
186
def get_edge(allocation, direction):
187
"""Return the edge of the supplied allocation that we will care about for
188
directional navigation"""
189
if direction == 'left':
191
elif direction == 'up':
193
elif direction == 'right':
194
edge = allocation.x + allocation.width
195
elif direction == 'down':
196
edge = allocation.y + allocation.height
198
raise ValueError('unknown direction %s' % direction)
202
def get_nav_possible(edge, allocation, direction):
203
"""Check if the supplied allocation is in the right direction of the
205
if direction == 'left':
206
return((allocation.x + allocation.width) < edge)
207
elif direction == 'right':
208
return(allocation.x > edge)
209
elif direction == 'up':
210
return((allocation.y + allocation.height) < edge)
211
elif direction == 'down':
212
return(allocation.y > edge)
214
raise ValueError('Unknown direction: %s' % direction)
216
def get_nav_offset(edge, allocation, direction):
217
"""Work out how far edge is from a particular point on the allocation
218
rectangle, in the given direction"""
219
if direction == 'left':
220
return(edge - (allocation.x + allocation.width))
221
elif direction == 'right':
222
return(edge + allocation.x)
223
elif direction == 'up':
224
return(edge - (allocation.y - allocation.height))
225
elif direction == 'down':
226
return(edge + allocation.y)
228
raise ValueError('Unknown direction: %s' % direction)
230
def get_nav_tiebreak(direction, cursor_x, cursor_y, rect):
231
"""We have multiple candidate terminals. Pick the closest by cursor
233
if direction in ['left', 'right']:
234
return(cursor_y >= rect.y and cursor_y <= (rect.y + rect.height))
235
elif direction in ['up', 'down']:
236
return(cursor_x >= rect.x and cursor_x <= (rect.x + rect.width))
238
raise ValueError('Unknown direction: %s' % direction)
240
def enumerate_descendants(parent):
241
"""Walk all our children and build up a list of containers and
243
# FIXME: Does having to import this here mean we should move this function
245
from factory import Factory
253
err('no parent widget specified')
256
for descendant in parent.get_children():
257
if maker.isinstance(descendant, 'Container'):
258
containerstmp.append(descendant)
259
elif maker.isinstance(descendant, 'Terminal'):
260
terminals.append(descendant)
262
while len(containerstmp) > 0:
263
child = containerstmp.pop()
264
for descendant in child.get_children():
265
if maker.isinstance(descendant, 'Container'):
266
containerstmp.append(descendant)
267
elif maker.isinstance(descendant, 'Terminal'):
268
terminals.append(descendant)
269
containers.append(child)
271
dbg('%d containers and %d terminals fall beneath %s' % (len(containers),
272
len(terminals), parent))
273
return(containers, terminals)