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