2
# TerminatorConfig - layered config classes
3
# Copyright (C) 2006-2008 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
18
"""TerminatorConfig by Chris Jones <cmsj@tenshu.net>
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."""
34
# import standard python libs
40
# set this to true to enable debugging output
45
print >> sys.stderr, log
48
print >> sys.stderr, log
50
class TerminatorConfig:
54
def __init__ (self, sources = []):
55
for source in sources:
56
if isinstance(source, TerminatorConfValuestore):
57
self.sources.append (source)
59
# We always add a default valuestore last so no valid config item ever goes unset
60
source = TerminatorConfValuestoreDefault ()
61
self.sources.append (source)
63
def __getattr__ (self, keyname):
64
for source in self.sources:
65
dbg ("TConfig: Looking for: '%s' in '%s'"%(keyname, source.type))
67
val = getattr (source, keyname)
68
dbg (" TConfig: got: '%s' from a '%s'"%(val, source.type))
73
dbg (" TConfig: Out of sources")
74
raise (AttributeError)
76
class TerminatorConfValuestore:
79
reconfigure_callback = None
83
'gt_dir' : '/apps/gnome-terminal',
84
'profile_dir' : '/apps/gnome-terminal/profiles',
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',
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,
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,
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,
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,
134
def __getattr__ (self, keyname):
135
if self.values.has_key (keyname):
136
dbg ("Returning '%s'"%keyname)
137
return self.values[keyname]
139
dbg ("Failed to find '%s'"%keyname)
140
raise (AttributeError)
142
class TerminatorConfValuestoreDefault (TerminatorConfValuestore):
144
self.type = "Default"
145
self.values = self.defaults
147
class TerminatorConfValuestoreRC (TerminatorConfValuestore):
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.
155
directory = os.environ['XDG_CONFIG_HOME']
157
directory = os.path.join (os.path.expanduser("~"), ".config/terminator/")
159
self.rcfilename = os.path.join(directory, "config")
161
if os.path.exists (self.rcfilename):
162
rcfile = open (self.rcfilename)
163
rc = rcfile.readlines ()
169
if item and item[0] != '#':
170
(key, value) = self.splitter.split (item)
172
# Check if this is actually a key we care about
173
if not self.defaults.has_key (key):
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
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"))
192
self.values[key] = value
194
dbg (" VS_RCFile: Set value '%s' to '%s'"%(key, self.values[key]))
196
dbg (" VS_RCFile: %s Exception handling: %s" % (type(e), item))
199
class TerminatorConfValuestoreGConf (TerminatorConfValuestore):
204
def __init__ (self, profile = None):
209
self.client = gconf.client_get_default ()
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']
215
dbg ('VSGConf: Profile requested is: "%s"'%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)
222
#set up the active encoding list
223
self.active_encodings = self.client.get_list (self._gt_dir + '/global/active_encodings', 'string')
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")
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__)
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)
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?
248
def set_reconfigure_callback (self, function):
249
dbg (" VSConf: setting callback to: %s"%function)
250
self.reconfigure_callback = function
253
def on_gconf_notify (self, client, cnxn_id, entry, what):
254
dbg (" VSGConf: invalidating cache")
256
dbg (" VSGConf: gconf changed, callback is: %s"%self.reconfigure_callback)
257
if self.reconfigure_callback:
258
self.reconfigure_callback ()
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])
268
dbg (' VSGConf: preparing: %s/%s'%(self.profile, key))
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')
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')
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'))
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'))
292
value = self.client.get ('%s/%s'%(self.profile, key))
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)
306
self.cache[key] = ret
309
raise (AttributeError)
311
if __name__ == '__main__':
314
stores.append (TerminatorConfValuestoreRC ())
318
stores.append (TerminatorConfValuestoreGConf ())
322
foo = TerminatorConfig (stores)
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
330
# This should come from gconf (it's set by gnome-terminal)
333
# This should come from RC
336
# This should come from defaults
339
# This should raise AttributeError
342
# http_proxy is a value that is allowed to not exist
343
print "final proxy: %s"%foo.http_proxy