*** empty log message ***
[bpt/emacs.git] / lisp / calendar / appt.el
CommitLineData
55535639 1;;; appt.el --- appointment notification functions
c0274f38 2
8d638c1b 3;; Copyright (C) 1989, 1990, 1994, 1998, 2004 Free Software Foundation, Inc.
3a801d0c 4
e5167999 5;; Author: Neil Mager <neilm@juliet.ll.mit.edu>
0dba5606 6;; Maintainer: FSF
e5167999
ER
7;; Keywords: calendar
8
902a0e3c
JB
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
e5167999 13;; the Free Software Foundation; either version 2, or (at your option)
902a0e3c
JB
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b578f267
EN
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
902a0e3c 25
e5167999
ER
26;;; Commentary:
27
902a0e3c
JB
28;;
29;; appt.el - visible and/or audible notification of
8d638c1b 30;; appointments from diary file.
902a0e3c 31;;
902a0e3c 32;;;
d073fa5b 33;;; Thanks to Edward M. Reingold for much help and many suggestions,
902a0e3c
JB
34;;; And to many others for bug fixes and suggestions.
35;;;
36;;;
a1506d29 37;;; This functions in this file will alert the user of a
8d638c1b
GM
38;;; pending appointment based on his/her diary file. This package
39;;; is documented in the Emacs manual.
902a0e3c 40;;;
8d638c1b
GM
41;;; To activate this package, simply use (appt-activate 1).
42;;; A `diary-file' with appointments of the format described in the
43;;; documentation of the function `appt-check' is required.
44;;; Relevant customizable variables are also listed in the
45;;; documentation of that function.
46;;;
47;;; Today's appointment list is initialized from the diary when this
48;;; package is activated. Additionally, the appointments list is
49;;; recreated automatically at 12:01am for those who do not logout
50;;; every day or are programming late. It is also updated when the
51;;; `diary-file' is saved. Calling `appt-check' with an argument forces
52;;; a re-initialization at any time.
902a0e3c 53;;;
8d638c1b
GM
54;;; In order to add or delete items from today's list, without
55;;; changing the diary file, use `appt-add' and `appt-delete'.
902a0e3c 56;;;
8d638c1b 57
d073fa5b 58;;; Brief internal description - Skip this if you are not interested!
902a0e3c 59;;;
8d638c1b
GM
60;;; The function `appt-make-list' creates the appointments list which
61;;; `appt-check' reads.
902a0e3c 62;;;
5b586155 63;;; You can change the way the appointment window is created/deleted by
8d638c1b 64;;; setting the variables
5b586155
RS
65;;;
66;;; appt-disp-window-function
67;;; and
68;;; appt-delete-window-function
69;;;
8d638c1b 70;;; For instance, these variables could be set to functions that display
5b586155 71;;; appointments in pop-up frames, which are lowered or iconified after
8d638c1b 72;;; `appt-display-interval' minutes.
5b586155 73;;;
e5167999
ER
74
75;;; Code:
76
6afadb57
RS
77;; Make sure calendar is loaded when we compile this.
78(require 'calendar)
79
902a0e3c 80
7e1dae73 81;;;###autoload
8db540c5
RS
82(defcustom appt-message-warning-time 12
83 "*Time in minutes before an appointment that the warning begins."
84 :type 'integer
85 :group 'appt)
902a0e3c 86
7e1dae73 87;;;###autoload
8db540c5
RS
88(defcustom appt-audible t
89 "*Non-nil means beep to indicate appointment."
90 :type 'boolean
91 :group 'appt)
902a0e3c 92
7e1dae73 93;;;###autoload
8db540c5 94(defcustom appt-visible t
8d638c1b
GM
95 "*Non-nil means display appointment message in echo area.
96This variable is only relevant if `appt-msg-window' is nil."
8db540c5
RS
97 :type 'boolean
98 :group 'appt)
902a0e3c 99
8d638c1b
GM
100(make-obsolete-variable 'appt-visible 'appt-display-format "21.4")
101
7e1dae73 102;;;###autoload
8d638c1b
GM
103(defcustom appt-msg-window t
104 "*Non-nil means display appointment message in another window.
105If non-nil, this variable overrides `appt-visible'."
8db540c5
RS
106 :type 'boolean
107 :group 'appt)
902a0e3c 108
8d638c1b
GM
109(make-obsolete-variable 'appt-msg-window 'appt-display-format "21.4")
110
111;; TODO - add popup.
112(defcustom appt-display-format (cond (appt-msg-window 'window)
113 (appt-visible 'echo)
114 (t nil))
115 "How appointment reminders should be displayed.
116The options are:
117 window - use a separate window
118 echo - use the echo area
119 nil - no visible reminder.
120See also `appt-audible' and `appt-display-mode-line'."
121 :type '(choice
122 (const :tag "Separate window" window)
123 (const :tag "Echo-area" echo)
124 (const :tag "No visible display" nil))
125 :group 'appt
126 :version "21.4")
127
7e1dae73 128;;;###autoload
8d638c1b
GM
129(defcustom appt-display-mode-line t
130 "*Non-nil means display minutes to appointment and time on the mode line.
131This is in addition to any other display of appointment messages."
8db540c5
RS
132 :type 'boolean
133 :group 'appt)
902a0e3c 134
7e1dae73 135;;;###autoload
8db540c5 136(defcustom appt-display-duration 10
8d638c1b
GM
137 "*The number of seconds an appointment message is displayed.
138Only relevant if reminders are to be displayed in their own window."
8db540c5
RS
139 :type 'integer
140 :group 'appt)
902a0e3c 141
7e1dae73 142;;;###autoload
8db540c5 143(defcustom appt-display-diary t
8d638c1b 144 "*Non-nil displays the diary when the appointment list is first initialized.
8db540c5
RS
145This will occur at midnight when the appointment list is updated."
146 :type 'boolean
147 :group 'appt)
902a0e3c 148
8db540c5
RS
149(defcustom appt-display-interval 3
150 "*Number of minutes to wait between checking the appointment list."
151 :type 'integer
152 :group 'appt)
a1506d29 153
8d638c1b
GM
154(defcustom appt-disp-window-function 'appt-disp-window
155 "Function called to display appointment window.
156Only relevant if reminders are being displayed in a window."
157 :type '(choice (const appt-disp-window)
158 function)
159 :group 'appt)
160
161(defcustom appt-delete-window-function 'appt-delete-window
162 "Function called to remove appointment window and buffer.
163Only relevant if reminders are being displayed in a window."
164 :type '(choice (const appt-delete-window)
165 function)
166 :group 'appt)
167
168
169;;; Internal variables below this point.
170
5b586155
RS
171(defvar appt-buffer-name " *appt-buf*"
172 "Name of the appointments buffer.")
a1506d29 173
8d638c1b
GM
174(defvar appt-time-msg-list nil
175 "The list of appointments for today.
176Use `appt-add' and `appt-delete' to add and delete appointments.
177The original list is generated from today's `diary-entries-list', and
178can be regenerated using the function `appt-check'.
179Each element of the generated list has the form (MINUTES) STRING; where
180MINUTES is the time in minutes of the appointment after midnight, and
181STRING is the description of the appointment.")
a1506d29 182
8d638c1b
GM
183(defconst appt-max-time 1439
184 "11:59pm in minutes - number of minutes in a day minus 1.")
b570e652 185
efa434d9 186(defvar appt-mode-string nil
f3e7c0dc 187 "String being displayed in the mode line saying you have an appointment.
8d638c1b
GM
188The actual string includes the amount of time till the appointment.
189Only used if `appt-display-mode-line' is non-nil.")
f3e7c0dc
KH
190
191(defvar appt-prev-comp-time nil
8d638c1b
GM
192 "Time of day (mins since midnight) at which we last checked appointments.
193A nil value forces the diary file to be (re-)checked for appointments.")
f3e7c0dc
KH
194
195(defvar appt-now-displayed nil
196 "Non-nil when we have started notifying about a appointment that is near.")
197
8d638c1b
GM
198(defvar appt-display-count nil
199 "Internal variable used to count number of consecutive reminders.")
efa434d9 200
8d638c1b
GM
201(defvar appt-timer nil
202 "Timer used for diary appointment notifications (`appt-check').
203If this is non-nil, appointment checking is active.")
204
205
206;;; Functions.
207
208(defun appt-display-message (string mins)
209 "Display a reminder about an appointment.
210The string STRING describes the appointment, due in integer MINS minutes.
211The format of the visible reminder is controlled by `appt-display-format'.
212The variable `appt-audible' controls the audible reminder."
213 (cond ((eq appt-display-format 'window)
214 (funcall appt-disp-window-function
215 (number-to-string mins)
216 (format-time-string "%a %b %e " (current-time))
217 string)
218 (run-at-time (format "%d sec" appt-display-duration)
219 nil
220 appt-delete-window-function))
221 ((eq appt-display-format 'echo)
222 (message "%s" string)))
223 (if appt-audible (beep 1)))
224
225
226(defun appt-check (&optional force)
227 "Check for an appointment and update any reminder display.
228If optional argument FORCE is non-nil, reparse the diary file for
229appointments. Otherwise the diary file is only parsed once per day,
230and when saved.
902a0e3c 231
8d638c1b
GM
232Note: the time must be the first thing in the line in the diary
233for a warning to be issued. The format of the time can be either
23424 hour or am/pm. For example:
902a0e3c 235
8d638c1b
GM
236 02/23/89
237 18:00 Dinner
a1506d29 238
902a0e3c
JB
239 Thursday
240 11:45am Lunch meeting.
241
f3e7c0dc
KH
242Appointments are checked every `appt-display-interval' minutes.
243The following variables control appointment notification:
902a0e3c 244
8d638c1b
GM
245`appt-display-format'
246 Controls the format in which reminders are displayed.
902a0e3c 247
efa434d9 248`appt-audible'
8d638c1b 249 Variable used to determine if reminder is audible.
b570e652 250 Default is t.
902a0e3c 251
8d638c1b
GM
252`appt-message-warning-time'
253 Variable used to determine when appointment message
254 should first be displayed.
255
256`appt-display-mode-line'
257 If non-nil, a generic message giving the time remaining
258 is shown in the mode-line when an appointment is due.
259
260`appt-display-interval'
261 Interval in minutes at which to check for pending appointments.
902a0e3c 262
8d638c1b
GM
263`appt-display-diary'
264 Display the diary buffer when the appointment list is
265 initialized for the first time in a day.
266
267The following variables are only relevant if reminders are being
268displayed in a window:
902a0e3c 269
efa434d9 270`appt-display-duration'
8d638c1b 271 The number of seconds an appointment message is displayed.
b570e652 272
f3e7c0dc 273`appt-disp-window-function'
8d638c1b 274 Function called to display appointment window.
a1506d29 275
f3e7c0dc 276`appt-delete-window-function'
8d638c1b 277 Function called to remove appointment window and buffer."
902a0e3c 278
f3e7c0dc 279 (let* ((min-to-app -1)
f3e7c0dc
KH
280 (prev-appt-mode-string appt-mode-string)
281 (prev-appt-display-count (or appt-display-count 0))
282 ;; Non-nil means do a full check for pending appointments
283 ;; and display in whatever ways the user has selected.
284 ;; When no appointment is being displayed,
285 ;; we always do a full check.
286 (full-check
287 (or (not appt-now-displayed)
288 ;; This is true every appt-display-interval minutes.
8d638c1b 289 (zerop (mod prev-appt-display-count appt-display-interval))))
f3e7c0dc
KH
290 ;; Non-nil means only update the interval displayed in the mode line.
291 (mode-line-only
292 (and (not full-check) appt-now-displayed)))
293
294 (when (or full-check mode-line-only)
295 (save-excursion
296
297 ;; Get the current time and convert it to minutes
298 ;; from midnight. ie. 12:01am = 1, midnight = 0.
902a0e3c 299
f3e7c0dc
KH
300 (let* ((now (decode-time))
301 (cur-hour (nth 2 now))
302 (cur-min (nth 1 now))
303 (cur-comp-time (+ (* cur-hour 60) cur-min)))
304
a1506d29 305 ;; At the first check in any given day, update our
f3e7c0dc
KH
306 ;; appointments to today's list.
307
8d638c1b
GM
308 (if (or force
309 (null appt-prev-comp-time) ; first check
310 (< cur-comp-time appt-prev-comp-time)) ; new day
f3e7c0dc
KH
311 (condition-case nil
312 (progn
8d638c1b
GM
313 (if appt-display-diary
314 (let ((diary-hook
315 (if (assoc 'appt-make-list diary-hook)
316 diary-hook
317 (cons 'appt-make-list diary-hook))))
318 (diary))
f3e7c0dc
KH
319 (let ((diary-display-hook 'appt-make-list))
320 (diary))))
321 (error nil)))
f3e7c0dc 322
8d638c1b
GM
323 (setq appt-prev-comp-time cur-comp-time
324 appt-mode-string nil
325 appt-display-count nil)
f3e7c0dc
KH
326
327 ;; If there are entries in the list, and the
328 ;; user wants a message issued,
329 ;; get the first time off of the list
330 ;; and calculate the number of minutes until the appointment.
331
8d638c1b 332 (if appt-time-msg-list
f3e7c0dc
KH
333 (let ((appt-comp-time (car (car (car appt-time-msg-list)))))
334 (setq min-to-app (- appt-comp-time cur-comp-time))
335
a1506d29 336 (while (and appt-time-msg-list
f3e7c0dc 337 (< appt-comp-time cur-comp-time))
a1506d29 338 (setq appt-time-msg-list (cdr appt-time-msg-list))
f3e7c0dc 339 (if appt-time-msg-list
a1506d29 340 (setq appt-comp-time
f3e7c0dc
KH
341 (car (car (car appt-time-msg-list))))))
342
343 ;; If we have an appointment between midnight and
344 ;; 'appt-message-warning-time' minutes after midnight,
345 ;; we must begin to issue a message before midnight.
346 ;; Midnight is considered 0 minutes and 11:59pm is
347 ;; 1439 minutes. Therefore we must recalculate the minutes
a1506d29
JB
348 ;; to appointment variable. It is equal to the number of
349 ;; minutes before midnight plus the number of
f3e7c0dc
KH
350 ;; minutes after midnight our appointment is.
351
352 (if (and (< appt-comp-time appt-message-warning-time)
353 (> (+ cur-comp-time appt-message-warning-time)
354 appt-max-time))
355 (setq min-to-app (+ (- (1+ appt-max-time) cur-comp-time))
356 appt-comp-time))
357
a1506d29 358 ;; issue warning if the appointment time is
f3e7c0dc
KH
359 ;; within appt-message-warning time
360
361 (when (and (<= min-to-app appt-message-warning-time)
362 (>= min-to-app 0))
8d638c1b
GM
363 (setq appt-now-displayed t
364 appt-display-count (1+ prev-appt-display-count))
f3e7c0dc 365 (unless mode-line-only
8d638c1b
GM
366 (appt-display-message (cadr (car appt-time-msg-list))
367 min-to-app))
f3e7c0dc
KH
368 (when appt-display-mode-line
369 (setq appt-mode-string
8d638c1b 370 (format " App't in %s min." min-to-app)))
f3e7c0dc
KH
371
372 ;; When an appointment is reached,
373 ;; delete it from the list.
374 ;; Reset the count to 0 in case we display another
375 ;; appointment on the next cycle.
8d638c1b
GM
376 (if (zerop min-to-app)
377 (setq appt-time-msg-list (cdr appt-time-msg-list)
f3e7c0dc
KH
378 appt-display-count nil)))))
379
380 ;; If we have changed the mode line string,
381 ;; redisplay all mode lines.
382 (and appt-display-mode-line
383 (not (equal appt-mode-string
384 prev-appt-mode-string))
385 (progn
386 (force-mode-line-update t)
387 ;; If the string now has a notification,
388 ;; redisplay right now.
389 (if appt-mode-string
390 (sit-for 0)))))))))
902a0e3c
JB
391
392
902a0e3c 393(defun appt-disp-window (min-to-app new-time appt-msg)
8d638c1b
GM
394 "Display appointment message APPT-MSG in a separate buffer.
395The appointment is due in MIN-TO-APP (a string) minutes.
396NEW-TIME is a string giving the date."
902a0e3c 397 (require 'electric)
5b586155
RS
398
399 ;; Make sure we're not in the minibuffer
400 ;; before splitting the window.
401
402 (if (equal (selected-window) (minibuffer-window))
a1506d29 403 (if (other-window 1)
5b586155 404 (select-window (other-window 1))
fbebec27 405 (if (display-multi-frame-p)
5b586155 406 (select-frame (other-frame 1)))))
a1506d29 407
8d638c1b
GM
408 (let ((this-window (selected-window))
409 (appt-disp-buf (set-buffer (get-buffer-create appt-buffer-name))))
5b586155 410
d5b22d88
RS
411 (if (cdr (assq 'unsplittable (frame-parameters)))
412 ;; In an unsplittable frame, use something somewhere else.
413 (display-buffer appt-disp-buf)
058961dd
RS
414 (unless (or (special-display-p (buffer-name appt-disp-buf))
415 (same-window-p (buffer-name appt-disp-buf)))
416 ;; By default, split the bottom window and use the lower part.
417 (appt-select-lowest-window)
418 (split-window))
d5b22d88 419 (pop-to-buffer appt-disp-buf))
a1506d29 420 (setq mode-line-format
5b586155
RS
421 (concat "-------------------- Appointment in "
422 min-to-app " minutes. " new-time " %-"))
efa434d9 423 (erase-buffer)
0e13751e 424 (insert appt-msg)
d5b22d88 425 (shrink-window-if-larger-than-buffer (get-buffer-window appt-disp-buf t))
5b586155 426 (set-buffer-modified-p nil)
725ec4bc 427 (raise-frame (selected-frame))
8d638c1b 428 (select-window this-window)))
a1506d29 429
5b586155
RS
430(defun appt-delete-window ()
431 "Function called to undisplay appointment messages.
432Usually just deletes the appointment buffer."
95cdbff5
RS
433 (let ((window (get-buffer-window appt-buffer-name t)))
434 (and window
d073fa5b 435 (or (eq window (frame-root-window (window-frame window)))
95cdbff5 436 (delete-window window))))
5b586155
RS
437 (kill-buffer appt-buffer-name)
438 (if appt-audible
439 (beep 1)))
902a0e3c 440
902a0e3c 441(defun appt-select-lowest-window ()
d073fa5b 442"Select the lowest window on the frame."
7c0d9b89
GM
443 (let ((lowest-window (selected-window))
444 (bottom-edge (nth 3 (window-edges))))
445 (walk-windows (lambda (w)
446 (let ((next-bottom-edge (nth 3 (window-edges w))))
447 (when (< bottom-edge next-bottom-edge)
448 (setq bottom-edge next-bottom-edge
449 lowest-window w)))))
450 (select-window lowest-window)))
902a0e3c 451
f3e7c0dc 452;;;###autoload
902a0e3c 453(defun appt-add (new-appt-time new-appt-msg)
d073fa5b 454 "Add an appointment for the day at NEW-APPT-TIME and issue message NEW-APPT-MSG.
902a0e3c
JB
455The time should be in either 24 hour format or am/pm format."
456
457 (interactive "sTime (hh:mm[am/pm]): \nsMessage: ")
8d638c1b 458 (unless (string-match "[0-9]?[0-9][:.][0-9][0-9]\\(am\\|pm\\)?"
3b42c185 459 new-appt-time)
902a0e3c 460 (error "Unacceptable time-string"))
902a0e3c
JB
461 (let* ((appt-time-string (concat new-appt-time " " new-appt-msg))
462 (appt-time (list (appt-convert-time new-appt-time)))
463 (time-msg (cons appt-time (list appt-time-string))))
d073fa5b 464 (setq appt-time-msg-list (nconc appt-time-msg-list (list time-msg)))
a1506d29 465 (setq appt-time-msg-list (appt-sort-list appt-time-msg-list))))
902a0e3c 466
f3e7c0dc 467;;;###autoload
902a0e3c
JB
468(defun appt-delete ()
469 "Delete an appointment from the list of appointments."
470 (interactive)
8d638c1b 471 (let ((tmp-msg-list appt-time-msg-list))
902a0e3c
JB
472 (while tmp-msg-list
473 (let* ((element (car tmp-msg-list))
a1506d29 474 (prompt-string (concat "Delete "
bf9a91d0
RS
475 ;; We want to quote any doublequotes
476 ;; in the string, as well as put
477 ;; doublequotes around it.
478 (prin1-to-string
479 (substring-no-properties
a1506d29 480 (car (cdr element)) 0))
902a0e3c
JB
481 " from list? "))
482 (test-input (y-or-n-p prompt-string)))
483 (setq tmp-msg-list (cdr tmp-msg-list))
484 (if test-input
95cdbff5 485 (setq appt-time-msg-list (delq element appt-time-msg-list)))))
85bbde63 486 (appt-check)
902a0e3c 487 (message "")))
a1506d29 488
902a0e3c 489
ea04824c
DL
490(eval-when-compile (defvar number)
491 (defvar original-date)
492 (defvar diary-entries-list))
637a8ae9 493;;;###autoload
902a0e3c 494(defun appt-make-list ()
8d638c1b 495 "Create the appointments list from today's diary buffer.
d073fa5b 496The time must be at the beginning of a line for it to be
8d638c1b
GM
497put in the appointments list (see examples in documentation of
498the function `appt-check'). We assume that the variables DATE and
499NUMBER hold the arguments that `list-diary-entries' received.
d073fa5b
DL
500They specify the range of dates that the diary is being processed for."
501
9c197f24
RS
502 ;; We have something to do if the range of dates that the diary is
503 ;; considering includes the current date.
504 (if (and (not (calendar-date-compare
505 (list (calendar-current-date))
506 (list original-date)))
507 (calendar-date-compare
508 (list (calendar-current-date))
509 (list (calendar-gregorian-from-absolute
510 (+ (calendar-absolute-from-gregorian original-date)
511 number)))))
512 (save-excursion
513 ;; Clear the appointments list, then fill it in from the diary.
514 (setq appt-time-msg-list nil)
515 (if diary-entries-list
516
517 ;; Cycle through the entry-list (diary-entries-list)
a1506d29 518 ;; looking for entries beginning with a time. If
9c197f24
RS
519 ;; the entry begins with a time, add it to the
520 ;; appt-time-msg-list. Then sort the list.
521
522 (let ((entry-list diary-entries-list)
523 (new-time-string ""))
524 ;; Skip diary entries for dates before today.
525 (while (and entry-list
526 (calendar-date-compare
527 (car entry-list) (list (calendar-current-date))))
528 (setq entry-list (cdr entry-list)))
529 ;; Parse the entries for today.
a1506d29
JB
530 (while (and entry-list
531 (calendar-date-equal
9c197f24 532 (calendar-current-date) (car (car entry-list))))
bf9a91d0 533 (let ((time-string (cadr (car entry-list))))
9c197f24 534 (while (string-match
3b42c185 535 "\\([0-9]?[0-9][:.][0-9][0-9]\\(am\\|pm\\)?\\).*"
9c197f24 536 time-string)
4e96d63b
RS
537 (let* ((beg (match-beginning 0))
538 ;; Get just the time for this appointment.
539 (only-time (match-string 1 time-string))
540 ;; Find the end of this appointment
541 ;; (the start of the next).
542 (end (string-match
3b42c185 543 "^[ \t]*[0-9]?[0-9][:.][0-9][0-9]\\(am\\|pm\\)?"
4e96d63b
RS
544 time-string
545 (match-end 0)))
546 ;; Get the whole string for this appointment.
547 (appt-time-string
548 (substring time-string beg (if end (1- end)))))
549
550 ;; Add this appointment to appt-time-msg-list.
551 (let* ((appt-time (list (appt-convert-time only-time)))
552 (time-msg (list appt-time appt-time-string)))
553 (setq appt-time-msg-list
554 (nconc appt-time-msg-list (list time-msg))))
555
556 ;; Discard this appointment from the string.
557 (setq time-string
558 (if end (substring time-string end) "")))))
9c197f24
RS
559 (setq entry-list (cdr entry-list)))))
560 (setq appt-time-msg-list (appt-sort-list appt-time-msg-list))
561
562 ;; Get the current time and convert it to minutes
563 ;; from midnight. ie. 12:01am = 1, midnight = 0,
564 ;; so that the elements in the list
565 ;; that are earlier than the present time can
566 ;; be removed.
567
0d26feea
KH
568 (let* ((now (decode-time))
569 (cur-hour (nth 2 now))
570 (cur-min (nth 1 now))
9c197f24 571 (cur-comp-time (+ (* cur-hour 60) cur-min))
8d638c1b 572 (appt-comp-time (car (caar appt-time-msg-list))))
9c197f24
RS
573
574 (while (and appt-time-msg-list (< appt-comp-time cur-comp-time))
a1506d29 575 (setq appt-time-msg-list (cdr appt-time-msg-list))
9c197f24 576 (if appt-time-msg-list
8d638c1b 577 (setq appt-comp-time (car (caar appt-time-msg-list)))))))))
a1506d29 578
902a0e3c 579
902a0e3c 580(defun appt-sort-list (appt-list)
8d638c1b
GM
581 "Sort an appointment list, putting earlier items at the front.
582APPT-LIST is a list of the same format as `appt-time-msg-list'."
583(sort appt-list (lambda (e1 e2) (< (caar e1) (caar e2)))))
902a0e3c
JB
584
585
586(defun appt-convert-time (time2conv)
3b42c185 587 "Convert hour:min[am/pm] format to minutes from midnight.
8d638c1b
GM
588A period (.) can be used instead of a colon (:) to separate the
589hour and minute parts."
902a0e3c
JB
590 (let ((conv-time 0)
591 (hr 0)
592 (min 0))
593
3b42c185 594 (string-match "[:.]\\([0-9][0-9]\\)" time2conv)
a1506d29 595 (setq min (string-to-int
d073fa5b 596 (match-string 1 time2conv)))
a1506d29 597
3b42c185 598 (string-match "[0-9]?[0-9][:.]" time2conv)
a1506d29 599 (setq hr (string-to-int
d073fa5b 600 (match-string 0 time2conv)))
a1506d29 601
902a0e3c 602 ;; convert the time appointment time into 24 hour time
a1506d29 603
85bbde63
GM
604 (cond ((and (string-match "pm" time2conv) (< hr 12))
605 (setq hr (+ 12 hr)))
606 ((and (string-match "am" time2conv) (= hr 12))
607 (setq hr 0)))
a1506d29 608
902a0e3c
JB
609 ;; convert the actual time
610 ;; into minutes for comparison
611 ;; against the actual time.
a1506d29 612
902a0e3c
JB
613 (setq conv-time (+ (* hr 60) min))
614 conv-time))
615
902a0e3c 616
8d638c1b
GM
617(defun appt-update-list ()
618 "If the current buffer is visiting the diary, update appointments.
619This function is intended for use with `write-file-functions'."
620 (and (equal buffer-file-name (expand-file-name diary-file))
621 appt-timer
622 (let ((appt-display-diary nil))
623 (appt-check t)))
624 nil)
625
626
627;;;###autoload
628(defun appt-activate (&optional arg)
629"Toggle checking of appointments.
630With optional numeric argument ARG, turn appointment checking on if
631ARG is positive, otherwise off."
632 (interactive "P")
633 (let ((appt-active appt-timer))
634 (setq appt-active (if arg (> (prefix-numeric-value arg) 0)
635 (not appt-active)))
636 (remove-hook 'write-file-functions 'appt-update-list)
637 (or global-mode-string (setq global-mode-string '("")))
638 (delq 'appt-mode-string global-mode-string)
639 (and appt-timer
640 (cancel-timer appt-timer)
641 (setq appt-timer nil))
642 (when appt-active
643 (add-hook 'write-file-functions 'appt-update-list)
644 (setq appt-timer (run-at-time t 60 'appt-check)
645 global-mode-string
646 (append global-mode-string '(appt-mode-string)))
647 (appt-check t))))
648
efa434d9 649
8d638c1b 650(provide 'appt)
5b586155 651
ab5796a9 652;;; arch-tag: bf5791c4-8921-499e-a26f-772b1788d347
efa434d9 653;;; appt.el ends here