65468
|
1 ;;; newsticker.el --- A Newsticker for Emacs.
|
|
2
|
|
3 ;; Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
|
|
4
|
|
5 ;; This file is part of GNU Emacs.
|
|
6
|
|
7 ;; Author: Ulf Jasper <ulf.jasper@web.de>
|
|
8 ;; Filename: newsticker.el
|
|
9 ;; URL: http://www.nongnu.org/newsticker
|
|
10 ;; Created: 17. June 2003
|
|
11 ;; Keywords: News, RSS
|
|
12 ;; Time-stamp: "26. August 2005, 16:33:46 (ulf)"
|
|
13 ;; CVS-Version: $Id: newsticker.el,v 1.130 2005/08/26 14:35:20 u11 Exp $
|
|
14
|
|
15 (defconst newsticker-version "1.8" "Version number of newsticker.el.")
|
|
16
|
|
17 ;; ======================================================================
|
|
18
|
|
19 ;; This program is free software; you can redistribute it and/or modify
|
|
20 ;; it under the terms of the GNU General Public License as published by
|
|
21 ;; the Free Software Foundation; either version 2 of the License, or (at
|
|
22 ;; your option) any later version.
|
|
23
|
|
24 ;; This program is distributed in the hope that it will be useful, but
|
|
25 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
26 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
27 ;; General Public License for more details.
|
|
28
|
|
29 ;; You should have received a copy of the GNU General Public License
|
|
30 ;; along with this program; if not, write to the Free Software
|
|
31 ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
32 ;; USA
|
|
33
|
|
34 ;; ======================================================================
|
|
35 ;;; Commentary:
|
|
36
|
|
37 ;; Overview
|
|
38 ;; --------
|
|
39
|
|
40 ;; Newsticker provides a newsticker for Emacs. A newsticker is a thing that
|
|
41 ;; asynchronously retrieves headlines from a list of news sites, prepares
|
|
42 ;; these headlines for reading, and allows for loading the corresponding
|
|
43 ;; articles in a web browser.
|
|
44
|
|
45 ;; Headlines consist of a title and (possibly) a small description. They
|
|
46 ;; are contained in RSS (RDF Site Summary) files. Newsticker should work
|
|
47 ;; with all RSS files that follow the RDF Rich Site Summary 1.0
|
|
48 ;; specification. It should also work with version 2.0 as well as
|
|
49 ;; other/older/alternative RSS formats (like 0.9<something> or such). In
|
|
50 ;; other words: Newsticker is a "RSS reader" or "RSS aggregator".
|
|
51
|
|
52 ;; Newsticker provides several commands for reading headlines, navigating
|
|
53 ;; through them, marking them as read/unread, hiding old headlines
|
|
54 ;; etc. Headlines can be displayed as plain text or as rendered HTML.
|
|
55
|
|
56 ;; Headlines can be displayed in the echo area, either scrolling like
|
|
57 ;; messages in a stock-quote ticker, or just changing.
|
|
58
|
|
59 ;; Newsticker allows for automatic processing of headlines by providing
|
|
60 ;; hooks and (sample) functions for automatically downloading images and
|
|
61 ;; enclosed files (as delivered by podcasts, e.g.).
|
|
62
|
|
63
|
|
64
|
|
65 ;; Requirements
|
|
66 ;; ------------
|
|
67 ;; Newsticker can be used with GNU Emacs version 21.1 or later as well as
|
|
68 ;; XEmacs. It requires an XML-parser (`xml.el') which is part of GNU
|
|
69 ;; Emacs. If you are using XEmacs you want to get the `net-utils' package
|
|
70 ;; which contains `xml.el' for XEmacs.
|
|
71
|
|
72 ;; Newsticker requires a program which can retrieve files via http and
|
|
73 ;; prints them to stdout. By default Newsticker will use wget for this
|
|
74 ;; task.
|
|
75
|
|
76 ;; Installation
|
|
77 ;; ------------
|
|
78 ;; Place Newsticker in a directory where Emacs can find it. Add the
|
|
79 ;; following line to your Emacs startup file (`~/.emacs').
|
|
80 ;; (add-to-list 'load-path "/path/to/newsticker/")
|
|
81 ;; (autoload 'newsticker-start "newsticker" "Emacs Newsticker" t)
|
|
82 ;; (autoload 'newsticker-show-news "newsticker" "Emacs Newsticker" t)
|
|
83
|
|
84 ;; Newsticker-mode supports imenu. It allows for navigating with the help
|
|
85 ;; of a menu. In order to use this feature you should also add the
|
|
86 ;; following.
|
|
87 ;; (add-hook 'newsticker-mode-hook 'imenu-add-menubar-index)
|
|
88
|
|
89 ;; That's it.
|
|
90
|
|
91 ;; Usage
|
|
92 ;; -----
|
|
93 ;; The command newsticker-show-news will display all available headlines in
|
|
94 ;; a special buffer, called `*newsticker*'. It will also start the
|
|
95 ;; asynchronous download of headlines. The modeline in the `*newsticker*'
|
|
96 ;; buffer informs whenever new headlines have arrived. Clicking
|
|
97 ;; mouse-button 2 or pressing RET in this buffer on a headline will call
|
|
98 ;; browse-url to load the corresponding news story in your favourite web
|
|
99 ;; browser.
|
|
100
|
|
101 ;; The scrolling, or flashing of headlines in the echo area, can be started
|
|
102 ;; with the command newsticker-start-ticker. It can be stopped with
|
|
103 ;; newsticker-stop-ticker.
|
|
104
|
|
105 ;; If you just want to start the periodic download of headlines use the
|
|
106 ;; command newsticker-start. Calling newsticker-stop will stop the periodic
|
|
107 ;; download, but will call newsticker-stop-ticker as well.
|
|
108
|
|
109 ;; Configuration
|
|
110 ;; -------------
|
|
111 ;; All Newsticker options are customizable, i.e. they can be changed with
|
|
112 ;; Emacs customization methods: Call the command customize-group and enter
|
|
113 ;; `newsticker' for the customization group.
|
|
114
|
|
115 ;; All Newsticker options have reasonable default values, so that in most
|
|
116 ;; cases it is not necessary to customize settings before starting
|
|
117 ;; Newsticker for the first time.
|
|
118
|
|
119 ;; Newsticker options are organized in the following groups.
|
|
120
|
|
121 ;; * newsticker-feed contains options that define which news
|
|
122 ;; feeds are retrieved and how this is done.
|
|
123 ;; o newsticker-url-list defines the list of headlines which are
|
|
124 ;; retrieved.
|
|
125 ;; o newsticker-retrieval-interval defines how often headlines are
|
|
126 ;; retrieved.
|
|
127 ;; * newsticker-headline-processing contains options that define how the
|
|
128 ;; retrieved headlines are processed.
|
|
129 ;; o newsticker-keep-obsolete-items decides whether unread headlines that
|
|
130 ;; have been removed from the feed are kept in the Newsticker cache.
|
|
131 ;; * newsticker-layout contains options that define how the buffer for
|
|
132 ;; reading RSS headlines is formatted.
|
|
133 ;; o newsticker-item-format defines how the title of a headline is
|
|
134 ;; formatted.
|
|
135 ;; * newsticker-ticker contains options that define how headlines are shown
|
|
136 ;; in the echo area.
|
|
137 ;; o newsticker-display-interval and newsticker-scroll-smoothly define
|
|
138 ;; how headlines are shown in the echo area.
|
|
139 ;; * newsticker-hooks contains options for hooking other Emacs commands to
|
|
140 ;; newsticker functions.
|
|
141 ;; o newsticker-new-item-functions allows for automatic processing of
|
|
142 ;; headlines. See `newsticker-download-images', and
|
|
143 ;; `newsticker-download-enclosures' for sample functions.
|
|
144 ;; * newsticker-miscellaneous contains other Newsticker options.
|
|
145
|
|
146 ;; Please have a look at the customization buffers for the complete list of
|
|
147 ;; options.
|
|
148
|
|
149 ;; Remarks
|
|
150 ;; -------
|
|
151 ;; This newsticker is designed do its job silently in the background
|
|
152 ;; without disturbing you. However, it is probably impossible to prevent
|
|
153 ;; such a tool from slightly attenuating your Editor's responsiveness every
|
|
154 ;; once in a while.
|
|
155
|
|
156 ;; Byte-compiling newsticker.el is recommended.
|
|
157
|
|
158 ;; ======================================================================
|
|
159 ;;; History:
|
|
160
|
|
161 ;; 1.8 (2005-08-26)
|
|
162 ;; * Added commands `newsticker-show-extra' and `newsticker-hide-extra'
|
|
163 ;; to show and hide extra RSS elements, bound to "sx" and "hx"
|
|
164 ;; resp. Changed default value of `newsticker-show-all-rss-elements'
|
|
165 ;; to nil.
|
|
166 ;; * mode-line: Introduced special mode-line-format for newsticker.
|
|
167 ;; * Get feed logos only once every 24 h.
|
|
168 ;; * Default faces changed.
|
|
169 ;; * Minor fixes.
|
|
170
|
|
171 ;; 1.7 (2005-06-25)
|
|
172 ;; * Tool-bar support: most important commands can be called from
|
|
173 ;; tool-bar buttons.
|
|
174 ;; * Auto-Narrowing introduced: *newsticker* buffer can be narrowed to
|
|
175 ;; a single item (bound to key `xi') or a single feed (bound to `xf').
|
|
176 ;; * Enclosure support: enclosed items are shown (see
|
|
177 ;; `newsticker-enclosure-face') and can be (automatically) downloaded
|
|
178 ;; (see below). For those of you who read "podcasts".
|
|
179 ;; * Added variable `newsticker-auto-mark-filter' for automatically
|
|
180 ;; marking items as immortal or old.
|
|
181 ;; * Added hook variable `newsticker-new-item-functions' for handling
|
|
182 ;; new items. Added sample functions `newsticker-download-images',
|
|
183 ;; and `newsticker-download-enclosures'.
|
|
184 ;; * Added hook variable `newsticker-select-item-hook' which is run
|
|
185 ;; after `newsticker-(next|previous)-(new-)?-item'.
|
|
186 ;; * Added hook variable `newsticker-select-feed-hook' which is run
|
|
187 ;; after `newsticker-(next|previous)-feed'.
|
|
188 ;; * Added hook variable `newsticker-buffer-change-hook' which is run
|
|
189 ;; after the contents or visibility of the newsticker buffer has
|
|
190 ;; changed, e.g. after `newsticker-buffer-update' or
|
|
191 ;; `newsticker-show-feed-desc'.
|
|
192 ;; * Added command `newsticker-handle-url' for interactively launching
|
|
193 ;; arbitrary programs for URLs, bound to `C-RET'.
|
|
194 ;; * URLs in extra elements are clickable.
|
|
195 ;; * Better support for w3, added command
|
|
196 ;; `newsticker-w3m-show-inline-images' for displaying all inline
|
|
197 ;; images.
|
|
198 ;; * Insert an artificial headline which notifies about failed retrievals.
|
|
199 ;; * Use pubDate element (RSS 2.0) instead of retrieval time when
|
|
200 ;; available.
|
|
201 ;; * Customizable options grouped.
|
|
202 ;; * Bugfixes: `newsticker--imenu-create-index'; strip whitespace
|
|
203 ;; from links; apply coding-system to extra-elements; time-comparison
|
|
204 ;; for obsolete items; and others which I have forgotten.
|
|
205 ;; * Workaround for another bug in xml-parse-region -- thanks to anonymous
|
|
206 ;; for sending patch.
|
|
207 ;; * Renamed invisible buffers ` *wget-newsticker-<feed>*' to
|
|
208 ;; ` *newsticker-wget-<feed>*'.
|
|
209 ;; * Tested with GNU Emacs versions 21.3 and 22.0 and XEmacs 21.something.
|
|
210
|
|
211 ;; 1.6 * Support for (some) optional RSS elements: guid, dc:date. See
|
|
212 ;; `newsticker-show-all-rss-elements' `newsticker-extra-face'.
|
|
213 ;; * Better support for w3m -- `newsticker-default-face' is obsolete
|
|
214 ;; now, removed `newsticker-w3m-toggle-inline-image'.
|
|
215 ;; * Added `newsticker-desc-comp-max' -- comparison of item descriptions
|
|
216 ;; can take quite some time.
|
|
217 ;; * Added `newsticker--buffer-make-item-completely-visible' to
|
|
218 ;; ensure that the current item is fully visible.
|
|
219 ;; * Allow for non-positive retrieval-interval, which make newsticker
|
|
220 ;; get news only once.
|
|
221 ;; * Use :set for customizable variables.
|
|
222 ;; * Added `newsticker-buffer-force-update', bound to key `U'.
|
|
223 ;; * Added concept of obsolete items, see
|
|
224 ;; `newsticker-keep-obsolete-items', `newsticker-obsolete-item-face',
|
|
225 ;; `newsticker-obsolete-item-max-age'.
|
|
226 ;; * Added `newsticker-add-url'.
|
|
227 ;; * OPML export.
|
|
228 ;; * Save pre-formatted titles => even better performance!!
|
|
229 ;; * `newsticker-*-new-item' wraps at beginning/end of buffer.
|
|
230 ;; * Always sort obsolete items to end of item list.
|
|
231 ;; * Bugfixes:
|
|
232 ;; - newsticker-hide-entry,
|
|
233 ;; - changes of feed-titles led to duplicate feed items,
|
|
234 ;; - faces for rendered HTML texts,
|
|
235 ;; - length of ticker-text (for "exotic"/multibyte texts),
|
|
236 ;; Thanks to Hiroshi Maruyama.
|
|
237 ;; - suppress items with empty title and description
|
|
238 ;; - newsticker-sort-method was ignored!
|
|
239 ;; - prevent call of fill-region on HTML-rendered descriptions.
|
|
240
|
|
241 ;; 1.5 * Rewrote the visibility stuff. newsticker does not inherit
|
|
242 ;; outline anymore. Now you have complete freedom for
|
|
243 ;; `newsticker-*-format'.
|
|
244 ;; * Save pre-formatted descriptions => incredible performance boost!!
|
|
245 ;; * Introduced `newsticker-(start|stop)-ticker'.
|
|
246 ;; * Introduced statistics for heading-format and
|
|
247 ;; `newsticker-statistics-face'.
|
|
248 ;; * Introduced `newsticker-enable-logo-manipulations'.
|
|
249 ;; * Compare link of items (as well as title and desc).
|
|
250 ;; * Added `newsticker-start-hook' and `newsticker-stop-hook', thanks
|
|
251 ;; to mace.
|
|
252 ;; * Bugfixes -- thanks to Ryan Yeske, Jari Aalto, Bruce Ingalls.
|
|
253 ;; * Tested with Emacs 21.3.50, 21.3.1, 21.2, 21.1; XEmacs 21.4.15
|
|
254
|
|
255 ;; 1.4 * Enabled HTML rendering, added `newsticker-html-renderer' to
|
|
256 ;; choose a HTML rendering engine, thanks to Greg Scott for testing
|
|
257 ;; * New Outline handling using text properties instead of "**"
|
|
258 ;; prefixes.
|
|
259 ;; * Added possibility to mark single item as old (bound to key
|
|
260 ;; `o' (`newsticker-mark-item-at-point-as-read').
|
|
261 ;; * Added possibility to mark single item as immortal (bound to key
|
|
262 ;; `i' (`newsticker-mark-item-at-point-as-immortal').
|
|
263 ;; * Added possibility to display feed logos.
|
|
264 ;; * Added `newsticker-heading-format', `newsticker-item-format'.
|
|
265 ;; * Added `newsticker-date-format'.
|
|
266 ;; * Added `newsticker-justification'.
|
|
267 ;; * Added `newsticker-automatically-mark-visited-items-as-old'.
|
|
268 ;; * Added `newsticker-w3m-toggle-inline-image' which calls
|
|
269 ;; `w3m-toggle-inline-image' if `newsticker-html-renderer' is
|
|
270 ;; `w3m-region'. Exists for convenience only (bound to key
|
|
271 ;; `RET').
|
|
272
|
|
273 ;; 1.3 * Compare title AND desc to check whether item is old, except
|
|
274 ;; for feed desc
|
|
275 ;; * Mark as not-up-to-date only after new items have arrived.
|
|
276 ;; * Added XEmacs compatibility code, tested with XEmacs 21.4.13.
|
|
277 ;; * Tested with Emacs 21.3.50 and Emacs 21.2.something.
|
|
278 ;; * Bugfix: Apply coding-systems to feed title and description,
|
|
279 ;; thanks to OHASHI Akira
|
|
280 ;; * Bugfix: xml-parser-workaround did not work for japanese texts,
|
|
281 ;; thanks to OHASHI Akira
|
|
282 ;; * Kill wget-buffers unless newsticker-debug is not nil.
|
|
283 ;; * Bugfix: xml-parser-workaround for "DOCTYPE rdf:RDF"
|
|
284
|
|
285 ;; 1.2 Peter S Galbraith <psg@debian.org>
|
|
286 ;; * Added `newsticker-url-list-defaults', splitting the URLs into
|
|
287 ;; a customizable selection list, and a user add-on list.
|
|
288 ;; * Minor checkdoc fixes.
|
|
289
|
|
290 ;; 1.1 * Introduced optional feed-specific wget-arguments.
|
|
291 ;; * Keep order of feeds as given in `newsticker-url-list' in
|
|
292 ;; *newsticker* buffer.
|
|
293 ;; * Ignore unsupported coding systems.
|
|
294
|
|
295 ;; 1.0 * Introduced feed-specific retrieval-timers.
|
|
296 ;; * Removed dependency on 'cl (cddddr).
|
|
297 ;; * Thanks to Kevin Rodgers and T.V. Raman for their help.
|
|
298 ;; * Use utf-8 for reading and writing cache data.
|
|
299 ;; * Reported to work with Emacs 21.3.50.
|
|
300
|
|
301 ;; 0.99 * Minor tweaks.
|
|
302 ;; * Tested with Emacs 21.3.2
|
|
303
|
|
304 ;; 0.98 * Check exit status of wget processes. Keep cache data if
|
|
305 ;; something went wrong. Throw error when old wget-processes
|
|
306 ;; are hanging around.
|
|
307 ;; * Introduced newsticker-specific faces.
|
|
308 ;; * Added `newsticker-show-descriptions-of-new-items'.
|
|
309 ;; * Added `newsticker-hide-old-items-in-newsticker-buffer'.
|
|
310 ;; * Added `newsticker-(hide|show)-old-items'.
|
|
311
|
|
312 ;; 0.97 * Minor tweaks.
|
|
313
|
|
314 ;; 0.96 * Added caching.
|
|
315 ;; * newsticker-mode inherits outline-mode.
|
|
316 ;; * newsticker-mode supports imenu.
|
|
317 ;; * Easy buffer-navigation with newsticker-mode's keymap.
|
|
318 ;; * Some bugs fixed.
|
|
319 ;; * Thanks to Moritz Epple for documentation tips.
|
|
320
|
|
321 ;; 0.95 * Added newsticker-mode -- Thanks to T.V. Raman.
|
|
322 ;; * Catch xml-parser errors -- Thanks to T.V. Raman.
|
|
323 ;; * Remove stupid newlines in titles (headlines) -- Thanks to
|
|
324 ;; Jeff Rancier.
|
|
325
|
|
326 ;; 0.94 * Added clickerability and description for channel headings.
|
|
327 ;; * Made it work for (at least some) rss 0.9<something> feeds.
|
|
328
|
|
329 ;; 0.93 * Added some more sites.
|
|
330 ;; * Do not flood the *Messages* buffer.
|
|
331 ;; * First attempt at handling coding systems.
|
|
332
|
|
333 ;; 0.92 * Added `newsticker-wget-name'.
|
|
334 ;; * Try to display message only if minibuffer and echo area are
|
|
335 ;; not in use already.
|
|
336 ;; * Dirty workaround for newer versions of xml.el: Remove
|
|
337 ;; whitespace in rdf.
|
|
338 ;; * Tested with Emacs 21.3.2 and CVS-snapshot of 2003-06-21.
|
|
339
|
|
340 ;; 0.91 * First bugfix: *newsticker* is read-only.
|
|
341
|
|
342 ;; 0.9 * First release.
|
|
343 ;; * Tested with Emacs 21.3.2 and wget 1.8.2.
|
|
344
|
|
345 ;; ======================================================================
|
|
346 ;;; To Do:
|
|
347
|
|
348 ;; * Image handling for XEmacs (create-image does not exist)
|
|
349
|
|
350 ;; ======================================================================
|
|
351 ;;; Code:
|
|
352
|
|
353 (require 'derived)
|
|
354 (require 'xml)
|
|
355
|
|
356 ;; ======================================================================
|
|
357 ;;; Customizables
|
|
358 ;; ======================================================================
|
|
359 (defgroup newsticker nil
|
|
360 "RSS aggregator."
|
|
361 :group 'applications)
|
|
362
|
|
363 (defconst newsticker--raw-url-list-defaults
|
|
364 '(("CNET News.com"
|
|
365 "http://export.cnet.com/export/feeds/news/rss/1,11176,,00.xml")
|
|
366 ("Debian Security Advisories"
|
|
367 "http://www.debian.org/security/dsa.en.rdf")
|
|
368 ("Debian Security Advisories - Long format"
|
|
369 "http://www.debian.org/security/dsa-long.en.rdf")
|
|
370 ("Emacs Wiki"
|
|
371 "http://www.emacswiki.org/cgi-bin/wiki.pl?action=rss"
|
|
372 nil
|
|
373 3600)
|
|
374 ("Freshmeat.net"
|
|
375 "http://freshmeat.net/backend/fm.rdf")
|
|
376 ("Kuro5hin.org"
|
|
377 "http://www.kuro5hin.org/backend.rdf")
|
|
378 ("LWN (Linux Weekly News)"
|
|
379 "http://lwn.net/headlines/rss")
|
|
380 ("NewsForge"
|
|
381 "http://newsforge.com/index.rss")
|
|
382 ("NY Times: Technology"
|
|
383 "http://partners.userland.com/nytRss/technology.xml")
|
|
384 ("NY Times"
|
|
385 "http://partners.userland.com/nytRss/nytHomepage.xml")
|
|
386 ("Quote of the day"
|
|
387 "http://www.quotationspage.com/data/qotd.rss"
|
|
388 "07:00"
|
|
389 86400)
|
|
390 ("The Register"
|
|
391 "http://www.theregister.co.uk/tonys/slashdot.rdf")
|
|
392 ("slashdot"
|
|
393 "http://slashdot.org/index.rss"
|
|
394 nil
|
|
395 3600) ;/. will ban you if under 3600 seconds!
|
|
396 ("Wired News"
|
|
397 "http://www.wired.com/news_drop/netcenter/netcenter.rdf")
|
|
398 ("Heise News (german)"
|
|
399 "http://www.heise.de/newsticker/heise.rdf")
|
|
400 ("Tagesschau (german)"
|
|
401 "http://www.tagesschau.de/newsticker.rdf"
|
|
402 nil
|
|
403 1800)
|
|
404 ("Telepolis (german)"
|
|
405 "http://www.heise.de/tp/news.rdf"))
|
|
406 "Default URL list in raw form.
|
|
407 This list is fed into defcustom via `newsticker--splicer'.")
|
|
408
|
|
409 (defun newsticker--splicer (item)
|
|
410 "Convert ITEM for splicing into `newsticker-url-list-defaults'."
|
|
411 (let ((result (list 'list :tag (nth 0 item) (list 'const (nth 0 item))))
|
|
412 (element (cdr item)))
|
|
413 (while element
|
|
414 (setq result (append result (list (list 'const (car element)))))
|
|
415 (setq element (cdr element)))
|
|
416 result))
|
|
417
|
|
418 ;; ======================================================================
|
|
419 ;;; Customization
|
|
420 ;; ======================================================================
|
|
421 (defun newsticker--set-customvar (symbol value)
|
|
422 "Set newsticker-variable SYMBOL value to VALUE.
|
|
423
|
|
424 Calls all necessary actions which are necessary in order to make
|
|
425 the new value effective. Changing `newsticker-url-list', for example,
|
|
426 will re-start the retrieval-timers."
|
|
427 (unless (condition-case nil
|
|
428 (eq (symbol-value symbol) value)
|
|
429 (error nil))
|
|
430 (set symbol value)
|
|
431 (cond ((eq symbol 'newsticker-sort-method)
|
|
432 (when (fboundp 'newsticker--cache-sort)
|
|
433 (message "Applying new sort method...")
|
|
434 (newsticker--cache-sort)
|
|
435 (newsticker--buffer-set-uptodate nil)
|
|
436 (message "Applying new sort method...done")))
|
|
437 ((memq symbol '(newsticker-url-list-defaults
|
|
438 newsticker-url-list
|
|
439 newsticker-retrieval-interval))
|
|
440 (when (and (fboundp 'newsticker-running-p)
|
|
441 (newsticker-running-p))
|
|
442 (message "Restarting newsticker")
|
|
443 (newsticker-stop)
|
|
444 (newsticker-start)))
|
|
445 ((eq symbol 'newsticker-display-interval)
|
|
446 (when (and (fboundp 'newsticker-running-p)
|
|
447 (newsticker-running-p))
|
|
448 (message "Restarting ticker")
|
|
449 (newsticker-stop-ticker)
|
|
450 (newsticker-start-ticker)
|
|
451 (message "")))
|
|
452 ((memq symbol '(newsticker-hide-old-items-in-echo-area
|
|
453 newsticker-hide-obsolete-items-in-echo-area
|
|
454 newsticker-hide-immortal-items-in-echo-area))
|
|
455 (when (and (fboundp 'newsticker-running-p)
|
|
456 (newsticker-running-p))
|
|
457 (message "Restarting newsticker")
|
|
458 (newsticker-stop-ticker)
|
|
459 (newsticker--ticker-text-setup)
|
|
460 (newsticker-start-ticker)
|
|
461 (message "")))
|
|
462 ((memq symbol '(newsticker-hide-old-items-in-newsticker-buffer
|
|
463 newsticker-show-descriptions-of-new-items))
|
|
464 (when (fboundp 'newsticker--buffer-set-uptodate)
|
|
465 (newsticker--buffer-set-uptodate nil)))
|
|
466 ((memq symbol '(newsticker-heading-format
|
|
467 newsticker-item-format
|
|
468 newsticker-desc-format
|
|
469 newsticker-date-format
|
|
470 newsticker-statistics-format
|
|
471 newsticker-justification
|
|
472 newsticker-use-full-width
|
|
473 newsticker-html-renderer
|
|
474 newsticker-feed-face
|
|
475 newsticker-new-item-face
|
|
476 newsticker-old-item-face
|
|
477 newsticker-immortal-item-face
|
|
478 newsticker-obsolete-item-face
|
|
479 newsticker-date-face
|
|
480 newsticker-statistics-face
|
|
481 ;;newsticker-default-face
|
|
482 ))
|
|
483 (when (fboundp 'newsticker--forget-preformatted)
|
|
484 (newsticker--forget-preformatted)))
|
|
485 (t
|
|
486 (error "Ooops %s" symbol)))))
|
|
487
|
|
488 ;; customization group feed
|
|
489 (defgroup newsticker-feed nil
|
|
490 "Settings for the RSS feeds."
|
|
491 :group 'newsticker)
|
|
492
|
|
493 (defcustom newsticker-url-list-defaults
|
|
494 '(("Emacs Wiki"
|
|
495 "http://www.emacswiki.org/cgi-bin/wiki.pl?action=rss"
|
|
496 nil
|
|
497 3600))
|
|
498 "A customizable list of news feeds to select from.
|
|
499 These were mostly extracted from the Radio Community Server at
|
|
500 http://subhonker6.userland.com/rcsPublic/rssHotlist.
|
|
501
|
|
502 You may add other entries in `newsticker-url-list'."
|
|
503 :type `(set ,@(mapcar `newsticker--splicer
|
|
504 newsticker--raw-url-list-defaults))
|
|
505 :set 'newsticker--set-customvar
|
|
506 :group 'newsticker-feed)
|
|
507
|
|
508 (defcustom newsticker-url-list nil
|
|
509 "The news feeds which you like to watch.
|
|
510
|
|
511 This alist will be used in addition to selection made customizing
|
|
512 `newsticker-url-list-defaults'.
|
|
513
|
|
514 This is an alist. Each element consists of two items: a LABEL and a URL,
|
|
515 optionally followed by a START-TIME, INTERVAL specifier and WGET-ARGUMENTS.
|
|
516
|
|
517 The LABEL gives the name of the news feed. It can be an arbitrary string.
|
|
518
|
|
519 The URL gives the location of the news feed. It must point to a valid
|
|
520 RSS file. The RSS file is retrieved by calling wget, or whatever you
|
|
521 specify as `newsticker-wget-name'.
|
|
522
|
|
523 The START-TIME can be either a string, or nil. If it is a string it
|
|
524 specifies a fixed time at which this feed shall be retrieved for the
|
|
525 first time. (Examples: \"11:00pm\", \"23:00\"). If it is nil (or
|
|
526 unspecified), this feed will be retrieved immediately after calling
|
|
527 `newsticker-start'.
|
|
528
|
|
529 The INTERVAL specifies the time between retrievals for this feed. If it
|
|
530 is nil (or unspecified) the default interval value as set in
|
|
531 `newsticker-retrieval-interval' is used.
|
|
532
|
|
533 \(newsticker.el calls `run-at-time'. The newsticker-parameters START-TIME
|
|
534 and INTERVAL correspond to the `run-at-time'-parameters TIME and REPEAT.)
|
|
535
|
|
536 WGET-ARGUMENTS specifies arguments for wget (see `newsticker-wget-name')
|
|
537 which apply for this feed only, overriding the value of
|
|
538 `newsticker-wget-arguments'."
|
|
539 :type '(repeat (list :tag "News feed"
|
|
540 (string :tag "Label")
|
|
541 (string :tag "URI")
|
|
542 (choice :tag "Start"
|
|
543 (const :tag "Default" nil)
|
|
544 (string :tag "Fixed Time"))
|
|
545 (choice :tag "Interval"
|
|
546 (const :tag "Default" nil)
|
|
547 (const :tag "Hourly" 3600)
|
|
548 (const :tag "Daily" 86400)
|
|
549 (integer :tag "Interval"))
|
|
550 (choice :tag "Wget Arguments"
|
|
551 (const :tag "Default arguments" nil)
|
|
552 (repeat :tag "Special arguments" string))))
|
|
553 :set 'newsticker--set-customvar
|
|
554 :group 'newsticker-feed)
|
|
555
|
|
556 (defcustom newsticker-wget-name
|
|
557 "wget"
|
|
558 "Name of the program which is called to retrieve news from the web.
|
|
559 The canonical choice is wget but you may take any other program which is
|
|
560 able to return the contents of a news feed file on stdout."
|
|
561 :type 'string
|
|
562 :group 'newsticker-feed)
|
|
563
|
|
564 (defcustom newsticker-wget-arguments
|
|
565 '("-q" "-O" "-")
|
|
566 "Arguments which are passed to wget.
|
|
567 There is probably no reason to change the default settings, unless you
|
|
568 are living behind a firewall."
|
|
569 :type '(repeat (string :tag "Argument"))
|
|
570 :group 'newsticker-feed)
|
|
571
|
|
572 (defcustom newsticker-retrieval-interval
|
|
573 3600
|
|
574 "Time interval for retrieving new news items (seconds).
|
|
575 If this value is not positive (i.e. less than or equal to 0)
|
|
576 items are retrieved only once!
|
|
577 Please note that some feeds, e.g. Slashdot, will ban you if you
|
|
578 make it less than 1800 seconds (30 minutes)!"
|
|
579 :type '(choice :tag "Interval"
|
|
580 (const :tag "No automatic retrieval" 0)
|
|
581 (const :tag "Hourly" 3600)
|
|
582 (const :tag "Daily" 86400)
|
|
583 (integer :tag "Interval"))
|
|
584 :set 'newsticker--set-customvar
|
|
585 :group 'newsticker-feed)
|
|
586
|
|
587 (defcustom newsticker-desc-comp-max
|
|
588 100
|
|
589 "Relevant length of headline descriptions.
|
|
590 This value gives the maximum number of characters which will be
|
|
591 taken into account when newsticker compares two headline
|
|
592 descriptions."
|
|
593 :type 'integer
|
|
594 :group 'newsticker-feed)
|
|
595
|
|
596 ;; customization group behaviour
|
|
597 (defgroup newsticker-headline-processing nil
|
|
598 "Settings for the automatic processing of RSS headlines."
|
|
599 :group 'newsticker)
|
|
600
|
|
601 (defcustom newsticker-automatically-mark-items-as-old
|
|
602 t
|
|
603 "Decides whether to automatically mark items as old.
|
|
604 If t a new item is considered as new only after its first retrieval. As
|
|
605 soon as it is retrieved a second time, it becomes old. If not t all
|
|
606 items stay new until you mark them as old. This is done in the
|
|
607 *newsticker* buffer."
|
|
608 :type 'boolean
|
|
609 :group 'newsticker-headline-processing)
|
|
610
|
|
611 (defcustom newsticker-automatically-mark-visited-items-as-old
|
|
612 t
|
|
613 "Decides whether to automatically mark visited items as old.
|
|
614 If t an item is marked as old as soon as the associated link is
|
|
615 visited, i.e. after pressing RET or mouse2 on the item's
|
|
616 headline."
|
|
617
|
|
618 :type 'boolean
|
|
619 :group 'newsticker-headline-processing)
|
|
620
|
|
621 (defcustom newsticker-keep-obsolete-items
|
|
622 t
|
|
623 "Decides whether to keep unread items which have been removed from feed.
|
|
624 If t a new item, which has been removed from the feed, is kept in
|
|
625 the cache until it is marked as read."
|
|
626 :type 'boolean
|
|
627 :group 'newsticker-headline-processing)
|
|
628
|
|
629 (defcustom newsticker-obsolete-item-max-age
|
|
630 (* 60 60 24)
|
|
631 "Maximal age of obsolete items, in seconds.
|
|
632 Obsolete items which are older than this value will be silently
|
|
633 deleted at the next retrieval."
|
|
634 :type 'integer
|
|
635 :group 'newsticker-headline-processing)
|
|
636
|
|
637 (defcustom newsticker-auto-mark-filter
|
|
638 nil
|
|
639 "A filter for automatically marking headlines.
|
|
640
|
|
641 This is an alist of the form (FEED-NAME OLD-LIST IMMORTAL-LIST). I.e. each
|
|
642 element consists of a FEED-NAME and two lists. Each list consists a set of
|
|
643 regular expressions. The first list contains patterns of headlines which
|
|
644 will be marked as old. The second list contains patterns of headlines which
|
|
645 will be marked as immortal.
|
|
646
|
|
647 This filter is checked after a new headline has been retrieved. If
|
|
648 FEED-NAME matches the name of the corresponding news feed, both sublists
|
|
649 are checked: If the title of the headline matches any of the regular
|
|
650 expressions in OLD-LIST, this headline is marked as old, if it matches any
|
|
651 of the expressions in IMMORTAL-LIST it is marked as immortal.
|
|
652
|
|
653 If, for example, `newsticker-auto-mark-filter' looks like
|
|
654 \((slashdot (\"^Forget me!$\") (\"^Read me$\" \"important\")))
|
|
655 then all articles from slashdot are marked as old if they have the title
|
|
656 \"Forget me!\". All articles which have the title \"Read me\" and all
|
|
657 articles which contain the string \"important\" in their title are marked
|
|
658 as immortal."
|
|
659 :type '(repeat (list :tag "Feed filter rule"
|
|
660 (string :tag "Feed name")
|
|
661 ;;(choice ,@(mapcar (lambda (i)
|
|
662 ;; (list :tag (car i) (car i)))
|
|
663 ;; newsticker-url-list))
|
|
664 (repeat :tag "Mark as old" string)
|
|
665 (repeat :tag "Mark as immortal" string)))
|
|
666 :group 'newsticker-headline-processing)
|
|
667
|
|
668 ;; customization group layout
|
|
669 (defgroup newsticker-layout nil
|
|
670 "Settings for layout of the RSS reader."
|
|
671 :group 'newsticker)
|
|
672
|
|
673 (defcustom newsticker-sort-method
|
|
674 'sort-by-original-order
|
|
675 "Sort method for news items.
|
|
676 The following sort methods are available:
|
|
677 * `sort-by-original-order' keeps the order in which the items
|
|
678 appear in the RSS file (please note that for immortal items,
|
|
679 which have been removed from the news feed, there is no original
|
|
680 order),
|
|
681 * `sort-by-time' looks at the time at which an item has been seen
|
|
682 the first time. The most recent item is put at top,
|
|
683 * `sort-by-title' will put the items in an alphabetical order."
|
|
684 :type '(choice
|
|
685 (const :tag "Keep original order" sort-by-original-order)
|
|
686 (const :tag "Sort by time" sort-by-time)
|
|
687 (const :tag "Sort by title" sort-by-title))
|
|
688 :set 'newsticker--set-customvar
|
|
689 :group 'newsticker-layout)
|
|
690
|
|
691 (defcustom newsticker-hide-old-items-in-newsticker-buffer
|
|
692 nil
|
|
693 "Decides whether to automatically hide old items in the *newsticker* buffer.
|
|
694 If set to t old items will be completely folded and only new items
|
|
695 will show up in the *newsticker* buffer. Otherwise old as well as new
|
|
696 items will be visible."
|
|
697 :type 'boolean
|
|
698 :set 'newsticker--set-customvar
|
|
699 :group 'newsticker-layout)
|
|
700
|
|
701 (defcustom newsticker-show-descriptions-of-new-items
|
|
702 t
|
|
703 "Whether to automatically show descriptions of new items in *newsticker*.
|
|
704 If set to t old items will be folded and new items will be
|
|
705 unfolded. Otherwise old as well as new items will be folded."
|
|
706 :type 'boolean
|
|
707 :set 'newsticker--set-customvar
|
|
708 :group 'newsticker-layout)
|
|
709
|
|
710 (defcustom newsticker-heading-format
|
|
711 "%l
|
|
712 %t %d %s"
|
|
713 "Format string for feed headings.
|
|
714 The following printf-like specifiers can be used:
|
|
715 %d The date the feed was retrieved. See `newsticker-date-format'.
|
|
716 %l The logo (image) of the feed. Most RSS feeds provide a small
|
|
717 image as logo. Newsticker can display them, if Emacs can --
|
|
718 see `image-types' for a list of supported image types.
|
|
719 %L The logo (image) of the feed. If the logo is not available
|
|
720 the title of the feed is used.
|
|
721 %s The statistical data of the feed. See `newsticker-statistics-format'.
|
|
722 %t The title of the feed, i.e. its name."
|
|
723 :type 'string
|
|
724 :set 'newsticker--set-customvar
|
|
725 :group 'newsticker-layout)
|
|
726
|
|
727 (defcustom newsticker-item-format
|
|
728 "%t %d"
|
|
729 "Format string for news item headlines.
|
|
730 The following printf-like specifiers can be used:
|
|
731 %d The date the item was (first) retrieved. See `newsticker-date-format'.
|
|
732 %l The logo (image) of the feed. Most RSS feeds provide a small
|
|
733 image as logo. Newsticker can display them, if Emacs can --
|
|
734 see `image-types' for a list of supported image types.
|
|
735 %L The logo (image) of the feed. If the logo is not available
|
|
736 the title of the feed is used.
|
|
737 %t The title of the item."
|
|
738 :type 'string
|
|
739 :set 'newsticker--set-customvar
|
|
740 :group 'newsticker-layout)
|
|
741
|
|
742 (defcustom newsticker-desc-format
|
|
743 "%d %c"
|
|
744 "Format string for news descriptions (contents).
|
|
745 The following printf-like specifiers can be used:
|
|
746 %c The contents (description) of the item.
|
|
747 %d The date the item was (first) retrieved. See `newsticker-date-format'."
|
|
748 :type 'string
|
|
749 :set 'newsticker--set-customvar
|
|
750 :group 'newsticker-layout)
|
|
751
|
|
752 (defcustom newsticker-date-format
|
|
753 "(%A, %H:%M)"
|
|
754 "Format for the date part in item and feed lines.
|
|
755 See `format-time-string' for a list of valid specifiers."
|
|
756 :type 'string
|
|
757 :set 'newsticker--set-customvar
|
|
758 :group 'newsticker-layout)
|
|
759
|
|
760 (defcustom newsticker-statistics-format
|
|
761 "[%n + %i + %o + %O = %a]"
|
|
762 "Format for the statistics part in feed lines.
|
|
763 The following printf-like specifiers can be used:
|
|
764 %a The number of all items in the feed.
|
|
765 %i The number of immortal items in the feed.
|
|
766 %n The number of new items in the feed.
|
|
767 %o The number of old items in the feed.
|
|
768 %O The number of obsolete items in the feed."
|
|
769 :type 'string
|
|
770 :set 'newsticker--set-customvar
|
|
771 :group 'newsticker-layout)
|
|
772
|
|
773 (defcustom newsticker-show-all-rss-elements
|
|
774 nil
|
|
775 "Show all RSS elements."
|
|
776 :type 'boolean
|
|
777 ;;:set 'newsticker--set-customvar
|
|
778 :group 'newsticker-layout)
|
|
779
|
|
780 ;; image related things
|
|
781 (defcustom newsticker-enable-logo-manipulations
|
|
782 t
|
|
783 "If non-nil newsticker manipulates logo images.
|
|
784 This enables the following image properties: heuristic mask for all
|
|
785 logos, and laplace-conversion for images without new items."
|
|
786 :type 'boolean
|
|
787 :group 'newsticker-layout)
|
|
788
|
|
789
|
|
790 ;; rendering
|
|
791 (defcustom newsticker-justification
|
|
792 'left
|
|
793 "How to fill item descriptions.
|
|
794 If non-nil newsticker calls `fill-region' to wrap long lines in
|
|
795 item descriptions. However, if an item description contains HTML
|
|
796 text and `newsticker-html-renderer' is non-nil, filling is not
|
|
797 done."
|
|
798 :type '(choice :tag "Justification"
|
|
799 (const :tag "No filling" nil)
|
|
800 (const :tag "Left" left)
|
|
801 (const :tag "Right" right)
|
|
802 (const :tag "Center" center)
|
|
803 (const :tag "Full" full))
|
|
804 :set 'newsticker--set-customvar
|
|
805 :group 'newsticker-layout)
|
|
806
|
|
807 (defcustom newsticker-use-full-width
|
|
808 t
|
|
809 "Decides whether to use the full window width when filling.
|
|
810 If non-nil newsticker sets `fill-column' so that the whole
|
|
811 window is used when filling. See also `newsticker-justification'."
|
|
812 :type 'boolean
|
|
813 :set 'newsticker--set-customvar
|
|
814 :group 'newsticker-layout)
|
|
815
|
|
816 (defcustom newsticker-html-renderer
|
|
817 nil
|
|
818 "Function for rendering HTML contents.
|
|
819 If non-nil, newsticker.el will call this function whenever it finds
|
|
820 HTML-like tags in item descriptions. Possible functions are, for
|
|
821 example, `w3m-region', `w3-region', and (if you have htmlr.el installed)
|
|
822 `newsticker-htmlr-render'.
|
|
823
|
|
824 In order to make sure that the HTML renderer is loaded when you
|
|
825 run newsticker, you should add one of the following statements to
|
|
826 your .emacs. If you use w3m,
|
|
827
|
|
828 (autoload 'w3m-region \"w3m\"
|
|
829 \"Render region in current buffer and replace with result.\" t)
|
|
830
|
|
831 or, if you use w3,
|
|
832
|
|
833 (require 'w3-auto)
|
|
834
|
|
835 or, if you use htmlr
|
|
836
|
|
837 (require 'htmlr)"
|
|
838 :type '(choice :tag "Function"
|
|
839 (const :tag "None" nil)
|
|
840 (const :tag "w3" w3-region)
|
|
841 (const :tag "w3m" w3m-region)
|
|
842 (const :tag "htmlr" newsticker-htmlr-render))
|
|
843 :set 'newsticker--set-customvar
|
|
844 :group 'newsticker-layout)
|
|
845
|
|
846
|
|
847 ;; faces
|
|
848 (defgroup newsticker-faces nil
|
|
849 "Settings for the faces of the RSS reader."
|
|
850 :group 'newsticker-layout)
|
|
851
|
|
852 (defface newsticker-feed-face
|
|
853 '((((class color) (background dark))
|
|
854 (:family "helvetica" :bold t :height 1.2 :foreground "misty rose"))
|
|
855 (((class color) (background light))
|
|
856 (:family "helvetica" :bold t :height 1.2 :foreground "black")))
|
|
857 "Face for news feeds."
|
|
858 :group 'newsticker-faces)
|
|
859
|
|
860 (defface newsticker-new-item-face
|
|
861 '((((class color) (background dark))
|
|
862 (:family "helvetica" :bold t))
|
|
863 (((class color) (background light))
|
|
864 (:family "helvetica" :bold t)))
|
|
865 "Face for old news items."
|
|
866 :group 'newsticker-faces)
|
|
867
|
|
868 (defface newsticker-old-item-face
|
|
869 '((((class color) (background dark))
|
|
870 (:family "helvetica" :bold t :foreground "orange3"))
|
|
871 (((class color) (background light))
|
|
872 (:family "helvetica" :bold t :foreground "red4")))
|
|
873 "Face for old news items."
|
|
874 :group 'newsticker-faces)
|
|
875
|
|
876 (defface newsticker-immortal-item-face
|
|
877 '((((class color) (background dark))
|
|
878 (:family "helvetica" :bold t :italic t :foreground "orange"))
|
|
879 (((class color) (background light))
|
|
880 (:family "helvetica" :bold t :italic t :foreground "blue")))
|
|
881 "Face for immortal news items."
|
|
882 :group 'newsticker-faces)
|
|
883
|
|
884 (defface newsticker-obsolete-item-face
|
|
885 '((((class color) (background dark))
|
|
886 (:family "helvetica" :bold t :strike-through t))
|
|
887 (((class color) (background light))
|
|
888 (:family "helvetica" :bold t :strike-through t)))
|
|
889 "Face for old news items."
|
|
890 :group 'newsticker-faces)
|
|
891
|
|
892 (defface newsticker-date-face
|
|
893 '((((class color) (background dark))
|
|
894 (:family "helvetica" :italic t :height 0.8))
|
|
895 (((class color) (background light))
|
|
896 (:family "helvetica" :italic t :height 0.8)))
|
|
897 "Face for newsticker dates."
|
|
898 :group 'newsticker-faces)
|
|
899
|
|
900 (defface newsticker-statistics-face
|
|
901 '((((class color) (background dark))
|
|
902 (:family "helvetica" :italic t :height 0.8))
|
|
903 (((class color) (background light))
|
|
904 (:family "helvetica" :italic t :height 0.8)))
|
|
905 "Face for newsticker dates."
|
|
906 :group 'newsticker-faces)
|
|
907
|
|
908 (defface newsticker-enclosure-face
|
|
909 '((((class color) (background dark))
|
|
910 (:bold t :background "orange"))
|
|
911 (((class color) (background light))
|
|
912 (:bold t :background "orange")))
|
|
913 "Face for enclosed elements."
|
|
914 :group 'newsticker-faces)
|
|
915
|
|
916 (defface newsticker-extra-face
|
|
917 '((((class color) (background dark))
|
|
918 (:italic t :foreground "gray50" :height 0.8))
|
|
919 (((class color) (background light))
|
|
920 (:italic t :foreground "gray50" :height 0.8)))
|
|
921 "Face for newsticker dates."
|
|
922 :group 'newsticker-faces)
|
|
923
|
|
924 ;; (defface newsticker-default-face
|
|
925 ;; '((((class color) (background dark))
|
|
926 ;; (:inherit default))
|
|
927 ;; (((class color) (background light))
|
|
928 ;; (:inherit default)))
|
|
929 ;; "Face for the description of news items."
|
|
930 ;; ;;:set 'newsticker--set-customvar
|
|
931 ;; :group 'newsticker-faces)
|
|
932
|
|
933
|
|
934 ;; customization group ticker
|
|
935 (defgroup newsticker-ticker nil
|
|
936 "Settings for the RSS ticker."
|
|
937 :group 'newsticker)
|
|
938
|
|
939 (defcustom newsticker-display-interval
|
|
940 0.3
|
|
941 "Time interval for displaying news items in the echo area (seconds).
|
|
942 If equal or less than 0 no messages are shown in the echo area. For
|
|
943 smooth display (see `newsticker-scroll-smoothly') a value of 0.3 seems
|
|
944 reasonable. For non-smooth display a value of 10 is a good starting
|
|
945 point."
|
|
946 :type 'number
|
|
947 :set 'newsticker--set-customvar
|
|
948 :group 'newsticker-ticker)
|
|
949
|
|
950 (defcustom newsticker-scroll-smoothly
|
|
951 t
|
|
952 "Decides whether to flash or scroll news items.
|
|
953 If t the news headlines are scrolled (more-or-less) smoothly in the echo
|
|
954 area. If nil one headline after another is displayed in the echo area.
|
|
955 The variable `newsticker-display-interval' determines how fast this
|
|
956 display moves/changes and whether headlines are shown in the echo area
|
|
957 at all. If you change `newsticker-scroll-smoothly' you should also change
|
|
958 `newsticker-display-interval'."
|
|
959 :type 'boolean
|
|
960 :group 'newsticker-ticker)
|
|
961
|
|
962 (defcustom newsticker-hide-immortal-items-in-echo-area
|
|
963 t
|
|
964 "Decides whether to show immortal/non-expiring news items in the ticker.
|
|
965 If t the echo area will not show immortal items. See also
|
|
966 `newsticker-hide-old-items-in-echo-area."
|
|
967 :type 'boolean
|
|
968 :set 'newsticker--set-customvar
|
|
969 :group 'newsticker-ticker)
|
|
970
|
|
971 (defcustom newsticker-hide-old-items-in-echo-area
|
|
972 t
|
|
973 "Decides whether to show only the newest news items in the ticker.
|
|
974 If t the echo area will show only new items, i.e. only items which have
|
|
975 been added between the last two retrievals."
|
|
976 :type 'boolean
|
|
977 :set 'newsticker--set-customvar
|
|
978 :group 'newsticker-ticker)
|
|
979
|
|
980 (defcustom newsticker-hide-obsolete-items-in-echo-area
|
|
981 t
|
|
982 "Decides whether to show obsolete items items in the ticker.
|
|
983 If t the echo area will not show obsolete items. See also
|
|
984 `newsticker-hide-old-items-in-echo-area."
|
|
985 :type 'boolean
|
|
986 :set 'newsticker--set-customvar
|
|
987 :group 'newsticker-ticker)
|
|
988
|
|
989 (defgroup newsticker-hooks nil
|
|
990 "Settings for newsticker hooks."
|
|
991 :group 'newsticker)
|
|
992
|
|
993 (defcustom newsticker-start-hook
|
|
994 nil
|
|
995 "Hook run when starting newsticker.
|
|
996 This hook is run at the very end of `newsticker-start'."
|
|
997 :options '(newsticker-start-ticker)
|
|
998 :type 'hook
|
|
999 :group 'newsticker-hooks)
|
|
1000
|
|
1001 (defcustom newsticker-stop-hook
|
|
1002 nil
|
|
1003 "Hook run when stopping newsticker.
|
|
1004 This hook is run at the very end of `newsticker-stop'."
|
|
1005 :options nil
|
|
1006 :type 'hook
|
|
1007 :group 'newsticker-hooks)
|
|
1008
|
|
1009 (defcustom newsticker-new-item-functions
|
|
1010 nil
|
|
1011 "List of functions run after a new headline has been retrieved.
|
|
1012 Each function is called with the following three arguments:
|
|
1013 FEED the name of the corresponding news feed,
|
|
1014 TITLE the title of the headline,
|
|
1015 DESC the decoded description of the headline.
|
|
1016
|
|
1017 See `newsticker-download-images', and
|
|
1018 `newsticker-download-enclosures' for sample functions.
|
|
1019
|
|
1020 Please note that these functions are called only once for a
|
|
1021 headline after it has been retrieved for the first time."
|
|
1022 :type 'hook
|
|
1023 :options '(newsticker-download-images
|
|
1024 newsticker-download-enclosures)
|
|
1025 :group 'newsticker-hooks)
|
|
1026
|
|
1027 (defcustom newsticker-select-item-hook
|
|
1028 'newsticker--buffer-make-item-completely-visible
|
|
1029 "List of functions run after a headline has been selected.
|
|
1030 Each function is called after one of `newsticker-next-item',
|
|
1031 `newsticker-next-new-item', `newsticker-previous-item',
|
|
1032 `newsticker-previous-new-item' has been called.
|
|
1033
|
|
1034 The default value 'newsticker--buffer-make-item-completely-visible
|
|
1035 assures that the current item is always completely visible."
|
|
1036 :type 'hook
|
|
1037 :options '(newsticker--buffer-make-item-completely-visible)
|
|
1038 :group 'newsticker-hooks)
|
|
1039
|
|
1040 (defcustom newsticker-select-feed-hook
|
|
1041 'newsticker--buffer-make-item-completely-visible
|
|
1042 "List of functions run after a feed has been selected.
|
|
1043 Each function is called after one of `newsticker-next-feed', and
|
|
1044 `newsticker-previous-feed' has been called.
|
|
1045
|
|
1046 The default value 'newsticker--buffer-make-item-completely-visible
|
|
1047 assures that the current feed is completely visible."
|
|
1048 :type 'hook
|
|
1049 :options '(newsticker--buffer-make-item-completely-visible)
|
|
1050 :group 'newsticker-hooks)
|
|
1051
|
|
1052 (defcustom newsticker-buffer-change-hook
|
|
1053 'newsticker-w3m-show-inline-images
|
|
1054 "List of functions run after the newsticker buffer has been updated.
|
|
1055 Each function is called after `newsticker-buffer-update' has been called.
|
|
1056
|
|
1057 The default value '`newsticker-w3m-show-inline-images' loads inline
|
|
1058 images."
|
|
1059 :type 'hook
|
|
1060 :group 'newsticker-hooks)
|
|
1061
|
|
1062 (defcustom newsticker-narrow-hook
|
|
1063 'newsticker-w3m-show-inline-images
|
|
1064 "List of functions run after narrowing in newsticker buffer has changed.
|
|
1065 Each function is called after
|
|
1066 `newsticker-toggle-auto-narrow-to-feed' or
|
|
1067 `newsticker-toggle-auto-narrow-to-item' has been called.
|
|
1068
|
|
1069 The default value '`newsticker-w3m-show-inline-images' loads inline
|
|
1070 images."
|
|
1071 :type 'hook
|
|
1072 :group 'newsticker-hooks)
|
|
1073
|
|
1074 (defgroup newsticker-miscellaneous nil
|
|
1075 "Miscellaneous newsticker settings."
|
|
1076 :group 'newsticker)
|
|
1077
|
|
1078 (defcustom newsticker-cache-filename
|
|
1079 "~/.newsticker-cache"
|
|
1080 "Name of the newsticker cache file."
|
|
1081 :type 'string
|
|
1082 :group 'newsticker-miscellaneous)
|
|
1083
|
|
1084 (defcustom newsticker-imagecache-dirname
|
|
1085 "~/.newsticker-images"
|
|
1086 "Name of the directory where newsticker stores cached images."
|
|
1087 :type 'string
|
|
1088 :group 'newsticker-miscellaneous)
|
|
1089
|
|
1090 ;; debugging
|
|
1091 (defcustom newsticker-debug
|
|
1092 nil
|
|
1093 "Enables some features needed for debugging newsticker.el.
|
|
1094
|
|
1095 If set to t newsticker.el will print lots of debugging messages, and the
|
|
1096 buffers *newsticker-wget-<feed>* will not be closed."
|
|
1097 :type 'boolean
|
|
1098 ;;:set 'newsticker--set-customvar
|
|
1099 :group 'newsticker-miscellaneous)
|
|
1100
|
|
1101 ;; ======================================================================
|
|
1102 ;;; Compatibility section, XEmacs, Emacs
|
|
1103 ;; ======================================================================
|
|
1104 (unless (fboundp 'time-add)
|
|
1105 (require 'time-date);;FIXME
|
|
1106 (defun time-add (t1 t2)
|
|
1107 (seconds-to-time (+ (time-to-seconds t1) (time-to-seconds t2)))))
|
|
1108
|
|
1109 (unless (fboundp 'match-string-no-properties)
|
|
1110 (defalias 'match-string-no-properties 'match-string))
|
|
1111
|
|
1112 (unless (fboundp 'replace-regexp-in-string)
|
|
1113 (defun replace-regexp-in-string (re rp st)
|
|
1114 (save-match-data ;; apparently XEmacs needs save-match-data
|
|
1115 (replace-in-string st re rp))))
|
|
1116
|
|
1117 ;; copied from subr.el
|
|
1118 (unless (fboundp 'add-to-invisibility-spec)
|
|
1119 (defun add-to-invisibility-spec (arg)
|
|
1120 "Add elements to `buffer-invisibility-spec'.
|
|
1121 See documentation for `buffer-invisibility-spec' for the kind of elements
|
|
1122 that can be added."
|
|
1123 (if (eq buffer-invisibility-spec t)
|
|
1124 (setq buffer-invisibility-spec (list t)))
|
|
1125 (setq buffer-invisibility-spec
|
|
1126 (cons arg buffer-invisibility-spec))))
|
|
1127
|
|
1128 ;; copied from subr.el
|
|
1129 (unless (fboundp 'remove-from-invisibility-spec)
|
|
1130 (defun remove-from-invisibility-spec (arg)
|
|
1131 "Remove elements from `buffer-invisibility-spec'."
|
|
1132 (if (consp buffer-invisibility-spec)
|
|
1133 (setq buffer-invisibility-spec (delete arg buffer-invisibility-spec)))))
|
|
1134
|
|
1135 ;; ======================================================================
|
|
1136 ;;; Internal variables
|
|
1137 ;; ======================================================================
|
|
1138 (defvar newsticker--display-timer nil
|
|
1139 "Timer for newsticker display.")
|
|
1140 (defvar newsticker--retrieval-timer-list nil
|
|
1141 "List of timers for news retrieval.
|
|
1142 This is an alist, each element consisting of (feed-name . timer)")
|
|
1143 (defvar newsticker--item-list nil
|
|
1144 "List of newsticker items.")
|
|
1145 (defvar newsticker--item-position 0
|
|
1146 "Actual position in list of newsticker items.")
|
|
1147 (defvar newsticker--prev-message "There was no previous message yet!"
|
|
1148 "Last message that the newsticker displayed.")
|
|
1149 (defvar newsticker--scrollable-text ""
|
|
1150 "The text which is scrolled smoothly in the echo area.")
|
|
1151 (defvar newsticker--buffer-uptodate-p nil
|
|
1152 "Tells whether the newsticker buffer is up to date.")
|
|
1153 (defvar newsticker--latest-update-time (current-time)
|
|
1154 "The time at which the latest news arrived.")
|
|
1155
|
|
1156 (defvar newsticker--cache nil "Cached newsticker data.
|
|
1157 This is a list of the form
|
|
1158
|
|
1159 ((label1
|
|
1160 (title description link time age index preformatted-contents
|
|
1161 preformatted-title)
|
|
1162 ...)
|
|
1163 (label2
|
|
1164 (title description link time age index preformatted-contents
|
|
1165 preformatted-title)
|
|
1166 ...)
|
|
1167 ...)
|
|
1168
|
|
1169 where LABEL is a symbol. TITLE, DESCRIPTION, and LINK are
|
|
1170 strings. TIME is a time value as returned by `current-time'.
|
|
1171 AGE is a symbol: 'new, 'old, 'immortal, and 'obsolete denote
|
|
1172 ordinary news items, whereas 'feed denotes an item which is not a
|
|
1173 headline but describes the feed itself. INDEX denotes the
|
|
1174 original position of the item -- used for restoring the original
|
|
1175 order. PREFORMATTED-CONTENTS and PREFORMATTED-TITLE hold the
|
|
1176 formatted contents of the item's description and title. This
|
|
1177 speeds things up if HTML rendering is used, which is rather
|
|
1178 slow.")
|
|
1179
|
|
1180 (defvar newsticker--auto-narrow-to-feed nil
|
|
1181 "Automatically narrow to current news feed.
|
|
1182 If non-nil only the items of the current news feed are visible.")
|
|
1183
|
|
1184 (defvar newsticker--auto-narrow-to-item nil
|
|
1185 "Automatically narrow to current news item.
|
|
1186 If non-nil only the current headline is visible.")
|
|
1187
|
|
1188 (defconst newsticker--error-headline
|
|
1189 "[COULD NOT DOWNLOAD HEADLINES!]"
|
|
1190 "Title of error headline which will be inserted if news retrieval fails.")
|
|
1191
|
|
1192 ;; ======================================================================
|
|
1193 ;;; Toolbar
|
|
1194 ;; ======================================================================
|
|
1195 (defconst newsticker--next-item-image
|
|
1196 (if (fboundp 'create-image)
|
|
1197 (create-image "/* XPM */
|
|
1198 static char * next_xpm[] = {
|
|
1199 \"24 24 42 1\",
|
|
1200 \" c None\",
|
|
1201 \". c #000000\",
|
|
1202 \"+ c #7EB6DE\",
|
|
1203 \"@ c #82BBE2\",
|
|
1204 \"# c #85BEE4\",
|
|
1205 \"$ c #88C1E7\",
|
|
1206 \"% c #8AC3E8\",
|
|
1207 \"& c #87C1E6\",
|
|
1208 \"* c #8AC4E9\",
|
|
1209 \"= c #8CC6EA\",
|
|
1210 \"- c #8CC6EB\",
|
|
1211 \"; c #88C2E7\",
|
|
1212 \"> c #8BC5E9\",
|
|
1213 \", c #8DC7EB\",
|
|
1214 \"' c #87C0E6\",
|
|
1215 \") c #8AC4E8\",
|
|
1216 \"! c #8BC5EA\",
|
|
1217 \"~ c #8BC4E9\",
|
|
1218 \"{ c #88C1E6\",
|
|
1219 \"] c #89C3E8\",
|
|
1220 \"^ c #86BFE5\",
|
|
1221 \"/ c #83BBE2\",
|
|
1222 \"( c #82BBE1\",
|
|
1223 \"_ c #86C0E5\",
|
|
1224 \": c #87C0E5\",
|
|
1225 \"< c #83BCE2\",
|
|
1226 \"[ c #81B9E0\",
|
|
1227 \"} c #81BAE1\",
|
|
1228 \"| c #78B0D9\",
|
|
1229 \"1 c #7BB3DB\",
|
|
1230 \"2 c #7DB5DD\",
|
|
1231 \"3 c #7DB6DD\",
|
|
1232 \"4 c #72A9D4\",
|
|
1233 \"5 c #75ACD6\",
|
|
1234 \"6 c #76AED7\",
|
|
1235 \"7 c #77AFD8\",
|
|
1236 \"8 c #6BA1CD\",
|
|
1237 \"9 c #6EA4CF\",
|
|
1238 \"0 c #6FA6D1\",
|
|
1239 \"a c #6298C6\",
|
|
1240 \"b c #659BC8\",
|
|
1241 \"c c #5C91C0\",
|
|
1242 \" \",
|
|
1243 \" \",
|
|
1244 \" . \",
|
|
1245 \" .. \",
|
|
1246 \" .+. \",
|
|
1247 \" .@#. \",
|
|
1248 \" .#$%. \",
|
|
1249 \" .&*=-. \",
|
|
1250 \" .;>,,,. \",
|
|
1251 \" .;>,,,=. \",
|
|
1252 \" .')!==~;. \",
|
|
1253 \" .#{]*%;^/. \",
|
|
1254 \" .(#_':#<. \",
|
|
1255 \" .+[@</}. \",
|
|
1256 \" .|1232. \",
|
|
1257 \" .4567. \",
|
|
1258 \" .890. \",
|
|
1259 \" .ab. \",
|
|
1260 \" .c. \",
|
|
1261 \" .. \",
|
|
1262 \" . \",
|
|
1263 \" \",
|
|
1264 \" \",
|
|
1265 \" \"};
|
|
1266 "
|
|
1267 'xpm t)
|
|
1268 "Image for the next item button."))
|
|
1269
|
|
1270 (defconst newsticker--previous-item-image
|
|
1271 (if (fboundp 'create-image)
|
|
1272 (create-image "/* XPM */
|
|
1273 static char * previous_xpm[] = {
|
|
1274 \"24 24 39 1\",
|
|
1275 \" c None\",
|
|
1276 \". c #000000\",
|
|
1277 \"+ c #7BB3DB\",
|
|
1278 \"@ c #83BCE2\",
|
|
1279 \"# c #7FB8DF\",
|
|
1280 \"$ c #89C2E7\",
|
|
1281 \"% c #86BFE5\",
|
|
1282 \"& c #83BBE2\",
|
|
1283 \"* c #8CC6EA\",
|
|
1284 \"= c #8BC4E9\",
|
|
1285 \"- c #88C2E7\",
|
|
1286 \"; c #85BEE4\",
|
|
1287 \"> c #8DC7EB\",
|
|
1288 \", c #89C3E8\",
|
|
1289 \"' c #8AC4E8\",
|
|
1290 \") c #8BC5EA\",
|
|
1291 \"! c #88C1E6\",
|
|
1292 \"~ c #8AC4E9\",
|
|
1293 \"{ c #8AC3E8\",
|
|
1294 \"] c #86C0E5\",
|
|
1295 \"^ c #87C0E6\",
|
|
1296 \"/ c #87C0E5\",
|
|
1297 \"( c #82BBE2\",
|
|
1298 \"_ c #81BAE1\",
|
|
1299 \": c #7FB7DF\",
|
|
1300 \"< c #7DB6DD\",
|
|
1301 \"[ c #7DB5DD\",
|
|
1302 \"} c #7CB4DC\",
|
|
1303 \"| c #79B1DA\",
|
|
1304 \"1 c #76ADD7\",
|
|
1305 \"2 c #77AFD8\",
|
|
1306 \"3 c #73AAD4\",
|
|
1307 \"4 c #70A7D1\",
|
|
1308 \"5 c #6EA5D0\",
|
|
1309 \"6 c #6CA2CE\",
|
|
1310 \"7 c #689ECB\",
|
|
1311 \"8 c #6399C7\",
|
|
1312 \"9 c #6095C4\",
|
|
1313 \"0 c #5C90C0\",
|
|
1314 \" \",
|
|
1315 \" \",
|
|
1316 \" . \",
|
|
1317 \" .. \",
|
|
1318 \" .+. \",
|
|
1319 \" .@#. \",
|
|
1320 \" .$%&. \",
|
|
1321 \" .*=-;. \",
|
|
1322 \" .>>*,%. \",
|
|
1323 \" .>>>*,%. \",
|
|
1324 \" .')**=-;. \",
|
|
1325 \" .;!,~{-%&. \",
|
|
1326 \" .;]^/;@#. \",
|
|
1327 \" .(@&_:+. \",
|
|
1328 \" .<[}|1. \",
|
|
1329 \" .2134. \",
|
|
1330 \" .567. \",
|
|
1331 \" .89. \",
|
|
1332 \" .0. \",
|
|
1333 \" .. \",
|
|
1334 \" . \",
|
|
1335 \" \",
|
|
1336 \" \",
|
|
1337 \" \"};
|
|
1338 "
|
|
1339 'xpm t)
|
|
1340 "Image for the previous item button."))
|
|
1341
|
|
1342 (defconst newsticker--previous-feed-image
|
|
1343 (if (fboundp 'create-image)
|
|
1344 (create-image "/* XPM */
|
|
1345 static char * prev_feed_xpm[] = {
|
|
1346 \"24 24 52 1\",
|
|
1347 \" c None\",
|
|
1348 \". c #000000\",
|
|
1349 \"+ c #70A7D2\",
|
|
1350 \"@ c #75ADD6\",
|
|
1351 \"# c #71A8D3\",
|
|
1352 \"$ c #79B1DA\",
|
|
1353 \"% c #7BB3DB\",
|
|
1354 \"& c #7DB5DD\",
|
|
1355 \"* c #83BBE2\",
|
|
1356 \"= c #7EB6DE\",
|
|
1357 \"- c #78B0D9\",
|
|
1358 \"; c #7FB7DE\",
|
|
1359 \"> c #88C2E7\",
|
|
1360 \", c #85BEE4\",
|
|
1361 \"' c #80B9E0\",
|
|
1362 \") c #80B8DF\",
|
|
1363 \"! c #8CC6EA\",
|
|
1364 \"~ c #89C3E8\",
|
|
1365 \"{ c #86BFE5\",
|
|
1366 \"] c #81BAE1\",
|
|
1367 \"^ c #7CB4DC\",
|
|
1368 \"/ c #7FB8DF\",
|
|
1369 \"( c #8DC7EB\",
|
|
1370 \"_ c #7BB3DC\",
|
|
1371 \": c #7EB7DE\",
|
|
1372 \"< c #8BC4E9\",
|
|
1373 \"[ c #8AC4E9\",
|
|
1374 \"} c #8AC3E8\",
|
|
1375 \"| c #87C0E6\",
|
|
1376 \"1 c #87C0E5\",
|
|
1377 \"2 c #83BCE2\",
|
|
1378 \"3 c #75ACD6\",
|
|
1379 \"4 c #7FB7DF\",
|
|
1380 \"5 c #77AED8\",
|
|
1381 \"6 c #71A8D2\",
|
|
1382 \"7 c #70A7D1\",
|
|
1383 \"8 c #76ADD7\",
|
|
1384 \"9 c #6CA2CE\",
|
|
1385 \"0 c #699FCC\",
|
|
1386 \"a c #73AAD4\",
|
|
1387 \"b c #6BA1CD\",
|
|
1388 \"c c #669CC9\",
|
|
1389 \"d c #6298C5\",
|
|
1390 \"e c #689ECB\",
|
|
1391 \"f c #6499C7\",
|
|
1392 \"g c #6095C3\",
|
|
1393 \"h c #5C91C0\",
|
|
1394 \"i c #5E93C2\",
|
|
1395 \"j c #5B90C0\",
|
|
1396 \"k c #588CBC\",
|
|
1397 \"l c #578CBC\",
|
|
1398 \"m c #5589BA\",
|
|
1399 \" \",
|
|
1400 \" \",
|
|
1401 \" ... . \",
|
|
1402 \" .+. .. \",
|
|
1403 \" .@. .#. \",
|
|
1404 \" .$. .%@. \",
|
|
1405 \" .&. .*=-. \",
|
|
1406 \" .;. .>,'%. \",
|
|
1407 \" .). .!~{]^. \",
|
|
1408 \" ./. .(!~{]_. \",
|
|
1409 \" .:. .!!<>,'%. \",
|
|
1410 \" .&. .~[}>{*=-. \",
|
|
1411 \" .$. .|1,2/%@. \",
|
|
1412 \" .3. .*]4%56. \",
|
|
1413 \" .7. .^$8#9. \",
|
|
1414 \" .0. .a7bc. \",
|
|
1415 \" .d. .efg. \",
|
|
1416 \" .h. .ij. \",
|
|
1417 \" .k. .l. \",
|
|
1418 \" .m. .. \",
|
|
1419 \" ... . \",
|
|
1420 \" \",
|
|
1421 \" \",
|
|
1422 \" \"};
|
|
1423 "
|
|
1424 'xpm t)
|
|
1425 "Image for the previous feed button."))
|
|
1426
|
|
1427 (defconst newsticker--next-feed-image
|
|
1428 (if (fboundp 'create-image)
|
|
1429 (create-image "/* XPM */
|
|
1430 static char * next_feed_xpm[] = {
|
|
1431 \"24 24 57 1\",
|
|
1432 \" c None\",
|
|
1433 \". c #000000\",
|
|
1434 \"+ c #6CA2CE\",
|
|
1435 \"@ c #75ADD6\",
|
|
1436 \"# c #71A8D3\",
|
|
1437 \"$ c #79B1DA\",
|
|
1438 \"% c #7EB7DE\",
|
|
1439 \"& c #7DB5DD\",
|
|
1440 \"* c #81BAE1\",
|
|
1441 \"= c #85BEE4\",
|
|
1442 \"- c #78B0D9\",
|
|
1443 \"; c #7FB7DE\",
|
|
1444 \"> c #83BCE3\",
|
|
1445 \", c #87C1E6\",
|
|
1446 \"' c #8AC4E9\",
|
|
1447 \") c #7BB3DB\",
|
|
1448 \"! c #80B8DF\",
|
|
1449 \"~ c #88C2E7\",
|
|
1450 \"{ c #8BC5E9\",
|
|
1451 \"] c #8DC7EB\",
|
|
1452 \"^ c #7CB4DC\",
|
|
1453 \"/ c #7FB8DF\",
|
|
1454 \"( c #84BDE3\",
|
|
1455 \"_ c #7BB3DC\",
|
|
1456 \": c #83BCE2\",
|
|
1457 \"< c #87C0E6\",
|
|
1458 \"[ c #8AC4E8\",
|
|
1459 \"} c #8BC5EA\",
|
|
1460 \"| c #8CC6EA\",
|
|
1461 \"1 c #88C1E6\",
|
|
1462 \"2 c #89C3E8\",
|
|
1463 \"3 c #8AC3E8\",
|
|
1464 \"4 c #7EB6DE\",
|
|
1465 \"5 c #82BBE1\",
|
|
1466 \"6 c #86C0E5\",
|
|
1467 \"7 c #87C0E5\",
|
|
1468 \"8 c #75ACD6\",
|
|
1469 \"9 c #7AB2DA\",
|
|
1470 \"0 c #81B9E0\",
|
|
1471 \"a c #82BBE2\",
|
|
1472 \"b c #71A8D2\",
|
|
1473 \"c c #70A7D1\",
|
|
1474 \"d c #74ACD6\",
|
|
1475 \"e c #699FCC\",
|
|
1476 \"f c #6EA5D0\",
|
|
1477 \"g c #72A9D4\",
|
|
1478 \"h c #669CC9\",
|
|
1479 \"i c #6298C5\",
|
|
1480 \"j c #679DCA\",
|
|
1481 \"k c #6BA1CD\",
|
|
1482 \"l c #6095C3\",
|
|
1483 \"m c #5C91C0\",
|
|
1484 \"n c #5F94C2\",
|
|
1485 \"o c #5B90C0\",
|
|
1486 \"p c #588CBC\",
|
|
1487 \"q c #578CBC\",
|
|
1488 \"r c #5589BA\",
|
|
1489 \" \",
|
|
1490 \" \",
|
|
1491 \" . ... \",
|
|
1492 \" .. .+. \",
|
|
1493 \" .@. .#. \",
|
|
1494 \" .$%. .@. \",
|
|
1495 \" .&*=. .-. \",
|
|
1496 \" .;>,'. .). \",
|
|
1497 \" .!=~{]. .^. \",
|
|
1498 \" ./(~{]]. ._. \",
|
|
1499 \" .%:<[}||. .). \",
|
|
1500 \" .&*=12'3~. .-. \",
|
|
1501 \" .$45=6<7. .@. \",
|
|
1502 \" .8940a:. .b. \",
|
|
1503 \" .cd-)&. .+. \",
|
|
1504 \" .efg8. .h. \",
|
|
1505 \" .ijk. .l. \",
|
|
1506 \" .mn. .o. \",
|
|
1507 \" .p. .q. \",
|
|
1508 \" .. .r. \",
|
|
1509 \" . ... \",
|
|
1510 \" \",
|
|
1511 \" \",
|
|
1512 \" \"};
|
|
1513 "
|
|
1514 'xpm t)
|
|
1515 "Image for the next feed button."))
|
|
1516
|
|
1517 (defconst newsticker--mark-read-image
|
|
1518 (if (fboundp 'create-image)
|
|
1519 (create-image "/* XPM */
|
|
1520 static char * mark_read_xpm[] = {
|
|
1521 \"24 24 44 1\",
|
|
1522 \" c None\",
|
|
1523 \". c #C20000\",
|
|
1524 \"+ c #BE0000\",
|
|
1525 \"@ c #C70000\",
|
|
1526 \"# c #CE0000\",
|
|
1527 \"$ c #C90000\",
|
|
1528 \"% c #BD0000\",
|
|
1529 \"& c #CB0000\",
|
|
1530 \"* c #D10000\",
|
|
1531 \"= c #D70000\",
|
|
1532 \"- c #D30000\",
|
|
1533 \"; c #CD0000\",
|
|
1534 \"> c #C60000\",
|
|
1535 \", c #D40000\",
|
|
1536 \"' c #DA0000\",
|
|
1537 \") c #DE0000\",
|
|
1538 \"! c #DB0000\",
|
|
1539 \"~ c #D60000\",
|
|
1540 \"{ c #D00000\",
|
|
1541 \"] c #DC0000\",
|
|
1542 \"^ c #E00000\",
|
|
1543 \"/ c #E40000\",
|
|
1544 \"( c #E10000\",
|
|
1545 \"_ c #DD0000\",
|
|
1546 \": c #D80000\",
|
|
1547 \"< c #E50000\",
|
|
1548 \"[ c #E70000\",
|
|
1549 \"} c #E60000\",
|
|
1550 \"| c #E20000\",
|
|
1551 \"1 c #E90000\",
|
|
1552 \"2 c #E80000\",
|
|
1553 \"3 c #E30000\",
|
|
1554 \"4 c #DF0000\",
|
|
1555 \"5 c #D90000\",
|
|
1556 \"6 c #CC0000\",
|
|
1557 \"7 c #C10000\",
|
|
1558 \"8 c #C30000\",
|
|
1559 \"9 c #BF0000\",
|
|
1560 \"0 c #B90000\",
|
|
1561 \"a c #BC0000\",
|
|
1562 \"b c #BB0000\",
|
|
1563 \"c c #B80000\",
|
|
1564 \"d c #B50000\",
|
|
1565 \"e c #B70000\",
|
|
1566 \" \",
|
|
1567 \" \",
|
|
1568 \" \",
|
|
1569 \" . + \",
|
|
1570 \" +@# $.% \",
|
|
1571 \" &*= -;> \",
|
|
1572 \" ,') !~{ \",
|
|
1573 \" ]^/ (_: \",
|
|
1574 \" (<[ }|) \",
|
|
1575 \" <[1 2<| \",
|
|
1576 \" }222[< \",
|
|
1577 \" }}}< \",
|
|
1578 \" 333| \",
|
|
1579 \" _4^4)] \",
|
|
1580 \" ~:' 5=- \",
|
|
1581 \" 6{- *#$ \",
|
|
1582 \" 7>$ @89 \",
|
|
1583 \" 0a+ %bc \",
|
|
1584 \" ddc edd \",
|
|
1585 \" ddd ddd \",
|
|
1586 \" d d \",
|
|
1587 \" \",
|
|
1588 \" \",
|
|
1589 \" \"};
|
|
1590 "
|
|
1591 'xpm t)
|
|
1592 "Image for the next feed button."))
|
|
1593
|
|
1594 (defconst newsticker--mark-immortal-image
|
|
1595 (if (fboundp 'create-image)
|
|
1596 (create-image "/* XPM */
|
|
1597 static char * mark_immortal_xpm[] = {
|
|
1598 \"24 24 93 2\",
|
|
1599 \" c None\",
|
|
1600 \". c #171717\",
|
|
1601 \"+ c #030303\",
|
|
1602 \"@ c #000000\",
|
|
1603 \"# c #181818\",
|
|
1604 \"$ c #090909\",
|
|
1605 \"% c #FFC960\",
|
|
1606 \"& c #FFCB61\",
|
|
1607 \"* c #FFCB62\",
|
|
1608 \"= c #FFC961\",
|
|
1609 \"- c #FFC75F\",
|
|
1610 \"; c #FFC65E\",
|
|
1611 \"> c #FFCA61\",
|
|
1612 \", c #FFCD63\",
|
|
1613 \"' c #FFCF65\",
|
|
1614 \") c #FFD065\",
|
|
1615 \"! c #FFCE64\",
|
|
1616 \"~ c #FFC35C\",
|
|
1617 \"{ c #FFC45D\",
|
|
1618 \"] c #FFD166\",
|
|
1619 \"^ c #FFD267\",
|
|
1620 \"/ c #FFD368\",
|
|
1621 \"( c #FFD167\",
|
|
1622 \"_ c #FFC05A\",
|
|
1623 \": c #010101\",
|
|
1624 \"< c #040404\",
|
|
1625 \"[ c #FFCC62\",
|
|
1626 \"} c #FFD569\",
|
|
1627 \"| c #FFD56A\",
|
|
1628 \"1 c #FFC860\",
|
|
1629 \"2 c #FFC25B\",
|
|
1630 \"3 c #FFBB56\",
|
|
1631 \"4 c #020202\",
|
|
1632 \"5 c #060606\",
|
|
1633 \"6 c #FFC15B\",
|
|
1634 \"7 c #FFC85F\",
|
|
1635 \"8 c #FFD469\",
|
|
1636 \"9 c #FFD66A\",
|
|
1637 \"0 c #FFBC57\",
|
|
1638 \"a c #1B1B1B\",
|
|
1639 \"b c #070707\",
|
|
1640 \"c c #FFBA55\",
|
|
1641 \"d c #FFB451\",
|
|
1642 \"e c #FFB954\",
|
|
1643 \"f c #FFB350\",
|
|
1644 \"g c #FFB652\",
|
|
1645 \"h c #FFBE58\",
|
|
1646 \"i c #FFCD64\",
|
|
1647 \"j c #FFD066\",
|
|
1648 \"k c #FFC059\",
|
|
1649 \"l c #FFB14E\",
|
|
1650 \"m c #0B0B0B\",
|
|
1651 \"n c #FFBB55\",
|
|
1652 \"o c #FFC15A\",
|
|
1653 \"p c #FFB552\",
|
|
1654 \"q c #FFAD4B\",
|
|
1655 \"r c #080808\",
|
|
1656 \"s c #FFAF4C\",
|
|
1657 \"t c #FFB853\",
|
|
1658 \"u c #FFA948\",
|
|
1659 \"v c #050505\",
|
|
1660 \"w c #FFB04E\",
|
|
1661 \"x c #FFB753\",
|
|
1662 \"y c #FFBC56\",
|
|
1663 \"z c #FFC55D\",
|
|
1664 \"A c #FFC55E\",
|
|
1665 \"B c #FFC45C\",
|
|
1666 \"C c #FFBD57\",
|
|
1667 \"D c #FFB854\",
|
|
1668 \"E c #FFB34F\",
|
|
1669 \"F c #FFAB4A\",
|
|
1670 \"G c #FFA545\",
|
|
1671 \"H c #FFAA49\",
|
|
1672 \"I c #FFB04D\",
|
|
1673 \"J c #FFB551\",
|
|
1674 \"K c #FFBF58\",
|
|
1675 \"L c #FFB24F\",
|
|
1676 \"M c #FFAC4A\",
|
|
1677 \"N c #FFA646\",
|
|
1678 \"O c #FFA344\",
|
|
1679 \"P c #FFA848\",
|
|
1680 \"Q c #FFB14F\",
|
|
1681 \"R c #FFAF4D\",
|
|
1682 \"S c #FFA546\",
|
|
1683 \"T c #FFA243\",
|
|
1684 \"U c #FFA445\",
|
|
1685 \"V c #FFAE4C\",
|
|
1686 \"W c #FFA444\",
|
|
1687 \"X c #FFA142\",
|
|
1688 \"Y c #FF9F41\",
|
|
1689 \"Z c #0A0A0A\",
|
|
1690 \"` c #FF9E40\",
|
|
1691 \" . c #FF9F40\",
|
|
1692 \" \",
|
|
1693 \" \",
|
|
1694 \" \",
|
|
1695 \" . + @ @ + # \",
|
|
1696 \" $ @ % & * * = - + + \",
|
|
1697 \" @ ; > , ' ) ' ! * - ~ @ \",
|
|
1698 \" @ { > ! ] ^ / / ( ' * ; _ : \",
|
|
1699 \" < _ ; [ ) / } | } / ] , 1 2 3 4 \",
|
|
1700 \" 5 6 7 , ] 8 9 9 9 } ^ ! = ~ 0 a \",
|
|
1701 \" b c 6 - , ] 8 9 9 9 } ^ ! % ~ 0 d 5 \",
|
|
1702 \" : e _ ; * ) / 8 } } / ] , 1 2 3 f 5 \",
|
|
1703 \" : g h { = i j ^ / ^ ] ! * ; k e l m \",
|
|
1704 \" : f n o ; > , ' ) ' ! * - 2 0 p q r \",
|
|
1705 \" : s g 0 6 ; % > * * = - ~ h t l u r \",
|
|
1706 \" v u w x y k ~ z A z B o C D E F G b \",
|
|
1707 \" 5 H I J e 0 h K h C c x L M N . \",
|
|
1708 \" 4 O P q Q d g x g J L R H S T < \",
|
|
1709 \" @ T U P F q V q M H N W X + \",
|
|
1710 \" @ Y T O W G G W O X Y @ \",
|
|
1711 \" 4 Z ` Y Y Y .` 4 4 \",
|
|
1712 \" 5 : : @ @ Z \",
|
|
1713 \" \",
|
|
1714 \" \",
|
|
1715 \" \"};
|
|
1716 "
|
|
1717 'xpm t)
|
|
1718 "Image for the next feed button."))
|
|
1719
|
|
1720
|
|
1721 (defconst newsticker--narrow-image
|
|
1722 (if (fboundp 'create-image)
|
|
1723 (create-image "/* XPM */
|
|
1724 static char * narrow_xpm[] = {
|
|
1725 \"24 24 48 1\",
|
|
1726 \" c None\",
|
|
1727 \". c #000000\",
|
|
1728 \"+ c #969696\",
|
|
1729 \"@ c #9E9E9E\",
|
|
1730 \"# c #A4A4A4\",
|
|
1731 \"$ c #AAAAAA\",
|
|
1732 \"% c #AEAEAE\",
|
|
1733 \"& c #B1B1B1\",
|
|
1734 \"* c #B3B3B3\",
|
|
1735 \"= c #B4B4B4\",
|
|
1736 \"- c #B2B2B2\",
|
|
1737 \"; c #AFAFAF\",
|
|
1738 \"> c #ABABAB\",
|
|
1739 \", c #A6A6A6\",
|
|
1740 \"' c #A0A0A0\",
|
|
1741 \") c #989898\",
|
|
1742 \"! c #909090\",
|
|
1743 \"~ c #73AAD4\",
|
|
1744 \"{ c #7AB2DA\",
|
|
1745 \"] c #7FB8DF\",
|
|
1746 \"^ c #84BDE3\",
|
|
1747 \"/ c #88C2E7\",
|
|
1748 \"( c #8BC5E9\",
|
|
1749 \"_ c #8DC7EB\",
|
|
1750 \": c #8CC6EA\",
|
|
1751 \"< c #89C3E8\",
|
|
1752 \"[ c #86BFE5\",
|
|
1753 \"} c #81BAE1\",
|
|
1754 \"| c #7BB3DC\",
|
|
1755 \"1 c #75ACD6\",
|
|
1756 \"2 c #6DA4CF\",
|
|
1757 \"3 c #979797\",
|
|
1758 \"4 c #A3A3A3\",
|
|
1759 \"5 c #A8A8A8\",
|
|
1760 \"6 c #ADADAD\",
|
|
1761 \"7 c #ACACAC\",
|
|
1762 \"8 c #A9A9A9\",
|
|
1763 \"9 c #A5A5A5\",
|
|
1764 \"0 c #9A9A9A\",
|
|
1765 \"a c #929292\",
|
|
1766 \"b c #8C8C8C\",
|
|
1767 \"c c #808080\",
|
|
1768 \"d c #818181\",
|
|
1769 \"e c #838383\",
|
|
1770 \"f c #848484\",
|
|
1771 \"g c #858585\",
|
|
1772 \"h c #868686\",
|
|
1773 \"i c #828282\",
|
|
1774 \" \",
|
|
1775 \" \",
|
|
1776 \" \",
|
|
1777 \" .................. \",
|
|
1778 \" .+@#$%&*=*-;>,')!. \",
|
|
1779 \" .................. \",
|
|
1780 \" \",
|
|
1781 \" \",
|
|
1782 \" .................. \",
|
|
1783 \" .~{]^/(___:<[}|12. \",
|
|
1784 \" .................. \",
|
|
1785 \" \",
|
|
1786 \" \",
|
|
1787 \" .................. \",
|
|
1788 \" .!3@45>666789'0ab. \",
|
|
1789 \" .................. \",
|
|
1790 \" \",
|
|
1791 \" \",
|
|
1792 \" .................. \",
|
|
1793 \" .cccdefghhgficccc. \",
|
|
1794 \" .................. \",
|
|
1795 \" \",
|
|
1796 \" \",
|
|
1797 \" \"};
|
|
1798 "
|
|
1799 'xpm t)
|
|
1800 "Image for the next feed button."))
|
|
1801
|
|
1802 (defconst newsticker--get-all-image
|
|
1803 (if (fboundp 'create-image)
|
|
1804 (create-image "/* XPM */
|
|
1805 static char * get_all_xpm[] = {
|
|
1806 \"24 24 70 1\",
|
|
1807 \" c None\",
|
|
1808 \". c #000000\",
|
|
1809 \"+ c #F3DA00\",
|
|
1810 \"@ c #F5DF00\",
|
|
1811 \"# c #F7E300\",
|
|
1812 \"$ c #F9E700\",
|
|
1813 \"% c #FAEA00\",
|
|
1814 \"& c #FBEC00\",
|
|
1815 \"* c #FBED00\",
|
|
1816 \"= c #FCEE00\",
|
|
1817 \"- c #FAEB00\",
|
|
1818 \"; c #F9E800\",
|
|
1819 \"> c #F8E500\",
|
|
1820 \", c #F6E000\",
|
|
1821 \"' c #F4DB00\",
|
|
1822 \") c #F1D500\",
|
|
1823 \"! c #EFD000\",
|
|
1824 \"~ c #B7CA00\",
|
|
1825 \"{ c #BFD100\",
|
|
1826 \"] c #C5D700\",
|
|
1827 \"^ c #CBDB00\",
|
|
1828 \"/ c #CFDF00\",
|
|
1829 \"( c #D2E200\",
|
|
1830 \"_ c #D4E400\",
|
|
1831 \": c #D3E300\",
|
|
1832 \"< c #D0E000\",
|
|
1833 \"[ c #CCDD00\",
|
|
1834 \"} c #C7D800\",
|
|
1835 \"| c #C1D300\",
|
|
1836 \"1 c #BACC00\",
|
|
1837 \"2 c #B1C500\",
|
|
1838 \"3 c #A8BC00\",
|
|
1839 \"4 c #20A900\",
|
|
1840 \"5 c #22AF00\",
|
|
1841 \"6 c #24B500\",
|
|
1842 \"7 c #26B900\",
|
|
1843 \"8 c #27BC00\",
|
|
1844 \"9 c #27BE00\",
|
|
1845 \"0 c #28BF00\",
|
|
1846 \"a c #27BD00\",
|
|
1847 \"b c #26BA00\",
|
|
1848 \"c c #25B600\",
|
|
1849 \"d c #23B100\",
|
|
1850 \"e c #21AB00\",
|
|
1851 \"f c #1FA400\",
|
|
1852 \"g c #1C9B00\",
|
|
1853 \"h c #21AA00\",
|
|
1854 \"i c #24B300\",
|
|
1855 \"j c #25B800\",
|
|
1856 \"k c #25B700\",
|
|
1857 \"l c #24B400\",
|
|
1858 \"m c #23B000\",
|
|
1859 \"n c #1FA500\",
|
|
1860 \"o c #1D9E00\",
|
|
1861 \"p c #20A800\",
|
|
1862 \"q c #21AC00\",
|
|
1863 \"r c #23B200\",
|
|
1864 \"s c #22AD00\",
|
|
1865 \"t c #1D9F00\",
|
|
1866 \"u c #20A700\",
|
|
1867 \"v c #1EA100\",
|
|
1868 \"w c #1C9C00\",
|
|
1869 \"x c #1DA000\",
|
|
1870 \"y c #1B9800\",
|
|
1871 \"z c #1A9600\",
|
|
1872 \"A c #1A9700\",
|
|
1873 \"B c #1A9500\",
|
|
1874 \"C c #199200\",
|
|
1875 \"D c #189100\",
|
|
1876 \"E c #178C00\",
|
|
1877 \" \",
|
|
1878 \" \",
|
|
1879 \" \",
|
|
1880 \" \",
|
|
1881 \" ................... \",
|
|
1882 \" .+@#$%&*=*&-;>,')!. \",
|
|
1883 \" ................... \",
|
|
1884 \" \",
|
|
1885 \" ................... \",
|
|
1886 \" .~{]^/(___:<[}|123. \",
|
|
1887 \" ................... \",
|
|
1888 \" \",
|
|
1889 \" ................... \",
|
|
1890 \" .45678909abcdefg. \",
|
|
1891 \" .h5icj7jklmeno. \",
|
|
1892 \" .pq5drrmshft. \",
|
|
1893 \" .fu4h4pnvw. \",
|
|
1894 \" .oxvxtwy. \",
|
|
1895 \" .zAAzB. \",
|
|
1896 \" .CCD. \",
|
|
1897 \" .E. \",
|
|
1898 \" . \",
|
|
1899 \" \",
|
|
1900 \" \"};
|
|
1901 "
|
|
1902 'xpm t)
|
|
1903 "Image for the next feed button."))
|
|
1904
|
|
1905
|
|
1906 (defconst newsticker--update-image
|
|
1907 (if (fboundp 'create-image)
|
|
1908 (create-image "/* XPM */
|
|
1909 static char * update_xpm[] = {
|
|
1910 \"24 24 37 1\",
|
|
1911 \" c None\",
|
|
1912 \". c #076D00\",
|
|
1913 \"+ c #0A8600\",
|
|
1914 \"@ c #0A8800\",
|
|
1915 \"# c #098400\",
|
|
1916 \"$ c #087200\",
|
|
1917 \"% c #087900\",
|
|
1918 \"& c #098500\",
|
|
1919 \"* c #098100\",
|
|
1920 \"= c #087600\",
|
|
1921 \"- c #097E00\",
|
|
1922 \"; c #097F00\",
|
|
1923 \"> c #0A8700\",
|
|
1924 \", c #0A8C00\",
|
|
1925 \"' c #097C00\",
|
|
1926 \") c #098300\",
|
|
1927 \"! c #0A8900\",
|
|
1928 \"~ c #0A8E00\",
|
|
1929 \"{ c #0B9200\",
|
|
1930 \"] c #087700\",
|
|
1931 \"^ c #076E00\",
|
|
1932 \"/ c #076C00\",
|
|
1933 \"( c #076B00\",
|
|
1934 \"_ c #076A00\",
|
|
1935 \": c #076900\",
|
|
1936 \"< c #076800\",
|
|
1937 \"[ c #066700\",
|
|
1938 \"} c #066500\",
|
|
1939 \"| c #066400\",
|
|
1940 \"1 c #066300\",
|
|
1941 \"2 c #066600\",
|
|
1942 \"3 c #066200\",
|
|
1943 \"4 c #076700\",
|
|
1944 \"5 c #065E00\",
|
|
1945 \"6 c #066100\",
|
|
1946 \"7 c #065F00\",
|
|
1947 \"8 c #066000\",
|
|
1948 \" \",
|
|
1949 \" \",
|
|
1950 \" \",
|
|
1951 \" . +@@@+# \",
|
|
1952 \" $% &@ +* \",
|
|
1953 \" =-# ; \",
|
|
1954 \" %*>, ' \",
|
|
1955 \" ')!~{ = \",
|
|
1956 \" ]$ \",
|
|
1957 \" ^ ^ \",
|
|
1958 \" . . \",
|
|
1959 \" / ( \",
|
|
1960 \" _ : \",
|
|
1961 \" < [ \",
|
|
1962 \" } | \",
|
|
1963 \" [[ \",
|
|
1964 \" 1 $.:23 \",
|
|
1965 \" 3 4}35 \",
|
|
1966 \" 6 655 \",
|
|
1967 \" 76 85 55 \",
|
|
1968 \" 5555555 5 \",
|
|
1969 \" \",
|
|
1970 \" \",
|
|
1971 \" \"};
|
|
1972 "
|
|
1973 'xpm t)
|
|
1974 "Image for the update button."))
|
|
1975
|
|
1976 (defconst newsticker--browse-image
|
|
1977 (if (fboundp 'create-image)
|
|
1978 (create-image "/* XPM */
|
|
1979 static char * visit_xpm[] = {
|
|
1980 \"24 24 39 1\",
|
|
1981 \" c None\",
|
|
1982 \". c #000000\",
|
|
1983 \"+ c #FFFFFF\",
|
|
1984 \"@ c #00E63D\",
|
|
1985 \"# c #00E83E\",
|
|
1986 \"$ c #00E73D\",
|
|
1987 \"% c #00E93E\",
|
|
1988 \"& c #00E63C\",
|
|
1989 \"* c #00E53C\",
|
|
1990 \"= c #00E23B\",
|
|
1991 \"- c #00E33B\",
|
|
1992 \"; c #00E83D\",
|
|
1993 \"> c #00E13A\",
|
|
1994 \", c #00DD38\",
|
|
1995 \"' c #00DE38\",
|
|
1996 \") c #00E23A\",
|
|
1997 \"! c #00E43C\",
|
|
1998 \"~ c #00DF39\",
|
|
1999 \"{ c #00DB37\",
|
|
2000 \"] c #00D634\",
|
|
2001 \"^ c #00D734\",
|
|
2002 \"/ c #00E039\",
|
|
2003 \"( c #00DC37\",
|
|
2004 \"_ c #00D835\",
|
|
2005 \": c #00D332\",
|
|
2006 \"< c #00CD2F\",
|
|
2007 \"[ c #00DB36\",
|
|
2008 \"} c #00D433\",
|
|
2009 \"| c #00CF30\",
|
|
2010 \"1 c #00DA36\",
|
|
2011 \"2 c #00D936\",
|
|
2012 \"3 c #00D533\",
|
|
2013 \"4 c #00D131\",
|
|
2014 \"5 c #00CE2F\",
|
|
2015 \"6 c #00CC2F\",
|
|
2016 \"7 c #00CA2D\",
|
|
2017 \"8 c #00C62B\",
|
|
2018 \"9 c #00C52A\",
|
|
2019 \"0 c #00BE27\",
|
|
2020 \" \",
|
|
2021 \" \",
|
|
2022 \" . \",
|
|
2023 \" .+. \",
|
|
2024 \" .+++. \",
|
|
2025 \" .++.++. \",
|
|
2026 \" .++.@.++. \",
|
|
2027 \" .++.##$.++. \",
|
|
2028 \" .++.%%%#&.++. \",
|
|
2029 \" .++.$%%%#*=.++. \",
|
|
2030 \" .++.-@;##$*>,.++. \",
|
|
2031 \" .++.')!&@@*=~{].++. \",
|
|
2032 \" .++.^{~>---)/(_:<.++. \",
|
|
2033 \" .++.^[,~/~'(_}|.++. \",
|
|
2034 \" .++.]_1[12^:|.++. \",
|
|
2035 \" .++.:}33:45.++. \",
|
|
2036 \" .++.<5567.++. \",
|
|
2037 \" .++.889.++. \",
|
|
2038 \" .++.0.++. \",
|
|
2039 \" .++.++. \",
|
|
2040 \" .+++. \",
|
|
2041 \" .+. \",
|
|
2042 \" . \",
|
|
2043 \" \"};
|
|
2044 "
|
|
2045 'xpm t)
|
|
2046 "Image for the browse button."))
|
|
2047
|
|
2048
|
|
2049 (defvar newsticker-tool-bar-map
|
|
2050 (if (featurep 'xemacs)
|
|
2051 nil
|
|
2052 (let ((tool-bar-map (make-sparse-keymap)))
|
|
2053 (define-key tool-bar-map [newsticker-sep-1]
|
|
2054 (list 'menu-item "--double-line"))
|
|
2055 (define-key tool-bar-map [newsticker-browse-url]
|
|
2056 (list 'menu-item "newsticker-browse-url" 'newsticker-browse-url
|
|
2057 :visible t
|
|
2058 :help "Browse URL for item at point"
|
|
2059 :image newsticker--browse-image))
|
|
2060 (define-key tool-bar-map [newsticker-buffer-force-update]
|
|
2061 (list 'menu-item "newsticker-buffer-force-update"
|
|
2062 'newsticker-buffer-force-update
|
|
2063 :visible t
|
|
2064 :help "Update newsticker buffer"
|
|
2065 :image newsticker--update-image
|
|
2066 :enable '(not newsticker--buffer-uptodate-p)))
|
|
2067 (define-key tool-bar-map [newsticker-get-all-news]
|
|
2068 (list 'menu-item "newsticker-get-all-news" 'newsticker-get-all-news
|
|
2069 :visible t
|
|
2070 :help "Get news for all feeds"
|
|
2071 :image newsticker--get-all-image))
|
|
2072 (define-key tool-bar-map [newsticker-mark-item-at-point-as-read]
|
|
2073 (list 'menu-item "newsticker-mark-item-at-point-as-read"
|
|
2074 'newsticker-mark-item-at-point-as-read
|
|
2075 :visible t
|
|
2076 :image newsticker--mark-read-image
|
|
2077 :help "Mark current item as read"
|
|
2078 :enable '(newsticker-item-not-old-p)))
|
|
2079 (define-key tool-bar-map [newsticker-mark-item-at-point-as-immortal]
|
|
2080 (list 'menu-item "newsticker-mark-item-at-point-as-immortal"
|
|
2081 'newsticker-mark-item-at-point-as-immortal
|
|
2082 :visible t
|
|
2083 :image newsticker--mark-immortal-image
|
|
2084 :help "Mark current item as immortal"
|
|
2085 :enable '(newsticker-item-not-immortal-p)))
|
|
2086 (define-key tool-bar-map [newsticker-toggle-auto-narrow-to-feed]
|
|
2087 (list 'menu-item "newsticker-toggle-auto-narrow-to-feed"
|
|
2088 'newsticker-toggle-auto-narrow-to-feed
|
|
2089 :visible t
|
|
2090 :help "Toggle visibility of other feeds"
|
|
2091 :image newsticker--narrow-image))
|
|
2092 (define-key tool-bar-map [newsticker-next-feed]
|
|
2093 (list 'menu-item "newsticker-next-feed" 'newsticker-next-feed
|
|
2094 :visible t
|
|
2095 :help "Go to next feed"
|
|
2096 :image newsticker--next-feed-image
|
|
2097 :enable '(newsticker-next-feed-available-p)))
|
|
2098 (define-key tool-bar-map [newsticker-next-item]
|
|
2099 (list 'menu-item "newsticker-next-item" 'newsticker-next-item
|
|
2100 :visible t
|
|
2101 :help "Go to next item"
|
|
2102 :image newsticker--next-item-image
|
|
2103 :enable '(newsticker-next-item-available-p)))
|
|
2104 (define-key tool-bar-map [newsticker-previous-item]
|
|
2105 (list 'menu-item "newsticker-previous-item" 'newsticker-previous-item
|
|
2106 :visible t
|
|
2107 :help "Go to previous item"
|
|
2108 :image newsticker--previous-item-image
|
|
2109 :enable '(newsticker-previous-item-available-p)))
|
|
2110 (define-key tool-bar-map [newsticker-previous-feed]
|
|
2111 (list 'menu-item "newsticker-previous-feed" 'newsticker-previous-feed
|
|
2112 :visible t
|
|
2113 :help "Go to previous feed"
|
|
2114 :image newsticker--previous-feed-image
|
|
2115 :enable '(newsticker-previous-feed-available-p)))
|
|
2116 ;; standard icons / actions
|
|
2117 (tool-bar-add-item "close"
|
|
2118 'newsticker-close-buffer
|
|
2119 'newsticker-close-buffer
|
|
2120 :help "Close newsticker buffer")
|
|
2121 (tool-bar-add-item "preferences"
|
|
2122 'newsticker-customize
|
|
2123 'newsticker-customize
|
|
2124 :help "Customize newsticker")
|
|
2125 tool-bar-map)))
|
|
2126
|
|
2127 ;; ======================================================================
|
|
2128 ;;; Newsticker mode
|
|
2129 ;; ======================================================================
|
|
2130
|
|
2131 (define-derived-mode newsticker-mode fundamental-mode
|
|
2132 "NewsTicker"
|
|
2133 "Viewing RSS news feeds in Emacs."
|
|
2134 (set (make-local-variable 'tool-bar-map) newsticker-tool-bar-map)
|
|
2135 (set (make-local-variable 'imenu-sort-function) nil)
|
|
2136 (set (make-local-variable 'scroll-conservatively) 999)
|
|
2137 (setq imenu-create-index-function 'newsticker--imenu-create-index)
|
|
2138 (setq imenu-default-goto-function 'newsticker--imenu-goto)
|
|
2139 (setq buffer-read-only t)
|
|
2140 (auto-fill-mode -1) ;; turn auto-fill off!
|
|
2141 (font-lock-mode -1) ;; turn off font-lock!!
|
|
2142 (set (make-local-variable 'font-lock-defaults) nil)
|
|
2143 (set (make-local-variable 'line-move-ignore-invisible) t)
|
|
2144 (setq mode-line-format
|
|
2145 (list "-"
|
|
2146 'mode-line-mule-info
|
|
2147 'mode-line-modified
|
|
2148 'mode-line-frame-identification
|
|
2149 " Newsticker ("
|
|
2150 '(newsticker--buffer-uptodate-p
|
|
2151 "up to date"
|
|
2152 "NEED UPDATE")
|
|
2153 ") -- "
|
|
2154 '(:eval (newsticker--buffer-get-feed-title-at-point))
|
|
2155 ": "
|
|
2156 '(:eval (newsticker--buffer-get-item-title-at-point))
|
|
2157 " %-"))
|
|
2158 (unless newsticker-show-all-rss-elements
|
|
2159 (add-to-invisibility-spec 'extra))
|
|
2160 (newsticker--buffer-set-uptodate nil))
|
|
2161
|
|
2162 ;; refine its mode-map
|
|
2163 (define-key newsticker-mode-map "sO" 'newsticker-show-old-items)
|
|
2164 (define-key newsticker-mode-map "hO" 'newsticker-hide-old-items)
|
|
2165 (define-key newsticker-mode-map "sa" 'newsticker-show-all-desc)
|
|
2166 (define-key newsticker-mode-map "ha" 'newsticker-hide-all-desc)
|
|
2167 (define-key newsticker-mode-map "sf" 'newsticker-show-feed-desc)
|
|
2168 (define-key newsticker-mode-map "hf" 'newsticker-hide-feed-desc)
|
|
2169 (define-key newsticker-mode-map "so" 'newsticker-show-old-item-desc)
|
|
2170 (define-key newsticker-mode-map "ho" 'newsticker-hide-old-item-desc)
|
|
2171 (define-key newsticker-mode-map "sn" 'newsticker-show-new-item-desc)
|
|
2172 (define-key newsticker-mode-map "hn" 'newsticker-hide-new-item-desc)
|
|
2173 (define-key newsticker-mode-map "se" 'newsticker-show-entry)
|
|
2174 (define-key newsticker-mode-map "he" 'newsticker-hide-entry)
|
|
2175 (define-key newsticker-mode-map "sx" 'newsticker-show-extra)
|
|
2176 (define-key newsticker-mode-map "hx" 'newsticker-hide-extra)
|
|
2177
|
|
2178 (define-key newsticker-mode-map " " 'scroll-up)
|
|
2179 (define-key newsticker-mode-map "q" 'newsticker-close-buffer)
|
|
2180 (define-key newsticker-mode-map "p" 'newsticker-previous-item)
|
|
2181 (define-key newsticker-mode-map "P" 'newsticker-previous-new-item)
|
|
2182 (define-key newsticker-mode-map "F" 'newsticker-previous-feed)
|
|
2183 (define-key newsticker-mode-map "\t" 'newsticker-next-item)
|
|
2184 (define-key newsticker-mode-map "n" 'newsticker-next-item)
|
|
2185 (define-key newsticker-mode-map "N" 'newsticker-next-new-item)
|
|
2186 (define-key newsticker-mode-map "f" 'newsticker-next-feed)
|
|
2187 (define-key newsticker-mode-map "M" 'newsticker-mark-all-items-as-read)
|
|
2188 (define-key newsticker-mode-map "m"
|
|
2189 'newsticker-mark-all-items-at-point-as-read)
|
|
2190 (define-key newsticker-mode-map "o" 'newsticker-mark-item-at-point-as-read)
|
|
2191 (define-key newsticker-mode-map "G" 'newsticker-get-all-news)
|
|
2192 (define-key newsticker-mode-map "g" 'newsticker-get-news-at-point)
|
|
2193 (define-key newsticker-mode-map "u" 'newsticker-buffer-update)
|
|
2194 (define-key newsticker-mode-map "U" 'newsticker-buffer-force-update)
|
|
2195 (define-key newsticker-mode-map "a" 'newsticker-add-url)
|
|
2196
|
|
2197 (define-key newsticker-mode-map "i"
|
|
2198 'newsticker-mark-item-at-point-as-immortal)
|
|
2199
|
|
2200 (define-key newsticker-mode-map "xf" 'newsticker-toggle-auto-narrow-to-feed)
|
|
2201 (define-key newsticker-mode-map "xi" 'newsticker-toggle-auto-narrow-to-item)
|
|
2202
|
|
2203 ;; maps for the clickable portions
|
|
2204 (defvar newsticker--url-keymap (make-sparse-keymap)
|
|
2205 "Key map for click-able headings in the newsticker buffer.")
|
|
2206 (define-key newsticker--url-keymap [mouse-2]
|
|
2207 'newsticker-mouse-browse-url)
|
|
2208 (define-key newsticker--url-keymap "\n"
|
|
2209 'newsticker-browse-url)
|
|
2210 (define-key newsticker--url-keymap "\C-m"
|
|
2211 'newsticker-browse-url)
|
|
2212 (define-key newsticker--url-keymap [(control return)]
|
|
2213 'newsticker-handle-url)
|
|
2214
|
|
2215 ;; newsticker menu
|
|
2216 (defvar newsticker-menu (make-sparse-keymap "Newsticker"))
|
|
2217
|
|
2218 (define-key newsticker-menu [newsticker-browse-url]
|
|
2219 '("Browse URL for item at point" . newsticker-browse-url))
|
|
2220 (define-key newsticker-menu [newsticker-separator-1]
|
|
2221 '("--"))
|
|
2222 (define-key newsticker-menu [newsticker-buffer-update]
|
|
2223 '("Update buffer" . newsticker-buffer-update))
|
|
2224 (define-key newsticker-menu [newsticker-separator-2]
|
|
2225 '("--"))
|
|
2226 (define-key newsticker-menu [newsticker-get-all-news]
|
|
2227 '("Get news from all feeds" . newsticker-get-all-news))
|
|
2228 (define-key newsticker-menu [newsticker-get-news-at-point]
|
|
2229 '("Get news from feed at point" . newsticker-get-news-at-point))
|
|
2230 (define-key newsticker-menu [newsticker-separator-3]
|
|
2231 '("--"))
|
|
2232 (define-key newsticker-menu [newsticker-mark-all-items-as-read]
|
|
2233 '("Mark all items as read" . newsticker-mark-all-items-as-read))
|
|
2234 (define-key newsticker-menu [newsticker-mark-all-items-at-point-as-read]
|
|
2235 '("Mark all items in feed at point as read" .
|
|
2236 newsticker-mark-all-items-at-point-as-read))
|
|
2237 (define-key newsticker-menu [newsticker-mark-item-at-point-as-read]
|
|
2238 '("Mark item at point as read" .
|
|
2239 newsticker-mark-item-at-point-as-read))
|
|
2240 (define-key newsticker-menu [newsticker-mark-item-at-point-as-immortal]
|
|
2241 '("Toggle immortality for item at point" .
|
|
2242 newsticker-mark-item-at-point-as-immortal))
|
|
2243 (define-key newsticker-menu [newsticker-separator-4]
|
|
2244 '("--"))
|
|
2245 (define-key newsticker-menu [newsticker-hide-old-items]
|
|
2246 '("Hide old items" . newsticker-hide-old-items))
|
|
2247 (define-key newsticker-menu [newsticker-show-old-items]
|
|
2248 '("Show old items" . newsticker-show-old-items))
|
|
2249 (define-key newsticker-menu [newsticker-next-item]
|
|
2250 '("Go to next item" . newsticker-next-item))
|
|
2251 (define-key newsticker-menu [newsticker-previous-item]
|
|
2252 '("Go to previous item" . newsticker-previous-item))
|
|
2253
|
|
2254 ;; bind menu to mouse
|
|
2255 (define-key newsticker-mode-map [down-mouse-3] newsticker-menu)
|
|
2256 ;; Put menu in menu-bar
|
|
2257 (define-key newsticker-mode-map [menu-bar Newsticker]
|
|
2258 (cons "Newsticker" newsticker-menu))
|
|
2259
|
|
2260
|
|
2261 ;; ======================================================================
|
|
2262 ;;; shortcuts
|
|
2263 ;; ======================================================================
|
|
2264 (defsubst newsticker--title (item)
|
|
2265 "Return title of ITEM."
|
|
2266 (nth 0 item))
|
|
2267 (defsubst newsticker--desc (item)
|
|
2268 "Return description of ITEM."
|
|
2269 (nth 1 item))
|
|
2270 (defsubst newsticker--link (item)
|
|
2271 "Return link of ITEM."
|
|
2272 (nth 2 item))
|
|
2273 (defsubst newsticker--time (item)
|
|
2274 "Return time of ITEM."
|
|
2275 (nth 3 item))
|
|
2276 (defsubst newsticker--age (item)
|
|
2277 "Return age of ITEM."
|
|
2278 (nth 4 item))
|
|
2279 (defsubst newsticker--pos (item)
|
|
2280 "Return position/index of ITEM."
|
|
2281 (nth 5 item))
|
|
2282 (defsubst newsticker--preformatted-contents (item)
|
|
2283 "Return pre-formatted text of ITEM."
|
|
2284 (nth 6 item))
|
|
2285 (defsubst newsticker--preformatted-title (item)
|
|
2286 "Return pre-formatted title of ITEM."
|
|
2287 (nth 7 item))
|
|
2288 (defsubst newsticker--extra (item)
|
|
2289 "Return extra attributes of ITEM."
|
|
2290 (nth 8 item))
|
|
2291 (defsubst newsticker--guid (item)
|
|
2292 "Return guid of ITEM."
|
|
2293 (let ((guid (assoc 'guid (newsticker--extra item))))
|
|
2294 (if (stringp guid)
|
|
2295 guid
|
|
2296 (car (xml-node-children guid)))))
|
|
2297 (defsubst newsticker--enclosure (item)
|
|
2298 "Return enclosure element of ITEM in the form \(...FIXME...\)or nil."
|
|
2299 (let ((enclosure (assoc 'enclosure (newsticker--extra item))))
|
|
2300 (if enclosure
|
|
2301 (xml-node-attributes enclosure))))
|
|
2302
|
|
2303 ;; ======================================================================
|
|
2304 ;;; User fun
|
|
2305 ;; ======================================================================
|
|
2306
|
|
2307 (defun newsticker-start (&optional do-not-complain-if-running)
|
|
2308 "Start the newsticker.
|
|
2309 Start the timers for display and retrieval. If the newsticker, i.e. the
|
|
2310 timers, are running already a warning message is printed unless
|
|
2311 DO-NOT-COMPLAIN-IF-RUNNING is not nil.
|
|
2312 Run `newsticker-start-hook' if newsticker was not running already."
|
|
2313 (interactive)
|
|
2314 (let ((running (newsticker-running-p)))
|
|
2315 ;; read old cache if it exists and newsticker is not running
|
|
2316 (unless running
|
|
2317 (let* ((coding-system-for-read 'utf-8)
|
|
2318 (buf (find-file-noselect newsticker-cache-filename)))
|
|
2319 (when buf
|
|
2320 (set-buffer buf)
|
|
2321 (goto-char (point-min))
|
|
2322 (condition-case nil
|
|
2323 (setq newsticker--cache (read buf))
|
|
2324 (error
|
|
2325 (message "Error while reading newsticker cache file!")
|
|
2326 (setq newsticker--cache nil))))))
|
|
2327 ;; start retrieval timers -- for sake of simplicity we will start
|
|
2328 ;; one timer for each feed
|
|
2329 (mapc (lambda (item)
|
|
2330 (let* ((feed-name (car item))
|
|
2331 (start-time (nth 2 item))
|
|
2332 (interval (or (nth 3 item)
|
|
2333 newsticker-retrieval-interval))
|
|
2334 (timer (assoc (car item)
|
|
2335 newsticker--retrieval-timer-list)))
|
|
2336 (if timer
|
|
2337 (or do-not-complain-if-running
|
|
2338 (message "Timer for %s is running already!"
|
|
2339 feed-name))
|
|
2340 (newsticker--debug-msg "Starting timer for %s: %s, %d"
|
|
2341 feed-name start-time interval)
|
|
2342 ;; do not repeat retrieval if interval not positive
|
|
2343 (if (<= interval 0)
|
|
2344 (setq interval nil))
|
|
2345 ;; Suddenly XEmacs doesn't like start-time 0
|
|
2346 (if (or (not start-time)
|
|
2347 (and (numberp start-time) (= start-time 0)))
|
|
2348 (setq start-time 1))
|
|
2349 (message "start-time %s" start-time)
|
|
2350 (setq timer (run-at-time start-time interval
|
|
2351 'newsticker-get-news feed-name))
|
|
2352 (if interval
|
|
2353 (add-to-list 'newsticker--retrieval-timer-list
|
|
2354 (cons feed-name timer))))))
|
|
2355 (append newsticker-url-list-defaults newsticker-url-list))
|
|
2356 (unless running
|
|
2357 (run-hooks 'newsticker-start-hook)
|
|
2358 (message "Newsticker started!"))))
|
|
2359
|
|
2360 (defun newsticker-start-ticker ()
|
|
2361 "Start newsticker's ticker (but not the news retrieval.
|
|
2362 Start display timer for the actual ticker if wanted and not
|
|
2363 running already."
|
|
2364 (interactive)
|
|
2365 (if (and (> newsticker-display-interval 0)
|
|
2366 (not newsticker--display-timer))
|
|
2367 (setq newsticker--display-timer
|
|
2368 (run-at-time newsticker-display-interval
|
|
2369 newsticker-display-interval
|
|
2370 'newsticker--display-tick))))
|
|
2371
|
|
2372 (defun newsticker-stop ()
|
|
2373 "Stop the newsticker and the newsticker-ticker.
|
|
2374 Cancel the timers for display and retrieval. Run `newsticker-stop-hook'
|
|
2375 if newsticker has been running."
|
|
2376 (interactive)
|
|
2377 (newsticker--cache-update t)
|
|
2378 (newsticker-stop-ticker)
|
|
2379 (when (newsticker-running-p)
|
|
2380 (mapc (lambda (name-and-timer)
|
|
2381 (cancel-timer (cdr name-and-timer)))
|
|
2382 newsticker--retrieval-timer-list)
|
|
2383 (setq newsticker--retrieval-timer-list nil)
|
|
2384 (run-hooks 'newsticker-stop-hook)
|
|
2385 (message "Newsticker stopped!")))
|
|
2386
|
|
2387 (defun newsticker-stop-ticker ()
|
|
2388 "Stop newsticker's ticker (but not the news retrieval)."
|
|
2389 (interactive)
|
|
2390 (when newsticker--display-timer
|
|
2391 (cancel-timer newsticker--display-timer)
|
|
2392 (setq newsticker--display-timer nil)))
|
|
2393
|
|
2394 ;; the functions we need for retrieval and display
|
|
2395 (defun newsticker-show-news ()
|
|
2396 "Switch to newsticker buffer. You may want to bind this to a key."
|
|
2397 (interactive)
|
|
2398 (newsticker-start t) ;; will start only if not running
|
|
2399 (newsticker-buffer-update)
|
|
2400 (switch-to-buffer "*newsticker*"))
|
|
2401
|
|
2402 (defun newsticker-buffer-force-update ()
|
|
2403 "Update the newsticker buffer, even if not necessary."
|
|
2404 (interactive)
|
|
2405 (newsticker-buffer-update t))
|
|
2406
|
|
2407 (defun newsticker-buffer-update (&optional force)
|
|
2408 "Update the *newsticker* buffer.
|
|
2409 Unless FORCE is t this is donly only if necessary, i.e. when the
|
|
2410 *newsticker* buffer is not up-to-date."
|
|
2411 (interactive)
|
|
2412 ;; bring cache data into proper order....
|
|
2413 (newsticker--cache-sort)
|
|
2414 ;; fill buffer
|
|
2415 (save-excursion
|
|
2416 (let ((buf (get-buffer "*newsticker*")))
|
|
2417 (if buf
|
|
2418 (switch-to-buffer buf)
|
|
2419 (switch-to-buffer (get-buffer-create "*newsticker*"))
|
|
2420 (newsticker--buffer-set-uptodate nil)))
|
|
2421 (when (or force
|
|
2422 (not newsticker--buffer-uptodate-p))
|
|
2423 (message "Preparing newsticker buffer...")
|
|
2424 (setq buffer-undo-list t)
|
|
2425 (let ((inhibit-read-only t))
|
|
2426 (set-buffer-modified-p nil)
|
|
2427 (erase-buffer)
|
|
2428 (newsticker-mode)
|
|
2429 ;; Emacs 21.3.50 does not care if we turn off auto-fill in the
|
|
2430 ;; definition of newsticker-mode, so we do it here (again)
|
|
2431 (auto-fill-mode -1)
|
|
2432
|
|
2433 (set-buffer-file-coding-system 'utf-8)
|
|
2434
|
|
2435 (if newsticker-use-full-width
|
|
2436 (set (make-local-variable 'fill-column) (1- (window-width))))
|
|
2437 (newsticker--buffer-insert-all-items)
|
|
2438
|
|
2439 ;; FIXME: needed for methods buffer in ecb
|
|
2440 ;; (set-visited-file-name "*newsticker*")
|
|
2441
|
|
2442 (set-buffer-modified-p nil)
|
|
2443 (newsticker-hide-all-desc)
|
|
2444 (if newsticker-hide-old-items-in-newsticker-buffer
|
|
2445 (newsticker-hide-old-items))
|
|
2446 (if newsticker-show-descriptions-of-new-items
|
|
2447 (newsticker-show-new-item-desc))
|
|
2448 )
|
|
2449 (message ""))
|
|
2450 (newsticker--buffer-set-uptodate t)
|
|
2451 (run-hooks 'newsticker-buffer-change-hook)))
|
|
2452
|
|
2453 (defun newsticker-get-all-news ()
|
|
2454 "Launch retrieval of news from all configured newsticker sites.
|
|
2455 This does NOT start the retrieval timers."
|
|
2456 (interactive)
|
|
2457 ;; launch retrieval of news
|
|
2458 (mapc (lambda (item)
|
|
2459 (newsticker-get-news (car item)))
|
|
2460 (append newsticker-url-list-defaults newsticker-url-list)))
|
|
2461
|
|
2462 (defun newsticker-get-news-at-point ()
|
|
2463 "Launch retrieval of news for the feed point is in.
|
|
2464 This does NOT start the retrieval timers."
|
|
2465 (interactive)
|
|
2466 ;; launch retrieval of news
|
|
2467 (let ((feed (get-text-property (point) 'feed)))
|
|
2468 (when feed
|
|
2469 (newsticker--debug-msg "Getting news for %s" (symbol-name feed))
|
|
2470 (newsticker-get-news (symbol-name feed)))))
|
|
2471
|
|
2472 (defun newsticker-add-url (url name)
|
|
2473 "Add given URL under given NAME to `newsticker-url-list'.
|
|
2474 If URL is nil it is searched at point."
|
|
2475 (interactive
|
|
2476 (list
|
|
2477 (read-string "URL: "
|
|
2478 (save-excursion
|
|
2479 (end-of-line)
|
|
2480 (and
|
|
2481 (re-search-backward
|
|
2482 "http://"
|
|
2483 (if (> (point) (+ (point-min) 100))
|
|
2484 (- (point) 100)
|
|
2485 (point-min))
|
|
2486 t)
|
|
2487 (re-search-forward
|
|
2488 "http://[-a-zA-Z0-9&/_.]*"
|
|
2489 (if (< (point) (- (point-max) 200))
|
|
2490 (+ (point) 200)
|
|
2491 (point-max))
|
|
2492 t)
|
|
2493 (buffer-substring-no-properties (match-beginning 0)
|
|
2494 (match-end 0)))))
|
|
2495 (read-string "Name: ")))
|
|
2496 (add-to-list 'newsticker-url-list (list name url nil nil nil) t)
|
|
2497 (customize-variable 'newsticker-url-list))
|
|
2498
|
|
2499 (defun newsticker-w3m-show-inline-images ()
|
|
2500 "Show inline images in visible text ranges.
|
|
2501 In-line images in invisible text ranges are hidden. This function
|
|
2502 calls `w3m-toggle-inline-image'. It works only if
|
|
2503 `newsticker-html-renderer' is set to `w3m-region'"
|
|
2504 (interactive)
|
|
2505 (if (eq newsticker-html-renderer 'w3m-region)
|
|
2506 (let ((inhibit-read-only t))
|
|
2507 (save-excursion
|
|
2508 (save-restriction
|
|
2509 (widen)
|
|
2510 (goto-char (point-min))
|
|
2511 (let ((pos (point)))
|
|
2512 (while pos
|
|
2513 (setq pos (next-single-property-change pos 'w3m-image))
|
|
2514 (when pos
|
|
2515 (goto-char pos)
|
|
2516 (when (get-text-property pos 'w3m-image)
|
|
2517 (let ((invis (newsticker--lists-intersect-p
|
|
2518 (get-text-property (1- (point)) 'invisible)
|
|
2519 buffer-invisibility-spec)))
|
|
2520 (if invis
|
|
2521 (w3m-remove-image
|
|
2522 pos (next-single-property-change pos 'w3m-image))
|
|
2523 (w3m-toggle-inline-image t))))))))))))
|
|
2524
|
|
2525 ;; ======================================================================
|
|
2526 ;;; keymap stuff
|
|
2527 ;; ======================================================================
|
|
2528 (defun newsticker-close-buffer ()
|
|
2529 "Close the newsticker buffer."
|
|
2530 (interactive)
|
|
2531 (newsticker--cache-update t)
|
|
2532 (bury-buffer))
|
|
2533
|
|
2534 (defun newsticker-next-new-item (&optional do-not-wrap-at-eob)
|
|
2535 "Go to next new news item.
|
|
2536 If no new item is found behind point, search is continued at
|
|
2537 beginning of buffer unless optional argument DO-NOT-WRAP-AT-EOB
|
|
2538 is non-nil."
|
|
2539 (interactive)
|
|
2540 (widen)
|
|
2541 (let ((go-ahead t))
|
|
2542 (while go-ahead
|
|
2543 (unless (newsticker--buffer-goto '(item) 'new)
|
|
2544 ;; found nothing -- wrap
|
|
2545 (unless do-not-wrap-at-eob
|
|
2546 (goto-char (point-min))
|
|
2547 (newsticker-next-new-item t))
|
|
2548 (setq go-ahead nil))
|
|
2549 (unless (newsticker--lists-intersect-p
|
|
2550 (get-text-property (point) 'invisible)
|
|
2551 buffer-invisibility-spec)
|
|
2552 ;; this item is invisible -- continue search
|
|
2553 (setq go-ahead nil))))
|
|
2554 (run-hooks 'newsticker-select-item-hook)
|
|
2555 (point))
|
|
2556
|
|
2557 (defun newsticker-previous-new-item (&optional do-not-wrap-at-bob)
|
|
2558 "Go to previous new news item.
|
|
2559 If no new item is found before point, search is continued at
|
|
2560 beginning of buffer unless optional argument DO-NOT-WRAP-AT-BOB
|
|
2561 is non-nil."
|
|
2562 (interactive)
|
|
2563 (widen)
|
|
2564 (let ((go-ahead t))
|
|
2565 (while go-ahead
|
|
2566 (unless (newsticker--buffer-goto '(item) 'new t)
|
|
2567 (unless do-not-wrap-at-bob
|
|
2568 (goto-char (point-max))
|
|
2569 (newsticker--buffer-goto '(item) 'new t)))
|
|
2570 (unless (newsticker--lists-intersect-p
|
|
2571 (get-text-property (point) 'invisible)
|
|
2572 buffer-invisibility-spec)
|
|
2573 (setq go-ahead nil))))
|
|
2574 (run-hooks 'newsticker-select-item-hook)
|
|
2575 (point))
|
|
2576
|
|
2577 (defun newsticker-next-item (&optional do-not-wrap-at-eob)
|
|
2578 "Go to next news item.
|
|
2579 Return new buffer position.
|
|
2580 If no item is found below point, search is continued at beginning
|
|
2581 of buffer unless optional argument DO-NOT-WRAP-AT-EOB is
|
|
2582 non-nil."
|
|
2583 (interactive)
|
|
2584 (widen)
|
|
2585 (let ((go-ahead t)
|
|
2586 (search-list '(item)))
|
|
2587 (if newsticker--auto-narrow-to-item
|
|
2588 (setq search-list '(item feed)))
|
|
2589 (while go-ahead
|
|
2590 (unless (newsticker--buffer-goto search-list)
|
|
2591 ;; found nothing -- wrap
|
|
2592 (unless do-not-wrap-at-eob
|
|
2593 (goto-char (point-min)))
|
|
2594 (setq go-ahead nil))
|
|
2595 (unless (newsticker--lists-intersect-p
|
|
2596 (get-text-property (point) 'invisible)
|
|
2597 buffer-invisibility-spec)
|
|
2598 (setq go-ahead nil))))
|
|
2599 (run-hooks 'newsticker-select-item-hook)
|
|
2600 (point))
|
|
2601
|
|
2602 (defun newsticker-previous-item (&optional do-not-wrap-at-bob)
|
|
2603 "Go to previous news item.
|
|
2604 Return new buffer position.
|
|
2605 If no item is found before point, search is continued at
|
|
2606 beginning of buffer unless optional argument DO-NOT-WRAP-AT-BOB
|
|
2607 is non-nil."
|
|
2608 (interactive)
|
|
2609 (widen)
|
|
2610 (let ((go-ahead t)
|
|
2611 (search-list '(item)))
|
|
2612 (if newsticker--auto-narrow-to-item
|
|
2613 (setq search-list '(item feed)))
|
|
2614 (when (bobp)
|
|
2615 (unless do-not-wrap-at-bob
|
|
2616 (goto-char (point-max))))
|
|
2617 (while go-ahead
|
|
2618 (if (newsticker--buffer-goto search-list nil t)
|
|
2619 (unless (newsticker--lists-intersect-p
|
|
2620 (get-text-property (point) 'invisible)
|
|
2621 buffer-invisibility-spec)
|
|
2622 (setq go-ahead nil))
|
|
2623 (goto-char (point-min))
|
|
2624 (setq go-ahead nil))))
|
|
2625 (run-hooks 'newsticker-select-item-hook)
|
|
2626 (point))
|
|
2627
|
|
2628 (defun newsticker-next-feed ()
|
|
2629 "Go to next news feed.
|
|
2630 Return new buffer position."
|
|
2631 (interactive)
|
|
2632 (widen)
|
|
2633 (newsticker--buffer-goto '(feed))
|
|
2634 (run-hooks 'newsticker-select-feed-hook)
|
|
2635 (point))
|
|
2636
|
|
2637 (defun newsticker-previous-feed ()
|
|
2638 "Go to previous news feed.
|
|
2639 Return new buffer position."
|
|
2640 (interactive)
|
|
2641 (widen)
|
|
2642 (newsticker--buffer-goto '(feed) nil t)
|
|
2643 (run-hooks 'newsticker-select-feed-hook)
|
|
2644 (point))
|
|
2645
|
|
2646 (defun newsticker-mark-all-items-at-point-as-read ()
|
|
2647 "Mark all items as read and clear ticker contents."
|
|
2648 (interactive)
|
|
2649 (when (or newsticker--buffer-uptodate-p
|
|
2650 (y-or-n-p
|
|
2651 "Buffer is not up to date -- really mark items as read? "))
|
|
2652 (let ((feed (get-text-property (point) 'feed))
|
|
2653 (pos (point)))
|
|
2654 (when feed
|
|
2655 (message "Marking all items as read for %s" (symbol-name feed))
|
|
2656 (newsticker--cache-replace-age newsticker--cache feed 'new 'old)
|
|
2657 (newsticker--cache-replace-age newsticker--cache feed 'obsolete 'old)
|
|
2658 (newsticker--cache-update)
|
|
2659 (newsticker--buffer-set-uptodate nil)
|
|
2660 (newsticker--ticker-text-setup)
|
|
2661 (newsticker-buffer-update)
|
|
2662 ;; go back to where we came frome
|
|
2663 (goto-char pos)
|
|
2664 (end-of-line)
|
|
2665 (newsticker--buffer-goto '(feed) nil t)))))
|
|
2666
|
|
2667 (defun newsticker-mark-item-at-point-as-read (&optional respect-immortality)
|
|
2668 "Mark item at point as read.
|
|
2669 If optional argument RESPECT-IMMORTALITY is not nil immortal items do
|
|
2670 not get changed."
|
|
2671 (interactive)
|
|
2672 (when (or newsticker--buffer-uptodate-p
|
|
2673 (y-or-n-p
|
|
2674 "Buffer is not up to date -- really mark this item as read? "))
|
|
2675 (let ((feed (get-text-property (point) 'feed))
|
|
2676 (item nil))
|
|
2677 (when feed
|
|
2678 (save-excursion
|
|
2679 (newsticker--buffer-beginning-of-item)
|
|
2680 (let ((inhibit-read-only t)
|
|
2681 (age (get-text-property (point) 'nt-age))
|
|
2682 (title (get-text-property (point) 'nt-title))
|
|
2683 (guid (get-text-property (point) 'nt-guid))
|
|
2684 (nt-desc (get-text-property (point) 'nt-desc))
|
|
2685 (pos (save-excursion (newsticker--buffer-end-of-item))))
|
|
2686 (when (or (eq age 'new)
|
|
2687 (eq age 'obsolete)
|
|
2688 (and (eq age 'immortal)
|
|
2689 (not respect-immortality)))
|
|
2690 ;; find item
|
|
2691 (setq item (newsticker--cache-contains newsticker--cache
|
|
2692 feed title nt-desc
|
|
2693 nil nil guid))
|
|
2694 ;; mark as old
|
|
2695 (when item
|
|
2696 (setcar (nthcdr 4 item) 'old)
|
|
2697 (newsticker--do-forget-preformatted item))
|
|
2698 ;; clean up ticker
|
|
2699 (if (or (and (eq age 'new)
|
|
2700 newsticker-hide-immortal-items-in-echo-area)
|
|
2701 (and (memq age '(old immortal))
|
|
2702 (not
|
|
2703 (eq newsticker-hide-old-items-in-newsticker-buffer
|
|
2704 newsticker-hide-immortal-items-in-echo-area))))
|
|
2705 (newsticker--ticker-text-remove feed title))
|
|
2706 ;; set faces etc.
|
|
2707 (save-excursion
|
|
2708 (save-restriction
|
|
2709 (widen)
|
|
2710 (put-text-property (point) pos 'nt-age 'old)
|
|
2711 (newsticker--buffer-set-faces (point) pos)))
|
|
2712 (set-buffer-modified-p nil))))
|
|
2713 ;; move forward
|
|
2714 (newsticker-next-item t)))))
|
|
2715
|
|
2716 (defun newsticker-mark-item-at-point-as-immortal ()
|
|
2717 "Mark item at point as read."
|
|
2718 (interactive)
|
|
2719 (when (or newsticker--buffer-uptodate-p
|
|
2720 (y-or-n-p
|
|
2721 "Buffer is not up to date -- really mark this item as read? "))
|
|
2722 (let ((feed (get-text-property (point) 'feed))
|
|
2723 (item nil))
|
|
2724 (when feed
|
|
2725 (save-excursion
|
|
2726 (newsticker--buffer-beginning-of-item)
|
|
2727 (let ((inhibit-read-only t)
|
|
2728 (oldage (get-text-property (point) 'nt-age))
|
|
2729 (title (get-text-property (point) 'nt-title))
|
|
2730 (guid (get-text-property (point) 'nt-guid))
|
|
2731 (pos (save-excursion (newsticker--buffer-end-of-item))))
|
|
2732 (let ((newage 'immortal))
|
|
2733 (if (eq oldage 'immortal)
|
|
2734 (setq newage 'old))
|
|
2735 (setq item (newsticker--cache-contains newsticker--cache
|
|
2736 feed title nil nil nil
|
|
2737 guid))
|
|
2738 ;; change age
|
|
2739 (when item
|
|
2740 (setcar (nthcdr 4 item) newage)
|
|
2741 (newsticker--do-forget-preformatted item))
|
|
2742 (if (or (and (eq newage 'immortal)
|
|
2743 newsticker-hide-immortal-items-in-echo-area)
|
|
2744 (and (eq newage 'obsolete)
|
|
2745 newsticker-hide-obsolete-items-in-echo-area)
|
|
2746 (and (eq oldage 'immortal)
|
|
2747 (not
|
|
2748 (eq newsticker-hide-old-items-in-newsticker-buffer
|
|
2749 newsticker-hide-immortal-items-in-echo-area))))
|
|
2750 (newsticker--ticker-text-remove feed title)
|
|
2751 (newsticker--ticker-text-setup))
|
|
2752 (save-excursion
|
|
2753 (save-restriction
|
|
2754 (widen)
|
|
2755 (put-text-property (point) pos 'nt-age newage)
|
|
2756 (if (eq newage 'immortal)
|
|
2757 (put-text-property (point) pos 'nt-age 'immortal)
|
|
2758 (put-text-property (point) pos 'nt-age 'old))
|
|
2759 (newsticker--buffer-set-faces (point) pos))))))
|
|
2760 (if item
|
|
2761 (newsticker-next-item t))))))
|
|
2762
|
|
2763 (defun newsticker-mark-all-items-as-read ()
|
|
2764 "Mark all items as read and clear ticker contents."
|
|
2765 (interactive)
|
|
2766 (when (or newsticker--buffer-uptodate-p
|
|
2767 (y-or-n-p
|
|
2768 "Buffer is not up to date -- really mark items as read? "))
|
|
2769 (newsticker--cache-replace-age newsticker--cache 'any 'new 'old)
|
|
2770 (newsticker--buffer-set-uptodate nil)
|
|
2771 (newsticker--ticker-text-setup)
|
|
2772 (newsticker--cache-update)
|
|
2773 (newsticker-buffer-update)))
|
|
2774
|
|
2775 (defun newsticker-hide-extra ()
|
|
2776 "Hide the extra elements of items."
|
|
2777 (interactive)
|
|
2778 (newsticker--buffer-hideshow 'extra nil)
|
|
2779 (newsticker--buffer-redraw))
|
|
2780
|
|
2781 (defun newsticker-show-extra ()
|
|
2782 "Show the extra elements of items."
|
|
2783 (interactive)
|
|
2784 (newsticker--buffer-hideshow 'extra t)
|
|
2785 (newsticker--buffer-redraw))
|
|
2786
|
|
2787 (defun newsticker-hide-old-item-desc ()
|
|
2788 "Hide the description of old items."
|
|
2789 (interactive)
|
|
2790 (newsticker--buffer-hideshow 'desc-old nil)
|
|
2791 (newsticker--buffer-redraw))
|
|
2792
|
|
2793 (defun newsticker-show-old-item-desc ()
|
|
2794 "Show the description of old items."
|
|
2795 (interactive)
|
|
2796 (newsticker--buffer-hideshow 'item-old t)
|
|
2797 (newsticker--buffer-hideshow 'desc-old t)
|
|
2798 (newsticker--buffer-redraw))
|
|
2799
|
|
2800 (defun newsticker-hide-new-item-desc ()
|
|
2801 "Hide the description of new items."
|
|
2802 (interactive)
|
|
2803 (newsticker--buffer-hideshow 'desc-new nil)
|
|
2804 (newsticker--buffer-hideshow 'desc-immortal nil)
|
|
2805 (newsticker--buffer-hideshow 'desc-obsolete nil)
|
|
2806 (newsticker--buffer-redraw))
|
|
2807
|
|
2808 (defun newsticker-show-new-item-desc ()
|
|
2809 "Show the description of new items."
|
|
2810 (interactive)
|
|
2811 (newsticker--buffer-hideshow 'desc-new t)
|
|
2812 (newsticker--buffer-hideshow 'desc-immortal t)
|
|
2813 (newsticker--buffer-hideshow 'desc-obsolete t)
|
|
2814 (newsticker--buffer-redraw))
|
|
2815
|
|
2816 (defun newsticker-hide-feed-desc ()
|
|
2817 "Hide the description of feeds."
|
|
2818 (interactive)
|
|
2819 (newsticker--buffer-hideshow 'desc-feed nil)
|
|
2820 (newsticker--buffer-redraw))
|
|
2821
|
|
2822 (defun newsticker-show-feed-desc ()
|
|
2823 "Show the description of old items."
|
|
2824 (interactive)
|
|
2825 (newsticker--buffer-hideshow 'desc-feed t)
|
|
2826 (newsticker--buffer-redraw))
|
|
2827
|
|
2828 (defun newsticker-hide-all-desc ()
|
|
2829 "Hide the descriptions of feeds and all items."
|
|
2830 (interactive)
|
|
2831 (newsticker--buffer-hideshow 'desc-feed nil)
|
|
2832 (newsticker--buffer-hideshow 'desc-immortal nil)
|
|
2833 (newsticker--buffer-hideshow 'desc-obsolete nil)
|
|
2834 (newsticker--buffer-hideshow 'desc-new nil)
|
|
2835 (newsticker--buffer-hideshow 'desc-old nil)
|
|
2836 (newsticker--buffer-redraw))
|
|
2837
|
|
2838 (defun newsticker-show-all-desc ()
|
|
2839 "Show the descriptions of feeds and all items."
|
|
2840 (interactive)
|
|
2841 (newsticker--buffer-hideshow 'desc-feed t)
|
|
2842 (newsticker--buffer-hideshow 'desc-immortal t)
|
|
2843 (newsticker--buffer-hideshow 'desc-obsolete t)
|
|
2844 (newsticker--buffer-hideshow 'desc-new t)
|
|
2845 (newsticker--buffer-hideshow 'desc-old t)
|
|
2846 (newsticker--buffer-redraw))
|
|
2847
|
|
2848 (defun newsticker-hide-old-items ()
|
|
2849 "Hide old items."
|
|
2850 (interactive)
|
|
2851 (newsticker--buffer-hideshow 'desc-old nil)
|
|
2852 (newsticker--buffer-hideshow 'item-old nil)
|
|
2853 (newsticker--buffer-redraw))
|
|
2854
|
|
2855 (defun newsticker-show-old-items ()
|
|
2856 "Show old items."
|
|
2857 (interactive)
|
|
2858 (newsticker--buffer-hideshow 'desc-old t)
|
|
2859 (newsticker--buffer-hideshow 'item-old t)
|
|
2860 (newsticker--buffer-redraw))
|
|
2861
|
|
2862 (defun newsticker-hide-entry ()
|
|
2863 "Hide description of entry at point."
|
|
2864 (interactive)
|
|
2865 (save-excursion
|
|
2866 (let* (pos1 pos2
|
|
2867 (inhibit-read-only t)
|
|
2868 inv-prop org-inv-prop
|
|
2869 is-invisible)
|
|
2870 (newsticker--buffer-beginning-of-item)
|
|
2871 (newsticker--buffer-goto '(desc))
|
|
2872 (setq pos1 (max (point-min) (1- (point))))
|
|
2873 (newsticker--buffer-goto '(extra feed item))
|
|
2874 (setq pos2 (max (point-min) (1- (point))))
|
|
2875 (setq inv-prop (get-text-property pos1 'invisible))
|
|
2876 (setq org-inv-prop (get-text-property pos1 'org-invisible))
|
|
2877 (cond ((eq inv-prop t)
|
|
2878 ;; do nothing
|
|
2879 )
|
|
2880 ((eq org-inv-prop nil)
|
|
2881 (add-text-properties pos1 pos2 (list 'invisible t
|
|
2882 'org-invisible inv-prop)))
|
|
2883 (t
|
|
2884 ;; toggle
|
|
2885 (add-text-properties pos1 pos2 (list 'invisible org-inv-prop))
|
|
2886 (remove-text-properties pos1 pos2 '(org-invisible))))))
|
|
2887 (newsticker--buffer-redraw))
|
|
2888
|
|
2889 (defun newsticker-show-entry ()
|
|
2890 "Show description of entry at point."
|
|
2891 (interactive)
|
|
2892 (save-excursion
|
|
2893 (let* (pos1 pos2
|
|
2894 (inhibit-read-only t)
|
|
2895 inv-prop org-inv-prop
|
|
2896 is-invisible)
|
|
2897 (newsticker--buffer-beginning-of-item)
|
|
2898 (newsticker--buffer-goto '(desc))
|
|
2899 (setq pos1 (max (point-min) (1- (point))))
|
|
2900 (newsticker--buffer-goto '(extra feed item))
|
|
2901 (setq pos2 (max (point-min) (1- (point))))
|
|
2902 (setq inv-prop (get-text-property pos1 'invisible))
|
|
2903 (setq org-inv-prop (get-text-property pos1 'org-invisible))
|
|
2904 (cond ((eq org-inv-prop nil)
|
|
2905 (add-text-properties pos1 pos2 (list 'invisible nil
|
|
2906 'org-invisible inv-prop)))
|
|
2907 (t
|
|
2908 ;; toggle
|
|
2909 (add-text-properties pos1 pos2 (list 'invisible org-inv-prop))
|
|
2910 (remove-text-properties pos1 pos2 '(org-invisible))))))
|
|
2911 (newsticker--buffer-redraw))
|
|
2912
|
|
2913 (defun newsticker-toggle-auto-narrow-to-feed ()
|
|
2914 "Toggle narrowing to current news feed.
|
|
2915 If auto-narrowing is active, only news item of the current feed
|
|
2916 are visible."
|
|
2917 (interactive)
|
|
2918 (newsticker-set-auto-narrow-to-feed (not newsticker--auto-narrow-to-feed)))
|
|
2919
|
|
2920 (defun newsticker-set-auto-narrow-to-feed (value)
|
|
2921 "Turn narrowing to current news feed on or off.
|
|
2922 If VALUE is nil, auto-narrowing is turned off, otherwise it is turned on."
|
|
2923 (interactive)
|
|
2924 (setq newsticker--auto-narrow-to-item nil)
|
|
2925 (setq newsticker--auto-narrow-to-feed value)
|
|
2926 (widen)
|
|
2927 (run-hooks 'newsticker-narrow-hook))
|
|
2928
|
|
2929 (defun newsticker-toggle-auto-narrow-to-item ()
|
|
2930 "Toggle narrowing to current news item.
|
|
2931 If auto-narrowing is active, only one item of the current feed
|
|
2932 is visible."
|
|
2933 (interactive)
|
|
2934 (newsticker-set-auto-narrow-to-item (not newsticker--auto-narrow-to-item)))
|
|
2935
|
|
2936 (defun newsticker-set-auto-narrow-to-item (value)
|
|
2937 "Turn narrowing to current news item on or off.
|
|
2938 If VALUE is nil, auto-narrowing is turned off, otherwise it is turned on."
|
|
2939 (interactive)
|
|
2940 (setq newsticker--auto-narrow-to-feed nil)
|
|
2941 (setq newsticker--auto-narrow-to-item value)
|
|
2942 (widen)
|
|
2943 (run-hooks 'newsticker-narrow-hook))
|
|
2944
|
|
2945 (defun newsticker-customize ()
|
|
2946 "Open the newsticker customization group."
|
|
2947 (interactive)
|
|
2948 (customize-group "newsticker"))
|
|
2949
|
|
2950 (defun newsticker-next-feed-available-p ()
|
|
2951 "Return t if position is before last feed, nil otherwise."
|
|
2952 (save-excursion
|
|
2953 (let ((p (point)))
|
|
2954 (newsticker--buffer-goto '(feed))
|
|
2955 (not (= p (point))))))
|
|
2956
|
|
2957 (defun newsticker-previous-feed-available-p ()
|
|
2958 "Return t if position is behind first feed, nil otherwise."
|
|
2959 (save-excursion
|
|
2960 (let ((p (point)))
|
|
2961 (newsticker--buffer-goto '(feed) nil t)
|
|
2962 (not (= p (point))))))
|
|
2963
|
|
2964 (defun newsticker-next-item-available-p ()
|
|
2965 "Return t if position is before last feed, nil otherwise."
|
|
2966 (save-excursion
|
|
2967 (catch 'result
|
|
2968 (while (< (point) (point-max))
|
|
2969 (unless (newsticker--buffer-goto '(item))
|
|
2970 (throw 'result nil))
|
|
2971 (unless (newsticker--lists-intersect-p
|
|
2972 (get-text-property (point) 'invisible)
|
|
2973 buffer-invisibility-spec)
|
|
2974 (throw 'result t))))))
|
|
2975
|
|
2976 (defun newsticker-previous-item-available-p ()
|
|
2977 "Return t if position is behind first item, nil otherwise."
|
|
2978 (save-excursion
|
|
2979 (catch 'result
|
|
2980 (while (> (point) (point-min))
|
|
2981 (unless (newsticker--buffer-goto '(item) nil t)
|
|
2982 (throw 'result nil))
|
|
2983 (unless (newsticker--lists-intersect-p
|
|
2984 (get-text-property (point) 'invisible)
|
|
2985 buffer-invisibility-spec)
|
|
2986 (throw 'result t))))))
|
|
2987
|
|
2988 (defun newsticker-item-not-old-p ()
|
|
2989 "Return t if there is an item at point which is not old, nil otherwise."
|
|
2990 (when (get-text-property (point) 'feed)
|
|
2991 (save-excursion
|
|
2992 (newsticker--buffer-beginning-of-item)
|
|
2993 (let ((age (get-text-property (point) 'nt-age)))
|
|
2994 (and (memq age '(new immortal obsolete)) t)))))
|
|
2995
|
|
2996 (defun newsticker-item-not-immortal-p ()
|
|
2997 "Return t if there is an item at point which is not immortal, nil otherwise."
|
|
2998 (when (get-text-property (point) 'feed)
|
|
2999 (save-excursion
|
|
3000 (newsticker--buffer-beginning-of-item)
|
|
3001 (let ((age (get-text-property (point) 'nt-age)))
|
|
3002 (and (memq age '(new old obsolete)) t)))))
|
|
3003
|
|
3004 ;; ======================================================================
|
|
3005 ;;; local stuff
|
|
3006 ;; ======================================================================
|
|
3007 (defun newsticker-running-p ()
|
|
3008 "Check whether newsticker is running.
|
|
3009 Return t if newsticker is running, nil otherwise. Newsticker is
|
|
3010 considered to be running if the newsticker timer list is not empty."
|
|
3011 (> (length newsticker--retrieval-timer-list) 0))
|
|
3012
|
|
3013 (defun newsticker-ticker-running-p ()
|
|
3014 "Check whether newsticker's actual ticker is running.
|
|
3015 Return t if ticker is running, nil otherwise. Newsticker is
|
|
3016 considered to be running if the newsticker timer list is not
|
|
3017 empty."
|
|
3018 (timerp newsticker--display-timer))
|
|
3019
|
|
3020 ;; ======================================================================
|
|
3021 ;;; local stuff
|
|
3022 ;; ======================================================================
|
|
3023 (defun newsticker-get-news (feed-name)
|
|
3024 "Get news from the site FEED-NAME and load feed logo.
|
|
3025 FEED-NAME must be a string which occurs as the label (i.e. the first element)
|
|
3026 in an element of `newsticker-url-list' or `newsticker-url-list-defaults'."
|
|
3027 (newsticker--debug-msg "%s: Getting news for %s"
|
|
3028 (format-time-string "%A, %H:%M" (current-time))
|
|
3029 feed-name)
|
|
3030 (let* ((buffername (concat " *newsticker-wget-" feed-name "*"))
|
|
3031 (item (or (assoc feed-name newsticker-url-list)
|
|
3032 (assoc feed-name newsticker-url-list-defaults)
|
|
3033 (error
|
|
3034 "Cannot get news for %s: Check newsticker-url-list"
|
|
3035 feed-name)))
|
|
3036 (url (cadr item))
|
|
3037 (wget-arguments (or (car (cdr (cdr (cdr (cdr item)))))
|
|
3038 newsticker-wget-arguments)))
|
|
3039 (save-excursion
|
|
3040 (set-buffer (get-buffer-create buffername))
|
|
3041 (erase-buffer)
|
|
3042 ;; throw an error if there is an old wget-process around
|
|
3043 (if (get-process feed-name)
|
|
3044 (error "Another wget-process is running for %s" feed-name))
|
|
3045 ;; start wget
|
|
3046 (let* ((args (append wget-arguments (list url)))
|
|
3047 (proc (apply 'start-process feed-name buffername
|
|
3048 newsticker-wget-name args)))
|
|
3049 (set-process-coding-system proc 'no-conversion 'no-conversion)
|
|
3050 (set-process-sentinel proc 'newsticker--sentinel)))))
|
|
3051
|
|
3052
|
|
3053 (defun newsticker-mouse-browse-url (event)
|
|
3054 "Call `browse-url' for the link of the item at which the EVENT occurred."
|
|
3055 (interactive "e")
|
|
3056 (save-excursion
|
|
3057 (switch-to-buffer (window-buffer (posn-window (event-end event))))
|
|
3058 (let ((url (get-text-property (posn-point (event-end event))
|
|
3059 'nt-link)))
|
|
3060 (when url
|
|
3061 (browse-url url)
|
|
3062 (save-excursion
|
|
3063 (goto-char (posn-point (event-end event)))
|
|
3064 (if newsticker-automatically-mark-visited-items-as-old
|
|
3065 (newsticker-mark-item-at-point-as-read t)))))))
|
|
3066
|
|
3067 (defun newsticker-browse-url ()
|
|
3068 "Call `browse-url' for the link of the item at point."
|
|
3069 (interactive)
|
|
3070 (let ((url (get-text-property (point) 'nt-link)))
|
|
3071 (when url
|
|
3072 (browse-url url)
|
|
3073 (if newsticker-automatically-mark-visited-items-as-old
|
|
3074 (newsticker-mark-item-at-point-as-read t)))))
|
|
3075
|
|
3076 (defvar newsticker-open-url-history
|
|
3077 '("wget" "xmms" "realplay")
|
|
3078 "...")
|
|
3079
|
|
3080 (defun newsticker-handle-url ()
|
|
3081 "Ask for a program to open the link of the item at point."
|
|
3082 (interactive)
|
|
3083 (let ((url (get-text-property (point) 'nt-link)))
|
|
3084 (when url
|
|
3085 (let ((prog (read-string "Open url with: " nil
|
|
3086 'newsticker-open-url-history)))
|
|
3087 (when prog
|
|
3088 (message "%s %s" prog url)
|
|
3089 (start-process prog prog prog url)
|
|
3090 (if newsticker-automatically-mark-visited-items-as-old
|
|
3091 (newsticker-mark-item-at-point-as-read t)))))))
|
|
3092
|
|
3093 (defun newsticker--sentinel (process event)
|
|
3094 "Sentinel for extracting news titles from an RDF buffer.
|
|
3095 Argument PROCESS is the process which has just changed its state.
|
|
3096 Argument EVENT tells what has happened to the process."
|
|
3097 (let* ((p-status (process-status process))
|
|
3098 (exit-status (process-exit-status process))
|
|
3099 (time (current-time))
|
|
3100 (name (process-name process))
|
|
3101 (name-symbol (intern name))
|
|
3102 (something-was-added nil))
|
|
3103 ;; catch known errors (zombie processes, rubbish-xml etc.
|
|
3104 ;; if an error occurs the news feed is not updated!
|
|
3105 (catch 'oops
|
|
3106 (unless (and (eq p-status 'exit)
|
|
3107 (= exit-status 0))
|
|
3108 (setq newsticker--cache
|
|
3109 (newsticker--cache-add
|
|
3110 newsticker--cache
|
|
3111 name-symbol
|
|
3112 newsticker--error-headline
|
|
3113 (format
|
|
3114 (concat "%s: Newsticker could not retrieve news from %s.\n"
|
|
3115 "Return status: `%s'\n"
|
|
3116 "Command was `%s'")
|
|
3117 (format-time-string "%A, %H:%M" (current-time))
|
|
3118 name event (process-command process))
|
|
3119 ""
|
|
3120 (current-time)
|
|
3121 'new
|
|
3122 0 nil))
|
|
3123 (message "%s: Error while retrieving news from %s"
|
|
3124 (format-time-string "%A, %H:%M" (current-time))
|
|
3125 (process-name process))
|
|
3126 (throw 'oops nil))
|
|
3127 (let* ((coding-system nil)
|
|
3128 (node-list
|
|
3129 (save-current-buffer
|
|
3130 (set-buffer (process-buffer process))
|
|
3131 ;; a very very dirty workaround to overcome the
|
|
3132 ;; problems with the newest (20030621) xml.el:
|
|
3133 ;; remove all unnecessary whitespace
|
|
3134 (goto-char (point-min))
|
|
3135 (while (re-search-forward ">[ \t\r\n]+<" nil t)
|
|
3136 (replace-match "><" nil t))
|
|
3137 ;; and another brutal workaround (20031105)! For some
|
|
3138 ;; reason the xml parser does not like the colon in the
|
|
3139 ;; doctype name "rdf:RDF"
|
|
3140 (goto-char (point-min))
|
|
3141 (if (re-search-forward "<!DOCTYPE[ \t\n]+rdf:RDF" nil t)
|
|
3142 (replace-match "<!DOCTYPE rdfColonRDF" nil t))
|
|
3143 ;; finally.... ~##^°!!!!!
|
|
3144 (goto-char (point-min))
|
|
3145 (while (search-forward "\r\n" nil t)
|
|
3146 (replace-match "\n" nil t))
|
|
3147 ;; still more brutal workarounds (20040309)! The xml
|
|
3148 ;; parser does not like doctype rss
|
|
3149 (goto-char (point-min))
|
|
3150 (if (re-search-forward "<!DOCTYPE[ \t\n]+rss[ \t\n]*>" nil t)
|
|
3151 (replace-match "" nil t))
|
|
3152 ;; And another one (20050618)! (Fixed in GNU Emacs 22.0.50.18)
|
|
3153 ;; Remove comments to avoid this xml-parsing bug:
|
|
3154 ;; "XML files can have only one toplevel tag"
|
|
3155 (goto-char (point-min))
|
|
3156 (while (search-forward "<!--" nil t)
|
|
3157 (let ((start (match-beginning 0)))
|
|
3158 (unless (search-forward "-->" nil t)
|
|
3159 (error "Can't find end of comment"))
|
|
3160 (delete-region start (point))))
|
|
3161 ;; And another one (20050702)! If description is HTML
|
|
3162 ;; encoded and starts with a `<', wrap the whole
|
|
3163 ;; description in a CDATA expression. This happened for
|
|
3164 ;; http://www.thefreedictionary.com/_/WoD/rss.aspx?type=quote
|
|
3165 (goto-char (point-min))
|
|
3166 (while (re-search-forward
|
|
3167 "<description>\\(<img.*?\\)</description>" nil t)
|
|
3168 (replace-match
|
|
3169 "<description><![CDATA[ \\1 ]]></description>"))
|
|
3170 ;;
|
|
3171 (set-buffer-modified-p nil)
|
|
3172 (goto-char (point-min))
|
|
3173 (if (re-search-forward "encoding=\"\\([^\"]+\\)\""
|
|
3174 nil t)
|
|
3175 (setq coding-system (intern
|
|
3176 (downcase(match-string 1)))))
|
|
3177 (condition-case errordata
|
|
3178 ;; The xml parser might fail
|
|
3179 ;; or the xml might be bugged
|
|
3180 (xml-parse-region (point-min) (point-max))
|
|
3181 (error (message "Could not parse %s: %s"
|
|
3182 (buffer-name) (cadr errordata))
|
|
3183 (throw 'oops nil)))))
|
|
3184 (topnode (car node-list))
|
|
3185 (channelnode (car (xml-get-children topnode 'channel)))
|
|
3186 (imageurl nil)
|
|
3187 (position 0))
|
|
3188 ;; mark all items as obsolete
|
|
3189 (newsticker--cache-replace-age newsticker--cache
|
|
3190 name-symbol
|
|
3191 'new 'obsolete-new)
|
|
3192 (newsticker--cache-replace-age newsticker--cache
|
|
3193 name-symbol
|
|
3194 'old 'obsolete-old)
|
|
3195 (newsticker--cache-replace-age newsticker--cache
|
|
3196 name-symbol
|
|
3197 'feed 'obsolete-old)
|
|
3198 ;; gather the news
|
|
3199 (if (eq (xml-node-name topnode) 'rss)
|
|
3200 ;; this is RSS 0.91 or something similar
|
|
3201 ;; all items are inside the channel node
|
|
3202 (setq topnode channelnode))
|
|
3203 (setq imageurl
|
|
3204 (car (xml-node-children
|
|
3205 (car (xml-get-children
|
|
3206 (car (xml-get-children
|
|
3207 topnode
|
|
3208 'image))
|
|
3209 'url)))))
|
|
3210 (let ((title (or (car (xml-node-children (car (xml-get-children
|
|
3211 channelnode 'title))))
|
|
3212 "[untitled]"))
|
|
3213 (link (or (car (xml-node-children (car (xml-get-children
|
|
3214 channelnode 'link))))
|
|
3215 ""))
|
|
3216 (desc (or (car (xml-node-children (car (xml-get-children
|
|
3217 channelnode
|
|
3218 'content:encoded))))
|
|
3219 (car (xml-node-children (car (xml-get-children
|
|
3220 channelnode
|
|
3221 'description))))
|
|
3222 "[No description available]"))
|
|
3223 (old-item nil))
|
|
3224 ;; check coding system
|
|
3225 (setq coding-system
|
|
3226 (condition-case nil
|
|
3227 (check-coding-system coding-system)
|
|
3228 (coding-system-error
|
|
3229 (message "newsticker.el: %s %s %s %s"
|
|
3230 "ignoring coding system "
|
|
3231 coding-system
|
|
3232 " for "
|
|
3233 name)
|
|
3234 nil)))
|
|
3235 ;; apply coding system
|
|
3236 (when coding-system
|
|
3237 (setq title (newsticker--decode-coding-string title coding-system))
|
|
3238 (if desc
|
|
3239 (setq desc (newsticker--decode-coding-string desc
|
|
3240 coding-system)))
|
|
3241 (setq link (newsticker--decode-coding-string link coding-system)))
|
|
3242 ;; decode numeric entities
|
|
3243 (setq title (newsticker--decode-numeric-entities title))
|
|
3244 (setq desc (newsticker--decode-numeric-entities desc))
|
|
3245 (setq link (newsticker--decode-numeric-entities link))
|
|
3246 ;; remove whitespace from title, desc, and link
|
|
3247 (setq title (newsticker--remove-whitespace title))
|
|
3248 (setq desc (newsticker--remove-whitespace desc))
|
|
3249 (setq link (newsticker--remove-whitespace link))
|
|
3250
|
|
3251 ;; handle the feed itself
|
|
3252 (unless (newsticker--cache-contains newsticker--cache
|
|
3253 name-symbol title
|
|
3254 desc link 'feed)
|
|
3255 (setq something-was-added t))
|
|
3256 (setq newsticker--cache
|
|
3257 (newsticker--cache-add
|
|
3258 newsticker--cache name-symbol
|
|
3259 title desc link time 'feed position
|
|
3260 (xml-node-children channelnode)
|
|
3261 'feed time))
|
|
3262 ;; gather all items for this feed
|
|
3263 (mapc (lambda (node)
|
|
3264 (when (eq (xml-node-name node) 'item)
|
|
3265 (setq position (1+ position))
|
|
3266 (setq title (or (car (xml-node-children
|
|
3267 (car (xml-get-children
|
|
3268 node 'title))))
|
|
3269 "[untitled]"))
|
|
3270 (setq link (or (car (xml-node-children
|
|
3271 (car (xml-get-children
|
|
3272 node 'link))))
|
|
3273 ""))
|
|
3274 (setq desc (or
|
|
3275 (car (xml-node-children
|
|
3276 (car (xml-get-children
|
|
3277 node 'content:encoded))))
|
|
3278 (car (xml-node-children
|
|
3279 (car (xml-get-children
|
|
3280 node 'description))))))
|
|
3281 ;; use pubDate value if present
|
|
3282 (setq time (or (newsticker--decode-rfc822-date
|
|
3283 (car (xml-node-children
|
|
3284 (car (xml-get-children
|
|
3285 node 'pubDate)))))
|
|
3286 time))
|
|
3287 ;; use dc:date value if present
|
|
3288 (setq time (or (newsticker--decode-iso8601-date
|
|
3289 (car (xml-node-children
|
|
3290 (car (xml-get-children
|
|
3291 node 'dc:date)))))
|
|
3292 time))
|
|
3293 ;; It happened that the title or description
|
|
3294 ;; contained evil HTML code that confused the
|
|
3295 ;; xml parser. Therefore:
|
|
3296 (unless (stringp title)
|
|
3297 (setq title (prin1-to-string title)))
|
|
3298 (unless (or (stringp desc) (not desc))
|
|
3299 (setq desc (prin1-to-string desc)))
|
|
3300 ;; ignore items with empty title AND empty desc
|
|
3301 (when (or (> (length title) 0)
|
|
3302 (> (length desc) 0))
|
|
3303 ;; apply coding system
|
|
3304 (when coding-system
|
|
3305 (setq title (newsticker--decode-coding-string
|
|
3306 title coding-system))
|
|
3307 (if desc
|
|
3308 (setq desc (newsticker--decode-coding-string desc
|
|
3309 coding-system)))
|
|
3310 (setq link (newsticker--decode-coding-string
|
|
3311 link coding-system)))
|
|
3312 ;; decode numeric entities
|
|
3313 (setq title (newsticker--decode-numeric-entities title))
|
|
3314 (when desc
|
|
3315 (setq desc (newsticker--decode-numeric-entities desc)))
|
|
3316 (setq link (newsticker--decode-numeric-entities link))
|
|
3317 ;; remove whitespace from title, desc, and link
|
|
3318 (setq title (newsticker--remove-whitespace title))
|
|
3319 (setq desc (newsticker--remove-whitespace desc))
|
|
3320 (setq link (newsticker--remove-whitespace link))
|
|
3321 ;; add data to cache
|
|
3322 ;; do we have this item already?
|
|
3323 (let* ((tguid (assoc 'guid (xml-node-children node)))
|
|
3324 (guid (if (stringp tguid)
|
|
3325 tguid
|
|
3326 (car (xml-node-children tguid)))))
|
|
3327 ;;(message "guid=%s" guid)
|
|
3328 (setq old-item
|
|
3329 (newsticker--cache-contains newsticker--cache
|
|
3330 name-symbol title
|
|
3331 desc link nil guid)))
|
|
3332 ;; add this item, or mark it as old, or do nothing
|
|
3333 (let ((age1 'new)
|
|
3334 (age2 'old)
|
|
3335 (item-new-p nil))
|
|
3336 (if old-item
|
|
3337 (let ((prev-age (newsticker--age old-item)))
|
|
3338 (unless
|
|
3339 newsticker-automatically-mark-items-as-old
|
|
3340 (if (eq prev-age 'obsolete-old)
|
|
3341 (setq age2 'old)
|
|
3342 (setq age2 'new)))
|
|
3343 (if (eq prev-age 'immortal)
|
|
3344 (setq age2 'immortal)))
|
|
3345 ;; item was not there
|
|
3346 (setq item-new-p t)
|
|
3347 (setq something-was-added t))
|
|
3348 (setq newsticker--cache
|
|
3349 (newsticker--cache-add
|
|
3350 newsticker--cache name-symbol title desc link
|
|
3351 time age1 position (xml-node-children node)
|
|
3352 age2))
|
|
3353 (when item-new-p
|
|
3354 (let ((item (newsticker--cache-contains
|
|
3355 newsticker--cache
|
|
3356 name-symbol title
|
|
3357 desc link nil)))
|
|
3358 (if newsticker-auto-mark-filter
|
|
3359 (newsticker--run-auto-mark-filter name item))
|
|
3360 (run-hook-with-args
|
|
3361 'newsticker-new-item-functions name item)))))))
|
|
3362 (xml-get-children topnode 'item)))
|
|
3363 ;; Remove those old items from cache which have been removed from
|
|
3364 ;; the feed
|
|
3365 (newsticker--cache-replace-age newsticker--cache
|
|
3366 name-symbol 'obsolete-old 'deleteme)
|
|
3367 (newsticker--cache-remove newsticker--cache name-symbol
|
|
3368 'deleteme)
|
|
3369 ;; Remove those new items from cache which have been removed from
|
|
3370 ;; the feed. Or keep them as `obsolete'
|
|
3371 (if (not newsticker-keep-obsolete-items)
|
|
3372 (newsticker--cache-remove newsticker--cache
|
|
3373 name-symbol 'obsolete-new)
|
|
3374 (setq newsticker--cache
|
|
3375 (newsticker--cache-mark-expired
|
|
3376 newsticker--cache name-symbol 'obsolete 'obsolete-expired
|
|
3377 newsticker-obsolete-item-max-age))
|
|
3378 (newsticker--cache-remove newsticker--cache
|
|
3379 name-symbol 'obsolete-expired)
|
|
3380 (newsticker--cache-replace-age newsticker--cache
|
|
3381 name-symbol 'obsolete-new
|
|
3382 'obsolete))
|
|
3383 ;; bring cache data into proper order....
|
|
3384 ;; (newsticker--cache-sort)
|
|
3385 ;; setup scrollable text
|
|
3386 (newsticker--ticker-text-setup)
|
|
3387 (setq newsticker--latest-update-time (current-time))
|
|
3388 (when something-was-added
|
|
3389 ;; FIXME: should we care about removed items as well?
|
|
3390 (newsticker--cache-update)
|
|
3391 (newsticker--buffer-set-uptodate nil))
|
|
3392 ;; kill the process buffer if wanted
|
|
3393 (unless newsticker-debug
|
|
3394 (kill-buffer (process-buffer process)))
|
|
3395 ;; launch retrieval of image
|
|
3396 (when (and imageurl
|
|
3397 (string-match "%l" newsticker-heading-format))
|
|
3398 (newsticker--image-get name imageurl))))))
|
|
3399
|
|
3400 (defun newsticker--display-tick ()
|
|
3401 "Called from the display timer.
|
|
3402 This function calls a display function, according to the variable
|
|
3403 `newsticker-scroll-smoothly'."
|
|
3404 (if newsticker-scroll-smoothly
|
|
3405 (newsticker--display-scroll)
|
|
3406 (newsticker--display-jump)))
|
|
3407
|
|
3408 (defsubst newsticker--echo-area-clean-p ()
|
|
3409 "Check whether somebody is using the echo area / minibuffer.
|
|
3410 Return t if echo area and minibuffer are unused."
|
|
3411 (not (or (active-minibuffer-window)
|
|
3412 (and (current-message)
|
|
3413 (not (string= (current-message)
|
|
3414 newsticker--prev-message))))))
|
|
3415
|
|
3416 (defun newsticker--display-jump ()
|
|
3417 "Called from the display timer.
|
|
3418 This function displays the next ticker item in the echo area, unless
|
|
3419 there is another message displayed or the minibuffer is active."
|
|
3420 (let ((message-log-max nil));; prevents message text from being logged
|
|
3421 (when (newsticker--echo-area-clean-p)
|
|
3422 (setq newsticker--item-position (1+ newsticker--item-position))
|
|
3423 (when (>= newsticker--item-position (length newsticker--item-list))
|
|
3424 (setq newsticker--item-position 0))
|
|
3425 (setq newsticker--prev-message
|
|
3426 (nth newsticker--item-position newsticker--item-list))
|
|
3427 (message newsticker--prev-message))))
|
|
3428
|
|
3429 (defun newsticker--display-scroll ()
|
|
3430 "Called from the display timer.
|
|
3431 This function scrolls the ticker items in the echo area, unless
|
|
3432 there is another message displayed or the minibuffer is active."
|
|
3433 (when (newsticker--echo-area-clean-p)
|
|
3434 (let* ((width (- (frame-width) 1))
|
|
3435 (message-log-max nil);; prevents message text from being logged
|
|
3436 (i newsticker--item-position)
|
|
3437 subtext
|
|
3438 (s-text newsticker--scrollable-text)
|
|
3439 (l (length s-text)))
|
|
3440 ;; don't show anything if there is nothing to show
|
|
3441 (unless (< (length s-text) 1)
|
|
3442 ;; repeat the ticker string if it is shorter than frame width
|
|
3443 (while (< (length s-text) width)
|
|
3444 (setq s-text (concat s-text s-text)))
|
|
3445 ;; get the width of the printed string
|
|
3446 (setq l (length s-text))
|
|
3447 (cond ((< i (- l width))
|
|
3448 (setq subtext (substring s-text i (+ i width))))
|
|
3449 (t
|
|
3450 (setq subtext (concat
|
|
3451 (substring s-text i l)
|
|
3452 (substring s-text 0 (- width (- l i)))))))
|
|
3453 ;; Take care of multibyte strings, for which (string-width) is
|
|
3454 ;; larger than (length).
|
|
3455 ;; Actually, such strings may be smaller than (frame-width)
|
|
3456 ;; because return values of (string-width) are too large:
|
|
3457 ;; (string-width "<japanese character>") => 2
|
|
3458 (let ((t-width (1- (length subtext))))
|
|
3459 (while (> (string-width subtext) width)
|
|
3460 (setq subtext (substring subtext 0 t-width))
|
|
3461 (setq t-width (1- t-width))))
|
|
3462 ;; show the ticker text and save current position
|
|
3463 (message subtext)
|
|
3464 (setq newsticker--prev-message subtext)
|
|
3465 (setq newsticker--item-position (1+ i))
|
|
3466 (when (>= newsticker--item-position l)
|
|
3467 (setq newsticker--item-position 0))))))
|
|
3468
|
|
3469 ;; ======================================================================
|
|
3470 ;;; misc
|
|
3471 ;; ======================================================================
|
|
3472 (defun newsticker--decode-coding-string (string coding-system)
|
|
3473 "Wrapper around `decode-coding-string'.
|
|
3474 This functions passes the arguments STRING and CODING-SYSTEM to
|
|
3475 `decode-coding-string'. If the decoding is successful the
|
|
3476 decoded string is returned, otherwise the unmodified input string
|
|
3477 is returned."
|
|
3478 (condition-case nil
|
|
3479 (decode-coding-string string coding-system)
|
|
3480 (error
|
|
3481 (message "Cannot decode encoded string `%s'" string)
|
|
3482 string)))
|
|
3483
|
|
3484 (defun newsticker--decode-numeric-entities (string)
|
|
3485 "Decode SGML numeric entities by their respective utf characters.
|
|
3486 This function replaces numeric entities in the input STRING and
|
|
3487 returns the modified string. For example \"*\" gets replaced
|
|
3488 by \"*\"."
|
|
3489 (let ((start 0))
|
|
3490 (while (string-match "&#\\([0-9]+\\);" string start)
|
|
3491 (condition-case nil
|
|
3492 (setq string (replace-match
|
|
3493 (string (read (substring string (match-beginning 1)
|
|
3494 (match-end 1))))
|
|
3495 nil nil string))
|
|
3496 (error nil))
|
|
3497 (setq start (1+ (match-beginning 0))))
|
|
3498 string))
|
|
3499
|
|
3500 (defun newsticker--remove-whitespace (string)
|
|
3501 "Remove leading and trailing whitespace from STRING."
|
|
3502 ;; we must have ...+ but not ...* in the regexps otherwise xemacs loops
|
|
3503 ;; endlessly...
|
|
3504 (when string
|
|
3505 (replace-regexp-in-string
|
|
3506 "[ \t\r\n]+$" ""
|
|
3507 (replace-regexp-in-string "^[ \t\r\n]+" "" string))))
|
|
3508
|
|
3509 (defun newsticker--do-forget-preformatted (item)
|
|
3510 "Forget all cached pre-formatted data.
|
|
3511 Remove the pre-formatted from `newsticker--cache'."
|
|
3512 (if (nthcdr 7 item)
|
|
3513 (setcar (nthcdr 7 item) nil))
|
|
3514 (if (nthcdr 6 item)
|
|
3515 (setcar (nthcdr 6 item) nil)))
|
|
3516
|
|
3517 (defun newsticker--forget-preformatted ()
|
|
3518 "Forget all cached pre-formatted data.
|
|
3519 Remove the pre-formatted from `newsticker--cache'."
|
|
3520 (mapc (lambda (feed)
|
|
3521 (mapc 'newsticker--do-forget-preformatted
|
|
3522 (cdr feed)))
|
|
3523 newsticker--cache)
|
|
3524 (newsticker--buffer-set-uptodate nil))
|
|
3525
|
|
3526 (defun newsticker--debug-msg (string &rest args)
|
|
3527 "Print newsticker debug messages.
|
|
3528 This function calls `message' with arguments STRING and ARGS, if
|
|
3529 `newsticker-debug' is non-nil."
|
|
3530 (and newsticker-debug
|
|
3531 ;;(not (active-minibuffer-window))
|
|
3532 ;;(not (current-message))
|
|
3533 (apply 'message string args)))
|
|
3534
|
|
3535 (defun newsticker--decode-iso8601-date (iso8601-string)
|
|
3536 "Return ISO8601-STRING in format like `decode-time'.
|
|
3537 Converts from ISO-8601 to Emacs representation. If ISO8601-STRING
|
|
3538 Examples:
|
|
3539 2004-09-17T05:09:49+00:00
|
|
3540 2004-09-17T05:09+00:00
|
|
3541 2004-09-17T05:09:49
|
|
3542 2004-09-17T05:09
|
|
3543 2004-09-17
|
|
3544 2004-09
|
|
3545 2004"
|
|
3546 (if iso8601-string
|
|
3547 (when (string-match
|
|
3548 (concat
|
|
3549 "\\([0-9]\\{4\\}\\)"
|
|
3550 "\\(-\\([0-9]\\{2\\}\\)"
|
|
3551 "\\(-\\([0-9]\\{2\\}\\)"
|
|
3552 "\\(T"
|
|
3553 "\\([0-9]\\{2\\}\\):\\([0-9]\\{2\\}\\)"
|
|
3554 "\\(:\\([0-9]\\{2\\}\\)\\)?"
|
|
3555 "\\(\\([-+Z]\\)\\(\\([0-9]\\{2\\}\\):\\([0-9]\\{2\\}\\)\\)?\\)?"
|
|
3556 "\\)?\\)?\\)?")
|
|
3557 iso8601-string)
|
|
3558 (let ((year (read (match-string 1 iso8601-string)))
|
|
3559 (month (read (match-string 3 iso8601-string)))
|
|
3560 (day (read (match-string 5 iso8601-string)))
|
|
3561 (hour (read (or (match-string 7 iso8601-string)
|
|
3562 "0")))
|
|
3563 (minute (read (or (match-string 8 iso8601-string)
|
|
3564 "0")))
|
|
3565 ;;(second (read (or (match-string 10 iso8601-string)
|
|
3566 ;; "0")))
|
|
3567 (sign (match-string 12 iso8601-string))
|
|
3568 (offset-hour (read (or (match-string 14 iso8601-string)
|
|
3569 "0")))
|
|
3570 (offset-minute (read (or (match-string 15 iso8601-string)
|
|
3571 "0")))
|
|
3572 (second 0))
|
|
3573 (cond ((string= sign "+")
|
|
3574 (setq hour (- hour offset-hour))
|
|
3575 (setq minute (- minute offset-minute)))
|
|
3576 ((string= sign "-")
|
|
3577 (setq hour (+ hour offset-hour))
|
|
3578 (setq minute (+ minute offset-minute))))
|
|
3579 ;; if UTC subtract current-time-zone offset
|
|
3580 ;;(setq second (+ (car (current-time-zone)) second)))
|
|
3581
|
|
3582 (condition-case nil
|
|
3583 (encode-time second minute hour day month year t)
|
|
3584 (error
|
|
3585 (message "Cannot decode \"%s\"" iso8601-string)
|
|
3586 nil))))
|
|
3587 nil))
|
|
3588
|
|
3589 (defun newsticker--decode-rfc822-date (rfc822-string)
|
|
3590 "Return RFC822-STRING in format like `decode-time'.
|
|
3591 Converts from RFC822 to Emacs representation.
|
|
3592 Examples:
|
|
3593 Sat, 07 Sep 2002 00:00:01 GMT
|
|
3594 07 Sep 2002 00:00:01 GMT"
|
|
3595 (if rfc822-string
|
|
3596 (when (string-match
|
|
3597 (concat
|
|
3598 "\\s-*"
|
|
3599 ;; week day
|
|
3600 "\\(\\(Mon\\|Tue\\|Wed\\|Thu\\|Fri\\|Sat\\|Sun\\)\\s-*,?\\)\\s-+"
|
|
3601 ;; day
|
|
3602 "\\([0-9]\\{1,2\\}\\)\\s-+"
|
|
3603 ;; month
|
|
3604 "\\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|"
|
|
3605 "Sep\\|Oct\\|Nov\\|Dec\\)\\s-+"
|
|
3606 ;; year
|
|
3607 "\\([0-9]\\{2,4\\}\\)\\s-+"
|
|
3608 ;; hour
|
|
3609 "\\([0-9]\\{2\\}\\)"
|
|
3610 ;; minute
|
|
3611 ":\\([0-9]\\{2\\}\\)"
|
|
3612 ;; second
|
|
3613 "\\(:\\([0-9]\\{2\\}\\)\\)?"
|
|
3614 ;; zone -- fixme
|
|
3615 "\\(\\s-+.*\\)?")
|
|
3616 rfc822-string)
|
|
3617 (let ((day (read (match-string 3 rfc822-string)))
|
|
3618 (month-name (match-string 4 rfc822-string))
|
|
3619 (month 0)
|
|
3620 (year (read (match-string 5 rfc822-string)))
|
|
3621 (hour (read (match-string 6 rfc822-string)))
|
|
3622 (minute (read (match-string 7 rfc822-string)))
|
|
3623 (second (read (or (match-string 9 rfc822-string)
|
|
3624 "0")))
|
|
3625 ;;(zone (match-string 10 rfc822-string))
|
|
3626 )
|
|
3627 (condition-case error-data
|
|
3628 (let ((i 1))
|
|
3629 (mapc (lambda (m)
|
|
3630 (if (string= month-name m)
|
|
3631 (setq month i))
|
|
3632 (setq i (1+ i)))
|
|
3633 '("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug"
|
|
3634 "Sep" "Oct" "Nov" "Dec"))
|
|
3635 (encode-time second minute hour day month year t))
|
|
3636 (error
|
|
3637 (message "Cannot decode \"%s\": %s %s" rfc822-string
|
|
3638 (car error-data) (cdr error-data))
|
|
3639 nil))))
|
|
3640 nil))
|
|
3641
|
|
3642 (defun newsticker--lists-intersect-p (list1 list2)
|
|
3643 "Return t if LIST1 and LIST2 share elements."
|
|
3644 (let ((result nil))
|
|
3645 (mapc (lambda (elt)
|
|
3646 (if (memq elt list2)
|
|
3647 (setq result t)))
|
|
3648 list1)
|
|
3649 result))
|
|
3650
|
|
3651 ;; ======================================================================
|
|
3652 ;;; images
|
|
3653 ;; ======================================================================
|
|
3654 (defun newsticker--image-get (feed-name url)
|
|
3655 "Get image of the news site FEED-NAME from URL.
|
|
3656 If the image has been downloaded in the last 24h do nothing."
|
|
3657 (let ((image-name (concat newsticker-imagecache-dirname "/"
|
|
3658 feed-name)))
|
|
3659 (if (and (file-exists-p image-name)
|
|
3660 (time-less-p (current-time)
|
|
3661 (time-add (nth 5 (file-attributes image-name))
|
|
3662 (seconds-to-time 86400))))
|
|
3663 (newsticker--debug-msg "%s: Getting image for %s skipped"
|
|
3664 (format-time-string "%A, %H:%M" (current-time))
|
|
3665 feed-name)
|
|
3666 ;; download
|
|
3667 (newsticker--debug-msg "%s: Getting image for %s"
|
|
3668 (format-time-string "%A, %H:%M" (current-time))
|
|
3669 feed-name)
|
|
3670 (let* ((buffername (concat " *newsticker-wget-image-" feed-name "*"))
|
|
3671 (item (or (assoc feed-name newsticker-url-list)
|
|
3672 (assoc feed-name newsticker-url-list-defaults)
|
|
3673 (error
|
|
3674 "Cannot get news for %s: Check newsticker-url-list"
|
|
3675 feed-name)))
|
|
3676 (wget-arguments (or (car (cdr (cdr (cdr (cdr item)))))
|
|
3677 newsticker-wget-arguments)))
|
|
3678 (save-excursion
|
|
3679 (set-buffer (get-buffer-create buffername))
|
|
3680 (erase-buffer)
|
|
3681 ;; throw an error if there is an old wget-process around
|
|
3682 (if (get-process feed-name)
|
|
3683 (error "Another wget-process is running for image %s"
|
|
3684 feed-name))
|
|
3685 ;; start wget
|
|
3686 (let* ((args (append wget-arguments (list url)))
|
|
3687 (proc (apply 'start-process feed-name buffername
|
|
3688 newsticker-wget-name args)))
|
|
3689 (set-process-coding-system proc 'no-conversion 'no-conversion)
|
|
3690 (set-process-sentinel proc 'newsticker--image-sentinel)))))))
|
|
3691
|
|
3692 (defun newsticker--image-sentinel (process event)
|
|
3693 "Sentinel for image-retrieving PROCESS caused by EVENT."
|
|
3694 (let* ((p-status (process-status process))
|
|
3695 (exit-status (process-exit-status process))
|
|
3696 (feed-name (process-name process)))
|
|
3697 ;; catch known errors (zombie processes, rubbish-xml etc.
|
|
3698 ;; if an error occurs the news feed is not updated!
|
|
3699 (catch 'oops
|
|
3700 (unless (and (eq p-status 'exit)
|
|
3701 (= exit-status 0))
|
|
3702 (message "%s: Error while retrieving image from %s"
|
|
3703 (format-time-string "%A, %H:%M" (current-time))
|
|
3704 feed-name)
|
|
3705 (throw 'oops nil))
|
|
3706 (let (image-name)
|
|
3707 (save-excursion
|
|
3708 (set-buffer (process-buffer process))
|
|
3709 (setq image-name (concat newsticker-imagecache-dirname "/"
|
|
3710 feed-name))
|
|
3711 (set-buffer-file-coding-system 'no-conversion)
|
|
3712 ;; make sure the cache dir exists
|
|
3713 (unless (file-directory-p newsticker-imagecache-dirname)
|
|
3714 (make-directory newsticker-imagecache-dirname))
|
|
3715 ;; write and close buffer
|
|
3716 (let ((require-final-newline nil)
|
|
3717 (backup-inhibited t)
|
|
3718 (coding-system-for-write 'no-conversion))
|
|
3719 (write-region nil nil image-name nil 'quiet))
|
|
3720 (set-buffer-modified-p nil)
|
|
3721 (kill-buffer (current-buffer)))))))
|
|
3722
|
|
3723 (defun newsticker--image-read (feed-name-symbol disabled)
|
|
3724 "Read the cached image for FEED-NAME-SYMBOL from disk.
|
|
3725 If DISABLED is non-nil the image will be converted to a disabled look
|
|
3726 \(unless `newsticker-enable-logo-manipulations' is not t\).
|
|
3727 Return the image."
|
|
3728 (let ((image-name (concat newsticker-imagecache-dirname "/"
|
|
3729 (symbol-name feed-name-symbol)))
|
|
3730 (img nil))
|
|
3731 (when (file-exists-p image-name)
|
|
3732 (condition-case error-data
|
|
3733 (setq img (create-image
|
|
3734 image-name nil nil
|
|
3735 :conversion (and newsticker-enable-logo-manipulations
|
|
3736 disabled
|
|
3737 'disabled)
|
|
3738 :mask (and newsticker-enable-logo-manipulations
|
|
3739 'heuristic)
|
|
3740 :ascent 70))
|
|
3741 (error
|
|
3742 (message "Error: cannot create image: %s"
|
|
3743 (cadr error-data)))))
|
|
3744 img))
|
|
3745
|
|
3746 ;; ======================================================================
|
|
3747 ;;; imenu stuff
|
|
3748 ;; ======================================================================
|
|
3749 (defun newsticker--imenu-create-index ()
|
|
3750 "Scan newsticker buffer and return an index for imenu."
|
|
3751 (save-excursion
|
|
3752 (goto-char (point-min))
|
|
3753 (let ((index-alist nil)
|
|
3754 (feed-list nil)
|
|
3755 (go-ahead t))
|
|
3756 (while go-ahead
|
|
3757 (let ((type (get-text-property (point) 'nt-type))
|
|
3758 (title (get-text-property (point) 'nt-title)))
|
|
3759 (cond ((eq type 'feed)
|
|
3760 ;; we're on a feed heading
|
|
3761 (when feed-list
|
|
3762 (if index-alist
|
|
3763 (nconc index-alist (list feed-list))
|
|
3764 (setq index-alist (list feed-list))))
|
|
3765 (setq feed-list (list title)))
|
|
3766 (t
|
|
3767 (nconc feed-list
|
|
3768 (list (cons title (point)))))))
|
|
3769 (setq go-ahead (newsticker--buffer-goto '(item feed))))
|
|
3770 (if index-alist
|
|
3771 (nconc index-alist (list feed-list))
|
|
3772 (setq index-alist (list feed-list)))
|
|
3773 index-alist)))
|
|
3774
|
|
3775 (defun newsticker--imenu-goto (name pos &rest args)
|
|
3776 "Go item NAME at position POS and show item.
|
|
3777 ARGS are ignored."
|
|
3778 (goto-char pos)
|
|
3779 (newsticker-show-entry))
|
|
3780
|
|
3781 ;; ======================================================================
|
|
3782 ;;; buffer stuff
|
|
3783 ;; ======================================================================
|
|
3784 (defun newsticker--buffer-set-uptodate (value)
|
|
3785 "Set the uptodate-status of the newsticker buffer to VALUE.
|
|
3786 The mode-line is changed accordingly."
|
|
3787 (setq newsticker--buffer-uptodate-p value)
|
|
3788 (let ((b (get-buffer "*newsticker*")))
|
|
3789 (when b
|
|
3790 (save-excursion
|
|
3791 (set-buffer b)
|
|
3792 (if value
|
|
3793 (setq mode-name "Newsticker -- up to date -- ")
|
|
3794 (setq mode-name "Newsticker -- NEED UPDATE -- ")))
|
|
3795 (sit-for 0))))
|
|
3796
|
|
3797 (defun newsticker--buffer-redraw ()
|
|
3798 "Sometimes (CVS) Emacs forgets to update the window..."
|
|
3799 (if (fboundp 'force-window-update)
|
|
3800 (force-window-update (current-buffer))
|
|
3801 (redraw-frame (selected-frame)))
|
|
3802 (run-hooks 'newsticker-buffer-change-hook)
|
|
3803 (sit-for 0))
|
|
3804
|
|
3805 (defun newsticker--buffer-insert-all-items ()
|
|
3806 "Insert all cached newsticker items into the current buffer.
|
|
3807 Keeps order of feeds as given in `newsticker-url-list' and
|
|
3808 `newsticker-url-list-defaults'."
|
|
3809 (goto-char (point-min))
|
|
3810 (mapc (lambda (url-item)
|
|
3811 (let* ((feed-name (car url-item))
|
|
3812 (feed-name-symbol (intern feed-name))
|
|
3813 (feed (assoc feed-name-symbol newsticker--cache))
|
|
3814 (items (cdr feed))
|
|
3815 (pos (point)))
|
|
3816 (when feed
|
|
3817 ;; insert the feed description
|
|
3818 (mapc (lambda (item)
|
|
3819 (when (eq (newsticker--age item) 'feed)
|
|
3820 (newsticker--buffer-insert-item item
|
|
3821 feed-name-symbol)))
|
|
3822 items)
|
|
3823 ;;insert the items
|
|
3824 (mapc (lambda (item)
|
|
3825 (if (memq (newsticker--age item) '(new immortal old
|
|
3826 obsolete))
|
|
3827 (newsticker--buffer-insert-item item
|
|
3828 feed-name-symbol)))
|
|
3829 items)
|
|
3830 (put-text-property pos (point) 'feed (car feed))
|
|
3831
|
|
3832 ;; insert empty line between feeds
|
|
3833 (let ((p (point)))
|
|
3834 (insert "\n")
|
|
3835 (put-text-property p (point) 'hard t)))))
|
|
3836 (append newsticker-url-list newsticker-url-list-defaults))
|
|
3837
|
|
3838 (newsticker--buffer-set-faces (point-min) (point-max))
|
|
3839 (newsticker--buffer-set-invisibility (point-min) (point-max))
|
|
3840 (goto-char (point-min)))
|
|
3841
|
|
3842 (defun newsticker--buffer-insert-item (item &optional feed-name-symbol)
|
|
3843 "Insert a news item in the current buffer.
|
|
3844 Insert the string PREFIX and a formatted representation of the
|
|
3845 ITEM. The optional parameter FEED-NAME-SYMBOL determines how the
|
|
3846 item is formatted and whether the item-retrieval time is added as
|
|
3847 well."
|
|
3848 ;; insert headline
|
|
3849 (if (eq (newsticker--age item) 'feed)
|
|
3850 (newsticker--buffer-do-insert-text item 'feed feed-name-symbol)
|
|
3851 (newsticker--buffer-do-insert-text item 'item feed-name-symbol))
|
|
3852 ;; insert the description
|
|
3853 (newsticker--buffer-do-insert-text item 'desc feed-name-symbol))
|
|
3854
|
|
3855 (defun newsticker--buffer-do-insert-text (item type feed-name-symbol)
|
|
3856 "Actually insert contents of news item, format it, render it and all that.
|
|
3857 ITEM is a news item, TYPE tells which part of the item shall be inserted,
|
|
3858 FEED-NAME-SYMBOL tells to which feed this item belongs."
|
|
3859 (let* ((pos (point))
|
|
3860 (format newsticker-desc-format)
|
|
3861 (pos-date-start nil)
|
|
3862 (pos-date-end nil)
|
|
3863 (pos-stat-start nil)
|
|
3864 (pos-stat-end nil)
|
|
3865 (pos-text-start nil)
|
|
3866 (pos-text-end nil)
|
|
3867 (pos-extra-start nil)
|
|
3868 (pos-extra-end nil)
|
|
3869 (pos-enclosure-start nil)
|
|
3870 (pos-enclosure-end nil)
|
|
3871 (age (newsticker--age item))
|
|
3872 (preformatted-contents (newsticker--preformatted-contents item))
|
|
3873 (preformatted-title (newsticker--preformatted-title item)))
|
|
3874 (cond ((and preformatted-contents
|
|
3875 (not (eq (aref preformatted-contents 0) ?\n));; we must
|
|
3876 ;; NOT have a line
|
|
3877 ;; break!
|
|
3878 (eq type 'desc))
|
|
3879 (insert preformatted-contents))
|
|
3880 ((and preformatted-title
|
|
3881 (not (eq (aref preformatted-title 0) ?\n));; we must NOT have a
|
|
3882 ;; line break!
|
|
3883 (eq type 'item))
|
|
3884 (insert preformatted-title))
|
|
3885 (t
|
|
3886 ;; item was not formatted before.
|
|
3887 ;; Let's go.
|
|
3888 (if (eq type 'item)
|
|
3889 (setq format newsticker-item-format)
|
|
3890 (if (eq type 'feed)
|
|
3891 (setq format newsticker-heading-format)))
|
|
3892
|
|
3893 (while (> (length format) 0)
|
|
3894 (let ((prefix (if (> (length format) 1)
|
|
3895 (substring format 0 2)
|
|
3896 "")))
|
|
3897 (cond ((string= "%c" prefix)
|
|
3898 ;; contents
|
|
3899 (when (newsticker--desc item)
|
|
3900 (setq pos-text-start (point-marker))
|
|
3901 (insert (newsticker--desc item))
|
|
3902 (setq pos-text-end (point-marker)))
|
|
3903 (setq format (substring format 2)))
|
|
3904 ((string= "%d" prefix)
|
|
3905 ;; date
|
|
3906 (setq pos-date-start (point-marker))
|
|
3907 (if (newsticker--time item)
|
|
3908 (insert (format-time-string newsticker-date-format
|
|
3909 (newsticker--time item))))
|
|
3910 (setq pos-date-end (point-marker))
|
|
3911 (setq format (substring format 2)))
|
|
3912 ((string= "%l" prefix)
|
|
3913 ;; logo
|
|
3914 (let ((disabled (cond ((eq (newsticker--age item) 'feed)
|
|
3915 (= (newsticker--stat-num-items
|
|
3916 feed-name-symbol 'new) 0))
|
|
3917 (t
|
|
3918 (not (eq (newsticker--age item)
|
|
3919 'new))))))
|
|
3920 (let ((img (newsticker--image-read feed-name-symbol
|
|
3921 disabled)))
|
|
3922 (when img
|
|
3923 (newsticker--insert-image img (car item)))))
|
|
3924 (setq format (substring format 2)))
|
|
3925 ((string= "%L" prefix)
|
|
3926 ;; logo or title
|
|
3927 (let ((disabled (cond ((eq (newsticker--age item) 'feed)
|
|
3928 (= (newsticker--stat-num-items
|
|
3929 feed-name-symbol 'new) 0))
|
|
3930 (t
|
|
3931 (not (eq (newsticker--age item)
|
|
3932 'new))))))
|
|
3933 (let ((img (newsticker--image-read feed-name-symbol
|
|
3934 disabled)))
|
|
3935 (if img
|
|
3936 (newsticker--insert-image img (car item))
|
|
3937 (when (car item)
|
|
3938 (setq pos-text-start (point-marker))
|
|
3939 (if (eq (newsticker--age item) 'feed)
|
|
3940 (insert (newsticker--title item))
|
|
3941 ;; FIXME: This is not the "real" title!
|
|
3942 (insert (format "%s"
|
|
3943 (car (newsticker--cache-get-feed
|
|
3944 feed-name-symbol)))))
|
|
3945 (setq pos-text-end (point-marker))))))
|
|
3946 (setq format (substring format 2)))
|
|
3947 ((string= "%s" prefix)
|
|
3948 ;; statistics
|
|
3949 (setq pos-stat-start (point-marker))
|
|
3950 (if (eq (newsticker--age item) 'feed)
|
|
3951 (insert (newsticker--buffer-statistics
|
|
3952 feed-name-symbol)))
|
|
3953 (setq pos-stat-end (point-marker))
|
|
3954 (setq format (substring format 2)))
|
|
3955 ((string= "%t" prefix)
|
|
3956 ;; title
|
|
3957 (when (car item)
|
|
3958 (setq pos-text-start (point-marker))
|
|
3959 (insert (car item))
|
|
3960 (setq pos-text-end (point-marker)))
|
|
3961 (setq format (substring format 2)))
|
|
3962 ((string-match "%." prefix)
|
|
3963 ;; unknown specifier!
|
|
3964 (insert prefix)
|
|
3965 (setq format (substring format 2)))
|
|
3966 ((string-match "^\\([^%]*\\)\\(.*\\)" format) ;; FIXME!
|
|
3967 ;; everything else
|
|
3968 (let ((p (point)))
|
|
3969 (insert (substring format
|
|
3970 (match-beginning 1) (match-end 1)))
|
|
3971 ;; in case that the format string contained newlines
|
|
3972 (put-text-property p (point) 'hard t))
|
|
3973 (setq format (substring format (match-beginning 2)))))))
|
|
3974
|
|
3975 ;; decode HTML if possible...
|
|
3976 (let ((is-rendered-HTML nil))
|
|
3977 (when (and newsticker-html-renderer pos-text-start pos-text-end)
|
|
3978 (condition-case error-data
|
|
3979 (save-excursion
|
|
3980 ;; check whether it is necessary to call html renderer
|
|
3981 ;; (regexp inspired by htmlr.el)
|
|
3982 (goto-char pos-text-start)
|
|
3983 (when (re-search-forward
|
|
3984 "</?[A-Za-z1-6]*\\|&#?[A-Za-z0-9]+;" pos-text-end t)
|
|
3985 ;; (message "%s" (newsticker--title item))
|
|
3986 (let ((w3m-fill-column (if newsticker-use-full-width
|
|
3987 -1 fill-column))
|
|
3988 (w3-maximum-line-length
|
|
3989 (if newsticker-use-full-width nil fill-column)))
|
|
3990 (save-excursion
|
|
3991 (funcall newsticker-html-renderer pos-text-start
|
|
3992 pos-text-end)))
|
|
3993 ;; FIXME: compiler warning about free variable
|
|
3994 ;; w3m-minor-mode-map
|
|
3995 (cond ((eq newsticker-html-renderer 'w3m-region)
|
|
3996 (add-text-properties pos (point-max)
|
|
3997 (list 'keymap
|
|
3998 w3m-minor-mode-map)))
|
|
3999 ((eq newsticker-html-renderer 'w3-region)
|
|
4000 (add-text-properties pos (point-max)
|
|
4001 (list 'keymap w3-mode-map))))
|
|
4002 (setq is-rendered-HTML t)))
|
|
4003 (error
|
|
4004 (message "Error: HTML rendering failed: %s, %s"
|
|
4005 (car error-data) (cdr error-data)))))
|
|
4006 (when (and newsticker-justification
|
|
4007 (eq type 'desc)
|
|
4008 (not is-rendered-HTML))
|
|
4009 (condition-case nil
|
|
4010 (let ((use-hard-newlines t))
|
|
4011 (fill-region pos (point-max) newsticker-justification))
|
|
4012 (error nil))))
|
|
4013
|
|
4014 ;; remove leading and trailing newlines
|
|
4015 (goto-char pos)
|
|
4016 (unless (= 0 (skip-chars-forward " \t\r\n"))
|
|
4017 (delete-region pos (point)))
|
|
4018 (goto-char (point-max))
|
|
4019 (let ((end (point)))
|
|
4020 (unless (= 0 (skip-chars-backward " \t\r\n" (1+ pos)))
|
|
4021 (delete-region (point) end)))
|
|
4022 (goto-char (point-max))
|
|
4023
|
|
4024 ;; closing newline
|
|
4025 (unless nil ;;(eq pos (point))
|
|
4026 (insert "\n")
|
|
4027 (put-text-property (1- (point)) (point) 'hard t))
|
|
4028
|
|
4029 ;; insert enclosure element
|
|
4030 (when (eq type 'desc)
|
|
4031 (setq pos-enclosure-start (point))
|
|
4032 (newsticker--buffer-insert-enclosure item)
|
|
4033 (setq pos-enclosure-end (point)))
|
|
4034
|
|
4035 ;; show extra elements
|
|
4036 (when (eq type 'desc)
|
|
4037 (goto-char (point-max))
|
|
4038 (setq pos-extra-start (point))
|
|
4039 (mapc (lambda (extra-element)
|
|
4040 (unless (memq (car extra-element)
|
|
4041 '(items link title description
|
|
4042 content:encoded
|
|
4043 dc:subject dc:date item guid
|
|
4044 pubDate
|
|
4045 enclosure))
|
|
4046 (newsticker--buffer-print-extra-element
|
|
4047 extra-element)))
|
|
4048 (newsticker--extra item))
|
|
4049 (setq pos-extra-end (point)))
|
|
4050
|
|
4051 ;; text properties
|
|
4052 (when (memq type '(feed item))
|
|
4053 (add-text-properties pos (1- (point))
|
|
4054 (list 'mouse-face 'highlight
|
|
4055 'nt-link (newsticker--link item)
|
|
4056 'help-echo
|
|
4057 (format "mouse-2: visit item (%s)"
|
|
4058 (newsticker--link item))
|
|
4059 'keymap newsticker--url-keymap))
|
|
4060 (add-text-properties pos (point)
|
|
4061 (list 'nt-title (newsticker--title item)
|
|
4062 'nt-desc (newsticker--desc item))))
|
|
4063
|
|
4064 (add-text-properties pos (point)
|
|
4065 (list 'nt-type type
|
|
4066 'nt-face type
|
|
4067 'nt-age age
|
|
4068 'nt-guid (newsticker--guid item)))
|
|
4069 (when (and pos-date-start pos-date-end)
|
|
4070 (put-text-property pos-date-start pos-date-end 'nt-face 'date))
|
|
4071 (when (and pos-stat-start pos-stat-end)
|
|
4072 (put-text-property pos-stat-start pos-stat-end 'nt-face 'stat))
|
|
4073 (when (and pos-extra-start pos-extra-end)
|
|
4074 (put-text-property pos-extra-start pos-extra-end
|
|
4075 'nt-face 'extra)
|
|
4076 (put-text-property pos-extra-start pos-extra-end
|
|
4077 'nt-type 'extra))
|
|
4078 (when (and pos-enclosure-start pos-enclosure-end
|
|
4079 (> pos-enclosure-end pos-enclosure-start))
|
|
4080 (put-text-property pos-enclosure-start (1- pos-enclosure-end)
|
|
4081 'nt-face 'enclosure))
|
|
4082
|
|
4083 ;; left margin
|
|
4084 ;;(unless (memq type '(feed item))
|
|
4085 ;;(set-left-margin pos (1- (point)) 1))
|
|
4086
|
|
4087 ;; save rendered stuff
|
|
4088 (cond ((eq type 'desc)
|
|
4089 ;; preformatted contents
|
|
4090 (newsticker--cache-set-preformatted-contents
|
|
4091 item (buffer-substring pos (point))))
|
|
4092 ((eq type 'item)
|
|
4093 ;; preformatted title
|
|
4094 (newsticker--cache-set-preformatted-title
|
|
4095 item (buffer-substring pos (point)))))))))
|
|
4096
|
|
4097 (defun newsticker--buffer-print-extra-element (extra-element)
|
|
4098 "Insert EXTRA-ELEMENT in a pretty form into the current buffer."
|
|
4099 (insert (format "%s:\t" (car extra-element)))
|
|
4100 (let (;;(attributes (cadr extra-element)) ;FIXME!!!!
|
|
4101 (contents (cddr extra-element)))
|
|
4102 (cond ((listp contents)
|
|
4103 (mapc (lambda (i)
|
|
4104 (if (and (stringp i)
|
|
4105 (string-match "^http://.*" i))
|
|
4106 (let ((pos (point)))
|
|
4107 (insert i " ") ; avoid self-reference from the
|
|
4108 ; nt-link thing
|
|
4109 (add-text-properties
|
|
4110 pos (point)
|
|
4111 (list 'mouse-face 'highlight
|
|
4112 'nt-link i
|
|
4113 'help-echo
|
|
4114 (format "mouse-2: visit (%s)" i)
|
|
4115 'keymap newsticker--url-keymap)))
|
|
4116 (insert (format "%s" i))))
|
|
4117 contents))
|
|
4118 (t
|
|
4119 (insert (format "%s" contents))))
|
|
4120 (insert "\n")))
|
|
4121
|
|
4122 (defun newsticker--buffer-insert-enclosure (item)
|
|
4123 "Insert enclosure element of an RSS ITEM into the current buffer."
|
|
4124 (let ((enclosure (newsticker--enclosure item))
|
|
4125 (beg (point)))
|
|
4126 (when enclosure
|
|
4127 (let ((url (cdr (assoc 'url enclosure)))
|
|
4128 (length (cdr (assoc 'length enclosure)))
|
|
4129 (type (cdr (assoc 'type enclosure))))
|
|
4130 (insert (format "Enclosed file (%s, %1.2f kBytes)" type
|
|
4131 (/ (string-to-number length) 1024)))
|
|
4132 (add-text-properties beg (point)
|
|
4133 (list 'mouse-face 'highlight
|
|
4134 'nt-link url
|
|
4135 'help-echo (format
|
|
4136 "mouse-2: visit (%s)" url)
|
|
4137 'keymap newsticker--url-keymap
|
|
4138 'nt-face 'enclosure
|
|
4139 'nt-type 'desc))
|
|
4140 (insert "\n")))))
|
|
4141
|
|
4142 (defun newsticker--buffer-statistics (feed-name-symbol)
|
|
4143 "Return a statistic string for the feed given by FEED-NAME-SYMBOL.
|
|
4144 See `newsticker-statistics-format'."
|
|
4145 (let ((case-fold-search nil))
|
|
4146 (replace-regexp-in-string
|
|
4147 "%a"
|
|
4148 (format "%d" (newsticker--stat-num-items feed-name-symbol))
|
|
4149 (replace-regexp-in-string
|
|
4150 "%i"
|
|
4151 (format "%d" (newsticker--stat-num-items feed-name-symbol 'immortal))
|
|
4152 (replace-regexp-in-string
|
|
4153 "%n"
|
|
4154 (format "%d" (newsticker--stat-num-items feed-name-symbol 'new))
|
|
4155 (replace-regexp-in-string
|
|
4156 "%o"
|
|
4157 (format "%d" (newsticker--stat-num-items feed-name-symbol 'old))
|
|
4158 (replace-regexp-in-string
|
|
4159 "%O"
|
|
4160 (format "%d" (newsticker--stat-num-items feed-name-symbol 'obsolete))
|
|
4161 newsticker-statistics-format)))))))
|
|
4162
|
|
4163 (defun newsticker--buffer-set-faces (start end)
|
|
4164 "Add face properties according to mark property.
|
|
4165 Scans the buffer between START and END."
|
|
4166 (save-excursion
|
|
4167 ;;(put-text-property start end 'face 'newsticker-default-face)
|
|
4168 (goto-char start)
|
|
4169 (let ((pos1 start)
|
|
4170 (pos2 1)
|
|
4171 (nt-face (get-text-property start 'nt-face))
|
|
4172 (nt-age (get-text-property start 'nt-age)))
|
|
4173 (when nt-face
|
|
4174 (setq pos2 (next-single-property-change (point) 'nt-face))
|
|
4175 (newsticker--set-face-properties pos1 pos2 nt-face nt-age)
|
|
4176 (setq nt-face (get-text-property pos2 'nt-face))
|
|
4177 (setq pos1 pos2))
|
|
4178 (while (and (setq pos2 (next-single-property-change pos1 'nt-face))
|
|
4179 (<= pos2 end)
|
|
4180 (> pos2 pos1))
|
|
4181 (newsticker--set-face-properties pos1 pos2 nt-face nt-age)
|
|
4182 (setq nt-face (get-text-property pos2 'nt-face))
|
|
4183 (setq nt-age (get-text-property pos2 'nt-age))
|
|
4184 (setq pos1 pos2)))))
|
|
4185
|
|
4186 (defun newsticker--buffer-set-invisibility (start end)
|
|
4187 "Add invisibility properties according to nt-type property.
|
|
4188 Scans the buffer between START and END. Sets the 'invisible
|
|
4189 property to '(<nt-type>-<nt-age> <nt-type> <nt-age>)."
|
|
4190 (save-excursion
|
|
4191 ;; reset invisibility settings
|
|
4192 (put-text-property start end 'invisible nil)
|
|
4193 ;; let's go
|
|
4194 (goto-char start)
|
|
4195 (let ((pos1 start)
|
|
4196 (pos2 1)
|
|
4197 (nt-type (get-text-property start 'nt-type))
|
|
4198 (nt-age (get-text-property start 'nt-age)))
|
|
4199 (when nt-type
|
|
4200 (setq pos2 (next-single-property-change (point) 'nt-type))
|
|
4201 (put-text-property (max (point-min) pos1) (1- pos2)
|
|
4202 'invisible
|
|
4203 (list (intern
|
|
4204 (concat
|
|
4205 (symbol-name
|
|
4206 (if (eq nt-type 'extra) 'desc nt-type))
|
|
4207 "-"
|
|
4208 (symbol-name nt-age)))
|
|
4209 nt-type
|
|
4210 nt-age))
|
|
4211 (setq nt-type (get-text-property pos2 'nt-type))
|
|
4212 (setq pos1 pos2))
|
|
4213 (while (and (setq pos2 (next-single-property-change pos1 'nt-type))
|
|
4214 (<= pos2 end)
|
|
4215 (> pos2 pos1))
|
|
4216 ;; must shift one char to the left in order to handle inivisible
|
|
4217 ;; newlines, motion in invisible text areas and all that correctly
|
|
4218 (put-text-property (1- pos1) (1- pos2)
|
|
4219 'invisible
|
|
4220 (list (intern
|
|
4221 (concat
|
|
4222 (symbol-name
|
|
4223 (if (eq nt-type 'extra) 'desc nt-type))
|
|
4224 "-"
|
|
4225 (symbol-name nt-age)))
|
|
4226 nt-type
|
|
4227 nt-age))
|
|
4228 (setq nt-type (get-text-property pos2 'nt-type))
|
|
4229 (setq nt-age (get-text-property pos2 'nt-age))
|
|
4230 (setq pos1 pos2)))))
|
|
4231
|
|
4232 (defun newsticker--set-face-properties (pos1 pos2 nt-face age)
|
|
4233 "Set the face for the text between the positions POS1 and POS2.
|
|
4234 The face is chosen according the values of NT-FACE and AGE."
|
|
4235 (let ((face (cond ((eq nt-face 'feed)
|
|
4236 'newsticker-feed-face)
|
|
4237 ((eq nt-face 'item)
|
|
4238 (cond ((eq age 'new)
|
|
4239 'newsticker-new-item-face)
|
|
4240 ((eq age 'old)
|
|
4241 'newsticker-old-item-face)
|
|
4242 ((eq age 'immortal)
|
|
4243 'newsticker-immortal-item-face)
|
|
4244 ((eq age 'obsolete)
|
|
4245 'newsticker-obsolete-item-face)))
|
|
4246 ((eq nt-face 'date)
|
|
4247 'newsticker-date-face)
|
|
4248 ((eq nt-face 'stat)
|
|
4249 'newsticker-statistics-face)
|
|
4250 ((eq nt-face 'extra)
|
|
4251 'newsticker-extra-face)
|
|
4252 ((eq nt-face 'enclosure)
|
|
4253 'newsticker-enclosure-face))))
|
|
4254 (when face
|
|
4255 (put-text-property pos1 (max pos1 pos2) 'face face))))
|
|
4256
|
|
4257 (defun newsticker--insert-image (img string)
|
|
4258 "Insert IMG with STRING at point.
|
|
4259 This is a work-around for a strange behavior of Emacs versions before
|
|
4260 21.3.50. Images inserted with `insert-image' vanished after doing
|
|
4261 `fill-region'."
|
|
4262 ;; This should work:
|
|
4263 ;;(insert-image img string)
|
|
4264 ;; but it does not. Therefore we do this, which should be equivalent!
|
|
4265 (let ((pos (point)))
|
|
4266 ;;(insert string)
|
|
4267 (insert ":-)")
|
|
4268 (add-text-properties pos (point) (list 'display img))))
|
|
4269
|
|
4270 ;; ======================================================================
|
|
4271 ;;; HTML rendering
|
|
4272 ;; ======================================================================
|
|
4273 (defun newsticker-htmlr-render (pos1 pos2) ;
|
|
4274 "Replacement for `htmlr-render'.
|
|
4275 Renders the HTML code in the region POS1 to POS2 using htmlr."
|
|
4276 (let ((str (buffer-substring-no-properties pos1 pos2)))
|
|
4277 (delete-region pos1 pos2)
|
|
4278 (insert
|
|
4279 (with-temp-buffer
|
|
4280 (insert str)
|
|
4281 (goto-char (point-min))
|
|
4282 ;; begin original htmlr-render
|
|
4283 (htmlr-reset)
|
|
4284 ;; something omitted here...
|
|
4285 (while (< (point) (point-max))
|
|
4286 (htmlr-step))
|
|
4287 ;; end original htmlr-render
|
|
4288 (newsticker--remove-whitespace (buffer-string))))))
|
|
4289
|
|
4290 ;; ======================================================================
|
|
4291 ;;; Functions working on the *newsticker* buffer
|
|
4292 ;; ======================================================================
|
|
4293 (defun newsticker--buffer-make-item-completely-visible ()
|
|
4294 "Scroll buffer until current item is completely visible."
|
|
4295 (switch-to-buffer (get-buffer-create "*newsticker*"))
|
|
4296 (when newsticker--auto-narrow-to-feed
|
|
4297 (let* ((min (or (save-excursion (newsticker--buffer-beginning-of-feed))
|
|
4298 (point-min)))
|
|
4299 (max (or (save-excursion (newsticker--buffer-end-of-feed))
|
|
4300 (point-max))))
|
|
4301 (narrow-to-region min max)))
|
|
4302 (when newsticker--auto-narrow-to-item
|
|
4303 (let* ((min (or (save-excursion (newsticker--buffer-beginning-of-item))
|
|
4304 (point-min)))
|
|
4305 (max (or (save-excursion (newsticker--buffer-end-of-item))
|
|
4306 (point-max))))
|
|
4307 (narrow-to-region min max)))
|
|
4308 (sit-for 0)
|
|
4309 ;; do not count lines and stuff because that does not work when images
|
|
4310 ;; are displayed. Do it the simple way:
|
|
4311 (save-excursion
|
|
4312 (newsticker--buffer-end-of-item)
|
|
4313 (unless (pos-visible-in-window-p)
|
|
4314 (recenter -1)))
|
|
4315 (unless (pos-visible-in-window-p)
|
|
4316 (recenter 0)))
|
|
4317
|
|
4318 (defun newsticker--buffer-get-feed-title-at-point ()
|
|
4319 "Return feed symbol of headline at point."
|
|
4320 (format "%s" (or (get-text-property (point) 'feed) " ")))
|
|
4321
|
|
4322 (defun newsticker--buffer-get-item-title-at-point ()
|
|
4323 "Return feed symbol of headline at point."
|
|
4324 (format "%s" (or (get-text-property (point) 'nt-title) " ")))
|
|
4325
|
|
4326 (defun newsticker--buffer-goto (types &optional age backwards)
|
|
4327 "Search next occurrence of TYPES in current buffer.
|
|
4328 TYPES is a list of symbols. If TYPES is found point is moved, if
|
|
4329 not point is left unchanged. If optional parameter AGE is not
|
|
4330 nil, the type AND the age must match. If BACKWARDS is t, search
|
|
4331 backwards."
|
|
4332 (let ((pos (save-excursion
|
|
4333 (save-restriction
|
|
4334 (widen)
|
|
4335 (catch 'found
|
|
4336 (let ((tpos (point)))
|
|
4337 (while (setq tpos
|
|
4338 (if backwards
|
|
4339 (if (eq tpos (point-min))
|
|
4340 nil
|
|
4341 (or (previous-single-property-change
|
|
4342 tpos 'nt-type)
|
|
4343 (point-min)))
|
|
4344 (next-single-property-change
|
|
4345 tpos 'nt-type)))
|
|
4346 (and (memq (get-text-property tpos 'nt-type) types)
|
|
4347 (or (not age)
|
|
4348 (eq (get-text-property tpos 'nt-age) age))
|
|
4349 (throw 'found tpos)))))))))
|
|
4350 (when pos
|
|
4351 (goto-char pos))
|
|
4352 pos))
|
|
4353
|
|
4354 (defun newsticker--buffer-hideshow (mark-age onoff )
|
|
4355 "Hide or show items of type MARK-AGE.
|
|
4356 If ONOFF is nil the item is hidden, otherwise it is shown."
|
|
4357 (if onoff
|
|
4358 (remove-from-invisibility-spec mark-age )
|
|
4359 (add-to-invisibility-spec mark-age)))
|
|
4360
|
|
4361 (defun newsticker--buffer-beginning-of-item ()
|
|
4362 "Move point to the beginning of the item at point.
|
|
4363 Return new position."
|
|
4364 (if (bobp)
|
|
4365 (point)
|
|
4366 (let ((type (get-text-property (point) 'nt-type))
|
|
4367 (typebefore (get-text-property (1- (point)) 'nt-type)))
|
|
4368 (if (and (memq type '(item feed))
|
|
4369 (not (eq type typebefore)))
|
|
4370 (point)
|
|
4371 (newsticker--buffer-goto '(item feed) nil t)
|
|
4372 (point)))))
|
|
4373
|
|
4374 (defun newsticker--buffer-beginning-of-feed ()
|
|
4375 "Move point to the beginning of the feed at point.
|
|
4376 Return new position."
|
|
4377 (if (bobp)
|
|
4378 (point)
|
|
4379 (let ((type (get-text-property (point) 'nt-type))
|
|
4380 (typebefore (get-text-property (1- (point)) 'nt-type)))
|
|
4381 (if (and (memq type '(feed))
|
|
4382 (not (eq type typebefore)))
|
|
4383 (point)
|
|
4384 (newsticker--buffer-goto '(feed) nil t)
|
|
4385 (point)))))
|
|
4386
|
|
4387 (defun newsticker--buffer-end-of-item ()
|
|
4388 "Move point to the end of the item at point.
|
|
4389 Take care: end of item is at the end of its last line!"
|
|
4390 (when (newsticker--buffer-goto '(item feed nil))
|
|
4391 (point)))
|
|
4392
|
|
4393 (defun newsticker--buffer-end-of-feed ()
|
|
4394 "Move point to the end of the last item of the feed at point.
|
|
4395 Take care: end of item is at the end of its last line!"
|
|
4396 (when (newsticker--buffer-goto '(feed nil))
|
|
4397 (backward-char 1)
|
|
4398 (point)))
|
|
4399
|
|
4400 ;; ======================================================================
|
|
4401 ;;; manipulation of ticker text
|
|
4402 ;; ======================================================================
|
|
4403 (defun newsticker--ticker-text-setup ()
|
|
4404 "Build the ticker text which is scrolled or flashed in the echo area."
|
|
4405 ;; reset scrollable text
|
|
4406 (setq newsticker--scrollable-text "")
|
|
4407 (setq newsticker--item-list nil)
|
|
4408 (setq newsticker--item-position 0)
|
|
4409 ;; build scrollable text from cache data
|
|
4410 (let ((have-something nil))
|
|
4411 (mapc
|
|
4412 (lambda (feed)
|
|
4413 (let ((feed-name (symbol-name (car feed))))
|
|
4414 (let ((num-new (newsticker--stat-num-items (car feed) 'new))
|
|
4415 (num-old (newsticker--stat-num-items (car feed) 'old))
|
|
4416 (num-imm (newsticker--stat-num-items (car feed) 'immortal))
|
|
4417 (num-obs (newsticker--stat-num-items (car feed) 'obsolete)))
|
|
4418 (when (or (> num-new 0)
|
|
4419 (and (> num-old 0)
|
|
4420 (not newsticker-hide-old-items-in-echo-area))
|
|
4421 (and (> num-imm 0)
|
|
4422 (not newsticker-hide-immortal-items-in-echo-area))
|
|
4423 (and (> num-obs 0)
|
|
4424 (not newsticker-hide-obsolete-items-in-echo-area)))
|
|
4425 (setq have-something t)
|
|
4426 (mapc
|
|
4427 (lambda (item)
|
|
4428 (let ((title (replace-regexp-in-string
|
|
4429 "[\r\n]+" " "
|
|
4430 (newsticker--title item)))
|
|
4431 (age (newsticker--age item)))
|
|
4432 (unless (string= title newsticker--error-headline)
|
|
4433 (when
|
|
4434 (or (eq age 'new)
|
|
4435 (and (eq age 'old)
|
|
4436 (not newsticker-hide-old-items-in-echo-area))
|
|
4437 (and (eq age 'obsolete)
|
|
4438 (not
|
|
4439 newsticker-hide-obsolete-items-in-echo-area))
|
|
4440 (and (eq age 'immortal)
|
|
4441 (not
|
|
4442 newsticker-hide-immortal-items-in-echo-area)))
|
|
4443 (setq title (newsticker--remove-whitespace title))
|
|
4444 ;; add to flash list
|
|
4445 (add-to-list 'newsticker--item-list
|
|
4446 (concat feed-name ": " title) t)
|
|
4447 ;; and to the scrollable text
|
|
4448 (setq newsticker--scrollable-text
|
|
4449 (concat newsticker--scrollable-text
|
|
4450 " " feed-name ": " title " +++"))))))
|
|
4451 (cdr feed))))))
|
|
4452 newsticker--cache)
|
|
4453 (when have-something
|
|
4454 (setq newsticker--scrollable-text
|
|
4455 (concat "+++ "
|
|
4456 (format-time-string "%A, %H:%M"
|
|
4457 newsticker--latest-update-time)
|
|
4458 " ++++++" newsticker--scrollable-text)))))
|
|
4459
|
|
4460 (defun newsticker--ticker-text-remove (feed title)
|
|
4461 "Remove the item of FEED with TITLE from the ticker text."
|
|
4462 ;; reset scrollable text
|
|
4463 (setq newsticker--item-position 0)
|
|
4464 (let ((feed-name (symbol-name feed))
|
|
4465 (t-title (replace-regexp-in-string "[\r\n]+" " " title)))
|
|
4466 ;; remove from flash list
|
|
4467 (setq newsticker--item-list (remove (concat feed-name ": " t-title)
|
|
4468 newsticker--item-list))
|
|
4469 ;; and from the scrollable text
|
|
4470 (setq newsticker--scrollable-text
|
|
4471 (replace-regexp-in-string
|
|
4472 (regexp-quote (concat " " feed-name ": " t-title " +++"))
|
|
4473 ""
|
|
4474 newsticker--scrollable-text))
|
|
4475 (if (string-match (concat "^\\+\\+\\+ [A-Z][a-z]+, "
|
|
4476 "[012]?[0-9]:[0-9][0-9] \\+\\+\\+\\+\\+\\+$")
|
|
4477 newsticker--scrollable-text)
|
|
4478 (setq newsticker--scrollable-text ""))))
|
|
4479
|
|
4480 ;; ======================================================================
|
|
4481 ;;; manipulation of cached data
|
|
4482 ;; ======================================================================
|
|
4483 (defun newsticker--cache-set-preformatted-contents (item contents)
|
|
4484 "Set preformatted contents of ITEM to CONTENTS."
|
|
4485 (if (nthcdr 6 item)
|
|
4486 (setcar (nthcdr 6 item) contents)
|
|
4487 (setcdr (nthcdr 5 item) (list contents))))
|
|
4488
|
|
4489 (defun newsticker--cache-set-preformatted-title (item title)
|
|
4490 "Set preformatted title of ITEM to TITLE."
|
|
4491 (if (nthcdr 7 item)
|
|
4492 (setcar (nthcdr 7 item) title)
|
|
4493 (setcdr (nthcdr 6 item) title)))
|
|
4494
|
|
4495 (defun newsticker--cache-replace-age (data feed old-age new-age)
|
|
4496 "Mark all items in DATA in FEED which carry age OLD-AGE with NEW-AGE.
|
|
4497 If FEED is 'any it applies to all feeds. If OLD-AGE is 'any,
|
|
4498 all marks are replaced by NEW-AGE. Removes all pre-formatted contents."
|
|
4499 (mapc (lambda (a-feed)
|
|
4500 (when (or (eq feed 'any)
|
|
4501 (eq (car a-feed) feed))
|
|
4502 (let ((items (cdr a-feed)))
|
|
4503 (mapc (lambda (item)
|
|
4504 (when (or (eq old-age 'any)
|
|
4505 (eq (newsticker--age item) old-age))
|
|
4506 (setcar (nthcdr 4 item) new-age)
|
|
4507 (newsticker--do-forget-preformatted item)))
|
|
4508 items))))
|
|
4509 data)
|
|
4510 data)
|
|
4511
|
|
4512 (defun newsticker--cache-mark-expired (data feed old-age new-age time)
|
|
4513 "Mark all expired entries.
|
|
4514 This function sets the age entries in DATA in the feed FEED. If
|
|
4515 an item's age is OLD-AGE it is set to NEW-AGE if the item is
|
|
4516 older than TIME."
|
|
4517 (mapc
|
|
4518 (lambda (a-feed)
|
|
4519 (when (or (eq feed 'any)
|
|
4520 (eq (car a-feed) feed))
|
|
4521 (let ((items (cdr a-feed)))
|
|
4522 (mapc
|
|
4523 (lambda (item)
|
|
4524 (when (eq (newsticker--age item) old-age)
|
|
4525 (let ((exp-time (time-add (newsticker--time item)
|
|
4526 (seconds-to-time time))))
|
|
4527 (when (time-less-p exp-time (current-time))
|
|
4528 (newsticker--debug-msg
|
|
4529 "Item `%s' from %s has expired on %s"
|
|
4530 (newsticker--title item)
|
|
4531 (format-time-string "%Y-%02m-%d, %H:%M"
|
|
4532 (newsticker--time item))
|
|
4533 (format-time-string "%Y-%02m-%d, %H:%M" exp-time))
|
|
4534 (setcar (nthcdr 4 item) new-age)))))
|
|
4535 items))))
|
|
4536 data)
|
|
4537 data)
|
|
4538
|
|
4539 (defun newsticker--cache-contains (data feed title desc link age
|
|
4540 &optional guid)
|
|
4541 "Check DATA whether FEED contains an item with the given properties.
|
|
4542 This function returns the contained item or nil if it is not
|
|
4543 contained.
|
|
4544 The properties which are checked are TITLE, DESC, LINK, AGE, and
|
|
4545 GUID. In general all properties must match in order to return a
|
|
4546 certain item, except for the following cases.
|
|
4547
|
|
4548 If AGE equals 'feed the TITLE, DESCription and LINK do not
|
|
4549 matter. If DESC is nil it is ignored as well. If
|
|
4550 `newsticker-desc-comp-max' is non-nil, only the first
|
|
4551 `newsticker-desc-comp-max' characters of DESC are taken into
|
|
4552 account.
|
|
4553
|
|
4554 If GUID is non-nil it is sufficient to match this value, and the
|
|
4555 other properties are ignored."
|
|
4556 (condition-case nil
|
|
4557 (catch 'found
|
|
4558 (when (and desc newsticker-desc-comp-max
|
|
4559 (> (length desc) newsticker-desc-comp-max))
|
|
4560 (setq desc (substring desc 0 newsticker-desc-comp-max)))
|
|
4561 (mapc
|
|
4562 (lambda (this-feed)
|
|
4563 (when (eq (car this-feed) feed)
|
|
4564 (mapc (lambda (anitem)
|
|
4565 (when (or
|
|
4566 ;; global unique id can match
|
|
4567 (and guid
|
|
4568 (string= guid (newsticker--guid anitem)))
|
|
4569 ;; or title, desc, etc.
|
|
4570 (and
|
|
4571 ;;(or (not (eq age 'feed))
|
|
4572 ;; (eq (newsticker--age anitem) 'feed))
|
|
4573 (string= (newsticker--title anitem)
|
|
4574 title)
|
|
4575 (or (not link)
|
|
4576 (string= (newsticker--link anitem)
|
|
4577 link))
|
|
4578 (or (not desc)
|
|
4579 (if (and desc newsticker-desc-comp-max
|
|
4580 (> (length (newsticker--desc anitem))
|
|
4581 newsticker-desc-comp-max))
|
|
4582 (string= (substring
|
|
4583 (newsticker--desc anitem)
|
|
4584 0 newsticker-desc-comp-max)
|
|
4585 desc)
|
|
4586 (string= (newsticker--desc anitem)
|
|
4587 desc)))))
|
|
4588 (throw 'found anitem)))
|
|
4589 (cdr this-feed))))
|
|
4590 data)
|
|
4591 nil)
|
|
4592 (error nil)))
|
|
4593
|
|
4594 (defun newsticker--cache-add (data feed-name-symbol title desc link time age
|
|
4595 position extra-elements
|
|
4596 &optional updated-age updated-time
|
|
4597 preformatted-contents
|
|
4598 preformatted-title)
|
|
4599 "Add another item to cache data.
|
|
4600 Add to DATA in the FEED-NAME-SYMBOL an item with TITLE, DESC,
|
|
4601 LINK, TIME, AGE, POSITION, and EXTRA-ELEMENTS. If this item is
|
|
4602 contained already, its mark is set to UPDATED-AGE, its time is
|
|
4603 set to UPDATED-TIME, and its pre-formatted contents is set to
|
|
4604 PREFORMATTED-CONTENTS and PREFORMATTED-TITLE. Returns the age
|
|
4605 which the item got."
|
|
4606 (let ((item (newsticker--cache-contains data feed-name-symbol title
|
|
4607 desc link age)))
|
|
4608 (if item
|
|
4609 ;; does exist already -- change age, update time and position
|
|
4610 (progn
|
|
4611 (if (nthcdr 5 item)
|
|
4612 (setcar (nthcdr 5 item) position)
|
|
4613 (setcdr (nthcdr 4 item) (list position)))
|
|
4614 (setcar (nthcdr 4 item) updated-age)
|
|
4615 (if updated-time
|
|
4616 (setcar (nthcdr 3 item) updated-time))
|
|
4617 ;; replace cached pre-formatted contents
|
|
4618 (newsticker--cache-set-preformatted-contents
|
|
4619 item preformatted-contents)
|
|
4620 (newsticker--cache-set-preformatted-title
|
|
4621 item preformatted-title))
|
|
4622 ;; did not exist or age equals 'feed-name-symbol
|
|
4623 (catch 'found
|
|
4624 (mapc (lambda (this-feed)
|
|
4625 (when (eq (car this-feed) feed-name-symbol)
|
|
4626 (setcdr this-feed (nconc (cdr this-feed)
|
|
4627 (list (list title desc link
|
|
4628 time age position
|
|
4629 preformatted-contents
|
|
4630 preformatted-title
|
|
4631 extra-elements))))
|
|
4632 (throw 'found this-feed)))
|
|
4633 data)
|
|
4634 ;; the feed is not contained
|
|
4635 (add-to-list 'data (list feed-name-symbol
|
|
4636 (list title desc link time age position
|
|
4637 preformatted-contents
|
|
4638 preformatted-title
|
|
4639 extra-elements))
|
|
4640 t))))
|
|
4641 data)
|
|
4642
|
|
4643 (defun newsticker--cache-remove (data feed-symbol age)
|
|
4644 "Remove all entries from DATA in the feed FEED-SYMBOL with AGE.
|
|
4645 FEED-SYMBOL may be 'any. Entries from old feeds, which are no longer in
|
|
4646 `newsticker-url-list' or `newsticker-url-list-defaults', are removed as
|
|
4647 well."
|
|
4648 (let* ((pos data)
|
|
4649 (feed (car pos))
|
|
4650 (last-pos nil))
|
|
4651 (while feed
|
|
4652 (if (or (assoc (symbol-name (car feed)) newsticker-url-list)
|
|
4653 (assoc (symbol-name (car feed)) newsticker-url-list-defaults))
|
|
4654 ;; feed is still valid=active
|
|
4655 ;; (message "Keeping feed %s" (car feed))
|
|
4656 (if (or (eq feed-symbol 'any)
|
|
4657 (eq feed-symbol (car feed)))
|
|
4658 (let* ((item-pos (cdr feed))
|
|
4659 (item (car item-pos))
|
|
4660 (prev-pos nil))
|
|
4661 (while item
|
|
4662 ;;(message "%s" (car item))
|
|
4663 (if (eq age (newsticker--age item))
|
|
4664 ;; remove this item
|
|
4665 (progn
|
|
4666 ;;(message "Removing item %s" (car item))
|
|
4667 (if prev-pos
|
|
4668 (setcdr prev-pos (cdr item-pos))
|
|
4669 (setcdr feed (cdr item-pos))))
|
|
4670 ;;(message "Keeping item %s" (car item))
|
|
4671 (setq prev-pos item-pos))
|
|
4672 (setq item-pos (cdr item-pos))
|
|
4673 (setq item (car item-pos)))))
|
|
4674 ;; feed is not active anymore
|
|
4675 ;; (message "Removing feed %s" (car feed))
|
|
4676 (if last-pos
|
|
4677 (setcdr last-pos (cdr pos))
|
|
4678 (setq data (cdr pos))))
|
|
4679 (setq last-pos pos)
|
|
4680 (setq pos (cdr pos))
|
|
4681 (setq feed (car pos)))))
|
|
4682
|
|
4683 ;; ======================================================================
|
|
4684 ;;; Sorting
|
|
4685 ;; ======================================================================
|
|
4686 (defun newsticker--cache-item-compare-by-time (item1 item2)
|
|
4687 "Compare two news items ITEM1 and ITEM2 by comparing their time values."
|
|
4688 (catch 'result
|
|
4689 (let ((age1 (newsticker--age item1))
|
|
4690 (age2 (newsticker--age item2)))
|
|
4691 (if (not (eq age1 age2))
|
|
4692 (cond ((eq age1 'obsolete)
|
|
4693 (throw 'result nil))
|
|
4694 ((eq age2 'obsolete)
|
|
4695 (throw 'result t)))))
|
|
4696 (let* ((time1 (newsticker--time item1))
|
|
4697 (time2 (newsticker--time item2)))
|
|
4698 (cond ((< (nth 0 time1) (nth 0 time2))
|
|
4699 nil)
|
|
4700 ((> (nth 0 time1) (nth 0 time2))
|
|
4701 t)
|
|
4702 ((< (nth 1 time1) (nth 1 time2))
|
|
4703 nil)
|
|
4704 ((> (nth 1 time1) (nth 1 time2))
|
|
4705 t)
|
|
4706 ((< (or (nth 2 time1) 0) (or (nth 2 time2) 0))
|
|
4707 nil)
|
|
4708 ((> (or (nth 2 time1) 0) (or (nth 2 time2) 0))
|
|
4709 t)
|
|
4710 (t
|
|
4711 nil)))))
|
|
4712
|
|
4713 (defun newsticker--cache-item-compare-by-title (item1 item2)
|
|
4714 "Compare ITEM1 and ITEM2 by comparing their titles."
|
|
4715 (catch 'result
|
|
4716 (let ((age1 (newsticker--age item1))
|
|
4717 (age2 (newsticker--age item2)))
|
|
4718 (if (not (eq age1 age2))
|
|
4719 (cond ((eq age1 'obsolete)
|
|
4720 (throw 'result nil))
|
|
4721 ((eq age2 'obsolete)
|
|
4722 (throw 'result t)))))
|
|
4723 (string< (newsticker--title item1) (newsticker--title item2))))
|
|
4724
|
|
4725 (defun newsticker--cache-item-compare-by-position (item1 item2)
|
|
4726 "Compare ITEM1 and ITEM2 by comparing their original positions."
|
|
4727 (catch 'result
|
|
4728 (let ((age1 (newsticker--age item1))
|
|
4729 (age2 (newsticker--age item2)))
|
|
4730 (if (not (eq age1 age2))
|
|
4731 (cond ((eq age1 'obsolete)
|
|
4732 (throw 'result nil))
|
|
4733 ((eq age2 'obsolete)
|
|
4734 (throw 'result t)))))
|
|
4735 (< (or (newsticker--pos item1) 0) (or (newsticker--pos item2) 0))))
|
|
4736
|
|
4737 (defun newsticker--cache-sort ()
|
|
4738 "Sort the newsticker cache data."
|
|
4739 (let ((sort-fun (cond ((eq newsticker-sort-method 'sort-by-time)
|
|
4740 'newsticker--cache-item-compare-by-time)
|
|
4741 ((eq newsticker-sort-method 'sort-by-title)
|
|
4742 'newsticker--cache-item-compare-by-title)
|
|
4743 ((eq newsticker-sort-method 'sort-by-original-order)
|
|
4744 'newsticker--cache-item-compare-by-position))))
|
|
4745 (mapc (lambda (feed-list)
|
|
4746 (setcdr feed-list (sort (cdr feed-list)
|
|
4747 sort-fun)))
|
|
4748 newsticker--cache)))
|
|
4749
|
|
4750 (defun newsticker--cache-update (&optional save)
|
|
4751 "Update newsticker cache file.
|
|
4752 If optional argument SAVE is not nil the cache file is saved to disk."
|
|
4753 (save-excursion
|
|
4754 (let ((coding-system-for-write 'utf-8)
|
|
4755 (buf (find-file-noselect newsticker-cache-filename)))
|
|
4756 (when buf
|
|
4757 (set-buffer buf)
|
|
4758 (setq buffer-undo-list t)
|
|
4759 (erase-buffer)
|
|
4760 (insert ";; -*- coding: utf-8 -*-\n")
|
|
4761 (insert (prin1-to-string newsticker--cache))
|
|
4762 (when save
|
|
4763 (save-buffer))))))
|
|
4764
|
|
4765 (defun newsticker--cache-get-feed (feed)
|
|
4766 "Return the cached data for the feed FEED.
|
|
4767 FEED is a symbol!"
|
|
4768 (assoc feed newsticker--cache))
|
|
4769
|
|
4770 ;; ======================================================================
|
|
4771 ;;; Statistics
|
|
4772 ;; ======================================================================
|
|
4773 (defun newsticker--stat-num-items (feed &optional age)
|
|
4774 "Return number of items in the given FEED which have the given AGE.
|
|
4775 If AGE is nil, the total number items is returned."
|
|
4776 (let ((items (cdr (newsticker--cache-get-feed feed)))
|
|
4777 (num 0))
|
|
4778 (while items
|
|
4779 (if age
|
|
4780 (if (eq (newsticker--age (car items)) age)
|
|
4781 (setq num (1+ num)))
|
|
4782 (if (memq (newsticker--age (car items)) '(new old immortal obsolete))
|
|
4783 (setq num (1+ num))))
|
|
4784 (setq items (cdr items)))
|
|
4785 num))
|
|
4786
|
|
4787 ;; ======================================================================
|
|
4788 ;;; OPML
|
|
4789 ;; ======================================================================
|
|
4790 (defun newsticker-opml-export ()
|
|
4791 "OPML subscription export.
|
|
4792 Export subscriptions to a buffer in OPML Format."
|
|
4793 (interactive)
|
|
4794 (with-current-buffer (get-buffer-create "*OPML Export*")
|
|
4795 (set-buffer-file-coding-system 'utf-8)
|
|
4796 (insert (concat
|
|
4797 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
|
4798 "<!-- OPML generated by Emacs newsticker.el -->\n"
|
|
4799 "<opml version=\"1.0\">\n"
|
|
4800 " <head>\n"
|
|
4801 " <title>mySubscriptions</title>\n"
|
|
4802 " <dateCreated>" (format-time-string "%a, %d %b %Y %T %z")
|
|
4803 "</dateCreated>\n"
|
|
4804 " <ownerEmail>" user-mail-address "</ownerEmail>\n"
|
|
4805 " <ownerName>" (user-full-name) "</ownerName>\n"
|
|
4806 " </head>\n"
|
|
4807 " <body>\n"))
|
|
4808 (mapc (lambda (sub)
|
|
4809 (insert " <outline text=\"")
|
|
4810 (insert (newsticker--title sub))
|
|
4811 (insert "\" xmlUrl=\"")
|
|
4812 (insert (cadr sub))
|
|
4813 (insert "\"/>\n"))
|
|
4814 (append newsticker-url-list newsticker-url-list-defaults))
|
|
4815 (insert " </body>\n</opml>\n"))
|
|
4816 (pop-to-buffer "*OPML Export*")
|
|
4817 (when (fboundp 'sgml-mode)
|
|
4818 (sgml-mode)))
|
|
4819
|
|
4820 (defun newsticker-opml-import (filename)
|
|
4821 "Import OPML data from FILENAME."
|
|
4822 (interactive "fOPML file: ")
|
|
4823 (set-buffer (find-file-noselect filename))
|
|
4824 (goto-char (point-min))
|
|
4825 (let* ((node-list (xml-parse-region (point-min) (point-max)))
|
|
4826 (body (car (xml-get-children (car node-list) 'body)))
|
|
4827 (outlines (xml-get-children body 'outline)))
|
|
4828 (mapc (lambda (outline)
|
|
4829 (let ((name (xml-get-attribute outline 'text))
|
|
4830 (url (xml-get-attribute outline 'xmlUrl)))
|
|
4831 (add-to-list 'newsticker-url-list
|
|
4832 (list name url nil nil nil) t)))
|
|
4833 outlines))
|
|
4834 (customize-variable 'newsticker-url-list))
|
|
4835
|
|
4836 ;; ======================================================================
|
|
4837 ;;; Auto marking
|
|
4838 ;; ======================================================================
|
|
4839 (defun newsticker--run-auto-mark-filter (feed item)
|
|
4840 "Automatically mark an item as old or immortal.
|
|
4841 This function checks the variable `newsticker-auto-mark-filter'
|
|
4842 for an entry that matches FEED and ITEM."
|
|
4843 (let ((case-fold-search t))
|
|
4844 (mapc (lambda (filter)
|
|
4845 (let ((filter-feed (car filter))
|
|
4846 (old-list (nth 1 filter))
|
|
4847 (imm-list (nth 2 filter)))
|
|
4848 (when (string-match filter-feed feed)
|
|
4849 (newsticker--do-run-auto-mark-filter item 'old old-list)
|
|
4850 (newsticker--do-run-auto-mark-filter item 'immortal imm-list))))
|
|
4851 newsticker-auto-mark-filter)))
|
|
4852
|
|
4853 (defun newsticker--do-run-auto-mark-filter (item age list)
|
|
4854 "Actually compare ITEM AGE LIST against `newsticker-auto-mark-filter'."
|
|
4855 (mapc (lambda (pattern)
|
|
4856 (when (string-match pattern (newsticker--title item))
|
|
4857 (setcar (nthcdr 4 item) age)))
|
|
4858 list))
|
|
4859
|
|
4860
|
|
4861 ;; ======================================================================
|
|
4862 ;;; hook samples
|
|
4863 ;; ======================================================================
|
|
4864 (defun newsticker-new-item-functions-sample (feed item)
|
|
4865 "Demonstrate the use of the `newsticker-new-item-functions' hook.
|
|
4866 This function just prints out the values of the FEED and title of the ITEM."
|
|
4867 (message (concat "newsticker-new-item-functions-sample: feed=`%s', "
|
|
4868 "title=`%s'")
|
|
4869 feed (newsticker--title item)))
|
|
4870
|
|
4871 (defun newsticker-download-images (feed item)
|
|
4872 "Download the first image.
|
|
4873 If FEED equals \"imagefeed\" download the first image URL found
|
|
4874 in the description=contents of ITEM to the directory
|
|
4875 \"~/tmp/newsticker/FEED/TITLE\" where TITLE is the title of the item."
|
|
4876 (when (string= feed "imagefeed")
|
|
4877 (let ((title (newsticker--title item))
|
|
4878 (desc (newsticker--desc item)))
|
|
4879 (when (string-match "<img src=\"\\(http://[^ \"]+\\)\"" desc)
|
|
4880 (let ((url (substring desc (match-beginning 1) (match-end 1)))
|
|
4881 (temp-dir (concat "~/tmp/newsticker/" feed "/" title))
|
|
4882 (org-dir default-directory))
|
|
4883 (unless (file-directory-p temp-dir)
|
|
4884 (make-directory temp-dir t))
|
|
4885 (cd temp-dir)
|
|
4886 (message "Getting image %s" url)
|
|
4887 (apply 'start-process "wget-image"
|
|
4888 " *newsticker-wget-download-images*"
|
|
4889 newsticker-wget-name
|
|
4890 (list url))
|
|
4891 (cd org-dir))))))
|
|
4892
|
|
4893 (defun newsticker-download-enclosures (feed item)
|
|
4894 "In all FEEDs download the enclosed object of the news ITEM.
|
|
4895 The object is saved to the directory \"~/tmp/newsticker/FEED/TITLE\", which
|
|
4896 is created if it does not exist. TITLE is the title of the news
|
|
4897 item. Argument FEED is ignored.
|
|
4898 This function is suited for adding it to `newsticker-new-item-functions'."
|
|
4899 (let ((title (newsticker--title item))
|
|
4900 (enclosure (newsticker--enclosure item)))
|
|
4901 (when enclosure
|
|
4902 (let ((url (cdr (assoc 'url enclosure)))
|
|
4903 (temp-dir (concat "~/tmp/newsticker/" feed "/" title))
|
|
4904 (org-dir default-directory))
|
|
4905 (unless (file-directory-p temp-dir)
|
|
4906 (make-directory temp-dir t))
|
|
4907 (cd temp-dir)
|
|
4908 (message "Getting enclosure %s" url)
|
|
4909 (apply 'start-process "wget-enclosure"
|
|
4910 " *newsticker-wget-download-enclosures*"
|
|
4911 newsticker-wget-name
|
|
4912 (list url))
|
|
4913 (cd org-dir)))))
|
|
4914
|
|
4915
|
|
4916 (provide 'newsticker)
|
|
4917
|
|
4918 ;;; newsticker.el ends here
|