(handle-shift-selection): Fix last change.
[bpt/emacs.git] / lisp / time.el
CommitLineData
b7eb6e8a 1;;; time.el --- display time, load and mail indicator in mode line of Emacs -*-coding: utf-8 -*-
c2acf685 2
0d30b337 3;; Copyright (C) 1985, 1986, 1987, 1993, 1994, 1996, 2000, 2001, 2002,
ae940284 4;; 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
c2acf685
SM
5
6;; Maintainer: FSF
7
8;; This file is part of GNU Emacs.
9
eb3fa2cf 10;; GNU Emacs is free software: you can redistribute it and/or modify
c2acf685 11;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
c2acf685
SM
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
eb3fa2cf 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
c2acf685
SM
22
23;;; Commentary:
24
25;; Facilities to display current time/date and a new-mail indicator
aff2ba04
RS
26;; in the Emacs mode line. The entry point is `display-time'.
27
28;; Display time world in a buffer, the entry point is
29;; `display-time-world'.
c2acf685
SM
30
31;;; Code:
32
33(defgroup display-time nil
34 "Display time and load in mode line of Emacs."
97904e51 35 :group 'mode-line
c2acf685
SM
36 :group 'mail)
37
38
c2acf685 39(defcustom display-time-mail-file nil
9201cc28 40 "File name of mail inbox file, for indicating existence of new mail.
cbb7527f 41Non-nil and not a string means don't check for mail; nil means use
c2acf685 42default, which is system-dependent, and is the same as used by Rmail."
fe6456af 43 :type '(choice (const :tag "None" none)
c2acf685
SM
44 (const :tag "Default" nil)
45 (file :format "%v"))
46 :group 'display-time)
47
cc351ed4 48(defcustom display-time-mail-directory nil
9201cc28 49 "Name of mail inbox directory, for indicating existence of new mail.
ad68bb68
RS
50Any nonempty regular file in the directory is regarded as newly arrived mail.
51If nil, do not check a directory for arriving mail."
cc351ed4
PJ
52 :type '(choice (const :tag "None" nil)
53 (directory :format "%v"))
54 :group 'display-time)
55
c2acf685 56(defcustom display-time-mail-function nil
9201cc28 57 "Function to call, for indicating existence of new mail.
ad68bb68
RS
58If nil, that means use the default method: check that the file
59specified by `display-time-mail-file' is nonempty or that the
60directory `display-time-mail-directory' contains nonempty files."
c2acf685
SM
61 :type '(choice (const :tag "Default" nil)
62 (function))
63 :group 'display-time)
64
4b05e68d 65(defcustom display-time-default-load-average 0
9201cc28 66 "Which load average value will be shown in the mode line.
4b05e68d
PJ
67Almost every system can provide values of load for past 1 minute, past 5 or
68past 15 minutes. The default is to display 1 minute load average."
69 :type '(choice (const :tag "1 minute load" 0)
70 (const :tag "5 minutes load" 1)
fe6456af
RS
71 (const :tag "15 minutes load" 2)
72 (const :tag "None" nil))
4b05e68d
PJ
73 :group 'display-time)
74
fe6456af 75(defvar display-time-load-average nil
b7eb6e8a 76 "Load average currently being shown in mode line.")
4b05e68d
PJ
77
78(defcustom display-time-load-average-threshold 0.1
9201cc28 79 "Load-average values below this value won't be shown in the mode line."
b7b4e4d4
PJ
80 :type 'number
81 :group 'display-time)
4b05e68d 82
c2acf685
SM
83;;;###autoload
84(defcustom display-time-day-and-date nil "\
85*Non-nil means \\[display-time] should display day and date as well as time."
86 :type 'boolean
87 :group 'display-time)
88
89(defvar display-time-timer nil)
90
91(defcustom display-time-interval 60
9201cc28 92 "Seconds between updates of time in the mode line."
c2acf685
SM
93 :type 'integer
94 :group 'display-time)
95
96(defcustom display-time-24hr-format nil
9201cc28 97 "Non-nil indicates time should be displayed as hh:mm, 0 <= hh <= 23.
7d326782 98A value of nil means 1 <= hh <= 12, and an AM/PM suffix is used."
c2acf685
SM
99 :type 'boolean
100 :group 'display-time)
101
102(defvar display-time-string nil)
103
104(defcustom display-time-hook nil
9201cc28 105 "List of functions to be called when the time is updated on the mode line."
c2acf685
SM
106 :type 'hook
107 :group 'display-time)
108
109(defvar display-time-server-down-time nil
110 "Time when mail file's file system was recorded to be down.
111If that file system seems to be up, the value is nil.")
112
dca2168c 113(defcustom zoneinfo-style-world-list
aff2ba04
RS
114 '(("America/Los_Angeles" "Seattle")
115 ("America/New_York" "New York")
116 ("Europe/London" "London")
117 ("Europe/Paris" "Paris")
118 ("Asia/Calcutta" "Bangalore")
119 ("Asia/Tokyo" "Tokyo"))
dca2168c
EZ
120 "Alist of zoneinfo-style time zones and places for `display-time-world'.
121Each element has the form (TIMEZONE LABEL).
122TIMEZONE should be a string of the form AREA/LOCATION, where AREA is
123the name of a region -- a continent or ocean, and LOCATION is the name
124of a specific location, e.g., a city, within that region.
125LABEL is a string to display as the label of that TIMEZONE's time."
126 :group 'display-time
127 :type '(repeat (list string string))
128 :version "23.1")
129
130(defcustom legacy-style-world-list
131 '(("PST8PDT" "Seattle")
132 ("EST5EDT" "New York")
9db0c29b 133 ("GMT0BST" "London")
dca2168c 134 ("CET-1CDT" "Paris")
9db0c29b
EZ
135 ("IST-5:30" "Bangalore")
136 ("JST-9" "Tokyo"))
dca2168c
EZ
137 "Alist of traditional-style time zones and places for `display-time-world'.
138Each element has the form (TIMEZONE LABEL).
139TIMEZONE should be a string of the form:
140
141 std[+|-]offset[dst[offset][,date[/time],date[/time]]]
142
143See the documentation of the TZ environment variable on your system,
144for more details about the format of TIMEZONE.
145LABEL is a string to display as the label of that TIMEZONE's time."
146 :group 'display-time
147 :type '(repeat (list string string))
148 :version "23.1")
149
150(defcustom display-time-world-list
36d4b145
JR
151 ;; Determine if zoneinfo style timezones are supported by testing that
152 ;; America/New York and Europe/London return different timezones.
153 (let (gmt nyt)
154 (set-time-zone-rule "America/New York")
155 (setq nyt (format-time-string "%z"))
156 (set-time-zone-rule "Europe/London")
157 (setq gmt (format-time-string "%z"))
158 (set-time-zone-rule nil)
159 (if (string-equal nyt gmt)
160 legacy-style-world-list
161 zoneinfo-style-world-list))
dca2168c 162 "Alist of time zones and places for `display-time-world' to display.
aff2ba04 163Each element has the form (TIMEZONE LABEL).
dca2168c
EZ
164TIMEZONE should be in the format supported by `set-time-zone-rule' on
165your system. See the documentation of `zoneinfo-style-world-list' and
166\`legacy-style-world-list' for two widely used formats.
167LABEL is a string to display as the label of that TIMEZONE's time."
aff2ba04
RS
168 :group 'display-time
169 :type '(repeat (list string string))
170 :version "23.1")
171
658dcd47 172(defcustom display-time-world-time-format "%A %d %B %R %Z"
aff2ba04
RS
173 "Format of the time displayed, see `format-time-string'."
174 :group 'display-time
175 :type 'string
176 :version "23.1")
177
178(defcustom display-time-world-buffer-name "*wclock*"
179 "Name of the wclock buffer."
180 :group 'display-time
181 :type 'string
182 :version "23.1")
183
184(defcustom display-time-world-timer-enable t
185 "If non-nil, a timer will update the world clock."
186 :group 'display-time
187 :type 'boolean
188 :version "23.1")
189
190(defcustom display-time-world-timer-second 60
191 "Interval in seconds for updating the world clock."
192 :group 'display-time
193 :type 'integer
194 :version "23.1")
195
196(defvar display-time-world-mode-map
197 (let ((map (make-sparse-keymap)))
198 (define-key map "q" 'kill-this-buffer)
199 map)
200 "Keymap of Display Time World mode")
201
c2acf685
SM
202;;;###autoload
203(defun display-time ()
204 "Enable display of time, load level, and mail flag in mode lines.
205This display updates automatically every minute.
206If `display-time-day-and-date' is non-nil, the current day and date
207are displayed as well.
208This runs the normal hook `display-time-hook' after each update."
209 (interactive)
210 (display-time-mode 1))
211
b7eb6e8a
DL
212;; This business used to be simpler when all mode lines had the same
213;; face and the image could just be pbm. Now we try to rely on an xpm
214;; image with a transparent background. Otherwise, set the background
215;; for pbm.
216
217(defcustom display-time-mail-face nil
c2acf685 218 "Face to use for `display-time-mail-string'.
b7eb6e8a 219If `display-time-use-mail-icon' is non-nil, the image's
6bd47b39
JB
220background color is the background of this face. Set this to
221make the mail indicator stand out on a color display."
83033a3a 222 :group 'mode-line-faces
c2acf685 223 :group 'display-time
bf247b6e 224 :version "22.1"
b7eb6e8a 225 :type '(choice (const :tag "None" nil) face))
c2acf685
SM
226
227(defvar display-time-mail-icon
cc351ed4 228 (find-image '((:type xpm :file "letter.xpm" :ascent center)
b7eb6e8a
DL
229 (:type pbm :file "letter.pbm" :ascent center)))
230 "Image specification to offer as the mail indicator on a graphic display.
231See `display-time-use-mail-icon' and `display-time-mail-face'.")
c2acf685 232
b7eb6e8a 233;; Fixme: Default to icon on graphical display?
c2acf685 234(defcustom display-time-use-mail-icon nil
d7baab9c 235 "Non-nil means use an icon as mail indicator on a graphic display.
b7eb6e8a
DL
236Otherwise use `display-time-mail-string'. The icon may consume less
237of the mode line. It is specified by `display-time-mail-icon'."
c2acf685
SM
238 :group 'display-time
239 :type 'boolean)
240
b7eb6e8a
DL
241;; Fixme: maybe default to the character if we can display Unicode.
242(defcustom display-time-mail-string "Mail"
243 "String to use as the mail indicator in `display-time-string-forms'.
244This can use the Unicode letter character if you can display it."
245 :group 'display-time
bf247b6e 246 :version "22.1"
b7eb6e8a
DL
247 :type '(choice (const "Mail")
248 ;; Use :tag here because the Lucid menu won't display
249 ;; multibyte text.
250 (const :tag "Unicode letter character" "✉")
251 string))
252
c2acf685 253(defcustom display-time-format nil
9201cc28 254 "String specifying format for displaying the time in the mode line.
c2acf685
SM
255See the function `format-time-string' for an explanation of
256how to write this string. If this is nil, the defaults
257depend on `display-time-day-and-date' and `display-time-24hr-format'."
258 :type '(choice (const :tag "Default" nil)
259 string)
260 :group 'display-time)
261
262(defcustom display-time-string-forms
263 '((if (and (not display-time-format) display-time-day-and-date)
264 (format-time-string "%a %b %e " now)
265 "")
e93b2f39
SM
266 (propertize
267 (format-time-string (or display-time-format
268 (if display-time-24hr-format "%H:%M" "%-I:%M%p"))
269 now)
ef3e9175 270 'help-echo (format-time-string "%a %b %e, %Y" now))
c2acf685
SM
271 load
272 (if mail
273 ;; Build the string every time to act on customization.
b7eb6e8a
DL
274 ;; :set-after doesn't help for `customize-option'. I think it
275 ;; should.
276 (concat
277 " "
278 (propertize
279 display-time-mail-string
280 'display `(when (and display-time-use-mail-icon
281 (display-graphic-p))
282 ,@display-time-mail-icon
283 ,@(if (and display-time-mail-face
284 (memq (plist-get (cdr display-time-mail-icon)
285 :type)
286 '(pbm xbm)))
287 (let ((bg (face-attribute display-time-mail-face
288 :background)))
289 (if (stringp bg)
290 (list :background bg)))))
291 'face display-time-mail-face
292 'help-echo "You have new mail; mouse-2: Read mail"
3a02b63e 293 'mouse-face 'mode-line-highlight
b7eb6e8a
DL
294 'local-map (make-mode-line-mouse-map 'mouse-2
295 read-mail-command)))
c2acf685 296 ""))
9201cc28 297 "List of expressions governing display of the time in the mode line.
c2acf685
SM
298For most purposes, you can control the time format using `display-time-format'
299which is a more standard interface.
300
301This expression is a list of expressions that can involve the keywords
302`load', `day', `month', and `year', `12-hours', `24-hours', `minutes',
303`seconds', all numbers in string form, and `monthname', `dayname', `am-pm',
304and `time-zone' all alphabetic strings, and `mail' a true/nil value.
305
306For example, the form
307
308 '((substring year -2) \"/\" month \"/\" day
309 \" \" 24-hours \":\" minutes \":\" seconds
310 (if time-zone \" (\") time-zone (if time-zone \")\")
311 (if mail \" Mail\" \"\"))
312
313would give mode line times like `94/12/30 21:07:48 (UTC)'."
314 :type 'sexp
315 :group 'display-time)
316
317(defun display-time-event-handler ()
318 (display-time-update)
319 ;; Do redisplay right now, if no input pending.
320 (sit-for 0)
321 (let* ((current (current-time))
322 (timer display-time-timer)
323 ;; Compute the time when this timer will run again, next.
324 (next-time (timer-relative-time
325 (list (aref timer 1) (aref timer 2) (aref timer 3))
326 (* 5 (aref timer 4)) 0)))
327 ;; If the activation time is far in the past,
328 ;; skip executions until we reach a time in the future.
329 ;; This avoids a long pause if Emacs has been suspended for hours.
330 (or (> (nth 0 next-time) (nth 0 current))
331 (and (= (nth 0 next-time) (nth 0 current))
332 (> (nth 1 next-time) (nth 1 current)))
333 (and (= (nth 0 next-time) (nth 0 current))
334 (= (nth 1 next-time) (nth 1 current))
335 (> (nth 2 next-time) (nth 2 current)))
336 (progn
337 (timer-set-time timer (timer-next-integral-multiple-of-time
338 current display-time-interval)
339 display-time-interval)
340 (timer-activate timer)))))
341
4b05e68d
PJ
342(defun display-time-next-load-average ()
343 (interactive)
344 (if (= 3 (setq display-time-load-average (1+ display-time-load-average)))
345 (setq display-time-load-average 0))
346 (display-time-update)
347 (sit-for 0))
348
cc351ed4
PJ
349(defun display-time-mail-check-directory ()
350 (let ((mail-files (directory-files display-time-mail-directory t))
351 (size 0))
352 (while (and mail-files (= size 0))
353 ;; Count size of regular files only.
354 (setq size (+ size (or (and (file-regular-p (car mail-files))
355 (nth 7 (file-attributes (car mail-files))))
356 0)))
357 (setq mail-files (cdr mail-files)))
358 (if (> size 0)
359 size
360 nil)))
361
c2acf685 362(defun display-time-update ()
b7eb6e8a
DL
363 "Update the display-time info for the mode line.
364However, don't redisplay right now.
365
366This is used for things like Rmail `g' that want to force an
367update which can wait for the next redisplay."
c2acf685
SM
368 (let* ((now (current-time))
369 (time (current-time-string now))
fe6456af
RS
370 (load (if (null display-time-load-average)
371 ""
372 (condition-case ()
373 ;; Do not show values less than
374 ;; `display-time-load-average-threshold'.
375 (if (> (* display-time-load-average-threshold 100)
376 (nth display-time-load-average (load-average)))
377 ""
378 ;; The load average number is mysterious, so
379 ;; provide some help.
b7eb6e8a
DL
380 (let ((str (format " %03d"
381 (nth display-time-load-average
382 (load-average)))))
fe6456af
RS
383 (propertize
384 (concat (substring str 0 -2) "." (substring str -2))
b7eb6e8a
DL
385 'local-map (make-mode-line-mouse-map
386 'mouse-2 'display-time-next-load-average)
3a02b63e 387 'mouse-face 'mode-line-highlight
b7eb6e8a
DL
388 'help-echo (concat
389 "System load average for past "
390 (if (= 0 display-time-load-average)
391 "1 minute"
392 (if (= 1 display-time-load-average)
393 "5 minutes"
394 "15 minutes"))
395 "; mouse-2: next"))))
fe6456af 396 (error ""))))
c2acf685
SM
397 (mail-spool-file (or display-time-mail-file
398 (getenv "MAIL")
399 (concat rmail-spool-directory
400 (user-login-name))))
401 (mail (or (and display-time-mail-function
402 (funcall display-time-mail-function))
cc351ed4
PJ
403 (and display-time-mail-directory
404 (display-time-mail-check-directory))
c2acf685
SM
405 (and (stringp mail-spool-file)
406 (or (null display-time-server-down-time)
407 ;; If have been down for 20 min, try again.
408 (> (- (nth 1 now) display-time-server-down-time)
409 1200)
410 (and (< (nth 1 now) display-time-server-down-time)
b7eb6e8a
DL
411 (> (- (nth 1 now)
412 display-time-server-down-time)
c2acf685
SM
413 -64336)))
414 (let ((start-time (current-time)))
415 (prog1
416 (display-time-file-nonempty-p mail-spool-file)
b7eb6e8a
DL
417 (if (> (- (nth 1 (current-time))
418 (nth 1 start-time))
c2acf685
SM
419 20)
420 ;; Record that mail file is not accessible.
421 (setq display-time-server-down-time
422 (nth 1 (current-time)))
423 ;; Record that mail file is accessible.
424 (setq display-time-server-down-time nil)))))))
425 (24-hours (substring time 11 13))
027a4b6b 426 (hour (string-to-number 24-hours))
c2acf685
SM
427 (12-hours (int-to-string (1+ (% (+ hour 11) 12))))
428 (am-pm (if (>= hour 12) "pm" "am"))
429 (minutes (substring time 14 16))
430 (seconds (substring time 17 19))
431 (time-zone (car (cdr (current-time-zone now))))
432 (day (substring time 8 10))
433 (year (substring time 20 24))
434 (monthname (substring time 4 7))
435 (month
436 (cdr
437 (assoc
438 monthname
439 '(("Jan" . "1") ("Feb" . "2") ("Mar" . "3") ("Apr" . "4")
440 ("May" . "5") ("Jun" . "6") ("Jul" . "7") ("Aug" . "8")
441 ("Sep" . "9") ("Oct" . "10") ("Nov" . "11") ("Dec" . "12")))))
442 (dayname (substring time 0 3)))
443 (setq display-time-string
444 (mapconcat 'eval display-time-string-forms ""))
445 ;; This is inside the let binding, but we are not going to document
446 ;; what variables are available.
447 (run-hooks 'display-time-hook))
448 (force-mode-line-update))
449
450(defun display-time-file-nonempty-p (file)
451 (and (file-exists-p file)
452 (< 0 (nth 7 (file-attributes (file-chase-links file))))))
453
57ce63c4
SM
454;;;###autoload
455(define-minor-mode display-time-mode
456 "Toggle display of time, load level, and mail flag in mode lines.
457With a numeric arg, enable this display if arg is positive.
458
459When this display is enabled, it updates automatically every minute.
460If `display-time-day-and-date' is non-nil, the current day and date
461are displayed as well.
462This runs the normal hook `display-time-hook' after each update."
bb7a71c5 463 :global t :group 'display-time
fe6456af
RS
464 (and display-time-timer (cancel-timer display-time-timer))
465 (setq display-time-timer nil)
466 (setq display-time-string "")
467 (or global-mode-string (setq global-mode-string '("")))
468 (setq display-time-load-average display-time-default-load-average)
469 (if display-time-mode
470 (progn
471 (or (memq 'display-time-string global-mode-string)
472 (setq global-mode-string
473 (append global-mode-string '(display-time-string))))
474 ;; Set up the time timer.
475 (setq display-time-timer
476 (run-at-time t display-time-interval
477 'display-time-event-handler))
478 ;; Make the time appear right away.
479 (display-time-update)
480 ;; When you get new mail, clear "Mail" from the mode line.
481 (add-hook 'rmail-after-get-new-mail-hook
482 'display-time-event-handler))
483 (remove-hook 'rmail-after-get-new-mail-hook
484 'display-time-event-handler)))
c2acf685 485
aff2ba04
RS
486
487(defun display-time-world-mode ()
488 "Major mode for buffer that displays times in various time zones.
489See `display-time-world'."
490 (interactive)
491 (kill-all-local-variables)
492 (setq
493 major-mode 'display-time-world-mode
494 mode-name "World clock")
495 (use-local-map display-time-world-mode-map))
496
497(defun display-time-world-display (alist)
498 "Replace current buffer text with times in various zones, based on ALIST."
499 (let ((inhibit-read-only t)
500 (buffer-undo-list t))
501 (erase-buffer)
502 (let ((max-width 0)
503 (result ()))
504 (unwind-protect
505 (dolist (zone alist)
506 (let* ((label (cadr zone))
507 (width (string-width label)))
508 (set-time-zone-rule (car zone))
509 (setq result
510 (append result
511 (list
512 label width
513 (format-time-string display-time-world-time-format))))
514 (when (> width max-width)
515 (setq max-width width))))
516 (set-time-zone-rule nil))
517 (while result
518 (insert (pop result)
519 (make-string (1+ (- max-width (pop result))) ?\s)
520 (pop result) "\n")))
521 (delete-backward-char 1)))
522
523;;;###autoload
524(defun display-time-world ()
525 "Enable updating display of times in various time zones.
526`display-time-world-list' specifies the zones.
527To turn off the world time display, go to that window and type `q'."
528 (interactive)
529 (when (and display-time-world-timer-enable
530 (not (get-buffer display-time-world-buffer-name)))
531 (run-at-time t display-time-world-timer-second 'display-time-world-timer))
532 (with-current-buffer (get-buffer-create display-time-world-buffer-name)
533 (display-time-world-display display-time-world-list))
534 (pop-to-buffer display-time-world-buffer-name)
535 (fit-window-to-buffer)
536 (display-time-world-mode))
537
538(defun display-time-world-timer ()
539 (if (get-buffer display-time-world-buffer-name)
540 (with-current-buffer (get-buffer display-time-world-buffer-name)
541 (display-time-world-display display-time-world-list))
542 ;; cancel timer
543 (let ((list timer-list))
544 (while list
545 (let ((elt (pop list)))
546 (when (equal (symbol-name (aref elt 5)) "display-time-world-timer")
547 (cancel-timer elt)))))))
548
2736785a 549;;;###autoload
04e56139
GM
550(defun emacs-uptime (&optional format)
551 "Return a string giving the uptime of this instance of Emacs.
552FORMAT is a string to format the result, using `format-seconds'.
553For example, the Unix uptime command format is \"%D, %z%2h:%.2m\"."
2736785a
GM
554 (interactive)
555 (let ((str
04e56139 556 (format-seconds (or format "%Y, %D, %H, %M, %z%S")
2736785a 557 (time-to-seconds
e279593d 558 (time-subtract (current-time) before-init-time)))))
2736785a
GM
559 (if (interactive-p)
560 (message "%s" str)
561 str)))
562
24d6c292
JL
563;;;###autoload
564(defun emacs-init-time ()
565 "Return a string giving the duration of the Emacs initialization."
566 (interactive)
567 (let ((str
ac1efd51
JL
568 (format "%.1f seconds"
569 (time-to-seconds
570 (time-subtract after-init-time before-init-time)))))
24d6c292
JL
571 (if (interactive-p)
572 (message "%s" str)
573 str)))
574
c2acf685
SM
575(provide 'time)
576
cbee283d 577;; arch-tag: b9c1623f-b5cb-48e4-b650-482a4d23c5a6
c2acf685 578;;; time.el ends here