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