3 Module for Straw's configuration settings.
5 __copyright__
= "Copyright (c) 2002-2005 Free Software Foundation, Inc."
6 __license__
= """ GNU General Public License
8 This program is free software; you can redistribute it and/or modify it under the
9 terms of the GNU General Public License as published by the Free Software
10 Foundation; either version 2 of the License, or (at your option) any later
13 This program is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along with
18 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 Place - Suite 330, Boston, MA 02111-1307, USA. """
21 import cPickle
, os
, traceback
23 from error
import log
, logtb
, logparam
29 GCONF_STRAW_ROOT
= "/apps/straw"
31 OPTION_LAST_POLL
= "/general/last_poll"
32 OPTION_ITEMS_STORED
= "/general/number_of_items_stored"
33 OPTION_ITEM_ORDER
= "/general/item_order_newest"
34 OPTION_BROWSER_CMD
= "/general/browser_cmd"
35 OPTION_WINDOW_SIZE_W
= "/ui/window_width"
36 OPTION_WINDOW_SIZE_H
= "/ui/window_height"
37 OPTION_MAIN_PANE_POS
= "/ui/main_pane_position"
38 OPTION_SUB_PANE_POS
= "/ui/sub_pane_position"
39 OPTION_WINDOW_MAX
= "/ui/window_maximized"
40 OPTION_MAGNIFICATION
= "/ui/text_magnification"
41 OPTION_OFFLINE
= "/general/offline"
42 OPTION_POLL_FREQUENCY
= "/general/poll_frequency"
43 OPTION_FEED_ID_SEQ
= "feed_id_seq"
44 OPTION_FEEDS
= "feeds"
45 OPTION_CATEGORIES
= "categories"
48 # straw's home directory
50 home
= os
.getenv('HOME')
52 home
= os
.path
.expanduser('~')
53 return os
.path
.join(home
, '.straw')
55 def ensure_directory(strawdir
):
56 if os
.path
.exists(strawdir
):
57 if not os
.path
.isdir(strawdir
):
64 # Config persistence classes
65 class ConfigPicklePersistence
:
66 def __init__(self
, filename
):
67 self
._config
_file
= filename
70 def save_option(self
, option
, value
):
71 if self
._dict
is None:
72 self
._initialize
_dict
()
73 temp_config_file
= self
._config
_file
+ ".working"
74 pickle_file
= open(temp_config_file
, "w")
75 self
._dict
[option
] = value
76 cPickle
.dump(self
._dict
, pickle_file
, True)
78 os
.rename(temp_config_file
, self
._config
_file
)
81 def load_option(self
, option
):
82 if self
._dict
is None:
83 self
._initialize
_dict
()
84 return self
._dict
.get(option
, None)
86 def _initialize_dict(self
):
87 if os
.path
.exists(self
._config
_file
):
88 pickle_file
= open(self
._config
_file
, "r")
89 self
._dict
= cPickle
.load(pickle_file
)
94 class ConfigGConfPersistence
:
95 SAVERS
= {OPTION_LAST_POLL
: 'int',
96 OPTION_ITEMS_STORED
: 'int',
97 OPTION_ITEM_ORDER
: 'bool',
98 OPTION_BROWSER_CMD
: 'string',
99 OPTION_WINDOW_SIZE_W
: 'int',
100 OPTION_WINDOW_SIZE_H
: 'int',
101 OPTION_MAIN_PANE_POS
: 'int',
102 OPTION_SUB_PANE_POS
: 'int',
103 OPTION_OFFLINE
: 'bool',
104 OPTION_WINDOW_MAX
: 'bool',
105 OPTION_MAGNIFICATION
: 'float',
106 OPTION_POLL_FREQUENCY
: 'int'}
108 def __init__(self
, client
):
111 def save_option(self
, option
, value
):
112 getattr(self
.client
, 'set_' + self
.SAVERS
[option
])(
113 GCONF_STRAW_ROOT
+ option
, value
)
115 def load_option(self
, option
):
116 return getattr(self
.client
, 'get_' + self
.SAVERS
[option
])(
117 GCONF_STRAW_ROOT
+ option
)
119 class ConfigPersistence
:
120 def __init__(self
, *backends
):
121 self
.backends
= backends
123 def save_option(self
, option
, value
):
124 for b
in self
.backends
:
126 b
[0].save_option(option
, value
)
128 def load_option(self
, option
):
129 for b
in self
.backends
:
131 return b
[0].load_option(option
)
133 class ProxyConfig(object):
139 self
._username
= None
140 self
._password
= None
144 doc
= "this will be true when one of the subclasses is used"
147 def fset(self
, active
):
148 self
._active
= active
149 return property(**locals())
153 doc
= "the host name where the proxy server lives"
156 def fset(self
, host
):
158 return property(**locals())
162 doc
= "the port of the proxy server to connect to"
165 def fset(self
, port
):
167 return property(**locals())
171 doc
= "true if we need to authenticate to the proxy server"
174 def fset(self
, isauth
):
176 return property(**locals())
180 doc
= "the username to use when authenticating to the proxy"
182 return self
._username
183 def fset(self
, username
):
184 self
._username
= username
185 return property(**locals())
189 doc
= "the password to use when authenticating to the proxy"
191 return self
._password
192 def fset(self
, password
):
193 self
._password
= password
194 return property(**locals())
197 class GconfProxyConfig(ProxyConfig
):
198 """Encapsulate proxy use and location information (host, port, ip),
199 gconf reading and name lookup logic"""
201 GCONF_HTTP_PROXY
= "/system/http_proxy"
202 GCONF_HTTP_PROXY_USE
= GCONF_HTTP_PROXY
+ "/use_http_proxy"
203 GCONF_HTTP_PROXY_HOST
= GCONF_HTTP_PROXY
+ "/host"
204 GCONF_HTTP_PROXY_PORT
= GCONF_HTTP_PROXY
+ "/port"
205 GCONF_HTTP_PROXY_AUTHENTICATION
= GCONF_HTTP_PROXY
+ "/use_authentication"
206 GCONF_HTTP_PROXY_USER
= GCONF_HTTP_PROXY
+ "/authentication_user"
207 GCONF_HTTP_PROXY_PASSWORD
= GCONF_HTTP_PROXY
+ "/authentication_password"
210 ProxyConfig
.__init
__(self
)
211 client
= gconf
.client_get_default()
212 self
.active
= client
.dir_exists(self
.GCONF_HTTP_PROXY
)
216 client
.add_dir(self
.GCONF_HTTP_PROXY
, gconf
.CLIENT_PRELOAD_RECURSIVE
)
217 client
.notify_add(self
.GCONF_HTTP_PROXY_USE
, self
.proxy_mode_changed
)
218 client
.notify_add(self
.GCONF_HTTP_PROXY_HOST
, self
.proxy_host_changed
)
219 client
.notify_add(self
.GCONF_HTTP_PROXY_PORT
, self
.proxy_port_changed
)
220 client
.notify_add(self
.GCONF_HTTP_PROXY_AUTHENTICATION
, self
.proxy_auth_changed
)
221 client
.notify_add(self
.GCONF_HTTP_PROXY_USER
, self
.proxy_auth_username_changed
)
222 client
.notify_add(self
.GCONF_HTTP_PROXY_PASSWORD
, self
.proxy_auth_password_changed
)
224 self
.active
= client
.get_bool(self
.GCONF_HTTP_PROXY_USE
)
225 self
.host
= client
.get_string(self
.GCONF_HTTP_PROXY_HOST
)
226 self
.port
= client
.get_int(self
.GCONF_HTTP_PROXY_PORT
)
227 self
.auth
= client
.get_bool(self
.GCONF_HTTP_PROXY_AUTHENTICATION
)
229 self
.username
= client
.get_string(self
.GCONF_HTTP_PROXY_USER
)
230 self
.password
= client
.get_string(self
.GCONF_HTTP_PROXY_PASSWORD
)
233 # here be gconf logic
234 def proxy_mode_changed(self
, client
, notify_id
, entry
, *args
):
235 self
.active
= entry
.value
.get_bool()
237 def proxy_host_changed(self
, client
, notify_id
, entry
, *args
):
238 value
= entry
.value
.get_string()
244 def proxy_port_changed(self
, client
, notify_id
, entry
, *args
):
245 value
= entry
.value
.get_int()
251 def proxy_auth_changed(self
, client
, notify_id
, entry
, *args
):
252 value
= entry
.value
.get_bool()
256 def proxy_auth_username_changed(self
, client
, notify_id
, entry
, *args
):
257 value
= entry
.value
.get_string()
258 self
.username
= value
260 def proxy_auth_password_changed(self
, client
, notify_id
, entry
, *args
):
261 value
= entry
.value
.get_string()
262 self
.password
= value
264 class EnvironmentProxyConfig(ProxyConfig
):
265 """Encapsulate proxy use and location information, environment reading"""
268 ProxyConfig
.__init
__(self
)
273 proxies
= urllib
.getproxies()
277 for key
, value
in proxies
.iteritems():
278 proxy
= proxies
.get(key
)
279 user
, authority
= urllib
.splituser(proxy
)
281 self
.host
, self
.port
= urllib
.splitport(authority
)
284 self
.username
, self
.password
= urllib
.splitpasswd(user
)
288 class Config(object, Event
.SignalEmitter
):
289 _straw_config_file
= os
.path
.join(straw_home(), "config")
291 def __init__(self
, persistence
):
292 Event
.SignalEmitter
.__init
__(self
)
293 self
.persistence
= persistence
294 self
.initialize_slots(Event
.ItemOrderChangedSignal
,
295 Event
.OfflineModeChangedSignal
,
296 Event
.PollFrequencyChangedSignal
,
297 Event
.NumberOfItemsStoredChangedSignal
)
298 self
._feed
_id
_seq
= 0
299 self
._poll
_freq
= 1800
301 self
._browser
_cmd
= ''
302 self
._items
_stored
= 30
303 self
._item
_order
= False
304 self
._main
_window
_size
= (640,480)
305 self
._main
_pane
_position
= 100
306 self
._sub
_pane
_position
= 100
307 self
.first_time
= None
309 self
._window
_maximized
= False
310 self
._reload
_css
= False
313 def initialize_proxy(self
):
314 # EnvironmentProxy has precedence
315 self
._proxy
= EnvironmentProxyConfig()
316 if not self
._proxy
.active
:
317 # .. default to GConfProxyConfig so we can listen for network
318 # setting changes (e.g, changes in network preference)
319 self
._proxy
= GconfProxyConfig()
321 def initialize_options(self
):
322 # Call this after calling Config's constructor
323 self
.first_time
= ensure_directory(straw_home())
325 if os
.path
.exists(self
.straw_config_file
):
326 self
._feed
_id
_seq
= self
.persistence
.load_option(OPTION_FEED_ID_SEQ
)
328 self
._poll
_freq
= self
.persistence
.load_option(OPTION_POLL_FREQUENCY
)
329 self
._last
_poll
= self
.persistence
.load_option(OPTION_LAST_POLL
)
330 self
._items
_stored
= self
.persistence
.load_option(OPTION_ITEMS_STORED
)
331 self
._browser
_cmd
= self
.persistence
.load_option(OPTION_BROWSER_CMD
)
332 self
._item
_order
= self
.persistence
.load_option(OPTION_ITEM_ORDER
)
334 width
= self
.persistence
.load_option(OPTION_WINDOW_SIZE_W
)
335 height
= self
.persistence
.load_option(OPTION_WINDOW_SIZE_H
)
340 self
._main
_window
_size
= (width
, height
)
342 self
._main
_pane
_position
= self
.persistence
.load_option(
343 OPTION_MAIN_PANE_POS
)
344 self
._sub
_pane
_position
= self
.persistence
.load_option(
346 self
._window
_maximized
= self
.persistence
.load_option(OPTION_WINDOW_MAX
)
347 self
._text
_magnification
= self
.persistence
.load_option(OPTION_MAGNIFICATION
)
348 self
._offline
= self
.persistence
.load_option(OPTION_OFFLINE
)
351 def straw_config_file(self
):
352 return self
._straw
_config
_file
360 doc
= "Marshalled feed data"
362 return self
.persistence
.load_option(OPTION_FEEDS
)
363 def fset(self
, feeddata
):
364 self
.persistence
.save_option(
365 OPTION_FEEDS
, feeddata
)
366 return property(**locals())
372 return self
.persistence
.load_option(OPTION_CATEGORIES
)
373 def fset(self
, categorydata
):
374 self
.persistence
.save_option(
375 OPTION_CATEGORIES
, categorydata
)
376 return property(**locals())
379 def poll_frequency():
380 doc
= "Polling frequency"
382 return self
._poll
_freq
383 def fset(self
, poll_frequency
):
384 if self
._poll
_freq
!= poll_frequency
:
385 self
._poll
_freq
= poll_frequency
386 self
.persistence
.save_option(OPTION_POLL_FREQUENCY
, poll_frequency
)
387 self
.emit_signal(Event
.PollFrequencyChangedSignal(self
, poll_frequency
))
388 return property(**locals())
394 return self
._last
_poll
395 def fset(self
, last_poll
):
396 if self
._last
_poll
!= last_poll
:
397 self
._last
_poll
= last_poll
398 self
.persistence
.save_option(OPTION_LAST_POLL
, last_poll
)
399 return property(**locals())
403 doc
= "The browser to use"
405 return self
._browser
_cmd
406 def fset(self
, browser_cmd
):
407 if self
._browser
_cmd
!= browser_cmd
:
408 self
._browser
_cmd
= browser_cmd
409 self
.persistence
.save_option(OPTION_BROWSER_CMD
, browser_cmd
)
410 return property(**locals())
413 def number_of_items_stored():
414 doc
= "Number of items to store per feed"
416 return self
._items
_stored
417 def fset(self
, num
=30):
418 if self
._items
_stored
!= num
:
419 self
._items
_stored
= num
420 self
.persistence
.save_option(OPTION_ITEMS_STORED
, num
)
421 self
.emit_signal(Event
.NumberOfItemsStoredChangedSignal(self
))
422 return property(**locals())
426 doc
= "Ordering of Items"
428 return self
._item
_order
429 def fset(self
, order
):
430 if self
._item
_order
!= order
:
431 self
._item
_order
= order
432 self
.persistence
.save_option(OPTION_ITEM_ORDER
, order
)
433 self
.emit_signal(Event
.ItemOrderChangedSignal(self
))
434 return property(**locals())
440 return self
._feed
_id
_seq
442 self
._feed
_id
_seq
= id
443 self
.persistence
.save_option(OPTION_FEED_ID_SEQ
, id)
444 return property(**locals())
446 def next_feed_id_seq(self
):
447 self
.feed_id_seq
+= 1
448 return self
._feed
_id
_seq
451 def main_window_size():
454 return self
._main
_window
_size
455 def fset(self
, size
):
456 if self
._main
_window
_size
!= size
:
457 self
._main
_window
_size
= size
458 self
.persistence
.save_option(OPTION_WINDOW_SIZE_W
, size
[0])
459 self
.persistence
.save_option(OPTION_WINDOW_SIZE_H
, size
[1])
460 return property(**locals())
467 def fset(self
, mode
):
468 if self
._offline
!= mode
:
470 self
.persistence
.save_option(OPTION_OFFLINE
, mode
)
471 self
.emit_signal(Event
.OfflineModeChangedSignal(self
))
472 return property(**locals())
475 def window_maximized():
478 return self
._window
_maximized
479 def fset(self
, state
):
480 if self
._window
_maximized
is not state
:
481 self
._window
_maximized
= state
482 self
.persistence
.save_option(OPTION_WINDOW_MAX
, state
)
483 return property(**locals())
486 def main_pane_position():
489 return self
._main
_pane
_position
490 def fset(self
, position
):
491 if self
._main
_pane
_position
!= position
:
492 self
._main
_pane
_position
= position
493 self
.persistence
.save_option(OPTION_MAIN_PANE_POS
, position
)
494 return property(**locals())
497 def sub_pane_position():
500 return self
._sub
_pane
_position
501 def fset(self
, position
):
502 if self
._sub
_pane
_position
!= position
:
503 self
._sub
_pane
_position
= position
504 self
.persistence
.save_option(OPTION_SUB_PANE_POS
, position
)
505 return property(**locals())
508 def text_magnification():
509 doc
= "sets the amount of magnification in the item view"
511 return self
._text
_magnification
513 if self
._text
_magnification
is not tm
:
514 self
._text
_magnification
= tm
515 self
.persistence
.save_option(OPTION_MAGNIFICATION
, tm
)
516 return property(**locals())
522 return self
._reload
_css
523 def fset(self
, reload_css
):
524 self
._reload
_css
= reload_css
525 return property(**locals())
527 def convert_if_necessary(config
):
528 if not os
.path
.exists(config
.straw_config_file
):
532 f
= open(config
.straw_config_file
, "r")
535 if cf
.has_key('poll_frequency'):
536 config
.poll_frequency
= cf
.get('poll_frequency')
537 config
.number_of_items_stored
= cf
.get('number_of_items_stored')
538 config
.item_order
= cf
.get('item_order')
539 config
.main_window_size
= cf
.get('main_window_size')
540 config
.main_pane_position
= cf
.get('main_pane_position')
541 config
.sub_pane_position
= cf
.get('sub_pane_position')
542 config
.offline
= cf
.get('offline')
544 del cf
['poll_frequency']
545 del cf
['number_of_items_stored']
547 del cf
['main_window_size']
548 del cf
['main_pane_position']
549 del cf
['sub_pane_position']
553 f
= open(config
.straw_config_file
, "w")
556 cPickle
.dump(cf
, f
, True)
561 def create_gconf_persistence():
562 client
= gconf
.client_get_default()
563 client
.add_dir(GCONF_STRAW_ROOT
, gconf
.CLIENT_PRELOAD_ONELEVEL
)
564 return ConfigGConfPersistence(client
)
566 def create_pickle_persistence():
567 return ConfigPicklePersistence(Config
._straw
_config
_file
)
569 def create_instance():
570 # Add your GConf key here as well!
571 cp
= ConfigPersistence(
572 (create_gconf_persistence(),
573 (OPTION_LAST_POLL
, OPTION_ITEMS_STORED
, OPTION_ITEM_ORDER
, OPTION_BROWSER_CMD
,
574 OPTION_WINDOW_SIZE_W
, OPTION_WINDOW_SIZE_H
,
575 OPTION_MAIN_PANE_POS
, OPTION_SUB_PANE_POS
, OPTION_OFFLINE
,
576 OPTION_MAGNIFICATION
,
577 OPTION_POLL_FREQUENCY
, OPTION_WINDOW_MAX
)),
578 (create_pickle_persistence(),
579 (OPTION_FEED_ID_SEQ
, OPTION_FEEDS
, OPTION_CATEGORIES
)))
581 config
.initialize_proxy()
582 config
.initialize_options()
583 convert_if_necessary(config
)
586 config_instance
= None
588 global config_instance
589 if config_instance
is None:
590 config_instance
= create_instance()
591 return config_instance