Applied patch from Istvan Marko <istvan@cmdmail.amd.com>
[bpt/emacs.git] / lisp / calendar / todo-mode.el
CommitLineData
579e1c67
OS
1;; todomode.el -- major mode for editing TODO list files
2
49c48a1b 3;; $Id: todomode.el,v 1.10 1997/08/06 08:56:03 os10000 Exp os10000 $
3cb152f9
OS
4
5;; ---------------------------------------------------------------------------
6
595b2334 7;; Copyright (C) 1997 Oliver Seidel
3cb152f9 8
595b2334
OS
9;; Keywords: todo-mode
10
11;; todomode.el is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; todomode.el is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
25
26;; ---------------------------------------------------------------------------
27
3cb152f9 28;;
579e1c67
OS
29;; Quickstart Installation:
30;; ========================
595b2334 31;;
579e1c67 32;; To get this to work, make emacs execute the line
3d8105fb 33;;
579e1c67 34;; (require 'todomode) ;; load the TODO package
a360be79 35;;
579e1c67
OS
36;; I would also recommend executing the following commands
37;; so as to extend the bindings in your global keymap:
da2ee685 38;;
579e1c67
OS
39;; (global-set-key "\C-ct" 'todo-show) ;; switch to TODO buffer
40;; (global-set-key "\C-ci" 'todo-cmd-inst) ;; insert new item
8cdc3b3d 41;;
7e6ed9b9 42;;
3cb152f9 43;;
7e6ed9b9 44;; Description:
579e1c67 45;; ============
7e6ed9b9 46;;
579e1c67
OS
47;; TODO is a major mode for EMACS which offers functionality to treat
48;; most lines in one buffer as a list of items one has to do. There
49;; are facilities to add new items, which are categorised, to edit or
50;; even delete items from the buffer. The buffer contents are currently
51;; compatible with the diary, so that the list of todo-items will show
52;; up in the FANCY diary mode.
7e6ed9b9 53;;
579e1c67
OS
54;; Notice: Besides the major mode, this file also exports the function
55;; "todo-show" which will change to the one specific TODO file that has
56;; been specified in the todo-file-do variable. If this file does not
57;; conform to the TODO mode conventions, the todo-show function will add
58;; the appropriate header and footer. I don't anticipate this to cause
59;; much grief, but be warned, in case you attempt to read a plain text file.
60;;
61;;
62;;
63;; Operation:
64;; ==========
a360be79
OS
65;;
66;; You will have the following facilities available:
7e6ed9b9 67;;
03944ca7
OS
68;; M-x todo-show will enter the todo list screen, here type
69;;
da2ee685
OS
70;; + to go to next category
71;; - to go to previous category
7e6ed9b9 72;; e to edit the current entry
da2ee685
OS
73;; f to file the current entry, including a
74;; comment and timestamp
7e6ed9b9
OS
75;; i to insert a new entry
76;; k to kill the current entry
8cdc3b3d 77;; l lower the current entry's priority
da2ee685
OS
78;; n for the next entry
79;; p for the previous entry
80;; q to save the list and exit the buffer
81;; r raise current entryk's priority
82;; s to save the list
7e6ed9b9 83;;
a360be79
OS
84;; When you add a new entry, you are asked for the text and then for the
85;; category. I for example have categories for things that I want to do
86;; in the office (like mail my mum), that I want to do in town (like buy
87;; cornflakes) and things I want to do at home (move my suitcases). The
88;; categories can be selected with the cursor keys and if you type in the
89;; name of a category which didn't exist before, an empty category of the
03944ca7 90;; desired name will be added and filled with the new entry.
a360be79 91;;
7e6ed9b9 92;;
7e6ed9b9 93;;
579e1c67
OS
94;; Configuration:
95;; ==============
7e6ed9b9 96;;
579e1c67
OS
97;; --- todo-prefix
98;;
99;; I would like to recommend that you use the prefix "*/*" (by
100;; leaving the variable 'todo-prefix' untouched) so that the diary
101;; displays each entry every day.
a360be79 102;;
03944ca7
OS
103;; To understand what I mean, please read the documentation that goes
104;; with the calendar since that will tell you how you can set up the
105;; fancy diary display and use the #include command to include your
106;; todo list file as part of your diary.
a360be79 107;;
579e1c67
OS
108;;
109;; --- todo-file-do
110;;
111;; This variable is fairly self-explanatory. You have to store your TODO
112;; list somewhere. This variable tells the package where to go and find
113;; this file.
114;;
115;;
116;; --- todo-file-done
117;;
118;; Even when you're done, you may wish to retain the entries. Given
119;; that they're timestamped and you are offered to add a comment, this
120;; can make a useful diary of past events. It will even blend in with
121;; the EMACS diary package. So anyway, this variable holds the name
122;; of the file for the filed todo-items.
123;;
124;;
125;; --- todo-mode-hook
126;;
127;; Just like other modes, too, this mode offers to call your functions
128;; before it goes about its business. This variable will be inspected
129;; for any functions you may wish to have called once the other TODO
130;; mode preparations have been completed.
131;;
132;;
133;; --- todo-ins-thresh
134;;
3d8105fb
OS
135;; Another nifty feature is the insertion accuracy. If you have 8 items
136;; in your TODO list, then you may get asked 4 questions by the binary
137;; insertion algorithm. However, you may not really have a need for such
138;; accurate priorities amongst your TODO items. If you now think about
139;; the binary insertion halfing the size of the window each time, then
140;; the threshhold is the window size at which it will stop. If you set
141;; the threshhold to zero, the upper and lower bound will coincide at the
142;; end of the loop and you will insert your item just before that point.
143;; If you set the threshhold to i.e. 8, it will stop as soon as the window
144;; size drops below that amount and will insert the item in the approximate
145;; centre of that window. I got the idea for this feature after reading
146;; a very helpful e-mail reply from Trey Jackson <tjackson@ichips.intel.com>
147;; who corrected some of my awful coding and pointed me towards some good
148;; reading. Thanks Trey!
149;;
579e1c67
OS
150;;
151;;
152;; History and Gossip:
153;; ===================
154;;
155;; Just for the case that you are wondering about the ugly name of this
156;; package: I am one of those unfortunate people who have DOS, LINUX and
157;; OS/2 on one of their computers, so part of my home-filespace is shared
158;; and stored on a DOS partition, which is accessible to all systems. If
159;; you wish, you can of course rename the name of the file (and the "provide"
160;; command near the end of this package) to something more aisthetically
161;; (please don't argue about this spelling ...) pleasing, like i.e. todo-mode.
162;;
a360be79
OS
163;; Enjoy this package and express your gratitude by sending valuables
164;; to my parents' address as listed above!!!
165;;
166;; Oliver Seidel
579e1c67
OS
167;;
168;;
169;;
170;; Contact information:
171;; ====================
172;;
173;; address ....: O Seidel, Lessingstr 8, Eschborn, FRG
174;; e-mail .....: Oliver.Seidel@cl.cam.ac.uk (was valid on 2 Aug 1997)
175;;
176
177;; ---------------------------------------------------------------------------
178
179;;
180;; $Log: todomode.el,v $
49c48a1b
OS
181;; Revision 1.10 1997/08/06 08:56:03 os10000
182;; Acted upon suggestion from Shane Holder <holder@rsn.hp.com>:
183;; Cancelling the editing of an entry will not delete it any more.
184;;
03944ca7
OS
185;; Revision 1.9 1997/08/06 08:12:03 os10000
186;; Improved documentation. Broke some lines to comply with
187;; Richard Stallman's email to please keep in sync with the
188;; rest of the Emacs distribution files.
189;;
579e1c67
OS
190;; Revision 1.8 1997/08/05 22:39:04 os10000
191;; Made todomode.el available under GPL.
192;;
193;; Revision 1.7 1997/08/05 22:34:14 os10000
194;; Fixed insertion routine with help from Trey Jackson
195;; <tjackson@ichips.intel.com>; added todo-ins-thresh;
196;; fixed keyboard layout to remove unwanted keys.
197;;
198;; Revision 1.6 1997/08/05 16:47:01 os10000
199;; Incorporated menus for XEmacs from Allan.Cochrane@soton.sc.philips.com,
200;; fixed TYPO, fixed todo-file-cmd, cleaned up rcs history.
201;;
202;; Revision 1.5 1997/08/05 14:43:39 os10000
203;; Added improvements from Ron Gut <rgut@aware.com>.
204;; Added category management.
205;;
206;; Revision 1.4 1997/08/04 16:18:45 os10000
207;; Added Raise/Lower item.
208;;
209;; Revision 1.3 1997/08/03 12:47:26 os10000
210;; Cleaned up variables, prefix and cursor position.
211;;
212;; Revision 1.2 1997/08/03 12:15:28 os10000
213;; It appears to work.
214;;
215;; Revision 1.1 1997/08/03 12:15:13 os10000
216;; Initial revision
217;;
7e6ed9b9
OS
218
219;; ---------------------------------------------------------------------------
220
3cb152f9
OS
221;; User-configurable variables:
222
da2ee685 223(defvar todo-prefix "*/*" "TODO mode prefix when creating entries")
7e6ed9b9 224(defvar todo-file-do "~/.todo-do" "TODO mode filename of list file")
3cb152f9 225(defvar todo-file-done "~/.todo-done" "TODO mode filename of archive file")
579e1c67 226(defvar todo-mode-hook nil "Hooks invoked when the TODO mode is entered.")
3d8105fb 227(defvar todo-ins-thresh 0 "TODO mode insertion accuracy.")
3cb152f9
OS
228
229;; ---------------------------------------------------------------------------
230
49c48a1b
OS
231;; Get some outside help ...
232
3cb152f9 233(require 'time-stamp)
49c48a1b
OS
234(require 'easymenu)
235
236;; ---------------------------------------------------------------------------
3cb152f9 237
3d8105fb
OS
238(defvar todo-mode-map nil "TODO mode keymap. See `todo-mode'")
239(if todo-mode-map
240 nil
241 (let ((map (make-keymap)))
242 (suppress-keymap map t)
243 (define-key map "+" 'todo-cmd-forw)
244 (define-key map "-" 'todo-cmd-back)
245 (define-key map "e" 'todo-cmd-edit)
246 (define-key map "f" 'todo-cmd-file)
247 (define-key map "i" 'todo-cmd-inst)
248 (define-key map "k" 'todo-cmd-kill)
249 (define-key map "l" 'todo-cmd-lowr)
250 (define-key map "n" 'todo-cmd-next)
251 (define-key map "p" 'todo-cmd-prev)
252 (define-key map "q" 'todo-cmd-done)
253 (define-key map "r" 'todo-cmd-rais)
254 (define-key map "s" 'todo-cmd-save)
255 (setq todo-mode-map map)))
3cb152f9 256
da2ee685
OS
257(defun todo-cat-slct ()
258 (let ((todo-category-name (nth todo-category-number todo-cats)))
579e1c67
OS
259 (setq mode-line-buffer-identification
260 (concat "Category: " todo-category-name))
da2ee685
OS
261 (widen)
262 (goto-char (point-min))
263 (search-forward (concat "--- " todo-category-name))
264 (setq begin (+ (point-at-eol) 1))
265 (search-forward "--- End")
266 (narrow-to-region begin (point-at-bol))
267 (goto-char (point-min))
268 )
269)
270
271(defun todo-cmd-forw () "Go forward to TODO list of next category."
272 (interactive)
273 (let ((todo-cat-cnt (- (length todo-cats) 1)))
274 (setq todo-category-number (if (< todo-category-number todo-cat-cnt)
275 (+ todo-category-number 1) 0))
276 (todo-cat-slct)
277 )
278 )
279
280(defun todo-cmd-back () "Go back to TODO list of previous category."
281 (interactive)
282 (let ((todo-cat-cnt (- (length todo-cats) 1)))
283 (setq todo-category-number (if (> todo-category-number 0)
284 (- todo-category-number 1) todo-cat-cnt))
285 (todo-cat-slct)
286 )
287 )
288
289(defun todo-cmd-prev () "Select previous entry of TODO list."
3cb152f9
OS
290 (interactive)
291 (forward-line -1)
292 (beginning-of-line nil)
293 (message "")
294 )
295
da2ee685 296(defun todo-cmd-next () "Select next entry of TODO list."
3cb152f9
OS
297 (interactive)
298 (forward-line 1)
299 (beginning-of-line nil)
300 (message "")
301 )
302
da2ee685
OS
303(defun todo-cmd-save () "Save the TODO list."
304 (interactive)
305 (save-buffer)
306 )
307
308(defun todo-cmd-done () "Done with TODO list for now."
3cb152f9 309 (interactive)
da2ee685
OS
310 (widen)
311 (save-buffer)
7e6ed9b9
OS
312 (beginning-of-line nil)
313 (message "")
3cb152f9
OS
314 (bury-buffer)
315 )
316
579e1c67
OS
317(defun todo-line () "Find current line in buffer."
318 (buffer-substring (point-at-bol) (point-at-eol)))
3cb152f9 319
da2ee685 320(defun todo-cmd-edit () "Edit current TODO list entry."
3cb152f9 321 (interactive)
03944ca7 322 (let ((todo-entry (read-from-minibuffer "Edit: " (todo-line))))
da2ee685 323 (delete-region (point-at-bol) (point-at-eol))
03944ca7 324 (insert todo-entry)
da2ee685 325 (beginning-of-line nil)
03944ca7 326 (message "")))
3cb152f9
OS
327
328(defvar todo-prv-lne 0 "previous line that I asked about.")
329(defvar todo-prv-ans 0 "previous answer that I got.")
330
da2ee685 331(defun todo-add-category (cat) "Add a new category to the TODO list."
3cb152f9 332 (interactive)
3cb152f9 333 (save-window-excursion
da2ee685 334 (setq todo-cats (cons cat todo-cats))
3cb152f9 335 (find-file todo-file-do)
da2ee685
OS
336 (widen)
337 (goto-char (point-min))
338 (let ((posn (search-forward "-*- mode: todo; " 17 t)))
339 (if (not (null posn)) (goto-char posn))
579e1c67
OS
340 (if (equal posn nil)
341 (progn
342 (insert "-*- mode: todo; \n")
343 (forward-char -1))
344 (kill-line)))
da2ee685
OS
345 (insert (format "todo-cats: %S; -*-" todo-cats))
346 (forward-char 1)
579e1c67
OS
347 (insert (format "%s --- %s\n--- End\n%s %s\n"
348 todo-prefix cat todo-prefix (make-string 75 ?-)))
3cb152f9 349 )
da2ee685
OS
350 0
351 )
352
3d8105fb
OS
353(defun todo-cmd-inst ()
354 "Insert new TODO list entry."
da2ee685 355 (interactive)
3cb152f9 356 (beginning-of-line nil)
579e1c67
OS
357 (let* ((todo-entry (concat todo-prefix " "
358 (read-from-minibuffer "New TODO entry: ")))
3d8105fb
OS
359 (temp-catgs todo-cats)
360 (todo-hstry (cons 'temp-catgs (+ todo-category-number 1))))
da2ee685
OS
361 (save-window-excursion
362 (setq todo-category
3d8105fb
OS
363 (read-from-minibuffer "Category: "
364 (nth todo-category-number todo-cats)
365 nil nil todo-hstry))
366
367 (let ((cat-exists (member todo-category todo-cats)))
368 (setq todo-category-number
369 (if cat-exists
370 (- (length todo-cats) (length cat-exists))
371 (todo-add-category todo-category))))
da2ee685
OS
372 (todo-show)
373 (setq todo-prv-lne 0)
3d8105fb
OS
374
375 (let ((todo-fst 1)
376 (todo-lst (+ 1 (count-lines (point-min) (point-max)))))
377 (while (> (- todo-lst todo-fst) todo-ins-thresh)
378 (let* ((todo-cur (/ (+ todo-fst todo-lst) 2))
579e1c67
OS
379 (todo-ans (if (< todo-cur todo-lst)
380 (todo-ask todo-cur) nil)))
3d8105fb
OS
381 (if todo-ans
382 (setq todo-lst todo-cur)
383 (setq todo-fst (+ todo-cur 1)))))
384
385 (setq todo-fst (/ (+ todo-fst todo-lst) 2))
386 ;; goto-line doesn't have the desired behavior in a narrowed buffer
387 (goto-char (point-min))
388 (message (format "todo-fst=%d" todo-fst))
389 (forward-line (- todo-fst 1)))
390
da2ee685 391 (insert (concat todo-entry "\n"))
3d8105fb 392 (forward-line -1))
da2ee685 393 (beginning-of-line nil)
3d8105fb
OS
394 (message "")))
395
396(defun todo-ask (lne)
397 "Ask whether entry is more important than at LNE."
398 (if (not (equal todo-prv-lne lne))
399 (progn
400 (setq todo-prv-lne lne)
401 (goto-char (point-min))
402 (forward-line (- todo-prv-lne 1))
579e1c67
OS
403 (setq todo-prv-ans (y-or-n-p
404 (concat "More important than '"
405 (todo-line) "'? ")))))
3d8105fb 406 todo-prv-ans)
3cb152f9 407
da2ee685 408(defun todo-cmd-kill () "Delete current TODO list entry."
3cb152f9
OS
409 (interactive)
410 (if (> (count-lines (point-min) (point-max)) 0)
411 (progn
da2ee685 412 (let* ((todo-entry (todo-line))
579e1c67
OS
413 (todo-answer (y-or-n-p (concat "Permanently remove '"
414 todo-entry "'? "))))
da2ee685
OS
415 (if todo-answer
416 (progn
417 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
418 (forward-line -1)
419 )
420 )
421 )
422 (message "")
3cb152f9 423 )
da2ee685 424 (message "No TODO list entry to delete.")
3cb152f9
OS
425 )
426 (beginning-of-line nil)
3cb152f9
OS
427 )
428
8cdc3b3d
OS
429(defun todo-cmd-rais () "Raise priority of current entry."
430 (interactive)
431 (if (> (count-lines (point-min) (point-max)) 0)
432 (progn
433 (setq todo-entry (todo-line))
da2ee685 434 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
8cdc3b3d
OS
435 (forward-line -1)
436 (insert (concat todo-entry "\n"))
da2ee685
OS
437 (forward-line -1)
438 (message "")
8cdc3b3d 439 )
da2ee685 440 (message "No TODO list entry to raise.")
8cdc3b3d
OS
441 )
442 (beginning-of-line nil)
8cdc3b3d
OS
443 )
444
445(defun todo-cmd-lowr () "Lower priority of current entry."
446 (interactive)
447 (if (> (count-lines (point-min) (point-max)) 0)
448 (progn
449 (setq todo-entry (todo-line))
da2ee685 450 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
8cdc3b3d
OS
451 (forward-line 1)
452 (insert (concat todo-entry "\n"))
da2ee685
OS
453 (forward-line -1)
454 (message "")
8cdc3b3d 455 )
da2ee685 456 (message "No TODO list entry to raise.")
8cdc3b3d
OS
457 )
458 (beginning-of-line nil)
8cdc3b3d
OS
459 )
460
da2ee685 461(defun todo-cmd-file () "File away the current TODO list entry."
3cb152f9
OS
462 (interactive)
463 (if (> (count-lines (point-min) (point-max)) 0)
464 (progn
da2ee685
OS
465 (let ((time-stamp-format "%3b %2d, %y, %02I:%02M%p"))
466 (beginning-of-line nil)
a360be79 467 (delete-region (point-at-bol) (search-forward todo-prefix))
da2ee685
OS
468 (insert (time-stamp-string))
469 (end-of-line nil)
a360be79 470 (insert (concat " (" (read-from-minibuffer "Comment: ") ")"))
da2ee685
OS
471 (append-to-file (point-at-bol) (+ 1 (point-at-eol)) todo-file-done)
472 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
473 (forward-line -1)
474 )
475 (message "")
3cb152f9 476 )
da2ee685 477 (message "No TODO list entry to delete.")
3cb152f9
OS
478 )
479 (beginning-of-line nil)
3cb152f9
OS
480 )
481
482;; ---------------------------------------------------------------------------
483
da2ee685
OS
484;; utility functions: These are available in XEmacs, but not in Emacs 19.34
485
486(if (not (fboundp 'point-at-bol))
487 (defun point-at-bol ()
488 (save-excursion
489 (beginning-of-line)
490 (point))))
491
492(if (not (fboundp 'point-at-eol))
493 (defun point-at-eol ()
494 (save-excursion
495 (end-of-line)
496 (point))))
497
498;; ---------------------------------------------------------------------------
499
49c48a1b
OS
500(easy-menu-define todo-menu todo-mode-map "Todo Menu"
501 '("Todo"
a360be79
OS
502 ["Forward item" todo-cmd-forw t]
503 ["Backward item" todo-cmd-back t]
504 "---"
505 ["Edit item" todo-cmd-edit t]
506 ["File item" todo-cmd-file t]
507 ["Insert new item" todo-cmd-inst t]
508 ["Kill item" todo-cmd-kill t]
509 "---"
510 ["Lower item priority" todo-cmd-lowr t]
511 ["Raise item priority" todo-cmd-rais t]
512 "---"
513 ["Next item" todo-cmd-next t]
514 ["Previous item" todo-cmd-prev t]
515 "---"
516 ["Save" todo-cmd-save t]
517 "---"
518 ["Quit" todo-cmd-done t]
49c48a1b 519 ))
a360be79 520
da2ee685
OS
521(defvar todo-cats nil "TODO categories.")
522(defvar todo-category-number 0 "TODO category number.")
523
524(defun todo-mode () "Major mode for editing TODO lists.\n\n\\{todo-mode-map}"
3cb152f9 525 (interactive)
3cb152f9
OS
526 (setq major-mode 'todo-mode)
527 (setq mode-name "TODO")
528 (use-local-map todo-mode-map)
49c48a1b
OS
529 (easy-menu-add todo-menu)
530 (run-hooks 'todo-mode-hook))
da2ee685
OS
531
532(defun todo-show () "Show TODO list."
533 (interactive)
534 (find-file todo-file-do)
535 (if (null todo-cats)
536 (progn
537 (todo-add-category "Todo")
538 (goto-char (point-min))
539 (goto-char (search-forward "--- End"))
540 (let ((bol (point-at-bol)))
541 (forward-line 1)
542 (let* ((eol (+ (point-at-eol) 1))
543 (mrkr (buffer-substring bol eol)))
544 (delete-region bol eol)
545 (goto-char (point-max))
546 (insert mrkr)
547 )
548 )
549 (save-buffer)
550 (kill-buffer (current-buffer))
551 (find-file todo-file-do)
552 )
553 )
7e6ed9b9 554 (beginning-of-line nil)
da2ee685
OS
555 (todo-cat-slct)
556 )
3cb152f9
OS
557
558(provide 'todomode)
559
560;; ---------------------------------------------------------------------------
561
da2ee685 562;; todomode.el ends here
3cb152f9
OS
563
564;; ---------------------------------------------------------------------------