>From cf17d3430324cf43d77e3260b4f7dabe6b005860 Mon Sep 17 00:00:00 2001 From: Ivan Kanis Date: Sun, 27 Mar 2011 17:59:07 +0200 Subject: [PATCH 12/13] implement multiple appointments Change strategy in appt-check : first remove old appointments, go through all appointments and pick relevant appointments, last display mode line and message. Add a new variable appt-display-multiple that is set to t by default. I think setting it to t is a good idea to avoid missing appointments. This can happen when a second appointment has a warning time that precedes the first. Add a new variable appt-disp-window-multiple-function that takes the function to display multiple appointments. Add the function appt-disp-multiple-window that does the actual displaying. Add function appt-display-multiple-message that is called from appt-check. Note that the minutes passed is an integer instead of a string for the single appointment version. I just feel the conversion is kind of silly. --- appt.el | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 125 insertions(+), 25 deletions(-) diff --git a/appt.el b/appt.el index 9bae717..1a9e9f3 100644 --- a/appt.el +++ b/appt.el @@ -164,13 +164,24 @@ This will occur at midnight when the appointment list is updated." (defcustom appt-disp-window-function 'appt-disp-window "Function called to display appointment window. -Only relevant if reminders are being displayed in a window. -It should take three string arguments: the number of minutes till -the appointment, the current time, and the text of the appointment." +It's called when `appt-display-multiple' is set to nil. Only +relevant if reminders are being displayed in a window. It should +take three string arguments: the number of minutes till the +appointment, the current time, and the text of the appointment." :type '(choice (const appt-disp-window) function) :group 'appt) +(defcustom appt-disp-window-multiple-function 'appt-disp-multiple-window + "Function called to display appointment window. +It's called when `appt-display-multiple' is set to t. Only +relevant if reminders are being displayed in a window. It takes +two argument the first is the number of minutes until the first +appointment the second contain a list of the appointments." + :type '(choice (const appt-disp-multiple-window) + function) + :group 'appt) + (defcustom appt-delete-window-function 'appt-delete-window "Function called to remove appointment window and buffer. Only relevant if reminders are being displayed in a window." @@ -178,6 +189,10 @@ Only relevant if reminders are being displayed in a window." function) :group 'appt) +(defcustom appt-display-multiple t + "Non-nil means display multiple appointment." + :type 'boolean + :group 'appt) ;;; Internal variables below this point. @@ -252,6 +267,26 @@ The variable `appt-audible' controls the audible reminder." ((eq appt-display-format 'echo) (message "%s" string))))) +(defun appt-display-multiple-message (appt-list mins) + "Display a list of due appointments, first appointment in MINS minutes. +The list APPT-LIST contains the list of appointments. +The format of the visible reminder is controlled by `appt-display-format'. +The variable `appt-audible' controls the audible reminder." + ;; 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) + (cond (appt-msg-window 'window) + (appt-visible 'echo)) + appt-display-format))) + (if appt-audible (beep 1)) + (cond ((eq appt-display-format 'window) + (funcall appt-disp-window-multiple-function mins appt-list) + (run-at-time (format "%d sec" appt-display-duration) + nil + appt-delete-window-function)) + ((eq appt-display-format 'echo) + (message "%s" (mapconcat 'identity (nreverse appt-list) " ")))))) (defvar diary-selective-display) @@ -312,24 +347,24 @@ displayed in a window: ;; Convert current time to minutes after midnight (12.01am = 1). (ac-cur-time (+ (* 60 (nth 2 ac-now)) (nth 1 ac-now))) (ac-appt-list appt-time-msg-list) - ac-appt-time ac-warn-time ac-min-to-app) + (ac-first nil) + ac-appt-time ac-warn-time ac-min-to-app ac-msg ac-msg-list) (save-excursion (appt-check-diary force ac-cur-time) ;; Discard appointments previous to current time (while (and ac-appt-list - (setq ac-appt-time (caar (car ac-appt-list))) - (< ac-appt-time ac-cur-time)) + (< (caar (car ac-appt-list)) ac-cur-time)) (setq ac-appt-list (cdr ac-appt-list))) - (setq appt-time-msg-list ac-appt-list) - ;; If there are entries in the list, and the user wants a - ;; message issued, get the first time off of the list and - ;; calculate the number of minutes until the appointment. - (when ac-appt-list + (setq appt-time-msg-list ac-appt-list + appt-mode-string nil + appt-display-count (1+ appt-display-count)) + + (while ac-appt-list (setq ac-warn-time (or (nth 3 (car ac-appt-list)) appt-message-warning-time) - ac-min-to-app (- ac-appt-time ac-cur-time) ac-appt-time (caar (car ac-appt-list)) - appt-mode-string nil) + ac-min-to-app (- ac-appt-time ac-cur-time)) + ;; If we have an appointment between midnight and ;; `ac-warn-time' minutes after midnight, we ;; must begin to issue a message before midnight. Midnight @@ -347,23 +382,28 @@ displayed in a window: ;; appt-message-warning time. (when (and (<= ac-min-to-app ac-warn-time) (>= ac-min-to-app 0)) - ;; This is true every appt-display-interval minutes. - (if (zerop (mod appt-display-count appt-display-interval)) - (appt-display-message (cadr (car ac-appt-list)) - ac-min-to-app)) - (setq appt-display-count (1+ appt-display-count)) - (when appt-display-mode-line - (setq appt-mode-string - (concat " " (propertize - (format "App't in %s min." ac-min-to-app) - 'face 'mode-line-emphasis)))) + (setq ac-msg (cadr (car ac-appt-list))) + (add-to-list 'ac-msg-list ac-msg) + (when (not ac-first) + (setq ac-first (list ac-min-to-app ac-msg))) ;; When an appointment is reached reset the count to 0 in ;; case we display another appointment on the next cycle. (if (zerop ac-min-to-app) - (setq appt-display-count 0)))) + (setq appt-display-count 0))) + (setq ac-appt-list (cdr ac-appt-list))) + (when (and ac-first + (zerop (mod appt-display-count appt-display-interval)) + (if appt-display-multiple + (appt-display-multiple-message ac-msg-list (car ac-first)) + (appt-display-message (cadr ac-first) (car ac-first))))) ;; Redisplay all mode lines. (when appt-display-mode-line - (force-mode-line-update))))) + (when ac-first + (setq appt-mode-string + (concat " " (propertize + (format "App't in %s min." (car ac-first)) + 'face 'mode-line-emphasis))))) + (force-mode-line-update)))) (defun appt-check-diary (force cur-comp-time) "Update appointments to today's list." @@ -450,6 +490,25 @@ message APPT-MSG in a separate buffer." (raise-frame (selected-frame)) (select-window this-window))) +(defun appt-disp-multiple-window (min-to-app appt-list) + "Display list of APPT-LIST." + (let ((this-window (selected-window))) + (with-current-buffer (get-buffer-create appt-buffer-name) + (setq buffer-read-only nil + buffer-undo-list t) + (erase-buffer) + (insert (mapconcat 'identity (nreverse appt-list) "\n"))) + (pop-to-buffer appt-buffer-name) + (fit-window-to-buffer) + (calendar-set-mode-line + (format " Appointment %s." + (if (= min-to-app 0) "now" + (format "in %d minute%s" min-to-app + (if (= min-to-app 1) "" "s"))))) + + (raise-frame (selected-frame)) + (select-window this-window))) + (defun appt-delete-window () "Function called to undisplay appointment messages. Usually just deletes the appointment buffer." @@ -705,6 +764,47 @@ ARG is positive, otherwise off." (appt-check t)))) +;;; Test functions +;; two helper functions for tests +;; (defun appt-test-add (min-from-now msg warntime) +;; (appt-add +;; (format-time-string +;; "%H:%M" +;; (let ((time (current-time))) +;; (cons (nth 0 time) +;; (+ (* 60 min-from-now) (nth 1 time))))) msg warntime)) + +;; (defun appt-test-init () +;; (setq appt-display-count 0) +;; (setq appt-display-interval 1) +;; (setq appt-time-msg-list nil)) + +;; appointment in a minute +;; (progn +;; (appt-test-init) +;; (add-appt 1 "one minute" 1)) + +;; two appointment one after the other, it should display: +;; 1 first +;; 2 first and second +;; 3 second +;; (progn +;; (appt-test-init) +;; (add-appt 1 "first" 1) +;; (add-appt 2 "second" 1)) + +;; typical shadowing problem solved by multiple appointments. The +;; second appointment has warning time that precedes the first. I have +;; actually missed an appointment because of this! It should display: +;; 1 second +;; 2 first and second +;; 3 first and second +;; 4 second +;; (progn +;; (appt-test-init) +;; (add-appt 2 "first" 1) +;; (add-appt 3 "second" 3)) + (provide 'appt) ;; arch-tag: bf5791c4-8921-499e-a26f-772b1788d347 -- 1.7.1