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