Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. ;; cal-desk-calendar.el --- Desk calendar style extensions to Emacs' Calendar/Diary
  2.  
  3. ;; Copyright (C) 1995, 1999 D. Dale Gulledge.
  4. ;;
  5. ;; Author: D. Dale Gulledge <dsplat@rochester.rr.com>
  6. ;; Version: 0.8 (1999/08/11)
  7. ;; Keywords: calendar
  8. ;; Human-Keywords: desk calendar, diary
  9.  
  10. ;; This file is derived from functions in the Calendar/Diary facility
  11. ;; of GNU Emacs.  The copyright is currently held by the author,
  12. ;; D. Dale Gulledge, pending assignment to the Free Software
  13. ;; Foundation.  It may be used under the terms of the GNU General
  14. ;; Public License (also known as the GPL or GNU Copyleft).
  15. ;;
  16. ;; GNU Emacs is free software; you can redistribute it and/or modify
  17. ;; it under the terms of the GNU General Public License as published by
  18. ;; the Free Software Foundation; either version 2, or (at your option)
  19. ;; any later version.
  20.  
  21. ;; GNU Emacs is distributed in the hope that it will be useful,
  22. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24. ;; GNU General Public License for more details.
  25.  
  26. ;; You should have received a copy of the GNU General Public License
  27. ;; along with GNU Emacs; see the file COPYING.  If not, write to the
  28. ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  29. ;; Boston, MA 02111-1307, USA.
  30.  
  31. ;; To activate this feature, include the following lines in your
  32. ;; .emacs:
  33. ;;
  34. ;; (load-library "cal-desk-calendar")
  35. ;; (add-hook 'diary-display-hook 'sort-diary-entries)
  36. ;; (add-hook 'diary-display-hook 'fancy-schedule-display-desk-calendar t)
  37. ;;
  38. ;; It will replace the normal diary display mode when you run either the d
  39. ;; command from the Calendar buffer after M-X calendar, or when you directly
  40. ;; format your diary using M-x diary.
  41.  
  42. ;; To Do:
  43. ;;
  44. ;; 1) Rewrite display engine into two separate passes.  The first
  45. ;;    builds a sorted list of appointments and the second displays it
  46. ;;    in the preferred style.  This will allow the first pass to be
  47. ;;    used for various different displays, including generation of
  48. ;;    LaTeX source for various formats.
  49. ;; 2) Write some comments describing some of the obscure portions, if
  50. ;;    they survive the rewrite.
  51. ;; 3) Add a variable to specify the increments in which the grid will
  52. ;;    be extended instead of simply using
  53. ;;    diary-schedule-interval-time.  That would allow, for example, an
  54. ;;    interval of 30 minutes, but extending the calendar in increments
  55. ;;    of an hour.
  56. ;; 4) Add some functions that directly implement some of the recurring
  57. ;;    event options described in RFC 2446 (especially section
  58. ;;    4.8.5.4).  All of these can be constructed with sexp entries,
  59. ;;    but some of the code gets a bit hairy to embed in the diary file
  60. ;;    for each entry.  It seems better to have a library of functions
  61. ;;    like the ones that are still tagging along at the end of this
  62. ;;    file.
  63. ;; 5) Implement handling of multiple timezones explicitly.  The real
  64. ;;    problem is when the calendar dates for a particular moment in
  65. ;;    time differ between the zones.  This will probably require
  66. ;;    grabbing the events for the previous and following dates and
  67. ;;    checking their times after the TZ calculations.
  68. ;; 6) Add an escape mechanism for the time format strings, or better still,
  69. ;;    convert to the mechanism that Ed is already using in the diary code.
  70. ;; 7) Prep for internationalization.  Pull text strings out into variables.
  71. ;;    Doctor the main diary code to allow internationalization of the
  72. ;;    sunrise/sunset messages, lunar phases, solstices and equinoxes.
  73. ;; 8) Add an option for a double | for periods containing overlapping events.
  74. ;; 9) Clean up the code by making variables local where possible.
  75. ;; 10) Pull the common code for dealing with the out of range events into a
  76. ;;    separate function instead of having two identical blocks.
  77. ;; x 11) Make the | configurable.
  78. ;; 12) Add an option to allow a fixed string to replace appointment text for
  79. ;;    diary entries.  This would allow producing a calendar that indicated
  80. ;;    which times were committed and which were free without revealing the
  81. ;;    details of those appointments, suitable for mailing to someone for
  82. ;;    setting up meetings.
  83. ;; x 13) Make special time patterns (Workday, Morning, etc.) configurable.  This
  84. ;;    is important for internationalization anyway.
  85.  
  86. (require 'calendar)
  87. ;;(require 'diary)
  88. (require 'diary-lib)
  89. ;;(require 'diary-ins)
  90. (require 'solar)
  91. (require 'lunar)
  92.  
  93. (defconst diary-subsequent-date-prefix-string "\n\f"
  94.   "The string which preceeds each day\'s diary except the first in the Fancy Diary Buffer.")
  95.  
  96. (defvar diary-default-schedule-start-time 800
  97.   "*The time to which diary-schedule-start-time is set for each day\'s schedule.")
  98.  
  99. (defvar diary-default-schedule-stop-time 1730
  100.   "*The time to which diary-schedule-stop-time is set for each day\'s schedule.")
  101.  
  102. (setq diary-schedule-start-time diary-default-schedule-start-time
  103.       diary-schedule-stop-time diary-default-schedule-stop-time)
  104.  
  105. (defvar diary-schedule-interval-time 30
  106.   "*The number of minutes per interval in the day\'s schedule.")
  107.  
  108. (defvar diary-schedule-line-offset 2
  109.   "The line in the fancy diary buffer on which the schedule starts.")
  110.  
  111. (defvar diary-schedule-expand-grid t
  112.   "*Determines whether the grid will be expanded to fit appointments outside the range.")
  113.  
  114. (defvar diary-morning-times '(800 1200)
  115.   "*The times that appointments labelled Morning begin and end.")
  116.  
  117. (defvar diary-afternoon-times '(1300 1700)
  118.   "*The times that appointments labelled Afternoon begin and end.")
  119.  
  120. (defvar diary-workday-times '(800 1200 1300 1700)
  121.   "*The times that appointments labelled Workday begin and end.")
  122.  
  123. (defvar diary-all-day-times '(800 1700)
  124.   "*The times that appointments labelled All Day begin and end.")
  125.  
  126. (defvar diary-schedule-time-display-format "24:mm"
  127.   "*The format in which to print the times on the fancy schedule.
  128. Options are 24:mm, 12:mm or \"12:mm ap\".")
  129.  
  130. (defvar diary-schedule-time-overflow-display-format "  :mm"
  131.   "*The format in which to print the times on the fancy schedule.
  132. Options are 24:mm, 12:mm or \"12:mm ap\".")
  133.  
  134. (defvar diary-schedule-appointment-separator "|"
  135.   "*This string will be used to separate the time from the text of each diary
  136. entry.")
  137.  
  138. (defvar diary-schedule-time-odd-end-time-format
  139.   (concat "  :mm " diary-schedule-appointment-separator " ")
  140.   "*The format in which odd end times are printed.  Should include the trailing
  141. \"  | \" and may include time formats to the right of that as well.")
  142.  
  143. (defvar diary-schedule-format-odd-end-time-with-hours nil
  144.   "*Whether to force hours to be printed in odd end times even when the end
  145. time does not fall on an hour boundary.")
  146.  
  147. (defvar diary-schedule-fill-prefix-for-broken-lines
  148.   (concat "      " diary-schedule-appointment-separator "   ")
  149.   "*The fill prefix to use at the beginning of long lines that are broken to fit.
  150. The suggested value for this is the same as the beginning of all of the other lines
  151. with the time replaced by spaces and a couple of extra spaces after the |.")
  152.  
  153. (defvar diary-am-string "am"
  154.   "*The string to display for morning times when the am format option is chosen.")
  155.  
  156. (defvar diary-pm-string "pm"
  157.   "*The string to display for afternoon times when the am format option is chosen.")
  158.  
  159. (defvar diary-AM-string "AM"
  160.   "*The string to display for morning times when the AM format option is chosen.")
  161.  
  162. (defvar diary-PM-string "PM"
  163.   "*The string to display for afternoon times when the AM format option is chosen.")
  164.  
  165. (defvar diary-schedule-first-time-format nil
  166.   "*The format for the first time of the day to be printed in.
  167. nil indicates that the old method of using either
  168. diary-schedule-time-display-format or
  169. diary-schedule-time-overflow-display-format should be employed.  Otherwise,
  170. the same formatting options that are used for those variables may be employed.")
  171.  
  172. (defvar diary-duplicate-time-display nil
  173.   "*Whether to display the time on second and subsequent lines when the time is
  174. the same.")
  175.  
  176. (defvar diary-schedule-odd-times-get-separate-entry t
  177.   "*Whether times that do not fall on an interval boundary get a separate line.")
  178.  
  179. (defvar diary-schedule-print-odd-end-time t
  180.   "*Whether the end time of a period that does not fall on an interval boundary
  181. gets printed on a separate line.")
  182.  
  183. (defvar diary-schedule-print-minute-after-odd-end-time nil
  184.   "*Whether the first minute after an end time that doesn't fall on an interval
  185. boundary gets printed on a separate line.")
  186.  
  187. (defvar diary-schedule-first-time-always-has-hours t
  188.   "*Whether the time printed for the first interval of the day should contain the
  189. hour regardless of whether it falls on an hour boundary.")
  190.  
  191. (defconst diary-schedule-entry-separator-string "%%DIARY-ENTRY-SEPARATOR%%"
  192.   "Used internally to separate entries during formatting to allow easy
  193. calculation of line numbers.")
  194.  
  195. (defvar diary-schedule-sunrise-sunset-pattern "^[   ]*Sunrise \\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\> *\\(([A-Za-z 0-9+-]*)\\)?, sunset \\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\> (\\([A-Za-z 0-9+-]*)\\)?"
  196.   "*Pattern to match for %%(diary-sunrise-sunset) diary entries.")
  197.  
  198. (defvar diary-schedule-lunar-phase-pattern "^[  ]*\\(New\\|First Quarter\\|Full\\|Last Quarter\\) Moon \\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\> ([A-Z0-9+-]*)"
  199.   "*Pattern to match for %%(diary-phases-of-moon) diary entries")
  200.  
  201. (defvar diary-schedule-equinox-solstice-pattern "^[     ]*\\(Vernal Equinox\\|Summer Solstice\\|Autumnal Equinox\\|Winter Solstice\\) \\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\> ([A-Z0-9+-]*)"
  202.   "*Pattern to match for %%(diary-equinoxes-solstices) diary entries")
  203.  
  204. (defvar diary-schedule-morning-pattern "^[  ]*\\([Mm][oO][rR][nN][iI][nN][gG]\\)"
  205.   "*Pattern to match for Morning diary entries.")
  206.  
  207. (defvar diary-schedule-afternoon-pattern "^[    ]*\\([Aa][fF][tT][eE][rR][nN][oO][oO][nN]\\)"
  208.   "*Pattern to match for Afternoon diary entries.")
  209.  
  210. (defvar diary-schedule-workday-pattern "^[  ]*\\([Ww][oO][rR][kK][dD][aA][yY]\\)"
  211.   "*Pattern to match for Workday diary entries.")
  212.  
  213. (defvar diary-schedule-all-day-pattern "^[  ]*\\([Aa][lL][lL] [Dd][aA][yY]\\)"
  214.   "*Pattern to match for All Day diary entries.")
  215.  
  216. (defvar diary-schedule-place-out-of-bounds-entries-last t
  217.   "*Whether to place entries which fall outside the schedule range or which
  218. have no specified time after the grid for the day.
  219. t indicates after, nil indicates before")
  220.  
  221. (defvar diary-schedule-sunrise-string "Sunrise"
  222.   "*A string containing the word for sunrise in the user\'s preferred language.")
  223.  
  224. (defvar diary-schedule-sunset-string "Sunset"
  225.   "*A string containing the word for sunset in the user\'s preferred language.")
  226.  
  227. (defvar diary-schedule-astronomical-event-time-format "HH:MM"
  228.   "*The format for times in sunrise/sunset, lunar phase, and equinox/solstice
  229. diary entries.")
  230.  
  231. (defvar diary-schedule-daylight-hours-string "hours of daylight"
  232.   "*A string containing the phrase for \"hours of daylight\" in the user\'s
  233. preferred language.")
  234.  
  235. (setq calendar-holidays
  236.   (append general-holidays local-holidays other-holidays solar-holidays))
  237.  
  238. (defun within-3-month-range (entry)
  239.   "Determine if a date falls within a month either way of the current month."
  240.   (if (and (= (nth 2 (car entry)) displayed-year)
  241.        (<= (abs (- (nth 1 (car entry)) displayed-month)) 1))
  242.       entry
  243.     nil))
  244.  
  245. (defun diary-entry-time (s)
  246.   "Time at the beginning of the string S in a military-style integer.
  247. For example, returns 1325 for 1:25pm.  Returns -9999 if no time is recognized.
  248. The recognized forms are XXXX or X:XX or XX:XX (military time), XXam or XXpm,
  249. and XX:XXam or XX:XXpm."
  250.   (cond ((string-match;; Military time  
  251.           "^[   ]*\\([0-9]?[0-9]\\):?\\([0-9][0-9]\\)\\(\\>\\|[^ap]\\)" s)
  252.          (+ (* 100 (string-to-int
  253.                     (substring s (match-beginning 1) (match-end 1))))
  254.             (string-to-int (substring s (match-beginning 2) (match-end 2)))))
  255.         ((string-match;; Hour only  XXam or XXpm
  256.           "^[   ]*\\([0-9]?[0-9]\\)\\([ap]\\)m\\>" s)
  257.          (+ (* 100 (% (string-to-int
  258.                          (substring s (match-beginning 1) (match-end 1)))
  259.                         12))
  260.             (if (string-equal "a"
  261.                               (substring s (match-beginning 2) (match-end 2)))
  262.                 0 1200)))
  263.         ((string-match;; Hour and minute  XX:XXam or XX:XXpm
  264.           "^[   ]*\\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\>" s)
  265.          (+ (* 100 (% (string-to-int
  266.                          (substring s (match-beginning 1) (match-end 1)))
  267.                         12))
  268.             (string-to-int (substring s (match-beginning 2) (match-end 2)))
  269.             (if (string-equal "a"
  270.                               (substring s (match-beginning 3) (match-end 3)))
  271.                 0 1200)))
  272.         (t -9999)));; Unrecognizable
  273.  
  274. (defun diary-format-time (format time display-hour)
  275.   "Using FORMAT produce string with formatted TIME.  DISPLAY_HOUR determines whether to display the hour.
  276.  Options:
  277.     hh, 12  Hour on 12 hour clock with leading space.
  278.     0h      Hour on 12 hour clock with leading 0.
  279.     HH, 24  Hour on 24 hour clock with leading space.
  280.     0H      Hour on 24 hour clock with leading 0.
  281.     mm, MM  Minutes with leading 0.
  282.     ap, am, pm  am|pm
  283.     AP, AM, PM  AM|PM"
  284.   (let ((hour (/ time 100))
  285.     (minute (% time 100)))
  286.     (if (< (length format) 2)
  287.     format
  288.       (let ((form (substring format 0 2)))
  289.     (cond
  290.      ((or (string= form "hh") (string= form "12"))
  291.       (concat (if display-hour (format "%2d" (1+ (% (1- hour) 12))) "  ")
  292.           (diary-format-time (substring format 2) time display-hour)))
  293.      ((or (string= form "HH") (string= form "24"))
  294.       (concat (if display-hour (format "%2d" hour) "  ")
  295.           (diary-format-time (substring format 2) time display-hour)))
  296.      ((string= form "0h")
  297.       (concat (if display-hour (format "%02d" (1+ (% (1- hour) 12))) "  ")
  298.           (diary-format-time (substring format 2) time display-hour)))
  299.      ((string= form "0H")
  300.       (concat (if display-hour (format "%02d" hour) "  ")
  301.           (diary-format-time (substring format 2) time display-hour)))
  302.      ((or (string= form "mm") (string= form "MM"))
  303.       (concat (format "%02d" minute)
  304.           (diary-format-time (substring format 2) time display-hour)))
  305.      ((or (string= form "ap") (string= form "am") (string= form "pm"))
  306.       (concat (if (>= hour 12) diary-pm-string diary-am-string)
  307.           (diary-format-time (substring format 2) time display-hour)))
  308.      ((or (string= form "AP") (string= form "AM") (string= form "PM"))
  309.       (concat (if (>= hour 12) diary-PM-string diary-AM-string)
  310.           (diary-format-time (substring format 2) time display-hour)))
  311.      (t (concat (substring format 0 1)
  312.             (diary-format-time (substring format 1) time display-hour))))))))
  313.  
  314. (defun diary-display-grid (start stop by display-first-hour)
  315.   "Display a schedule of diary times from START to STOP in BY minute increments.
  316. It is not inclusive of the STOP time.  START and STOP are military time
  317. expressed as integers.  This a a fancy display style based on a desk calendar."
  318.   (message (format "(diary-display-grid start=%d stop=%d by=%d display-first-hour=%s)" start stop by display-first-hour))
  319.   (let ((hour (/ start 100))
  320.     (minute (% start 100)))
  321.     (insert (diary-format-time diary-schedule-time-display-format
  322.              start
  323.              (or (= minute 0) display-first-hour))
  324.         " \n")
  325.     (if (>= (setq minute (+ minute by)) 60)
  326.     (setq minute (- minute 60)
  327.           hour (1+ hour)))
  328.     (while (< (+ (* hour 100) minute) stop)
  329.       (insert (diary-format-time diary-schedule-time-display-format
  330.                (+ (* hour 100) minute)
  331.                (= minute 0))
  332.           " \n")
  333.       (if (>= (setq minute (+ minute by)) 60)
  334.       (setq minute (- minute 60)
  335.         hour (1+ hour))))))
  336. ;;    (insert "\n")))
  337.  
  338. (defun diary-calc-display-line (start time stop by)
  339.   "Calculate the line number within the diary buffer on which an event at TIME
  340. will appear given a schedule starting time of START and an interval of BY minutes.
  341. START and TIME are military time expressed as integers."
  342.   (let ((minutes (- (% time 100) (% start 100))))
  343.     (if (> time 0)
  344.     (+
  345.      1
  346.      (*
  347.       (/ 60 by)
  348.       (- (/ time 100) (/ start 100)))
  349.      (/
  350.       (if (< minutes 0)
  351.           (- minutes by -1)
  352.         minutes)
  353.       by))
  354.       time)))
  355.  
  356. (defun diary-minutes-since-midnight (military-time)
  357.   (+ (% military-time 100)
  358.      (* (/ military-time 100) 60)))
  359.  
  360. (defun diary-military-time (minutes-since-midnight)
  361.   (+ (% minutes-since-midnight 60)
  362.      (* (/ minutes-since-midnight 60) 100)))
  363.  
  364. (defun diary-display-at (start stop by begin end text offset)
  365.   "Display a diary entry with the text TEXT running from BEGIN to END on a
  366. schedule running from START to STOP in intervals of BY minutes.  All times
  367. are in military time expressed as integers."
  368.   (let* ((start-line (+ offset (diary-calc-display-line start begin stop by)))
  369.      (line (1+ start-line))
  370.      (original-begin begin)
  371.      (original-end end)
  372.      (end-line (+ offset (diary-calc-display-line start end stop by))))
  373.     (if (> (% (diary-minutes-since-midnight end) by) 0)
  374.     (setq end-line (1+ end-line)))
  375.     (cond ((= begin -9999)
  376.        (message (setq diary-schedule-out-of-bounds-entry-text
  377.          (concat diary-schedule-out-of-bounds-entry-text
  378.              "\n"
  379.              text))))
  380. ;      (progn
  381. ;        (goto-char (point-max))
  382. ;        (insert text ?\n)))
  383.       ((< begin start)
  384.        (setq begin-in-minutes-from-midnight
  385.          (* (/ (diary-minutes-since-midnight begin) by) by)
  386.          begin (diary-military-time begin-in-minutes-from-midnight))
  387.        (if diary-schedule-expand-grid
  388.            (progn
  389.          (goto-line (1+ offset))
  390.          (beginning-of-line)
  391.          (diary-display-grid begin
  392.                      diary-schedule-start-time
  393.                      diary-schedule-interval-time
  394.                      nil)
  395.          (setq diary-schedule-start-time begin)
  396.          (diary-display-at begin stop by original-begin end text offset))
  397.          (progn
  398.            (goto-char (point-max))
  399.            (insert text ?\n))))
  400.       ((>= end stop)
  401.        (setq end-in-minutes-from-midnight
  402. ; (if (= end stop) by 0) extracted from + below.
  403.          (* (/ (+ (* (/ end 100) 60) (% end 100) by) by) by))
  404.        (setq new-end (diary-military-time end-in-minutes-from-midnight))
  405.        (if diary-schedule-expand-grid
  406.            (progn
  407.          (goto-line (+ offset (diary-calc-display-line start stop stop by)))
  408.          (beginning-of-line)
  409.          (diary-display-grid diary-schedule-stop-time
  410.                      new-end
  411.                      diary-schedule-interval-time
  412.                      nil)
  413.          (insert ?\n)
  414.          (setq diary-schedule-stop-time new-end)
  415.          (diary-display-at start diary-schedule-stop-time by begin end text offset))
  416.          (progn
  417.            (goto-char (point-max))
  418.            (insert text ?\n))))
  419.       (t
  420.        (let ((start-column (1+ (length diary-schedule-time-display-format))))
  421.          (goto-line start-line)
  422.          (end-of-line)
  423.          (message "  original-begin = %d" original-begin)
  424.          (if (and (<= (current-column) start-column)
  425.               (or
  426.                (not diary-schedule-odd-times-get-separate-entry)
  427.                (= 0 (% (+
  428.                 (* (/ original-begin 100) 60)
  429.                 (% original-begin 100)) by))))
  430.          (insert diary-schedule-appointment-separator " " text)
  431.  
  432.            ; Let's take a little of the mystery out of this.  The string
  433.            ; %%DIARY-ENTRY-SEPARATOR%% was chosen as an arbitrary string
  434.            ; that is unlikely to appear in a diary entry.  It is placed on
  435.            ; a line to indicate a need for later splitting.  This permits
  436.            ; easy calculation of the lines to place entries on without
  437.            ; knowledge of how the lines may have already been split.  The
  438.            ; split itself is performed in
  439.            ; fancy-schedule-display-desk-calendar.
  440.  
  441.            (insert diary-schedule-entry-separator-string
  442.                (diary-format-time
  443.             diary-schedule-time-overflow-display-format
  444.             begin
  445.             nil)
  446.                " " diary-schedule-appointment-separator " "
  447.                text))
  448.          (while (< line end-line)
  449.            (forward-line 1)
  450.            (end-of-line)
  451.            (if (= (current-column) start-column)
  452.            (insert diary-schedule-appointment-separator " "))
  453.            (setq line (1+ line)))
  454.          (setq original-end-minutes-from-midnight
  455.            (+ (* (/ original-end 100) 60) (% original-end 100)))
  456.          (if (and diary-schedule-odd-times-get-separate-entry
  457.               (/= original-begin original-end)
  458.               (/= 0 (% original-end-minutes-from-midnight by)))
  459.          (progn
  460.            (if diary-schedule-print-odd-end-time
  461.                (insert diary-schedule-entry-separator-string
  462.                    (diary-format-time
  463.                 diary-schedule-time-odd-end-time-format
  464.                 original-end
  465.                 diary-schedule-format-odd-end-time-with-hours)))
  466.            (if (and diary-schedule-print-minute-after-odd-end-time
  467.                 (/= 0
  468.                 (% (1+ original-end-minutes-from-midnight)
  469.                    by)))
  470.                (progn
  471.              (setq one-minute-after
  472.                    (+
  473.                 (* (/ (1+ original-end-minutes-from-midnight)
  474.                       60)
  475.                    100)
  476.                 (% (1+ original-end-minutes-from-midnight) 60)))
  477.              (insert diary-schedule-entry-separator-string
  478.                  (diary-format-time
  479.                   diary-schedule-time-overflow-display-format
  480.                   one-minute-after
  481.                   nil)
  482.                  " "))))))))))
  483.  
  484. (defun display-schedule-entry (start stop by entry-text offset)
  485.   (let ((times (diary-entry-times entry-text)))
  486.     (cond ((= (length times) 3)
  487.        (diary-display-at start stop by
  488.                  (car times) (car (cdr times)) (car (cdr (cdr times)))
  489.                  offset))
  490.       ((= (length times) 6)
  491.        (progn
  492.          (diary-display-at diary-schedule-start-time diary-schedule-stop-time by
  493.                    (car times) (car (cdr times)) (car (cdr (cdr times)))
  494.                    offset)
  495.          (diary-display-at diary-schedule-start-time diary-schedule-stop-time by
  496.                    (nth 3 times) (nth 4 times) (nth 5 times)
  497.                    offset))))))
  498.  
  499. (defun diary-schedule-display (entry-text)
  500.   (if (string-match "\\(\n\\)" entry-text)
  501.       (let ((line-end-position (match-end 1)))
  502.     (display-schedule-entry diary-schedule-start-time
  503.                 diary-schedule-stop-time
  504.                 diary-schedule-interval-time
  505.                 (substring entry-text 0 (1- line-end-position))
  506.                 diary-schedule-line-offset)
  507.     (diary-schedule-display (substring entry-text (1+ line-end-position))))
  508.     (display-schedule-entry diary-schedule-start-time
  509.                 diary-schedule-stop-time
  510.                 diary-schedule-interval-time
  511.                 entry-text
  512.                 diary-schedule-line-offset)))
  513.  
  514. (defun diary-build-time-list (times s)
  515.   "Build the result of diary-entry-times from a list of 2 or 4 TIMES and a string S
  516. describing the event.  The times are in start stop pairs."
  517.   (cond ((= (length times) 2)
  518.      (list (car times) (nth 1 times) s))
  519.     ((= (length times) 4)
  520.      (list (car times) (nth 1 times) s (nth 2 times) (nth 3 times) s))
  521.     (t (list -9999 -9999 s))))
  522.  
  523. (defun diary-entry-times (s)
  524.   "List of times at the beginning of the string S in military-style integers.
  525. For example, returns 1325 for 1:25pm.  Returns -9999 if no time is recognized.
  526. The recognized forms are XXXX or X:XX or XX:XX (military time), XXam or XXpm,
  527. and XX:XXam or XX:XXpm.  If a range is given, the list contains two elements
  528. which will be the start and end of the range.  If only one time is given, both
  529. elements of the list will contain the time given."
  530.   (cond
  531.    ;; Hour and minute range XX:XX-XX:XX[ap]m
  532.    ((string-match
  533.      "^[    ]*\\([0-9]?[0-9]\\):\\([0-9][0-9]\\)-\\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\>"
  534.      s)
  535.     (list
  536.      (+ (* 100 (% (string-to-int
  537.            (substring s (match-beginning 1) (match-end 1)))
  538.           12))
  539.     (string-to-int (substring s (match-beginning 2) (match-end 2)))
  540.     (if (string-equal "a"
  541.               (substring s (match-beginning 5) (match-end 5)))
  542.         0 1200))
  543.      (+ (* 100 (% (string-to-int
  544.            (substring s (match-beginning 3) (match-end 3)))
  545.           12))
  546.     (string-to-int (substring s (match-beginning 4) (match-end 4)))
  547.     (if (string-equal "a"
  548.               (substring s (match-beginning 5) (match-end 5)))
  549.         0 1200))
  550.      (substring s (+ 2 (match-end 5)))))
  551.  
  552.    ;; Military time range
  553.    ((string-match
  554.      "^[    ]*\\([0-9]?[0-9]\\):?\\([0-9][0-9]\\)-\\([0-9]?[0-9]\\):?\\([0-9][0-9]\\)\\(\\|[^ap]\\)"
  555.      s)
  556.     (list
  557.      (+ (* 100 (string-to-int
  558.            (substring s (match-beginning 1) (match-end 1))))
  559.        (string-to-int (substring s (match-beginning 2) (match-end 2))))
  560.      (+ (* 100 (string-to-int
  561.            (substring s (match-beginning 3) (match-end 3))))
  562.        (string-to-int (substring s (match-beginning 4) (match-end 4))))
  563.      (substring s (1+ (match-end 4)))))
  564.  
  565.    ;; Hour range HH[ap]m-HH[ap]m
  566.    ((string-match
  567.      "^[    ]*\\([0-9]?[0-9]\\)\\([ap]\\)m-\\([0-9]?[0-9]\\)\\([ap]\\)m\\>" s)
  568.     (list
  569.      (+ (* 100 (% (string-to-int
  570.           (substring s (match-beginning 1) (match-end 1)))
  571.          12))
  572.        (if (string-equal "a"
  573.              (substring s (match-beginning 2) (match-end 2)))
  574.        0 1200))
  575.      (+ (* 100 (% (string-to-int
  576.           (substring s (match-beginning 3) (match-end 3)))
  577.          12))
  578.        (if (string-equal "a"
  579.              (substring s (match-beginning 4) (match-end 4)))
  580.        0 1200))
  581.      (substring s (+ 2 (match-end 4)))))
  582.  
  583.    ;; Hour range HH-HH[ap]m
  584.    ((string-match
  585.      "^[    ]*\\([0-9]?[0-9]\\)-\\([0-9]?[0-9]\\)\\([ap]\\)m\\>" s)
  586.     (list
  587.      (+ (* 100 (% (string-to-int
  588.           (substring s (match-beginning 1) (match-end 1)))
  589.          12))
  590.        (if (string-equal "a"
  591.              (substring s (match-beginning 3) (match-end 3)))
  592.        0 1200))
  593.      (+ (* 100 (% (string-to-int
  594.           (substring s (match-beginning 2) (match-end 2)))
  595.          12))
  596.        (if (string-equal "a"
  597.              (substring s (match-beginning 3) (match-end 3)))
  598.        0 1200))
  599.      (substring s (+ 2 (match-end 3)))))
  600.  
  601.    ;; Hour and minute range XX:XX[ap]m-XX:XX[ap]m
  602.    ((string-match
  603.      "^[    ]*\\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m-\\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\>"
  604.      s)
  605.     (list
  606.      (+ (* 100 (% (string-to-int
  607.            (substring s (match-beginning 1) (match-end 1)))
  608.           12))
  609.     (string-to-int (substring s (match-beginning 2) (match-end 2)))
  610.     (if (string-equal "a"
  611.               (substring s (match-beginning 3) (match-end 3)))
  612.         0 1200))
  613.      (+ (* 100 (% (string-to-int
  614.            (substring s (match-beginning 4) (match-end 4)))
  615.           12))
  616.     (string-to-int (substring s (match-beginning 5) (match-end 5)))
  617.     (if (string-equal "a"
  618.               (substring s (match-beginning 6) (match-end 6)))
  619.         0 1200))
  620.      (substring s (+ 2 (match-end 6)))))
  621.  
  622.    ;; Military time
  623.    ((string-match
  624.      "^[    ]*\\([0-9]?[0-9]\\):?\\([0-9][0-9]\\)\\(\\>\\|[^ap]\\)" s)
  625.     (let ((time (+ (* 100 (string-to-int
  626.                (substring s (match-beginning 1) (match-end 1))))
  627.            (string-to-int (substring s (match-beginning 2) (match-end 2))))))
  628.       (list time time (substring s (1+ (match-end 2))))))
  629.  
  630.    ;; Hour only XXam or XXpm
  631.    ((string-match
  632.      "^[    ]*\\([0-9]?[0-9]\\)\\([ap]\\)m\\>" s)
  633.     (let ((time (+ (* 100 (% (string-to-int
  634.                   (substring s (match-beginning 1) (match-end 1)))
  635.                  12))
  636.            (if (string-equal "a"
  637.                      (substring s (match-beginning 2) (match-end 2)))
  638.                0 1200))))
  639.       (list time time (substring s (+ 2 (match-end 2))))))
  640.  
  641.    ;; Hour and minute XX:XXam or XX:XXpm
  642.    ((string-match
  643.      "^[    ]*\\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\>" s)
  644.     (let ((time (+ (* 100 (% (string-to-int
  645.                   (substring s (match-beginning 1) (match-end 1)))
  646.                  12))
  647.            (string-to-int (substring s (match-beginning 2) (match-end 2)))
  648.            (if (string-equal "a"
  649.                      (substring s (match-beginning 3) (match-end 3)))
  650.                0 1200))))
  651.       (list time time (substring s (+ 2 (match-end 3))))))
  652.  
  653.    ;; Sunrise/sunset produced by %%(diary-sunrise-sunset)
  654.    ((string-match
  655. ;;     "^[  ]*Sunrise \\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\> ([A-Za-z 0-9+-]*), sunset \\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\> \\(([A-Za-z 0-9+-]*)\\)"
  656.      diary-schedule-sunrise-sunset-pattern s)
  657.     (let ((sunrise-time (+ (* 100 (% (string-to-int
  658.                       (substring s (match-beginning 1) (match-end 1)))
  659.                      12))
  660.                (string-to-int (substring s (match-beginning 2) (match-end 2)))
  661.                (if (string-equal "a"
  662.                          (substring s (match-beginning 3) (match-end 3)))
  663.                    0 1200)))
  664.       (sunset-time (+ (* 100 (% (string-to-int
  665.                      (substring s (match-beginning 5) (match-end 5)))
  666.                     12))
  667.               (string-to-int (substring s (match-beginning 6) (match-end 6)))
  668.               (if (string-equal "a"
  669.                         (substring s (match-beginning 7) (match-end 7)))
  670.                   0 1200))))
  671. ;      (list sunrise-time sunrise-time (concat "Sunrise "
  672. ;                         (substring s (match-beginning 1) (match-end 2)) "am "
  673. ;                         (substring s (1+ (match-end 7))))
  674. ;       sunset-time sunset-time (concat "Sunset "
  675. ;                       (substring s (match-beginning 5) (match-end 6)) "pm "
  676. ;                       (substring s (1+ (match-end 7)))))))
  677.       (list sunrise-time sunrise-time
  678.         (diary-desk-sunrise-sunset-entry-text "Sunrise"
  679.                           sunrise-time
  680.                           (substring s (match-beginning 4) (match-end 4))
  681.                           (substring s (match-beginning 8) (match-end 8)))
  682.         sunset-time sunset-time
  683.         (diary-desk-sunrise-sunset-entry-text "Sunset"
  684.                           sunset-time
  685.                           (substring s (match-beginning 4) (match-end 4))
  686.                           (substring s (match-beginning 8) (match-end 8))))))                        
  687.  
  688.    ;; Lunar phase produced by %%(diary-phases-of-moon)
  689.    ((string-match
  690. ;;     "^[  ]*\\(New\\|First Quarter\\|Full\\|Last Quarter\\) Moon \\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\> ([A-Z0-9+-]*)" s)
  691.      diary-schedule-lunar-phase-pattern s)
  692.     (let ((time (+ (* 100 (% (string-to-int
  693.                   (substring s (match-beginning 2) (match-end 2)))
  694.                  12))
  695.            (string-to-int (substring s (match-beginning 3) (match-end 3)))
  696.            (if (string-equal "a"
  697.                      (substring s (match-beginning 4) (match-end 4)))
  698.                0 1200))))
  699.       (list time time s)))
  700.  
  701.    ;; Equinox/Solstice produced by %%(diary-equinoxes-solstices)
  702.    ((string-match
  703. ;;     "^[  ]*\\(Vernal Equinox\\|Summer Solstice\\|Autumnal Equinox\\|Winter Solstice\\) \\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\> ([A-Z0-9+-]*)" s)
  704.      diary-schedule-equinox-solstice-pattern s)
  705.     (let ((time  (+ (* 100 (% (string-to-int
  706.                   (substring s (match-beginning 2) (match-end 2)))
  707.                  12))
  708.            (string-to-int (substring s (match-beginning 3) (match-end 3)))
  709.            (if (string-equal "a"
  710.                      (substring s (match-beginning 4) (match-end 4)))
  711.                0 1200))))
  712.       (list time time s)))
  713.  
  714.    ;; Morning
  715.    ((string-match ;; "^[    ]*\\([Mm][oO][rR][nN][iI][nN][gG]\\)" s)
  716.      diary-schedule-morning-pattern s)
  717.     (diary-build-time-list diary-morning-times (substring s (1+ (match-end 1)))))
  718.  
  719.    ;; Afternoon
  720.    ((string-match ;; "^[    ]*\\([Aa][fF][tT][eE][rR][nN][oO][oO][nN]\\)" s)
  721.      diary-schedule-afternoon-pattern s)
  722.     (diary-build-time-list diary-afternoon-times (substring s (1+ (match-end 1)))))
  723.  
  724.    ;; Workday
  725.    ((string-match ;; "^[    ]*\\([Ww][oO][rR][kK][dD][aA][yY]\\)" s)
  726.      diary-schedule-workday-pattern s)
  727.     (diary-build-time-list diary-workday-times (substring s (1+ (match-end 1)))))
  728.  
  729.    ;; All Day
  730.    ((string-match ;; "^[    ]*\\([Aa][lL][lL] [Dd][aA][yY]\\)" s)
  731.      diary-schedule-all-day-pattern s)
  732.     (diary-build-time-list diary-all-day-times (substring s (1+ (match-end 1)))))
  733.  
  734.    ;; Unrecognizable
  735.    (t (list -9999 -9999 s))))
  736.  
  737. (defun fancy-schedule-display-desk-calendar ()
  738.   "Prepare a diary buffer with relevant entries in a fancy, noneditable form.
  739. This function is provided for optional use as the `diary-display-hook'."
  740.   (setq diary-schedule-out-of-bounds-entry-text "")
  741.   (if (or (not diary-entries-list)
  742.           (and (not (cdr diary-entries-list))
  743.                (string-equal (car (cdr (car diary-entries-list))) "")))
  744.       (let* ((holiday-list (if holidays-in-diary-buffer
  745.                                (check-calendar-holidays original-date)))
  746.              (msg (format "No diary entries for %s %s"
  747.                           (concat date-string (if holiday-list ":" ""))
  748.                           (mapconcat 'identity holiday-list "; "))))
  749.         (if (<= (length msg) (frame-width))
  750.             (message msg)
  751.           (set-buffer (get-buffer-create holiday-buffer))
  752.           (setq buffer-read-only nil)
  753.           (calendar-set-mode-line date-string)
  754.           (erase-buffer)
  755.           (insert (mapconcat 'identity holiday-list "\n"))
  756.           (goto-char (point-min))
  757.           (set-buffer-modified-p nil)
  758.           (setq buffer-read-only t)
  759.           (display-buffer holiday-buffer)
  760.           (message  "No diary entries for %s" date-string)))
  761.     (save-excursion;; Turn off selective-display in the diary file's buffer.
  762.       (set-buffer (find-buffer-visiting (substitute-in-file-name diary-file)))
  763.       (let ((diary-modified (buffer-modified-p)))
  764.         (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)
  765.         (setq selective-display nil)
  766.         (kill-local-variable 'mode-line-format)
  767.         (set-buffer-modified-p diary-modified)))
  768.     (save-excursion;; Prepare the fancy diary buffer.
  769.       (set-buffer (get-buffer-create fancy-diary-buffer))
  770.       (setq buffer-read-only nil)
  771.       (make-local-variable 'mode-line-format)
  772.       (calendar-set-mode-line " Desk Calendar ")
  773.       (erase-buffer)
  774.       (let ((entry-list diary-entries-list)
  775.             (holiday-list)
  776.             (holiday-list-last-month 1)
  777.             (holiday-list-last-year 1)
  778.             (date (list 0 0 0)))
  779.         (while entry-list
  780.           (if (not (calendar-date-equal date (car (car entry-list))))
  781.               (progn
  782.         (message "Adding %s" diary-schedule-out-of-bounds-entry-text)
  783.         (if (> (length diary-schedule-out-of-bounds-entry-text) 0)
  784.             (progn
  785.               (if diary-schedule-place-out-of-bounds-entries-last
  786.               (goto-char (point-max))
  787.             (goto-line (1+ diary-schedule-line-offset)))
  788.               (insert diary-schedule-out-of-bounds-entry-text ?\n ?\n)
  789.               (if (not diary-schedule-place-out-of-bounds-entries-last)
  790.               (setq diary-schedule-line-offset (1- (current-line))))
  791.               (setq diary-schedule-out-of-bounds-entry-text "")))
  792.                 (setq date (car (car entry-list)))
  793.                 (and holidays-in-diary-buffer
  794.                      (calendar-date-compare
  795.                       (list (list holiday-list-last-month
  796.                                   (calendar-last-day-of-month
  797.                                    holiday-list-last-month
  798.                                    holiday-list-last-year)
  799.                                   holiday-list-last-year))
  800.                       (list date))
  801.                      ;; We need to get the holidays for the next 3 months.
  802.                      (setq holiday-list-last-month
  803.                            (extract-calendar-month date))
  804.                      (setq holiday-list-last-year
  805.                            (extract-calendar-year date))
  806.                      (increment-calendar-month
  807.                       holiday-list-last-month holiday-list-last-year 1)
  808.                      (setq holiday-list
  809.                            (let ((displayed-month holiday-list-last-month)
  810.                                  (displayed-year holiday-list-last-year))
  811.                              (calendar-holiday-list)))
  812.                      (increment-calendar-month
  813.                       holiday-list-last-month holiday-list-last-year 1))
  814.                 (let* ((date-string (calendar-date-string date))
  815.                        (date-holiday-list
  816.                         (let ((h holiday-list)
  817.                               (d))
  818.                           ;; Make a list of all holidays for date.
  819.                           (while h
  820.                             (if (calendar-date-equal date (car (car h)))
  821.                                 (setq d (append d (cdr (car h)))))
  822.                             (setq h (cdr h)))
  823.                           d)))
  824.           (goto-char (point-max))
  825.                   (insert (if (= (point) (point-min))
  826.                   ""
  827.                 diary-subsequent-date-prefix-string)
  828.               date-string)
  829.                   (if date-holiday-list (insert ":  "))
  830.                   (let ((l (current-column)))
  831.                     (insert (mapconcat 'identity date-holiday-list
  832.                                        (concat "\n" (make-string l ? )))))
  833.                   (let ((l (current-column)))
  834.                     (insert ?\n (make-string l ?=) ?\n)))
  835.  
  836.         ;; Massage the time format for the first interval of the day.
  837.  
  838.         (if (and diary-schedule-first-time-always-has-hours
  839.              (> (current-line) 3))
  840.             (progn
  841.               (goto-line (1+ diary-schedule-line-offset))
  842.               (delete-char (length diary-schedule-time-display-format))
  843.               (insert (diary-format-time
  844.                    (or diary-schedule-first-time-format
  845.                    diary-schedule-time-display-format)
  846.                    diary-schedule-start-time
  847.                    t))
  848.               (goto-char (point-max))))
  849.         (setq diary-schedule-line-offset (1- (current-line))
  850.               diary-schedule-start-time
  851.               (round-to-nearest-interval
  852.                diary-default-schedule-start-time
  853.                diary-schedule-interval-time
  854.                t)
  855.               diary-schedule-stop-time
  856.               (round-to-nearest-interval
  857.                diary-default-schedule-stop-time
  858.                diary-schedule-interval-time
  859.                nil))
  860.         (diary-display-grid diary-schedule-start-time
  861.                     diary-schedule-stop-time
  862.                     diary-schedule-interval-time
  863.                     nil)
  864.         (insert ?\n)))
  865.           (if (< 0 (length (car (cdr (car entry-list)))))
  866.           (diary-schedule-display (car (cdr (car entry-list)))))
  867.           (setq entry-list (cdr entry-list))))
  868.       (set-buffer-modified-p nil)
  869.  
  870.       (message "Adding %s" diary-schedule-out-of-bounds-entry-text)
  871.       (if (> (length diary-schedule-out-of-bounds-entry-text) 0)
  872.       (progn
  873.         (if diary-schedule-place-out-of-bounds-entries-last
  874.         (goto-char (point-max))
  875.           (goto-line (1+ diary-schedule-line-offset)))
  876.         (insert diary-schedule-out-of-bounds-entry-text ?\n ?\n)
  877.         (if (not diary-schedule-place-out-of-bounds-entries-last)
  878.         (setq diary-schedule-line-offset (1- (current-line))))
  879.         (setq diary-schedule-out-of-bounds-entry-text "")))
  880.  
  881.       ;; Massage the time format for the first interval of the final day.
  882.  
  883.       (if (and diary-schedule-first-time-always-has-hours
  884.            (> (current-line) 3))
  885.       (progn
  886.         (goto-line (1+ diary-schedule-line-offset))
  887.         (delete-char (length diary-schedule-time-display-format))
  888.         (insert (diary-format-time
  889.              (or diary-schedule-first-time-format
  890.              diary-schedule-time-display-format)
  891.              diary-schedule-start-time
  892.              t))))
  893.  
  894.       ;; Split lines containing multiple entries.
  895.  
  896.       (goto-char (point-min))
  897.       ;; TBH: HERE!
  898.       (perform-replace diary-schedule-entry-separator-string "\n" nil nil nil nil nil)
  899.       (goto-char (point-min))
  900.  
  901.       ;; Eliminate duplicate times from grid if desired.
  902.  
  903.       (if (not diary-duplicate-time-display)
  904.       (progn
  905.         (setq time-on-previous-line
  906.           (buffer-substring
  907.            (point)
  908.            (+ (point) (length diary-schedule-time-display-format))))
  909.         (while (< (point) (point-max))
  910.           (forward-line 1)
  911.           (let ((start-of-current-line (point)))
  912.         (if (< (+ (point) (length diary-schedule-time-display-format))
  913.                (point-max))
  914.             (progn
  915.               (setq time-on-current-line
  916.                 (buffer-substring
  917.                  (point)
  918.                  (+ (point) (length diary-schedule-time-display-format))))
  919.               (if (string-equal-by-format
  920.                time-on-previous-line
  921.                time-on-current-line
  922.                (if (= 0 (% diary-schedule-interval-time 60))
  923.                    diary-schedule-time-display-format
  924.                  diary-schedule-time-overflow-display-format))
  925.               (progn
  926.                 (delete-char (length diary-schedule-time-display-format))
  927.                 (insert (make-string
  928.                      (length diary-schedule-time-display-format)
  929.                      ?\ ))
  930.                 (goto-char start-of-current-line)))
  931.             (setq time-on-previous-line time-on-current-line))
  932.         (goto-char (point-max)))))))
  933.  
  934.       ;; Break long lines.
  935.  
  936.       (goto-char (point-min))
  937.       (save-excursion
  938.     (setq old-fill-prefix fill-prefix
  939.           fill-prefix diary-schedule-fill-prefix-for-broken-lines)
  940.     (let ((start-of-current-line (point)))
  941.       (while (< (point) (point-max))
  942.         (if (looking-at "[^0-9 ]")
  943.         (setq fill-prefix ""))
  944.         (forward-line 1)
  945.         (fill-region start-of-current-line (point))
  946.         (setq start-of-current-line (point))
  947.         (setq fill-prefix diary-schedule-fill-prefix-for-broken-lines)))
  948.     (setq fill-prefix old-fill-prefix))
  949.  
  950.       (goto-char (point-min))
  951.       (setq buffer-read-only t)
  952.       (display-buffer fancy-diary-buffer)
  953.       (message "Preparing diary...done"))))
  954.  
  955. (defun string-equal-by-format (s1 s2 format)
  956.   "Compare the characters in S1 and S2 which correspond to non-blank characters
  957. in FORMAT."
  958.   (let ((i 0)
  959.     (flag nil))
  960.     (while (and (< i (length format)) (not flag))
  961.       (let ((f (string-to-char (substring format i (1+ i))))
  962.         (c1 (string-to-char (substring s1 i (1+ i))))
  963.         (c2 (string-to-char (substring s2 i (1+ i)))))
  964.     (if (not (= f ?\ ))
  965.         (if (or (/= c1 c2)
  966.             (and (= f ?1)
  967.              (not (or (= c1 ?\ ) (= c1 ?0) (= c1 ?1))))
  968.             (and (or (= f ?2) (= f ?4) (= f ?h) (= f ?H) (= f ?m) (= f ?M))
  969.              (not (or (= c1 ?\ )
  970.                   (and (>= c1 ?0) (<= c1 ?9)))))
  971.             (and (= f ?:) (/= c1 ?:)))
  972.         (setq flag t)
  973.           (setq i (1+ i)))
  974.       (setq i (1+ i)))))
  975.     (not flag)))
  976.  
  977. (defun diary-equinoxes-solstices ()
  978.   "Equinox and solstice diary entry."
  979.   (let* ((displayed-month (car date))
  980.      (displayed-year (car (cdr (cdr date))))
  981.      (equinox (solar-equinoxes-solstices)))
  982.     (if (calendar-date-equal (car (car equinox)) date)
  983.     (car (cdr (car equinox))))))
  984.  
  985. ;(defun diary-phases-of-moon ()
  986. ;  "Lunar phases diary entry."
  987. ;  (let* ((displayed-month (car date))
  988. ;    (displayed-year (car (cdr (cdr date))))
  989. ;    (phases (lunar-phase-list displayed-month displayed-year)))
  990. ;    (if (calendar-date-equal (car (car phases)) date)
  991. ;   (car (cdr (car phases))))))
  992.  
  993. (defun diary-today ()
  994.   "Current day diary entry."
  995.   (equal date (calendar-current-date)))
  996.  
  997. (defun diary-relative (n)
  998.   "Diary entry that will always appear N days from today"
  999.   (=
  1000.    (calendar-absolute-from-gregorian date)
  1001.    (+ n (calendar-absolute-from-gregorian (calendar-current-date)))))
  1002.  
  1003. (defun diary-tomorrow ()
  1004.   "Diary entry for tomorrow."
  1005.   (diary-relative 1))
  1006.  
  1007. (defun diary-yesterday ()
  1008.   "Diary entry for yesterday."
  1009.   (diary-relative -1))
  1010.  
  1011. (defun diary-days-of-week (days)
  1012.   "Diary entry for specified days of the week.
  1013. See calendar-day-of-week for numbers."
  1014.   (if (memq (calendar-day-of-week date) days) t nil))
  1015.  
  1016. (defun diary-weekday ()
  1017.   "Diary entry to appear every weekday."
  1018.   (diary-days-of-week '(1 2 3 4 5)))
  1019.  
  1020. (defun diary-weekend ()
  1021.   "Diary entry to appear every weekday."
  1022.   (diary-days-of-week '(0 6)))
  1023.  
  1024. (defun diary-mwf ()
  1025.   "Diary entry to appear every Mon, Wed and Fri."
  1026.   (diary-days-of-week '(1 3 5)))
  1027.  
  1028. (defun diary-tt ()
  1029.   "Diary entry to appear every Tue & Thu."
  1030.   (diary-days-of-week '(2 4)))
  1031.  
  1032. (defun diary-twf ()
  1033.   "Diary entry to appear every Tue, Wed and Fri."
  1034.   (diary-days-of-week '(2 3 5)))
  1035.  
  1036. (defun diary-mt ()
  1037.   "Diary entry to appear every Mon & Thu."
  1038.   (diary-days-of-week '(1 4)))
  1039.  
  1040. (defun diary-desk-sunrise-sunset-entry-text (rise-set time timezone day-duration)
  1041.   "Produce a string suitable for the desktop calendar for the sunrise or
  1042. sunset.  RISE-SET is a string containing \"Sunrise\" or \"Sunset\".  TIME is
  1043. the time as an integer in the internal format used by the desk calendar
  1044. (hour*100 + minutes).  DAY-DURATION is the length of the day as a string
  1045. (e.g., \"15:16\")"
  1046.   (interactive)
  1047.   (concat
  1048.    (if (string= rise-set "Sunrise")
  1049.        diary-schedule-sunrise-string
  1050.      diary-schedule-sunset-string)
  1051.    " "
  1052.    (diary-format-time diary-schedule-astronomical-event-time-format time t)
  1053.    " "
  1054.    timezone
  1055.    " "
  1056.    calendar-location-name))
  1057.  
  1058. (defun sunrises-and-sunsets (start-date end-date)
  1059.   (interactive)
  1060.   (let ((date start-date)
  1061.     (times "")
  1062.     (stop-date (calendar-absolute-from-gregorian end-date)))
  1063.     (while (<= (calendar-absolute-from-gregorian date) stop-date)
  1064.       (progn
  1065.     (setq times (concat times
  1066.                 (calendar-date-string date t) ", "
  1067.                 (solar-sunrise-sunset date) "\n"))
  1068.     (setq date (calendar-gregorian-from-absolute
  1069.             (1+ (calendar-absolute-from-gregorian date))))))
  1070.     times))
  1071.  
  1072. ;; (defun diary-task (status id start-month start-date start-year duration completed dependencies)
  1073. ;;  "Diary entry for a task.  These do not necessarily appear as schedule items."
  1074. ;;  )
  1075.  
  1076. (defun round-to-nearest-interval (time interval downp)
  1077.   "Round TIME up or down to the nearest INTERVAL number of minutes since midnight."
  1078.   (let ((minutes-since-midnight (+ (* (/ time 100) 60) (% time 100))))
  1079.     (if (= 0 (% minutes-since-midnight interval))
  1080.     time
  1081.       (progn
  1082.     (setq minutes-since-midnight
  1083.           (+ (* interval (/ minutes-since-midnight interval))
  1084.          (if downp 0 interval)))
  1085.     (+ (% minutes-since-midnight 60)
  1086.        (* 100 (/ minutes-since-midnight 60)))))))
  1087.  
  1088. (defun current-line ()
  1089.   "Get the current line number (in the buffer) of point."
  1090.   (interactive)
  1091.   (save-restriction
  1092.     (widen)
  1093.     (save-excursion
  1094.       (beginning-of-line)
  1095.       (1+ (count-lines 1 (point))))))
  1096.  
  1097. (provide 'cal-desk-calendar)
  1098.  
  1099. ;; cal-desktop-calendar.el ends here