redesigned feed properties window - first cut
[straw.git] / src / lib / FeedPropertiesDialog.py
blobedebe437347e76b2ddd4a4bbfd907ff69f258312
1 """ FeedPropertiesDialog.py
3 Provides a module for handling the properties of a feed
4 """
5 __copyright__ = "Copyright (c) 2002-2005 Free Software Foundation, Inc."
6 __license__ = """
7 Straw is free software; you can redistribute it and/or modify it under the
8 terms of the GNU General Public License as published by the Free Software
9 Foundation; either version 2 of the License, or (at your option) any later
10 version.
12 Straw is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License along with
17 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 Place - Suite 330, Boston, MA 02111-1307, USA. """
21 from error import log, logparam, logtb
22 from xml.sax import saxutils
23 import pygtk
24 pygtk.require('2.0')
25 import gtk
26 import gobject
27 import time
28 import utils
29 import Feed
30 import Config
31 import FeedCategoryList
32 import SummaryParser
33 import gettext
34 import MVP
36 class FeedPropertyView(MVP.GladeView):
38 COLUMN_NAME = 0
39 COLUMN_MEMBER = 1
40 COLUMN_OBJECT = 2
42 def _initialize(self):
43 self._feed = None
44 self._defaults = {'title':'','url':'','username':'','password':''}
45 self._window = self._widget.get_widget('feed_properties')
46 self._title = self._widget.get_widget('properties_title_entry')
47 self._location = self._widget.get_widget('properties_location_entry')
48 self._username = self._widget.get_widget('properties_username_entry')
49 self._password = self._widget.get_widget('properties_password_entry')
50 self._username_title = self._widget.get_widget('properties_username_label')
51 self._password_title = self._widget.get_widget('properties_password_label')
52 self._restore_button = self._widget.get_widget('properties_reset_button')
53 self._refresh_spin = self._widget.get_widget('properties_refresh_spin')
54 self._articles_spin = self._widget.get_widget('properties_articles_spin')
55 self._refresh_default_check = self._widget.get_widget('properties_keep_refresh_default')
56 self._articles_default_check = self._widget.get_widget('properties_keep_articles_default')
57 self._next_refresh_label = self._widget.get_widget('properties_next_refresh_label')
58 self._previous_refresh_label = self._widget.get_widget('properties_previous_refresh_label')
59 self._categories_treeview = self._widget.get_widget('feed_categories_treeview')
60 self._feed_info_href_container = self._widget.get_widget('feed_info_href_container')
61 self._feed_info_description = self._widget.get_widget('feed_info_description')
62 self._feed_info_copyright = self._widget.get_widget('feed_info_copyright')
63 self._feed_info_href = self._widget.get_widget('properties_link')
65 model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN,
66 gobject.TYPE_PYOBJECT)
67 self._categories_treeview.set_model(model)
68 self._categories_treeview.set_rules_hint(True)
70 r = gtk.CellRendererToggle()
71 r.connect('toggled', self.member_toggled)
72 column = gtk.TreeViewColumn(_('Member'), r, active=self.COLUMN_MEMBER)
73 self._categories_treeview.append_column(column)
75 r = gtk.CellRendererText()
76 column = gtk.TreeViewColumn(_('Name'), r, text=self.COLUMN_NAME)
77 self._categories_treeview.append_column(column)
79 def set_feed(self, feed):
80 self._feed = feed
82 def set_parent(self, parent):
83 self._window.set_transient_for(parent)
85 def member_toggled(self, cell, path):
86 model = self._categories_treeview.get_model()
87 category = model[path][self.COLUMN_OBJECT]
88 if self._feed in category.feeds:
89 category.remove_feed(self._feed)
90 model[path][self.COLUMN_MEMBER] = False
91 else:
92 category.append_feed(self._feed, False)
93 model[path][self.COLUMN_MEMBER] = True
95 def show(self):
96 self._initializing_window = True
97 self.display_properties()
98 self._window.show()
99 self._initializing_window = False
101 def hide(self, *args):
102 self._presenter.activate_deleter()
103 self._window.hide()
104 self._window.destroy()
106 def _on_feed_properties_delete_event(self, *args):
107 self.hide()
108 return True
110 def _on_properties_close_button_clicked(self, *args):
111 self.hide()
112 return
114 def display_properties(self):
115 self._defaults['title'] = self._feed.title
116 loc, un, pw = self._feed.access_info
117 self._defaults['url'] = loc
118 self._defaults['username'] = un
119 self._defaults['password'] = pw
120 self._defaults['frequency'] = self._feed.poll_frequency
121 self._defaults['stored'] = self._feed.number_of_items_stored
122 self._window.set_title(_("%s Properties") % self._feed.title)
123 self._title.set_text(self._feed.title)
124 self._location.set_text(self._feed.access_info[0])
125 config = Config.get_instance()
126 if self._feed.poll_frequency == Feed.Feed.DEFAULT:
127 freq = config.poll_frequency
128 fdefault = True
129 self._refresh_keep = True
130 else:
131 freq = self._feed.poll_frequency
132 fdefault = False
133 self._refresh_keep = False
134 self._refresh_spin.set_value(float(freq / 60))
135 self._refresh_spin.set_sensitive(not fdefault)
136 self._refresh_default_check.set_active(fdefault)
137 if self._feed.number_of_items_stored == Feed.Feed.DEFAULT:
138 nitems = config.number_of_items_stored
139 nidefault = True
140 self._articles_keep = True
141 else:
142 nitems = self._feed.number_of_items_stored
143 nidefault = False
144 self._articles_keep = False
145 self._articles_spin.set_value(float(nitems))
146 self._articles_spin.set_sensitive(not nidefault)
147 self._articles_default_check.set_active(nidefault)
149 if un:
150 self._username.set_text(un)
151 self._password.set_text(pw)
153 model = self._categories_treeview.get_model()
154 model.clear()
155 fclist = FeedCategoryList.get_instance()
156 for c in fclist.user_categories:
157 iter = model.append()
158 model.set(iter,
159 self.COLUMN_NAME, c.title,
160 self.COLUMN_MEMBER, self._feed in c.feeds,
161 self.COLUMN_OBJECT, c)
163 self._previous_refresh_label.set_text(
164 utils.format_date(time.gmtime(self._feed.last_poll)))
165 next = self._feed.next_refresh
166 if next is not None:
167 self._next_refresh_label.set_text(
168 utils.format_date(time.gmtime(next)))
169 self._next_refresh_label.show()
170 else:
171 self._next_refresh_label.hide()
173 self._display_feed_information(self._feed)
174 self._restore_button.set_sensitive(False)
176 def _display_feed_information(self, feed):
177 title = utils.convert_entities(feed.channel_title)
178 if len(title) == 0:
179 title = feed.title
180 if len(title) == 0:
181 title = feed.channel_link
182 title = title.strip()
184 link = feed.channel_link.strip()
185 if len(link) < 1: link = feed.location
187 if len(title) > 0:
188 #self._feed_info_href.set_text(saxutils.escape(title))
189 self._feed_info_href.set_uri(link)
190 self._feed_info_href.set_sensitive(len(link) > 0)
191 self._feed_info_href.show()
192 else:
193 self._feed_info_href.hide()
195 description = utils.convert_entities(feed.channel_description.strip())
196 size = len(description)
197 if size and description != title:
198 description = read_text(description, size)
199 self._feed_info_description.set_text(utils.convert_entities(description))
200 self._feed_info_description.show()
201 else:
202 self._feed_info_description.hide()
204 copyright = utils.convert_entities(feed.channel_copyright)
205 self._feed_info_copyright.set_text(copyright)
206 if len(copyright):
207 self._feed_info_copyright.show()
208 else:
209 self._feed_info_copyright.hide()
211 def restore_defaults(self):
212 # FIXME: add frequency and number of items and the default flags
213 self._feed.title = self._defaults['title']
214 self._feed.access_info = (
215 self._defaults['url'], self._defaults['username'], self._defaults['password'])
216 self._title.set_text(self._feed.title)
217 self._location.set_text(self._feed.location)
218 self._username.set_text(self._defaults.get('username',''))
219 self._password.set_text(self._defaults.get('password',''))
220 self._refresh_default_check.set_active(self._refresh_keep)
221 if not self._refresh_keep:
222 self._refresh_spin.set_value(float(self._refresh_default))
223 self._articles_default_check.set_active(self._articles_keep)
224 if not self._articles_keep:
225 self._articles_spin.set_value(float(self._articles_default))
226 self._restore_button.set_sensitive(False)
228 def _on_properties_title_entry_insert_text(self, *args):
229 if self._initializing_window:
230 return
231 self._restore_button.set_sensitive(True)
233 def _on_properties_title_entry_delete_text(self, *args):
234 if self._initializing_window:
235 return
236 self._restore_button.set_sensitive(True)
238 def _on_properties_title_entry_focus_out_event(self, widget, *args):
239 self._feed.title = widget.get_text().strip()
241 def _on_properties_location_entry_insert_text(self, *args):
242 if self._initializing_window:
243 return
244 self._restore_button.set_sensitive(True)
246 def _on_properties_location_entry_delete_text(self, *args):
247 if self._initializing_window:
248 return
249 self._restore_button.set_sensitive(True)
251 def _on_properties_location_entry_focus_out_event(self, widget, *args):
252 loc, username, pw = self._feed.access_info
253 self._feed.access_info = (widget.get_text().strip(), username, pw)
255 def _on_properties_username_entry_insert_text(self, *args):
256 if self._initializing_window:
257 return
258 self._restore_button.set_sensitive(True)
260 def _on_properties_username_entry_delete_text(self, *args):
261 if self._initializing_window:
262 return
263 self._restore_button.set_sensitive(False)
265 def _on_properties_username_entry_focus_out_event(self, widget, *args):
266 self._presenter.set_username(widget.get_text().strip())
268 def _on_properties_password_entry_insert_text(self, *args):
269 if self._initializing_window:
270 return
271 self._restore_button.set_sensitive(True)
273 def _on_properties_password_entry_delete_text(self, *args):
274 if self._initializing_window:
275 return
276 self._restore_button.set_sensitive(False)
278 def _on_properties_password_entry_focus_out_event(self, widget, *args):
279 self._presenter.set_password(widget.get_text().strip())
281 def _on_properties_reset_button_clicked(self, *args):
282 self.restore_defaults()
284 def _on_properties_refresh_spin_focus_out_event(self, widget, *args):
285 widget.update()
286 value = widget.get_value_as_int()
287 self._presenter.set_poll_frequency(value)
289 def _on_properties_articles_spin_focus_out_event(self, widget, *args):
290 widget.update()
291 value = widget.get_value_as_int()
292 self._presenter.set_items_stored(value)
294 def _on_properties_articles_spin_value_changed(self, widget, *args):
295 if self._initializing_window:
296 return
297 self._restore_button.set_sensitive(True)
299 def _on_properties_refresh_spin_value_changed(self, widget, *args):
300 if self._initializing_window:
301 return
302 self._restore_button.set_sensitive(True)
304 def _on_properties_keep_refresh_default_toggled(self, widget, *args):
305 if self._initializing_window:
306 return
307 isactive = widget.get_active()
308 if isactive:
309 self._presenter.set_poll_frequency(Feed.Feed.DEFAULT)
310 self._refresh_spin.set_value(float(Config.get_instance().poll_frequency / 60))
311 else:
312 self._presenter.set_poll_frequency(self._refresh_spin.get_value_as_int() * 60)
314 self._refresh_spin.set_sensitive(isactive)
315 self._restore_button.set_sensitive(True)
317 def _on_properties_keep_articles_default_toggled(self, widget, *args):
318 if self._initializing_window:
319 return
320 isactive = widget.get_active()
321 if isactive:
322 self._presenter.set_items_stored(Feed.Feed.DEFAULT)
323 self._articles_spin.set_value(float(Config.get_instance().number_of_items_stored))
324 else:
325 self._presenter.set_items_stored(self._articles_spin.get_value_as_int())
326 self._articles_spin.set_sensitive(isactive)
327 self._restore_button.set_sensitive(True)
330 class FeedPropertyPresenter(MVP.BasicPresenter):
332 TIME_INTERVAL = 60
334 def _initialize(self):
335 self._feed = None
336 self._deleter = None
338 def set_deleter(self, deleter):
339 self._deleter = deleter
341 def activate_deleter(self):
342 if self._deleter:
343 self._deleter()
345 def set_feed(self, feed):
346 self._feed = feed
347 self._view.set_feed(self._feed)
349 def set_username(self, username):
350 loc, uname, pw = self._feed.access_info
351 self._feed.access_info = (loc, username, pw)
353 def set_password(self, password):
354 loc, uname, pw = self._feed.access_info
355 self._feed.access_info = (loc, uname, password)
357 def set_poll_frequency(self, pf):
358 if pf != self._feed.poll_frequency:
359 self._feed.poll_frequency = value * TIME_INTERVAL
361 def set_items_stored(self, nitems):
362 if nitems != self._feed.number_of_items_stored:
363 self._feed.number_of_items_stored = nitems
365 dialogs = {}
366 def show_feed_properties(parent, feed):
367 global dialogs
368 class _dialogdeleter:
369 def __init__(self, feed, hash):
370 self.feed = feed
371 self.hash = hash
373 def __call__(self):
374 del self.hash[feed]
376 dialog = dialogs.get(feed, None)
377 if not dialog:
378 widget_tree = MVP.get_widget_tree('feed_properties')
379 dialog = FeedPropertyPresenter(view=FeedPropertyView(widget_tree))
380 dialog.view.set_parent(parent)
381 dialog.set_feed(feed)
382 dialog.set_deleter(_dialogdeleter(feed, dialogs))
383 dialogs[feed] = dialog
384 dialog.view.show()
385 return
387 def read_text(fragment, chars):
388 """Read chars cdata characters from html fragment fragment"""
389 parser = SummaryParser.TitleImgParser()
390 parser.feed(fragment)
391 text = parser.get_text(chars)
392 parser.close()
393 return text