~ubuntu-branches/ubuntu/saucy/terminator/saucy

« back to all changes in this revision

Viewing changes to terminatorlib/util.py

  • Committer: Bazaar Package Importer
  • Author(s): Nicolas Valcárcel Scerpella (Canonical)
  • Date: 2010-04-07 17:10:31 UTC
  • mfrom: (1.1.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20100407171031-35nsuj0tmbub0bj5
Tags: 0.92-0ubuntu1
* New upstream release
* Remove python-xdg from Recommends. (Closes: #567967)
* Downgrade python-gnome2 to Recommends.
* Update python-gtk2 dependency to (>= 2.14.0)
* Add python-keybinder to Recommends

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
#    Terminator.util - misc utility functions
 
3
#    Copyright (C) 2006-2010  cmsj@tenshu.net
 
4
#
 
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.
 
8
#
 
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.
 
13
#
 
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
 
18
 
 
19
>>> a = {'foo': 'bar', 'baz': 'bjonk'}
 
20
>>> b = {'foo': 'far', 'baz': 'bjonk'}
 
21
>>> dict_diff(a, b)
 
22
{'foo': 'far'}
 
23
 
 
24
"""
 
25
 
 
26
import sys
 
27
import gtk
 
28
import os
 
29
import pwd
 
30
import inspect
 
31
 
 
32
# set this to true to enable debugging output
 
33
DEBUG = False
 
34
# set this to true to additionally list filenames in debugging
 
35
DEBUGFILES = False
 
36
# list of classes to show debugging for. empty list means show all classes
 
37
DEBUGCLASSES = []
 
38
# list of methods to show debugging for. empty list means show all methods
 
39
DEBUGMETHODS = []
 
40
 
 
41
def dbg(log = ""):
 
42
    """Print a message if debugging is enabled"""
 
43
    if DEBUG:
 
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)
 
48
        try:
 
49
            self_name = names[0]
 
50
            classname = local_vars[self_name].__class__.__name__
 
51
        except IndexError:
 
52
            classname = "noclass"
 
53
        if DEBUGFILES:
 
54
            line = stackitem[2]
 
55
            filename = parent_frame.f_code.co_filename
 
56
            extra = " (%s:%s)" % (filename, line)
 
57
        else:
 
58
            extra = ""
 
59
        if DEBUGCLASSES != [] and classname not in DEBUGCLASSES:
 
60
            return
 
61
        if DEBUGMETHODS != [] and method not in DEBUGMETHODS:
 
62
            return
 
63
        print >> sys.stderr, "%s::%s: %s%s" % (classname, method, log, extra)
 
64
 
 
65
def err(log = ""):
 
66
    """Print an error message"""
 
67
    print >> sys.stderr, log
 
68
 
 
69
def gerr(message = None):
 
70
    """Display a graphical error. This should only be used for serious
 
71
    errors as it will halt execution"""
 
72
 
 
73
    dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
 
74
            gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message)
 
75
    dialog.run()
 
76
 
 
77
def has_ancestor(widget, wtype):
 
78
    """Walk up the family tree of widget to see if any ancestors are of type"""
 
79
    while widget:
 
80
        widget = widget.get_parent()
 
81
        if isinstance(widget, wtype):
 
82
            return(True)
 
83
    return(False)
 
84
 
 
85
def get_top_window(widget):
 
86
    """Return the Window instance a widget belongs to"""
 
87
    parent = widget.get_parent()
 
88
    while parent:
 
89
        widget = parent
 
90
        parent = widget.get_parent()
 
91
    return(widget)
 
92
 
 
93
def path_lookup(command):
 
94
    '''Find a command in our path'''
 
95
    if os.path.isabs(command):
 
96
        if os.path.isfile(command):
 
97
            return(command)
 
98
        else:
 
99
            return(None)
 
100
    elif command[:2] == './' and os.path.isfile(command):
 
101
        dbg('path_lookup: Relative filename %s found in cwd' % command)
 
102
        return(command)
 
103
 
 
104
    try:
 
105
        paths = os.environ['PATH'].split(':')
 
106
        if len(paths[0]) == 0: 
 
107
            raise(ValueError)
 
108
    except (ValueError, NameError):
 
109
        dbg('path_lookup: PATH not set in environment, using fallbacks')
 
110
        paths = ['/usr/local/bin', '/usr/bin', '/bin']
 
111
 
 
112
    dbg('path_lookup: Using %d paths: %s' % (len(paths), paths))
 
113
 
 
114
    for path in paths:
 
115
        target = os.path.join(path, command)
 
116
        if os.path.isfile(target):
 
117
            dbg('path_lookup: found %s' % target)
 
118
            return(target)
 
119
 
 
120
    dbg('path_lookup: Unable to locate %s' % command)
 
121
 
 
122
def shell_lookup():
 
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']
 
126
 
 
127
    for shell in shells:
 
128
        if shell is None:
 
129
            continue
 
130
        elif os.path.isfile(shell):
 
131
            return(shell)
 
132
        else:
 
133
            rshell = path_lookup(shell)
 
134
            if rshell is not None:
 
135
                dbg('shell_lookup: Found %s at %s' % (shell, rshell))
 
136
                return(rshell)
 
137
    dbg('shell_lookup: Unable to locate a shell')
 
138
 
 
139
def widget_pixbuf(widget, maxsize=None):
 
140
    """Generate a pixbuf of a widget"""
 
141
    if gtk.gtk_version < (2, 14):
 
142
        return(None)
 
143
 
 
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,
 
148
            height)
 
149
 
 
150
    longest = max(width, height)
 
151
 
 
152
    if maxsize is not None:
 
153
        factor = float(maxsize) / float(longest)
 
154
 
 
155
    if not maxsize or (width * factor) > width or (height * factor) > height:
 
156
        factor = 1
 
157
 
 
158
    scaledpixbuf = pixbuf.scale_simple(int(width * factor), int(height * factor), gtk.gdk.INTERP_BILINEAR)
 
159
 
 
160
    return(scaledpixbuf)
 
161
 
 
162
def get_config_dir():
 
163
    """Expand all the messy nonsense for finding where ~/.config/terminator
 
164
    really is"""
 
165
    try:
 
166
        configdir = os.environ['XDG_CONFIG_HOME']
 
167
    except KeyError:
 
168
        configdir = os.path.join(os.path.expanduser('~'), '.config')
 
169
 
 
170
    return(os.path.join(configdir, 'terminator'))
 
171
 
 
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"""
 
176
 
 
177
    result = {}
 
178
 
 
179
    for key in reference:
 
180
        if reference[key] != working[key]:
 
181
            result[key] = working[key]
 
182
 
 
183
    return(result)
 
184
 
 
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':
 
190
        edge = allocation.x
 
191
    elif direction == 'up':
 
192
        edge = allocation.y
 
193
    elif direction == 'right':
 
194
        edge = allocation.x + allocation.width
 
195
    elif direction == 'down':
 
196
        edge = allocation.y + allocation.height
 
197
    else:
 
198
        raise ValueError('unknown direction %s' % direction)
 
199
    
 
200
    return(edge)
 
201
 
 
202
def get_nav_possible(edge, allocation, direction):
 
203
    """Check if the supplied allocation is in the right direction of the
 
204
    supplied edge"""
 
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)
 
213
    else:
 
214
        raise ValueError('Unknown direction: %s' % direction)
 
215
 
 
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)
 
227
    else:
 
228
        raise ValueError('Unknown direction: %s' % direction)
 
229
 
 
230
def get_nav_tiebreak(direction, cursor_x, cursor_y, rect):
 
231
    """We have multiple candidate terminals. Pick the closest by cursor
 
232
    position"""
 
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))
 
237
    else:
 
238
        raise ValueError('Unknown direction: %s' % direction)
 
239
 
 
240
def enumerate_descendants(parent):
 
241
    """Walk all our children and build up a list of containers and
 
242
    terminals"""
 
243
    # FIXME: Does having to import this here mean we should move this function
 
244
    # back to Container?
 
245
    from factory import Factory
 
246
 
 
247
    containerstmp = []
 
248
    containers = []
 
249
    terminals = []
 
250
    maker = Factory()
 
251
 
 
252
    if parent is None:
 
253
        err('no parent widget specified')
 
254
        return
 
255
 
 
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)
 
261
 
 
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)
 
270
 
 
271
    dbg('%d containers and %d terminals fall beneath %s' % (len(containers), 
 
272
        len(terminals), parent))
 
273
    return(containers, terminals)
 
274