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