Update copyright year to 2014 by running admin/update-copyright.
[emacs.git] / lisp / net / newst-ticker.el
blobbf0e3981824921ff9536945be69511225904f32f
1 ;; newst-ticker.el --- mode line ticker for newsticker.
3 ;; Copyright (C) 2003-2014 Free Software Foundation, Inc.
5 ;; Author: Ulf Jasper <ulf.jasper@web.de>
6 ;; Filename: newst-ticker.el
7 ;; URL: http://www.nongnu.org/newsticker
8 ;; Keywords: News, RSS, Atom
9 ;; Time-stamp: "6. Dezember 2009, 19:16:00 (ulf)"
10 ;; Package: newsticker
12 ;; ======================================================================
14 ;; This file is part of GNU Emacs.
16 ;; GNU Emacs is free software: you can redistribute it and/or modify
17 ;; it under the terms of the GNU General Public License as published by
18 ;; the Free Software Foundation, either version 3 of the License, or
19 ;; (at your option) any later version.
21 ;; GNU Emacs is distributed in the hope that it will be useful,
22 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 ;; GNU General Public License for more details.
26 ;; You should have received a copy of the GNU General Public License
27 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
29 ;; ======================================================================
31 ;;; Commentary:
33 ;; See newsticker.el
35 ;; ======================================================================
36 ;;; Code:
38 (require 'newst-backend)
40 (defvar newsticker--ticker-timer nil
41 "Timer for newsticker ticker.")
43 ;;;###autoload
44 (defun newsticker-ticker-running-p ()
45 "Check whether newsticker's actual ticker is running.
46 Return t if ticker is running, nil otherwise. Newsticker is
47 considered to be running if the newsticker timer list is not
48 empty."
49 (timerp newsticker--ticker-timer))
51 ;; customization group ticker
52 (defgroup newsticker-ticker nil
53 "Settings for the headline ticker."
54 :group 'newsticker)
56 (defun newsticker--set-customvar-ticker (symbol value)
57 "Set newsticker-variable SYMBOL value to VALUE.
58 Calls all actions which are necessary in order to make the new
59 value effective."
60 (if (or (not (boundp symbol))
61 (equal (symbol-value symbol) value))
62 (set symbol value)
63 ;; something must have changed -- restart ticker
64 (when (newsticker-running-p)
65 (message "Restarting ticker")
66 (newsticker-stop-ticker)
67 (newsticker--ticker-text-setup)
68 (newsticker-start-ticker)
69 (message ""))))
71 (defcustom newsticker-ticker-interval
72 0.3
73 "Time interval for displaying news items in the echo area (seconds).
74 If equal or less than 0 no messages are shown in the echo area. For
75 smooth display (see `newsticker-scroll-smoothly') a value of 0.3 seems
76 reasonable. For non-smooth display a value of 10 is a good starting
77 point."
78 :type 'number
79 :set 'newsticker--set-customvar-ticker
80 :group 'newsticker-ticker)
82 (defcustom newsticker-scroll-smoothly
84 "Decides whether to flash or scroll news items.
85 If t the news headlines are scrolled (more-or-less) smoothly in the echo
86 area. If nil one headline after another is displayed in the echo area.
87 The variable `newsticker-ticker-interval' determines how fast this
88 display moves/changes and whether headlines are shown in the echo area
89 at all. If you change `newsticker-scroll-smoothly' you should also change
90 `newsticker-ticker-interval'."
91 :type 'boolean
92 :group 'newsticker-ticker)
94 (defcustom newsticker-hide-immortal-items-in-echo-area
96 "Decides whether to show immortal/non-expiring news items in the ticker.
97 If t the echo area will not show immortal items. See also
98 `newsticker-hide-old-items-in-echo-area'."
99 :type 'boolean
100 :set 'newsticker--set-customvar-ticker
101 :group 'newsticker-ticker)
103 (defcustom newsticker-hide-old-items-in-echo-area
105 "Decides whether to show only the newest news items in the ticker.
106 If t the echo area will show only new items, i.e. only items which have
107 been added between the last two retrievals."
108 :type 'boolean
109 :set 'newsticker--set-customvar-ticker
110 :group 'newsticker-ticker)
112 (defcustom newsticker-hide-obsolete-items-in-echo-area
114 "Decides whether to show obsolete items items in the ticker.
115 If t the echo area will not show obsolete items. See also
116 `newsticker-hide-old-items-in-echo-area'."
117 :type 'boolean
118 :set 'newsticker--set-customvar-ticker
119 :group 'newsticker-ticker)
121 (defun newsticker--display-tick ()
122 "Called from the display timer.
123 This function calls a display function, according to the variable
124 `newsticker-scroll-smoothly'."
125 (if newsticker-scroll-smoothly
126 (newsticker--display-scroll)
127 (newsticker--display-jump)))
129 (defsubst newsticker--echo-area-clean-p ()
130 "Check whether somebody is using the echo area / minibuffer.
131 Return t if echo area and minibuffer are unused."
132 (not (or (active-minibuffer-window)
133 (and (current-message)
134 (not (string= (current-message)
135 newsticker--prev-message))))))
137 (defun newsticker--display-jump ()
138 "Called from the display timer.
139 This function displays the next ticker item in the echo area, unless
140 there is another message displayed or the minibuffer is active."
141 (let ((message-log-max nil));; prevents message text from being logged
142 (when (newsticker--echo-area-clean-p)
143 (setq newsticker--item-position (1+ newsticker--item-position))
144 (when (>= newsticker--item-position (length newsticker--item-list))
145 (setq newsticker--item-position 0))
146 (setq newsticker--prev-message
147 (nth newsticker--item-position newsticker--item-list))
148 (message "%s" newsticker--prev-message))))
150 (defun newsticker--display-scroll ()
151 "Called from the display timer.
152 This function scrolls the ticker items in the echo area, unless
153 there is another message displayed or the minibuffer is active."
154 (when (newsticker--echo-area-clean-p)
155 (let* ((width (- (frame-width) 1))
156 (message-log-max nil);; prevents message text from being logged
157 (i newsticker--item-position)
158 subtext
159 (s-text newsticker--scrollable-text)
160 (l (length s-text)))
161 ;; don't show anything if there is nothing to show
162 (unless (< (length s-text) 1)
163 ;; repeat the ticker string if it is shorter than frame width
164 (while (< (length s-text) width)
165 (setq s-text (concat s-text s-text)))
166 ;; get the width of the printed string
167 (setq l (length s-text))
168 (cond ((< i (- l width))
169 (setq subtext (substring s-text i (+ i width))))
171 (setq subtext (concat
172 (substring s-text i l)
173 (substring s-text 0 (- width (- l i)))))))
174 ;; Take care of multibyte strings, for which (string-width) is
175 ;; larger than (length).
176 ;; Actually, such strings may be smaller than (frame-width)
177 ;; because return values of (string-width) are too large:
178 ;; (string-width "<japanese character>") => 2
179 (let ((t-width (1- (length subtext))))
180 (while (> (string-width subtext) width)
181 (setq subtext (substring subtext 0 t-width))
182 (setq t-width (1- t-width))))
183 ;; show the ticker text and save current position
184 (message "%s" subtext)
185 (setq newsticker--prev-message subtext)
186 (setq newsticker--item-position (1+ i))
187 (when (>= newsticker--item-position l)
188 (setq newsticker--item-position 0))))))
190 ;;;###autoload
191 (defun newsticker-start-ticker ()
192 "Start newsticker's ticker (but not the news retrieval).
193 Start display timer for the actual ticker if wanted and not
194 running already."
195 (interactive)
196 (if (and (> newsticker-ticker-interval 0)
197 (not newsticker--ticker-timer))
198 (setq newsticker--ticker-timer
199 (run-at-time newsticker-ticker-interval
200 newsticker-ticker-interval
201 'newsticker--display-tick))))
203 (defun newsticker-stop-ticker ()
204 "Stop newsticker's ticker (but not the news retrieval)."
205 (interactive)
206 (when newsticker--ticker-timer
207 (cancel-timer newsticker--ticker-timer)
208 (setq newsticker--ticker-timer nil)))
210 ;; ======================================================================
211 ;;; Manipulation of ticker text
212 ;; ======================================================================
213 (defun newsticker--ticker-text-setup ()
214 "Build the ticker text which is scrolled or flashed in the echo area."
215 ;; reset scrollable text
216 (setq newsticker--scrollable-text "")
217 (setq newsticker--item-list nil)
218 (setq newsticker--item-position 0)
219 ;; build scrollable text from cache data
220 (let ((have-something nil))
221 (mapc
222 (lambda (feed)
223 (let ((feed-name (symbol-name (car feed))))
224 (let ((num-new (newsticker--stat-num-items (car feed) 'new))
225 (num-old (newsticker--stat-num-items (car feed) 'old))
226 (num-imm (newsticker--stat-num-items (car feed) 'immortal))
227 (num-obs (newsticker--stat-num-items (car feed) 'obsolete)))
228 (when (or (> num-new 0)
229 (and (> num-old 0)
230 (not newsticker-hide-old-items-in-echo-area))
231 (and (> num-imm 0)
232 (not newsticker-hide-immortal-items-in-echo-area))
233 (and (> num-obs 0)
234 (not newsticker-hide-obsolete-items-in-echo-area)))
235 (setq have-something t)
236 (mapc
237 (lambda (item)
238 (let ((title (replace-regexp-in-string
239 "[\r\n]+" " "
240 (newsticker--title item)))
241 (age (newsticker--age item)))
242 (unless (string= title newsticker--error-headline)
243 (when
244 (or (eq age 'new)
245 (and (eq age 'old)
246 (not newsticker-hide-old-items-in-echo-area))
247 (and (eq age 'obsolete)
248 (not
249 newsticker-hide-obsolete-items-in-echo-area))
250 (and (eq age 'immortal)
251 (not
252 newsticker-hide-immortal-items-in-echo-area)))
253 (setq title (newsticker--remove-whitespace title))
254 ;; add to flash list
255 (add-to-list 'newsticker--item-list
256 (concat feed-name ": " title) t)
257 ;; and to the scrollable text
258 (setq newsticker--scrollable-text
259 (concat newsticker--scrollable-text
260 " " feed-name ": " title " +++"))))))
261 (cdr feed))))))
262 newsticker--cache)
263 (when have-something
264 (setq newsticker--scrollable-text
265 (concat "+++ "
266 (format-time-string "%A, %H:%M"
267 newsticker--latest-update-time)
268 " ++++++" newsticker--scrollable-text)))))
270 (defun newsticker--ticker-text-remove (feed title)
271 "Remove the item of FEED with TITLE from the ticker text."
272 ;; reset scrollable text
273 (setq newsticker--item-position 0)
274 (let ((feed-name (symbol-name feed))
275 (t-title (replace-regexp-in-string "[\r\n]+" " " title)))
276 ;; remove from flash list
277 (setq newsticker--item-list (remove (concat feed-name ": " t-title)
278 newsticker--item-list))
279 ;; and from the scrollable text
280 (setq newsticker--scrollable-text
281 (replace-regexp-in-string
282 (regexp-quote (concat " " feed-name ": " t-title " +++"))
284 newsticker--scrollable-text))
285 (if (string-match (concat "^\\+\\+\\+ [A-Z][a-z]+, "
286 "[012]?[0-9]:[0-9][0-9] \\+\\+\\+\\+\\+\\+$")
287 newsticker--scrollable-text)
288 (setq newsticker--scrollable-text ""))))
290 (provide 'newst-ticker)
292 ;;; newst-ticker.el ends here