| |
1 #!/usr/bin/env python |
| |
2 |
| |
3 # A very simple and stupid RSS reader |
| |
4 # |
| |
5 # Uses the Universal Feed Parser |
| |
6 # |
| |
7 |
| |
8 import gntrss |
| |
9 import gnt |
| |
10 import gobject |
| |
11 import sys |
| |
12 |
| |
13 __version__ = "0.0.1alpha" |
| |
14 __author__ = "Sadrul Habib Chowdhury (sadrul@pidgin.im)" |
| |
15 __copyright__ = "Copyright 2007, Sadrul Habib Chowdhury" |
| |
16 __license__ = "GPL" # see full license statement below |
| |
17 |
| |
18 gnt.gnt_init() |
| |
19 |
| |
20 class RssTree(gnt.Tree): |
| |
21 __gsignals__ = { |
| |
22 'active_changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)), |
| |
23 'key_pressed' : 'override' |
| |
24 } |
| |
25 |
| |
26 def __init__(self): |
| |
27 self.active = None |
| |
28 gnt.Tree.__init__(self) |
| |
29 |
| |
30 def set_active(self, active): |
| |
31 if self.active == active: |
| |
32 return |
| |
33 if self.active: |
| |
34 self.set_row_flags(self.active, gnt.TEXT_FLAG_NORMAL) |
| |
35 old = self.active |
| |
36 self.active = active |
| |
37 self.set_row_flags(self.active, gnt.TEXT_FLAG_UNDERLINE) |
| |
38 self.emit('active_changed', old) |
| |
39 |
| |
40 def do_key_pressed(self, text): |
| |
41 if text == '\r': |
| |
42 now = self.get_selection_data() |
| |
43 self.set_active(now) |
| |
44 return True |
| |
45 return False |
| |
46 |
| |
47 gobject.type_register(RssTree) |
| |
48 |
| |
49 win = gnt.Box(homo = False, vert = True) |
| |
50 win.set_toplevel(True) |
| |
51 win.set_title("GntRss") |
| |
52 win.set_pad(0) |
| |
53 |
| |
54 # |
| |
55 # [[[ Generic feed/item callbacks |
| |
56 # |
| |
57 def feed_item_added(feed, item): |
| |
58 add_feed_item(item) |
| |
59 |
| |
60 def add_feed(feed): |
| |
61 if not feed.get_data('gntrss-connected'): |
| |
62 feed.connect('added', feed_item_added) |
| |
63 feed.connect('notify', update_feed_title) |
| |
64 feed.set_data('gntrss-connected', True) |
| |
65 feeds.add_row_after(feed, [feed.title, str(feed.unread)], None, None) |
| |
66 |
| |
67 def remove_item(item, feed): |
| |
68 items.remove(item) |
| |
69 |
| |
70 def update_feed_item(item, property): |
| |
71 if property.name == 'unread': |
| |
72 if feeds.active != item.parent: |
| |
73 return |
| |
74 if item.unread: |
| |
75 item.parent.unread = item.parent.unread + 1 |
| |
76 items.set_row_flags(item, gnt.TEXT_FLAG_BOLD) |
| |
77 else: |
| |
78 item.parent.unread = item.parent.unread - 1 |
| |
79 items.set_row_flags(item, gnt.TEXT_FLAG_NORMAL) |
| |
80 item.parent.notify('unread') |
| |
81 |
| |
82 def add_feed_item(item): |
| |
83 currentfeed = feeds.active |
| |
84 if item.parent != currentfeed: |
| |
85 return |
| |
86 items.add_row_after(item, [str(item.title)], None, None) |
| |
87 if item.unread: |
| |
88 items.set_row_flags(item, gnt.TEXT_FLAG_BOLD) |
| |
89 if not item.get_data('gntrss-connected'): |
| |
90 item.set_data('gntrss-connected', True) |
| |
91 item.connect('notify', update_feed_item) |
| |
92 item.connect('delete', remove_item) |
| |
93 |
| |
94 # |
| |
95 # ]]] Generic feed/item callbacks |
| |
96 # |
| |
97 |
| |
98 |
| |
99 #### |
| |
100 # [[[ The list of feeds |
| |
101 ### |
| |
102 |
| |
103 # |
| |
104 # The active row in the feed-list has changed. Update the feed-item table. |
| |
105 def feed_active_changed(tree, old): |
| |
106 items.remove_all() |
| |
107 if not tree.active: |
| |
108 return |
| |
109 for item in tree.active.items: |
| |
110 add_feed_item(item) |
| |
111 |
| |
112 # |
| |
113 # Check for the action keys and decide how to deal with them. |
| |
114 def feed_key_pressed(tree, text): |
| |
115 if text == 'r': |
| |
116 feed = tree.get_selection_data() |
| |
117 tree.perform_action_key('j') |
| |
118 #tree.perform_action('move-down') |
| |
119 feed.refresh() |
| |
120 elif text == 'R': |
| |
121 feeds = tree.get_rows() |
| |
122 for feed in feeds: |
| |
123 feed.refresh() |
| |
124 else: |
| |
125 return False |
| |
126 return True |
| |
127 |
| |
128 feeds = RssTree() |
| |
129 feeds.set_property('columns', 2) |
| |
130 feeds.set_col_width(0, 20) |
| |
131 feeds.set_col_width(1, 4) |
| |
132 feeds.set_column_resizable(0, False) |
| |
133 feeds.set_column_resizable(1, False) |
| |
134 feeds.set_column_is_right_aligned(1, True) |
| |
135 feeds.set_show_separator(False) |
| |
136 |
| |
137 feeds.connect('active_changed', feed_active_changed) |
| |
138 feeds.connect('key_pressed', feed_key_pressed) |
| |
139 |
| |
140 #### |
| |
141 # ]]] The list of feeds |
| |
142 ### |
| |
143 |
| |
144 #### |
| |
145 # [[[ The list of items in the feed |
| |
146 #### |
| |
147 |
| |
148 # |
| |
149 # The active item in the feed-item list has changed. Update the |
| |
150 # summary content. |
| |
151 def item_active_changed(tree, old): |
| |
152 details.clear() |
| |
153 if not tree.active: |
| |
154 return |
| |
155 item = tree.active |
| |
156 details.append_text_with_flags(str(item.title) + "\n", gnt.TEXT_FLAG_BOLD) |
| |
157 details.append_text_with_flags(str(item.summary), gnt.TEXT_FLAG_NORMAL) |
| |
158 details.scroll(0) |
| |
159 if item.unread: |
| |
160 item.set_property('unread', False) |
| |
161 win.give_focus_to_child(browser) |
| |
162 |
| |
163 # |
| |
164 # Look for action keys in the feed-item list. |
| |
165 def item_key_pressed(tree, text): |
| |
166 current = tree.get_selection_data() |
| |
167 if text == 'M': # Mark all of the items 'read' |
| |
168 all = tree.get_rows() |
| |
169 for item in all: |
| |
170 item.unread = False |
| |
171 elif text == 'm': # Mark the current item 'read' |
| |
172 if current.unread: |
| |
173 current.set_property('unread', False) |
| |
174 elif text == 'U': # Mark the current item 'unread' |
| |
175 if not current.unread: |
| |
176 current.set_property('unread', True) |
| |
177 else: |
| |
178 return False |
| |
179 return True |
| |
180 |
| |
181 items = RssTree() |
| |
182 items.set_property('columns', 1) |
| |
183 items.set_col_width(0, 40) |
| |
184 items.connect('key_pressed', item_key_pressed) |
| |
185 items.connect('active_changed', item_active_changed) |
| |
186 |
| |
187 #### |
| |
188 # ]]] The list of items in the feed |
| |
189 #### |
| |
190 |
| |
191 # The container on the top |
| |
192 box = gnt.Box(homo = False, vert = False) |
| |
193 box.set_pad(0) |
| |
194 box.add_widget(feeds) |
| |
195 box.add_widget(items) |
| |
196 |
| |
197 win.add_widget(box) |
| |
198 |
| |
199 win.add_widget(gnt.Line(vertical = False)) |
| |
200 |
| |
201 # The textview to show the details of a feed |
| |
202 details = gnt.TextView() |
| |
203 |
| |
204 win.add_widget(details) |
| |
205 |
| |
206 browser = gnt.Button("Open in Browser") |
| |
207 win.add_widget(browser) |
| |
208 details.attach_scroll_widget(browser) |
| |
209 |
| |
210 win.show() |
| |
211 |
| |
212 def update_feed_title(feed, property): |
| |
213 if property.name == 'title': |
| |
214 feeds.change_text(feed, 0, feed.title) |
| |
215 elif property.name == 'unread': |
| |
216 feeds.change_text(feed, 1, str(feed.unread)) |
| |
217 |
| |
218 # populate everything |
| |
219 for feed in gntrss.feeds: |
| |
220 add_feed(feed) |
| |
221 |
| |
222 gnt.gnt_main() |
| |
223 |
| |
224 gnt.gnt_quit() |
| |
225 |