;;; appt.el --- appointment notification functions
;; Copyright (C) 1989, 1990, 1994, 1998, 2001, 2002, 2003, 2004, 2005,
-;; 2006, 2007, 2008 Free Software Foundation, Inc.
+;; 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
;; Author: Neil Mager <neilm@juliet.ll.mit.edu>
;; Maintainer: Glenn Morris <rgm@gnu.org>
;; documentation of that function.
;;
;; Today's appointment list is initialized from the diary when this
-;; package is activated. Additionally, the appointments list is
+;; package is activated. Additionally, the appointments list is
;; recreated automatically at 12:01am for those who do not logout
-;; every day or are programming late. It is also updated when the
-;; `diary-file' is saved. Calling `appt-check' with an argument forces
-;; a re-initialization at any time.
+;; every day or are programming late. It is also updated when the
+;; `diary-file' is saved. Calling `appt-check' with an argument (or
+;; re-enabling the package) forces a re-initialization at any time.
;;
;; In order to add or delete items from today's list, without
;; changing the diary file, use `appt-add' and `appt-delete'.
(defconst appt-buffer-name "*appt-buf*"
"Name of the appointments buffer.")
+;; TODO Turn this into an alist? It would be easier to add more
+;; optional elements.
+;; TODO There should be a way to set WARNTIME (and other properties)
+;; from the diary-file. Implementing that would be a good reason
+;; to change this to an alist.
(defvar appt-time-msg-list nil
"The list of appointments for today.
Use `appt-add' and `appt-delete' to add and delete appointments.
The original list is generated from today's `diary-entries-list', and
can be regenerated using the function `appt-check'.
-Each element of the generated list has the form (MINUTES STRING [FLAG]); where
-MINUTES is the time in minutes of the appointment after midnight, and
-STRING is the description of the appointment.
-FLAG, if non-nil, says that the element was made with `appt-add'
-so calling `appt-make-list' again should preserve it.")
+Each element of the generated list has the form
+\(MINUTES STRING [FLAG] [WARNTIME])
+where MINUTES is the time in minutes of the appointment after midnight,
+and STRING is the description of the appointment.
+FLAG and WARNTIME can only be present if the element was made
+with `appt-add'. A non-nil FLAG indicates that the element was made
+with `appt-add', so calling `appt-make-list' again should preserve it.
+If WARNTIME is non-nil, it is an integer to use in place
+of `appt-message-warning-time'.")
(defconst appt-max-time (1- (* 24 60))
"11:59pm in minutes - number of minutes in a day minus 1.")
(zerop (mod prev-appt-display-count appt-display-interval))))
;; Non-nil means only update the interval displayed in the mode line.
(mode-line-only (unless full-check appt-now-displayed))
- now cur-comp-time appt-comp-time)
+ now cur-comp-time appt-comp-time appt-warn-time)
(when (or full-check mode-line-only)
(save-excursion
;; Convert current time to minutes after midnight (12.01am = 1).
(cons 'appt-make-list diary-hook))))
(diary))
(let* ((diary-display-function 'appt-make-list)
- (d-buff (find-buffer-visiting
- (substitute-in-file-name diary-file)))
+ (d-buff (find-buffer-visiting diary-file))
(selective
(if d-buff ; diary buffer exists
(with-current-buffer d-buff
diary-selective-display))))
+ ;; FIXME why not using diary-list-entries with
+ ;; non-nil LIST-ONLY?
(diary)
;; If the diary buffer existed before this command,
;; restore its display state. Otherwise, kill it.
(if d-buff
;; Displays the diary buffer.
(or selective (diary-show-all-entries))
- (and (setq d-buff (find-buffer-visiting
- (substitute-in-file-name diary-file)))
+ (and (setq d-buff (find-buffer-visiting diary-file))
(kill-buffer d-buff)))))
(error nil)))
(setq appt-prev-comp-time cur-comp-time
;; calculate the number of minutes until the appointment.
(when (and appt-issue-message appt-time-msg-list)
(setq appt-comp-time (caar (car appt-time-msg-list))
+ appt-warn-time (or (nth 3 (car appt-time-msg-list))
+ appt-message-warning-time)
min-to-app (- appt-comp-time cur-comp-time))
(while (and appt-time-msg-list
(< appt-comp-time cur-comp-time))
(if appt-time-msg-list
(setq appt-comp-time (caar (car appt-time-msg-list)))))
;; If we have an appointment between midnight and
- ;; `appt-message-warning-time' minutes after midnight, we
+ ;; `appt-warn-time' minutes after midnight, we
;; must begin to issue a message before midnight. Midnight
;; is considered 0 minutes and 11:59pm is 1439
;; minutes. Therefore we must recalculate the minutes to
;; appointment variable. It is equal to the number of
;; minutes before midnight plus the number of minutes after
;; midnight our appointment is.
- (if (and (< appt-comp-time appt-message-warning-time)
- (> (+ cur-comp-time appt-message-warning-time)
+ (if (and (< appt-comp-time appt-warn-time)
+ (> (+ cur-comp-time appt-warn-time)
appt-max-time))
(setq min-to-app (+ (- (1+ appt-max-time) cur-comp-time)
appt-comp-time)))
;; Issue warning if the appointment time is within
;; appt-message-warning time.
- (when (and (<= min-to-app appt-message-warning-time)
+ (when (and (<= min-to-app appt-warn-time)
(>= min-to-app 0))
(setq appt-now-displayed t
appt-display-count (1+ prev-appt-display-count))
"Display appointment due in MIN-TO-APP (a string) minutes.
NEW-TIME is a string giving the date. Displays the appointment
message APPT-MSG in a separate buffer."
- ;; Make sure we're not in the minibuffer before splitting the window.
- ;; FIXME this seems needlessly complicated?
- (when (minibufferp)
- (other-window 1)
- (and (minibufferp) (display-multi-frame-p) (other-frame 1)))
(let ((this-window (selected-window))
- (appt-disp-buf (set-buffer (get-buffer-create appt-buffer-name))))
+ (appt-disp-buf (get-buffer-create appt-buffer-name)))
+ ;; Make sure we're not in the minibuffer before splitting the window.
+ ;; FIXME this seems needlessly complicated?
+ (when (minibufferp)
+ (other-window 1)
+ (and (minibufferp) (display-multi-frame-p) (other-frame 1)))
(if (cdr (assq 'unsplittable (frame-parameters)))
;; In an unsplittable frame, use something somewhere else.
- (display-buffer appt-disp-buf)
+ (progn
+ (set-buffer appt-disp-buf)
+ (display-buffer appt-disp-buf))
(unless (or (special-display-p (buffer-name appt-disp-buf))
(same-window-p (buffer-name appt-disp-buf)))
;; By default, split the bottom window and use the lower part.
"[0-9]?[0-9]\\(h\\([0-9][0-9]\\)?\\|[:.][0-9][0-9]\\)\\(am\\|pm\\)?")
;;;###autoload
-(defun appt-add (new-appt-time new-appt-msg)
- "Add an appointment for today at NEW-APPT-TIME with message NEW-APPT-MSG.
-The time should be in either 24 hour format or am/pm format."
- (interactive "sTime (hh:mm[am/pm]): \nsMessage: ")
- (unless (string-match appt-time-regexp new-appt-time)
+(defun appt-add (time msg &optional warntime)
+ "Add an appointment for today at TIME with message MSG.
+The time should be in either 24 hour format or am/pm format.
+Optional argument WARNTIME is an integer (or string) giving the number
+of minutes before the appointment at which to start warning.
+The default is `appt-message-warning-time'."
+ (interactive "sTime (hh:mm[am/pm]): \nsMessage:
+sMinutes before the appointment to start warning: ")
+ (unless (string-match appt-time-regexp time)
(error "Unacceptable time-string"))
- (let ((time-msg (list (list (appt-convert-time new-appt-time))
- (concat new-appt-time " " new-appt-msg) t)))
+ (and (stringp warntime)
+ (setq warntime (unless (string-equal warntime "")
+ (string-to-number warntime))))
+ (and warntime
+ (not (integerp warntime))
+ (error "Argument WARNTIME must be an integer, or nil"))
+ (let ((time-msg (list (list (appt-convert-time time))
+ (concat time " " msg) t)))
+ ;; It is presently non-sensical to have multiple warnings about
+ ;; the same appointment with just different delays, but it might
+ ;; not always be so. TODO
+ (if warntime (setq time-msg (append time-msg (list warntime))))
(unless (member time-msg appt-time-msg-list)
(setq appt-time-msg-list
(appt-sort-list (nconc appt-time-msg-list (list time-msg)))))))
(delq elt appt-time-msg-list))))
(if diary-entries-list
;; Cycle through the entry-list (diary-entries-list)
- ;; looking for entries beginning with a time. If the
+ ;; looking for entries beginning with a time. If the
;; entry begins with a time, add it to the
;; appt-time-msg-list. Then sort the list.
(let ((entry-list diary-entries-list)
(match-end 0)))
;; Get the whole string for this appointment.
(appt-time-string
- (substring time-string beg (if end (1- end))))
+ (substring time-string beg end))
(appt-time (list (appt-convert-time only-time)))
(time-msg (list appt-time appt-time-string)))
;; Add this appointment to appt-time-msg-list.
(defun appt-update-list ()
"If the current buffer is visiting the diary, update appointments.
-This function is intended for use with `write-file-functions'."
- (and (string-equal buffer-file-name (expand-file-name diary-file))
+This function also acts on any file listed in `diary-included-files'.
+It is intended for use with `write-file-functions'."
+ (and (member buffer-file-name (append diary-included-files
+ (list (expand-file-name diary-file))))
appt-timer
(let ((appt-display-diary nil))
(appt-check t)))
;; The display-time call was not necessary, AFAICS.
;; What was really needed was to add the hook and load this file.
;; Calling (diary 0) once the hook had been added was in some sense a
-;; roundabout way of loading this file. This file used to have code at
+;; roundabout way of loading this file. This file used to have code at
;; the top-level that set up the appt-timer and global-mode-string.
;; One way to maintain backwards compatibility would be to call
-;; (appt-activate 1) at top-level. However, this goes against the
+;; (appt-activate 1) at top-level. However, this goes against the
;; convention that just loading an Emacs package should not activate
-;; it. Instead, we make appt-make-list activate the package (after a
-;; suggestion from rms). This means that one has to call diary in
+;; it. Instead, we make appt-make-list activate the package (after a
+;; suggestion from rms). This means that one has to call diary in
;; order to get it to work, but that is in line with the old (weird,
;; IMO) documented behavior for activating the package.
;; Actually, since (diary 0) does not run diary-hook, I don't think