planner-el.texi (Keeping Track of Time): Recover paragraph that had
[planner-el.git] / planner-notes-index.el
blob2681a7cad47ad5bbeb66674897ea57160e660206
1 ;;; planner-notes-index.el --- Note indexing support for the Emacs planner
3 ;; Copyright (C) 2004, 2005, 2006, 2008 Free Software Foundation, Inc.
5 ;;; Commentary:
7 ;;;_+ Package description
9 ;; Author: Sandra Jean Chua <sacha@free.net.ph>
10 ;; Filename: planner-notes-index.el
11 ;; URL: http://www.wjsullivan.net/PlannerMode.html
13 ;; This file is part of Planner. It is not part of GNU Emacs.
15 ;; Planner is free software; you can redistribute it and/or modify it
16 ;; under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation; either version 3, or (at your option)
18 ;; any later version.
20 ;; Planner is distributed in the hope that it will be useful, but
21 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 ;; General Public License for more details.
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with Planner; see the file COPYING. If not, write to the
27 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 ;; Boston, MA 02110-1301, USA.
30 ;;;_+ Usage
32 ;; Place planner-notes-index.el in your load path and add this to your
33 ;; .emacs:
35 ;; (require 'planner-notes-index)
37 ;; Then you can use tags of the form
39 ;; <planner-notes-index page="2004.03.02">
40 ;; <planner-notes-index from="2004.03.01" to="2004.03.31">
41 ;; <planner-notes-index limit="10">
42 ;; <planner-notes-index page="PlanPage">
43 ;; <planner-notes-index-month-table month="2004.03" limit="5">
44 ;; <planner-notes-index-month-table month="2004.03">
46 ;; You can also use the following interactive functions:
48 ;; planner-notes-index
49 ;; planner-notes-index-days
50 ;; planner-notes-index-weeks
51 ;; planner-notes-index-months
52 ;; planner-notes-index-years (wow!)
54 ;; These work based on the current date (date of current buffer, or today).
56 ;; If a single page is specified, this page is scanned for headlines
57 ;; of the form:
59 ;; .#1 Headline
61 ;; and the results are presented as a bulleted list.
63 ;; If FROM and TO are specified, all date pages between them (inclusive)
64 ;; are scanned. If FROM is omitted, it is assumed to be the earliest entry.
65 ;; If TO is omitted, it is assumed to be the latest entry.
67 ;; If RECENT is specified, the list includes only that many recent headlines.
69 ;; and the results are presented as a bulleted list.
71 ;; To customize presentation, you can write a function that generates
72 ;; the appropriate <planner-notes-index> tags. You can also use
73 ;; planner-extract-note-headlines in your own functions.
75 ;;;_+ Contributors
77 ;; Yann Hodique helped port this to Muse.
79 (require 'planner)
80 (require 'calendar)
82 ;;; Code:
84 ;;;_* Internal functions
86 ;; Old names, but then planner-notes-get-headlines was created.
87 ;; These may disappear.
88 (defalias 'planner-notes-index-get-headlines
89 'planner-notes-index-headlines-on-page)
90 (defalias 'planner-notes-index-get-headlines-range
91 'planner-notes-index-headlines-in-range)
93 (defun planner-notes-index-headlines-on-page (page &optional limit)
94 "Return a list of headlines in PAGE.
95 If LIMIT is non-nil, only that many headlines are returned."
96 (when (stringp limit) (setq limit (string-to-number limit)))
97 (with-temp-buffer
98 (insert-file-contents (planner-page-file page))
99 (planner-notes-get-headlines limit)))
101 (defun planner-notes-index-headlines-in-range (&optional from to limit)
102 "Return a list of headlines over a span of day pages.
103 FROM (inclusive) and TO (inclusive) limit the dates. If FROM is
104 nil, start from the earliest entry. If TO is nil, include the
105 last entry. If LIMIT is non-nil, return at most LIMIT entries."
106 (with-planner
107 (when (and limit (stringp limit)) (setq limit (string-to-number limit)))
108 (let ((pages (mapcar 'car (planner-get-day-pages from to)))
109 data
110 headlines)
111 (while (and pages (if limit (> limit 0) t))
112 (setq data (planner-notes-index-headlines-on-page (car pages) limit))
113 (when limit
114 (setq limit (- limit (length data))))
115 (when data
116 (add-to-list 'headlines
117 (cons (car pages) data) t))
118 (setq pages (cdr pages)))
119 headlines)))
121 (defun planner-notes-index-insert-as-list (page-headlines &optional
122 limit prefix suffix)
123 "Link and format PAGE-HEADLINES.
124 PAGE-HEADLINES is a list of the form (page ((anchor headline) ...).
125 If LIMIT is non-nil, only display that number of recent items.
126 If PREFIX is non-nil, it is prepended to the item.
127 If SUFFIX is non-nil, it is appended to the item."
128 (when (and limit (stringp limit)) (setq limit (string-to-number limit)))
129 (let ((page (car page-headlines)))
130 (setq page-headlines (cdr page-headlines))
131 (while (and page-headlines (if limit (> limit 0) t))
132 (when prefix (insert prefix))
133 (insert (planner-make-link (concat page (caar page-headlines))
134 (cdr (car page-headlines))
136 (when suffix (insert suffix))
137 (setq page-headlines (cdr page-headlines))
138 (when limit (setq limit (1- limit))))))
140 ;;;_* Emacs-wiki tags
142 ;;;###autoload
143 (defun planner-notes-index-tag (tag-beg tag-end attrs)
144 "Mark up planner-notes-index tags.
146 Tags can be of the form:
148 <planner-notes-index page=\"2004.03.02\">
149 <planner-notes-index from=\"2004.03.01\" to=\"2004.03.31\">
150 <planner-notes-index limit=\"10\">"
151 (let (last-month last-year)
152 (mapc
153 (lambda (item)
154 (when (string-match planner-date-regexp (car item))
155 (unless (and last-year
156 (string= (match-string 1 (car item)) last-year))
157 (insert "* "
158 (setq last-year (match-string 1 (car item)))
159 "\n\n"))
160 (unless (and last-month
161 (string= (match-string 2 (car item)) last-month))
162 (insert "** " last-year "."
163 (setq last-month (match-string 2 (car item))) "\n\n")))
164 (insert "*** " (car item) "\n\n")
165 (planner-notes-index-insert-as-list item nil " - " "\n")
166 (insert "\n"))
167 (if (assoc "page" attrs)
168 (cons (cdr (assoc "page" attrs))
169 (planner-notes-index-headlines-on-page
170 (cdr (assoc "page" attrs))
171 (cdr (assoc "limit" attrs))))
172 (planner-notes-index-headlines-in-range
173 (cdr (assoc "from" attrs))
174 (cdr (assoc "to" attrs))
175 (cdr (assoc "limit" attrs)))))))
177 ;;;###autoload
178 (defun planner-notes-index-month-table-tag (beg end attrs)
179 "Mark up a month note index. Tag is from BEG to END.
180 ATTRS is a list of attributes. \"Month\" is a yyyy.mm
181 string (default: current month). \"Limit\" is the maximum number
182 of items per day (default: all).
184 Examples:
185 <planner-notes-index-month-table month=\"2004.02\">
186 <planner-notes-index-month-table month=\"2004.02\" limit=\"4\">"
187 (let ((month (cdr (assoc "month" attrs)))
188 (limit (cdr (assoc "limit" attrs)))
189 day headlines last dow)
190 (unless month
191 (setq month (substring (planner-get-current-date-filename) 0 7)))
192 (when limit (setq limit (string-to-number limit)))
193 (setq last (planner-filename-to-calendar-date (concat month ".31")))
194 (setq last (calendar-last-day-of-month (elt last 0) (elt last 2)))
195 ;; Chronologically sorted, at least by day
196 (setq headlines
197 (nreverse
198 (planner-notes-index-headlines-in-range
199 (concat month ".01") (concat month ".31")))) ;; works
200 ;; Offset to negative if month does not start on Sunday
201 (setq day (- 1 (calendar-day-of-week
202 (planner-filename-to-calendar-date (concat month ".01")))))
203 (setq dow 0)
204 (insert
205 "<table class=\"month_notes\">"
206 "<tr><th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th>"
207 "<th>Thu</th><th>Fri</th><th>Sat</th></tr>")
208 (while (or (< day 31) (< day 6))
209 (when (= dow 0) (insert "<tr>"))
210 (insert
211 "<td><div class=\"month_day\">"
212 (if (and (>= day 1) (<= day last))
213 (format "[[%s.%02d]]" month day)
215 "</div><div class=\"month_details\">\n")
216 (let ((data (assoc (format "%s.%02d" month day)
217 headlines))
218 extra)
219 (planner-notes-index-insert-as-list
220 data limit nil "<br>")
222 (when (and limit data (> (length data) limit))
223 (setq extra (- (length data) limit))
224 (insert (format "%d more entr%s" extra
225 (if (= extra 1) "y" "ies")))))
226 (insert "</div></td>")
227 (when (= dow 6)
228 (insert "</tr>"))
229 (setq day (1+ day))
230 (setq dow (% (1+ dow) 7)))
231 (insert "</table>")))
233 ;;;_* Other user functions
235 (defvar planner-notes-index-buffer "*Notes Index*"
236 "Buffer for planner note index.")
238 ;;;###autoload
239 (defun planner-notes-index (&optional from to limit)
240 "Display a clickable notes index.
241 If called from a Lisp program, display only dates between FROM
242 and TO. With a prefix arg LIMIT, display only that number of
243 entries."
244 (interactive "i
247 (when limit (setq limit (prefix-numeric-value limit)))
248 (let ((headlines
249 (planner-notes-index-headlines-in-range from to limit))
250 last-month last-year)
251 (with-current-buffer (get-buffer-create planner-notes-index-buffer)
252 (setq buffer-read-only nil)
253 (erase-buffer)
254 (cd (planner-directory))
255 (mapcar (lambda (item)
256 (when (string-match planner-date-regexp (car item))
257 (unless (and last-year
258 (string= (match-string 1 (car item)) last-year))
259 (insert "* "
260 (setq last-year (match-string 1 (car item)))
261 "\n\n"))
262 (unless (and last-month
263 (string= (match-string 2 (car item)) last-month))
264 (insert "** " last-year "."
265 (setq last-month
266 (match-string 2 (car item))) "\n\n")))
267 (insert "*** " (car item) "\n\n")
268 (planner-notes-index-insert-as-list item nil " - " "\n")
269 (insert "\n"))
270 headlines)
271 (planner-mode)
272 (goto-char (point-min))
273 (pop-to-buffer (current-buffer)))))
275 ;;;###autoload
276 (defun planner-notes-index-days (days)
277 "Display an index of notes posted over the past few DAYS.
278 The list ends with the day of the current buffer or `planner-today'."
279 (interactive (list (read-string "Number of days (1): " nil nil "1")))
280 (when (stringp days) (setq days (string-to-number days)))
281 (planner-notes-index
282 (planner-calculate-date-from-day-offset
283 (planner-get-current-date-filename) (- 1 days))
284 (planner-get-current-date-filename)))
286 ;;;###autoload
287 (defun planner-notes-index-weeks (weeks)
288 "Display an index of notes posted over the past few WEEKS.
289 The list ends with the week of the current buffer or `planner-today'.
290 Weeks start from Sunday."
291 (interactive (list (read-string "Number of weeks (1): " nil nil "1")))
292 (when (stringp weeks) (setq weeks (string-to-number weeks)))
293 (let ((date (planner-filename-to-calendar-date
294 (planner-get-current-date-filename))))
295 (planner-notes-index
296 (planner-date-to-filename
297 (encode-time 0 0 0 (- (elt date 1)
298 (calendar-day-of-week date)
299 (* (- weeks 1) 7))
300 (elt date 0) (elt date 2)))
301 (planner-get-current-date-filename))))
303 ;;;###autoload
304 (defun planner-notes-index-months (months)
305 "Display an index of notes posted over the past few MONTHS.
306 The list ends with the month of the current buffer or `planner-today'."
307 (interactive (list (read-string "Number of months (1): " nil nil "1")))
308 (when (stringp months) (setq months (string-to-number months)))
309 (let ((date (planner-filename-to-calendar-date
310 (planner-get-current-date-filename))))
311 (planner-notes-index
312 (planner-date-to-filename
313 (encode-time 0 0 0 1
314 (- (elt date 0)
315 months -1) (elt date 2)))
316 (planner-get-current-date-filename))))
318 ;;;###autoload
319 (defun planner-notes-index-years (years)
320 "Display an index of notes posted over the past few YEARS.
321 The current year is included."
322 (interactive (list (read-string "Number of years (1): " nil nil "1")))
323 (when (stringp years) (setq years (string-to-number years)))
324 (let ((date (planner-filename-to-calendar-date (planner-today))))
325 (planner-notes-index
326 (planner-date-to-filename
327 (encode-time 0 0 0 1
329 (- (elt date 2)
330 years -1)))
331 (planner-get-current-date-filename))))
333 ;;;_* Initialization
335 (add-hook 'muse-publish-markup-tags
336 (if (featurep 'muse-nestable-tags)
337 '("planner-notes-index" nil t nil planner-notes-index-tag)
338 '("planner-notes-index" nil t planner-notes-index-tag)))
339 (add-hook 'muse-publish-markup-tags
340 (if (featurep 'muse-nestable-tags)
341 '("planner-notes-index-month-table" nil t nil
342 planner-notes-index-month-table-tag)
343 '("planner-notes-index-month-table" nil t
344 planner-notes-index-month-table-tag)))
346 (provide 'planner-notes-index)
348 ;;;_* Local emacs vars.
350 ;; Local variables:
351 ;; allout-layout: (* : )
352 ;; End:
354 ;;; planner.el ends here