sfeed_tk - randomcrap - random crap programs of varying quality
(HTM) git clone git://git.codemadness.org/randomcrap
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
sfeed_tk (10619B)
---
1 #!/usr/bin/wish
2 package require Tk
3
4 # ttk::style theme names (default, clam, classic, etc)
5 ttk::style theme use classic
6 #set scrollbar "ttk::scrollbar"
7 set scrollbar "scrollbar"
8
9 set fontname TkDefaultFont
10 #set fontname "monospace"
11 font create font_item_normal -family $fontname -size 10 -weight normal
12 font create font_item_bold -family $fontname -size 10 -weight bold
13
14 # global config variables.
15 set config_onlynew 0
16 set config_newage ""
17 set config_plumber "xdg-open"
18 # global feeds list
19 set comparetime 0
20 set feeds {}
21 set mode_read_stdin 0
22
23 proc parse_sfeed_line {line} {
24 return [split $line "\t"]
25 }
26
27 # read items in a sfeed(5) file and parse all the fields.
28 proc read_sfeed_file {file} {
29 set total 0
30 set totalnew 0
31 set comparetime $::comparetime
32
33 # clear list
34 .tree_items delete [.tree_items children {}]
35
36 if {$::mode_read_stdin == 1} {
37 set f stdin
38 } else {
39 set f [open $file]
40 }
41 while {[gets $f line] >= 0} {
42 incr total
43
44 set fields [parse_sfeed_line $line]
45 set timestampstr [lindex $fields 0]
46 set title [lindex $fields 1]
47 set link [lindex $fields 2]
48 set content [lindex $fields 3]
49 set contenttype [lindex $fields 4]
50 set id [lindex $fields 5]
51 set author [lindex $fields 6]
52 set enclosure [lindex $fields 7]
53 set category [lindex $fields 8]
54
55 # parse timestamp
56
57 # convert to long long
58 set e [scan $timestampstr "%lld" timestamp]
59
60 if {$e == 1} {
61 if {$timestamp >= $comparetime} {
62 incr totalnew
63 }
64 set date [clock format $timestamp -gmt 0 -format "%Y-%m-%d %H:%M"]
65 } else {
66 set date ""
67 }
68
69 if {$e == 1 && $timestamp >= $comparetime} {
70 set isnew "N"
71 set style "bold"
72 } else {
73 set isnew ""
74 set style "normal"
75 }
76 .tree_items insert {} end -id $total -text "" -tags "item item_${style}" \
77 -values [list \
78 $timestampstr $title $link $content $contenttype $id $author $enclosure $category \
79 $isnew $date]
80 }
81 if {$::mode_read_stdin == 0} {
82 close $f
83 }
84
85 set feed(path) $file
86 set feed(name) [file tail $file]
87 set feed(total) $total
88 set feed(totalnew) $totalnew
89
90 # return struct serialized
91 return [array get feed]
92 }
93
94 # count new items in a sfeed(5) file, but do not parse all the lines.
95 proc count_sfeed_file {file} {
96 set total 0
97 set totalnew 0
98 set comparetime $::comparetime
99
100 set f [open $file]
101 while {[gets $f line] >= 0} {
102 incr total
103
104 # get first timestamp field
105 set idx [string first " " $line]
106 if {$idx == -1} {
107 continue
108 }
109 set timestampstr [string range $line 0 $idx]
110 # convert to long long
111 set e [scan $timestampstr "%lld" timestamp]
112 if {$e == 1 && $timestamp >= $comparetime} {
113 incr totalnew
114 }
115 }
116 close $f
117
118 set feed(path) $file
119 set feed(name) [file tail $file]
120 set feed(total) $total
121 set feed(totalnew) $totalnew
122
123 # return struct serialized
124 return [array get feed]
125 }
126
127 proc getcomparetime {} {
128 set newage 86400
129 if { $::config_newage != "" } {
130 set e [scan $::config_newage "%lld" l]
131 if {$e == 1} {
132 set newage $l
133 }
134 }
135 # get current time
136 set now [clock seconds]
137 # 1 day is old news
138 return [expr {$now - $newage}]
139 }
140
141 # TODO: set and restore divider position per mode.
142 proc window_set_mode {win mode} {
143 if {$mode == "1"} {
144 $win configure -orient horizontal
145 $win paneconfigure .pw.frame_feed -hide 0
146 }
147 if {$mode == "2"} {
148 $win configure -orient vertical
149 $win paneconfigure .pw.frame_feed -hide 0
150 }
151 # set mode monocle
152 if {$mode == "3"} {
153 $win paneconfigure .pw.frame_feed -hide 1
154 }
155 }
156
157 proc feeds_update_list {} {
158 set feeds $::feeds
159
160 # clear list
161 .tree_feed delete [.tree_feed children {}]
162
163 foreach f $feeds {
164 array set feed $f
165
166 if { ${feed(totalnew)} > 0 } {
167 .tree_feed insert {} end -text "" -tags "item item_bold" \
168 -values [list ${feed(name)} ${feed(totalnew)} ${feed(total)} ${feed(path)} ]
169 } else {
170 if {$::config_onlynew == 0} {
171 .tree_feed insert {} end -text "" -tags "item item_normal" \
172 -values [list ${feed(name)} ${feed(totalnew)} ${feed(total)} ${feed(path)} ]
173 }
174 }
175 }
176 }
177
178 proc global_key_release {key} {
179 # exit
180 if {$key == "q"} {
181 exit 0
182 }
183 # "/": start search/filter (if not enabled already).
184 # TODO: support ctrl-f also? ctrl-n, etc?
185 if {$key == "slash"} {
186 # TODO
187 }
188 # toggle showing feeds with new items only.
189 if {$key == "t"} {
190 if {$::config_onlynew == 0} {
191 set ::config_onlynew 1
192 feeds_update_list
193 } else {
194 set ::config_onlynew 0
195 feeds_update_list
196 }
197 }
198 # set mode horizontal, vertical, monocle
199 if {$key == "1"} {
200 window_set_mode .pw $key
201 }
202 if {$key == "2"} {
203 window_set_mode .pw $key
204 }
205 if {$key == "3"} {
206 window_set_mode .pw $key
207 }
208 }
209
210 # update status bar
211 proc status_set {text} {
212 .status configure -state normal
213 # .status configure -background "#cccccc"
214 .status delete 1.0 end
215 .status insert end $text
216 .status configure -state disabled
217 }
218
219 proc main {argc argv} {
220 # environment variables
221 if {[info exists ::env(SFEED_PLUMBER)]} {
222 set ::config_plumber $::env(SFEED_PLUMBER)
223 }
224 if {[info exists ::env(SFEED_NEW_AGE)]} {
225 set ::config_newage $::env(SFEED_NEW_AGE)
226 }
227 set ::comparetime [getcomparetime]
228
229 # make essential widgets
230
231 # paned window, set default mode to horizontal.
232 panedwindow .pw -sashwidth 8
233
234 # create treeviews:
235 # selectmode=browse: select one item at a time.
236 ttk::treeview .tree_feed -selectmode browse \
237 -columns "feedname feednew feedtotal filename" \
238 -displaycolumns "feedname feednew feedtotal" -show headings
239 ttk::treeview .tree_items -selectmode browse \
240 -columns "timestamp title link content contenttype id author enclosure category isnew date" \
241 -displaycolumns "isnew date title" -show headings
242
243 frame .pw.frame_feed -width 100
244 frame .pw.frame_items -width 100
245
246 set feedcount 0
247 set feeds []
248 set feedcount 0
249 set feednewcount 0
250 set totalnew 0
251 set total 0
252
253 # if no files, read from stdin and hide feeds panel by default (monocle layout).
254 if {$argc == 0} {
255 set lfiles {"/dev/stdin"}
256 # read and count sfeed file: stdin cannot be reopened.
257 set read_func "read_sfeed_file"
258 set ::mode_read_stdin 1
259 # set monocle by default.
260 window_set_mode .pw "3"
261 } else {
262 set lfiles $argv
263 set read_func "count_sfeed_file"
264 set ::mode_read_stdin 0
265 # set vertical by default.
266 window_set_mode .pw "1"
267 }
268
269 foreach file $lfiles {
270 array set feed [$read_func $file]
271
272 # add feed struct serialized
273 lappend feeds [array get feed]
274 incr feedcount
275
276 set linecount $feed(total)
277 set filename $feed(name)
278
279 set total [expr {$total + $feed(total)}]
280 set totalnew [expr {$totalnew + $feed(totalnew)}]
281
282 if {$feed(totalnew) > 0} {
283 incr feednewcount
284 }
285 }
286
287 set title "(${totalnew}/${total}) - sfeed_tk"
288
289 wm title . $title
290
291 # UI
292
293 # Make the other widgets.
294
295 .tree_feed column feedname -anchor w -stretch 1
296 .tree_feed column feednew -width 50 -anchor e -stretch 0
297 .tree_feed column feedtotal -width 50 -anchor e -stretch 0
298 .tree_feed heading feedname -text "Feeds (${feednewcount}/${feedcount})" -anchor w
299 .tree_feed heading feednew -text $totalnew -anchor e
300 .tree_feed heading feedtotal -text $total -anchor e
301 .tree_feed configure -padding {0 0 0 0}
302
303 .tree_items column isnew -width 50 -anchor c -stretch 0
304 .tree_items column date -width 135 -anchor e -stretch 0
305 .tree_items column title -anchor w -stretch 1
306 .tree_items heading isnew -text "New?" -anchor c
307 .tree_items heading date -text "Date" -anchor e
308 .tree_items heading title -text "Title" -anchor w
309 .tree_items configure -padding {0 0 0 0}
310
311 # scrollbar for feeds
312 .tree_feed configure -yscrollcommand {.tree_feed_scroll set}
313 ::scrollbar .tree_feed_scroll -command {.tree_feed yview}
314
315 # scrollbar for feed items
316 .tree_items configure -yscrollcommand {.tree_items_scroll set}
317 ::scrollbar .tree_items_scroll -command {.tree_items yview}
318
319 pack .tree_feed -in .pw.frame_feed -fill both -expand yes -side left
320 pack .tree_feed_scroll -in .pw.frame_feed -side right -fill y
321 pack .tree_items -in .pw.frame_items -fill both -expand yes -side left
322 pack .tree_items_scroll -in .pw.frame_items -side right -fill y
323
324 .pw add .pw.frame_feed -minsize 1
325 .pw add .pw.frame_items -minsize 1
326
327 # make global
328 set ::feeds $feeds
329 feeds_update_list
330
331 proc feeditem_button_1_release {} {
332 }
333
334 # can only (re)read feeds when not stdin.
335 proc feeditem_doublebutton_1_release {} {
336 if {$::mode_read_stdin == 0} {
337 set item [.tree_feed item [.tree_feed selection] -values]
338 set file [lindex $item 3]
339 read_sfeed_file $file
340 }
341 }
342
343 proc feeditem_button_2_release {} {
344 puts stdout [.tree_feed selection]
345 }
346
347 proc feeditem_button_3_release {} {
348 puts stdout [.tree_feed selection]
349 }
350
351 proc feeditem_key_release {key} {
352 if {$key == "Return" } {
353 feeditem_doublebutton_1_release
354 }
355 }
356
357 proc item_button_1_release {} {
358 }
359
360 proc item_doublebutton_1_release {} {
361 set item [.tree_items item [.tree_items selection] -values]
362 set link [lindex $item 2]
363 if {$link != ""} {
364 exec $::config_plumber "$link" "2>/dev/null" "&"
365 }
366 }
367
368 proc item_button_2_release {} {
369 puts stdout [.tree_items selection]
370 }
371
372 proc item_button_3_release {} {
373 puts stdout [.tree_items selection]
374 }
375
376 proc item_key_release {key} {
377 if {$key == "Return" } {
378 item_doublebutton_1_release
379 }
380 }
381
382 proc item_selection_change {} {
383 set item [.tree_items item [.tree_items selection] -values]
384 set link [lindex $item 2]
385 status_set $link
386 }
387
388 .tree_feed tag bind "item" <Double-1> "feeditem_doublebutton_1_release"
389 .tree_feed tag bind "item" <ButtonRelease-1> "feeditem_button_1_release"
390 .tree_feed tag bind "item" <ButtonRelease-2> "feeditem_button_2_release"
391 .tree_feed tag bind "item" <ButtonRelease-3> "feeditem_button_3_release"
392 .tree_feed tag bind "item" <KeyRelease> "feeditem_key_release %K"
393
394 .tree_items tag bind "item" <Double-1> "item_doublebutton_1_release"
395 .tree_items tag bind "item" <ButtonRelease-1> "item_button_1_release"
396 .tree_items tag bind "item" <ButtonRelease-2> "item_button_2_release"
397 .tree_items tag bind "item" <ButtonRelease-3> "item_button_3_release"
398 .tree_items tag bind "item" <KeyRelease> "item_key_release %K"
399
400 .tree_items tag bind "item" <<TreeviewSelect>> "item_selection_change"
401
402 # global key handler.
403 bind . <Key> "global_key_release %K"
404
405 .tree_feed tag configure item_normal -font font_item_normal
406 .tree_feed tag configure item_bold -font font_item_bold
407 .tree_items tag configure item_normal -font font_item_normal
408 .tree_items tag configure item_bold -font font_item_bold
409
410 # status bar: read-only text field (so text can still be selection and copied).
411 text .status -height 1 -state disabled
412
413 pack .pw -fill both -expand yes -side top
414 pack .status -side bottom -fill x
415
416 status_set ""
417 }
418
419 main $argc $argv