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