planner-el.texi (Keeping Track of Time): Recover paragraph that had
[planner-el.git] / planner-diary.el
blob477ebf408708831b0174109494695a9e8f4ed43d
1 ;;; planner-diary.el --- Diary integration for the Emacs Planner (planner.el)
3 ;; Copyright (C) 2003, 2004, 2008 Thomas Gehrlein <Thomas.Gehrlein AT t-online.de>
4 ;; Parts copyright (C) 2004, 2005, 2008 Free Software Foundation, Inc.
5 ;; Parts copyright (C) 2005, 2008 Jody Klymak
7 ;; Emacs Lisp Archive Entry
8 ;; Filename: planner-diary.el
9 ;; Time-stamp: "2004-03-27 16:51:20 Thomas Gehrlein"
10 ;; Version: 1.0-devel
11 ;; Keywords: hypermedia
12 ;; Author: Thomas Gehrlein <Thomas.Gehrlein@t-online.de>
13 ;; Maintainer: Thomas Gehrlein <Thomas.Gehrlein@t-online.de>
14 ;; Description: Integrate the Emacs Planner with Calendar and Diary
15 ;; URL: http://www.wjsullivan.net/PlannerMode.html
16 ;; Compatibility: Emacs20, Emacs21, Emacs22, XEmacs21
18 ;; This file is part of Planner. It is not part of GNU Emacs.
20 ;; Planner is free software; you can redistribute it and/or modify it
21 ;; under the terms of the GNU General Public License as published by
22 ;; the Free Software Foundation; either version 3, or (at your option)
23 ;; any later version.
25 ;; Planner is distributed in the hope that it will be useful, but
26 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
27 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 ;; General Public License for more details.
30 ;; You should have received a copy of the GNU General Public License
31 ;; along with Planner; see the file COPYING. If not, write to the
32 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
33 ;; Boston, MA 02110-1301, USA.
35 ;;; PLANNER-DIARY
37 ;; The following documentation describes the stable version of
38 ;; planner-diary (v1.0). If planner-diary doesn't do what I claim
39 ;; here, then it's a bug and you will fix it. This file contains
40 ;; additional code that is part of the development version of planner.
41 ;; This code may or may not work. Use it at your own risk.
43 ;;; Commentary:
45 ;; If you use Emacs' diary feature, planner-diary could be helpful for
46 ;; you. It puts all diary entries for the current day in the "*
47 ;; Diary" section of your day plan page. This section is updated
48 ;; every time you display the file in Emacs. By default the diary
49 ;; section of past pages is not updated (it's pretty unlikely that you
50 ;; want to add new diary entries for the past).
52 ;; If you want to use planner-diary.el, put the file in your load path and add
53 ;; this to your .emacs:
55 ;; (require 'planner-diary)
57 ;; (This step should not be necessary once a stable planner package will be put
58 ;; together.)
60 ;; planner-diary.el needs fancy-diary-display. To use fancy-diary-display add
61 ;; this to your .emacs:
63 ;; (add-hook 'diary-display-hook 'fancy-diary-display)
65 ;; You can use planner diary in two different ways:
67 ;; 1) If you want the saved files to contain your entries and not just a line
68 ;; of lisp, add the following lines to your .emacs:
70 ;; (setq planner-diary-use-diary t)
71 ;; (planner-diary-insinuate)
73 ;; You should also customize or set planner-day-page-template to include a
74 ;; "* Diary":
76 ;; (setq planner-day-page-template
77 ;; "* Tasks\n\n\n* Schedule\n\n\n* Diary\n\n\n* Notes")
79 ;; C-c C-e updates the diary sections. C-u C-c C-e forces an update, it
80 ;; inserts the diary section for the day, even if the day is in the past or
81 ;; if there is no Diary section in the buffer.
83 ;; 2) (GNU EMACS ONLY) You can put the following line of lisp code in
84 ;; your day plan pages to display your diary entries:
86 ;; <lisp>(planner-diary-entries-here)</lisp>
88 ;; You can do this automatically for all day plan pages:
90 ;; (setq planner-day-page-template
91 ;; "* Tasks\n\n\n* Diary\n\n<lisp>(planner-diary-entries-here)</lisp>\n\n* Notes")
93 ;; When you open a day plan page outside emacs, you will see the
94 ;; line of lisp code and not your diary entries.
96 ;; If you want to see your diary entries for more than just 1 day, set
97 ;; `planner-diary-number-of-diary-entries' accordingly. This works for
98 ;; either of the 2 approaches.
100 ;; If you want to use the cal-desk package, simply follow the
101 ;; instructions in cal-desk.el. If you get the cal-desk layout from
102 ;; the Calendar buffer, you get it in the day plan buffer, too.
104 ;; If you use planner-diary, you might consider using the Calendar
105 ;; support of planner. To get Calendar integration add this to your
106 ;; .emacs:
108 ;; (planner-insinuate-calendar)
110 ;; For further information refer to the documentation for this function.
112 ;; If you have questions about planner-diary, feature requests, bug reports or
113 ;; anything else you want to tell me: thomas.gehrlein@t-online.de.
115 ;;; HISTORY:
117 ;; version 1.0 First stable version. Expected to work as documented. Same
118 ;; feature of previous versions are not supported.
120 ;; version 0.7 Major rewrite. New diary types (cal-desk, appointments, privat,
121 ;; public diaries). Add diary entries from planner pages.
123 ;; version 0.6 minor changes
125 ;; version 0.5 appointment integration (thanks to Bastien Guerry)
127 ;;; ADVANCED FEATURES
129 ;; (The features described here are part of the development version. They are
130 ;; subject to change without notice. They may be buggy. The documentation may
131 ;; be inaccurate. Use at your own risk.)
133 ;; There is a lot of code redundancy in the development version. This is
134 ;; intentional and makes it easier to change the code for one type of diary
135 ;; section without breaking others.
137 ;; Currently planner-diary supports 6 different views of your diary entries:
139 ;; 1) Ordinary fancy diary display (what you get by pressing d in the calendar
140 ;; buffer with fancy-diary-display switched on)
141 ;; 2) Schedule/Appointments (all entries from 1 that have a time assigned to
142 ;; them.
143 ;; 3) Diary without appts (1 without 2).
144 ;; 4) cal-desk display (appts on top, non appts entries at bottom)
145 ;; 5) A private diary (same as 1, but uses a different diary-file)
146 ;; 6) A public diary (same as 1, but uses a different diary-file)
148 ;; 1) Put the following line of lisp code in you day plan pages to display your
149 ;; diary entries:
151 ;; <lisp>(planner-diary-entries-here)</lisp>
153 ;; The function `planner-diary-entries-here' takes two optional args: The
154 ;; diary file you want to use and the number of days you want to display.
157 ;;; RANDOM THOUGHTS ABOUT PLANNER, CALENDAR, AND DIARY
159 ;; This is planner-diary, not planner-calendar! Include only stuff that has to
160 ;; do with diary and planner, leave the calendar stuff to
161 ;; planner-insinuate-calendar.
163 ;; Deal with holidays.
165 ;; There are 3 ways to use planner with calendar and diary:
167 ;; 1) Use calendar for browsing the day plan pages.
169 ;; Cool features: all days with day plan pages are marked. ">" and "<" move to
170 ;; the next or previous page. (Ordinary diary entries should not be marked.)
171 ;; Entry point: a command (planner-browse-calendar ??) What about key-bindings
172 ;; for "n" and "N"?
174 ;; 2) Display the day plan pages when you move in calendar.
176 ;; Add a hook to calendar-move-hook. Add key-bindings for "n" and "N".
178 ;; 3) Automatically update the diary section in day plan pages.
180 ;; Add a function to planner-goto-hook. This work independent of the first 2.
182 ;;;_ + Contributors
184 ;; Jody Klymak (jklymak AT coas DOT oregonstate DOT edu) added some
185 ;; documentation.
187 ;; Sacha Chua (sacha AT free DOT net DOT ph) provided thoughts and
188 ;; ideas.
190 ;; Bastien Guerry provided a bug report and appointment integration.
192 ;; Travis B. Hartwell (nafai AT travishartwell DOT net) helped fix
193 ;; calendar desk entries and fixed a typo or two.
195 (require 'planner)
196 (require 'diary-lib)
197 (require 'calendar)
198 (require 'cl)
200 ;;; Code:
201 (defgroup planner-diary nil
202 "Diary integration for planner.el."
203 :prefix "planner-diary-"
204 :group 'planner)
206 ;;; USER VARIABLES
207 ;; STANDARD DIARY SUPPORT (no fancy extras - this is what I, the author of
208 ;: this file, use)
209 (defcustom planner-diary-string "* Diary"
210 "*Header for the diary section in a day plan page."
211 :type 'string
212 :group 'planner-diary)
214 (defcustom planner-diary-create-section-flag t
215 "Non-nil means create diary section in future or present day pages."
216 :type 'boolean
217 :group 'planner-diary)
219 (defcustom planner-diary-file diary-file
220 ;; FIXME: Test if this works as buffer-local var
221 "*The diary file you want to use with planner-diary."
222 :type 'file
223 :group 'planner-diary)
225 (defcustom planner-diary-use-diary nil
226 ;; (if (string-match planner-diary-string planner-day-page-template)
227 ;; t
228 ;; nil)
229 "*Non-nil means: Automatically insert a diary section into day plan pages.
230 Uses the standard fancy-diary display."
231 :type 'boolean
232 :group 'planner-diary)
234 (defcustom planner-diary-number-of-days 1
235 "*Number of days of diary entries displayed in the standard diary section."
236 :type 'number
237 :group 'planner-diary)
239 (defcustom planner-diary-include-all-output-flag nil
240 "Non-nil means don't omit any data when copying diary entries into day pages."
241 :type 'boolean
242 :group 'planner-diary)
244 ;; for backward compatability
245 ;; (defcustom planner-diary-number-of-diary-entries 1
246 ;; "*Obsolete, use `planner-diary-number-of-days' instead."
247 ;; :type 'number
248 ;; :group 'planner-diary)
251 ;; CAL-DESK
252 (defcustom planner-diary-cal-desk-string "* Cal-Desk"
253 "Header for the cal-desk section in a day plan page.
254 You might want to use \"* Diary\" or \"* Schedule\"."
255 :type 'string
256 :group 'planner-diary)
258 (defcustom planner-diary-cal-desk-file planner-diary-file
259 "The diary file you want to use for your cal-desk section."
260 :type 'file
261 :group 'planner-diary)
263 (defcustom planner-diary-use-cal-desk nil
264 ;; (if (string-match planner-diary-cal-desk-string planner-day-page-template)
265 ;; t
266 ;; nil)
267 "Non-nil means include a cal-desk section in day pages.
268 This section contains only entries from `planner-diary-cal-desk-file'."
269 :type 'boolean
270 :group 'planner-diary)
272 (defcustom planner-diary-cal-desk-number-of-days
273 1 ; 1 is good for cal-desk's format.
274 "Number of days of diary entries displayed in the cal-desk diary section."
275 :type 'number
276 :group 'planner-diary)
278 ;; DIARY APPOINTMENTS (idea and most of this code from Bastien Guerry)
279 (defcustom planner-diary-appts-string "* Diary Appointments"
280 "*Header for the diary appointments section in a day plan page."
281 :type 'string
282 :group 'planner-diary)
284 (defcustom planner-diary-appts-file planner-diary-file
285 "*The diary file you want to use for your diary appointments section."
286 :type 'file
287 :group 'planner-diary)
289 (defcustom planner-diary-use-appts nil
290 ;; (if (string-match planner-diary-appts-string planner-day-page-template)
291 ;; t
292 ;; nil)
293 "*Non-nil means: Insert a diary appointments section into day plan pages.
294 This displays all diary entries `planner-diary-appts-file' with a time assigned
295 to them."
296 :type 'boolean
297 :group 'planner-diary)
299 (defcustom planner-diary-appts-number-of-days
300 1 ; 1 is good for appts
301 "Number of days of diary entries displayed in the appointments diary section."
302 :type 'number
303 :group 'planner-diary)
305 (defcustom planner-diary-exclude-appts-from-diary
306 planner-diary-use-appts ; if you use appts, you want to exclude
307 ; them from the diary section
308 "Non-nil means that appts are not shown in the diary section.
309 This is useful if you use a diary appointments section. Diary entries with a
310 time assigned to them will then only be displayed in the diary appointments
311 section and not in the diary section."
312 :type 'boolean
313 :group 'planner-diary)
315 ;; PRIVATE DIARY
316 (defcustom planner-diary-private-string "* Private Diary"
317 "Header for the private diary section in a day plan page.
318 This is the section you don't want to publish. planner-diary doesn't do
319 anything about the publishing, it's up to you to make sure this section doesn't
320 get published."
321 :type 'string
322 :group 'planner-diary)
324 (defcustom planner-diary-private-file planner-diary-file
325 ;; FIXME: Test if this works as buffer-local var
326 "The diary file you want to use with for your private diary."
327 :type 'file
328 :group 'planner-diary)
330 (defcustom planner-diary-use-private-diary nil
331 ;; (if (string-match planner-diary-private-string planner-day-page-template)
332 ;; t
333 ;; nil)
334 "Non-nil means: Insert a private diary section into day plan pages.
335 This section contains only entries from `planner-diary-private-file'."
336 :type 'boolean
337 :group 'planner-diary)
339 (defcustom planner-diary-private-number-of-days
340 planner-diary-number-of-days
341 "Number of days of diary entries displayed in the private diary section."
342 :type 'number
343 :group 'planner-diary)
345 ;; PUBLIC DIARY
346 (defcustom planner-diary-public-string "* Public Diary"
347 "Header for the public diary section in a day plan page.
348 This is the section you want to publish. Obviously, this only makes sense if
349 you have a Private Diary section, too. planner-diary doesn't do anything about
350 the publishing, it's up to you to make sure this section gets published."
351 :type 'string
352 :group 'planner-diary)
354 (defcustom planner-diary-public-file diary-file
355 "The diary file you want to use with for your public diary."
356 :type 'file
357 :group 'planner-diary)
359 (defcustom planner-diary-use-public-diary nil
360 ;; (if (string-match planner-diary-public-string planner-day-page-template)
361 ;; t
362 ;; nil)
363 "Non-nil means: Insert a public diary section into day plan pages.
364 This section contains only entries from `planner-diary-public-file'."
365 :type 'boolean
366 :group 'planner-diary)
368 (defcustom planner-diary-public-number-of-days
369 planner-diary-number-of-days
370 "Number of days of diary entries displayed in the public diary section."
371 :type 'number
372 :group 'planner-diary)
375 ;;; INTERNAL VARS
376 ;; FIXME: Is this used for anything?
377 (defcustom planner-diary-exclude-regexp ""
378 "Regexp for diary entries not displayed in the diary section.
379 Used by the schedule code."
380 :type 'regexp
381 :group 'planner-diary)
383 (defcustom planner-diary-time-regexp "[0-2][0-9]:[0-5][0-9]"
384 ;; FIXME: Internationalize? (AM and PM)
385 "A regexp for time in a diary appt entry."
386 :type 'regexp
387 :group 'planner-diary)
389 ;;; FUNCTIONS
390 ;; GETTING THE RELEVANT ENTRIES
391 ;; planner-diary-get-diary-entries is the main function,
392 ;; planner-diary-get-appts-entries needs to be rewritten.
393 ;; planner-diary-get-[public/private/cal-desk]-entries call
394 ;; planner-diary-get-diary-entries.
395 (defun planner-diary-get-diary-entries (date &optional no-of-days file
396 use-cal-desk)
397 "Get the diary entries for DATE and the following NO-OF-DAYS days from FILE.
398 DATE is a list (month day year). NO-OF-DAYS defaults to
399 `planner-diary-number-of-days'. FILE defaults to
400 `planner-diary-file'. Optional argument USE-CAL-DESK means
401 display using a fancy desk calendar."
402 (save-window-excursion
403 (let* ((fancy-diary-buffer "temporary-fancy-diary-buffer")
404 (entries)
405 (font-lock-defaults nil)
406 (font-lock-mode nil)
407 (diary-display-hook
408 (if use-cal-desk
409 '(sort-diary-entries fancy-diary-display
410 fancy-schedule-display-desk-calendar)
411 '(sort-diary-entries fancy-diary-display)))
412 (no-of-days (or no-of-days planner-diary-number-of-days))
413 (diary-file (or file planner-diary-file)))
414 (if (fboundp 'diary-list-entries)
415 (diary-list-entries date no-of-days)
416 (list-diary-entries date no-of-days))
417 (switch-to-buffer fancy-diary-buffer)
418 (let ((inhibit-read-only t))
419 ;; return "No entries" if buffer is empty
420 (setq entries
421 (if (= (point-max) 1)
422 "No entries"
423 (buffer-substring
424 (if (> no-of-days 1) ; if more than 1 day, show everything
425 (progn
426 (while (re-search-forward "^=+$" nil t)
427 (replace-match
428 (make-string (length (match-string 0)) ?-)))
429 (point-min))
430 (if planner-diary-include-all-output-flag
431 ;; remove date and lots of =s if just for 1 day
432 (point-min)
433 (goto-char (point-min))
434 (search-forward-regexp "^=+$") ; one or more =
435 (1+ (point))))
436 ;; remove final newline
437 (progn
438 (goto-char (point-max))
439 (when (bolp) (backward-char 1))
440 (point)))))
441 (kill-buffer fancy-diary-buffer)
442 entries))))
444 (defun planner-diary-get-cal-desk-entries (date &optional no-of-days file)
445 "Get the cal-desk style diary entries for DATE.
446 Consider the following NO-OF-DAYS days from FILE. DATE is a
447 list (month day year). NO-OF-DAYS defaults to
448 `planner-diary-cal-desk-number-of-days'. FILE defaults to
449 `planner-diary-cal-desk-file'."
450 (planner-diary-get-diary-entries date
451 (or no-of-days
452 planner-diary-cal-desk-number-of-days)
453 (or file
454 planner-diary-cal-desk-file)
458 ;;; TODO: Merge the following two functions into one:
459 (defun planner-diary-get-appts-entries (date &optional no-of-days file)
460 ;; FIXME: use optional args
461 "Call `planner-diary-get-entries' for appointments on DATE.
462 Arguments NO-OF-DAYS and FILE are ignored."
463 (planner-diary-get-entries date 'appt))
465 (defun planner-diary-get-entries (date &optional type)
466 "Get the appointment diary entries for DATE and the following days.
467 DATE is a list (month day year). Optional arg TYPE ..." ; FIXME: doc for type
468 (save-window-excursion
469 (let* ((regexp planner-diary-exclude-regexp)
470 (no-of-days planner-diary-number-of-days)
471 (init-entries (flet ((message (&rest args) (ignore args)))
472 (if (fboundp 'diary-list-entries)
473 (diary-list-entries date (if type 1 no-of-days))
474 (list-diary-entries date
475 (if type 1 no-of-days)))))
476 (end-entries
477 (mapcar
478 '(lambda (seq)
479 (let ((seq2 (if (and (eq type 'appt)
480 (string-match planner-diary-time-regexp
481 (cadr seq)))
482 (planner-diary-appt-to-planner (cadr seq))
483 (cadr seq))))
484 (unless ; first check no excluded regexp
485 (or (and (not (equal regexp ""))
486 (string-match regexp seq2))
487 ; then check appt not already there
488 (string-match (substring seq2 0 (1- (length seq2)))
489 ; bug in planner.el ? a space is added
490 ; from diary entries to .diary.planner
491 (buffer-string))
492 (if (eq type 'appt)
493 (not (string-match planner-diary-time-regexp
494 seq2))
495 (and (eq type nil)
496 planner-diary-exclude-appts-from-diary
497 (string-match planner-diary-time-regexp
498 (cadr seq)))))
499 seq2))) init-entries)))
500 (mapconcat 'eval (delete-duplicates (remove nil end-entries)
501 :test 'equal) "\n"))))
503 (defun planner-diary-appt-to-planner (appt)
504 "Convert APPT from diary format to planner format."
505 (string-match "\\([0-9]+:[0-9]+[ap]?m?\\)[ ]+\\(.*\\)" appt)
506 (let ((time (match-string 1 appt))
507 (task (match-string 2 appt)))
508 (concat time " | " task)))
511 (defun planner-diary-get-private-entries (date &optional no-of-days file)
512 "Get private diary entries for DATE and the next NO-OF-DAYS from FILE.
513 DATE is a list (month day year). NO-OF-DAYS defaults to
514 `planner-diary-private-number-of-days'. FILE defaults to
515 `planner-diary-private-file'."
516 (planner-diary-get-diary-entries date
517 (or no-of-days
518 planner-diary-private-number-of-days)
519 (or file
520 planner-diary-private-file)))
522 (defun planner-diary-get-public-entries (date &optional no-of-days file)
523 "Get public diary entries for DATE and the next NO-OF-DAYS days from FILE.
524 DATE is a list (month day year). NO-OF-DAYS defaults to
525 `planner-diary-public-number-of-days'. FILE defaults to
526 `planner-diary-public-file'."
527 (planner-diary-get-diary-entries date
528 (or no-of-days
529 planner-diary-public-number-of-days)
530 (or file
531 planner-diary-public-file)))
533 (defun planner-diary-get-name ()
534 "Return current filename."
535 (planner-page-name))
537 ;;; LISP FUNCTIONS FOR USE IN PLANNER DAY PAGES
538 ;;; arg FILE to specify a diary-file (suggested by David O'Toole)
539 (defun planner-diary-entries-here (&optional file no-of-days)
540 "Display the diary entries from FILE for the next NO-OF-DAYS days.
541 FILE defaults to `planner-diary-file', NO-OF-DAYS defaults to
542 `planner-diary-number-of-days'.
544 Put this is your day pages:
545 \"<lisp>(planner-diary-entries-here)</lisp>\"
546 or this, if you want to do fancy things:
547 \"<lisp>(planner-diary-entries-here \"/path/to/diary/file\" 1)</lisp>\"
549 You might want to use `planner-day-page-template' to do so."
550 (planner-diary-get-diary-entries
551 (planner-filename-to-calendar-date
552 (planner-diary-get-name))
553 (or no-of-days planner-diary-number-of-days)
554 (or file planner-diary-file)))
556 (defun planner-diary-appts-entries-here (&optional file no-of-days)
557 "Display the diary appointments entries from FILE for the next NO-OF-DAYS.
558 FILE defaults to `planner-diary-appts-file', NO-OF-DAYS defaults to
559 `planner-diary-appts-number-of-days'.
561 Put this is your day pages:
562 \"<lisp>(planner-diary-appts-entries-here)</lisp>\"
563 or this, if you want to do fancy things:
564 \"<lisp>(planner-diary-appts-entries-here \"/path/to/diary/file\" 1)</lisp>\"
566 You might want to use `planner-day-page-template' to do so."
567 (planner-diary-get-appts-entries
568 (planner-filename-to-calendar-date
569 (planner-diary-get-name))
570 (or file planner-diary-appts-file)
571 (or no-of-days planner-diary-appts-number-of-days)))
574 (defun planner-diary-cal-desk-entries-here (&optional file no-of-days)
575 "Display the diary appointments entries from FILE for the next NO-OF-DAYS.
576 FILE defaults to `planner-diary-cal-desk-file', NO-OF-DAYS defaults to
577 `planner-diary-cal-desk-number-of-days'.
579 Put this is your day pages:
580 \"<lisp>(planner-diary-cal-desk-entries-here)</lisp>\"
581 or this, if you want to do fancy things:
582 \"<lisp>(planner-diary-cal-desk-entries-here \"/path/to/diary/file\" 1)</lisp>\"
584 You might want to use `planner-day-page-template' to do so."
585 (planner-diary-get-cal-desk-entries
586 (planner-filename-to-calendar-date
587 (planner-diary-get-name))
588 (or file planner-diary-cal-desk-file)
589 (or no-of-days planner-diary-cal-desk-number-of-days)))
591 (defun planner-diary-public-entries-here (&optional file no-of-days)
592 "Display the diary appointments entries from FILE for the next NO-OF-DAYS.
593 FILE defaults to `planner-diary-public-file', NO-OF-DAYS defaults to
594 `planner-diary-public-number-of-days'.
596 Put this is your day pages:
597 \"<lisp>(planner-diary-public-entries-here)</lisp>\"
598 or this, if you want to do fancy things:
599 \"<lisp>(planner-diary-public-entries-here \"/path/to/diary/file\" 1)</lisp>\"
601 You might want to use `planner-day-page-template' to do so."
602 (planner-diary-get-public-entries
603 (planner-filename-to-calendar-date
604 (planner-diary-get-name))
605 (or file planner-diary-public-file)
606 (or no-of-days planner-diary-public-number-of-days)))
608 (defun planner-diary-private-entries-here (&optional file no-of-days)
609 "Display the diary appointments entries from FILE for the next NO-OF-DAYS.
610 FILE defaults to `planner-diary-private-file', NO-OF-DAYS defaults to
611 `planner-diary-private-number-of-days'.
613 Put this is your day pages:
614 \"<lisp>(planner-diary-private-entries-here)</lisp>\"
615 or this, if you want to do fancy things:
616 \"<lisp>(planner-diary-private-entries-here \"/path/to/diary/file\" 1)</lisp>\"
618 You might want to use `planner-day-page-template' to do so."
619 (planner-diary-get-private-entries
620 (planner-filename-to-calendar-date
621 (planner-diary-get-name))
622 (or file planner-diary-private-file)
623 (or no-of-days planner-diary-private-number-of-days)))
625 ;;; CODE FOR DEALING WITH SECTIONS
626 ;; There's a lot of code duplication in the following 3 functions.
627 ;;;###autoload
628 (defun planner-diary-update-section (file title text &optional force)
629 ;;FIXME: Find a good place to insert a new section
630 "Update FILE's existing section TITLE by replacing existing text with TEXT.
631 If optional arg FORCE is non-nil, update the section even if it doesn't exist,
632 i.e. insert TITLE followed by TEXT at the top of the buffer."
633 ;; search for something like "^* Diary$", delete buffer content to the next
634 ;; "^* "
635 ;; sanity checks
636 (unless (equal major-mode 'planner-mode)
637 (error "This is not a planner buffer"))
638 (save-excursion
639 (goto-char (point-min))
640 (or (re-search-forward (concat "^" title "$") (point-max) t)
641 (when (or force planner-diary-create-section-flag)
642 (insert title "\n\n\n")
643 (backward-char 3)
645 (error "No \"%s\" section in this buffer" title))
646 ;; point is at the end of something like "* Diary"
647 ;; delete the old text
648 (let ((beg (point))
649 (end (if (re-search-forward "^* " (point-max) t)
650 (progn (beginning-of-line)
651 (backward-char 1)
652 (point))
653 (point-max))))
654 (delete-region beg end)
655 ;; point is at the end of "* Diary"
656 (insert "\n\n")
657 (unless (string= text "")
658 (insert text "\n")))))
660 (defun planner-diary-insert-diary (&optional force)
661 "Insert the fancy diary for the day into the day plan file.
662 If FORCE is non-nil, insert a diary section even if there is no
663 `planner-diary-string' in the buffer."
664 (interactive "P")
665 ;; sanity check
666 (let ((date (planner-diary-get-name)))
667 (unless (string-match planner-date-regexp date)
668 (error "Cannot insert diary in this buffer"))
669 (planner-diary-update-section
670 date ; file
671 planner-diary-string ; title
672 (planner-diary-get-diary-entries ; text
673 (planner-filename-to-calendar-date ; date
674 date)
675 planner-diary-number-of-days
676 planner-diary-file)
677 force)))
679 (defun planner-diary-insert-diary-maybe (&optional force)
680 "Maybe insert the fancy diary for the day into the day plan file.
681 If the current day is in the past and FORCE is nil, don't do anything. If
682 FORCE is non-nil, insert a diary section even if there is no
683 `planner-diary-string' in the buffer."
684 (interactive "P")
685 (let ((date (planner-diary-get-name)))
686 ;; check if we're in a day plan buffer
687 (if (and (string-match planner-date-regexp date)
688 (or force ; don't care about the date
689 (not (string< date (planner-today))))) ; not past
690 ;; today, future, or force
691 (planner-diary-insert-diary force)
692 ;; we are in the past -> do nothing, message when interactive
693 (when (interactive-p)
694 (message "No day plan buffer or date is in the past. No diary entries inserted.")))))
696 (defun planner-diary-insert-appts (&optional force)
697 "Insert the diary appointments for the day into the day plan file.
698 If FORCE is non-nil, insert a diary appointments section even if there is no
699 `planner-diary-appts-string' in the buffer."
700 (interactive "P")
701 ;; sanity check
702 (let ((date (planner-diary-get-name)))
703 (unless (string-match planner-date-regexp date)
704 (error "Cannot insert diary in this buffer"))
705 (planner-diary-update-section
706 date ; file
707 planner-diary-appts-string ; title
708 (planner-diary-get-appts-entries ; text
709 (planner-filename-to-calendar-date ; date
710 date)
711 planner-diary-appts-number-of-days
712 planner-diary-appts-file)
713 force)))
715 (defun planner-diary-insert-appts-maybe (&optional force)
716 "Maybe insert the diary appointments for the day into the day plan file.
717 If the current day is in the past and FORCE is nil, don't do anything. If
718 FORCE is non-nil, insert a diary appointments section even if there is no
719 `planner-diary-appts-string' in the buffer."
720 (interactive "P")
721 ;; check if we're in a day plan buffer
722 (let ((date (planner-diary-get-name)))
723 (if (and (string-match planner-date-regexp date)
724 (or force ; don't care about the date
725 (not (string< date (planner-today))))) ; not past
726 ;; today, future, or force
727 (planner-diary-insert-appts force)
728 ;; we are in the past -> do nothing, message when interactive
729 (when (interactive-p)
730 (message "No day plan buffer or date is in the past. No diary entries inserted.")))))
732 (defun planner-diary-insert-cal-desk (&optional force)
733 "Insert the cal-desk diary for the day into the day plan file.
734 If FORCE is non-nil, insert a cal-desk diary section even if there is no
735 `planner-diary-cal-desk-string' in the buffer."
736 (interactive "P")
737 ;; sanity check
738 (let ((date (planner-diary-get-name)))
739 (unless (string-match planner-date-regexp date)
740 (error "Cannot insert diary in this buffer"))
741 (planner-diary-update-section
742 date ; file
743 planner-diary-cal-desk-string ; title
744 (planner-diary-get-cal-desk-entries ; text
745 (planner-filename-to-calendar-date ; date
746 (file-name-nondirectory date))
747 planner-diary-cal-desk-number-of-days
748 planner-diary-cal-desk-file)
749 force)))
751 (defun planner-diary-insert-cal-desk-maybe (&optional force)
752 "Maybe insert the cal-desk diary for the day into the day plan file.
753 If the current day is in the past and FORCE is nil, don't do anything. If
754 FORCE is non-nil, insert a cal-desk appointments section even if there is no
755 `planner-diary-cal-desk-string' in the buffer."
756 (interactive "P")
757 ;; check if we're in a day plan buffer
758 (let ((date (planner-diary-get-name)))
759 (if (and (string-match planner-date-regexp date)
760 (or force ; don't care about the date
761 (not (string< date (planner-today))))) ; not past
762 ;; today, future, or force
763 (planner-diary-insert-cal-desk force)
764 ;; we are in the past -> do nothing, message when interactive
765 (when (interactive-p)
766 (message "No day plan buffer or date is in the past. No diary entries inserted.")))))
769 (defun planner-diary-insert-public (&optional force)
770 "Insert the public diary for the day into the day plan file.
771 If FORCE is non-nil, insert a public diary section even if there is no
772 `planner-diary-public-string' in the buffer."
773 (interactive "P")
774 ;; sanity check
775 (let ((date (planner-diary-get-name)))
776 (unless (string-match planner-date-regexp date)
777 (error "Cannot insert diary in this buffer"))
778 (planner-diary-update-section
779 date ; file
780 planner-diary-public-string ; title
781 (planner-diary-get-public-entries ; text
782 (planner-filename-to-calendar-date ; date
783 date)
784 planner-diary-public-number-of-days
785 planner-diary-public-file)
786 force)))
788 (defun planner-diary-insert-public-maybe (&optional force)
789 "Maybe insert the public diary for the day into the day plan file.
790 If the current day is in the past and FORCE is nil, don't do anything. If
791 FORCE is non-nil, insert a public appointments section even if there is no
792 `planner-diary-public-string' in the buffer."
793 (interactive "P")
794 ;; check if we're in a day plan buffer
795 (let ((date (planner-diary-get-name)))
796 (if (and (string-match planner-date-regexp date)
797 (or force ; don't care about the date
798 (not (string< date (planner-today))))) ; not past
799 ;; today, future, or force
800 (planner-diary-insert-public force)
801 ;; we are in the past -> do nothing, message when interactive
802 (when (interactive-p)
803 (message "No day plan buffer or date is in the past. No diary entries inserted.")))))
805 (defun planner-diary-insert-private (&optional force)
806 "Insert the private diary for the day into the day plan file.
807 If FORCE is non-nil, insert a private diary section even if there is no
808 `planner-diary-private-string' in the buffer."
809 (interactive "P")
810 ;; sanity check
811 (let ((date (planner-diary-get-name)))
812 (unless (string-match planner-date-regexp date)
813 (error "Cannot insert diary in this buffer"))
814 (planner-diary-update-section
815 date ; file
816 planner-diary-private-string ; title
817 (planner-diary-get-private-entries ; text
818 (planner-filename-to-calendar-date ; date
819 date)
820 planner-diary-private-number-of-days
821 planner-diary-private-file))
822 force))
824 (defun planner-diary-insert-private-maybe (&optional force)
825 "Maybe insert the private diary for the day into the day plan file.
826 If the current day is in the past and FORCE is nil, don't do anything. If
827 FORCE is non-nil, insert a private appointments section even if there is no
828 `planner-diary-private-string' in the buffer."
829 (interactive "P")
830 ;; check if we're in a day plan buffer
831 (let ((date (planner-diary-get-name)))
832 (if (and (string-match planner-date-regexp date)
833 (or force ; don't care about the date
834 (not (string< date (planner-today))))) ; not past
835 ;; today, future, or force
836 (planner-diary-insert-private force)
837 ;; we are in the past -> do nothing, message when interactive
838 (when (interactive-p)
839 (message "No day plan buffer or date is in the past. No diary entries inserted.")))))
841 ;; UPDATE ALL DIARIES
842 ;;;###autoload
843 (defun planner-diary-insert-all-diaries (&optional force)
844 "Update all diary sections in a day plan file.
845 If FORCE is non-nil, insert a diary section even if there is no section header.
846 Inserts only diaries if the corresponding `planner-diary-use-*' variable is t."
847 (interactive)
848 (when planner-diary-use-diary
849 (planner-diary-insert-diary force))
850 (when planner-diary-use-cal-desk
851 (planner-diary-insert-cal-desk force))
852 (when planner-diary-use-appts
853 (planner-diary-insert-appts force))
854 (when planner-diary-use-private-diary
855 (planner-diary-insert-private force))
856 (when planner-diary-use-public-diary
857 (planner-diary-insert-public force)))
859 ;;;###autoload
860 (defun planner-diary-insert-all-diaries-maybe (&optional force)
861 "Update all diary sections in a day plan file.
862 If the current day is in the past and FORCE is nil, don't do anything.
863 If FORCE is non-nil, insert a diary section even if there is no section header.
864 Inserts only diaries if the corresponding `planner-diary-use-*' variable is t."
865 (interactive)
866 ;; I intentionally call these individual functions rather than
867 ;; planner-diary-insert-all-diaries. It might make future code changes
868 ;; simpler.
869 (when planner-diary-use-diary
870 (planner-diary-insert-diary-maybe force))
871 (when planner-diary-use-cal-desk
872 (planner-diary-insert-cal-desk-maybe force))
873 (when planner-diary-use-appts
874 (planner-diary-insert-appts-maybe force))
875 (when planner-diary-use-private-diary
876 (planner-diary-insert-private-maybe force))
877 (when planner-diary-use-public-diary
878 (planner-diary-insert-public-maybe force)))
880 ;;;###autoload
881 (defun planner-diary-show-day-plan-or-diary ()
882 "Show the day plan or diary entries for the date under point in calendar.
883 Add this to `calendar-move-hook' if you want to use it. In that case you
884 should also `remove-hook' `planner-calendar-show' from `calendar-move-hook'."
885 (interactive)
886 (or (planner-calendar-show)
887 (and (fboundp 'diary-view-entries)
888 (diary-view-entries 1))
889 (view-diary-entries 1)))
891 ;;;###autoload
892 (defun planner-diary-insinuate ()
893 "Hook Diary into Planner.
894 Automatically insert and update a Diary section in day plan files.
895 This adds a new key binding to `planner-mode-map':
896 C-cC-e updates the diary sections."
897 ;; FIXME: update all diary sections: planner-diary-insert-all-diaries-maybe
898 (define-key planner-mode-map "\C-c\C-e"
899 'planner-diary-insert-all-diaries-maybe)
900 (add-hook 'planner-goto-hook 'planner-diary-insert-all-diaries-maybe))
902 ;;;###autoload
903 (defalias 'planner-insinuate-diary 'planner-diary-insinuate)
905 ;;;###autoload
906 (defun planner-diary-add-entry (date time text)
907 "Prompt for a diary entry to add to `diary-file' on DATE.
908 Uses `planner-annotation-functions' to make hyperlinks.
909 TIME and TEXT are used in the description."
910 (interactive (list (planner-read-date nil t)
911 (read-string "Time: ")
912 (read-string "Diary entry: ")))
913 (save-window-excursion
914 (make-diary-entry
915 (concat
916 (let ((cal-date (planner-filename-to-calendar-date date)))
917 (calendar-date-string cal-date t t))
918 " " time " " text " "
919 (run-hook-with-args-until-success
920 'planner-annotation-functions))
921 nil planner-diary-file)))
923 (provide 'planner-diary)
925 ;;; planner-diary.el ends here