Initial check-in.
[bpt/emacs.git] / lisp / net / newsticker-plainview.el
CommitLineData
2415d4c6
UJ
1;;; newsticker-plainview.el --- Single buffer frontend for newsticker.
2
3;; Copyright (C) 2008 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-plainview.el
9;; URL: http://www.nongnu.org/newsticker
10;; Time-stamp: "7. Juni 2008, 23:37:09 (ulf)"
11;; CVS-Version: $Id: newsticker-plainview.el,v 1.10 2008/05/04 15:04:34 u11 Exp $
12
13;; ======================================================================
14
15;; GNU Emacs is free software: you can redistribute it and/or modify
16;; it under the terms of the GNU General Public License as published by
17;; the Free Software Foundation, either version 3 of the License, or
18;; (at your option) any later version.
19
20;; GNU Emacs is distributed in the hope that it will be useful,
21;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23;; GNU General Public License for more details.
24
25;; You should have received a copy of the GNU General Public License
26;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
27
28;; ======================================================================
29;;; Commentary:
30
31;; See newsticker.el
32
33;; ======================================================================
34;;; Code:
35
36(require 'newsticker-ticker)
37(require 'newsticker-reader)
38(require 'derived)
39(require 'xml)
40
41;; Silence warnings
42(defvar tool-bar-map)
43(defvar w3-mode-map)
44(defvar w3m-minor-mode-map)
45
46;; ======================================================================
47;;; Customization
48;; ======================================================================
49(defgroup newsticker-plainview nil
50 "Settings for the simple plain view reader.
51See also `newsticker-plainview-hooks'."
52 :group 'newsticker-reader)
53
54
55(defun newsticker--set-customvar-buffer (symbol value)
56 "Set newsticker-variable SYMBOL value to VALUE.
57Calls all actions which are necessary in order to make the new
58value effective."
59 (if (or (not (boundp symbol))
60 (equal (symbol-value symbol) value))
61 (set symbol value)
62 ;; something must have changed
63 (set symbol value)
64 (newsticker--buffer-set-uptodate nil)))
65
66(defun newsticker--set-customvar-sorting (symbol value)
67 "Set newsticker-variable SYMBOL value to VALUE.
68Calls all actions which are necessary in order to make the new
69value effective."
70 (if (or (not (boundp symbol))
71 (equal (symbol-value symbol) value))
72 (set symbol value)
73 ;; something must have changed
74 (set symbol value)
75 (message "Applying new sort method...")
76 (when (fboundp 'newsticker--cache-sort) (newsticker--cache-sort))
77 (when (fboundp 'newsticker--buffer-set-uptodate)
78 (newsticker--buffer-set-uptodate nil))
79 (message "Applying new sort method...done")))
80
81(defcustom newsticker-sort-method
82 'sort-by-original-order
83 "Sort method for news items.
84The following sort methods are available:
85* `sort-by-original-order' keeps the order in which the items
86 appear in the headline file (please note that for immortal items,
87 which have been removed from the news feed, there is no original
88 order),
89* `sort-by-time' looks at the time at which an item has been seen
90 the first time. The most recent item is put at top,
91* `sort-by-title' will put the items in an alphabetical order."
92 :type '(choice
93 (const :tag "Keep original order" sort-by-original-order)
94 (const :tag "Sort by time" sort-by-time)
95 (const :tag "Sort by title" sort-by-title))
96 :set 'newsticker--set-customvar-sorting
97 :group 'newsticker-plainview)
98
99(defcustom newsticker-heading-format
100 "%l
101%t %d %s"
102 "Format string for feed headings.
103The following printf-like specifiers can be used:
104%d The date the feed was retrieved. See `newsticker-date-format'.
105%l The logo (image) of the feed. Most news feeds provide a small
106 image as logo. Newsticker can display them, if Emacs can --
107 see `image-types' for a list of supported image types.
108%L The logo (image) of the feed. If the logo is not available
109 the title of the feed is used.
110%s The statistical data of the feed. See `newsticker-statistics-format'.
111%t The title of the feed, i.e. its name."
112 :type 'string
113 :set 'newsticker--set-customvar-formatting
114 :group 'newsticker-plainview)
115
116(defcustom newsticker-item-format
117 "%t %d"
118 "Format string for news item headlines.
119The following printf-like specifiers can be used:
120%d The date the item was (first) retrieved. See `newsticker-date-format'.
121%l The logo (image) of the feed. Most news feeds provide a small
122 image as logo. Newsticker can display them, if Emacs can --
123 see `image-types' for a list of supported image types.
124%L The logo (image) of the feed. If the logo is not available
125 the title of the feed is used.
126%t The title of the item."
127 :type 'string
128 :set 'newsticker--set-customvar-formatting
129 :group 'newsticker-plainview)
130
131(defcustom newsticker-desc-format
132 "%d %c"
133 "Format string for news descriptions (contents).
134The following printf-like specifiers can be used:
135%c The contents (description) of the item.
136%d The date the item was (first) retrieved. See
137 `newsticker-date-format'."
138 :type 'string
139 :set 'newsticker--set-customvar-formatting
140 :group 'newsticker-plainview)
141
142(defcustom newsticker-statistics-format
143 "[%n + %i + %o + %O = %a]"
144 "Format for the statistics part in feed lines.
145The following printf-like specifiers can be used:
146%a The number of all items in the feed.
147%i The number of immortal items in the feed.
148%n The number of new items in the feed.
149%o The number of old items in the feed.
150%O The number of obsolete items in the feed."
151 :type 'string
152 :set 'newsticker--set-customvar-formatting
153 :group 'newsticker-plainview)
154
155
156;; ======================================================================
157;; faces
158(defgroup newsticker-faces nil
159 "Settings for the faces of the feed reader."
160 :group 'newsticker-plainview)
161
162(defface newsticker-feed-face
163 '((((class color) (background dark))
164 (:family "helvetica" :bold t :height 1.2 :foreground "misty rose"))
165 (((class color) (background light))
166 (:family "helvetica" :bold t :height 1.2 :foreground "black")))
167 "Face for news feeds."
168 :group 'newsticker-faces)
169
170(defface newsticker-new-item-face
171 '((((class color) (background dark))
172 (:family "helvetica" :bold t))
173 (((class color) (background light))
174 (:family "helvetica" :bold t)))
175 "Face for new news items."
176 :group 'newsticker-faces)
177
178(defface newsticker-old-item-face
179 '((((class color) (background dark))
180 (:family "helvetica" :bold t :foreground "orange3"))
181 (((class color) (background light))
182 (:family "helvetica" :bold t :foreground "red4")))
183 "Face for old news items."
184 :group 'newsticker-faces)
185
186(defface newsticker-immortal-item-face
187 '((((class color) (background dark))
188 (:family "helvetica" :bold t :italic t :foreground "orange"))
189 (((class color) (background light))
190 (:family "helvetica" :bold t :italic t :foreground "blue")))
191 "Face for immortal news items."
192 :group 'newsticker-faces)
193
194(defface newsticker-obsolete-item-face
195 '((((class color) (background dark))
196 (:family "helvetica" :bold t :strike-through t))
197 (((class color) (background light))
198 (:family "helvetica" :bold t :strike-through t)))
199 "Face for old news items."
200 :group 'newsticker-faces)
201
202(defface newsticker-date-face
203 '((((class color) (background dark))
204 (:family "helvetica" :italic t :height 0.8))
205 (((class color) (background light))
206 (:family "helvetica" :italic t :height 0.8)))
207 "Face for newsticker dates."
208 :group 'newsticker-faces)
209
210(defface newsticker-statistics-face
211 '((((class color) (background dark))
212 (:family "helvetica" :italic t :height 0.8))
213 (((class color) (background light))
214 (:family "helvetica" :italic t :height 0.8)))
215 "Face for newsticker dates."
216 :group 'newsticker-faces)
217
218(defface newsticker-enclosure-face
219 '((((class color) (background dark))
220 (:bold t :background "orange"))
221 (((class color) (background light))
222 (:bold t :background "orange")))
223 "Face for enclosed elements."
224 :group 'newsticker-faces)
225
226(defface newsticker-extra-face
227 '((((class color) (background dark))
228 (:italic t :foreground "gray50" :height 0.8))
229 (((class color) (background light))
230 (:italic t :foreground "gray50" :height 0.8)))
231 "Face for newsticker dates."
232 :group 'newsticker-faces)
233
234(defface newsticker-default-face
235 '((((class color) (background dark))
236 (:inherit default))
237 (((class color) (background light))
238 (:inherit default)))
239 "Face for the description of news items."
240 ;;:set 'newsticker--set-customvar
241 :group 'newsticker-faces)
242
243(defcustom newsticker-hide-old-items-in-newsticker-buffer
244 nil
245 "Decides whether to automatically hide old items in the *newsticker* buffer.
246If set to t old items will be completely folded and only new
247items will show up in the *newsticker* buffer. Otherwise old as
248well as new items will be visible."
249 :type 'boolean
250 :set 'newsticker--set-customvar-buffer
251 :group 'newsticker-plainview)
252
253(defcustom newsticker-show-descriptions-of-new-items
254 t
255 "Whether to automatically show descriptions of new items in *newsticker*.
256If set to t old items will be folded and new items will be
257unfolded. Otherwise old as well as new items will be folded."
258 :type 'boolean
259 :set 'newsticker--set-customvar-buffer
260 :group 'newsticker-plainview)
261
262(defcustom newsticker-show-all-news-elements
263 nil
264 "Show all news elements."
265 :type 'boolean
266 ;;:set 'newsticker--set-customvar
267 :group 'newsticker-plainview)
268
269;; ======================================================================
270;; hooks
271(defgroup newsticker-plainview-hooks nil
272 "Settings for newsticker hooks which apply to plainview only."
273 :group 'newsticker-hooks)
274
275(defcustom newsticker-select-item-hook
276 'newsticker--buffer-make-item-completely-visible
277 "List of functions run after a headline has been selected.
278Each function is called after one of `newsticker-next-item',
279`newsticker-next-new-item', `newsticker-previous-item',
280`newsticker-previous-new-item' has been called.
281
282The default value 'newsticker--buffer-make-item-completely-visible
283assures that the current item is always completely visible."
284 :type 'hook
285 :options '(newsticker--buffer-make-item-completely-visible)
286 :group 'newsticker-plainview-hooks)
287
288(defcustom newsticker-select-feed-hook
289 'newsticker--buffer-make-item-completely-visible
290 "List of functions run after a feed has been selected.
291Each function is called after one of `newsticker-next-feed', and
292`newsticker-previous-feed' has been called.
293
294The default value 'newsticker--buffer-make-item-completely-visible
295assures that the current feed is completely visible."
296 :type 'hook
297 :options '(newsticker--buffer-make-item-completely-visible)
298 :group 'newsticker-plainview-hooks)
299
300(defcustom newsticker-buffer-change-hook
301 'newsticker-w3m-show-inline-images
302 "List of functions run after the newsticker buffer has been updated.
303Each function is called after `newsticker-buffer-update' has been called.
304
305The default value '`newsticker-w3m-show-inline-images' loads inline
306images."
307 :type 'hook
308 :group 'newsticker-plainview-hooks)
309
310(defcustom newsticker-narrow-hook
311 'newsticker-w3m-show-inline-images
312 "List of functions run after narrowing in newsticker buffer has changed.
313Each function is called after
314`newsticker-toggle-auto-narrow-to-feed' or
315`newsticker-toggle-auto-narrow-to-item' has been called.
316
317The default value '`newsticker-w3m-show-inline-images' loads inline
318images."
319 :type 'hook
320 :group 'newsticker-plainview-hooks)
321
322;; ======================================================================
323;;; Toolbar
324;; ======================================================================
325
326(defvar newsticker--plainview-tool-bar-map
327 (if (featurep 'xemacs)
328 nil
329 (let ((tool-bar-map (make-sparse-keymap)))
330 (define-key tool-bar-map [newsticker-sep-1]
331 (list 'menu-item "--double-line"))
332 (define-key tool-bar-map [newsticker-browse-url]
333 (list 'menu-item "newsticker-browse-url" 'newsticker-browse-url
334 :visible t
335 :help "Browse URL for item at point"
336 :image newsticker--browse-image))
337 (define-key tool-bar-map [newsticker-buffer-force-update]
338 (list 'menu-item "newsticker-buffer-force-update"
339 'newsticker-buffer-force-update
340 :visible t
341 :help "Update newsticker buffer"
342 :image newsticker--update-image
343 :enable '(not newsticker--buffer-uptodate-p)))
344 (define-key tool-bar-map [newsticker-get-all-news]
345 (list 'menu-item "newsticker-get-all-news" 'newsticker-get-all-news
346 :visible t
347 :help "Get news for all feeds"
348 :image newsticker--get-all-image))
349 (define-key tool-bar-map [newsticker-mark-item-at-point-as-read]
350 (list 'menu-item "newsticker-mark-item-at-point-as-read"
351 'newsticker-mark-item-at-point-as-read
352 :visible t
353 :image newsticker--mark-read-image
354 :help "Mark current item as read"
355 :enable '(newsticker-item-not-old-p)))
356 (define-key tool-bar-map [newsticker-mark-item-at-point-as-immortal]
357 (list 'menu-item "newsticker-mark-item-at-point-as-immortal"
358 'newsticker-mark-item-at-point-as-immortal
359 :visible t
360 :image newsticker--mark-immortal-image
361 :help "Mark current item as immortal"
362 :enable '(newsticker-item-not-immortal-p)))
363 (define-key tool-bar-map [newsticker-toggle-auto-narrow-to-feed]
364 (list 'menu-item "newsticker-toggle-auto-narrow-to-feed"
365 'newsticker-toggle-auto-narrow-to-feed
366 :visible t
367 :help "Toggle visibility of other feeds"
368 :image newsticker--narrow-image))
369 (define-key tool-bar-map [newsticker-next-feed]
370 (list 'menu-item "newsticker-next-feed" 'newsticker-next-feed
371 :visible t
372 :help "Go to next feed"
373 :image newsticker--next-feed-image
374 :enable '(newsticker-next-feed-available-p)))
375 (define-key tool-bar-map [newsticker-next-item]
376 (list 'menu-item "newsticker-next-item" 'newsticker-next-item
377 :visible t
378 :help "Go to next item"
379 :image newsticker--next-item-image
380 :enable '(newsticker-next-item-available-p)))
381 (define-key tool-bar-map [newsticker-previous-item]
382 (list 'menu-item "newsticker-previous-item" 'newsticker-previous-item
383 :visible t
384 :help "Go to previous item"
385 :image newsticker--previous-item-image
386 :enable '(newsticker-previous-item-available-p)))
387 (define-key tool-bar-map [newsticker-previous-feed]
388 (list 'menu-item "newsticker-previous-feed" 'newsticker-previous-feed
389 :visible t
390 :help "Go to previous feed"
391 :image newsticker--previous-feed-image
392 :enable '(newsticker-previous-feed-available-p)))
393 ;; standard icons / actions
394 (tool-bar-add-item "close"
395 'newsticker-close-buffer
396 'newsticker-close-buffer
397 :help "Close newsticker buffer")
398 (tool-bar-add-item "preferences"
399 'newsticker-customize
400 'newsticker-customize
401 :help "Customize newsticker")
402 tool-bar-map)))
403
404;; ======================================================================
405;;; Newsticker mode
406;; ======================================================================
407
408(define-derived-mode newsticker-mode fundamental-mode
409 "NewsTicker"
410 "Viewing news feeds in Emacs."
411 (set (make-local-variable 'tool-bar-map) newsticker--plainview-tool-bar-map)
412 (set (make-local-variable 'imenu-sort-function) nil)
413 (set (make-local-variable 'scroll-conservatively) 999)
414 (setq imenu-create-index-function 'newsticker--imenu-create-index)
415 (setq imenu-default-goto-function 'newsticker--imenu-goto)
416 (setq buffer-read-only t)
417 (auto-fill-mode -1) ;; turn auto-fill off!
418 (font-lock-mode -1) ;; turn off font-lock!!
419 (set (make-local-variable 'font-lock-defaults) nil)
420 (set (make-local-variable 'line-move-ignore-invisible) t)
421 (setq mode-line-format
422 (list "-"
423 'mode-line-mule-info
424 'mode-line-modified
425 'mode-line-frame-identification
426 " Newsticker ("
427 '(newsticker--buffer-uptodate-p
428 "up to date"
429 "NEED UPDATE")
430 ") "
431 '(:eval (format "[%d]" (length newsticker--process-ids)))
432 " -- "
433 '(:eval (newsticker--buffer-get-feed-title-at-point))
434 ": "
435 '(:eval (newsticker--buffer-get-item-title-at-point))
436 " %-"))
437 (add-to-invisibility-spec 't)
438 (unless newsticker-show-all-news-elements
439 (add-to-invisibility-spec 'extra))
440 (newsticker--buffer-set-uptodate nil))
441
442;; refine its mode-map
443(define-key newsticker-mode-map "sO" 'newsticker-show-old-items)
444(define-key newsticker-mode-map "hO" 'newsticker-hide-old-items)
445(define-key newsticker-mode-map "sa" 'newsticker-show-all-desc)
446(define-key newsticker-mode-map "ha" 'newsticker-hide-all-desc)
447(define-key newsticker-mode-map "sf" 'newsticker-show-feed-desc)
448(define-key newsticker-mode-map "hf" 'newsticker-hide-feed-desc)
449(define-key newsticker-mode-map "so" 'newsticker-show-old-item-desc)
450(define-key newsticker-mode-map "ho" 'newsticker-hide-old-item-desc)
451(define-key newsticker-mode-map "sn" 'newsticker-show-new-item-desc)
452(define-key newsticker-mode-map "hn" 'newsticker-hide-new-item-desc)
453(define-key newsticker-mode-map "se" 'newsticker-show-entry)
454(define-key newsticker-mode-map "he" 'newsticker-hide-entry)
455(define-key newsticker-mode-map "sx" 'newsticker-show-extra)
456(define-key newsticker-mode-map "hx" 'newsticker-hide-extra)
457
458(define-key newsticker-mode-map " " 'scroll-up)
459(define-key newsticker-mode-map "q" 'newsticker-close-buffer)
460(define-key newsticker-mode-map "p" 'newsticker-previous-item)
461(define-key newsticker-mode-map "P" 'newsticker-previous-new-item)
462(define-key newsticker-mode-map "F" 'newsticker-previous-feed)
463(define-key newsticker-mode-map "\t" 'newsticker-next-item)
464(define-key newsticker-mode-map "n" 'newsticker-next-item)
465(define-key newsticker-mode-map "N" 'newsticker-next-new-item)
466(define-key newsticker-mode-map "f" 'newsticker-next-feed)
467(define-key newsticker-mode-map "M" 'newsticker-mark-all-items-as-read)
468(define-key newsticker-mode-map "m"
469 'newsticker-mark-all-items-at-point-as-read-and-redraw)
470(define-key newsticker-mode-map "o"
471 'newsticker-mark-item-at-point-as-read)
472(define-key newsticker-mode-map "O"
473 'newsticker-mark-all-items-at-point-as-read)
474(define-key newsticker-mode-map "G" 'newsticker-get-all-news)
475(define-key newsticker-mode-map "g" 'newsticker-get-news-at-point)
476(define-key newsticker-mode-map "u" 'newsticker-buffer-update)
477(define-key newsticker-mode-map "U" 'newsticker-buffer-force-update)
478(define-key newsticker-mode-map "a" 'newsticker-add-url)
479
480(define-key newsticker-mode-map "i"
481 'newsticker-mark-item-at-point-as-immortal)
482
483(define-key newsticker-mode-map "xf"
484 'newsticker-toggle-auto-narrow-to-feed)
485(define-key newsticker-mode-map "xi"
486 'newsticker-toggle-auto-narrow-to-item)
487
488;; maps for the clickable portions
489(defvar newsticker--url-keymap (make-sparse-keymap)
490 "Key map for click-able headings in the newsticker buffer.")
491(define-key newsticker--url-keymap [mouse-1]
492 'newsticker-mouse-browse-url)
493(define-key newsticker--url-keymap [mouse-2]
494 'newsticker-mouse-browse-url)
495(define-key newsticker--url-keymap "\n"
496 'newsticker-browse-url)
497(define-key newsticker--url-keymap "\C-m"
498 'newsticker-browse-url)
499(define-key newsticker--url-keymap [(control return)]
500 'newsticker-handle-url)
501
502;; newsticker menu
503(defvar newsticker-menu (make-sparse-keymap "Newsticker"))
504
505(define-key newsticker-menu [newsticker-browse-url]
506 '("Browse URL for item at point" . newsticker-browse-url))
507(define-key newsticker-menu [newsticker-separator-1]
508 '("--"))
509(define-key newsticker-menu [newsticker-buffer-update]
510 '("Update buffer" . newsticker-buffer-update))
511(define-key newsticker-menu [newsticker-separator-2]
512 '("--"))
513(define-key newsticker-menu [newsticker-get-all-news]
514 '("Get news from all feeds" . newsticker-get-all-news))
515(define-key newsticker-menu [newsticker-get-news-at-point]
516 '("Get news from feed at point" . newsticker-get-news-at-point))
517(define-key newsticker-menu [newsticker-separator-3]
518 '("--"))
519(define-key newsticker-menu [newsticker-mark-all-items-as-read]
520 '("Mark all items as read" . newsticker-mark-all-items-as-read))
521(define-key newsticker-menu [newsticker-mark-all-items-at-point-as-read]
522 '("Mark all items in feed at point as read" .
523 newsticker-mark-all-items-at-point-as-read))
524(define-key newsticker-menu [newsticker-mark-item-at-point-as-read]
525 '("Mark item at point as read" .
526 newsticker-mark-item-at-point-as-read))
527(define-key newsticker-menu [newsticker-mark-item-at-point-as-immortal]
528 '("Toggle immortality for item at point" .
529 newsticker-mark-item-at-point-as-immortal))
530(define-key newsticker-menu [newsticker-separator-4]
531 '("--"))
532(define-key newsticker-menu [newsticker-toggle-auto-narrow-to-item]
533 '("Narrow to single item" . newsticker-toggle-auto-narrow-to-item))
534(define-key newsticker-menu [newsticker-toggle-auto-narrow-to-feed]
535 '("Narrow to single news feed" . newsticker-toggle-auto-narrow-to-feed))
536(define-key newsticker-menu [newsticker-hide-old-items]
537 '("Hide old items" . newsticker-hide-old-items))
538(define-key newsticker-menu [newsticker-show-old-items]
539 '("Show old items" . newsticker-show-old-items))
540(define-key newsticker-menu [newsticker-next-item]
541 '("Go to next item" . newsticker-next-item))
542(define-key newsticker-menu [newsticker-previous-item]
543 '("Go to previous item" . newsticker-previous-item))
544
545;; bind menu to mouse
546(define-key newsticker-mode-map [down-mouse-3] newsticker-menu)
547;; Put menu in menu-bar
548(define-key newsticker-mode-map [menu-bar Newsticker]
549 (cons "Newsticker" newsticker-menu))
550
551
552;; ======================================================================
553;;; User fun
554;; ======================================================================
555(defun newsticker-plainview ()
556 "Start newsticker plainview."
557 (interactive)
558 (newsticker-buffer-update t)
559 (switch-to-buffer "*newsticker*"))
560
561(defun newsticker-buffer-force-update ()
562 "Update the newsticker buffer, even if not necessary."
563 (interactive)
564 (newsticker-buffer-update t))
565
566(defun newsticker-buffer-update (&optional force)
567 "Update the *newsticker* buffer.
568Unless FORCE is t this is done only if necessary, i.e. when the
569*newsticker* buffer is not up-to-date."
570 (interactive)
571 ;; bring cache data into proper order....
572 (newsticker--cache-sort)
573 ;; fill buffer
574 (save-excursion
575 (let ((buf (get-buffer "*newsticker*")))
576 (if buf
577 (switch-to-buffer buf)
578 (switch-to-buffer (get-buffer-create "*newsticker*"))
579 (newsticker--buffer-set-uptodate nil)))
580 (when (or force
581 (not newsticker--buffer-uptodate-p))
582 (message "Preparing newsticker buffer...")
583 (setq buffer-undo-list t)
584 (let ((inhibit-read-only t))
585 (set-buffer-modified-p nil)
586 (erase-buffer)
587 (newsticker-mode)
588 ;; Emacs 21.3.50 does not care if we turn off auto-fill in the
589 ;; definition of newsticker-mode, so we do it here (again)
590 (auto-fill-mode -1)
591
592 (set-buffer-file-coding-system 'utf-8)
593
594 (if newsticker-use-full-width
595 (set (make-local-variable 'fill-column) (1- (window-width))))
596 (newsticker--buffer-insert-all-items)
597
598 ;; FIXME: needed for methods buffer in ecb
599 ;; (set-visited-file-name "*newsticker*")
600
601 (set-buffer-modified-p nil)
602 (newsticker-hide-all-desc)
603 (if newsticker-hide-old-items-in-newsticker-buffer
604 (newsticker-hide-old-items))
605 (if newsticker-show-descriptions-of-new-items
606 (newsticker-show-new-item-desc))
607 )
608 (message ""))
609 (newsticker--buffer-set-uptodate t)
610 (run-hooks 'newsticker-buffer-change-hook)))
611
612(defun newsticker-get-news-at-point ()
613 "Launch retrieval of news for the feed point is in.
614This does NOT start the retrieval timers."
615 (interactive)
616 ;; launch retrieval of news
617 (let ((feed (get-text-property (point) 'feed)))
618 (when feed
619 (newsticker--debug-msg "Getting news for %s" (symbol-name feed))
620 (newsticker-get-news (symbol-name feed)))))
621
622(defun newsticker-w3m-show-inline-images ()
623 "Show inline images in visible text ranges.
624In-line images in invisible text ranges are hidden. This function
625calls `w3m-toggle-inline-image'. It works only if
626`newsticker-html-renderer' is set to `w3m-region'."
627 (interactive)
628 (if (eq newsticker-html-renderer 'w3m-region)
629 (let ((inhibit-read-only t))
630 (save-excursion
631 (save-restriction
632 (widen)
633 (goto-char (point-min))
634 (let ((pos (point)))
635 (while pos
636 (setq pos (next-single-property-change pos 'w3m-image))
637 (when pos
638 (goto-char pos)
639 (when (get-text-property pos 'w3m-image)
640 (let ((invis (newsticker--lists-intersect-p
641 (get-text-property (1- (point))
642 'invisible)
643 buffer-invisibility-spec)))
644 (unless (car (get-text-property (1- (point))
645 'display))
646 (unless invis
647 (w3m-toggle-inline-image t)))))))))))))
648
649;; ======================================================================
650;;; Keymap stuff
651;; ======================================================================
652(defun newsticker-close-buffer ()
653 "Close the newsticker buffer."
654 (interactive)
655 (newsticker--cache-update t)
656 (bury-buffer))
657
658(defun newsticker-next-new-item (&optional do-not-wrap-at-eob)
659 "Go to next new news item.
660If no new item is found behind point, search is continued at
661beginning of buffer unless optional argument DO-NOT-WRAP-AT-EOB
662is non-nil."
663 (interactive)
664 (widen)
665 (let ((go-ahead t))
666 (while go-ahead
667 (unless (newsticker--buffer-goto '(item) 'new)
668 ;; found nothing -- wrap
669 (unless do-not-wrap-at-eob
670 (goto-char (point-min))
671 (newsticker-next-new-item t))
672 (setq go-ahead nil))
673 (unless (newsticker--lists-intersect-p
674 (get-text-property (point) 'invisible)
675 buffer-invisibility-spec)
676 ;; this item is invisible -- continue search
677 (setq go-ahead nil))))
678 (run-hooks 'newsticker-select-item-hook)
679 (point))
680
681(defun newsticker-previous-new-item (&optional do-not-wrap-at-bob)
682 "Go to previous new news item.
683If no new item is found before point, search is continued at
684beginning of buffer unless optional argument DO-NOT-WRAP-AT-BOB
685is non-nil."
686 (interactive)
687 (widen)
688 (let ((go-ahead t))
689 (while go-ahead
690 (unless (newsticker--buffer-goto '(item) 'new t)
691 (unless do-not-wrap-at-bob
692 (goto-char (point-max))
693 (newsticker--buffer-goto '(item) 'new t)))
694 (unless (newsticker--lists-intersect-p
695 (get-text-property (point) 'invisible)
696 buffer-invisibility-spec)
697 (setq go-ahead nil))))
698 (run-hooks 'newsticker-select-item-hook)
699 (point))
700
701(defun newsticker-next-item (&optional do-not-wrap-at-eob)
702 "Go to next news item.
703Return new buffer position.
704If no item is found below point, search is continued at beginning
705of buffer unless optional argument DO-NOT-WRAP-AT-EOB is
706non-nil."
707 (interactive)
708 (widen)
709 (let ((go-ahead t)
710 (search-list '(item)))
711 (if newsticker--auto-narrow-to-item
712 (setq search-list '(item feed)))
713 (while go-ahead
714 (unless (newsticker--buffer-goto search-list)
715 ;; found nothing -- wrap
716 (unless do-not-wrap-at-eob
717 (goto-char (point-min)))
718 (setq go-ahead nil))
719 (unless (newsticker--lists-intersect-p
720 (get-text-property (point) 'invisible)
721 buffer-invisibility-spec)
722 (setq go-ahead nil))))
723 (run-hooks 'newsticker-select-item-hook)
724 (force-mode-line-update)
725 (point))
726
727(defun newsticker-next-item-same-feed ()
728 "Go to next news item in the same feed.
729Return new buffer position. If no item is found below point or if
730auto-narrow-to-item is enabled, nil is returned."
731 (interactive)
732 (if newsticker--auto-narrow-to-item
733 nil
734 (let ((go-ahead t)
735 (current-pos (point))
736 (end-of-feed (save-excursion (newsticker--buffer-end-of-feed))))
737 (while go-ahead
738 (unless (newsticker--buffer-goto '(item))
739 (setq go-ahead nil))
740 (unless (newsticker--lists-intersect-p
741 (get-text-property (point) 'invisible)
742 buffer-invisibility-spec)
743 (setq go-ahead nil)))
744 (if (and (> (point) current-pos)
745 (< (point) end-of-feed))
746 (point)
747 (goto-char current-pos)
748 nil))))
749
750(defun newsticker-previous-item (&optional do-not-wrap-at-bob)
751 "Go to previous news item.
752Return new buffer position.
753If no item is found before point, search is continued at
754beginning of buffer unless optional argument DO-NOT-WRAP-AT-BOB
755is non-nil."
756 (interactive)
757 (widen)
758 (let ((go-ahead t)
759 (search-list '(item)))
760 (if newsticker--auto-narrow-to-item
761 (setq search-list '(item feed)))
762 (when (bobp)
763 (unless do-not-wrap-at-bob
764 (goto-char (point-max))))
765 (while go-ahead
766 (if (newsticker--buffer-goto search-list nil t)
767 (unless (newsticker--lists-intersect-p
768 (get-text-property (point) 'invisible)
769 buffer-invisibility-spec)
770 (setq go-ahead nil))
771 (goto-char (point-min))
772 (setq go-ahead nil))))
773 (run-hooks 'newsticker-select-item-hook)
774 (force-mode-line-update)
775 (point))
776
777(defun newsticker-next-feed ()
778 "Go to next news feed.
779Return new buffer position."
780 (interactive)
781 (widen)
782 (newsticker--buffer-goto '(feed))
783 (run-hooks 'newsticker-select-feed-hook)
784 (force-mode-line-update)
785 (point))
786
787(defun newsticker-previous-feed ()
788 "Go to previous news feed.
789Return new buffer position."
790 (interactive)
791 (widen)
792 (newsticker--buffer-goto '(feed) nil t)
793 (run-hooks 'newsticker-select-feed-hook)
794 (force-mode-line-update)
795 (point))
796
797(defun newsticker-mark-all-items-at-point-as-read-and-redraw ()
798 "Mark all items as read and clear ticker contents."
799 (interactive)
800 (when (or newsticker--buffer-uptodate-p
801 (y-or-n-p
802 "Buffer is not up to date -- really mark items as read? "))
803 (newsticker-mark-all-items-of-feed-as-read
804 (get-text-property (point) 'feed))))
805
806(defun newsticker-mark-all-items-of-feed-as-read (feed)
807 "Mark all items of FEED as read, clear ticker, and redraw buffer."
808 (when feed
809 (let ((pos (point)))
810 (message "Marking all items as read for %s" (symbol-name feed))
811 (newsticker--cache-replace-age newsticker--cache feed 'new 'old)
812 (newsticker--cache-replace-age newsticker--cache feed 'obsolete
813 'old)
814 (newsticker--cache-update)
815 (newsticker--buffer-set-uptodate nil)
816 (newsticker--ticker-text-setup)
817 (newsticker-buffer-update)
818 ;; go back to where we came frome
819 (goto-char pos)
820 (end-of-line)
821 (newsticker--buffer-goto '(feed) nil t))))
822
823(defun newsticker-mark-all-items-at-point-as-read ()
824 "Mark all items as read and clear ticker contents."
825 (interactive)
826 (when (or newsticker--buffer-uptodate-p
827 (y-or-n-p
828 "Buffer is not up to date -- really mark items as read? "))
829 (newsticker--do-mark-item-at-point-as-read t)
830 (while (newsticker-next-item-same-feed)
831 (newsticker--do-mark-item-at-point-as-read t))
832 (newsticker-next-item t)))
833
834(defun newsticker-mark-item-at-point-as-read (&optional respect-immortality)
835 "Mark item at point as read and move to next item.
836If optional argument RESPECT-IMMORTALITY is not nil immortal items do
837not get changed."
838 (interactive)
839 (when (or newsticker--buffer-uptodate-p
840 (y-or-n-p
841 "Buffer is not up to date -- really mark this item as read? "))
842 (newsticker--do-mark-item-at-point-as-read respect-immortality)
843 ;; move forward
844 (newsticker-next-item t)))
845
846(defun newsticker--do-mark-item-at-point-as-read (&optional respect-immortality)
847 "Mark item at point as read.
848If optional argument RESPECT-IMMORTALITY is not nil immortal items do
849not get changed."
850 (let ((feed (get-text-property (point) 'feed)))
851 (when feed
852 (save-excursion
853 (newsticker--buffer-beginning-of-item)
854 (let ((inhibit-read-only t)
855 (age (get-text-property (point) 'nt-age))
856 (title (get-text-property (point) 'nt-title))
857 (guid (get-text-property (point) 'nt-guid))
858 (nt-desc (get-text-property (point) 'nt-desc))
859 (pos (save-excursion (newsticker--buffer-end-of-item)))
860 item)
861 (when (or (eq age 'new)
862 (eq age 'obsolete)
863 (and (eq age 'immortal)
864 (not respect-immortality)))
865 ;; find item
866 (setq item (newsticker--cache-contains newsticker--cache
867 feed title nt-desc
868 nil nil guid))
869 ;; mark as old
870 (when item
871 (setcar (nthcdr 4 item) 'old)
872 (newsticker--do-forget-preformatted item))
873 ;; clean up ticker
874 (if (or (and (eq age 'new)
875 newsticker-hide-immortal-items-in-echo-area)
876 (and (memq age '(old immortal))
877 (not
878 (eq newsticker-hide-old-items-in-newsticker-buffer
879 newsticker-hide-immortal-items-in-echo-area))))
880 (newsticker--ticker-text-remove feed title))
881 ;; set faces etc.
882 (save-excursion
883 (save-restriction
884 (widen)
885 (put-text-property (point) pos 'nt-age 'old)
886 (newsticker--buffer-set-faces (point) pos)))
887 (set-buffer-modified-p nil)))))))
888
889(defun newsticker-mark-item-at-point-as-immortal ()
890 "Mark item at point as read."
891 (interactive)
892 (when (or newsticker--buffer-uptodate-p
893 (y-or-n-p
894 "Buffer is not up to date -- really mark this item as read? "))
895 (let ((feed (get-text-property (point) 'feed))
896 (item nil))
897 (when feed
898 (save-excursion
899 (newsticker--buffer-beginning-of-item)
900 (let ((inhibit-read-only t)
901 (oldage (get-text-property (point) 'nt-age))
902 (title (get-text-property (point) 'nt-title))
903 (guid (get-text-property (point) 'nt-guid))
904 (pos (save-excursion (newsticker--buffer-end-of-item))))
905 (let ((newage 'immortal))
906 (if (eq oldage 'immortal)
907 (setq newage 'old))
908 (setq item (newsticker--cache-contains newsticker--cache
909 feed title nil nil nil
910 guid))
911 ;; change age
912 (when item
913 (setcar (nthcdr 4 item) newage)
914 (newsticker--do-forget-preformatted item))
915 (if (or (and (eq newage 'immortal)
916 newsticker-hide-immortal-items-in-echo-area)
917 (and (eq newage 'obsolete)
918 newsticker-hide-obsolete-items-in-echo-area)
919 (and (eq oldage 'immortal)
920 (not
921 (eq newsticker-hide-old-items-in-newsticker-buffer
922 newsticker-hide-immortal-items-in-echo-area))))
923 (newsticker--ticker-text-remove feed title)
924 (newsticker--ticker-text-setup))
925 (save-excursion
926 (save-restriction
927 (widen)
928 (put-text-property (point) pos 'nt-age newage)
929 (if (eq newage 'immortal)
930 (put-text-property (point) pos 'nt-age 'immortal)
931 (put-text-property (point) pos 'nt-age 'old))
932 (newsticker--buffer-set-faces (point) pos))))))
933 (if item
934 (newsticker-next-item t))))))
935
936(defun newsticker-mark-all-items-as-read ()
937 "Mark all items as read and clear ticker contents."
938 (interactive)
939 (when (or newsticker--buffer-uptodate-p
940 (y-or-n-p
941 "Buffer is not up to date -- really mark items as read? "))
942 (newsticker--cache-replace-age newsticker--cache 'any 'new 'old)
943 (newsticker--buffer-set-uptodate nil)
944 (newsticker--ticker-text-setup)
945 (newsticker--cache-update)
946 (newsticker-buffer-update)))
947
948(defun newsticker-hide-extra ()
949 "Hide the extra elements of items."
950 (interactive)
951 (newsticker--buffer-hideshow 'extra nil)
952 (newsticker--buffer-redraw))
953
954(defun newsticker-show-extra ()
955 "Show the extra elements of items."
956 (interactive)
957 (newsticker--buffer-hideshow 'extra t)
958 (newsticker--buffer-redraw))
959
960(defun newsticker-hide-old-item-desc ()
961 "Hide the description of old items."
962 (interactive)
963 (newsticker--buffer-hideshow 'desc-old nil)
964 (newsticker--buffer-redraw))
965
966(defun newsticker-show-old-item-desc ()
967 "Show the description of old items."
968 (interactive)
969 (newsticker--buffer-hideshow 'item-old t)
970 (newsticker--buffer-hideshow 'desc-old t)
971 (newsticker--buffer-redraw))
972
973(defun newsticker-hide-new-item-desc ()
974 "Hide the description of new items."
975 (interactive)
976 (newsticker--buffer-hideshow 'desc-new nil)
977 (newsticker--buffer-hideshow 'desc-immortal nil)
978 (newsticker--buffer-hideshow 'desc-obsolete nil)
979 (newsticker--buffer-redraw))
980
981(defun newsticker-show-new-item-desc ()
982 "Show the description of new items."
983 (interactive)
984 (newsticker--buffer-hideshow 'desc-new t)
985 (newsticker--buffer-hideshow 'desc-immortal t)
986 (newsticker--buffer-hideshow 'desc-obsolete t)
987 (newsticker--buffer-redraw))
988
989(defun newsticker-hide-feed-desc ()
990 "Hide the description of feeds."
991 (interactive)
992 (newsticker--buffer-hideshow 'desc-feed nil)
993 (newsticker--buffer-redraw))
994
995(defun newsticker-show-feed-desc ()
996 "Show the description of old items."
997 (interactive)
998 (newsticker--buffer-hideshow 'desc-feed t)
999 (newsticker--buffer-redraw))
1000
1001(defun newsticker-hide-all-desc ()
1002 "Hide the descriptions of feeds and all items."
1003 (interactive)
1004 (newsticker--buffer-hideshow 'desc-feed nil)
1005 (newsticker--buffer-hideshow 'desc-immortal nil)
1006 (newsticker--buffer-hideshow 'desc-obsolete nil)
1007 (newsticker--buffer-hideshow 'desc-new nil)
1008 (newsticker--buffer-hideshow 'desc-old nil)
1009 (newsticker--buffer-redraw))
1010
1011(defun newsticker-show-all-desc ()
1012 "Show the descriptions of feeds and all items."
1013 (interactive)
1014 (newsticker--buffer-hideshow 'desc-feed t)
1015 (newsticker--buffer-hideshow 'desc-immortal t)
1016 (newsticker--buffer-hideshow 'desc-obsolete t)
1017 (newsticker--buffer-hideshow 'desc-new t)
1018 (newsticker--buffer-hideshow 'desc-old t)
1019 (newsticker--buffer-redraw))
1020
1021(defun newsticker-hide-old-items ()
1022 "Hide old items."
1023 (interactive)
1024 (newsticker--buffer-hideshow 'desc-old nil)
1025 (newsticker--buffer-hideshow 'item-old nil)
1026 (newsticker--buffer-redraw))
1027
1028(defun newsticker-show-old-items ()
1029 "Show old items."
1030 (interactive)
1031 (newsticker--buffer-hideshow 'item-old t)
1032 (newsticker--buffer-redraw))
1033
1034(defun newsticker-hide-entry ()
1035 "Hide description of entry at point."
1036 (interactive)
1037 (save-excursion
1038 (let* (pos1 pos2
1039 (inhibit-read-only t)
1040 inv-prop org-inv-prop
1041 is-invisible)
1042 (newsticker--buffer-beginning-of-item)
1043 (newsticker--buffer-goto '(desc))
1044 (setq pos1 (max (point-min) (1- (point))))
1045 (newsticker--buffer-goto '(extra feed item nil))
1046 (setq pos2 (max (point-min) (1- (point))))
1047 (setq inv-prop (get-text-property pos1 'invisible))
1048 (setq org-inv-prop (get-text-property pos1 'org-invisible))
1049 (cond ((eq inv-prop t)
1050 ;; do nothing
1051 )
1052 ((eq org-inv-prop nil)
1053 (add-text-properties pos1 pos2
1054 (list 'invisible (list t)
1055 'org-invisible inv-prop)))
1056 (t
1057 ;; toggle
1058 (add-text-properties pos1 pos2
1059 (list 'invisible org-inv-prop))
1060 (remove-text-properties pos1 pos2 '(org-invisible))))))
1061 (newsticker--buffer-redraw))
1062
1063(defun newsticker-show-entry ()
1064 "Show description of entry at point."
1065 (interactive)
1066 (save-excursion
1067 (let* (pos1 pos2
1068 (inhibit-read-only t)
1069 inv-prop org-inv-prop
1070 is-invisible)
1071 (newsticker--buffer-beginning-of-item)
1072 (newsticker--buffer-goto '(desc))
1073 (setq pos1 (max (point-min) (1- (point))))
1074 (newsticker--buffer-goto '(extra feed item))
1075 (setq pos2 (max (point-min) (1- (point))))
1076 (setq inv-prop (get-text-property pos1 'invisible))
1077 (setq org-inv-prop (get-text-property pos1 'org-invisible))
1078 (cond ((eq org-inv-prop nil)
1079 (add-text-properties pos1 pos2
1080 (list 'invisible nil
1081 'org-invisible inv-prop)))
1082 (t
1083 ;; toggle
1084 (add-text-properties pos1 pos2
1085 (list 'invisible org-inv-prop))
1086 (remove-text-properties pos1 pos2 '(org-invisible))))))
1087 (newsticker--buffer-redraw))
1088
1089(defun newsticker-toggle-auto-narrow-to-feed ()
1090 "Toggle narrowing to current news feed.
1091If auto-narrowing is active, only news item of the current feed
1092are visible."
1093 (interactive)
1094 (newsticker-set-auto-narrow-to-feed
1095 (not newsticker--auto-narrow-to-feed)))
1096
1097(defun newsticker-set-auto-narrow-to-feed (value)
1098 "Turn narrowing to current news feed on or off.
1099If VALUE is nil, auto-narrowing is turned off, otherwise it is turned on."
1100 (interactive)
1101 (setq newsticker--auto-narrow-to-item nil)
1102 (setq newsticker--auto-narrow-to-feed value)
1103 (widen)
1104 (newsticker--buffer-make-item-completely-visible)
1105 (run-hooks 'newsticker-narrow-hook))
1106
1107(defun newsticker-toggle-auto-narrow-to-item ()
1108 "Toggle narrowing to current news item.
1109If auto-narrowing is active, only one item of the current feed
1110is visible."
1111 (interactive)
1112 (newsticker-set-auto-narrow-to-item
1113 (not newsticker--auto-narrow-to-item)))
1114
1115(defun newsticker-set-auto-narrow-to-item (value)
1116 "Turn narrowing to current news item on or off.
1117If VALUE is nil, auto-narrowing is turned off, otherwise it is turned on."
1118 (interactive)
1119 (setq newsticker--auto-narrow-to-feed nil)
1120 (setq newsticker--auto-narrow-to-item value)
1121 (widen)
1122 (newsticker--buffer-make-item-completely-visible)
1123 (run-hooks 'newsticker-narrow-hook))
1124
1125(defun newsticker-next-feed-available-p ()
1126 "Return t if position is before last feed, nil otherwise."
1127 (save-excursion
1128 (let ((p (point)))
1129 (newsticker--buffer-goto '(feed))
1130 (not (= p (point))))))
1131
1132(defun newsticker-previous-feed-available-p ()
1133 "Return t if position is behind first feed, nil otherwise."
1134 (save-excursion
1135 (let ((p (point)))
1136 (newsticker--buffer-goto '(feed) nil t)
1137 (not (= p (point))))))
1138
1139(defun newsticker-next-item-available-p ()
1140 "Return t if position is before last feed, nil otherwise."
1141 (save-excursion
1142 (catch 'result
1143 (while (< (point) (point-max))
1144 (unless (newsticker--buffer-goto '(item))
1145 (throw 'result nil))
1146 (unless (newsticker--lists-intersect-p
1147 (get-text-property (point) 'invisible)
1148 buffer-invisibility-spec)
1149 (throw 'result t))))))
1150
1151(defun newsticker-previous-item-available-p ()
1152 "Return t if position is behind first item, nil otherwise."
1153 (save-excursion
1154 (catch 'result
1155 (while (> (point) (point-min))
1156 (unless (newsticker--buffer-goto '(item) nil t)
1157 (throw 'result nil))
1158 (unless (newsticker--lists-intersect-p
1159 (get-text-property (point) 'invisible)
1160 buffer-invisibility-spec)
1161 (throw 'result t))))))
1162
1163(defun newsticker-item-not-old-p ()
1164 "Return t if there is an item at point which is not old, nil otherwise."
1165 (when (get-text-property (point) 'feed)
1166 (save-excursion
1167 (newsticker--buffer-beginning-of-item)
1168 (let ((age (get-text-property (point) 'nt-age)))
1169 (and (memq age '(new immortal obsolete)) t)))))
1170
1171(defun newsticker-item-not-immortal-p ()
1172 "Return t if there is an item at point which is not immortal, nil otherwise."
1173 (when (get-text-property (point) 'feed)
1174 (save-excursion
1175 (newsticker--buffer-beginning-of-item)
1176 (let ((age (get-text-property (point) 'nt-age)))
1177 (and (memq age '(new old obsolete)) t)))))
1178
1179;; ======================================================================
1180;;; Imenu stuff
1181;; ======================================================================
1182(defun newsticker--imenu-create-index ()
1183 "Scan newsticker buffer and return an index for imenu."
1184 (save-excursion
1185 (goto-char (point-min))
1186 (let ((index-alist nil)
1187 (feed-list nil)
1188 (go-ahead t))
1189 (while go-ahead
1190 (let ((type (get-text-property (point) 'nt-type))
1191 (title (get-text-property (point) 'nt-title)))
1192 (cond ((eq type 'feed)
1193 ;; we're on a feed heading
1194 (when feed-list
1195 (if index-alist
1196 (nconc index-alist (list feed-list))
1197 (setq index-alist (list feed-list))))
1198 (setq feed-list (list title)))
1199 (t
1200 (nconc feed-list
1201 (list (cons title (point)))))))
1202 (setq go-ahead (newsticker--buffer-goto '(item feed))))
1203 (if index-alist
1204 (nconc index-alist (list feed-list))
1205 (setq index-alist (list feed-list)))
1206 index-alist)))
1207
1208(defun newsticker--imenu-goto (name pos &rest args)
1209 "Go to item NAME at position POS and show item.
1210ARGS are ignored."
1211 (goto-char pos)
1212 ;; show headline
1213 (newsticker--buffer-goto '(desc extra feed item))
1214 (let* ((inhibit-read-only t)
1215 (pos1 (max (point-min) (1- pos)))
1216 (pos2 (max pos1 (1- (point))))
1217 (inv-prop (get-text-property pos 'invisible))
1218 (org-inv-prop (get-text-property pos 'org-invisible)))
1219 (when (eq org-inv-prop nil)
1220 (add-text-properties pos1 pos2 (list 'invisible nil
1221 'org-invisible inv-prop))))
1222 ;; show desc
1223 (newsticker-show-entry))
1224
1225;; ======================================================================
1226;;; Buffer stuff
1227;; ======================================================================
1228(defun newsticker--buffer-set-uptodate (value)
1229 "Set the uptodate-status of the newsticker buffer to VALUE.
1230The mode-line is changed accordingly."
1231 (setq newsticker--buffer-uptodate-p value)
1232 (let ((b (get-buffer "*newsticker*")))
1233 (when b
1234 (save-excursion
1235 (set-buffer b)
1236 (if value
1237 (setq mode-name "Newsticker -- up to date -- ")
1238 (setq mode-name "Newsticker -- NEED UPDATE -- ")))
1239 (force-mode-line-update 0))))
1240
1241(defun newsticker--buffer-redraw ()
1242 "Redraw the newsticker window."
1243 (if (fboundp 'force-window-update)
1244 (force-window-update (current-buffer))
1245 (redraw-frame (selected-frame)))
1246 (run-hooks 'newsticker-buffer-change-hook)
1247 (sit-for 0))
1248
1249(defun newsticker--buffer-insert-all-items ()
1250 "Insert all cached newsticker items into the current buffer.
1251Keeps order of feeds as given in `newsticker-url-list' and
1252`newsticker-url-list-defaults'."
1253 (goto-char (point-min))
1254 (mapc (lambda (url-item)
1255 (let* ((feed-name (car url-item))
1256 (feed-name-symbol (intern feed-name))
1257 (feed (assoc feed-name-symbol newsticker--cache))
1258 (items (cdr feed))
1259 (pos (point)))
1260 (when feed
1261 ;; insert the feed description
1262 (mapc (lambda (item)
1263 (when (eq (newsticker--age item) 'feed)
1264 (newsticker--buffer-insert-item item
1265 feed-name-symbol)))
1266 items)
1267 ;;insert the items
1268 (mapc (lambda (item)
1269 (if (memq (newsticker--age item) '(new immortal old
1270 obsolete))
1271 (newsticker--buffer-insert-item item
1272 feed-name-symbol)))
1273 items)
1274 (put-text-property pos (point) 'feed (car feed))
1275
1276 ;; insert empty line between feeds
1277 (let ((p (point)))
1278 (insert "\n")
1279 (put-text-property p (point) 'hard t)))))
1280 (append newsticker-url-list newsticker-url-list-defaults))
1281
1282 (newsticker--buffer-set-faces (point-min) (point-max))
1283 (newsticker--buffer-set-invisibility (point-min) (point-max))
1284 (goto-char (point-min)))
1285
1286(defun newsticker--buffer-insert-item (item &optional feed-name-symbol)
1287 "Insert a news item in the current buffer.
1288Insert a formatted representation of the ITEM. The optional parameter
1289FEED-NAME-SYMBOL determines how the item is formatted and whether the
1290item-retrieval time is added as well."
1291 ;; insert headline
1292 (if (eq (newsticker--age item) 'feed)
1293 (newsticker--buffer-do-insert-text item 'feed feed-name-symbol)
1294 (newsticker--buffer-do-insert-text item 'item feed-name-symbol))
1295 ;; insert the description
1296 (newsticker--buffer-do-insert-text item 'desc feed-name-symbol))
1297
1298(defun newsticker--buffer-do-insert-text (item type feed-name-symbol)
1299 "Actually insert contents of news item, format it, render it and all that.
1300ITEM is a news item, TYPE tells which part of the item shall be inserted,
1301FEED-NAME-SYMBOL tells to which feed this item belongs."
1302 (let* ((pos (point))
1303 (format newsticker-desc-format)
1304 (pos-date-start nil)
1305 (pos-date-end nil)
1306 (pos-stat-start nil)
1307 (pos-stat-end nil)
1308 (pos-text-start nil)
1309 (pos-text-end nil)
1310 (pos-extra-start nil)
1311 (pos-extra-end nil)
1312 (pos-enclosure-start nil)
1313 (pos-enclosure-end nil)
1314 (age (newsticker--age item))
1315 (preformatted-contents (newsticker--preformatted-contents item))
1316 (preformatted-title (newsticker--preformatted-title item)))
1317 (cond ((and preformatted-contents
1318 (not (eq (aref preformatted-contents 0) ?\n));; we must
1319 ;; NOT have a line
1320 ;; break!
1321 (eq type 'desc))
1322 (insert preformatted-contents))
1323 ((and preformatted-title
1324 (not (eq (aref preformatted-title 0) ?\n));; we must NOT have a
1325 ;; line break!
1326 (eq type 'item))
1327 (insert preformatted-title))
1328 (t
1329 ;; item was not formatted before.
1330 ;; Let's go.
1331 (if (eq type 'item)
1332 (setq format newsticker-item-format)
1333 (if (eq type 'feed)
1334 (setq format newsticker-heading-format)))
1335
1336 (while (> (length format) 0)
1337 (let ((prefix (if (> (length format) 1)
1338 (substring format 0 2)
1339 "")))
1340 (cond ((string= "%c" prefix)
1341 ;; contents
1342 (when (newsticker--desc item)
1343 (setq pos-text-start (point-marker))
1344 (insert (newsticker--desc item))
1345 (setq pos-text-end (point-marker)))
1346 (setq format (substring format 2)))
1347 ((string= "%d" prefix)
1348 ;; date
1349 (setq pos-date-start (point-marker))
1350 (if (newsticker--time item)
1351 (insert (format-time-string newsticker-date-format
1352 (newsticker--time item))))
1353 (setq pos-date-end (point-marker))
1354 (setq format (substring format 2)))
1355 ((string= "%l" prefix)
1356 ;; logo
1357 (let ((disabled (cond ((eq (newsticker--age item) 'feed)
1358 (= (newsticker--stat-num-items
1359 feed-name-symbol 'new) 0))
1360 (t
1361 (not (eq (newsticker--age item)
1362 'new))))))
1363 (let ((img (newsticker--image-read feed-name-symbol
1364 disabled)))
1365 (when img
1366 (newsticker--insert-image img (car item)))))
1367 (setq format (substring format 2)))
1368 ((string= "%L" prefix)
1369 ;; logo or title
1370 (let ((disabled (cond ((eq (newsticker--age item) 'feed)
1371 (= (newsticker--stat-num-items
1372 feed-name-symbol 'new) 0))
1373 (t
1374 (not (eq (newsticker--age item)
1375 'new))))))
1376 (let ((img (newsticker--image-read feed-name-symbol
1377 disabled)))
1378 (if img
1379 (newsticker--insert-image img (car item))
1380 (when (car item)
1381 (setq pos-text-start (point-marker))
1382 (if (eq (newsticker--age item) 'feed)
1383 (insert (newsticker--title item))
1384 ;; FIXME: This is not the "real" title!
1385 (insert (format "%s"
1386 (car (newsticker--cache-get-feed
1387 feed-name-symbol)))))
1388 (setq pos-text-end (point-marker))))))
1389 (setq format (substring format 2)))
1390 ((string= "%s" prefix)
1391 ;; statistics
1392 (setq pos-stat-start (point-marker))
1393 (if (eq (newsticker--age item) 'feed)
1394 (insert (newsticker--buffer-statistics
1395 feed-name-symbol)))
1396 (setq pos-stat-end (point-marker))
1397 (setq format (substring format 2)))
1398 ((string= "%t" prefix)
1399 ;; title
1400 (when (car item)
1401 (setq pos-text-start (point-marker))
1402 (insert (car item))
1403 (setq pos-text-end (point-marker)))
1404 (setq format (substring format 2)))
1405 ((string-match "%." prefix)
1406 ;; unknown specifier!
1407 (insert prefix)
1408 (setq format (substring format 2)))
1409 ((string-match "^\\([^%]*\\)\\(.*\\)" format) ;; FIXME!
1410 ;; everything else
1411 (let ((p (point)))
1412 (insert (substring format
1413 (match-beginning 1) (match-end 1)))
1414 ;; in case that the format string contained newlines
1415 (put-text-property p (point) 'hard t))
1416 (setq format (substring format (match-beginning 2)))))))
1417
1418 ;; decode HTML if possible...
1419 (let ((is-rendered-HTML nil))
1420 (when (and newsticker-html-renderer pos-text-start pos-text-end)
1421 (condition-case error-data
1422 (save-excursion
1423 ;; check whether it is necessary to call html renderer
1424 ;; (regexp inspired by htmlr.el)
1425 (goto-char pos-text-start)
1426 (when (re-search-forward
1427 "</?[A-Za-z1-6]*\\|&#?[A-Za-z0-9]+;" pos-text-end t)
1428 ;; (message "%s" (newsticker--title item))
1429 (let ((w3m-fill-column (if newsticker-use-full-width
1430 -1 fill-column))
1431 (w3-maximum-line-length
1432 (if newsticker-use-full-width nil fill-column)))
1433 (save-excursion
1434 (funcall newsticker-html-renderer pos-text-start
1435 pos-text-end)))
1436 (cond ((eq newsticker-html-renderer 'w3m-region)
1437 (add-text-properties pos (point-max)
1438 (list 'keymap
1439 w3m-minor-mode-map)))
1440 ((eq newsticker-html-renderer 'w3-region)
1441 (add-text-properties pos (point-max)
1442 (list 'keymap w3-mode-map))))
1443 (setq is-rendered-HTML t)))
1444 (error
1445 (message "Error: HTML rendering failed: %s, %s"
1446 (car error-data) (cdr error-data)))))
1447 ;; After html rendering there might be chunks of blank
1448 ;; characters between rendered text and date, statistics or
1449 ;; whatever. Remove it
1450 (when (and (eq type 'item) is-rendered-HTML)
1451 (goto-char pos)
1452 (while (re-search-forward "[ \t]*\n[ \t]*" nil t)
1453 (replace-match " " nil nil))
1454 (goto-char (point-max)))
1455 (when (and newsticker-justification
1456 (memq type '(item desc))
1457 (not is-rendered-HTML))
1458 (condition-case nil
1459 (let ((use-hard-newlines t))
1460 (fill-region pos (point-max) newsticker-justification))
1461 (error nil))))
1462
1463 ;; remove leading and trailing newlines
1464 (goto-char pos)
1465 (unless (= 0 (skip-chars-forward " \t\r\n"))
1466 (delete-region pos (point)))
1467 (goto-char (point-max))
1468 (let ((end (point)))
1469 (unless (= 0 (skip-chars-backward " \t\r\n" (1+ pos)))
1470 (delete-region (point) end)))
1471 (goto-char (point-max))
1472 ;; closing newline
1473 (unless nil ;;(eq pos (point))
1474 (insert "\n")
1475 (put-text-property (1- (point)) (point) 'hard t))
1476
1477 ;; insert enclosure element
1478 (when (eq type 'desc)
1479 (setq pos-enclosure-start (point))
1480 (newsticker--insert-enclosure item newsticker--url-keymap)
1481 (setq pos-enclosure-end (point)))
1482
1483 ;; show extra elements
1484 (when (eq type 'desc)
1485 (goto-char (point-max))
1486 (setq pos-extra-start (point))
1487 (newsticker--print-extra-elements item newsticker--url-keymap)
1488 (setq pos-extra-end (point)))
1489
1490 ;; text properties
1491 (when (memq type '(feed item))
1492 (add-text-properties pos (1- (point))
1493 (list 'mouse-face 'highlight
1494 'nt-link (newsticker--link item)
1495 'help-echo
1496 (format "mouse-2: visit item (%s)"
1497 (newsticker--link item))
1498 'keymap newsticker--url-keymap))
1499 (add-text-properties pos (point)
1500 (list 'nt-title (newsticker--title item)
1501 'nt-desc (newsticker--desc item))))
1502
1503 (add-text-properties pos (point)
1504 (list 'nt-type type
1505 'nt-face type
1506 'nt-age age
1507 'nt-guid (newsticker--guid item)))
1508 (when (and pos-date-start pos-date-end)
1509 (put-text-property pos-date-start pos-date-end 'nt-face 'date))
1510 (when (and pos-stat-start pos-stat-end)
1511 (put-text-property pos-stat-start pos-stat-end 'nt-face 'stat))
1512 (when (and pos-extra-start pos-extra-end)
1513 (put-text-property pos-extra-start pos-extra-end
1514 'nt-face 'extra)
1515 (put-text-property pos-extra-start pos-extra-end
1516 'nt-type 'extra))
1517 (when (and pos-enclosure-start pos-enclosure-end
1518 (> pos-enclosure-end pos-enclosure-start))
1519 (put-text-property pos-enclosure-start (1- pos-enclosure-end)
1520 'nt-face 'enclosure))
1521
1522 ;; left margin
1523 ;;(unless (memq type '(feed item))
1524 ;;(set-left-margin pos (1- (point)) 1))
1525
1526 ;; save rendered stuff
1527 (cond ((eq type 'desc)
1528 ;; preformatted contents
1529 (newsticker--cache-set-preformatted-contents
1530 item (buffer-substring pos (point))))
1531 ((eq type 'item)
1532 ;; preformatted title
1533 (newsticker--cache-set-preformatted-title
1534 item (buffer-substring pos (point)))))))))
1535
1536(defun newsticker--buffer-statistics (feed-name-symbol)
1537 "Return a statistic string for the feed given by FEED-NAME-SYMBOL.
1538See `newsticker-statistics-format'."
1539 (let ((case-fold-search nil))
1540 (replace-regexp-in-string
1541 "%a"
1542 (format "%d" (newsticker--stat-num-items feed-name-symbol))
1543 (replace-regexp-in-string
1544 "%i"
1545 (format "%d" (newsticker--stat-num-items feed-name-symbol 'immortal))
1546 (replace-regexp-in-string
1547 "%n"
1548 (format "%d" (newsticker--stat-num-items feed-name-symbol 'new))
1549 (replace-regexp-in-string
1550 "%o"
1551 (format "%d" (newsticker--stat-num-items feed-name-symbol 'old))
1552 (replace-regexp-in-string
1553 "%O"
1554 (format "%d" (newsticker--stat-num-items feed-name-symbol 'obsolete))
1555 newsticker-statistics-format)))))))
1556
1557(defun newsticker--buffer-set-faces (start end)
1558 "Add face properties according to mark property.
1559Scans the buffer between START and END."
1560 (save-excursion
1561 (put-text-property start end 'face 'newsticker-default-face)
1562 (goto-char start)
1563 (let ((pos1 start)
1564 (pos2 1)
1565 (nt-face (get-text-property start 'nt-face))
1566 (nt-age (get-text-property start 'nt-age)))
1567 (when nt-face
1568 (setq pos2 (next-single-property-change (point) 'nt-face))
1569 (newsticker--set-face-properties pos1 pos2 nt-face nt-age)
1570 (setq nt-face (get-text-property pos2 'nt-face))
1571 (setq pos1 pos2))
1572 (while (and (setq pos2 (next-single-property-change pos1 'nt-face))
1573 (<= pos2 end)
1574 (> pos2 pos1))
1575 (newsticker--set-face-properties pos1 pos2 nt-face nt-age)
1576 (setq nt-face (get-text-property pos2 'nt-face))
1577 (setq nt-age (get-text-property pos2 'nt-age))
1578 (setq pos1 pos2)))))
1579
1580(defun newsticker--buffer-set-invisibility (start end)
1581 "Add invisibility properties according to nt-type property.
1582Scans the buffer between START and END. Sets the 'invisible
1583property to '(<nt-type>-<nt-age> <nt-type> <nt-age>)."
1584 (save-excursion
1585 ;; reset invisibility settings
1586 (put-text-property start end 'invisible nil)
1587 ;; let's go
1588 (goto-char start)
1589 (let ((pos1 start)
1590 (pos2 1)
1591 (nt-type (get-text-property start 'nt-type))
1592 (nt-age (get-text-property start 'nt-age)))
1593 (when nt-type
1594 (setq pos2 (next-single-property-change (point) 'nt-type))
1595 (put-text-property (max (point-min) pos1) (1- pos2)
1596 'invisible
1597 (list (intern
1598 (concat
1599 (symbol-name
1600 (if (eq nt-type 'extra) 'desc nt-type))
1601 "-"
1602 (symbol-name nt-age)))
1603 nt-type
1604 nt-age))
1605 (setq nt-type (get-text-property pos2 'nt-type))
1606 (setq pos1 pos2))
1607 (while (and (setq pos2 (next-single-property-change pos1 'nt-type))
1608 (<= pos2 end)
1609 (> pos2 pos1))
1610 ;; must shift one char to the left in order to handle inivisible
1611 ;; newlines, motion in invisible text areas and all that correctly
1612 (put-text-property (1- pos1) (1- pos2)
1613 'invisible
1614 (list (intern
1615 (concat
1616 (symbol-name
1617 (if (eq nt-type 'extra) 'desc nt-type))
1618 "-"
1619 (symbol-name nt-age)))
1620 nt-type
1621 nt-age))
1622 (setq nt-type (get-text-property pos2 'nt-type))
1623 (setq nt-age (get-text-property pos2 'nt-age))
1624 (setq pos1 pos2)))))
1625
1626(defun newsticker--set-face-properties (pos1 pos2 nt-face age)
1627 "Set the face for the text between the positions POS1 and POS2.
1628The face is chosen according the values of NT-FACE and AGE."
1629 (let ((face (cond ((eq nt-face 'feed)
1630 'newsticker-feed-face)
1631 ((eq nt-face 'item)
1632 (cond ((eq age 'new)
1633 'newsticker-new-item-face)
1634 ((eq age 'old)
1635 'newsticker-old-item-face)
1636 ((eq age 'immortal)
1637 'newsticker-immortal-item-face)
1638 ((eq age 'obsolete)
1639 'newsticker-obsolete-item-face)))
1640 ((eq nt-face 'date)
1641 'newsticker-date-face)
1642 ((eq nt-face 'stat)
1643 'newsticker-statistics-face)
1644 ((eq nt-face 'extra)
1645 'newsticker-extra-face)
1646 ((eq nt-face 'enclosure)
1647 'newsticker-enclosure-face))))
1648 (when face
1649 (put-text-property pos1 (max pos1 pos2) 'face face))))
1650
1651;; ======================================================================
1652;;; Functions working on the *newsticker* buffer
1653;; ======================================================================
1654(defun newsticker--buffer-make-item-completely-visible ()
1655 "Scroll buffer until current item is completely visible."
1656 (when newsticker--auto-narrow-to-feed
1657 (let* ((min (or (save-excursion (newsticker--buffer-beginning-of-feed))
1658 (point-min)))
1659 (max (or (save-excursion (newsticker--buffer-end-of-feed))
1660 (point-max))))
1661 (narrow-to-region min max)))
1662 (when newsticker--auto-narrow-to-item
1663 (let* ((min (or (save-excursion (newsticker--buffer-beginning-of-item))
1664 (point-min)))
1665 (max (or (save-excursion (newsticker--buffer-end-of-item))
1666 (point-max))))
1667 (narrow-to-region min max)))
1668 (sit-for 0)
1669 ;; do not count lines and stuff because that does not work when images
1670 ;; are displayed. Do it the simple way:
1671 (save-excursion
1672 (newsticker--buffer-end-of-item)
1673 (unless (pos-visible-in-window-p)
1674 (recenter -1)))
1675 (unless (pos-visible-in-window-p)
1676 (recenter 0)))
1677
1678(defun newsticker--buffer-get-feed-title-at-point ()
1679 "Return feed symbol of headline at point."
1680 (format "%s" (or (get-text-property (point) 'feed) " ")))
1681
1682(defun newsticker--buffer-get-item-title-at-point ()
1683 "Return feed symbol of headline at point."
1684 (format "%s" (or (get-text-property (point) 'nt-title) " ")))
1685
1686(defun newsticker--buffer-goto (types &optional age backwards)
1687 "Search next occurrence of TYPES in current buffer.
1688TYPES is a list of symbols. If TYPES is found point is moved, if
1689not point is left unchanged. If optional parameter AGE is not
1690nil, the type AND the age must match. If BACKWARDS is t, search
1691backwards."
1692 (let ((pos (save-excursion
1693 (save-restriction
1694 (widen)
1695 (catch 'found
1696 (let ((tpos (point)))
1697 (while (setq tpos
1698 (if backwards
1699 (if (eq tpos (point-min))
1700 nil
1701 (or (previous-single-property-change
1702 tpos 'nt-type)
1703 (point-min)))
1704 (next-single-property-change
1705 tpos 'nt-type)))
1706 (and (memq (get-text-property tpos 'nt-type) types)
1707 (or (not age)
1708 (eq (get-text-property tpos 'nt-age) age))
1709 (throw 'found tpos)))))))))
1710 (when pos
1711 (goto-char pos))
1712 pos))
1713
1714(defun newsticker--buffer-hideshow (mark-age onoff)
1715 "Hide or show items of type MARK-AGE.
1716If ONOFF is nil the item is hidden, otherwise it is shown."
1717 (if onoff
1718 (remove-from-invisibility-spec mark-age)
1719 (add-to-invisibility-spec mark-age)))
1720
1721(defun newsticker--buffer-beginning-of-item ()
1722 "Move point to the beginning of the item at point.
1723Return new position."
1724 (if (bobp)
1725 (point)
1726 (let ((type (get-text-property (point) 'nt-type))
1727 (typebefore (get-text-property (1- (point)) 'nt-type)))
1728 (if (and (memq type '(item feed))
1729 (not (eq type typebefore)))
1730 (point)
1731 (newsticker--buffer-goto '(item feed) nil t)
1732 (point)))))
1733
1734(defun newsticker--buffer-beginning-of-feed ()
1735 "Move point to the beginning of the feed at point.
1736Return new position."
1737 (if (bobp)
1738 (point)
1739 (let ((type (get-text-property (point) 'nt-type))
1740 (typebefore (get-text-property (1- (point)) 'nt-type)))
1741 (if (and (memq type '(feed))
1742 (not (eq type typebefore)))
1743 (point)
1744 (newsticker--buffer-goto '(feed) nil t)
1745 (point)))))
1746
1747(defun newsticker--buffer-end-of-item ()
1748 "Move point to the end of the item at point.
1749Take care: end of item is at the end of its last line!"
1750 (when (newsticker--buffer-goto '(item feed nil))
1751 (point)))
1752
1753(defun newsticker--buffer-end-of-feed ()
1754 "Move point to the end of the last item of the feed at point.
1755Take care: end of item is at the end of its last line!"
1756 (when (newsticker--buffer-goto '(feed nil))
1757 (backward-char 1)
1758 (point)))
1759
1760;; ======================================================================
1761;;; misc
1762;; ======================================================================
1763
1764(defun newsticker-mouse-browse-url (event)
1765 "Call `browse-url' for the link of the item at which the EVENT occurred."
1766 (interactive "e")
1767 (save-excursion
1768 (switch-to-buffer (window-buffer (posn-window (event-end event))))
1769 (let ((url (get-text-property (posn-point (event-end event))
1770 'nt-link)))
1771 (when url
1772 (browse-url url)
1773 (save-excursion
1774 (goto-char (posn-point (event-end event)))
1775 (if newsticker-automatically-mark-visited-items-as-old
1776 (newsticker-mark-item-at-point-as-read t)))))))
1777
1778(defun newsticker-browse-url ()
1779 "Call `browse-url' for the link of the item at point."
1780 (interactive)
1781 (let ((url (get-text-property (point) 'nt-link)))
1782 (when url
1783 (browse-url url)
1784 (if newsticker-automatically-mark-visited-items-as-old
1785 (newsticker-mark-item-at-point-as-read t)))))
1786
1787(defvar newsticker-open-url-history
1788 '("wget" "xmms" "realplay")
1789 "...")
1790
1791(defun newsticker-handle-url ()
1792 "Ask for a program to open the link of the item at point."
1793 (interactive)
1794 (let ((url (get-text-property (point) 'nt-link)))
1795 (when url
1796 (let ((prog (read-string "Open url with: " nil
1797 'newsticker-open-url-history)))
1798 (when prog
1799 (message "%s %s" prog url)
1800 (start-process prog prog prog url)
1801 (if newsticker-automatically-mark-visited-items-as-old
1802 (newsticker-mark-item-at-point-as-read t)))))))
1803
1804
1805;; ======================================================================
1806;;; Misc
1807;; ======================================================================
1808
1809(defun newsticker--cache-sort ()
1810 "Sort the newsticker cache data."
1811 (let ((sort-fun (cond ((eq newsticker-sort-method 'sort-by-time)
1812 'newsticker--cache-item-compare-by-time)
1813 ((eq newsticker-sort-method 'sort-by-title)
1814 'newsticker--cache-item-compare-by-title)
1815 ((eq newsticker-sort-method 'sort-by-original-order)
1816 'newsticker--cache-item-compare-by-position))))
1817 (mapc (lambda (feed-list)
1818 (setcdr feed-list (sort (cdr feed-list)
1819 sort-fun)))
1820 newsticker--cache)))
1821
1822(provide 'newsticker-plainview)
1823;;; newsticker-plainview.el ends here