1 |
james |
76 |
# |
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) |