root/trunk/lisp/calendar/diary-lib.el

Revision 4220, 94.8 kB (checked in by miyoshi, 9 months ago)

Sync up with Emacs22.2.

  • Property svn:eol-style set to LF
Line 
1 ;;; diary-lib.el --- diary functions
2
3 ;; Copyright (C) 1989, 1990, 1992, 1993, 1994, 1995, 2001, 2002, 2003,
4 ;;   2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
5
6 ;; Author: Edward M. Reingold <reingold@cs.uiuc.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 3, 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 ;; This collection of functions implements the diary features as described
30 ;; in calendar.el.
31
32 ;;; Code:
33
34 (require 'calendar)
35
36 (defun diary-check-diary-file ()
37   "Check that the file specified by `diary-file' exists and is readable.
38 If so, return the expanded file name, otherwise signal an error."
39   (let ((d-file (substitute-in-file-name diary-file)))
40     (if (and d-file (file-exists-p d-file))
41         (if (file-readable-p d-file)
42             d-file
43           (error "Diary file `%s' is not readable" diary-file))
44       (error "Diary file `%s' does not exist" diary-file))))
45
46 ;;;###autoload
47 (defun diary (&optional arg)
48   "Generate the diary window for ARG days starting with the current date.
49 If no argument is provided, the number of days of diary entries is governed
50 by the variable `number-of-diary-entries'.  A value of ARG less than 1
51 does nothing.  This function is suitable for execution in a `.emacs' file."
52   (interactive "P")
53   (diary-check-diary-file)
54   (let ((date (calendar-current-date)))
55     (diary-list-entries date (if arg (prefix-numeric-value arg)))))
56
57 (define-obsolete-function-alias 'view-diary-entries 'diary-view-entries)
58 (defun diary-view-entries (&optional arg)
59   "Prepare and display a buffer with diary entries.
60 Searches the file named in `diary-file' for entries that
61 match ARG days starting with the date indicated by the cursor position
62 in the displayed three-month calendar."
63   (interactive "p")
64   (diary-check-diary-file)
65   (diary-list-entries (calendar-cursor-to-date t) arg))
66
67 (defun view-other-diary-entries (arg d-file)
68   "Prepare and display buffer of diary entries from an alternative diary file.
69 Searches for entries that match ARG days, starting with the date indicated
70 by the cursor position in the displayed three-month calendar.
71 D-FILE specifies the file to use as the diary file."
72   (interactive
73    (list (prefix-numeric-value current-prefix-arg)
74          (read-file-name "Enter diary file name: " default-directory nil t)))
75   (let ((diary-file d-file))
76     (diary-view-entries arg)))
77
78 (autoload 'check-calendar-holidays "holidays"
79   "Check the list of holidays for any that occur on DATE.
80 The value returned is a list of strings of relevant holiday descriptions.
81 The holidays are those in the list `calendar-holidays'.")
82
83 (autoload 'calendar-holiday-list "holidays"
84   "Form the list of holidays that occur on dates in the calendar window.
85 The holidays are those in the list `calendar-holidays'.")
86
87 (autoload 'diary-french-date "cal-french"
88   "French calendar equivalent of date diary entry.")
89
90 (autoload 'diary-mayan-date "cal-mayan"
91   "Mayan calendar equivalent of date diary entry.")
92
93 (autoload 'diary-iso-date "cal-iso"
94   "ISO calendar equivalent of date diary entry.")
95
96 (autoload 'diary-julian-date "cal-julian"
97   "Julian calendar equivalent of date diary entry.")
98
99 (autoload 'diary-astro-day-number "cal-julian"
100   "Astronomical (Julian) day number diary entry.")
101
102 (autoload 'diary-chinese-date "cal-china"
103   "Chinese calendar equivalent of date diary entry.")
104
105 (autoload 'diary-islamic-date "cal-islam"
106   "Islamic calendar equivalent of date diary entry.")
107
108 (autoload 'list-islamic-diary-entries "cal-islam"
109   "Add any Islamic date entries from the diary file to `diary-entries-list'.")
110
111 (autoload 'mark-islamic-diary-entries "cal-islam"
112   "Mark days in the calendar window that have Islamic date diary entries.")
113
114 (autoload 'mark-islamic-calendar-date-pattern "cal-islam"
115    "Mark dates in calendar window that conform to Islamic date MONTH/DAY/YEAR.")
116
117 (autoload 'diary-bahai-date "cal-bahai"
118   "Baha'i calendar equivalent of date diary entry.")
119
120 (autoload 'list-bahai-diary-entries "cal-bahai"
121   "Add any Baha'i date entries from the diary file to `diary-entries-list'.")
122
123 (autoload 'mark-bahai-diary-entries "cal-bahai"
124   "Mark days in the calendar window that have Baha'i date diary entries.")
125
126 (autoload 'mark-bahai-calendar-date-pattern "cal-bahai"
127    "Mark dates in calendar window that conform to Baha'i date MONTH/DAY/YEAR.")
128
129 (autoload 'diary-hebrew-date "cal-hebrew"
130   "Hebrew calendar equivalent of date diary entry.")
131
132 (autoload 'diary-omer "cal-hebrew"
133   "Omer count diary entry.")
134
135 (autoload 'diary-yahrzeit "cal-hebrew"
136   "Yahrzeit diary entry--entry applies if date is yahrzeit or the day before.")
137
138 (autoload 'diary-parasha "cal-hebrew"
139   "Parasha diary entry--entry applies if date is a Saturday.")
140
141 (autoload 'diary-rosh-hodesh "cal-hebrew"
142   "Rosh Hodesh diary entry.")
143
144 (autoload 'list-hebrew-diary-entries "cal-hebrew"
145   "Add any Hebrew date entries from the diary file to `diary-entries-list'.")
146
147 (autoload 'mark-hebrew-diary-entries "cal-hebrew"
148   "Mark days in the calendar window that have Hebrew date diary entries.")
149
150 (autoload 'mark-hebrew-calendar-date-pattern "cal-hebrew"
151    "Mark dates in calendar window that conform to Hebrew date MONTH/DAY/YEAR.")
152
153 (autoload 'diary-coptic-date "cal-coptic"
154   "Coptic calendar equivalent of date diary entry.")
155
156 (autoload 'diary-ethiopic-date "cal-coptic"
157   "Ethiopic calendar equivalent of date diary entry.")
158
159 (autoload 'diary-persian-date "cal-persia"
160   "Persian calendar equivalent of date diary entry.")
161
162 (autoload 'diary-phases-of-moon "lunar" "Moon phases diary entry.")
163
164 (autoload 'diary-sunrise-sunset "solar"
165   "Local time of sunrise and sunset as a diary entry.")
166
167 (autoload 'diary-sabbath-candles "solar"
168   "Local time of candle lighting diary entry--applies if date is a Friday.
169 No diary entry if there is no sunset on that date.")
170
171 (defvar diary-syntax-table
172   (let ((st (copy-syntax-table (standard-syntax-table))))
173     (modify-syntax-entry ?* "w" st)
174     (modify-syntax-entry ?: "w" st)
175     st)
176   "The syntax table used when parsing dates in the diary file.
177 It is the standard syntax table used in Fundamental mode, but with the
178 syntax of `*' and `:' changed to be word constituents.")
179
180 (defvar diary-entries-list)
181 (defvar displayed-year)
182 (defvar displayed-month)
183 (defvar entry)
184 (defvar date)
185 (defvar number)
186 (defvar date-string)
187 (defvar original-date)
188
189 (defun diary-attrtype-convert (attrvalue type)
190   "Convert string ATTRVALUE to TYPE appropriate for a face description.
191 Valid TYPEs are: string, symbol, int, stringtnil, tnil."
192   (let (ret)
193     (setq ret (cond ((eq type 'string) attrvalue)
194                     ((eq type 'symbol) (read attrvalue))
195                     ((eq type 'int) (string-to-number attrvalue))
196                     ((eq type 'stringtnil)
197                      (cond ((string= "t" attrvalue) t)
198                            ((string= "nil" attrvalue) nil)
199                            (t attrvalue)))
200                     ((eq type 'tnil)
201                      (cond ((string= "t" attrvalue) t)
202                            ((string= "nil" attrvalue) nil)))))
203 ;    (message "(%s)[%s]=[%s]" (print type) attrvalue ret)
204     ret))
205
206
207 (defun diary-pull-attrs (entry fileglobattrs)
208   "Pull the face-related attributes off the entry, merge with the
209 fileglobattrs, and return the (possibly modified) entry and face
210 data in a list of attrname attrvalue values.
211 The entry will be modified to drop all tags that are used for face matching.
212 If entry is nil, then the fileglobattrs are being searched for,
213 the fileglobattrs variable is ignored, and
214 diary-glob-file-regexp-prefix is prepended to the regexps before each
215 search."
216   (save-excursion
217     (let (regexp regnum attrname attr-list attrname attrvalue type
218                  ret-attr attr)
219       (if (null entry)
220           (progn
221             (setq ret-attr '()
222                   attr-list diary-face-attrs)
223             (while attr-list
224               (goto-char (point-min))
225               (setq attr (car attr-list)
226                     regexp (nth 0 attr)
227                     regnum (nth 1 attr)
228                     attrname (nth 2 attr)
229                     type (nth 3 attr)
230                     regexp (concat diary-glob-file-regexp-prefix regexp))
231               (setq attrvalue nil)
232               (if (re-search-forward regexp (point-max) t)
233                   (setq attrvalue (match-string-no-properties regnum)))
234               (if (and attrvalue
235                        (setq attrvalue (diary-attrtype-convert attrvalue type)))
236                   (setq ret-attr (append ret-attr (list attrname attrvalue))))
237               (setq attr-list (cdr attr-list)))
238             (setq fileglobattrs ret-attr))
239         (progn
240           (setq ret-attr fileglobattrs
241                 attr-list diary-face-attrs)
242           (while attr-list
243             (goto-char (point-min))
244             (setq attr (car attr-list)
245                   regexp (nth 0 attr)
246                   regnum (nth 1 attr)
247                   attrname (nth 2 attr)
248                   type (nth 3 attr))
249             (setq attrvalue nil)
250             (if (string-match regexp entry)
251                 (progn
252                   (setq attrvalue (match-string-no-properties regnum entry))
253                   (setq entry (replace-match "" t t entry))))
254             (if (and attrvalue
255                      (setq attrvalue (diary-attrtype-convert attrvalue type)))
256                 (setq ret-attr (append ret-attr (list attrname attrvalue))))
257             (setq attr-list (cdr attr-list)))))
258       (list entry ret-attr))))
259
260 (defun diary-set-maybe-redraw (symbol value)
261   "Set SYMBOL's value to VALUE, and redraw the diary if necessary.
262 Redraws the diary if it is being displayed (note this is not the same as
263 just visiting the `diary-file'), and SYMBOL's value is to be changed."
264   (let ((oldvalue (eval symbol)))
265     (custom-set-default symbol value)
266     (and (not (equal value oldvalue))
267          (diary-live-p)
268          ;; Note this assumes diary was called without prefix arg.
269          (diary))))
270
271 ;; This can be removed once the kill/yank treatment of invisible text
272 ;; (see etc/TODO) is fixed. -- gm
273 (defcustom diary-header-line-flag t
274   "If non-nil, `simple-diary-display' will show a header line.
275 The format of the header is specified by `diary-header-line-format'."
276   :group   'diary
277   :type    'boolean
278   :initialize 'custom-initialize-default
279   ;; FIXME overkill.
280   :set 'diary-set-maybe-redraw
281   :version "22.1")
282
283 (defvar diary-selective-display nil)
284
285 (defcustom diary-header-line-format
286   '(:eval (calendar-string-spread
287            (list (if diary-selective-display
288                      "Selective display active - press \"s\" in calendar \
289 before edit/copy"
290                    "Diary"))
291            ?\s (frame-width)))
292   "Format of the header line displayed by `simple-diary-display'.
293 Only used if `diary-header-line-flag' is non-nil."
294   :group   'diary
295   :type    'sexp
296   :initialize 'custom-initialize-default
297   ;; FIXME overkill.
298   :set 'diary-set-maybe-redraw
299   :version "22.1")
300
301 (defvar diary-saved-point)              ; internal
302
303 ;; The first version of this also checked for diary-selective-display
304 ;; in the non-fancy case. This was an attempt to distinguish between
305 ;; displaying the diary and just visiting the diary file. However,
306 ;; when using fancy diary, calling diary when there are no entries to
307 ;; display does not create the fancy buffer, nor does it switch on
308 ;; selective-display in the diary buffer. This means some
309 ;; customizations will not take effect, eg:
310 ;; http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-03/msg00466.html
311 ;; So the check for selective-display was dropped. This means the
312 ;; diary will be displayed if one customizes a diary variable while
313 ;; just visiting the diary-file. This is i) unlikely, and ii) no great loss.
314 (defun diary-live-p ()
315   "Return non-nil if the diary is being displayed."
316   (or (get-buffer fancy-diary-buffer)
317       (and diary-file
318            (find-buffer-visiting (substitute-in-file-name diary-file)))))
319
320 (defcustom number-of-diary-entries 1
321   "Specifies how many days of diary entries are to be displayed initially.
322 This variable affects the diary display when the command \\[diary] is used,
323 or if the value of the variable `view-diary-entries-initially' is t.  For
324 example, if the default value 1 is used, then only the current day's diary
325 entries will be displayed.  If the value 2 is used, then both the current
326 day's and the next day's entries will be displayed.
327
328 The value can also be a vector such as [0 2 2 2 2 4 1]; this value
329 says to display no diary entries on Sunday, the entries for
330 the current date and the day after on Monday through Thursday,
331 Friday through Monday's entries on Friday, and only Saturday's
332 entries on Saturday.
333
334 This variable does not affect the diary display with the `d' command
335 from the calendar; in that case, the prefix argument controls the
336 number of days of diary entries displayed."
337   :type '(choice (integer :tag "Entries")
338                  (vector :value [0 0 0 0 0 0 0]
339                          (integer :tag "Sunday")
340                          (integer :tag "Monday")
341                          (integer :tag "Tuesday")
342                          (integer :tag "Wednesday")
343                          (integer :tag "Thursday")
344                          (integer :tag "Friday")
345                          (integer :tag "Saturday")))
346   :initialize 'custom-initialize-default
347   :set 'diary-set-maybe-redraw
348   :group 'diary)
349
350
351 (defvar diary-modify-entry-list-string-function nil
352   "Function applied to entry string before putting it into the entries list.
353 Can be used by programs integrating a diary list into other buffers (e.g.
354 org.el and planner.el) to modify the string or add properties to it.
355 The function takes a string argument and must return a string.")
356
357 (defun add-to-diary-list (date string specifier &optional marker
358                                globcolor literal)
359   "Add an entry to `diary-entries-list'.
360 Do nothing if DATE or STRING is nil.  DATE is the (MONTH DAY
361 YEAR) for which the entry applies; STRING is the text of the
362 entry as it will appear in the diary (i.e. with any format
363 strings such as \"%d\" expanded); SPECIFIER is the date part of
364 the entry as it appears in the diary-file; LITERAL is the entry
365 as it appears in the diary-file (i.e. before expansion).  If
366 LITERAL is nil, it is taken to be the same as STRING.
367
368 The entry is added to the list as (DATE STRING SPECIFIER LOCATOR
369 GLOBCOLOR), where LOCATOR has the form (MARKER FILENAME LITERAL),
370 FILENAME being the file containing the diary entry."
371   (when (and date string)
372     (if diary-file-name-prefix
373         (let ((prefix (funcall diary-file-name-prefix-function
374                                (buffer-file-name))))
375           (or (string= prefix "")
376               (setq string (format "[%s] %s" prefix string)))))
377     (and diary-modify-entry-list-string-function
378          (setq string (funcall diary-modify-entry-list-string-function
379                                string)))
380     (setq diary-entries-list
381           (append diary-entries-list
382                   (list (list date string specifier
383                               (list marker (buffer-file-name) literal)
384                               globcolor))))))
385
386 (define-obsolete-function-alias 'list-diary-entries 'diary-list-entries)
387 (defun diary-list-entries (date number &optional list-only)
388   "Create and display a buffer containing the relevant lines in `diary-file'.
389 The arguments are DATE and NUMBER; the entries selected are those
390 for NUMBER days starting with date DATE.  The other entries are hidden
391 using selective display.  If NUMBER is less than 1, this function does nothing.
392
393 Returns a list of all relevant diary entries found, if any, in order by date.
394 The list entries have the form ((MONTH DAY YEAR) STRING SPECIFIER) where
395 \(MONTH DAY YEAR) is the date of the entry, STRING is the entry text, and
396 SPECIFIER is the applicability.  If the variable `diary-list-include-blanks'
397 is t, this list includes a dummy diary entry consisting of the empty string
398 for a date with no diary entries.
399
400 After the list is prepared, the hooks `nongregorian-diary-listing-hook',
401 `list-diary-entries-hook', `diary-display-hook', and `diary-hook' are run.
402 These hooks have the following distinct roles:
403
404     `nongregorian-diary-listing-hook' can cull dates from the diary
405         and each included file.  Usually used for Hebrew or Islamic
406         diary entries in files.  Applied to *each* file.
407
408     `list-diary-entries-hook' adds or manipulates diary entries from
409         external sources.  Used, for example, to include diary entries
410         from other files or to sort the diary entries.  Invoked *once* only,
411         before the display hook is run.
412
413     `diary-display-hook' does the actual display of information.  If this is
414         nil, simple-diary-display will be used.  Use add-hook to set this to
415         fancy-diary-display, if desired.  If you want no diary display, use
416         add-hook to set this to ignore.
417
418     `diary-hook' is run last.  This can be used for an appointment
419         notification function.
420
421 If LIST-ONLY is non-nil don't modify or display the buffer, only return a list."
422   (unless number
423     (setq number (if (vectorp number-of-diary-entries)
424                      (aref number-of-diary-entries (calendar-day-of-week date))
425                    number-of-diary-entries)))
426   (when (> number 0)
427     (let ((original-date date);; save for possible use in the hooks
428           diary-entries-list
429           file-glob-attrs
430           (date-string (calendar-date-string date))
431           (d-file (substitute-in-file-name diary-file)))
432       (message "Preparing diary...")
433       (save-excursion
434         (let ((diary-buffer (find-buffer-visiting d-file)))
435           (if (not diary-buffer)
436               (set-buffer (find-file-noselect d-file t))
437             (set-buffer diary-buffer)
438             (or (verify-visited-file-modtime diary-buffer)
439                 (revert-buffer t t))))
440         ;; Setup things like the header-line-format and invisibility-spec.
441         (if (eq major-mode default-major-mode)
442             (diary-mode)
443           ;; This kludge is to make customizations to
444           ;; diary-header-line-flag after diary has been displayed
445           ;; take effect. Unconditionally calling (diary-mode)
446           ;; clobbers file local variables.
447           ;; http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-03/msg00363.html
448           ;; http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-04/msg00404.html
449           (if (eq major-mode 'diary-mode)
450               (setq header-line-format (and diary-header-line-flag
451                                             diary-header-line-format))))
452         ;; d-s-p is passed to the diary display function.
453         (let ((diary-saved-point (point)))
454           (save-excursion
455             (setq file-glob-attrs (nth 1 (diary-pull-attrs nil "")))
456             (with-syntax-table diary-syntax-table
457               (let ((mark (regexp-quote diary-nonmarking-symbol)))
458                 (goto-char (point-min))
459                 (unless list-only
460                   (let ((ol (make-overlay (point-min) (point-max) nil t nil)))
461                     (set (make-local-variable 'diary-selective-display) t)
462                     (overlay-put ol 'invisible 'diary)
463                     (overlay-put ol 'evaporate t)))
464                 (calendar-for-loop
465                  i from 1 to number do
466                  (let ((month (extract-calendar-month date))
467                        (day (extract-calendar-day date))
468                        (year (extract-calendar-year date))
469                        (entry-found (list-sexp-diary-entries date)))
470                    (dolist (date-form diary-date-forms)
471                      (let*
472                          ((backup (when (eq (car date-form) 'backup)
473                                     (setq date-form (cdr date-form))
474                                     t))
475                           (dayname
476                            (format "%s\\|%s\\.?"
477                                    (calendar-day-name date)
478                                    (calendar-day-name date 'abbrev)))
479                           (monthname
480                            (format "\\*\\|%s\\|%s\\.?"
481                                    (calendar-month-name month)
482                                    (calendar-month-name month 'abbrev)))
483                           (month (concat "\\*\\|0*" (int-to-string month)))
484                           (day (concat "\\*\\|0*" (int-to-string day)))
485                           (year
486                            (concat
487                             "\\*\\|0*" (int-to-string year)
488                             (if abbreviated-calendar-year
489                                 (concat "\\|" (format "%02d" (% year 100)))
490                               "")))
491                           (regexp
492                            (concat
493                             "\\(\\`\\|\^M\\|\n\\)" mark "?\\("
494                             (mapconcat 'eval date-form "\\)\\(?:")
495                             "\\)"))
496                           (case-fold-search t))
497                        (goto-char (point-min))
498                        (while (re-search-forward regexp nil t)
499                          (if backup (re-search-backward "\\<" nil t))
500                          (if (and (or (char-equal (preceding-char) ?\^M)
501                                       (char-equal (preceding-char) ?\n))
502                                   (not (looking-at " \\|\^I")))
503                              ;;  Diary entry that consists only of date.
504                              (backward-char 1)
505                            ;; Found a nonempty diary entry--make it
506                            ;; visible and add it to the list.
507                            (setq entry-found t)
508                            (let ((entry-start (point))
509                                  date-start temp)
510                              (re-search-backward "\^M\\|\n\\|\\`")
511                              (setq date-start (point))
512                              ;; When selective display (rather than
513                              ;; overlays) was used, diary file used to
514                              ;; start in a blank line and end in a
515                              ;; newline. Now that neither of these
516                              ;; need be true, 'move handles the latter
517                              ;; and 1/2 kludge the former.
518                              (re-search-forward
519                               "\^M\\|\n" nil 'move
520                               (if (and (bobp) (not (looking-at "\^M\\|\n")))
521                                   1
522                                 2))
523                              (while (looking-at " \\|\^I")
524                                (re-search-forward "\^M\\|\n" nil 'move))
525                              (unless (and (eobp) (not (bolp)))
526                                (backward-char 1))
527                              (unless list-only
528                                (remove-overlays date-start (point)
529                                                 'invisible 'diary))
530                              (setq entry (buffer-substring entry-start (point))
531                                    temp (diary-pull-attrs entry file-glob-attrs)
532                                    entry (nth 0 temp))
533                              (add-to-diary-list
534                               date
535                               entry
536                               (buffer-substring
537                                (1+ date-start) (1- entry-start))
538                               (copy-marker entry-start) (nth 1 temp)))))))
539                    (or entry-found
540                        (not diary-list-include-blanks)
541                        (add-to-diary-list date "" "" "" ""))
542                    (setq date
543                          (calendar-gregorian-from-absolute
544                           (1+ (calendar-absolute-from-gregorian date))))
545                    (setq entry-found nil)))))
546             (goto-char (point-min))
547             (run-hooks 'nongregorian-diary-listing-hook
548                        'list-diary-entries-hook)
549             (unless list-only
550               (if diary-display-hook
551                   (run-hooks 'diary-display-hook)
552                 (simple-diary-display)))
553             (run-hooks 'diary-hook)
554             diary-entries-list))))))
555
556 (defun diary-unhide-everything ()
557   (kill-local-variable 'diary-selective-display)
558   (remove-overlays (point-min) (point-max) 'invisible 'diary)
559   (kill-local-variable 'mode-line-format))
560
561 (defun include-other-diary-files ()
562   "Include the diary entries from other diary files with those of diary-file.
563 This function is suitable for use in `list-diary-entries-hook';
564 it enables you to use shared diary files together with your own.
565 The files included are specified in the diaryfile by lines of this form:
566         #include \"filename\"
567 This is recursive; that is, #include directives in diary files thus included
568 are obeyed.  You can change the `#include' to some other string by
569 changing the variable `diary-include-string'."
570   (goto-char (point-min))
571   (while (re-search-forward
572           (concat
573            "\\(?:\\`\\|\^M\\|\n\\)"
574            (regexp-quote diary-include-string)
575            " \"\\([^\"]*\\)\"")
576           nil t)
577     (let* ((diary-file (substitute-in-file-name
578                         (match-string-no-properties 1)))
579            (diary-list-include-blanks nil)
580            (list-diary-entries-hook 'include-other-diary-files)
581            (diary-display-hook 'ignore)
582            (diary-hook nil))
583       (if (file-exists-p diary-file)
584           (if (file-readable-p diary-file)
585               (unwind-protect
586                   (setq diary-entries-list
587                         (append diary-entries-list
588                                 (diary-list-entries original-date number)))
589                 (with-current-buffer (find-buffer-visiting diary-file)
590                   (diary-unhide-everything)))
591             (beep)
592             (message "Can't read included diary file %s" diary-file)
593             (sleep-for 2))
594         (beep)
595         (message "Can't find included diary file %s" diary-file)
596         (sleep-for 2))))
597     (goto-char (point-min)))
598
599 (defun simple-diary-display ()
600   "Display the diary buffer if there are any relevant entries or holidays."
601   (let* ((holiday-list (if holidays-in-diary-buffer
602                            (check-calendar-holidays original-date)))
603          (hol-string (format "%s%s%s"
604                              date-string
605                              (if holiday-list ": " "")
606                              (mapconcat 'identity holiday-list "; ")))
607          (msg (format "No diary entries for %s" hol-string))
608          ;; If selected window is dedicated (to the calendar),
609          ;; need a new one to display the diary.
610          (pop-up-frames (window-dedicated-p (selected-window))))
611     (calendar-set-mode-line (format "Diary for %s" hol-string))
612     (if (or (not diary-entries-list)
613             (and (not (cdr diary-entries-list))
614                  (string-equal (car (cdr (car diary-entries-list))) "")))
615         (if (< (length msg) (frame-width))
616             (message "%s" msg)
617           (set-buffer (get-buffer-create holiday-buffer))
618           (setq buffer-read-only nil)
619           (calendar-set-mode-line date-string)
620           (erase-buffer)
621           (insert (mapconcat 'identity holiday-list "\n"))
622           (goto-char (point-min))
623           (set-buffer-modified-p nil)
624           (setq buffer-read-only t)
625           (display-buffer holiday-buffer)
626           (message  "No diary entries for %s" date-string))
627       (with-current-buffer
628           (find-buffer-visiting (substitute-in-file-name diary-file))
629         (let ((window (display-buffer (current-buffer))))
630           ;; d-s-p is passed from list-diary-entries.
631           (set-window-point window diary-saved-point)
632           (set-window-start window (point-min))))
633       (message "Preparing diary...done"))))
634
635 (defface diary-button '((((type pc) (class color))
636                          (:foreground "lightblue")))
637   "Default face used for buttons."
638   :version "22.1"
639   :group 'diary)
640 ;; backward-compatibility alias
641 (put 'diary-button-face 'face-alias 'diary-button)
642
643 (define-button-type 'diary-entry
644   'action #'diary-goto-entry
645   'face 'diary-button)
646
647 (defun diary-goto-entry (button)
648   (let* ((locator (button-get button 'locator))
649          (marker (car locator))
650          markbuf file)
651     ;; If marker pointing to diary location is valid, use that.
652     (if (and marker (setq markbuf (marker-buffer marker)))
653         (progn
654           (pop-to-buffer markbuf)
655           (goto-char (marker-position marker)))
656       ;; Marker is invalid (eg buffer has been killed).
657       (or (and (setq file (cadr locator))
658                (file-exists-p file)
659                (find-file-other-window file)
660                (progn
661                  (when (eq major-mode default-major-mode) (diary-mode))
662                  (goto-char (point-min))
663                  (if (re-search-forward (format "%s.*\\(%s\\)"
664                                                 (regexp-quote (nth 2 locator))
665                                                 (regexp-quote (nth 3 locator)))
666                                         nil t)
667                      (goto-char (match-beginning 1)))))
668           (message "Unable to locate this diary entry")))))
669
670 (defun fancy-diary-display ()
671   "Prepare a diary buffer with relevant entries in a fancy, noneditable form.
672 This function is provided for optional use as the `diary-display-hook'."
673   (with-current-buffer ;; Turn off selective-display in the diary file's buffer.
674       (find-buffer-visiting (substitute-in-file-name diary-file))
675     (diary-unhide-everything))
676   (if (or (not diary-entries-list)
677           (and (not (cdr diary-entries-list))
678                (string-equal (car (cdr (car diary-entries-list))) "")))
679       (let* ((holiday-list (if holidays-in-diary-buffer
680                                (check-calendar-holidays original-date)))
681              (msg (format "No diary entries for %s %s"
682                           (concat date-string (if holiday-list ":" ""))
683                           (mapconcat 'identity holiday-list "; "))))
684         (if (<= (length msg) (frame-width))
685             (message "%s" msg)
686           (set-buffer (get-buffer-create holiday-buffer))
687           (setq buffer-read-only nil)
688           (erase-buffer)
689           (insert (mapconcat 'identity holiday-list "\n"))
690           (goto-char (point-min))
691           (set-buffer-modified-p nil)
692           (setq buffer-read-only t)
693           (display-buffer holiday-buffer)
694           (message  "No diary entries for %s" date-string)))
695     (with-current-buffer;; Prepare the fancy diary buffer.
696         (make-fancy-diary-buffer)
697       (setq buffer-read-only nil)
698       (let ((entry-list diary-entries-list)
699             (holiday-list)
700             (holiday-list-last-month 1)
701             (holiday-list-last-year 1)
702             (date (list 0 0 0)))
703         (while entry-list
704           (if (not (calendar-date-equal date (car (car entry-list))))
705               (progn
706                 (setq date (car (car entry-list)))
707                 (and holidays-in-diary-buffer
708                      (calendar-date-compare
709                       (list (list holiday-list-last-month
710                                   (calendar-last-day-of-month
711                                    holiday-list-last-month
712                                    holiday-list-last-year)
713                                   holiday-list-last-year))
714                       (list date))
715                      ;; We need to get the holidays for the next 3 months.
716                      (setq holiday-list-last-month
717                            (extract-calendar-month date))
718                      (setq holiday-list-last-year
719                            (extract-calendar-year date))
720                      (progn
721                        (increment-calendar-month
722                         holiday-list-last-month holiday-list-last-year 1)
723                        t)
724                      (setq holiday-list
725                            (let ((displayed-month holiday-list-last-month)
726                                  (displayed-year holiday-list-last-year))
727                              (calendar-holiday-list)))
728                      (increment-calendar-month
729                       holiday-list-last-month holiday-list-last-year 1))
730                 (let* ((date-string (calendar-date-string date))
731                        (date-holiday-list
732                         (let ((h holiday-list)
733                               (d))
734                           ;; Make a list of all holidays for date.
735                           (while h
736                             (if (calendar-date-equal date (car (car h)))
737                                 (setq d (append d (cdr (car h)))))
738                             (setq h (cdr h)))
739                           d)))
740                   (insert (if (= (point) (point-min)) "" ?\n) date-string)
741                   (if date-holiday-list (insert ":  "))
742                   (let* ((l (current-column))
743                          (longest 0))
744                     (insert (mapconcat (lambda (x)
745                                          (if (< longest (length x))
746                                              (setq longest (length x)))
747                                          x)
748                                        date-holiday-list
749                                        (concat "\n" (make-string l ? ))))
750                     (insert ?\n (make-string (+ l longest) ?=) ?\n)))))
751
752           (setq entry (car (cdr (car entry-list))))
753           (if (< 0 (length entry))
754               (let ((this-entry (car entry-list))
755                     this-loc)
756                 (if (setq this-loc (nth 3 this-entry))
757                     (insert-button (concat entry "\n")
758