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_PANE_LAYOUT
= "/ui/pane_layout"
42 OPTION_OFFLINE
= "/general/offline"
43 OPTION_POLL_FREQUENCY
= "/general/poll_frequency"
44 OPTION_FEED_ID_SEQ
= "feed_id_seq"
45 OPTION_FEEDS
= "feeds"
46 OPTION_CATEGORIES
= "categories"
49 # straw's home directory
51 home
= os
.getenv('HOME')
53 home
= os
.path
.expanduser('~')
54 return os
.path
.join(home
, '.straw')
56 def ensure_directory(strawdir
):
57 if os
.path
.exists(strawdir
):
58 if not os
.path
.isdir(strawdir
):
65 # Config persistence classes
66 class ConfigPicklePersistence
:
67 def __init__(self
, filename
):
68 self
._config
_file
= filename
71 def save_option(self
, option
, value
):
72 if self
._dict
is None:
73 self
._initialize
_dict
()
74 temp_config_file
= self
._config
_file
+ ".working"
75 pickle_file
= open(temp_config_file
, "w")
76 self
._dict
[option
] = value
77 cPickle
.dump(self
._dict
, pickle_file
, True)
79 os
.rename(temp_config_file
, self
._config
_file
)
82 def load_option(self
, option
):
83 if self
._dict
is None:
84 self
._initialize
_dict
()
85 return self
._dict
.get(option
, None)
87 def _initialize_dict(self
):
88 if os
.path
.exists(self
._config
_file
):
89 pickle_file
= open(self
._config
_file
, "r")
90 self
._dict
= cPickle
.load(pickle_file
)
95 class ConfigGConfPersistence
:
96 SAVERS
= {OPTION_LAST_POLL
: 'int',
97 OPTION_ITEMS_STORED
: 'int',
98 OPTION_ITEM_ORDER
: 'bool',
99 OPTION_BROWSER_CMD
: 'string',
100 OPTION_WINDOW_SIZE_W
: 'int',
101 OPTION_WINDOW_SIZE_H
: 'int',
102 OPTION_MAIN_PANE_POS
: 'int',
103 OPTION_SUB_PANE_POS
: 'int',
104 OPTION_OFFLINE
: 'bool',
105 OPTION_WINDOW_MAX
: 'bool',
106 OPTION_MAGNIFICATION
: 'float',
107 OPTION_POLL_FREQUENCY
: 'int',
108 OPTION_PANE_LAYOUT
: 'string',
111 def __init__(self
, client
):
114 def save_option(self
, option
, value
):
115 getattr(self
.client
, 'set_' + self
.SAVERS
[option
])(
116 GCONF_STRAW_ROOT
+ option
, value
)
118 def load_option(self
, option
):
119 return getattr(self
.client
, 'get_' + self
.SAVERS
[option
])(
120 GCONF_STRAW_ROOT
+ option
)
122 class ConfigPersistence
:
123 def __init__(self
, *backends
):
124 self
.backends
= backends
126 def save_option(self
, option
, value
):
127 for b
in self
.backends
:
129 b
[0].save_option(option
, value
)
131 def load_option(self
, option
):
132 for b
in self
.backends
:
134 return b
[0].load_option(option
)
136 class ProxyConfig(object):
142 self
._username
= None
143 self
._password
= None
147 doc
= "this will be true when one of the subclasses is used"
150 def fset(self
, active
):
151 self
._active
= active
152 return property(**locals())
156 doc
= "the host name where the proxy server lives"
159 def fset(self
, host
):
161 return property(**locals())
165 doc
= "the port of the proxy server to connect to"
168 def fset(self
, port
):
170 return property(**locals())
174 doc
= "true if we need to authenticate to the proxy server"
177 def fset(self
, isauth
):
179 return property(**locals())
183 doc
= "the username to use when authenticating to the proxy"
185 return self
._username
186 def fset(self
, username
):
187 self
._username
= username
188 return property(**locals())
192 doc
= "the password to use when authenticating to the proxy"
194 return self
._password
195 def fset(self
, password
):
196 self
._password
= password
197 return property(**locals())
200 class GconfProxyConfig(ProxyConfig
):
201 """Encapsulate proxy use and location information (host, port, ip),
202 gconf reading and name lookup logic"""
204 GCONF_HTTP_PROXY
= "/system/http_proxy"
205 GCONF_HTTP_PROXY_USE
= GCONF_HTTP_PROXY
+ "/use_http_proxy"
206 GCONF_HTTP_PROXY_HOST
= GCONF_HTTP_PROXY
+ "/host"
207 GCONF_HTTP_PROXY_PORT
= GCONF_HTTP_PROXY
+ "/port"
208 GCONF_HTTP_PROXY_AUTHENTICATION
= GCONF_HTTP_PROXY
+ "/use_authentication"
209 GCONF_HTTP_PROXY_USER
= GCONF_HTTP_PROXY
+ "/authentication_user"
210 GCONF_HTTP_PROXY_PASSWORD
= GCONF_HTTP_PROXY
+ "/authentication_password"
213 ProxyConfig
.__init
__(self
)
214 client
= gconf
.client_get_default()
215 self
.active
= client
.dir_exists(self
.GCONF_HTTP_PROXY
)
219 client
.add_dir(self
.GCONF_HTTP_PROXY
, gconf
.CLIENT_PRELOAD_RECURSIVE
)
220 client
.notify_add(self
.GCONF_HTTP_PROXY_USE
, self
.proxy_mode_changed
)
221 client
.notify_add(self
.GCONF_HTTP_PROXY_HOST
, self
.proxy_host_changed
)
222 client
.notify_add(self
.GCONF_HTTP_PROXY_PORT
, self
.proxy_port_changed
)
223 client
.notify_add(self
.GCONF_HTTP_PROXY_AUTHENTICATION
, self
.proxy_auth_changed
)
224 client
.notify_add(self
.GCONF_HTTP_PROXY_USER
, self
.proxy_auth_username_changed
)
225 client
.notify_add(self
.GCONF_HTTP_PROXY_PASSWORD
, self
.proxy_auth_password_changed
)
227 self
.active
= client
.get_bool(self
.GCONF_HTTP_PROXY_USE
)
228 self
.host
= client
.get_string(self
.GCONF_HTTP_PROXY_HOST
)
229 self
.port
= client
.get_int(self
.GCONF_HTTP_PROXY_PORT
)
230 self
.auth
= client
.get_bool(self
.GCONF_HTTP_PROXY_AUTHENTICATION
)
232 self
.username
= client
.get_string(self
.GCONF_HTTP_PROXY_USER
)
233 self
.password
= client
.get_string(self
.GCONF_HTTP_PROXY_PASSWORD
)
236 # here be gconf logic
237 def proxy_mode_changed(self
, client
, notify_id
, entry
, *args
):
238 self
.active
= entry
.value
.get_bool()
240 def proxy_host_changed(self
, client
, notify_id
, entry
, *args
):
241 value
= entry
.value
.get_string()
247 def proxy_port_changed(self
, client
, notify_id
, entry
, *args
):
248 value
= entry
.value
.get_int()
254 def proxy_auth_changed(self
, client
, notify_id
, entry
, *args
):
255 value
= entry
.value
.get_bool()
259 def proxy_auth_username_changed(self
, client
, notify_id
, entry
, *args
):
260 value
= entry
.value
.get_string()
261 self
.username
= value
263 def proxy_auth_password_changed(self
, client
, notify_id
, entry
, *args
):
264 value
= entry
.value
.get_string()
265 self
.password
= value
267 class EnvironmentProxyConfig(ProxyConfig
):
268 """Encapsulate proxy use and location information, environment reading"""
271 ProxyConfig
.__init
__(self
)
276 proxies
= urllib
.getproxies()
280 for key
, value
in proxies
.iteritems():
281 proxy
= proxies
.get(key
)
282 user
, authority
= urllib
.splituser(proxy
)
284 self
.host
, self
.port
= urllib
.splitport(authority
)
287 self
.username
, self
.password
= urllib
.splitpasswd(user
)
291 class Config(gobject
.GObject
):
292 _straw_config_file
= os
.path
.join(straw_home(), "config")
295 'item-order-changed' : (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, ()),
296 'offline-mode-changed' : (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, ()),
297 'refresh-changed' : (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, ()),
298 'item-stored-changed' : (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, ())
301 def __init__(self
, persistence
):
302 gobject
.GObject
.__init
__(self
)
303 self
.persistence
= persistence
304 self
._feed
_id
_seq
= 0
305 self
._poll
_freq
= 1800
307 self
._browser
_cmd
= ''
308 self
._items
_stored
= 30
309 self
._item
_order
= True
310 self
._main
_window
_size
= (640,480)
311 self
._main
_pane
_position
= 100
312 self
._sub
_pane
_position
= 100
313 self
.first_time
= None
315 self
._window
_maximized
= False
316 self
._reload
_css
= False
318 self
._pane
_layout
= 'vertical'
320 def initialize_proxy(self
):
321 # EnvironmentProxy has precedence
322 self
._proxy
= EnvironmentProxyConfig()
323 if not self
._proxy
.active
:
324 # .. defaults to GConfProxyConfig so we can listen for network
325 # setting changes (e.g, changes in network preference)
326 self
._proxy
= GconfProxyConfig()
328 def initialize_options(self
):
329 # Call this after calling Config's constructor
330 self
.first_time
= ensure_directory(straw_home())
332 if os
.path
.exists(self
.straw_config_file
):
333 self
._feed
_id
_seq
= self
.persistence
.load_option(OPTION_FEED_ID_SEQ
)
335 self
._poll
_freq
= self
.persistence
.load_option(OPTION_POLL_FREQUENCY
)
336 self
._last
_poll
= self
.persistence
.load_option(OPTION_LAST_POLL
)
337 self
._items
_stored
= self
.persistence
.load_option(OPTION_ITEMS_STORED
)
338 self
._browser
_cmd
= self
.persistence
.load_option(OPTION_BROWSER_CMD
)
339 self
._item
_order
= self
.persistence
.load_option(OPTION_ITEM_ORDER
)
341 if not self
._poll
_freq
:
342 self
.poll_frequency
= 1800
343 if not self
._items
_stored
:
344 self
.number_of_items_stored
= 30
345 if not self
._item
_order
:
346 self
.item_order
= True
348 width
= self
.persistence
.load_option(OPTION_WINDOW_SIZE_W
)
349 height
= self
.persistence
.load_option(OPTION_WINDOW_SIZE_H
)
354 self
._main
_window
_size
= (width
, height
)
356 self
._main
_pane
_position
= self
.persistence
.load_option(OPTION_MAIN_PANE_POS
)
357 self
._sub
_pane
_position
= self
.persistence
.load_option(OPTION_SUB_PANE_POS
)
358 if not self
._main
_pane
_position
:
359 self
._main
_pane
_position
= 100
360 if not self
._sub
_pane
_position
:
361 self
._sub
_pane
_position
= 100
362 print (self
.main_pane_position
, self
.sub_pane_position
)
364 self
._window
_maximized
= self
.persistence
.load_option(OPTION_WINDOW_MAX
)
365 self
._text
_magnification
= self
.persistence
.load_option(OPTION_MAGNIFICATION
)
366 self
._offline
= self
.persistence
.load_option(OPTION_OFFLINE
)
367 self
._pane
_layout
= self
.persistence
.load_option(OPTION_PANE_LAYOUT
)
370 def straw_config_file(self
):
371 return self
._straw
_config
_file
379 doc
= "Marshalled feed data"
381 return self
.persistence
.load_option(OPTION_FEEDS
)
382 def fset(self
, feeddata
):
383 self
.persistence
.save_option(OPTION_FEEDS
, feeddata
)
384 return property(**locals())
390 return self
.persistence
.load_option(OPTION_CATEGORIES
)
391 def fset(self
, categorydata
):
392 self
.persistence
.save_option(
393 OPTION_CATEGORIES
, categorydata
)
394 return property(**locals())
397 def poll_frequency():
398 doc
= "Polling frequency"
400 return self
._poll
_freq
401 def fset(self
, poll_frequency
):
402 if self
._poll
_freq
!= poll_frequency
:
403 self
._poll
_freq
= poll_frequency
404 self
.persistence
.save_option(OPTION_POLL_FREQUENCY
, poll_frequency
)
405 self
.emit('refresh-changed')
406 return property(**locals())
412 return self
._last
_poll
413 def fset(self
, last_poll
):
414 if self
._last
_poll
!= last_poll
:
415 self
._last
_poll
= last_poll
416 self
.persistence
.save_option(OPTION_LAST_POLL
, last_poll
)
417 return property(**locals())
421 doc
= "The browser to use"
423 return self
._browser
_cmd
424 def fset(self
, browser_cmd
):
425 if self
._browser
_cmd
!= browser_cmd
:
426 self
._browser
_cmd
= browser_cmd
427 self
.persistence
.save_option(OPTION_BROWSER_CMD
, browser_cmd
)
428 return property(**locals())
431 def number_of_items_stored():
432 doc
= "Number of items to store per feed"
434 return self
._items
_stored
435 def fset(self
, num
=30):
436 if self
._items
_stored
!= num
:
437 self
._items
_stored
= num
438 self
.persistence
.save_option(OPTION_ITEMS_STORED
, num
)
439 self
.emit('item-stored-changed')
440 return property(**locals())
444 doc
= "Ordering of Items"
446 return self
._item
_order
447 def fset(self
, order
):
448 if self
._item
_order
!= order
:
449 self
._item
_order
= order
450 self
.persistence
.save_option(OPTION_ITEM_ORDER
, order
)
451 self
.emit('item-order-changed')
452 return property(**locals())
458 return self
._feed
_id
_seq
460 self
._feed
_id
_seq
= id
461 self
.persistence
.save_option(OPTION_FEED_ID_SEQ
, id)
462 return property(**locals())
464 def next_feed_id_seq(self
):
465 self
.feed_id_seq
+= 1
466 return self
._feed
_id
_seq
469 def main_window_size():
472 return self
._main
_window
_size
473 def fset(self
, size
):
474 if self
._main
_window
_size
!= size
:
475 self
._main
_window
_size
= size
476 self
.persistence
.save_option(OPTION_WINDOW_SIZE_W
, size
[0])
477 self
.persistence
.save_option(OPTION_WINDOW_SIZE_H
, size
[1])
478 return property(**locals())
485 def fset(self
, mode
):
486 if self
._offline
!= mode
:
488 self
.persistence
.save_option(OPTION_OFFLINE
, mode
)
489 self
.emit('offline-mode-changed')
490 return property(**locals())
493 def window_maximized():
496 return self
._window
_maximized
497 def fset(self
, state
):
498 if self
._window
_maximized
is not state
:
499 self
._window
_maximized
= state
500 self
.persistence
.save_option(OPTION_WINDOW_MAX
, state
)
501 return property(**locals())
504 def main_pane_position():
507 return self
._main
_pane
_position
508 def fset(self
, position
):
509 if self
._main
_pane
_position
!= position
:
510 self
._main
_pane
_position
= position
511 self
.persistence
.save_option(OPTION_MAIN_PANE_POS
, position
)
512 return property(**locals())
515 def sub_pane_position():
518 return self
._sub
_pane
_position
519 def fset(self
, position
):
520 if self
._sub
_pane
_position
!= position
:
521 self
._sub
_pane
_position
= position
522 self
.persistence
.save_option(OPTION_SUB_PANE_POS
, position
)
523 return property(**locals())
527 doc
= "The pane layout to use"
529 return self
._pane
_layout
530 def fset(self
, pane_layout
):
531 if self
._pane
_layout
!= pane_layout
:
532 self
._pane
_layout
= pane_layout
533 self
.persistence
.save_option(OPTION_PANE_LAYOUT
, pane_layout
)
534 return property(**locals())
537 def text_magnification():
538 doc
= "sets the amount of magnification in the item view"
540 return self
._text
_magnification
542 if self
._text
_magnification
is not tm
:
543 self
._text
_magnification
= tm
544 self
.persistence
.save_option(OPTION_MAGNIFICATION
, tm
)
545 return property(**locals())
551 return self
._reload
_css
552 def fset(self
, reload_css
):
553 self
._reload
_css
= reload_css
554 return property(**locals())
556 def convert_if_necessary(config
):
557 if not os
.path
.exists(config
.straw_config_file
):
561 f
= open(config
.straw_config_file
, "r")
564 if cf
.has_key('poll_frequency'):
565 config
.poll_frequency
= cf
.get('poll_frequency')
566 config
.number_of_items_stored
= cf
.get('number_of_items_stored')
567 config
.item_order
= cf
.get('item_order')
568 config
.main_window_size
= cf
.get('main_window_size')
569 config
.main_pane_position
= cf
.get('main_pane_position')
570 config
.sub_pane_position
= cf
.get('sub_pane_position')
571 config
.offline
= cf
.get('offline')
573 del cf
['poll_frequency']
574 del cf
['number_of_items_stored']
576 del cf
['main_window_size']
577 del cf
['main_pane_position']
578 del cf
['sub_pane_position']
582 f
= open(config
.straw_config_file
, "w")
585 cPickle
.dump(cf
, f
, True)
590 def create_gconf_persistence():
591 client
= gconf
.client_get_default()
592 client
.add_dir(GCONF_STRAW_ROOT
, gconf
.CLIENT_PRELOAD_ONELEVEL
)
593 return ConfigGConfPersistence(client
)
595 def create_pickle_persistence():
596 return ConfigPicklePersistence(Config
._straw
_config
_file
)
598 def create_instance():
599 # Add your GConf key here as well!
600 cp
= ConfigPersistence(
601 (create_gconf_persistence(),
602 (OPTION_LAST_POLL
, OPTION_ITEMS_STORED
, OPTION_ITEM_ORDER
, OPTION_BROWSER_CMD
,
603 OPTION_WINDOW_SIZE_W
, OPTION_WINDOW_SIZE_H
,
604 OPTION_MAIN_PANE_POS
, OPTION_SUB_PANE_POS
, OPTION_OFFLINE
,
605 OPTION_MAGNIFICATION
,
606 OPTION_POLL_FREQUENCY
, OPTION_WINDOW_MAX
, OPTION_PANE_LAYOUT
)),
607 (create_pickle_persistence(),
608 (OPTION_FEED_ID_SEQ
, OPTION_FEEDS
, OPTION_CATEGORIES
)))
610 config
.initialize_proxy()
611 config
.initialize_options()
612 convert_if_necessary(config
)
615 config_instance
= None
617 global config_instance
618 if not config_instance
:
619 config_instance
= create_instance()
620 return config_instance