Fix bug#6999.
[bpt/emacs.git] / lisp / calendar / appt.el
index 9823abb..0818492 100644 (file)
@@ -1,7 +1,7 @@
 ;;; 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>
@@ -9,10 +9,10 @@
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,9 +20,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 ;; 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'.
@@ -185,16 +183,25 @@ Only relevant if reminders are being displayed in a window."
 (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.")
@@ -227,7 +234,7 @@ If this is non-nil, appointment checking is active.")
 The string STRING describes the appointment, due in integer MINS minutes.
 The format of the visible reminder is controlled by `appt-display-format'.
 The variable `appt-audible' controls the audible reminder."
-  ;; Let-binding for backwards compatability.  Remove when obsolete
+  ;; Let-binding for backwards compatibility.  Remove when obsolete
   ;; vars appt-msg-window and appt-visible are dropped.
   (let ((appt-display-format
          (if (eq appt-display-format 'ignore)
@@ -315,7 +322,7 @@ displayed in a window:
               (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).
@@ -332,21 +339,21 @@ displayed in a window:
                                diary-hook
                              (cons 'appt-make-list diary-hook))))
                       (diary))
-                  (let* ((diary-display-hook 'appt-make-list)
-                         (d-buff (find-buffer-visiting
-                                  (substitute-in-file-name diary-file)))
+                  (let* ((diary-display-function 'appt-make-list)
+                         (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
@@ -357,6 +364,8 @@ displayed in a window:
         ;; 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))
@@ -364,21 +373,21 @@ displayed in a window:
             (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))
@@ -410,16 +419,18 @@ displayed in a window:
   "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.
@@ -472,14 +483,28 @@ Usually just deletes the appointment buffer."
   "[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)))))))
@@ -540,7 +565,7 @@ appointment package (if it is not already active)."
                     (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)
@@ -568,7 +593,7 @@ appointment package (if it is not already active)."
                                  (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.
@@ -619,8 +644,10 @@ hour and minute parts."
 
 (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)))
@@ -634,13 +661,13 @@ This function is intended for use with `write-file-functions'."
 ;; 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