1 |
# |
2 |
# This file is part of Sargasso, http://zamez.org/sargasso |
3 |
# Licensed under the GNU General Public License, |
4 |
# http://www.opensource.org/licenses/gpl-license |
5 |
# Copyright 2005 James Bursa <james@semichrome.net> |
6 |
# |
7 |
|
8 |
import os |
9 |
import urlparse |
10 |
import wimp |
11 |
import sargasso |
12 |
import swi |
13 |
import rufl |
14 |
|
15 |
|
16 |
# styles for text: font family, style, size, text colour, background colour |
17 |
style_feed_title = ('Homerton', rufl.regular, 200, 0x000000, 0xffaa99) |
18 |
style_feed_title_new = ('Homerton', rufl.bold, 200, 0x000000, 0xffaa99) |
19 |
style_feed_tagline = ('NewHall', rufl.regular, 200, 0x000000, None) |
20 |
style_feed_error = ('NewHall', rufl.regular, 200, 0x0000a0, None) |
21 |
style_feed_date = ('NewHall', rufl.slanted, 160, 0x0060ee, None) |
22 |
style_feed_type = ('NewHall', rufl.slanted, 160, 0x666666, None) |
23 |
style_entry_title = ('Homerton', rufl.regular, 200, 0x000000, 0xa0dddd) |
24 |
style_entry_title_new = ('Homerton', rufl.bold, 200, 0x000000, 0xa0dddd) |
25 |
style_entry_author = ('NewHall', rufl.slanted, 160, 0x666666, None) |
26 |
style_entry_date = ('NewHall', rufl.slanted, 160, 0x0060ee, None) |
27 |
style_entry_summary = ('NewHall', rufl.regular, 180, 0x000000, None) |
28 |
|
29 |
|
30 |
feed_type = { |
31 |
'rss090': 'RSS 0.90', |
32 |
'rss091n': 'Netscape RSS 0.91', |
33 |
'rss091u': 'Userland RSS 0.91', |
34 |
'rss10': 'RSS 1.0', |
35 |
'rss092': 'RSS 0.92', |
36 |
'rss093': 'RSS 0.93', |
37 |
'rss094': 'RSS 0.94', |
38 |
'rss20': 'RSS 2.0', |
39 |
'rss': 'RSS', |
40 |
'atom01': 'Atom 0.1', |
41 |
'atom02': 'Atom 0.2', |
42 |
'atom03': 'Atom 0.3', |
43 |
'atom': 'Atom', |
44 |
'cdf': 'CDF', |
45 |
'hotrss': 'Hot RSS', |
46 |
'': 'Unknown', |
47 |
} |
48 |
|
49 |
|
50 |
default_feeds = ( |
51 |
'http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml', |
52 |
'http://today.reuters.co.uk/rss/topNews/', |
53 |
'http://news.ft.com/rss/home/uk', |
54 |
'http://www.drobe.co.uk/rss.php', |
55 |
'http://www.iconbar.com/cgi-bin/iconbar.rss', |
56 |
'http://www.theregister.co.uk/feeds/latest.rdf', |
57 |
'http://www.snackspot.org.uk/rss/rss.xml', |
58 |
) |
59 |
|
60 |
|
61 |
def init(): |
62 |
"Initialise program specific stuff" |
63 |
global main_window, add_feed_box |
64 |
wimp.hourglass_on() |
65 |
iconbar_init() |
66 |
main_window = MainWindow('main') |
67 |
add_feed_box = AddFeedBox('add_feed') |
68 |
wimp.poll_mask = 0 |
69 |
wimp.null_reason_handler = null_reason_handler |
70 |
load_choices() |
71 |
if sargasso.feeds == []: |
72 |
for url in default_feeds: |
73 |
sargasso.add_feed(url) |
74 |
for feed in sargasso.feeds: |
75 |
feed.last_status = feed.status |
76 |
feed.window = wimp.window('main') |
77 |
make_main_window() |
78 |
main_window.open() |
79 |
wimp.hourglass_off() |
80 |
|
81 |
|
82 |
def quit(): |
83 |
"Tidy up before quitting" |
84 |
pass |
85 |
|
86 |
|
87 |
def iconbar_init(): |
88 |
"Setup iconbar icon" |
89 |
global window_info, menu_iconbar |
90 |
wimp.iconbar_icon('!sargasso', iconbar_click) |
91 |
window_info = wimp.info_window() |
92 |
menu_iconbar = wimp.menu('iconbar', |
93 |
('task', ('info', window_info), 'update', 'quit')) |
94 |
|
95 |
|
96 |
def null_reason_handler(): |
97 |
sargasso.poll() |
98 |
remake = False |
99 |
for feed in sargasso.feeds: |
100 |
if feed.last_status != feed.status: |
101 |
remake = True |
102 |
feed.last_status = feed.status |
103 |
if feed.window.is_open: |
104 |
make_feed_window(feed) |
105 |
feed.window.refresh(0, 0, 900, 10000) |
106 |
if remake: |
107 |
make_main_window() |
108 |
main_window.refresh(0, 0, 1000, 10000) |
109 |
|
110 |
|
111 |
def iconbar_click(buttons, x): |
112 |
"Handle click on iconbar" |
113 |
if buttons == 2: |
114 |
reopen = 1 |
115 |
while reopen: |
116 |
selection, reopen = menu_iconbar.menu(x - 64, 96 + 44 * 3) |
117 |
if selection is None: |
118 |
pass |
119 |
elif selection[0] == 0: |
120 |
window_info.open_as_menu() |
121 |
reopen = 0 |
122 |
elif selection[0] == 1: |
123 |
for feed in sargasso.feeds: |
124 |
feed.update() |
125 |
elif selection[0] == 2: |
126 |
wimp.quit() |
127 |
elif buttons in (1, 4): |
128 |
main_window.open_full_size(-1) |
129 |
|
130 |
|
131 |
def make_main_window(): |
132 |
main_window.contents = [] |
133 |
y = 0 |
134 |
for feed in sargasso.feeds: |
135 |
title = try_detail(feed.feed, 'title_detail') |
136 |
if title == '': |
137 |
title = feed.url |
138 |
if len(feed.new_entries): |
139 |
title_style = style_feed_title_new |
140 |
else: |
141 |
title_style = style_feed_title |
142 |
title = MainWindowIcon(main_window, 0, y, 700, |
143 |
title_style[0], title_style[1], |
144 |
title_style[2], title, |
145 |
title_style[3], title_style[4]) |
146 |
title.feed = feed |
147 |
main_window.add_content(title) |
148 |
|
149 |
status = '' |
150 |
if feed.status == sargasso.STATUS_DONE: |
151 |
if len(feed.new_entries): |
152 |
status = '%i items (%i new)' % (len(feed.entries), |
153 |
len(feed.new_entries)) |
154 |
else: |
155 |
status = '%i items' % (len(feed.entries)) |
156 |
elif feed.status == sargasso.STATUS_ERROR: |
157 |
status = 'Failed' |
158 |
elif feed.status in (sargasso.STATUS_IDLE, sargasso.STATUS_FETCHING): |
159 |
status = 'Fetching' |
160 |
status = MainWindowIcon(main_window, 700, y, 1000, |
161 |
title_style[0], title_style[1], |
162 |
title_style[2], status, |
163 |
title_style[3], title_style[4]) |
164 |
status.feed = feed |
165 |
main_window.add_content(status) |
166 |
|
167 |
status.pos[3] = title.pos[3] |
168 |
y = title.y1 |
169 |
|
170 |
if feed.status == sargasso.STATUS_ERROR: |
171 |
summary = MainWindowIcon(main_window, 0, y, 1000, |
172 |
style_feed_error[0], style_feed_error[1], |
173 |
style_feed_error[2], feed.error, |
174 |
style_feed_error[3], style_feed_error[4]) |
175 |
else: |
176 |
summary = MainWindowIcon(main_window, 0, y, 1000, |
177 |
style_feed_tagline[0], style_feed_tagline[1], |
178 |
style_feed_tagline[2], |
179 |
try_detail(feed.feed, 'tagline_detail'), |
180 |
style_feed_tagline[3], style_feed_tagline[4]) |
181 |
summary.feed = feed |
182 |
main_window.add_content(summary) |
183 |
|
184 |
y = summary.y1 + 10 |
185 |
main_window.set_extent(0, 0, 1000, y) |
186 |
if main_window.is_open: |
187 |
main_window.open_full_size() |
188 |
|
189 |
|
190 |
class MainWindow(wimp.window): |
191 |
|
192 |
def click(self, icon, buttons, x, y, sx, sy): |
193 |
self.sx = sx |
194 |
self.sy = sy |
195 |
if wimp.window.click(self, icon, buttons, x, y, sx, sy): |
196 |
return True |
197 |
if buttons == 2: |
198 |
menu = wimp.menu('menu', |
199 |
('task', 'add_feed', '-remove_feed', 'update')) |
200 |
reopen = 1 |
201 |
while reopen: |
202 |
selection, reopen = menu.menu(sx - 64, sy) |
203 |
if selection is None: |
204 |
pass |
205 |
elif selection[0] == 0: |
206 |
add_feed_box.icon_text(0, 'http://') |
207 |
add_feed_box.open_centred() |
208 |
add_feed_box.put_caret(0) |
209 |
elif selection[0] == 2: |
210 |
for feed in sargasso.feeds: |
211 |
feed.update() |
212 |
return True |
213 |
|
214 |
|
215 |
class MainWindowIcon(wimp.paragraph): |
216 |
|
217 |
def click(self, buttons, x, y): |
218 |
feed = self.feed |
219 |
if buttons == 2: |
220 |
title = try_detail(feed.feed, 'title_detail') |
221 |
if title == '': |
222 |
title = feed.url |
223 |
title = title.encode('latin1', 'replace') |
224 |
if 30 < len(title): |
225 |
title = title[:30] + '...' |
226 |
else: |
227 |
title = title |
228 |
menu = wimp.menu('menu', |
229 |
('task', 'add_feed', |
230 |
wimp.lookup('remove_feed_x') % title, |
231 |
'update')) |
232 |
reopen = 1 |
233 |
while reopen: |
234 |
selection, reopen = menu.menu(self.window.sx - 64, |
235 |
self.window.sy) |
236 |
if selection is None: |
237 |
pass |
238 |
elif selection[0] == 0: |
239 |
add_feed_box.icon_text(0, 'http://') |
240 |
add_feed_box.open_centred() |
241 |
add_feed_box.put_caret(0) |
242 |
elif selection[0] == 1: |
243 |
if feed.window: |
244 |
feed.window.remove() |
245 |
sargasso.remove_feed(feed.url) |
246 |
make_main_window() |
247 |
main_window.refresh(0, 0, 1000, 10000) |
248 |
save_choices() |
249 |
elif selection[0] == 2: |
250 |
for feed in sargasso.feeds: |
251 |
feed.update() |
252 |
return True |
253 |
if feed.status == sargasso.STATUS_DONE: |
254 |
if feed.window.is_open: |
255 |
feed.window.open_centred(-1) |
256 |
else: |
257 |
make_feed_window(feed) |
258 |
feed.window.open_centred(-1) |
259 |
if feed.new_entries: |
260 |
feed.new_entries = [] |
261 |
make_main_window() |
262 |
main_window.refresh(0, 0, 1000, 10000) |
263 |
return True |
264 |
|
265 |
|
266 |
def make_feed_window(feed): |
267 |
w = feed.window |
268 |
w.contents = [] |
269 |
w.y1 = 0 |
270 |
link = feed.feed.get('link', '') |
271 |
title = try_detail(feed.feed, 'title_detail') |
272 |
tagline = try_detail(feed.feed, 'tagline_detail') |
273 |
modified = feed.feed.get('modified', '') |
274 |
type = feed_type[feed.version] |
275 |
add_para(w, style_feed_title, title, link) |
276 |
add_para(w, style_feed_tagline, tagline, link) |
277 |
add_para(w, style_feed_date, modified, link) |
278 |
add_para(w, style_feed_type, type, link) |
279 |
for entry in feed.entries: |
280 |
link = entry.get('link', '') |
281 |
entry_title = try_detail(entry, 'title_detail') |
282 |
entry_author = entry.get('author', '') |
283 |
entry_modified = entry.get('modified', '') |
284 |
entry_summary = try_detail(entry, 'summary_detail') |
285 |
if entry in feed.new_entries: |
286 |
add_para(w, style_entry_title_new, entry_title, link) |
287 |
else: |
288 |
add_para(w, style_entry_title, entry_title, link) |
289 |
add_para(w, style_entry_author, entry_author, link) |
290 |
add_para(w, style_entry_date, entry_modified, link) |
291 |
add_para(w, style_entry_summary, entry_summary, link) |
292 |
if 'content' in entry: |
293 |
for content in entry.content: |
294 |
value = read_detail(content) |
295 |
add_para(w, style_entry_summary, value, link) |
296 |
w.set_extent(0, 0, 900, w.y1) |
297 |
w.title(title[:98]) |
298 |
|
299 |
|
300 |
def add_para(w, style, text, url): |
301 |
if text == '': |
302 |
return |
303 |
icon = FeedWindowIcon(w, 0, w.y1, 900, style[0], style[1], |
304 |
style[2], text, style[3], style[4]) |
305 |
w.add_content(icon) |
306 |
icon.url = url |
307 |
w.y1 = icon.y1 |
308 |
|
309 |
|
310 |
class FeedWindowIcon(wimp.paragraph): |
311 |
|
312 |
def click(self, buttons, x, y): |
313 |
if buttons == 2: |
314 |
return False |
315 |
swi.swi('URI_Dispatch', '0s0', self.url) |
316 |
return True |
317 |
|
318 |
|
319 |
def try_detail(feed_or_item, detail_name): |
320 |
if detail_name not in feed_or_item: |
321 |
return '' |
322 |
return read_detail(feed_or_item[detail_name]) |
323 |
|
324 |
|
325 |
def read_detail(detail): |
326 |
if detail['type'] == 'text/plain': |
327 |
t = detail['value'] |
328 |
elif detail['type'] == 'text/html': |
329 |
t = remove_html(detail['value']) |
330 |
else: |
331 |
t = '' |
332 |
return ' '.join(t.split()) |
333 |
|
334 |
|
335 |
def remove_html(s): |
336 |
return ''.join(map(lambda z: z[-1], |
337 |
map(lambda z: z.split('>'), |
338 |
s.split('<')))) |
339 |
|
340 |
|
341 |
def load_choices(): |
342 |
try: |
343 |
f = open('Choices:Sargasso.Feeds') |
344 |
except IOError: |
345 |
return |
346 |
for line in f: |
347 |
sargasso.add_feed(line[:-1]) |
348 |
|
349 |
|
350 |
def save_choices(): |
351 |
os.mkdir('<Choices$Write>.Sargasso') |
352 |
try: |
353 |
f = open('<Choices$Write>.Sargasso.Feeds', 'w') |
354 |
except IOError: |
355 |
return |
356 |
for feed in sargasso.feeds: |
357 |
f.write(feed.url + '\n') |
358 |
f.close() |
359 |
|
360 |
|
361 |
class AddFeedBox(wimp.window): |
362 |
|
363 |
def click(self, icon, buttons, x, y, sx, sy): |
364 |
if icon == 2: |
365 |
self.close() |
366 |
return True |
367 |
elif icon == 1: |
368 |
url = self.icon_text(0) |
369 |
scheme, host, path, params, query, fragment = urlparse.urlparse(url) |
370 |
if scheme != 'http': |
371 |
wimp.warning(wimp.lookup('http')) |
372 |
return True |
373 |
sargasso.add_feed(url) |
374 |
sargasso.feeds[-1].last_status = sargasso.feeds[-1].status |
375 |
sargasso.feeds[-1].window = wimp.window('main') |
376 |
save_choices() |
377 |
self.close() |
378 |
return True |
379 |
return False |
380 |
|
381 |
def key(self, icon, key): |
382 |
if key == 0x1b: |
383 |
self.close() |
384 |
elif key == 0xd: |
385 |
self.click(1, 0, 0, 0, 0, 0) |
386 |
else: |
387 |
wimp.window.key(self, icon, key) |