Implement STRAW_HTML environment variable for engine selection
[straw.git] / straw / subscribe.py
blobb5dfa4cd55613858e8ea8d79a0f298ee6d21c549
1 """ subscribe.py
3 Module for handling feed subscription process.
4 """
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
11 version.
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. """
22 from gtk.glade import XML
23 from straw import helpers
24 import Config
25 import FeedManager
26 import MVP
27 import SummaryParser
28 import error
29 import gettext
30 import gobject
31 import gtk
32 import os, os.path
33 import pygtk
34 import straw
35 import time
36 import urllib
37 import urlparse
38 import xml
40 pygtk.require('2.0')
42 STATE_INTRO = 1
43 STATE_FINISH = 2
45 class SubscribeView(MVP.GladeView):
46 def _initialize(self):
47 self._window = self._widget.get_widget('subscribe_dialog')
48 self._button1 = self._widget.get_widget('button1')
49 self._button2 = self._widget.get_widget('button2')
50 self._button3 = self._widget.get_widget('button3')
51 self._progress_bar = self._widget.get_widget('progress_bar')
52 self._notebook = self._widget.get_widget('notebook')
53 self._location_entry = self._widget.get_widget('feed_location_entry')
54 self._username_entry = self._widget.get_widget('username_entry')
55 self._password_entry = self._widget.get_widget('password_entry')
56 self._category_tree = self._widget.get_widget('category_tree')
57 self._error_text = self._widget.get_widget('error_text')
58 self._error_box = self._widget.get_widget('error_box')
59 self._result_tree = self._widget.get_widget('result_tree')
61 self.category_pixbuf = gtk.Label().render_icon(gtk.STOCK_DIRECTORY, gtk.ICON_SIZE_MENU)
63 def _populate_tree(self, parent_id, parent = None):
64 if not self.nodes.has_key(parent_id):
65 return
67 for node in self.nodes[parent_id].children:
68 if node.type == "C":
69 treeiter = self.category_store.append(parent, [self.category_pixbuf, node.name, node])
71 if self.nodes.has_key(node.id):
72 self._populate_tree(node.id, treeiter)
74 def _on_forward(self):
75 location = self._location_entry.get_text()
76 if not location:
77 self.display_error(_("Feed Location must not be empty"))
78 return False
79 #location = "file:///home/ppawel/Desktop/test.rss"
80 username = self._username_entry.get_text()
81 username = username.strip()
82 password = self._password_entry.get_text()
83 password = password.strip()
85 config = Config.get_instance()
86 if config.offline:
87 self._window.hide()
88 response = helpers.report_offline_status(self._window)
89 if response == gtk.RESPONSE_CANCEL:
90 self.show()
91 return False
92 config.offline = not config.offline
94 import FeedDiscovery
95 FeedDiscovery.discover(location, [ { "job-done": [ self._discovery_finished ] } ])
97 #self._presenter.subscribe(location, username, password)
98 return True
100 def progress_pulse(self, obj):
101 self._progress_bar.pulse()
102 return True
104 def progress_destroy(self):
105 if self.timer != 0:
106 gobject.source_remove(self.timer)
107 self.timer = 0
109 def _setup_entries(self):
110 self._location_entry.set_text("http://")
111 self._username_entry.delete_text(0,-1)
112 self._password_entry.delete_text(0,-1)
114 self._location_entry.grab_focus()
116 def show(self):
117 self.state = STATE_INTRO
118 self.set_state(gtk.STOCK_CLOSE, gtk.STOCK_GO_FORWARD, None)
119 self.timer = 0
121 self._setup_entries()
122 self._error_box.set_property('visible',False)
123 self._notebook.set_current_page(0)
124 self._window.show()
126 self.store = gtk.ListStore(bool, str, gobject.TYPE_PYOBJECT)
128 renderer = gtk.CellRendererToggle()
129 renderer.set_property("activatable", True)
130 column = gtk.TreeViewColumn(_('Keep'), renderer, active = 0)
131 renderer.connect('toggled', self.col1_toggled_cb, self.store)
132 self._result_tree.append_column(column)
133 #renderer.connect('toggled', self._sticky_toggled)
135 renderer2 = gtk.CellRendererText()
136 column = gtk.TreeViewColumn(_('_Title'), renderer2, markup = 1)
138 self._result_tree.append_column(column)
139 self._result_tree.set_model(self.store)
141 column = gtk.TreeViewColumn()
143 status_renderer = gtk.CellRendererPixbuf()
144 column.pack_start(status_renderer, False)
145 column.set_attributes(status_renderer, pixbuf = 0)
147 title_renderer = gtk.CellRendererText()
148 column.pack_start(title_renderer, False)
149 column.set_attributes(title_renderer, markup = 1)
151 self._category_tree.append_column(column)
153 def col1_toggled_cb( self, cell, path, model ):
154 self.store[path][0] = not self.store[path][0]
156 def setup_category_tree(self):
157 self.category_store = gtk.TreeStore(gtk.gdk.Pixbuf, str, gobject.TYPE_PYOBJECT)
158 self.nodes = FeedManager.get_model()
160 self._category_tree.set_model(None)
161 self._category_tree.set_model(self.category_store)
163 treeiter = self.category_store.append(None, [self.category_pixbuf, "Main category", None])
165 self._populate_tree(1, parent = treeiter)
166 self._category_tree.expand_all()
168 def display_error(self, text):
169 self._error_box.set_property('visible',True)
170 self._error_text.set_text(text)
172 def set_location_entry(self, location):
173 self._location_entry.set_text(location)
175 def set_parent(self, parent):
176 self._window.set_transient_for(parent)
178 def _on_button1_clicked(self, *args):
179 self.progress_destroy()
180 self._window.hide()
182 def _on_button2_clicked(self, *args):
183 if self.state == STATE_INTRO:
184 if self._on_forward():
185 self._progress_bar.props.visible = True
186 self.timer = gobject.timeout_add(100, self.progress_pulse, self)
187 self._button2.set_sensitive(False)
188 elif self.state == STATE_FINISH:
189 self.state = STATE_INTRO
190 self.set_state(gtk.STOCK_CLOSE, gtk.STOCK_GO_FORWARD, None)
191 self._notebook.set_current_page(0)
193 def _on_button3_clicked(self, *args):
194 if self.state == STATE_FINISH:
195 new_feeds = []
197 # Obtain selected category.
199 parent = None
200 iter = self._category_tree.get_selection().get_selected()[1]
202 if iter != None:
203 path = self.category_store.get_path(iter)
204 parent = self.category_store[path][2]
206 # Save selected feeds.
208 for feed in [row[2] for row in self.store if row[0]]:
209 feed.parent = parent
210 new_feeds.append(feed)
212 FeedManager.save_all(new_feeds)
214 self._window.hide()
216 def _discovery_finished(self, handler, feeds):
217 gtk.gdk.threads_enter()
219 for feed in feeds:
220 label = xml.sax.saxutils.escape(feed.title) + '\n<span size="smaller">' + xml.sax.saxutils.escape(feed.location) + '</span>'
221 self.store.append([None, label, feed])
223 self._progress_bar.props.visible = False
224 self._button2.set_sensitive(True)
225 self.progress_destroy()
226 self.state = STATE_FINISH
227 self.set_state(gtk.STOCK_CLOSE, gtk.STOCK_GO_BACK, gtk.STOCK_APPLY)
228 self._notebook.set_current_page(1)
230 self.setup_category_tree()
232 gtk.gdk.threads_leave()
234 def set_state(self, b1, b2, b3):
235 if b1 == None:
236 self._button1.hide()
237 else:
238 self._button1.set_label(b1)
239 self._button1.show()
241 if b2 == None:
242 self._button2.hide()
243 else:
244 self._button2.set_label(b2)
245 self._button2.show()
247 if b3 == None:
248 self._button3.hide()
249 else:
250 self._button3.set_label(b3)
251 self._button3.show()
253 def _on_feed_location_entry_key_press_event(self, widget, event):
254 if event.keyval == gtk.keysyms.Return:
255 self._presenter.subscribe(self._location_entry.get_text())
257 class SubscribePresenter(MVP.BasicPresenter):
259 def _normalize(self, url):
260 u = urlparse.urlsplit(url.strip())
261 # we check if 'scheme' is not empty here because URIs like
262 # "google.com" IS valid but, in this case, 'scheme' is empty because
263 # urlsplit() expects urls are in the format of scheme://netloc/...
264 if not u[0] or (u[0] != "http" and u[0] != "feed"):
265 return None
266 if u[0] == 'feed':
267 u = urlparse.urlsplit(u[2])
268 # .. if that happens then we munge the url by adding a // and assume
269 # that 'http' is the scheme.
270 if u[1] == '':
271 u = urlparse.urlsplit("//" + url, 'http')
272 return u
274 def subscribe(self, location, username=None, password=None):
275 self._username = username
276 self._password = password
277 self._location = location
279 import FeedManager
280 import model
282 feed = model.Feed()
283 feed.location = location
284 feed.title = location
285 FeedManager.save_feed(feed)
287 def set_parent(self, parent):
288 self.view.set_parent(parent)
290 def get_credential(self):
291 return (self._location, self._username, self._password)
293 def show(self, url=None):
294 if url:
295 self.view.set_location_entry(url)
296 self.view.show()
298 # def set_url_dnd(self, url):
299 # self._window.show()
300 # self._feed_location_presenter.view.find_feed(url)
302 def set_location_from_clipboard(self):
303 def _clipboard_cb(cboard, url, data=None):
304 if url:
305 url = self._normalize(url)
306 if url and url[0] == "http":
307 url = urlparse.urlunsplit(url)
308 self.view.set_location_entry(url)
309 clipboard = gtk.clipboard_get(selection="CLIPBOARD")
310 clipboard.request_text(_clipboard_cb, None)
312 def subscribe_show(url=None, parent=None):
313 gladefile = XML(os.path.join(straw.STRAW_DATA_DIR, "subscribe.glade"))
314 _dialog = SubscribePresenter(view=SubscribeView(gladefile))
315 _dialog.set_parent(parent)
316 _dialog.set_location_from_clipboard()
317 _dialog.show()