3 Module for handling feed subscription process.
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 from Constants
import *
22 from gtk
.glade
import XML
45 class SubscribeView(MVP
.GladeView
):
46 def _initialize(self
):
47 self
._window
= self
._widget
.get_widget('subscribe_dialog')
48 self
._button
1 = self
._widget
.get_widget('button1')
49 self
._button
2 = self
._widget
.get_widget('button2')
50 self
._button
3 = 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
._result
_size
_label
= self
._widget
.get_widget('result_size_label')
62 self
._result
_size
_text
= self
._result
_size
_label
.get_text()
64 self
.category_pixbuf
= gtk
.Label().render_icon(gtk
.STOCK_DIRECTORY
, gtk
.ICON_SIZE_MENU
)
68 def _populate_tree(self
, parent_id
, parent
= None):
69 if not self
.nodes
.has_key(parent_id
):
72 for node
in self
.nodes
[parent_id
].children
:
74 treeiter
= self
.category_store
.append(parent
, [self
.category_pixbuf
, helpers
.pango_escape(node
.name
), node
])
76 if self
.nodes
.has_key(node
.id):
77 self
._populate
_tree
(node
.id, treeiter
)
79 def _on_forward(self
):
80 location
= self
._location
_entry
.get_text()
82 self
.display_error(_("Feed Location must not be empty"))
84 #location = "file:///home/ppawel/Desktop/test.rss"
85 username
= self
._username
_entry
.get_text()
86 username
= username
.strip()
87 password
= self
._password
_entry
.get_text()
88 password
= password
.strip()
90 if Config
.get(OPTION_OFFLINE
):
92 response
= helpers
.report_offline_status(self
._window
)
94 if response
== gtk
.RESPONSE_CANCEL
:
98 Config
.set(OPTION_OFFLINE
, True)
102 if username
or password
:
103 credentials
= username
, password
, ""
107 observers
= [ { "job-done": [ self
._discovery
_finished
],
108 "feed-discovered": [ self
._on
_feed
_discovered
] } ]
110 FeedDiscovery
.discover(location
, credentials
, observers
)
114 def progress_pulse(self
, obj
):
115 self
._progress
_bar
.pulse()
118 def progress_destroy(self
):
120 gobject
.source_remove(self
.timer
)
123 def _setup_entries(self
):
124 self
._location
_entry
.set_text("http://")
125 self
._username
_entry
.delete_text(0,-1)
126 self
._password
_entry
.delete_text(0,-1)
128 self
._location
_entry
.grab_focus()
131 self
.state
= STATE_INTRO
132 self
.set_state(gtk
.STOCK_CLOSE
, gtk
.STOCK_GO_FORWARD
, None)
135 self
._setup
_entries
()
136 self
._error
_box
.set_property('visible',False)
137 self
._notebook
.set_current_page(0)
140 self
.store
= gtk
.ListStore(bool, str, gobject
.TYPE_PYOBJECT
)
142 renderer
= gtk
.CellRendererToggle()
143 renderer
.set_property("activatable", True)
144 column
= gtk
.TreeViewColumn(_('Keep'), renderer
, active
= 0)
145 renderer
.connect('toggled', self
.col1_toggled_cb
, self
.store
)
146 self
._result
_tree
.append_column(column
)
148 renderer2
= gtk
.CellRendererText()
149 column
= gtk
.TreeViewColumn(_('_Title'), renderer2
, markup
= 1)
151 self
._result
_tree
.append_column(column
)
152 self
._result
_tree
.set_model(self
.store
)
154 column
= gtk
.TreeViewColumn()
156 status_renderer
= gtk
.CellRendererPixbuf()
157 column
.pack_start(status_renderer
, False)
158 column
.set_attributes(status_renderer
, pixbuf
= 0)
160 title_renderer
= gtk
.CellRendererText()
161 column
.pack_start(title_renderer
, False)
162 column
.set_attributes(title_renderer
, markup
= 1)
164 self
._category
_tree
.append_column(column
)
166 def col1_toggled_cb(self
, cell
, path
, model
):
167 self
.store
[path
][0] = not self
.store
[path
][0]
169 # Enable "Apply" button when at least one discovered feed is selected.
171 self
._button
3.set_sensitive(bool(sum([feed
[0] for feed
in self
.store
])))
173 def setup_category_tree(self
):
174 self
.category_store
= gtk
.TreeStore(gtk
.gdk
.Pixbuf
, str, gobject
.TYPE_PYOBJECT
)
175 self
.nodes
= FeedManager
.get_model()
177 self
._category
_tree
.set_model(None)
178 self
._category
_tree
.set_model(self
.category_store
)
180 treeiter
= self
.category_store
.append(None, [self
.category_pixbuf
, "Main category", None])
182 self
._populate
_tree
(1, parent
= treeiter
)
183 self
._category
_tree
.expand_all()
185 selection
= self
._category
_tree
.get_selection()
186 selection
.select_path("0")
188 def display_error(self
, text
):
189 self
._error
_box
.set_property('visible',True)
190 self
._error
_text
.set_text(text
)
192 def set_location_entry(self
, location
):
193 self
._location
_entry
.set_text(location
)
195 def set_parent(self
, parent
):
196 self
._window
.set_transient_for(parent
)
198 def _on_button1_clicked(self
, *args
):
199 self
.progress_destroy()
202 def _on_button2_clicked(self
, *args
):
203 if self
.state
== STATE_INTRO
:
204 if self
._on
_forward
():
205 self
._progress
_bar
.props
.visible
= True
206 self
.timer
= gobject
.timeout_add(100, self
.progress_pulse
, self
)
207 self
._button
2.set_sensitive(False)
208 elif self
.state
== STATE_FINISH
:
209 self
.state
= STATE_INTRO
210 self
.set_state(gtk
.STOCK_CLOSE
, gtk
.STOCK_GO_FORWARD
, None)
211 self
._notebook
.set_current_page(0)
213 def _on_button3_clicked(self
, *args
):
214 if self
.state
== STATE_FINISH
:
217 # Obtain selected category.
220 iter = self
._category
_tree
.get_selection().get_selected()[1]
223 path
= self
.category_store
.get_path(iter)
224 parent
= self
.category_store
[path
][2]
226 # Save selected feeds.
228 for feed
in [row
[2] for row
in self
.store
if row
[0]]:
230 new_feeds
.append(feed
)
232 FeedManager
.save_all(new_feeds
)
236 def _on_feed_discovered(self
, handler
, feed
):
237 self
.feeds
.append(feed
)
239 def _discovery_finished(self
, handler
, task_result
):
240 gtk
.gdk
.threads_enter()
243 self
._result
_size
_label
.set_text(self
._result
_size
_text
% len(feeds
))
246 label
= helpers
.pango_escape(feed
.title
) + '\n<span size="smaller">' + \
247 helpers
.pango_escape(feed
.location
) + '</span>'
249 self
.store
.append([None, label
, feed
])
251 self
._progress
_bar
.props
.visible
= False
252 self
._button
2.set_sensitive(True)
253 self
._button
3.set_sensitive(False)
254 self
.progress_destroy()
255 self
.state
= STATE_FINISH
256 self
.set_state(gtk
.STOCK_CLOSE
, gtk
.STOCK_GO_BACK
, gtk
.STOCK_APPLY
)
257 self
._notebook
.set_current_page(1)
259 self
.setup_category_tree()
261 gtk
.gdk
.threads_leave()
263 def set_state(self
, b1
, b2
, b3
):
267 self
._button
1.set_label(b1
)
273 self
._button
2.set_label(b2
)
279 self
._button
3.set_label(b3
)
282 def _on_feed_location_entry_key_press_event(self
, widget
, event
):
283 if event
.keyval
== gtk
.keysyms
.Return
:
284 self
._presenter
.subscribe(self
._location
_entry
.get_text())
286 class SubscribePresenter(MVP
.BasicPresenter
):
288 def _normalize(self
, url
):
289 u
= urlparse
.urlsplit(url
.strip())
290 # we check if 'scheme' is not empty here because URIs like
291 # "google.com" IS valid but, in this case, 'scheme' is empty because
292 # urlsplit() expects urls are in the format of scheme://netloc/...
293 if not u
[0] or (u
[0] != "http" and u
[0] != "feed"):
296 u
= urlparse
.urlsplit(u
[2])
297 # .. if that happens then we munge the url by adding a // and assume
298 # that 'http' is the scheme.
300 u
= urlparse
.urlsplit("//" + url
, 'http')
303 def subscribe(self
, location
, username
=None, password
=None):
304 self
._username
= username
305 self
._password
= password
306 self
._location
= location
312 feed
.location
= location
313 feed
.title
= location
314 FeedManager
.save_feed(feed
)
316 def set_parent(self
, parent
):
317 self
.view
.set_parent(parent
)
319 def get_credential(self
):
320 return (self
._location
, self
._username
, self
._password
)
322 def show(self
, url
=None):
324 self
.view
.set_location_entry(url
)
327 # def set_url_dnd(self, url):
328 # self._window.show()
329 # self._feed_location_presenter.view.find_feed(url)
331 def set_location_from_clipboard(self
):
332 def _clipboard_cb(cboard
, url
, data
=None):
334 url
= self
._normalize
(url
)
335 if url
and url
[0] == "http":
336 url
= urlparse
.urlunsplit(url
)
337 self
.view
.set_location_entry(url
)
338 clipboard
= gtk
.clipboard_get(selection
="CLIPBOARD")
339 clipboard
.request_text(_clipboard_cb
, None)
341 def subscribe_show(url
=None, parent
=None):
342 gladefile
= XML(os
.path
.join(straw
.defs
.STRAW_DATA_DIR
, "subscribe.glade"))
343 _dialog
= SubscribePresenter(view
=SubscribeView(gladefile
))
344 _dialog
.set_parent(parent
)
345 _dialog
.set_location_from_clipboard()