comparison lisp/calendar/appt.el @ 89909:68c22ea6027c

Sync to HEAD
author Kenichi Handa <handa@m17n.org>
date Fri, 16 Apr 2004 12:51:06 +0000
parents 375f2633d815
children 4c90ffeb71c5
comparison
equal deleted inserted replaced
89908:ee1402f7b568 89909:68c22ea6027c
1 ;;; appt.el --- appointment notification functions 1 ;;; appt.el --- appointment notification functions
2 2
3 ;; Copyright (C) 1989, 1990, 1994, 1998 Free Software Foundation, Inc. 3 ;; Copyright (C) 1989, 1990, 1994, 1998, 2004 Free Software Foundation, Inc.
4 4
5 ;; Author: Neil Mager <neilm@juliet.ll.mit.edu> 5 ;; Author: Neil Mager <neilm@juliet.ll.mit.edu>
6 ;; Maintainer: FSF 6 ;; Maintainer: FSF
7 ;; Keywords: calendar 7 ;; Keywords: calendar
8 8
25 25
26 ;;; Commentary: 26 ;;; Commentary:
27 27
28 ;; 28 ;;
29 ;; appt.el - visible and/or audible notification of 29 ;; appt.el - visible and/or audible notification of
30 ;; appointments from ~/diary file. 30 ;; appointments from diary file.
31 ;; 31 ;;
32 ;;; 32 ;;;
33 ;;; Thanks to Edward M. Reingold for much help and many suggestions, 33 ;;; Thanks to Edward M. Reingold for much help and many suggestions,
34 ;;; And to many others for bug fixes and suggestions. 34 ;;; And to many others for bug fixes and suggestions.
35 ;;; 35 ;;;
36 ;;; 36 ;;;
37 ;;; This functions in this file will alert the user of a 37 ;;; This functions in this file will alert the user of a
38 ;;; pending appointment based on their diary file. 38 ;;; pending appointment based on his/her diary file. This package
39 ;;; 39 ;;; is documented in the Emacs manual.
40 ;;; A message will be displayed in the mode line of the Emacs buffer 40 ;;;
41 ;;; and (if you request) the terminal will beep and display a message 41 ;;; To activate this package, simply use (appt-activate 1).
42 ;;; from the diary in the mini-buffer, or you can choose to 42 ;;; A `diary-file' with appointments of the format described in the
43 ;;; have a message displayed in a new buffer. 43 ;;; documentation of the function `appt-check' is required.
44 ;;; 44 ;;; Relevant customizable variables are also listed in the
45 ;;; The variable `appt-message-warning-time' allows the 45 ;;; documentation of that function.
46 ;;; user to specify how much notice they want before the appointment. The 46 ;;;
47 ;;; variable `appt-issue-message' specifies whether the user wants 47 ;;; Today's appointment list is initialized from the diary when this
48 ;;; to be notified of a pending appointment. 48 ;;; package is activated. Additionally, the appointments list is
49 ;;; 49 ;;; recreated automatically at 12:01am for those who do not logout
50 ;;; In order to use the appt package, you only need 50 ;;; every day or are programming late. It is also updated when the
51 ;;; to load it---provided you have appointments. 51 ;;; `diary-file' is saved. Calling `appt-check' with an argument forces
52 ;;; 52 ;;; a re-initialization at any time.
53 ;;; Before that, you can also set some options if you want 53 ;;;
54 ;;; (setq view-diary-entries-initially t) 54 ;;; In order to add or delete items from today's list, without
55 ;;; (setq appt-issue-message t) 55 ;;; changing the diary file, use `appt-add' and `appt-delete'.
56 ;;; 56 ;;;
57 ;;; This is an example of what can be in your diary file: 57
58 ;;; Monday
59 ;;; 9:30am Coffee break
60 ;;; 12:00pm Lunch
61 ;;;
62 ;;; Based upon the above lines in your .emacs and diary files,
63 ;;; the calendar and diary will be displayed when you enter
64 ;;; Emacs and your appointments list will automatically be created.
65 ;;; You will then be reminded at 9:20am about your coffee break
66 ;;; and at 11:50am to go to lunch.
67 ;;;
68 ;;; Use describe-function on appt-check for a description of other variables
69 ;;; that can be used to personalize the notification system.
70 ;;;
71 ;;; In order to add or delete items from todays list, use appt-add
72 ;;; and appt-delete.
73 ;;;
74 ;;; Additionally, the appointments list is recreated automatically
75 ;;; at 12:01am for those who do not logout every day or are programming
76 ;;; late.
77 ;;;
78 ;;; Brief internal description - Skip this if you are not interested! 58 ;;; Brief internal description - Skip this if you are not interested!
79 ;;; 59 ;;;
80 ;;; The function appt-make-list creates the appointments list which appt-check 60 ;;; The function `appt-make-list' creates the appointments list which
81 ;;; reads. This is all done automatically. 61 ;;; `appt-check' reads.
82 ;;; It is invoked from the function list-diary-entries.
83 ;;; 62 ;;;
84 ;;; You can change the way the appointment window is created/deleted by 63 ;;; You can change the way the appointment window is created/deleted by
85 ;;; setting the variables 64 ;;; setting the variables
86 ;;; 65 ;;;
87 ;;; appt-disp-window-function 66 ;;; appt-disp-window-function
88 ;;; and 67 ;;; and
89 ;;; appt-delete-window-function 68 ;;; appt-delete-window-function
90 ;;; 69 ;;;
91 ;;; For instance, these variables can be set to functions that display 70 ;;; For instance, these variables could be set to functions that display
92 ;;; appointments in pop-up frames, which are lowered or iconified after 71 ;;; appointments in pop-up frames, which are lowered or iconified after
93 ;;; appt-display-interval minutes. 72 ;;; `appt-display-interval' minutes.
94 ;;; 73 ;;;
95 74
96 ;;; Code: 75 ;;; Code:
97 76
98 ;; Make sure calendar is loaded when we compile this. 77 ;; Make sure calendar is loaded when we compile this.
99 (require 'calendar) 78 (require 'calendar)
100 79
101 (provide 'appt)
102 80
103 ;;;###autoload 81 ;;;###autoload
104 (defcustom appt-issue-message t 82 (defcustom appt-issue-message t
105 "*Non-nil means check for appointments in the diary buffer. 83 "*Non-nil means check for appointments in the diary buffer.
106 To be detected, the diary entry must have the time 84 To be detected, the diary entry must have the format described in the
107 as the first thing on a line." 85 documentation of the function `appt-check'."
108 :type 'boolean 86 :type 'boolean
109 :group 'appt) 87 :group 'appt)
88
89 (make-obsolete-variable 'appt-issue-message
90 "use the function `appt-activate', and the \
91 variable `appt-display-format' instead." "21.4")
110 92
111 ;;;###autoload 93 ;;;###autoload
112 (defcustom appt-message-warning-time 12 94 (defcustom appt-message-warning-time 12
113 "*Time in minutes before an appointment that the warning begins." 95 "*Time in minutes before an appointment that the warning begins."
114 :type 'integer 96 :type 'integer
120 :type 'boolean 102 :type 'boolean
121 :group 'appt) 103 :group 'appt)
122 104
123 ;;;###autoload 105 ;;;###autoload
124 (defcustom appt-visible t 106 (defcustom appt-visible t
125 "*Non-nil means display appointment message in echo area." 107 "*Non-nil means display appointment message in echo area.
108 This variable is only relevant if `appt-msg-window' is nil."
126 :type 'boolean 109 :type 'boolean
127 :group 'appt) 110 :group 'appt)
128 111
112 (make-obsolete-variable 'appt-visible 'appt-display-format "21.4")
113
114 ;;;###autoload
115 (defcustom appt-msg-window t
116 "*Non-nil means display appointment message in another window.
117 If non-nil, this variable overrides `appt-visible'."
118 :type 'boolean
119 :group 'appt)
120
121 (make-obsolete-variable 'appt-msg-window 'appt-display-format "21.4")
122
123 ;; TODO - add popup.
124 (defcustom appt-display-format 'ignore
125 "How appointment reminders should be displayed.
126 The options are:
127 window - use a separate window
128 echo - use the echo area
129 nil - no visible reminder.
130 See also `appt-audible' and `appt-display-mode-line'.
131
132 The default value is 'ignore, which means to fall back on the value
133 of the (obsolete) variables `appt-msg-window' and `appt-visible'."
134 :type '(choice
135 (const :tag "Separate window" window)
136 (const :tag "Echo-area" echo)
137 (const :tag "No visible display" nil))
138 :group 'appt
139 :version "21.4")
140
129 ;;;###autoload 141 ;;;###autoload
130 (defcustom appt-display-mode-line t 142 (defcustom appt-display-mode-line t
131 "*Non-nil means display minutes to appointment and time on the mode line." 143 "*Non-nil means display minutes to appointment and time on the mode line.
144 This is in addition to any other display of appointment messages."
132 :type 'boolean 145 :type 'boolean
133 :group 'appt) 146 :group 'appt)
134 147
135 ;;;###autoload 148 ;;;###autoload
136 (defcustom appt-msg-window t
137 "*Non-nil means display appointment message in another window."
138 :type 'boolean
139 :group 'appt)
140
141 ;;;###autoload
142 (defcustom appt-display-duration 10 149 (defcustom appt-display-duration 10
143 "*The number of seconds an appointment message is displayed." 150 "*The number of seconds an appointment message is displayed.
151 Only relevant if reminders are to be displayed in their own window."
144 :type 'integer 152 :type 'integer
145 :group 'appt) 153 :group 'appt)
146 154
147 ;;;###autoload 155 ;;;###autoload
148 (defcustom appt-display-diary t 156 (defcustom appt-display-diary t
149 "*Non-nil means to display the next days diary on the screen. 157 "*Non-nil displays the diary when the appointment list is first initialized.
150 This will occur at midnight when the appointment list is updated." 158 This will occur at midnight when the appointment list is updated."
151 :type 'boolean 159 :type 'boolean
152 :group 'appt) 160 :group 'appt)
153
154 (defvar appt-time-msg-list nil
155 "The list of appointments for today.
156 Use `appt-add' and `appt-delete' to add and delete appointments from list.
157 The original list is generated from the today's `diary-entries-list'.
158 The number before each time/message is the time in minutes from midnight.")
159
160 (defconst appt-max-time 1439
161 "11:59pm in minutes - number of minutes in a day minus 1.")
162 161
163 (defcustom appt-display-interval 3 162 (defcustom appt-display-interval 3
164 "*Number of minutes to wait between checking the appointment list." 163 "*Number of minutes to wait between checking the appointment list."
165 :type 'integer 164 :type 'integer
166 :group 'appt) 165 :group 'appt)
167 166
167 (defcustom appt-disp-window-function 'appt-disp-window
168 "Function called to display appointment window.
169 Only relevant if reminders are being displayed in a window."
170 :type '(choice (const appt-disp-window)
171 function)
172 :group 'appt)
173
174 (defcustom appt-delete-window-function 'appt-delete-window
175 "Function called to remove appointment window and buffer.
176 Only relevant if reminders are being displayed in a window."
177 :type '(choice (const appt-delete-window)
178 function)
179 :group 'appt)
180
181
182 ;;; Internal variables below this point.
183
168 (defvar appt-buffer-name " *appt-buf*" 184 (defvar appt-buffer-name " *appt-buf*"
169 "Name of the appointments buffer.") 185 "Name of the appointments buffer.")
170 186
171 (defvar appt-disp-window-function 'appt-disp-window 187 (defvar appt-time-msg-list nil
172 "Function called to display appointment window.") 188 "The list of appointments for today.
173 189 Use `appt-add' and `appt-delete' to add and delete appointments.
174 (defvar appt-delete-window-function 'appt-delete-window 190 The original list is generated from today's `diary-entries-list', and
175 "Function called to remove appointment window and buffer.") 191 can be regenerated using the function `appt-check'.
192 Each element of the generated list has the form (MINUTES) STRING; where
193 MINUTES is the time in minutes of the appointment after midnight, and
194 STRING is the description of the appointment.")
195
196 (defconst appt-max-time 1439
197 "11:59pm in minutes - number of minutes in a day minus 1.")
176 198
177 (defvar appt-mode-string nil 199 (defvar appt-mode-string nil
178 "String being displayed in the mode line saying you have an appointment. 200 "String being displayed in the mode line saying you have an appointment.
179 The actual string includes the amount of time till the appointment.") 201 The actual string includes the amount of time till the appointment.
202 Only used if `appt-display-mode-line' is non-nil.")
180 203
181 (defvar appt-prev-comp-time nil 204 (defvar appt-prev-comp-time nil
182 "Time of day (mins since midnight) at which we last checked appointments.") 205 "Time of day (mins since midnight) at which we last checked appointments.
206 A nil value forces the diary file to be (re-)checked for appointments.")
183 207
184 (defvar appt-now-displayed nil 208 (defvar appt-now-displayed nil
185 "Non-nil when we have started notifying about a appointment that is near.") 209 "Non-nil when we have started notifying about a appointment that is near.")
186 210
187 (defvar appt-display-count nil) 211 (defvar appt-display-count nil
188 212 "Internal variable used to count number of consecutive reminders.")
189 (defun appt-check () 213
190 "Check for an appointment and update the mode line. 214 (defvar appt-timer nil
215 "Timer used for diary appointment notifications (`appt-check').
216 If this is non-nil, appointment checking is active.")
217
218
219 ;;; Functions.
220
221 (defun appt-display-message (string mins)
222 "Display a reminder about an appointment.
223 The string STRING describes the appointment, due in integer MINS minutes.
224 The format of the visible reminder is controlled by `appt-display-format'.
225 The variable `appt-audible' controls the audible reminder."
226 ;; let binding for backwards compatability. Remove when obsolete
227 ;; vars appt-msg-window and appt-visible are dropped.
228 (let ((appt-display-format
229 (if (eq appt-display-format 'ignore)
230 (cond (appt-msg-window 'window)
231 (appt-visible 'echo))
232 appt-display-format)))
233 (cond ((eq appt-display-format 'window)
234 (funcall appt-disp-window-function
235 (number-to-string mins)
236 (format-time-string "%a %b %e " (current-time))
237 string)
238 (run-at-time (format "%d sec" appt-display-duration)
239 nil
240 appt-delete-window-function))
241 ((eq appt-display-format 'echo)
242 (message "%s" string)))
243 (if appt-audible (beep 1))))
244
245
246 (defun appt-check (&optional force)
247 "Check for an appointment and update any reminder display.
248 If optional argument FORCE is non-nil, reparse the diary file for
249 appointments. Otherwise the diary file is only parsed once per day,
250 and when saved.
251
191 Note: the time must be the first thing in the line in the diary 252 Note: the time must be the first thing in the line in the diary
192 for a warning to be issued. 253 for a warning to be issued. The format of the time can be either
193 254 24 hour or am/pm. For example:
194 The format of the time can be either 24 hour or am/pm. 255
195 Example: 256 02/23/89
196 257 18:00 Dinner
197 02/23/89
198 18:00 Dinner
199 258
200 Thursday 259 Thursday
201 11:45am Lunch meeting. 260 11:45am Lunch meeting.
202 261
203 Appointments are checked every `appt-display-interval' minutes. 262 Appointments are checked every `appt-display-interval' minutes.
204 The following variables control appointment notification: 263 The following variables control appointment notification:
205 264
206 `appt-issue-message' 265 `appt-display-format'
207 If t, the diary buffer is checked for appointments. 266 Controls the format in which reminders are displayed.
267
268 `appt-audible'
269 Variable used to determine if reminder is audible.
270 Default is t.
208 271
209 `appt-message-warning-time' 272 `appt-message-warning-time'
210 Variable used to determine if appointment message 273 Variable used to determine when appointment message
211 should be displayed. 274 should first be displayed.
212 275
213 `appt-audible' 276 `appt-display-mode-line'
214 Variable used to determine if appointment is audible. 277 If non-nil, a generic message giving the time remaining
215 Default is t. 278 is shown in the mode-line when an appointment is due.
216 279
217 `appt-visible' 280 `appt-display-interval'
218 Variable used to determine if appointment message should be 281 Interval in minutes at which to check for pending appointments.
219 displayed in the mini-buffer. Default is t. 282
220 283 `appt-display-diary'
221 `appt-msg-window' 284 Display the diary buffer when the appointment list is
222 Variable used to determine if appointment message 285 initialized for the first time in a day.
223 should temporarily appear in another window. Mutually exclusive 286
224 to `appt-visible'. 287 The following variables are only relevant if reminders are being
288 displayed in a window:
225 289
226 `appt-display-duration' 290 `appt-display-duration'
227 The number of seconds an appointment message 291 The number of seconds an appointment message is displayed.
228 is displayed in another window.
229 292
230 `appt-disp-window-function' 293 `appt-disp-window-function'
231 Function called to display appointment window. You can customize 294 Function called to display appointment window.
232 appt.el by setting this variable to a function different from the
233 one provided with this package.
234 295
235 `appt-delete-window-function' 296 `appt-delete-window-function'
236 Function called to remove appointment window and buffer. You can 297 Function called to remove appointment window and buffer."
237 customize appt.el by setting this variable to a function different
238 from the one provided with this package."
239 298
240 (let* ((min-to-app -1) 299 (let* ((min-to-app -1)
241 (new-time "")
242 (prev-appt-mode-string appt-mode-string) 300 (prev-appt-mode-string appt-mode-string)
243 (prev-appt-display-count (or appt-display-count 0)) 301 (prev-appt-display-count (or appt-display-count 0))
244 ;; Non-nil means do a full check for pending appointments 302 ;; Non-nil means do a full check for pending appointments
245 ;; and display in whatever ways the user has selected. 303 ;; and display in whatever ways the user has selected.
246 ;; When no appointment is being displayed, 304 ;; When no appointment is being displayed,
247 ;; we always do a full check. 305 ;; we always do a full check.
248 (full-check 306 (full-check
249 (or (not appt-now-displayed) 307 (or (not appt-now-displayed)
250 ;; This is true every appt-display-interval minutes. 308 ;; This is true every appt-display-interval minutes.
251 (= 0 (mod prev-appt-display-count appt-display-interval)))) 309 (zerop (mod prev-appt-display-count appt-display-interval))))
252 ;; Non-nil means only update the interval displayed in the mode line. 310 ;; Non-nil means only update the interval displayed in the mode line.
253 (mode-line-only 311 (mode-line-only
254 (and (not full-check) appt-now-displayed))) 312 (and (not full-check) appt-now-displayed)))
255 313
256 (when (or full-check mode-line-only) 314 (when (or full-check mode-line-only)
265 (cur-comp-time (+ (* cur-hour 60) cur-min))) 323 (cur-comp-time (+ (* cur-hour 60) cur-min)))
266 324
267 ;; At the first check in any given day, update our 325 ;; At the first check in any given day, update our
268 ;; appointments to today's list. 326 ;; appointments to today's list.
269 327
270 (if (or (null appt-prev-comp-time) 328 (if (or force ; eg initialize, diary save
271 (< cur-comp-time appt-prev-comp-time)) 329 (null appt-prev-comp-time) ; first check
330 (< cur-comp-time appt-prev-comp-time)) ; new day
272 (condition-case nil 331 (condition-case nil
273 (progn 332 (if appt-display-diary
274 (if (and view-diary-entries-initially appt-display-diary) 333 (let ((diary-hook
275 (diary) 334 (if (assoc 'appt-make-list diary-hook)
276 (let ((diary-display-hook 'appt-make-list)) 335 diary-hook
277 (diary)))) 336 (cons 'appt-make-list diary-hook))))
337 (diary))
338 (let ((diary-display-hook 'appt-make-list)
339 (d-buff (find-buffer-visiting
340 (substitute-in-file-name diary-file)))
341 selective)
342 (if d-buff ; diary buffer exists
343 (with-current-buffer d-buff
344 (setq selective selective-display)))
345 (diary)
346 ;; If the diary buffer existed before this command,
347 ;; restore its display state. Otherwise, kill it.
348 (if d-buff
349 ;; Displays the diary buffer.
350 (or selective (show-all-diary-entries))
351 (and
352 (setq d-buff (find-buffer-visiting
353 (substitute-in-file-name diary-file)))
354 (kill-buffer d-buff)))))
278 (error nil))) 355 (error nil)))
279 (setq appt-prev-comp-time cur-comp-time) 356
280 357 (setq appt-prev-comp-time cur-comp-time
281 (setq appt-mode-string nil) 358 appt-mode-string nil
282 (setq appt-display-count nil) 359 appt-display-count nil)
283 360
284 ;; If there are entries in the list, and the 361 ;; If there are entries in the list, and the
285 ;; user wants a message issued, 362 ;; user wants a message issued,
286 ;; get the first time off of the list 363 ;; get the first time off of the list
287 ;; and calculate the number of minutes until the appointment. 364 ;; and calculate the number of minutes until the appointment.
315 ;; issue warning if the appointment time is 392 ;; issue warning if the appointment time is
316 ;; within appt-message-warning time 393 ;; within appt-message-warning time
317 394
318 (when (and (<= min-to-app appt-message-warning-time) 395 (when (and (<= min-to-app appt-message-warning-time)
319 (>= min-to-app 0)) 396 (>= min-to-app 0))
320 (setq appt-now-displayed t) 397 (setq appt-now-displayed t
321 (setq appt-display-count 398 appt-display-count (1+ prev-appt-display-count))
322 (1+ prev-appt-display-count))
323 (unless mode-line-only 399 (unless mode-line-only
324 (if appt-msg-window 400 (appt-display-message (cadr (car appt-time-msg-list))
325 (progn 401 min-to-app))
326 (setq new-time (format-time-string "%a %b %e "
327 (current-time)))
328 (funcall
329 appt-disp-window-function
330 (number-to-string min-to-app) new-time
331 (car (cdr (car appt-time-msg-list))))
332
333 (run-at-time
334 (format "%d sec" appt-display-duration)
335 nil
336 appt-delete-window-function))
337 ;;; else
338
339 (if appt-visible
340 (message "%s"
341 (car (cdr (car appt-time-msg-list)))))
342
343 (if appt-audible
344 (beep 1))))
345
346 (when appt-display-mode-line 402 (when appt-display-mode-line
347 (setq appt-mode-string 403 (setq appt-mode-string
348 (concat " App't in " 404 (format " App't in %s min." min-to-app)))
349 (number-to-string min-to-app)
350 " min. ")))
351 405
352 ;; When an appointment is reached, 406 ;; When an appointment is reached,
353 ;; delete it from the list. 407 ;; delete it from the list.
354 ;; Reset the count to 0 in case we display another 408 ;; Reset the count to 0 in case we display another
355 ;; appointment on the next cycle. 409 ;; appointment on the next cycle.
356 (if (= min-to-app 0) 410 (if (zerop min-to-app)
357 (setq appt-time-msg-list 411 (setq appt-time-msg-list (cdr appt-time-msg-list)
358 (cdr appt-time-msg-list)
359 appt-display-count nil))))) 412 appt-display-count nil)))))
360 413
361 ;; If we have changed the mode line string, 414 ;; If we have changed the mode line string,
362 ;; redisplay all mode lines. 415 ;; redisplay all mode lines.
363 (and appt-display-mode-line 416 (and appt-display-mode-line
370 (if appt-mode-string 423 (if appt-mode-string
371 (sit-for 0))))))))) 424 (sit-for 0)))))))))
372 425
373 426
374 (defun appt-disp-window (min-to-app new-time appt-msg) 427 (defun appt-disp-window (min-to-app new-time appt-msg)
375 "Display appointment message APPT-MSG in a separate buffer." 428 "Display appointment message APPT-MSG in a separate buffer.
429 The appointment is due in MIN-TO-APP (a string) minutes.
430 NEW-TIME is a string giving the date."
376 (require 'electric) 431 (require 'electric)
377 432
378 ;; Make sure we're not in the minibuffer 433 ;; Make sure we're not in the minibuffer
379 ;; before splitting the window. 434 ;; before splitting the window.
380 435
382 (if (other-window 1) 437 (if (other-window 1)
383 (select-window (other-window 1)) 438 (select-window (other-window 1))
384 (if (display-multi-frame-p) 439 (if (display-multi-frame-p)
385 (select-frame (other-frame 1))))) 440 (select-frame (other-frame 1)))))
386 441
387 (let* ((this-buffer (current-buffer)) 442 (let ((this-window (selected-window))
388 (this-window (selected-window)) 443 (appt-disp-buf (set-buffer (get-buffer-create appt-buffer-name))))
389 (appt-disp-buf (set-buffer (get-buffer-create appt-buffer-name))))
390 444
391 (if (cdr (assq 'unsplittable (frame-parameters))) 445 (if (cdr (assq 'unsplittable (frame-parameters)))
392 ;; In an unsplittable frame, use something somewhere else. 446 ;; In an unsplittable frame, use something somewhere else.
393 (display-buffer appt-disp-buf) 447 (display-buffer appt-disp-buf)
394 (unless (or (special-display-p (buffer-name appt-disp-buf)) 448 (unless (or (special-display-p (buffer-name appt-disp-buf))
403 (erase-buffer) 457 (erase-buffer)
404 (insert appt-msg) 458 (insert appt-msg)
405 (shrink-window-if-larger-than-buffer (get-buffer-window appt-disp-buf t)) 459 (shrink-window-if-larger-than-buffer (get-buffer-window appt-disp-buf t))
406 (set-buffer-modified-p nil) 460 (set-buffer-modified-p nil)
407 (raise-frame (selected-frame)) 461 (raise-frame (selected-frame))
408 (select-window this-window) 462 (select-window this-window)))
409 (if appt-audible
410 (beep 1))))
411 463
412 (defun appt-delete-window () 464 (defun appt-delete-window ()
413 "Function called to undisplay appointment messages. 465 "Function called to undisplay appointment messages.
414 Usually just deletes the appointment buffer." 466 Usually just deletes the appointment buffer."
415 (let ((window (get-buffer-window appt-buffer-name t))) 467 (let ((window (get-buffer-window appt-buffer-name t)))
435 (defun appt-add (new-appt-time new-appt-msg) 487 (defun appt-add (new-appt-time new-appt-msg)
436 "Add an appointment for the day at NEW-APPT-TIME and issue message NEW-APPT-MSG. 488 "Add an appointment for the day at NEW-APPT-TIME and issue message NEW-APPT-MSG.
437 The time should be in either 24 hour format or am/pm format." 489 The time should be in either 24 hour format or am/pm format."
438 490
439 (interactive "sTime (hh:mm[am/pm]): \nsMessage: ") 491 (interactive "sTime (hh:mm[am/pm]): \nsMessage: ")
440 (if (string-match "[0-9]?[0-9]:[0-9][0-9]\\(am\\|pm\\)?" new-appt-time) 492 (unless (string-match "[0-9]?[0-9][:.][0-9][0-9]\\(am\\|pm\\)?"
441 nil 493 new-appt-time)
442 (error "Unacceptable time-string")) 494 (error "Unacceptable time-string"))
443
444 (let* ((appt-time-string (concat new-appt-time " " new-appt-msg)) 495 (let* ((appt-time-string (concat new-appt-time " " new-appt-msg))
445 (appt-time (list (appt-convert-time new-appt-time))) 496 (appt-time (list (appt-convert-time new-appt-time)))
446 (time-msg (cons appt-time (list appt-time-string)))) 497 (time-msg (cons appt-time (list appt-time-string))))
447 (setq appt-time-msg-list (nconc appt-time-msg-list (list time-msg))) 498 (setq appt-time-msg-list (nconc appt-time-msg-list (list time-msg)))
448 (setq appt-time-msg-list (appt-sort-list appt-time-msg-list)))) 499 (setq appt-time-msg-list (appt-sort-list appt-time-msg-list))))
449 500
450 ;;;###autoload 501 ;;;###autoload
451 (defun appt-delete () 502 (defun appt-delete ()
452 "Delete an appointment from the list of appointments." 503 "Delete an appointment from the list of appointments."
453 (interactive) 504 (interactive)
454 (let* ((tmp-msg-list appt-time-msg-list)) 505 (let ((tmp-msg-list appt-time-msg-list))
455 (while tmp-msg-list 506 (while tmp-msg-list
456 (let* ((element (car tmp-msg-list)) 507 (let* ((element (car tmp-msg-list))
457 (prompt-string (concat "Delete " 508 (prompt-string (concat "Delete "
458 ;; We want to quote any doublequotes 509 ;; We want to quote any doublequotes
459 ;; in the string, as well as put 510 ;; in the string, as well as put
473 (eval-when-compile (defvar number) 524 (eval-when-compile (defvar number)
474 (defvar original-date) 525 (defvar original-date)
475 (defvar diary-entries-list)) 526 (defvar diary-entries-list))
476 ;;;###autoload 527 ;;;###autoload
477 (defun appt-make-list () 528 (defun appt-make-list ()
478 "Create the appointments list from todays diary buffer. 529 "Create the appointments list from today's diary buffer.
479 The time must be at the beginning of a line for it to be 530 The time must be at the beginning of a line for it to be
480 put in the appointments list. 531 put in the appointments list (see examples in documentation of
481 02/23/89 532 the function `appt-check'). We assume that the variables DATE and
482 12:00pm lunch 533 NUMBER hold the arguments that `list-diary-entries' received.
483 Wednesday
484 10:00am group meeting
485 We assume that the variables DATE and NUMBER
486 hold the arguments that `list-diary-entries' received.
487 They specify the range of dates that the diary is being processed for." 534 They specify the range of dates that the diary is being processed for."
488 535
489 ;; We have something to do if the range of dates that the diary is 536 ;; We have something to do if the range of dates that the diary is
490 ;; considering includes the current date. 537 ;; considering includes the current date.
491 (if (and (not (calendar-date-compare 538 (if (and (not (calendar-date-compare
517 (while (and entry-list 564 (while (and entry-list
518 (calendar-date-equal 565 (calendar-date-equal
519 (calendar-current-date) (car (car entry-list)))) 566 (calendar-current-date) (car (car entry-list))))
520 (let ((time-string (cadr (car entry-list)))) 567 (let ((time-string (cadr (car entry-list))))
521 (while (string-match 568 (while (string-match
522 "\\([0-9]?[0-9]:[0-9][0-9]\\(am\\|pm\\)?\\).*" 569 "\\([0-9]?[0-9][:.][0-9][0-9]\\(am\\|pm\\)?\\).*"
523 time-string) 570 time-string)
524 (let* ((beg (match-beginning 0)) 571 (let* ((beg (match-beginning 0))
525 ;; Get just the time for this appointment. 572 ;; Get just the time for this appointment.
526 (only-time (match-string 1 time-string)) 573 (only-time (match-string 1 time-string))
527 ;; Find the end of this appointment 574 ;; Find the end of this appointment
528 ;; (the start of the next). 575 ;; (the start of the next).
529 (end (string-match 576 (end (string-match
530 "^[ \t]*[0-9]?[0-9]:[0-9][0-9]\\(am\\|pm\\)?" 577 "^[ \t]*[0-9]?[0-9][:.][0-9][0-9]\\(am\\|pm\\)?"
531 time-string 578 time-string
532 (match-end 0))) 579 (match-end 0)))
533 ;; Get the whole string for this appointment. 580 ;; Get the whole string for this appointment.
534 (appt-time-string 581 (appt-time-string
535 (substring time-string beg (if end (1- end))))) 582 (substring time-string beg (if end (1- end)))))
554 601
555 (let* ((now (decode-time)) 602 (let* ((now (decode-time))
556 (cur-hour (nth 2 now)) 603 (cur-hour (nth 2 now))
557 (cur-min (nth 1 now)) 604 (cur-min (nth 1 now))
558 (cur-comp-time (+ (* cur-hour 60) cur-min)) 605 (cur-comp-time (+ (* cur-hour 60) cur-min))
559 (appt-comp-time (car (car (car appt-time-msg-list))))) 606 (appt-comp-time (car (caar appt-time-msg-list))))
560 607
561 (while (and appt-time-msg-list (< appt-comp-time cur-comp-time)) 608 (while (and appt-time-msg-list (< appt-comp-time cur-comp-time))
562 (setq appt-time-msg-list (cdr appt-time-msg-list)) 609 (setq appt-time-msg-list (cdr appt-time-msg-list))
563 (if appt-time-msg-list 610 (if appt-time-msg-list
564 (setq appt-comp-time (car (car (car appt-time-msg-list)))))))))) 611 (setq appt-comp-time (car (caar appt-time-msg-list)))))))))
565 612
566 613
567 (defun appt-sort-list (appt-list) 614 (defun appt-sort-list (appt-list)
568 "Simple sort to put the appointments list APPT-LIST in order. 615 "Sort an appointment list, putting earlier items at the front.
569 Scan the list for the smallest element left in the list. 616 APPT-LIST is a list of the same format as `appt-time-msg-list'."
570 Append the smallest element left into the new list, and remove 617 (sort appt-list (lambda (e1 e2) (< (caar e1) (caar e2)))))
571 it from the original list."
572 (let ((order-list nil))
573 (while appt-list
574 (let* ((element (car appt-list))
575 (element-time (car (car element)))
576 (tmp-list (cdr appt-list)))
577 (while tmp-list
578 (if (< element-time (car (car (car tmp-list))))
579 nil
580 (setq element (car tmp-list))
581 (setq element-time (car (car element))))
582 (setq tmp-list (cdr tmp-list)))
583 (setq order-list (nconc order-list (list element)))
584 (setq appt-list (delq element appt-list))))
585 order-list))
586 618
587 619
588 (defun appt-convert-time (time2conv) 620 (defun appt-convert-time (time2conv)
589 "Convert hour:min[am/pm] format to minutes from midnight." 621 "Convert hour:min[am/pm] format to minutes from midnight.
590 622 A period (.) can be used instead of a colon (:) to separate the
623 hour and minute parts."
591 (let ((conv-time 0) 624 (let ((conv-time 0)
592 (hr 0) 625 (hr 0)
593 (min 0)) 626 (min 0))
594 627
595 (string-match ":\\([0-9][0-9]\\)" time2conv) 628 (string-match "[:.]\\([0-9][0-9]\\)" time2conv)
596 (setq min (string-to-int 629 (setq min (string-to-int
597 (match-string 1 time2conv))) 630 (match-string 1 time2conv)))
598 631
599 (string-match "[0-9]?[0-9]:" time2conv) 632 (string-match "[0-9]?[0-9][:.]" time2conv)
600 (setq hr (string-to-int 633 (setq hr (string-to-int
601 (match-string 0 time2conv))) 634 (match-string 0 time2conv)))
602 635
603 ;; convert the time appointment time into 24 hour time 636 ;; convert the time appointment time into 24 hour time
604 637
612 ;; against the actual time. 645 ;; against the actual time.
613 646
614 (setq conv-time (+ (* hr 60) min)) 647 (setq conv-time (+ (* hr 60) min))
615 conv-time)) 648 conv-time))
616 649
617 (defvar appt-timer nil 650
618 "Timer used for diary appointment notifications (`appt-check').") 651 (defun appt-update-list ()
619 652 "If the current buffer is visiting the diary, update appointments.
620 (unless appt-timer 653 This function is intended for use with `write-file-functions'."
621 (setq appt-timer (run-at-time t 60 'appt-check))) 654 (and (string-equal buffer-file-name (expand-file-name diary-file))
622 655 appt-timer
623 (or global-mode-string (setq global-mode-string '(""))) 656 (let ((appt-display-diary nil))
624 (or (memq 'appt-mode-string global-mode-string) 657 (appt-check t)))
625 (setq global-mode-string 658 nil)
626 (append global-mode-string '(appt-mode-string)))) 659
627 660
661 ;;;###autoload
662 (defun appt-activate (&optional arg)
663 "Toggle checking of appointments.
664 With optional numeric argument ARG, turn appointment checking on if
665 ARG is positive, otherwise off."
666 (interactive "P")
667 (let ((appt-active appt-timer))
668 (setq appt-active (if arg (> (prefix-numeric-value arg) 0)
669 (not appt-active)))
670 (remove-hook 'write-file-functions 'appt-update-list)
671 (or global-mode-string (setq global-mode-string '("")))
672 (delq 'appt-mode-string global-mode-string)
673 (when appt-timer
674 (cancel-timer appt-timer)
675 (setq appt-timer nil))
676 (when appt-active
677 (add-hook 'write-file-functions 'appt-update-list)
678 (setq appt-timer (run-at-time t 60 'appt-check)
679 global-mode-string
680 (append global-mode-string '(appt-mode-string)))
681 (appt-check t))))
682
683
684 ;; This is needed for backwards compatibility. Feh.
685 (appt-activate 1)
686
687
688 (provide 'appt)
689
690 ;;; arch-tag: bf5791c4-8921-499e-a26f-772b1788d347
628 ;;; appt.el ends here 691 ;;; appt.el ends here