~ubuntu-branches/ubuntu/precise/terminator/precise

« back to all changes in this revision

Viewing changes to terminatorlib/config.py

  • Committer: Bazaar Package Importer
  • Author(s): Nicolas Valcárcel
  • Date: 2008-07-08 07:24:49 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20080708072449-n7ey6xc46bo9lo2x
Tags: 0.9-1
New upstream release. (Closes: #489858)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
#    TerminatorConfig - layered config classes
 
3
#    Copyright (C) 2006-2008  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
 
 
18
"""TerminatorConfig by Chris Jones <cmsj@tenshu.net>
 
19
 
 
20
The config scheme works in layers, with defaults at the base,
 
21
and a simple/flexible class which can be placed over the top
 
22
in multiple layers. This was written for Terminator, but
 
23
could be used generically. Its original use is to guarantee
 
24
default values for any config item, while allowing them to be
 
25
overridden by at least two other stores of configuration values.
 
26
Those being gconf and a plain config file.
 
27
In addition to the value, the default layer must also provide
 
28
the datatype (str, int, float and bool are currently supported).
 
29
values are found as attributes of the TerminatorConfig object.
 
30
Trying to read a value that doesn't exist will raise an 
 
31
AttributeError. This is by design. If you want to look something 
 
32
up, set a default for it first."""
 
33
 
 
34
# import standard python libs
 
35
import os, sys, re
 
36
 
 
37
# import unix-lib
 
38
import pwd
 
39
 
 
40
# set this to true to enable debugging output
 
41
debug = False
 
42
 
 
43
def dbg (log = ""):
 
44
  if debug:
 
45
    print >> sys.stderr, log
 
46
 
 
47
def err (log = ""):
 
48
  print >> sys.stderr, log
 
49
 
 
50
class TerminatorConfig:
 
51
  callback = None
 
52
  sources = []
 
53
 
 
54
  def __init__ (self, sources = []):
 
55
    for source in sources:
 
56
      if isinstance(source, TerminatorConfValuestore):
 
57
        self.sources.append (source)
 
58
 
 
59
    # We always add a default valuestore last so no valid config item ever goes unset
 
60
    source = TerminatorConfValuestoreDefault ()
 
61
    self.sources.append (source)
 
62
    
 
63
  def __getattr__ (self, keyname):
 
64
    for source in self.sources:
 
65
      dbg ("TConfig: Looking for: '%s' in '%s'"%(keyname, source.type))
 
66
      try:
 
67
        val = getattr (source, keyname)
 
68
        dbg (" TConfig: got: '%s' from a '%s'"%(val, source.type))
 
69
        return (val)
 
70
      except:
 
71
        pass
 
72
 
 
73
    dbg (" TConfig: Out of sources")
 
74
    raise (AttributeError)
 
75
 
 
76
class TerminatorConfValuestore:
 
77
  type = "Base"
 
78
  values = {}
 
79
  reconfigure_callback = None
 
80
 
 
81
  # Our settings
 
82
  defaults = {
 
83
    'gt_dir'                : '/apps/gnome-terminal',
 
84
    'profile_dir'           : '/apps/gnome-terminal/profiles',
 
85
    'titlebars'             : True,
 
86
    'titletips'             : False,
 
87
    'allow_bold'            : True,
 
88
    'silent_bell'           : True,
 
89
    'background_color'      : '#000000',
 
90
    'background_darkness'   : 0.5,
 
91
    'background_type'       : 'solid',
 
92
    'background_image'      : '',
 
93
    'backspace_binding'     : 'ascii-del',
 
94
    'delete_binding'        : 'delete-sequence',
 
95
    'cursor_blink'          : True,
 
96
    'emulation'             : 'xterm',
 
97
    'font'                  : 'Mono 8',
 
98
    'foreground_color'      : '#AAAAAA',
 
99
    'scrollbar_position'    : "right",
 
100
    'scroll_background'     : True,
 
101
    'scroll_on_keystroke'   : True,
 
102
    'scroll_on_output'      : True,
 
103
    'scrollback_lines'      : 500,
 
104
    'focus'                 : 'click',
 
105
    'exit_action'           : 'close',
 
106
    'palette'               : '#000000000000:#CDCD00000000:#0000CDCD0000:#CDCDCDCD0000:#30BF30BFA38E:#A53C212FA53C:#0000CDCDCDCD:#FAFAEBEBD7D7:#404040404040:#FFFF00000000:#0000FFFF0000:#FFFFFFFF0000:#00000000FFFF:#FFFF0000FFFF:#0000FFFFFFFF:#FFFFFFFFFFFF',
 
107
    'word_chars'            : '-A-Za-z0-9,./?%&#:_',
 
108
    'mouse_autohide'        : True,
 
109
    'update_records'        : True,
 
110
    'login_shell'           : False,
 
111
    'use_custom_command'    : False,
 
112
    'custom_command'        : '',
 
113
    'use_system_font'       : True,
 
114
    'use_theme_colors'      : False,
 
115
    'http_proxy'            : '',
 
116
    'ignore_hosts'          : ['localhost','127.0.0.0/8','*.local'],
 
117
    'encoding'              : 'UTF-8',
 
118
    'active_encodings'      : ['UTF-8', 'ISO-8859-1'],
 
119
    'background_image'      : '',
 
120
    'extreme_tabs'          : False,
 
121
    'fullscreen'            : False,
 
122
    'borderless'            : False,
 
123
    'maximise'              : False,
 
124
    'handle_size'           : -1,
 
125
    'focus_on_close'        : 'auto',
 
126
    'f11_modifier'          : False,
 
127
    'force_no_bell'         : False,
 
128
    'cycle_term_tab'        : True,
 
129
    'copy_on_selection'     : False,
 
130
    'close_button_on_tab'   : True,
 
131
    'enable_real_transparency'  : False,
 
132
  }
 
133
 
 
134
  def __getattr__ (self, keyname):
 
135
    if self.values.has_key (keyname):
 
136
      dbg ("Returning '%s'"%keyname)
 
137
      return self.values[keyname]
 
138
    else:
 
139
      dbg ("Failed to find '%s'"%keyname)
 
140
      raise (AttributeError)
 
141
 
 
142
class TerminatorConfValuestoreDefault (TerminatorConfValuestore):
 
143
  def __init__ (self):
 
144
    self.type = "Default"
 
145
    self.values = self.defaults
 
146
 
 
147
class TerminatorConfValuestoreRC (TerminatorConfValuestore):
 
148
  rcfilename = ""
 
149
  splitter = re.compile("\s*=\s*")
 
150
  #FIXME: use inotify to watch the rc, split __init__ into a parsing function
 
151
  #       that can be re-used when rc changes.
 
152
  def __init__ (self):
 
153
    self.type = "RCFile"
 
154
    try:
 
155
      directory = os.environ['XDG_CONFIG_HOME']
 
156
    except:
 
157
      directory = os.path.join (os.path.expanduser("~"), ".config/terminator/")
 
158
 
 
159
    self.rcfilename = os.path.join(directory, "config")
 
160
 
 
161
    if os.path.exists (self.rcfilename):
 
162
      rcfile = open (self.rcfilename)
 
163
      rc = rcfile.readlines ()
 
164
      rcfile.close ()
 
165
 
 
166
      for item in rc:
 
167
        try:
 
168
          item = item.strip ()
 
169
          if item and item[0] != '#':
 
170
            (key, value) = self.splitter.split (item)
 
171
 
 
172
            # Check if this is actually a key we care about
 
173
            if not self.defaults.has_key (key):
 
174
              continue
 
175
 
 
176
            deftype = self.defaults[key].__class__.__name__
 
177
            if deftype == 'bool':
 
178
              if value.lower () == 'true':
 
179
                self.values[key] = True
 
180
              elif value.lower () == 'false':
 
181
                self.values[key] = False
 
182
              else:
 
183
                raise AttributeError
 
184
            elif deftype == 'int':
 
185
              self.values[key] = int (value)
 
186
            elif deftype == 'float':
 
187
              self.values[key] = float (value)
 
188
            elif deftype == 'list':
 
189
              err (_("Reading list values from .config/terminator/config is not currently supported"))
 
190
              continue
 
191
            else:
 
192
              self.values[key] = value
 
193
 
 
194
            dbg (" VS_RCFile: Set value '%s' to '%s'"%(key, self.values[key]))
 
195
        except Exception, e:
 
196
          dbg (" VS_RCFile: %s Exception handling: %s" % (type(e), item))
 
197
          pass
 
198
 
 
199
class TerminatorConfValuestoreGConf (TerminatorConfValuestore):
 
200
  profile = ""
 
201
  client = None
 
202
  cache = {}
 
203
 
 
204
  def __init__ (self, profile = None):
 
205
    self.type = "GConf"
 
206
 
 
207
    import gconf
 
208
 
 
209
    self.client = gconf.client_get_default ()
 
210
 
 
211
    # Grab a couple of values from base class to avoid recursing with our __getattr__
 
212
    self._gt_dir = self.defaults['gt_dir']
 
213
    self._profile_dir = self.defaults['profile_dir']
 
214
 
 
215
    dbg ('VSGConf: Profile requested is: "%s"'%profile)
 
216
    if not profile:
 
217
      profile = self.client.get_string (self._gt_dir + '/global/default_profile')
 
218
    dbg ('VSGConf: Profile bet on is: "%s"'%profile)
 
219
    profiles = self.client.get_list (self._gt_dir + '/global/profile_list','string')
 
220
    dbg ('VSGConf: Found profiles: "%s"'%profiles)
 
221
 
 
222
    #set up the active encoding list
 
223
    self.active_encodings = self.client.get_list (self._gt_dir + '/global/active_encodings', 'string')
 
224
    
 
225
    #need to handle the list of Gconf.value
 
226
    if profile in profiles:
 
227
      dbg (" VSGConf: Found profile '%s' in profile_list"%profile)
 
228
      self.profile = '%s/%s'%(self._profile_dir, profile)
 
229
    elif "Default" in profiles:
 
230
      dbg (" VSGConf: profile '%s' not found, but 'Default' exists"%profile)
 
231
      self.profile = '%s/%s'%(self._profile_dir, "Default")
 
232
    else:
 
233
      # We're a bit stuck, there is no profile in the list
 
234
      # FIXME: Find a better way to handle this than setting a non-profile
 
235
      dbg ("VSGConf: No profile found, deleting __getattr__")
 
236
      del (self.__getattr__)
 
237
 
 
238
    self.client.add_dir (self.profile, gconf.CLIENT_PRELOAD_RECURSIVE)
 
239
    if self.on_gconf_notify:
 
240
      self.client.notify_add (self.profile, self.on_gconf_notify)
 
241
 
 
242
    self.client.add_dir ('/apps/metacity/general', gconf.CLIENT_PRELOAD_RECURSIVE)
 
243
    self.client.notify_add ('/apps/metacity/general/focus_mode', self.on_gconf_notify)
 
244
    self.client.add_dir ('/desktop/gnome/interface', gconf.CLIENT_PRELOAD_RECURSIVE)
 
245
    self.client.notify_add ('/desktop/gnome/interface/monospace_font_name', self.on_gconf_notify)
 
246
    # FIXME: Do we need to watch more non-profile stuff here?
 
247
 
 
248
  def set_reconfigure_callback (self, function):
 
249
    dbg (" VSConf: setting callback to: %s"%function)
 
250
    self.reconfigure_callback = function
 
251
    return (True)
 
252
 
 
253
  def on_gconf_notify (self, client, cnxn_id, entry, what):
 
254
    dbg (" VSGConf: invalidating cache")
 
255
    self.cache = {}
 
256
    dbg (" VSGConf: gconf changed, callback is: %s"%self.reconfigure_callback)
 
257
    if self.reconfigure_callback:
 
258
      self.reconfigure_callback ()
 
259
 
 
260
  def __getattr__ (self, key = ""):
 
261
    if self.cache.has_key (key):
 
262
      dbg (" VSGConf: returning cached value: %s"%self.cache[key])
 
263
      return (self.cache[key])
 
264
 
 
265
    ret = None
 
266
    value = None
 
267
 
 
268
    dbg (' VSGConf: preparing: %s/%s'%(self.profile, key))
 
269
 
 
270
    # FIXME: Ugly special cases we should look to fix in some other way.
 
271
    if key == 'font' and self.use_system_font:
 
272
      value = self.client.get ('/desktop/gnome/interface/monospace_font_name')
 
273
    elif key == 'focus':
 
274
      value = self.client.get ('/apps/metacity/general/focus_mode')
 
275
    elif key == 'http_proxy':
 
276
      if self.client.get_bool ('/system/http_proxy/use_http_proxy'):
 
277
        dbg ('HACK: Mangling http_proxy')
 
278
 
 
279
        if self.client.get_bool ('use_authentication'):
 
280
          dbg ('HACK: Using proxy authentication')
 
281
          value = 'http://%s:%s@%s:%s/'%(
 
282
            self.client.get_string ('/system/http_proxy/authentication_user'), 
 
283
            self.client.get_string ('/system/http_proxy/authentication_password'), 
 
284
            self.client.get_string ('/system/http_proxy/host'), 
 
285
            self.client.get_int ('/system/http_proxy/port'))
 
286
        else:
 
287
          dbg ('HACK: Not using proxy authentication')
 
288
          value = 'http://%s:%s/'%(
 
289
            self.client.get_string ('/system/http_proxy/host'),
 
290
            self.client.get_int ('/system/http_proxy/port'))
 
291
    else:
 
292
      value = self.client.get ('%s/%s'%(self.profile, key))
 
293
 
 
294
    if value:
 
295
      funcname = "get_" + self.defaults[key].__class__.__name__
 
296
      dbg ('  GConf: picked function: %s'%funcname)
 
297
      # Special case for str
 
298
      if funcname == "get_str":
 
299
        funcname = "get_string"
 
300
      # Special case for strlist
 
301
      if funcname == "get_strlist":
 
302
        funcname = "get_list"
 
303
      typefunc = getattr (value, funcname)
 
304
      ret = typefunc ()
 
305
 
 
306
      self.cache[key] = ret
 
307
      return (ret)
 
308
    else:
 
309
      raise (AttributeError)
 
310
 
 
311
if __name__ == '__main__':
 
312
 
 
313
  stores = []
 
314
  stores.append (TerminatorConfValuestoreRC ())
 
315
 
 
316
  try:
 
317
    import gconf
 
318
    stores.append (TerminatorConfValuestoreGConf ())
 
319
  except:
 
320
    pass
 
321
 
 
322
  foo = TerminatorConfig (stores)
 
323
 
 
324
  ## cmsj: this is my testing ground
 
325
  ##       ensure that font is set in the Default gconf profile
 
326
  ##       set titlebars in the RC file
 
327
  ##       remove titletips from gconf/RC
 
328
  ##       do not define blimnle in any way
 
329
 
 
330
  # This should come from gconf (it's set by gnome-terminal)
 
331
  print foo.font
 
332
 
 
333
  # This should come from RC
 
334
  print foo.titlebars
 
335
 
 
336
  # This should come from defaults
 
337
  print foo.titletips
 
338
 
 
339
  # This should raise AttributeError
 
340
  #print foo.blimnle
 
341
 
 
342
  # http_proxy is a value that is allowed to not exist
 
343
  print "final proxy: %s"%foo.http_proxy