(x-cut-buffer-or-selection-value): Fix previous change.
[bpt/emacs.git] / lisp / calendar / todo-mode.el
CommitLineData
da2ee685 1; todomode.el -- major mode for editing TODO list files
3cb152f9
OS
2
3;; ---------------------------------------------------------------------------
4
5;; Note: You may copy this file freely for non-commercial use; otherwise,
6;; please contact (address) O Seidel, Lessingstr 8, Eschborn, FRG
7;; (e-mail ) Oliver.Seidel@cl.cam.ac.uk (2 Aug 1997)
8
a360be79 9;; $Id: todomode.el,v 1.5 1997/08/05 14:43:39 os10000 Exp os10000 $
3cb152f9 10;;
384af0dd 11;; $Log: todomode.el,v $
a360be79
OS
12;; Revision 1.5 1997/08/05 14:43:39 os10000
13;; Added improvements from Ron Gut <rgut@aware.com>.
14;; Added category management.
15;;
da2ee685
OS
16;; Revision 1.4 1997/08/04 16:18:45 os10000
17;; Added Raise/Lower item.
18;;
8cdc3b3d
OS
19;; Revision 1.3 1997/08/03 12:47:26 os10000
20;; Cleaned up variables, prefix and cursor position.
21;;
7e6ed9b9
OS
22;; Revision 1.2 1997/08/03 12:15:28 os10000
23;; It appears to work.
24;;
384af0dd
OS
25;; Revision 1.1 1997/08/03 12:15:13 os10000
26;; Initial revision
3cb152f9
OS
27;;
28
29;; ---------------------------------------------------------------------------
30
7e6ed9b9
OS
31;; Description:
32;;
33;; To get this to work, make emacs execute the line "(require 'todomode)"
34;; and maybe initialise the variables below on startup.
35;;
a360be79
OS
36;; Just for the case that you are wondering about the ugly name of this
37;; package: I am one of those unfortunate people who have DOS, LINUX and
38;; OS/2 on one of their computers, so part of my home-filespace is shared
39;; and stored on a DOS partition, which is accessible to all systems. If
40;; you wish, you can of course rename the name of the file (and the last
41;; command) to something more aisthetically (please don't argue about
42;; this spelling ...) pleasing, like i.e. todo-mode.
43;;
44;; You will have the following facilities available:
7e6ed9b9
OS
45;;
46;; M-x todo-mode will enter the todo list screen, here type
da2ee685
OS
47;; + to go to next category
48;; - to go to previous category
7e6ed9b9 49;; e to edit the current entry
da2ee685
OS
50;; f to file the current entry, including a
51;; comment and timestamp
7e6ed9b9
OS
52;; i to insert a new entry
53;; k to kill the current entry
8cdc3b3d 54;; l lower the current entry's priority
da2ee685
OS
55;; n for the next entry
56;; p for the previous entry
57;; q to save the list and exit the buffer
58;; r raise current entryk's priority
59;; s to save the list
7e6ed9b9 60;;
a360be79
OS
61;; When you add a new entry, you are asked for the text and then for the
62;; category. I for example have categories for things that I want to do
63;; in the office (like mail my mum), that I want to do in town (like buy
64;; cornflakes) and things I want to do at home (move my suitcases). The
65;; categories can be selected with the cursor keys and if you type in the
66;; name of a category which didn't exist before, an empty category of the
67;; desired name will be added.
68;;
7e6ed9b9
OS
69;; I would recommend to add the following bindings to your global keymap:
70;;
da2ee685 71;; (global-set-key "\C-ct" 'todo-show)
7e6ed9b9
OS
72;; (global-set-key "\C-ci" 'todo-cmd-inst)
73;;
74;; This will enable you to quickly find the todo-list, or to simply add an
75;; entry, without changing to it and getting sidetracked from your current
76;; project.
77;;
da2ee685
OS
78;; I would also recommend that use the prefix "*/*" (by leaving the
79;; variable 'todo-prefix' untouched) so that the diary displays
80;; each entry every day.
a360be79
OS
81;;
82;; For this, please read the documentation that goes with the calendar
83;; since that will tell you how you can set up the fancy diary display
84;; and use the #include command to include your todo list file as part
85;; of your diary.
86;;
87;; Enjoy this package and express your gratitude by sending valuables
88;; to my parents' address as listed above!!!
89;;
90;; Oliver Seidel
7e6ed9b9
OS
91
92;; ---------------------------------------------------------------------------
93
3cb152f9
OS
94;; User-configurable variables:
95
da2ee685 96(defvar todo-prefix "*/*" "TODO mode prefix when creating entries")
7e6ed9b9 97(defvar todo-file-do "~/.todo-do" "TODO mode filename of list file")
3cb152f9
OS
98(defvar todo-file-done "~/.todo-done" "TODO mode filename of archive file")
99(defvar todo-mode-hook nil "Hooks invoked when the *TODO* buffer is created.")
100
101;; ---------------------------------------------------------------------------
102
103(require 'time-stamp)
104
da2ee685
OS
105(defvar todo-mode-map (make-sparse-keymap) "TODO mode keymap. See `todo-mode'")
106(define-key todo-mode-map "+" 'todo-cmd-forw)
107(define-key todo-mode-map "-" 'todo-cmd-back)
3cb152f9 108(define-key todo-mode-map "e" 'todo-cmd-edit)
da2ee685 109(define-key todo-mode-map "f" 'todo-cmd-file)
3cb152f9
OS
110(define-key todo-mode-map "i" 'todo-cmd-inst)
111(define-key todo-mode-map "k" 'todo-cmd-kill)
8cdc3b3d 112(define-key todo-mode-map "l" 'todo-cmd-lowr)
da2ee685
OS
113(define-key todo-mode-map "n" 'todo-cmd-next)
114(define-key todo-mode-map "p" 'todo-cmd-prev)
115(define-key todo-mode-map "q" 'todo-cmd-done)
116(define-key todo-mode-map "r" 'todo-cmd-rais)
117(define-key todo-mode-map "s" 'todo-cmd-save)
3cb152f9 118
da2ee685
OS
119(defun todo-cat-slct ()
120 (let ((todo-category-name (nth todo-category-number todo-cats)))
121 (setq mode-line-buffer-identification (concat "Category: " todo-category-name))
122 (widen)
123 (goto-char (point-min))
124 (search-forward (concat "--- " todo-category-name))
125 (setq begin (+ (point-at-eol) 1))
126 (search-forward "--- End")
127 (narrow-to-region begin (point-at-bol))
128 (goto-char (point-min))
129 )
130)
131
132(defun todo-cmd-forw () "Go forward to TODO list of next category."
133 (interactive)
134 (let ((todo-cat-cnt (- (length todo-cats) 1)))
135 (setq todo-category-number (if (< todo-category-number todo-cat-cnt)
136 (+ todo-category-number 1) 0))
137 (todo-cat-slct)
138 )
139 )
140
141(defun todo-cmd-back () "Go back to TODO list of previous category."
142 (interactive)
143 (let ((todo-cat-cnt (- (length todo-cats) 1)))
144 (setq todo-category-number (if (> todo-category-number 0)
145 (- todo-category-number 1) todo-cat-cnt))
146 (todo-cat-slct)
147 )
148 )
149
150(defun todo-cmd-prev () "Select previous entry of TODO list."
3cb152f9
OS
151 (interactive)
152 (forward-line -1)
153 (beginning-of-line nil)
154 (message "")
155 )
156
da2ee685 157(defun todo-cmd-next () "Select next entry of TODO list."
3cb152f9
OS
158 (interactive)
159 (forward-line 1)
160 (beginning-of-line nil)
161 (message "")
162 )
163
da2ee685
OS
164(defun todo-cmd-save () "Save the TODO list."
165 (interactive)
166 (save-buffer)
167 )
168
169(defun todo-cmd-done () "Done with TODO list for now."
3cb152f9 170 (interactive)
da2ee685
OS
171 (widen)
172 (save-buffer)
7e6ed9b9
OS
173 (beginning-of-line nil)
174 (message "")
3cb152f9
OS
175 (bury-buffer)
176 )
177
da2ee685 178(defun todo-line () "Find current line in buffer." (buffer-substring (point-at-bol) (point-at-eol)))
3cb152f9 179
da2ee685 180(defun todo-cmd-edit () "Edit current TODO list entry."
3cb152f9 181 (interactive)
da2ee685
OS
182 (let ((todo-entry (todo-line)))
183 (delete-region (point-at-bol) (point-at-eol))
184 (insert (read-from-minibuffer "Edit: " todo-entry))
185 (beginning-of-line nil)
186 (message "")
187 )
3cb152f9
OS
188 )
189
190(defvar todo-prv-lne 0 "previous line that I asked about.")
191(defvar todo-prv-ans 0 "previous answer that I got.")
192
193(defun todo-ask (lne) "Ask whether entry is more important than at LNE."
da2ee685 194 (if (not (equal todo-prv-lne lne))
3cb152f9
OS
195 (progn
196 (setq todo-prv-lne lne)
197 (goto-line todo-prv-lne)
198 (setq todo-prv-ans (y-or-n-p (concat "More important than '" (todo-line) "'? ")))
199 )
200 )
201 todo-prv-ans
202 )
203
da2ee685 204(defun todo-add-category (cat) "Add a new category to the TODO list."
3cb152f9 205 (interactive)
3cb152f9 206 (save-window-excursion
da2ee685 207 (setq todo-cats (cons cat todo-cats))
3cb152f9 208 (find-file todo-file-do)
da2ee685
OS
209 (widen)
210 (goto-char (point-min))
211 (let ((posn (search-forward "-*- mode: todo; " 17 t)))
212 (if (not (null posn)) (goto-char posn))
213 (if (equal posn nil) (progn (insert "-*- mode: todo; \n") (forward-char -1)) (kill-line))
3cb152f9 214 )
da2ee685
OS
215 (insert (format "todo-cats: %S; -*-" todo-cats))
216 (forward-char 1)
217 (insert (format "%s --- %s\n--- End\n%s %s\n" todo-prefix cat todo-prefix (make-string 75 ?-)))
3cb152f9 218 )
da2ee685
OS
219 0
220 )
221
222(defun todo-cmd-inst () "Insert new TODO list entry."
223 (interactive)
3cb152f9 224 (beginning-of-line nil)
da2ee685
OS
225 (let* ((todo-entry (concat todo-prefix " " (read-from-minibuffer "New TODO entry: ")))
226 (temp-catgs todo-cats)
a360be79 227 (todo-hstry (cons 'temp-catgs (+ todo-category-number 1))))
da2ee685
OS
228 (save-window-excursion
229 (setq todo-category
230 (read-from-minibuffer "Category: " (nth todo-category-number todo-cats) nil nil todo-hstry))
231 (let* ((ltrgt todo-category)
232 (lnmbr 0)
233 (ltext (car todo-cats))
234 (lrest (cdr todo-cats)))
235 (setq ltext (car todo-cats))
236 (while (not (or (null lrest) (string-equal ltext ltrgt)))
237 (setq ltext (car lrest))
238 (setq lrest (cdr lrest))
239 (setq lnmbr (+ 1 lnmbr))
240 )
241 (setq todo-category-number
242 (if (string-equal ltext todo-category) lnmbr (todo-add-category todo-category)))
243 )
244 (todo-show)
245 (setq todo-prv-lne 0)
246 (let* ((todo-fst 1)
247 (todo-lst (+ 1 (count-lines (point-min) (point-max)))))
248 (while (< todo-fst todo-lst)
249 (let* ((todo-cur (/ (+ todo-fst todo-lst) 2))
250 (todo-ans (if (< todo-cur todo-lst) (todo-ask todo-cur) nil)))
251 (if todo-ans
252 (setq todo-lst todo-cur)
253 (setq todo-fst (+ todo-cur 1)))
254 )
255 )
256 (goto-line todo-fst)
257 )
258 (insert (concat todo-entry "\n"))
259 (forward-line -1)
260 )
261 (beginning-of-line nil)
262 (message "")
263 )
3cb152f9
OS
264 )
265
da2ee685 266(defun todo-cmd-kill () "Delete current TODO list entry."
3cb152f9
OS
267 (interactive)
268 (if (> (count-lines (point-min) (point-max)) 0)
269 (progn
da2ee685
OS
270 (let* ((todo-entry (todo-line))
271 (todo-answer (y-or-n-p (concat "Permanently remove '" todo-entry "'? "))))
272 (if todo-answer
273 (progn
274 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
275 (forward-line -1)
276 )
277 )
278 )
279 (message "")
3cb152f9 280 )
da2ee685 281 (message "No TODO list entry to delete.")
3cb152f9
OS
282 )
283 (beginning-of-line nil)
3cb152f9
OS
284 )
285
8cdc3b3d
OS
286(defun todo-cmd-rais () "Raise priority of current entry."
287 (interactive)
288 (if (> (count-lines (point-min) (point-max)) 0)
289 (progn
290 (setq todo-entry (todo-line))
da2ee685 291 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
8cdc3b3d
OS
292 (forward-line -1)
293 (insert (concat todo-entry "\n"))
da2ee685
OS
294 (forward-line -1)
295 (message "")
8cdc3b3d 296 )
da2ee685 297 (message "No TODO list entry to raise.")
8cdc3b3d
OS
298 )
299 (beginning-of-line nil)
8cdc3b3d
OS
300 )
301
302(defun todo-cmd-lowr () "Lower priority of current entry."
303 (interactive)
304 (if (> (count-lines (point-min) (point-max)) 0)
305 (progn
306 (setq todo-entry (todo-line))
da2ee685 307 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
8cdc3b3d
OS
308 (forward-line 1)
309 (insert (concat todo-entry "\n"))
da2ee685
OS
310 (forward-line -1)
311 (message "")
8cdc3b3d 312 )
da2ee685 313 (message "No TODO list entry to raise.")
8cdc3b3d
OS
314 )
315 (beginning-of-line nil)
8cdc3b3d
OS
316 )
317
da2ee685 318(defun todo-cmd-file () "File away the current TODO list entry."
3cb152f9
OS
319 (interactive)
320 (if (> (count-lines (point-min) (point-max)) 0)
321 (progn
da2ee685
OS
322 (let ((time-stamp-format "%3b %2d, %y, %02I:%02M%p"))
323 (beginning-of-line nil)
a360be79 324 (delete-region (point-at-bol) (search-forward todo-prefix))
da2ee685
OS
325 (insert (time-stamp-string))
326 (end-of-line nil)
a360be79 327 (insert (concat " (" (read-from-minibuffer "Comment: ") ")"))
da2ee685
OS
328 (append-to-file (point-at-bol) (+ 1 (point-at-eol)) todo-file-done)
329 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
330 (forward-line -1)
331 )
332 (message "")
3cb152f9 333 )
da2ee685 334 (message "No TODO list entry to delete.")
3cb152f9
OS
335 )
336 (beginning-of-line nil)
3cb152f9
OS
337 )
338
339;; ---------------------------------------------------------------------------
340
da2ee685
OS
341;; utility functions: These are available in XEmacs, but not in Emacs 19.34
342
343(if (not (fboundp 'point-at-bol))
344 (defun point-at-bol ()
345 (save-excursion
346 (beginning-of-line)
347 (point))))
348
349(if (not (fboundp 'point-at-eol))
350 (defun point-at-eol ()
351 (save-excursion
352 (end-of-line)
353 (point))))
354
355;; ---------------------------------------------------------------------------
356
a360be79
OS
357(defvar todo-mode-popup-menu
358 (purecopy '("Todo Mode Menu"
359 ["Forward item" todo-cmd-forw t]
360 ["Backward item" todo-cmd-back t]
361 "---"
362 ["Edit item" todo-cmd-edit t]
363 ["File item" todo-cmd-file t]
364 ["Insert new item" todo-cmd-inst t]
365 ["Kill item" todo-cmd-kill t]
366 "---"
367 ["Lower item priority" todo-cmd-lowr t]
368 ["Raise item priority" todo-cmd-rais t]
369 "---"
370 ["Next item" todo-cmd-next t]
371 ["Previous item" todo-cmd-prev t]
372 "---"
373 ["Save" todo-cmd-save t]
374 "---"
375 ["Quit" todo-cmd-done t]
376 )
377 )
378 )
379
da2ee685
OS
380(defvar todo-cats nil "TODO categories.")
381(defvar todo-category-number 0 "TODO category number.")
382
383(defun todo-mode () "Major mode for editing TODO lists.\n\n\\{todo-mode-map}"
3cb152f9 384 (interactive)
3cb152f9
OS
385 (setq major-mode 'todo-mode)
386 (setq mode-name "TODO")
387 (use-local-map todo-mode-map)
a360be79 388 (setq mode-popup-menu todo-mode-popup-menu)
da2ee685
OS
389 (run-hooks 'todo-mode-hook)
390 )
391
392(defun todo-show () "Show TODO list."
393 (interactive)
394 (find-file todo-file-do)
395 (if (null todo-cats)
396 (progn
397 (todo-add-category "Todo")
398 (goto-char (point-min))
399 (goto-char (search-forward "--- End"))
400 (let ((bol (point-at-bol)))
401 (forward-line 1)
402 (let* ((eol (+ (point-at-eol) 1))
403 (mrkr (buffer-substring bol eol)))
404 (delete-region bol eol)
405 (goto-char (point-max))
406 (insert mrkr)
407 )
408 )
409 (save-buffer)
410 (kill-buffer (current-buffer))
411 (find-file todo-file-do)
412 )
413 )
7e6ed9b9 414 (beginning-of-line nil)
da2ee685
OS
415 (todo-cat-slct)
416 )
3cb152f9
OS
417
418(provide 'todomode)
419
420;; ---------------------------------------------------------------------------
421
da2ee685 422;; todomode.el ends here
3cb152f9
OS
423
424;; ---------------------------------------------------------------------------