rox.settings now uses rox.session to communicate with ROX-Session and
[rox-lib.git] / python / rox / settings.py
blobc5406b5f1e36b3ebb41f9453ef74c16cd6f63262
1 """ROX-Session settings with D-Bus and optional Gnome (gconf) setting
3 Setting and Settings are derived from ROX-Lib's Option and OptionGroup
4 respectively. A Setting sends a dbus message to ROX-Session when changed.
6 Use get_xsettings to get the dbus interface, then create a Settings object
7 with it to pass to each Setting.
8 """
9 import os
10 import rox
11 from rox.options import OptionGroup, Option
12 from rox import OptionsBox
13 import rox.session
14 import gobject
16 gconf = None
18 _warned_import = False
19 _warned_connect = False
20 _warned_norox = False
22 def get_xsettings():
23 """Returns ROX-Session's Settings dbus/xxmlrpc interface.
25 Called automatically if and when necessary
26 """
27 return rox.session.get_settings()
29 def get_gconf():
30 """Get GConf connection.
32 Some of the options have corresponding gconf entries; this gets
33 the gconf client connection. It will be called automatically if
34 and when necessary.
35 """
36 global gconf
37 try:
38 import gconf
39 client = gconf.client_get_default ()
40 client.add_dir ("/desktop/gnome/interface",
41 gconf.CLIENT_PRELOAD_NONE)
42 except:
43 client = None
44 return client
46 class Settings(OptionGroup):
47 """A group of options associated with the dbus interface. """
49 program = os.path.basename(rox.app_dir) # For dialog box title
51 def __init__(self, bus = None, client = None):
52 """Constructor
54 bus: ROX-Session's dbus interface. Omit to use default
55 client: gconf client connection. Omit to use default
56 """
57 self.options = {}
58 self.callbacks = []
59 self.bus = bus or get_xsettings()
60 self.client = client
62 def notify(self):
63 map(apply, self.callbacks)
64 for option in self:
65 option.has_changed = False
67 def save(self):
68 pass
70 class Setting(Option):
71 def __init__(self, name, default, settings, garbage = False,
72 gconf_key = None):
73 """Constructor
75 name: Option name as sent in dbus message.
76 default: Default value.
77 settings: The group of Settings this one belongs to.
78 garbage: Font and theme changes cause (some versions of?) GTK to
79 update all windows even if they're supposed to have been
80 destroyed. If we've just closed a dialog eg font selection (or
81 menu?), this can cause a crash, so this option forces a garbage
82 collection to make sure there is no stale reference.
83 gconf_key: Optional gconf setting key. If it begins with / it
84 will be treated as the absolute path, otherwise it will
85 have /desktop/gnome/interface/ prepended.
86 """
87 self.name = name
88 self.default = default
89 self.settings = settings
90 settings.options[name] = self
91 self.garbage = garbage
92 self.value = None
93 if gconf_key and gconf_key[0] != '/':
94 gconf_key = "/desktop/gnome/interface/" + gconf_key
95 self.gconf_key = gconf_key
96 try:
97 type, value = settings.bus.GetSetting(name)
98 except: #XXX: dbus.DBusException:
99 self._set(default)
100 else:
101 self._set(value, notify = False)
103 def make_gconf_value(self):
104 """Returns value ready to be converted to a GConfValue.
106 Override if necessary. Return a bool, int or string
107 (so the name is slightly misleading).
109 if type(self.default) is str:
110 return str(self.value)
111 else:
112 return self.int_value
114 def pre_notify_hook(self):
115 """Called just before notifying dbus the standard way.
117 Override to perform additional operations and return True
118 if you want to prevent normal notification.
119 Won't be called if there's no bus.
121 return False
123 def post_notify_hook(self):
124 """Called just after notifying dbus the standard way.
126 Override to perform additional operations.
127 Won't be called if there's no bus, but otherwise will be called
128 even if pre_notif_hook() returns True.
130 pass
132 def _set(self, value, notify = True):
133 Option._set(self, value)
134 if not notify: return
136 # This is a separate function because it used to be called via a
137 # GObject idle timeout instead of immediately. But that seems to be
138 # more of a hinrance than a help.
139 def set():
140 if self.garbage:
141 import gc
142 gc.collect()
144 if not self.settings.bus is None:
145 if not self.pre_notify_hook():
146 if type(self.default) is str:
147 self.settings.bus.SetString(self.name, self.value)
148 else:
149 self.settings.bus.SetInt(self.name, self.int_value)
150 self.post_notify_hook()
152 if self.gconf_key:
153 if not self.settings.client:
154 self.settings.client = get_gconf()
155 if self.settings.client:
156 val = self.make_gconf_value()
157 # Unfortunately GConfClient.set can't coerce builtin
158 # types to GConfValues
159 if type(val) is bool:
160 self.settings.client.set_bool(self.gconf_key, val)
161 elif type(val) is int:
162 self.settings.client.set_int(self.gconf_key, val)
163 else:
164 self.settings.client.set_string(self.gconf_key, val)
166 return False
168 if self.garbage:
169 gobject.idle_add(set)
170 else:
171 set()
173 class BoolSetting(Setting):
174 """Bool setting for GConf/D-Bus
176 Option doesn't distinguish between int and bool, but gconf does,
177 so use this for bool options.
179 def __init__(self, name, default, settings, theme, gconf_key = None):
180 Setting.__init__(self, name, default, settings, theme, gconf_key)
181 def make_gconf_value(self):
182 return self.int_value != 0
184 # Test routine
185 if __name__=='__main__':
186 setobj=get_xsettings()
187 print 'object=', setobj
188 v='Gtk/KeyThemeName'
189 print '%s = %s' % (v, setobj.GetSetting(v))
190 v='Net/ThemeName'
191 print '%s = %s' % (v, setobj.GetSetting(v))