diff options
| -rw-r--r-- | lisp/ChangeLog | 28 | ||||
| -rw-r--r-- | lisp/calendar/todos.el | 750 |
2 files changed, 482 insertions, 296 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 0520dc97576..fff7be6e20b 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,5 +1,33 @@ | |||
| 1 | 2012-09-23 Stephen Berman <stephen.berman@gmx.net> | 1 | 2012-09-23 Stephen Berman <stephen.berman@gmx.net> |
| 2 | 2 | ||
| 3 | * calendar/todos.el: Further significant code rearrangement; | ||
| 4 | further comment revision. | ||
| 5 | (todos-mode-display): New defgroup. | ||
| 6 | (todos-prefix, todos-number-priorities) | ||
| 7 | (todos-done-separator-string, todos-done-string) | ||
| 8 | (todos-comment-string, todos-show-with-done) | ||
| 9 | (todos-mode-line-function, todos-skip-archived-categories) | ||
| 10 | (todos-highlight-item, todos-wrap-lines) | ||
| 11 | (todos-line-wrapping-function): Use it. | ||
| 12 | (todos-item-insertion): New defgroup. | ||
| 13 | (todos-include-in-diary, todos-diary-nonmarking) | ||
| 14 | (todos-nondiary-marker, todos-always-add-time-string) | ||
| 15 | (todos-use-only-highlighted-region): Use it. | ||
| 16 | (todos-forward-button, todos-backward-button): New commands. | ||
| 17 | (todos-categories-mode-map): Use them, replacing forward-button | ||
| 18 | and backward-button. | ||
| 19 | (todos-merge-category): Fix and improve implementation; handle | ||
| 20 | archived items. | ||
| 21 | (todos-insert-item, todos-set-date-from-calendar): Handle setting | ||
| 22 | date by calling todos-insert-item-from-calendar. | ||
| 23 | (todos-delete-item): Fix overlay handling. | ||
| 24 | (todos-move-item): Highlight item to be moved. | ||
| 25 | (todos-item-undo): Handle marked items. | ||
| 26 | (todos-insert-item-from-calendar): Rewrite using | ||
| 27 | todos-date-from-calendar. | ||
| 28 | |||
| 29 | 2012-09-23 Stephen Berman <stephen.berman@gmx.net> | ||
| 30 | |||
| 3 | * calendar/todos.el: Further comment revision. | 31 | * calendar/todos.el: Further comment revision. |
| 4 | (todos-sorted-column): Change default value, also taking tty into | 32 | (todos-sorted-column): Change default value, also taking tty into |
| 5 | account. | 33 | account. |
diff --git a/lisp/calendar/todos.el b/lisp/calendar/todos.el index 33d68936e23..e5b9996d9b4 100644 --- a/lisp/calendar/todos.el +++ b/lisp/calendar/todos.el | |||
| @@ -127,12 +127,42 @@ displayed correctly." | |||
| 127 | :type 'boolean | 127 | :type 'boolean |
| 128 | :group 'todos) | 128 | :group 'todos) |
| 129 | 129 | ||
| 130 | (defcustom todos-completion-ignore-case nil | ||
| 131 | "Non-nil means case is ignored by `todos-read-*' functions." | ||
| 132 | :type 'boolean | ||
| 133 | :group 'todos) | ||
| 134 | |||
| 135 | (defcustom todos-print-function 'ps-print-buffer-with-faces | ||
| 136 | "Function called to print buffer content; see `todos-print'." | ||
| 137 | :type 'symbol | ||
| 138 | :group 'todos) | ||
| 139 | |||
| 140 | (defcustom todos-todo-mode-date-time-regexp | ||
| 141 | (concat "\\(?1:[0-9]\\{4\\}\\)-\\(?2:[0-9]\\{2\\}\\)-" | ||
| 142 | "\\(?3:[0-9]\\{2\\}\\) \\(?4:[0-9]\\{2\\}:[0-9]\\{2\\}\\)") | ||
| 143 | "Regexp matching legacy todo-mode.el item date-time strings. | ||
| 144 | In order for `todos-convert-legacy-files' to correctly convert this | ||
| 145 | string to the current Todos format, the regexp must contain four | ||
| 146 | explicitly numbered groups (see `(elisp) Regexp Backslash'), | ||
| 147 | where group 1 matches a string for the year, group 2 a string for | ||
| 148 | the month, group 3 a string for the day and group 4 a string for | ||
| 149 | the time. The default value converts date-time strings built | ||
| 150 | using the default value of `todo-time-string-format' from | ||
| 151 | todo-mode.el." | ||
| 152 | :type 'regexp | ||
| 153 | :group 'todos) | ||
| 154 | |||
| 155 | (defgroup todos-mode-display nil | ||
| 156 | "User display options for Todos mode." | ||
| 157 | :version "24.2" | ||
| 158 | :group 'todos) | ||
| 159 | |||
| 130 | (defcustom todos-prefix "" | 160 | (defcustom todos-prefix "" |
| 131 | "String prefixed to todo items for visual distinction." | 161 | "String prefixed to todo items for visual distinction." |
| 132 | :type 'string | 162 | :type 'string |
| 133 | :initialize 'custom-initialize-default | 163 | :initialize 'custom-initialize-default |
| 134 | :set 'todos-reset-prefix | 164 | :set 'todos-reset-prefix |
| 135 | :group 'todos) | 165 | :group 'todos-mode-display) |
| 136 | 166 | ||
| 137 | (defcustom todos-number-priorities t | 167 | (defcustom todos-number-priorities t |
| 138 | "Non-nil to prefix items with consecutively increasing integers. | 168 | "Non-nil to prefix items with consecutively increasing integers. |
| @@ -140,7 +170,7 @@ These reflect the priorities of the items in each category." | |||
| 140 | :type 'boolean | 170 | :type 'boolean |
| 141 | :initialize 'custom-initialize-default | 171 | :initialize 'custom-initialize-default |
| 142 | :set 'todos-reset-prefix | 172 | :set 'todos-reset-prefix |
| 143 | :group 'todos) | 173 | :group 'todos-mode-display) |
| 144 | 174 | ||
| 145 | (defun todos-reset-prefix (symbol value) | 175 | (defun todos-reset-prefix (symbol value) |
| 146 | "The :set function for `todos-prefix' and `todos-number-priorities'." | 176 | "The :set function for `todos-prefix' and `todos-number-priorities'." |
| @@ -173,7 +203,7 @@ the value of `todos-done-separator'." | |||
| 173 | :type 'string | 203 | :type 'string |
| 174 | :initialize 'custom-initialize-default | 204 | :initialize 'custom-initialize-default |
| 175 | :set 'todos-reset-done-separator-string | 205 | :set 'todos-reset-done-separator-string |
| 176 | :group 'todos) | 206 | :group 'todos-mode-display) |
| 177 | 207 | ||
| 178 | (defun todos-reset-done-separator-string (symbol value) | 208 | (defun todos-reset-done-separator-string (symbol value) |
| 179 | "The :set function for `todos-done-separator-string'." | 209 | "The :set function for `todos-done-separator-string'." |
| @@ -190,7 +220,7 @@ the value of `todos-done-separator'." | |||
| 190 | :type 'string | 220 | :type 'string |
| 191 | :initialize 'custom-initialize-default | 221 | :initialize 'custom-initialize-default |
| 192 | :set 'todos-reset-done-string | 222 | :set 'todos-reset-done-string |
| 193 | :group 'todos) | 223 | :group 'todos-mode-display) |
| 194 | 224 | ||
| 195 | (defun todos-reset-done-string (symbol value) | 225 | (defun todos-reset-done-string (symbol value) |
| 196 | "The :set function for user option `todos-done-string'." | 226 | "The :set function for user option `todos-done-string'." |
| @@ -220,7 +250,7 @@ the value of `todos-done-separator'." | |||
| 220 | :type 'string | 250 | :type 'string |
| 221 | :initialize 'custom-initialize-default | 251 | :initialize 'custom-initialize-default |
| 222 | :set 'todos-reset-comment-string | 252 | :set 'todos-reset-comment-string |
| 223 | :group 'todos) | 253 | :group 'todos-mode-display) |
| 224 | 254 | ||
| 225 | (defun todos-reset-comment-string (symbol value) | 255 | (defun todos-reset-comment-string (symbol value) |
| 226 | "The :set function for user option `todos-comment-string'." | 256 | "The :set function for user option `todos-comment-string'." |
| @@ -246,7 +276,7 @@ the value of `todos-done-separator'." | |||
| 246 | (defcustom todos-show-with-done nil | 276 | (defcustom todos-show-with-done nil |
| 247 | "Non-nil to display done items in all categories." | 277 | "Non-nil to display done items in all categories." |
| 248 | :type 'boolean | 278 | :type 'boolean |
| 249 | :group 'todos) | 279 | :group 'todos-mode-display) |
| 250 | 280 | ||
| 251 | (defun todos-mode-line-control (cat) | 281 | (defun todos-mode-line-control (cat) |
| 252 | "Return a mode line control for Todos buffers. | 282 | "Return a mode line control for Todos buffers. |
| @@ -262,7 +292,7 @@ The function expects one argument holding the name of the current | |||
| 262 | Todos category. The resulting control becomes the local value of | 292 | Todos category. The resulting control becomes the local value of |
| 263 | `mode-line-buffer-identification' in each Todos buffer." | 293 | `mode-line-buffer-identification' in each Todos buffer." |
| 264 | :type 'function | 294 | :type 'function |
| 265 | :group 'todos) | 295 | :group 'todos-mode-display) |
| 266 | 296 | ||
| 267 | (defcustom todos-skip-archived-categories nil | 297 | (defcustom todos-skip-archived-categories nil |
| 268 | "Non-nil to skip categories with only archived items when browsing. | 298 | "Non-nil to skip categories with only archived items when browsing. |
| @@ -275,24 +305,81 @@ mode (reached with \\[todos-display-categories]) these categories | |||
| 275 | shown in `todos-archived-only' face and clicking them in Todos | 305 | shown in `todos-archived-only' face and clicking them in Todos |
| 276 | Categories mode visits the archived categories." | 306 | Categories mode visits the archived categories." |
| 277 | :type 'boolean | 307 | :type 'boolean |
| 278 | :group 'todos) | 308 | :group 'todos-mode-display) |
| 279 | 309 | ||
| 280 | (defcustom todos-use-only-highlighted-region t | 310 | (defcustom todos-highlight-item nil |
| 281 | "Non-nil to enable inserting only highlighted region as new item." | 311 | "Non-nil means highlight items at point." |
| 282 | :type 'boolean | 312 | :type 'boolean |
| 313 | :initialize 'custom-initialize-default | ||
| 314 | :set 'todos-reset-highlight-item | ||
| 315 | :group 'todos-mode-display) | ||
| 316 | |||
| 317 | (defun todos-reset-highlight-item (symbol value) | ||
| 318 | "The :set function for `todos-highlight-item'." | ||
| 319 | (let ((oldvalue (symbol-value symbol)) | ||
| 320 | (files (append todos-files todos-archives))) | ||
| 321 | (custom-set-default symbol value) | ||
| 322 | (when (not (equal value oldvalue)) | ||
| 323 | (dolist (f files) | ||
| 324 | (let ((buf (find-buffer-visiting f))) | ||
| 325 | (when buf | ||
| 326 | (with-current-buffer buf | ||
| 327 | (require 'hl-line) | ||
| 328 | (if value | ||
| 329 | (hl-line-mode 1) | ||
| 330 | (hl-line-mode -1))))))))) | ||
| 331 | |||
| 332 | (defcustom todos-wrap-lines t | ||
| 333 | "Non-nil to wrap long lines via `todos-line-wrapping-function'." | ||
| 334 | :group 'todos-mode-display | ||
| 335 | :type 'boolean) | ||
| 336 | |||
| 337 | (defcustom todos-line-wrapping-function 'todos-wrap-and-indent | ||
| 338 | "Line wrapping function used with non-nil `todos-wrap-lines'." | ||
| 339 | :group 'todos-mode-display | ||
| 340 | :type 'function) | ||
| 341 | |||
| 342 | (defun todos-wrap-and-indent () | ||
| 343 | "Use word wrapping on long lines and indent with a wrap prefix. | ||
| 344 | The amount of indentation is given by user option | ||
| 345 | `todos-indent-to-here'." | ||
| 346 | (set (make-local-variable 'word-wrap) t) | ||
| 347 | (set (make-local-variable 'wrap-prefix) (make-string todos-indent-to-here 32)) | ||
| 348 | (unless (member '(continuation) fringe-indicator-alist) | ||
| 349 | (push '(continuation) fringe-indicator-alist))) | ||
| 350 | |||
| 351 | ;; FIXME: :set function to refill items with hard newlines and to immediately | ||
| 352 | ;; update wrapped prefix display | ||
| 353 | (defcustom todos-indent-to-here 6 | ||
| 354 | "Number of spaces `todos-line-wrapping-function' indents to." | ||
| 355 | :type '(integer :validate | ||
| 356 | (lambda (widget) | ||
| 357 | (unless (> (widget-value widget) 0) | ||
| 358 | (widget-put widget :error | ||
| 359 | "Invalid value: must be a positive integer") | ||
| 360 | widget))) | ||
| 361 | :group 'todos) | ||
| 362 | |||
| 363 | (defun todos-indent () | ||
| 364 | "Indent from point to `todos-indent-to-here'." | ||
| 365 | (indent-to todos-indent-to-here todos-indent-to-here)) | ||
| 366 | |||
| 367 | (defgroup todos-item-insertion nil | ||
| 368 | "User options for adding new todo items." | ||
| 369 | :version "24.2" | ||
| 283 | :group 'todos) | 370 | :group 'todos) |
| 284 | 371 | ||
| 285 | (defcustom todos-include-in-diary nil | 372 | (defcustom todos-include-in-diary nil |
| 286 | "Non-nil to allow new Todo items to be included in the diary." | 373 | "Non-nil to allow new Todo items to be included in the diary." |
| 287 | :type 'boolean | 374 | :type 'boolean |
| 288 | :group 'todos) | 375 | :group 'todos-item-insertion) |
| 289 | 376 | ||
| 290 | (defcustom todos-diary-nonmarking nil | 377 | (defcustom todos-diary-nonmarking nil |
| 291 | "Non-nil to insert new Todo diary items as nonmarking by default. | 378 | "Non-nil to insert new Todo diary items as nonmarking by default. |
| 292 | This appends `diary-nonmarking-symbol' to the front of an item on | 379 | This appends `diary-nonmarking-symbol' to the front of an item on |
| 293 | insertion provided it doesn't begin with `todos-nondiary-marker'." | 380 | insertion provided it doesn't begin with `todos-nondiary-marker'." |
| 294 | :type 'boolean | 381 | :type 'boolean |
| 295 | :group 'todos) | 382 | :group 'todos-item-insertion) |
| 296 | 383 | ||
| 297 | (defcustom todos-nondiary-marker '("[" "]") | 384 | (defcustom todos-nondiary-marker '("[" "]") |
| 298 | "List of strings surrounding item date to block diary inclusion. | 385 | "List of strings surrounding item date to block diary inclusion. |
| @@ -301,7 +388,7 @@ non-empty string that does not match a diary date in order to | |||
| 301 | have its intended effect. The second string is inserted after | 388 | have its intended effect. The second string is inserted after |
| 302 | the diary date." | 389 | the diary date." |
| 303 | :type '(list string string) | 390 | :type '(list string string) |
| 304 | :group 'todos | 391 | :group 'todos-item-insertion |
| 305 | :initialize 'custom-initialize-default | 392 | :initialize 'custom-initialize-default |
| 306 | :set 'todos-reset-nondiary-marker) | 393 | :set 'todos-reset-nondiary-marker) |
| 307 | 394 | ||
| @@ -344,89 +431,12 @@ argument, this reverses the effect of | |||
| 344 | `todos-always-add-time-string': if t, these commands omit the | 431 | `todos-always-add-time-string': if t, these commands omit the |
| 345 | current time, if nil, they include it." | 432 | current time, if nil, they include it." |
| 346 | :type 'boolean | 433 | :type 'boolean |
| 347 | :group 'todos) | 434 | :group 'todos-item-insertion) |
| 348 | |||
| 349 | (defcustom todos-completion-ignore-case nil | ||
| 350 | "Non-nil means case of user input in `todos-read-*' is ignored." | ||
| 351 | :type 'boolean | ||
| 352 | :group 'todos) | ||
| 353 | 435 | ||
| 354 | (defcustom todos-highlight-item nil | 436 | (defcustom todos-use-only-highlighted-region t |
| 355 | "Non-nil means highlight items at point." | 437 | "Non-nil to enable inserting only highlighted region as new item." |
| 356 | :type 'boolean | 438 | :type 'boolean |
| 357 | :initialize 'custom-initialize-default | 439 | :group 'todos-item-insertion) |
| 358 | :set 'todos-reset-highlight-item | ||
| 359 | :group 'todos) | ||
| 360 | |||
| 361 | (defun todos-reset-highlight-item (symbol value) | ||
| 362 | "The :set function for `todos-highlight-item'." | ||
| 363 | (let ((oldvalue (symbol-value symbol)) | ||
| 364 | (files (append todos-files todos-archives))) | ||
| 365 | (custom-set-default symbol value) | ||
| 366 | (when (not (equal value oldvalue)) | ||
| 367 | (dolist (f files) | ||
| 368 | (let ((buf (find-buffer-visiting f))) | ||
| 369 | (when buf | ||
| 370 | (with-current-buffer buf | ||
| 371 | (require 'hl-line) | ||
| 372 | (if value | ||
| 373 | (hl-line-mode 1) | ||
| 374 | (hl-line-mode -1))))))))) | ||
| 375 | |||
| 376 | (defcustom todos-wrap-lines t | ||
| 377 | "Non-nil to wrap long lines via `todos-line-wrapping-function'." | ||
| 378 | :group 'todos | ||
| 379 | :type 'boolean) | ||
| 380 | |||
| 381 | (defcustom todos-line-wrapping-function 'todos-wrap-and-indent | ||
| 382 | "Line wrapping function used with non-nil `todos-wrap-lines'." | ||
| 383 | :group 'todos | ||
| 384 | :type 'function) | ||
| 385 | |||
| 386 | (defun todos-wrap-and-indent () | ||
| 387 | "Use word wrapping on long lines and indent with a wrap prefix. | ||
| 388 | The amount of indentation is given by user option | ||
| 389 | `todos-indent-to-here'." | ||
| 390 | (set (make-local-variable 'word-wrap) t) | ||
| 391 | (set (make-local-variable 'wrap-prefix) (make-string todos-indent-to-here 32)) | ||
| 392 | (unless (member '(continuation) fringe-indicator-alist) | ||
| 393 | (push '(continuation) fringe-indicator-alist))) | ||
| 394 | |||
| 395 | ;; FIXME: :set function to refill items with hard newlines and to immediately | ||
| 396 | ;; update wrapped prefix display | ||
| 397 | (defcustom todos-indent-to-here 6 | ||
| 398 | "Number of spaces `todos-line-wrapping-function' indents to." | ||
| 399 | :type '(integer :validate | ||
| 400 | (lambda (widget) | ||
| 401 | (unless (> (widget-value widget) 0) | ||
| 402 | (widget-put widget :error | ||
| 403 | "Invalid value: must be a positive integer") | ||
| 404 | widget))) | ||
| 405 | :group 'todos) | ||
| 406 | |||
| 407 | (defun todos-indent () | ||
| 408 | "Indent from point to `todos-indent-to-here'." | ||
| 409 | (indent-to todos-indent-to-here todos-indent-to-here)) | ||
| 410 | |||
| 411 | (defcustom todos-todo-mode-date-time-regexp | ||
| 412 | (concat "\\(?1:[0-9]\\{4\\}\\)-\\(?2:[0-9]\\{2\\}\\)-" | ||
| 413 | "\\(?3:[0-9]\\{2\\}\\) \\(?4:[0-9]\\{2\\}:[0-9]\\{2\\}\\)") | ||
| 414 | "Regexp matching legacy todo-mode.el item date-time strings. | ||
| 415 | In order for `todos-convert-legacy-files' to correctly convert this | ||
| 416 | string to the current Todos format, the regexp must contain four | ||
| 417 | explicitly numbered groups (see `(elisp) Regexp Backslash'), | ||
| 418 | where group 1 matches a string for the year, group 2 a string for | ||
| 419 | the month, group 3 a string for the day and group 4 a string for | ||
| 420 | the time. The default value converts date-time strings built | ||
| 421 | using the default value of `todo-time-string-format' from | ||
| 422 | todo-mode.el." | ||
| 423 | :type 'regexp | ||
| 424 | :group 'todos) | ||
| 425 | |||
| 426 | (defcustom todos-print-function 'ps-print-buffer-with-faces | ||
| 427 | "Function called to print buffer content; see `todos-print'." | ||
| 428 | :type 'symbol | ||
| 429 | :group 'todos) | ||
| 430 | 440 | ||
| 431 | (defgroup todos-filtered nil | 441 | (defgroup todos-filtered nil |
| 432 | "User options for Todos Filter Items mode." | 442 | "User options for Todos Filter Items mode." |
| @@ -930,6 +940,26 @@ See `todos-display-categories-first'.") | |||
| 930 | Set by the command `todos-show-done-only' and used by | 940 | Set by the command `todos-show-done-only' and used by |
| 931 | `todos-category-select'.") | 941 | `todos-category-select'.") |
| 932 | 942 | ||
| 943 | (defun todos-reset-and-enable-done-separator () | ||
| 944 | "Show resized catagory separator overlay after window size change. | ||
| 945 | Added to `window-configuration-change-hook' in `todos-mode'." | ||
| 946 | (when (= 1 (length todos-done-separator-string)) | ||
| 947 | (let ((sep todos-done-separator)) | ||
| 948 | (setq todos-done-separator (todos-done-separator)) | ||
| 949 | (save-match-data (todos-reset-done-separator sep))) | ||
| 950 | ;; FIXME: If this is called while the separator overlay is shown, the | ||
| 951 | ;; separator with deleted overlay becomes visible when waiting for user | ||
| 952 | ;; input and remains so. The following workaround prevents this, but it | ||
| 953 | ;; also prevents widening when edebugging todos.el. | ||
| 954 | ;; (save-excursion | ||
| 955 | ;; (goto-char (point-min)) | ||
| 956 | ;; (when (re-search-forward todos-done-string-start nil t) | ||
| 957 | ;; (let ((todos-show-with-done nil)) | ||
| 958 | ;; (todos-category-select)) | ||
| 959 | ;; (let ((todos-show-with-done t)) | ||
| 960 | ;; (todos-category-select)))) | ||
| 961 | )) | ||
| 962 | |||
| 933 | ;; --------------------------------------------------------------------------- | 963 | ;; --------------------------------------------------------------------------- |
| 934 | ;;; Global variables and helper functions | 964 | ;;; Global variables and helper functions |
| 935 | 965 | ||
| @@ -1054,26 +1084,6 @@ done items are shown. Its value is determined by user option | |||
| 1054 | (overlay-put new-sep 'display | 1084 | (overlay-put new-sep 'display |
| 1055 | todos-done-separator))))))) | 1085 | todos-done-separator))))))) |
| 1056 | 1086 | ||
| 1057 | (defun todos-reset-and-enable-done-separator () | ||
| 1058 | "Hook function for activating new separator overlay. | ||
| 1059 | Added to `window-configuration-change-hook' in `todos-mode'." | ||
| 1060 | (when (= 1 (length todos-done-separator-string)) | ||
| 1061 | (let ((sep todos-done-separator)) | ||
| 1062 | (setq todos-done-separator (todos-done-separator)) | ||
| 1063 | (save-match-data (todos-reset-done-separator sep))) | ||
| 1064 | ;; If the separator overlay is now shown, we have to hide and then show it | ||
| 1065 | ;; again in order to let the change in length take effect. | ||
| 1066 | ;; FIXME: But this breaks e.g. (widen) when edebugging. But how to | ||
| 1067 | ;; restrict it? | ||
| 1068 | ;; (save-excursion | ||
| 1069 | ;; (goto-char (point-min)) | ||
| 1070 | ;; (when (re-search-forward todos-done-string-start nil t) | ||
| 1071 | ;; (let ((todos-show-with-done nil)) | ||
| 1072 | ;; (todos-category-select)) | ||
| 1073 | ;; (let ((todos-show-with-done t)) | ||
| 1074 | ;; (todos-category-select)))) | ||
| 1075 | )) | ||
| 1076 | |||
| 1077 | (defun todos-category-select () | 1087 | (defun todos-category-select () |
| 1078 | "Display the current category correctly." | 1088 | "Display the current category correctly." |
| 1079 | (let ((name (todos-current-category)) | 1089 | (let ((name (todos-current-category)) |
| @@ -2506,10 +2516,10 @@ which is the value of the user option | |||
| 2506 | (define-key map "+" 'todos-lower-category-priority) | 2516 | (define-key map "+" 'todos-lower-category-priority) |
| 2507 | (define-key map "r" 'todos-raise-category-priority) | 2517 | (define-key map "r" 'todos-raise-category-priority) |
| 2508 | (define-key map "-" 'todos-raise-category-priority) | 2518 | (define-key map "-" 'todos-raise-category-priority) |
| 2509 | (define-key map "n" 'forward-button) | 2519 | (define-key map "n" 'todos-forward-button) |
| 2510 | (define-key map "p" 'backward-button) | 2520 | (define-key map "p" 'todos-backward-button) |
| 2511 | (define-key map [tab] 'forward-button) | 2521 | (define-key map [tab] 'todos-forward-button) |
| 2512 | (define-key map [backtab] 'backward-button) | 2522 | (define-key map [backtab] 'todos-backward-button) |
| 2513 | (define-key map "q" 'todos-quit) | 2523 | (define-key map "q" 'todos-quit) |
| 2514 | ;; (define-key map "A" 'todos-add-category) | 2524 | ;; (define-key map "A" 'todos-add-category) |
| 2515 | ;; (define-key map "D" 'todos-delete-category) | 2525 | ;; (define-key map "D" 'todos-delete-category) |
| @@ -2585,7 +2595,7 @@ which is the value of the user option | |||
| 2585 | (when todos-show-current-file | 2595 | (when todos-show-current-file |
| 2586 | (add-hook 'pre-command-hook 'todos-show-current-file nil t)) | 2596 | (add-hook 'pre-command-hook 'todos-show-current-file nil t)) |
| 2587 | (add-hook 'window-configuration-change-hook | 2597 | (add-hook 'window-configuration-change-hook |
| 2588 | 'todos-reset-and-enable-done-separator nil t) | 2598 | 'todos-reset-and-enable-done-separator nil t) |
| 2589 | (add-hook 'kill-buffer-hook 'todos-reset-global-current-todos-file nil t)) | 2599 | (add-hook 'kill-buffer-hook 'todos-reset-global-current-todos-file nil t)) |
| 2590 | 2600 | ||
| 2591 | (defun todos-unload-hook () | 2601 | (defun todos-unload-hook () |
| @@ -3239,6 +3249,22 @@ upward." | |||
| 3239 | nil t) | 3249 | nil t) |
| 3240 | (forward-line -1)))))) | 3250 | (forward-line -1)))))) |
| 3241 | 3251 | ||
| 3252 | (defun todos-forward-button (n &optional wrap display-message) | ||
| 3253 | "" | ||
| 3254 | (interactive "p\nd\nd") | ||
| 3255 | (forward-button n wrap display-message) | ||
| 3256 | (and (bolp) (button-at (point)) | ||
| 3257 | ;; Align with beginning of category label. | ||
| 3258 | (forward-char (+ 4 (length todos-categories-number-separator))))) | ||
| 3259 | |||
| 3260 | (defun todos-backward-button (n &optional wrap display-message) | ||
| 3261 | "" | ||
| 3262 | (interactive "p\nd\nd") | ||
| 3263 | (backward-button n wrap display-message) | ||
| 3264 | (and (bolp) (button-at (point)) | ||
| 3265 | ;; Align with beginning of category label. | ||
| 3266 | (forward-char (+ 4 (length todos-categories-number-separator))))) | ||
| 3267 | |||
| 3242 | ;; FIXME: (i) Extend search to other Todos files. (ii) Allow navigating among | 3268 | ;; FIXME: (i) Extend search to other Todos files. (ii) Allow navigating among |
| 3243 | ;; hits. (But these features are effectively available with | 3269 | ;; hits. (But these features are effectively available with |
| 3244 | ;; todos-regexp-items-multifile, so maybe it's not worth the trouble here.) | 3270 | ;; todos-regexp-items-multifile, so maybe it's not worth the trouble here.) |
| @@ -3766,59 +3792,133 @@ archive of the file moved to, creating it if it does not exist." | |||
| 3766 | 3792 | ||
| 3767 | (defun todos-merge-category () | 3793 | (defun todos-merge-category () |
| 3768 | "Merge current category into another category in this file. | 3794 | "Merge current category into another category in this file. |
| 3795 | |||
| 3769 | The current category's todo and done items are appended to the | 3796 | The current category's todo and done items are appended to the |
| 3770 | chosen category's todo and done items, respectively, which | 3797 | chosen goal category's todo and done items, respectively. The |
| 3771 | becomes the current category, and the category moved from is | 3798 | goal category becomes the current category, and the previous |
| 3772 | deleted." | 3799 | current category is deleted. |
| 3800 | |||
| 3801 | If both the first and goal categories also have archived items, | ||
| 3802 | the former are merged to the latter. If only the first category | ||
| 3803 | has archived items, the archived category is renamed to the goal | ||
| 3804 | category." | ||
| 3773 | (interactive) | 3805 | (interactive) |
| 3774 | (let ((buffer-read-only nil) | 3806 | (let* ((tfile todos-current-todos-file) |
| 3775 | (cat (todos-current-category)) | 3807 | (archive (concat (file-name-sans-extension tfile) ".toda")) |
| 3776 | (goal (todos-read-category "Category to merge to: " t))) | 3808 | (cat (todos-current-category)) |
| 3777 | (widen) | 3809 | (goal (todos-read-category "Category to merge to: " t)) |
| 3778 | ;; FIXME: check if cat has archived items and merge those too | 3810 | archived-count here) |
| 3779 | (let* ((cbeg (progn | 3811 | ;; Merge in todo file. |
| 3780 | (re-search-backward | 3812 | (with-current-buffer (get-buffer (find-file-noselect tfile)) |
| 3781 | (concat "^" (regexp-quote todos-category-beg)) nil t) | 3813 | (widen) |
| 3782 | (point))) | 3814 | (let* ((buffer-read-only nil) |
| 3783 | (tbeg (progn (forward-line) (point))) | 3815 | (cbeg (progn |
| 3784 | (dbeg (progn | 3816 | (re-search-backward |
| 3785 | (re-search-forward | ||
| 3786 | (concat "^" (regexp-quote todos-category-done)) nil t) | ||
| 3787 | (forward-line) (point))) | ||
| 3788 | (tend (progn (forward-line -2) (point))) | ||
| 3789 | (cend (progn | ||
| 3790 | (if (re-search-forward | ||
| 3791 | (concat "^" (regexp-quote todos-category-beg)) nil t) | ||
| 3792 | (match-beginning 0) | ||
| 3793 | (point-max)))) | ||
| 3794 | (todo (buffer-substring-no-properties tbeg tend)) | ||
| 3795 | (done (buffer-substring-no-properties dbeg cend)) | ||
| 3796 | here) | ||
| 3797 | (goto-char (point-min)) | ||
| 3798 | (re-search-forward | ||
| 3799 | (concat "^" (regexp-quote (concat todos-category-beg goal))) nil t) | ||
| 3800 | (re-search-forward | ||
| 3801 | (concat "^" (regexp-quote todos-category-done)) nil t) | ||
| 3802 | (forward-line -1) | ||
| 3803 | (setq here (point)) | ||
| 3804 | (insert todo) | ||
| 3805 | (goto-char (if (re-search-forward | ||
| 3806 | (concat "^" (regexp-quote todos-category-beg)) nil t) | 3817 | (concat "^" (regexp-quote todos-category-beg)) nil t) |
| 3807 | (match-beginning 0) | 3818 | (point-marker))) |
| 3808 | (point-max))) | 3819 | (tbeg (progn (forward-line) (point-marker))) |
| 3809 | (insert done) | 3820 | (dbeg (progn |
| 3810 | (remove-overlays cbeg cend) | 3821 | (re-search-forward |
| 3811 | (delete-region cbeg cend) | 3822 | (concat "^" (regexp-quote todos-category-done)) nil t) |
| 3812 | (todos-update-count 'todo (todos-get-count 'todo cat) goal) | 3823 | (forward-line) (point-marker))) |
| 3813 | (todos-update-count 'done (todos-get-count 'done cat) goal) | 3824 | ;; Omit empty line between todo and done items. |
| 3814 | (setq todos-categories (delete (assoc cat todos-categories) | 3825 | (tend (progn (forward-line -2) (point-marker))) |
| 3815 | todos-categories)) | 3826 | (cend (progn |
| 3816 | (todos-update-categories-sexp) | 3827 | (if (re-search-forward |
| 3828 | (concat "^" (regexp-quote todos-category-beg)) nil t) | ||
| 3829 | (progn | ||
| 3830 | (goto-char (match-beginning 0)) | ||
| 3831 | (point-marker)) | ||
| 3832 | (point-max-marker)))) | ||
| 3833 | (todo (buffer-substring-no-properties tbeg tend)) | ||
| 3834 | (done (buffer-substring-no-properties dbeg cend))) | ||
| 3835 | (goto-char (point-min)) | ||
| 3836 | ;; Merge any todo items. | ||
| 3837 | (unless (zerop (length todo)) | ||
| 3838 | (re-search-forward | ||
| 3839 | (concat "^" (regexp-quote (concat todos-category-beg goal))) nil t) | ||
| 3840 | (re-search-forward | ||
| 3841 | (concat "^" (regexp-quote todos-category-done)) nil t) | ||
| 3842 | (forward-line -1) | ||
| 3843 | (setq here (point-marker)) | ||
| 3844 | (insert todo) | ||
| 3845 | (todos-update-count 'todo (todos-get-count 'todo cat) goal)) | ||
| 3846 | ;; Merge any done items. | ||
| 3847 | (unless (zerop (length done)) | ||
| 3848 | (goto-char (if (re-search-forward | ||
| 3849 | (concat "^" (regexp-quote todos-category-beg)) nil t) | ||
| 3850 | (match-beginning 0) | ||
| 3851 | (point-max))) | ||
| 3852 | (when (zerop (length todo)) (setq here (point-marker))) | ||
| 3853 | (insert done) | ||
| 3854 | (todos-update-count 'done (todos-get-count 'done cat) goal)) | ||
| 3855 | (remove-overlays cbeg cend) | ||
| 3856 | (delete-region cbeg cend) | ||
| 3857 | (setq todos-categories (delete (assoc cat todos-categories) | ||
| 3858 | todos-categories)) | ||
| 3859 | (todos-update-categories-sexp) | ||
| 3860 | (mapc (lambda (m) (set-marker m nil)) (list cbeg tbeg dbeg tend cend)))) | ||
| 3861 | (when (file-exists-p archive) | ||
| 3862 | ;; Merge in archive file. | ||
| 3863 | (with-current-buffer (get-buffer (find-file-noselect archive)) | ||
| 3864 | (widen) | ||
| 3865 | (goto-char (point-min)) | ||
| 3866 | (let ((buffer-read-only nil) | ||
| 3867 | (cbeg (save-excursion | ||
| 3868 | (when (re-search-forward | ||
| 3869 | (concat "^" (regexp-quote | ||
| 3870 | (concat todos-category-beg cat))) | ||
| 3871 | nil t) | ||
| 3872 | (goto-char (match-beginning 0)) | ||
| 3873 | (point-marker)))) | ||
| 3874 | (gbeg (save-excursion | ||
| 3875 | (when (re-search-forward | ||
| 3876 | (concat "^" (regexp-quote | ||
| 3877 | (concat todos-category-beg goal))) | ||
| 3878 | nil t) | ||
| 3879 | (goto-char (match-beginning 0)) | ||
| 3880 | (point-marker)))) | ||
| 3881 | cend carch) | ||
| 3882 | (when cbeg | ||
| 3883 | (setq archived-count (todos-get-count 'done cat)) | ||
| 3884 | (setq cend (save-excursion | ||
| 3885 | (if (re-search-forward | ||
| 3886 | (concat "^" (regexp-quote todos-category-beg)) | ||
| 3887 | nil t) | ||
| 3888 | (match-beginning 0) | ||
| 3889 | (point-max)))) | ||
| 3890 | (setq carch (save-excursion (goto-char cbeg) (forward-line) | ||
| 3891 | (buffer-substring-no-properties (point) cend))) | ||
| 3892 | ;; If both categories of the merge have archived items, merge the | ||
| 3893 | ;; source items to the goal items, else "merge" by renaming the | ||
| 3894 | ;; source category to goal. | ||
| 3895 | (if gbeg | ||
| 3896 | (progn | ||
| 3897 | (goto-char (if (re-search-forward | ||
| 3898 | (concat "^" (regexp-quote todos-category-beg)) | ||
| 3899 | nil t) | ||
| 3900 | (match-beginning 0) | ||
| 3901 | (point-max))) | ||
| 3902 | (insert carch) | ||
| 3903 | (remove-overlays cbeg cend) | ||
| 3904 | (delete-region cbeg cend)) | ||
| 3905 | (goto-char cbeg) | ||
| 3906 | (search-forward cat) | ||
| 3907 | (replace-match goal)) | ||
| 3908 | (setq todos-categories (todos-make-categories-list t)) | ||
| 3909 | (todos-update-categories-sexp))))) | ||
| 3910 | (with-current-buffer (get-file-buffer tfile) | ||
| 3911 | (when archived-count | ||
| 3912 | (unless (zerop archived-count) | ||
| 3913 | (todos-update-count 'archived archived-count goal) | ||
| 3914 | (todos-update-categories-sexp))) | ||
| 3817 | (todos-category-number goal) | 3915 | (todos-category-number goal) |
| 3818 | (todos-category-select) | 3916 | ;; If there are only merged done items, show them. |
| 3819 | ;; Put point at the start of the merged todo items. | 3917 | (let ((todos-show-with-done (zerop (todos-get-count 'todo goal)))) |
| 3820 | ;; FIXME: what if there are no merged todo items but only done items? | 3918 | (todos-category-select) |
| 3821 | (goto-char here)))) | 3919 | ;; Put point on the first merged item. |
| 3920 | (goto-char here))) | ||
| 3921 | (set-marker here nil))) | ||
| 3822 | 3922 | ||
| 3823 | (defun todos-set-category-priority (&optional arg) | 3923 | (defun todos-set-category-priority (&optional arg) |
| 3824 | "Change priority of category at point in Todos Categories buffer. | 3924 | "Change priority of category at point in Todos Categories buffer. |
| @@ -3922,6 +4022,11 @@ mandatory date header string and how it is added: | |||
| 3922 | when the user puts the cursor on a date and hits RET, that | 4022 | when the user puts the cursor on a date and hits RET, that |
| 3923 | date, in the format set by `calendar-date-display-form', | 4023 | date, in the format set by `calendar-date-display-form', |
| 3924 | becomes the date in the header. | 4024 | becomes the date in the header. |
| 4025 | - If DATE-TYPE is a string matching the regexp | ||
| 4026 | `todos-date-pattern', that string becomes the date in the | ||
| 4027 | header. This case is for the command | ||
| 4028 | `todos-insert-item-from-calendar' which is called from the | ||
| 4029 | Calendar. | ||
| 3925 | - If DATE-TYPE is the symbol `date', the header contains the date | 4030 | - If DATE-TYPE is the symbol `date', the header contains the date |
| 3926 | in the format set by `calendar-date-display-form', with year, | 4031 | in the format set by `calendar-date-display-form', with year, |
| 3927 | month and day individually prompted for (month with tab | 4032 | month and day individually prompted for (month with tab |
| @@ -3999,6 +4104,9 @@ the priority is not given by HERE but by prompting." | |||
| 3999 | ((eq date-type 'calendar) | 4104 | ((eq date-type 'calendar) |
| 4000 | (setq todos-date-from-calendar t) | 4105 | (setq todos-date-from-calendar t) |
| 4001 | (todos-set-date-from-calendar)) | 4106 | (todos-set-date-from-calendar)) |
| 4107 | ((string-match todos-date-pattern date-type) | ||
| 4108 | (setq todos-date-from-calendar date-type) | ||
| 4109 | (todos-set-date-from-calendar)) | ||
| 4002 | (t (calendar-date-string (calendar-current-date) t t)))) | 4110 | (t (calendar-date-string (calendar-current-date) t t)))) |
| 4003 | (time-string (or (and time (todos-read-time)) | 4111 | (time-string (or (and time (todos-read-time)) |
| 4004 | (and todos-always-add-time-string | 4112 | (and todos-always-add-time-string |
| @@ -4055,19 +4163,21 @@ the priority is not given by HERE but by prompting." | |||
| 4055 | 4163 | ||
| 4056 | (defun todos-set-date-from-calendar () | 4164 | (defun todos-set-date-from-calendar () |
| 4057 | "Return string of date chosen from Calendar." | 4165 | "Return string of date chosen from Calendar." |
| 4058 | (when todos-date-from-calendar | 4166 | (cond ((string-match todos-date-pattern todos-date-from-calendar) |
| 4059 | (let (calendar-view-diary-initially-flag) | 4167 | todos-date-from-calendar) |
| 4060 | (calendar)) | 4168 | ((todos-date-from-calendar t) |
| 4061 | ;; *Calendar* is now current buffer. | 4169 | (let (calendar-view-diary-initially-flag) |
| 4062 | (local-set-key (kbd "RET") 'exit-recursive-edit) | 4170 | (calendar)) |
| 4063 | (message "Put cursor on a date and type <return> to set it.") | 4171 | ;; *Calendar* is now current buffer. |
| 4064 | ;; FIXME: is there a better way than recursive-edit? Use unwind-protect? | 4172 | (local-set-key (kbd "RET") 'exit-recursive-edit) |
| 4065 | ;; Check recursive-depth? | 4173 | (message "Put cursor on a date and type <return> to set it.") |
| 4066 | (recursive-edit) | 4174 | ;; FIXME: is there a better way than recursive-edit? Use unwind-protect? |
| 4067 | (setq todos-date-from-calendar | 4175 | ;; Check recursive-depth? |
| 4068 | (calendar-date-string (calendar-cursor-to-date t) t t)) | 4176 | (recursive-edit) |
| 4069 | (calendar-exit) | 4177 | (setq todos-date-from-calendar |
| 4070 | todos-date-from-calendar)) | 4178 | (calendar-date-string (calendar-cursor-to-date t) t t)) |
| 4179 | (calendar-exit) | ||
| 4180 | todos-date-from-calendar))) | ||
| 4071 | 4181 | ||
| 4072 | (defun todos-delete-item () | 4182 | (defun todos-delete-item () |
| 4073 | "Delete at least one item in this category. | 4183 | "Delete at least one item in this category. |
| @@ -4075,45 +4185,49 @@ the priority is not given by HERE but by prompting." | |||
| 4075 | If there are marked items, delete all of these; otherwise, delete | 4185 | If there are marked items, delete all of these; otherwise, delete |
| 4076 | the item at point." | 4186 | the item at point." |
| 4077 | (interactive) | 4187 | (interactive) |
| 4078 | (let* ((cat (todos-current-category)) | 4188 | (let (ov) |
| 4079 | (marked (assoc cat todos-categories-with-marks)) | 4189 | (unwind-protect |
| 4080 | (item (unless marked (todos-item-string))) | 4190 | (let* ((cat (todos-current-category)) |
| 4081 | (ov (make-overlay (save-excursion (todos-item-start)) | 4191 | (marked (assoc cat todos-categories-with-marks)) |
| 4082 | (save-excursion (todos-item-end)))) | 4192 | (item (unless marked (todos-item-string))) |
| 4083 | ;; FIXME: make confirmation an option? | 4193 | ;; FIXME: make confirmation an option? |
| 4084 | (answer (if marked | 4194 | (answer (if marked |
| 4085 | (y-or-n-p "Permanently delete all marked items? ") | 4195 | (y-or-n-p "Permanently delete all marked items? ") |
| 4086 | (when item | 4196 | (when item |
| 4087 | (overlay-put ov 'face 'todos-search) | 4197 | (setq ov (make-overlay |
| 4088 | (y-or-n-p (concat "Permanently delete this item? "))))) | 4198 | (save-excursion (todos-item-start)) |
| 4089 | (opoint (point)) | 4199 | (save-excursion (todos-item-end)))) |
| 4090 | buffer-read-only) | 4200 | (overlay-put ov 'face 'todos-search) |
| 4091 | (when answer | 4201 | (y-or-n-p (concat "Permanently delete this item? "))))) |
| 4092 | (and marked (goto-char (point-min))) | 4202 | (opoint (point)) |
| 4093 | (catch 'done | 4203 | buffer-read-only) |
| 4094 | (while (not (eobp)) | 4204 | (when answer |
| 4095 | (if (or (and marked (todos-marked-item-p)) item) | 4205 | (and marked (goto-char (point-min))) |
| 4096 | (progn | 4206 | (catch 'done |
| 4097 | (if (todos-done-item-p) | 4207 | (while (not (eobp)) |
| 4098 | (todos-update-count 'done -1) | 4208 | (if (or (and marked (todos-marked-item-p)) item) |
| 4099 | (todos-update-count 'todo -1 cat) | 4209 | (progn |
| 4100 | (and (todos-diary-item-p) (todos-update-count 'diary -1))) | 4210 | (if (todos-done-item-p) |
| 4101 | (delete-overlay ov) | 4211 | (todos-update-count 'done -1) |
| 4102 | (todos-remove-item) | 4212 | (todos-update-count 'todo -1 cat) |
| 4103 | ;; Don't leave point below last item. | 4213 | (and (todos-diary-item-p) (todos-update-count 'diary -1))) |
| 4104 | (and item (bolp) (eolp) (< (point-min) (point-max)) | 4214 | (if ov (delete-overlay ov)) |
| 4105 | (todos-backward-item)) | 4215 | (todos-remove-item) |
| 4106 | (when item | 4216 | ;; Don't leave point below last item. |
| 4107 | (throw 'done (setq item nil)))) | 4217 | (and item (bolp) (eolp) (< (point-min) (point-max)) |
| 4108 | (todos-forward-item)))) | 4218 | (todos-backward-item)) |
| 4109 | (when marked | 4219 | (when item |
| 4110 | (remove-overlays (point-min) (point-max) 'before-string todos-item-mark) | 4220 | (throw 'done (setq item nil)))) |
| 4111 | (setq todos-categories-with-marks | 4221 | (todos-forward-item)))) |
| 4112 | (assq-delete-all cat todos-categories-with-marks)) | 4222 | (when marked |
| 4113 | (goto-char opoint)) | 4223 | (remove-overlays (point-min) (point-max) |
| 4114 | (todos-update-categories-sexp) | 4224 | 'before-string todos-item-mark) |
| 4115 | (todos-prefix-overlays)) | 4225 | (setq todos-categories-with-marks |
| 4116 | (if ov (delete-overlay ov)))) | 4226 | (assq-delete-all cat todos-categories-with-marks)) |
| 4227 | (goto-char opoint)) | ||
| 4228 | (todos-update-categories-sexp) | ||
| 4229 | (todos-prefix-overlays))) | ||
| 4230 | (if ov (delete-overlay ov))))) | ||
| 4117 | 4231 | ||
| 4118 | (defun todos-edit-item () | 4232 | (defun todos-edit-item () |
| 4119 | "Edit the Todo item at point. | 4233 | "Edit the Todo item at point. |
| @@ -4539,35 +4653,42 @@ entry/entries in that category." | |||
| 4539 | file1)) | 4653 | file1)) |
| 4540 | (count 0) | 4654 | (count 0) |
| 4541 | (count-diary 0) | 4655 | (count-diary 0) |
| 4542 | cat2 nmark) | 4656 | ov cat2 nmark) |
| 4543 | (set-buffer (find-file-noselect file2)) | 4657 | (set-buffer (find-file-noselect file2)) |
| 4544 | (setq cat2 (let* ((pl (if (and marked (> (cdr marked) 1)) "s" "")) | 4658 | (unwind-protect |
| 4545 | (name (todos-read-category | 4659 | (progn |
| 4546 | (concat "Move item" pl " to category: "))) | 4660 | (unless marked |
| 4547 | (prompt (concat "Choose a different category than " | 4661 | (setq ov (make-overlay (save-excursion (todos-item-start)) |
| 4548 | "the current one\n(type `" | 4662 | (save-excursion (todos-item-end)))) |
| 4549 | (key-description | 4663 | (overlay-put ov 'face 'todos-search)) |
| 4550 | (car (where-is-internal | 4664 | (setq cat2 (let* ((pl (if (and marked (> (cdr marked) 1)) "s" "")) |
| 4551 | 'todos-set-item-priority))) | 4665 | (name (todos-read-category |
| 4552 | "' to reprioritize item " | 4666 | (concat "Move item" pl " to category: "))) |
| 4553 | "within the same category): "))) | 4667 | (prompt (concat "Choose a different category than " |
| 4554 | (while (equal name cat1) | 4668 | "the current one\n(type `" |
| 4555 | (setq name (todos-read-category prompt))) | 4669 | (key-description |
| 4556 | name)) | 4670 | (car (where-is-internal |
| 4671 | 'todos-set-item-priority))) | ||
| 4672 | "' to reprioritize item " | ||
| 4673 | "within the same category): "))) | ||
| 4674 | (while (equal name cat1) | ||
| 4675 | (setq name (todos-read-category prompt))) | ||
| 4676 | name))) | ||
| 4677 | (if ov (delete-overlay ov))) | ||
| 4557 | (set-buffer (find-buffer-visiting file1)) | 4678 | (set-buffer (find-buffer-visiting file1)) |
| 4558 | (if marked | 4679 | (if marked |
| 4559 | (progn | 4680 | (progn |
| 4560 | (setq item nil) | 4681 | (setq item nil) |
| 4561 | (goto-char (point-min)) | 4682 | (goto-char (point-min)) |
| 4562 | (while (not (eobp)) | 4683 | (while (not (eobp)) |
| 4563 | (when (todos-marked-item-p) | 4684 | (when (todos-marked-item-p) |
| 4564 | (setq item (concat item (todos-item-string) "\n")) | 4685 | (setq item (concat item (todos-item-string) "\n")) |
| 4565 | (setq count (1+ count)) | 4686 | (setq count (1+ count)) |
| 4566 | (when (todos-diary-item-p) | 4687 | (when (todos-diary-item-p) |
| 4567 | (setq count-diary (1+ count-diary)))) | 4688 | (setq count-diary (1+ count-diary)))) |
| 4568 | (todos-forward-item)) | 4689 | (todos-forward-item)) |
| 4569 | ;; Chop off last newline. | 4690 | ;; Chop off last newline. |
| 4570 | (setq item (substring item 0 -1))) | 4691 | (setq item (substring item 0 -1))) |
| 4571 | (setq count 1) | 4692 | (setq count 1) |
| 4572 | (when (todos-diary-item-p) (setq count-diary 1))) | 4693 | (when (todos-diary-item-p) (setq count-diary 1))) |
| 4573 | (set-window-buffer (selected-window) | 4694 | (set-window-buffer (selected-window) |
| @@ -4598,6 +4719,7 @@ entry/entries in that category." | |||
| 4598 | (if (todos-marked-item-p) | 4719 | (if (todos-marked-item-p) |
| 4599 | (todos-remove-item) | 4720 | (todos-remove-item) |
| 4600 | (todos-forward-item)))) | 4721 | (todos-forward-item)))) |
| 4722 | (if ov (delete-overlay ov)) | ||
| 4601 | (todos-remove-item)))) | 4723 | (todos-remove-item)))) |
| 4602 | (todos-update-count 'todo (- count) cat1) | 4724 | (todos-update-count 'todo (- count) cat1) |
| 4603 | (todos-update-count 'diary (- count-diary) cat1) | 4725 | (todos-update-count 'diary (- count-diary) cat1) |
| @@ -4712,47 +4834,90 @@ With prefix ARG delete an existing comment." | |||
| 4712 | (insert " [" todos-comment-string ": " comment "]")))))) | 4834 | (insert " [" todos-comment-string ": " comment "]")))))) |
| 4713 | 4835 | ||
| 4714 | ;; FIXME: also with marked items | 4836 | ;; FIXME: also with marked items |
| 4715 | ;; FIXME: delete comment from restored item or just leave it up to user? | ||
| 4716 | (defun todos-item-undo () | 4837 | (defun todos-item-undo () |
| 4717 | "Restore this done item to the todo section of this category. | 4838 | "Restore this done item to the todo section of this category. |
| 4718 | If done item has a comment, ask whether to omit the comment from | 4839 | If done item has a comment, ask whether to omit the comment from |
| 4719 | the restored item." | 4840 | the restored item." |
| 4720 | (interactive) | 4841 | (interactive) |
| 4721 | (when (todos-done-item-p) | 4842 | (let* ((cat (todos-current-category)) |
| 4722 | (let* ((buffer-read-only) | 4843 | (marked (assoc cat todos-categories-with-marks))) |
| 4723 | (done-item (todos-item-string)) | 4844 | (when (or marked (todos-done-item-p)) |
| 4724 | (opoint (point)) | 4845 | (let ((buffer-read-only) |
| 4725 | (orig-mrk (progn (todos-item-start) (point-marker))) | 4846 | (done-item (todos-item-string)) |
| 4726 | ;; Find the end of the date string added upon tagging item as done. | 4847 | (opoint (point)) |
| 4727 | (start (search-forward "] ")) | 4848 | (orig-mrk (progn (todos-item-start) (point-marker))) |
| 4728 | (end (save-excursion (todos-item-end))) | 4849 | (first 'first) |
| 4729 | item undone) | 4850 | (item-count 0) |
| 4730 | (todos-item-start) | 4851 | (diary-count 0) |
| 4731 | (when (and (re-search-forward (concat " \\[" | 4852 | start end item undone) |
| 4732 | (regexp-quote todos-comment-string) | 4853 | (and marked (goto-char (point-min))) |
| 4733 | ": \\([^]]+\\)\\]") end t) | 4854 | (catch 'done |
| 4734 | (y-or-n-p "Omit comment from restored item? ")) | 4855 | (while (not (eobp)) |
| 4735 | (delete-region (match-beginning 0) (match-end 0))) | 4856 | (if (or (not marked) (and marked (todos-marked-item-p))) |
| 4736 | (setq item (buffer-substring start end)) | 4857 | (if (not (todos-done-item-p)) |
| 4737 | (todos-remove-item) | 4858 | (error "Only done items can be undone") |
| 4738 | ;; If user cancels before setting new priority, then leave the done item | 4859 | (todos-item-start) |
| 4739 | ;; unchanged. | 4860 | ;; Find the end of the date string added upon tagging item as |
| 4740 | (unwind-protect | 4861 | ;; done. |
| 4741 | (progn | 4862 | (setq start (search-forward "] ")) |
| 4742 | (todos-set-item-priority item (todos-current-category) t) | 4863 | (setq item-count (1+ item-count)) |
| 4743 | (setq undone t) | 4864 | (unless (looking-at (regexp-quote todos-nondiary-start)) |
| 4744 | (todos-update-count 'todo 1) | 4865 | (setq diary-count (1+ diary-count))) |
| 4745 | (todos-update-count 'done -1) | 4866 | (setq end (save-excursion (todos-item-end))) |
| 4746 | (and (todos-diary-item-p) (todos-update-count 'diary 1)) | 4867 | ;; Ask (once) whether to omit done item's comment. If |
| 4747 | (todos-update-categories-sexp)) | 4868 | ;; affirmed, omit subsequent comments without asking. |
| 4748 | (unless undone | 4869 | (when (re-search-forward |
| 4749 | (widen) | 4870 | (concat " \\[" (regexp-quote todos-comment-string) |
| 4750 | (goto-char orig-mrk) | 4871 | ": [^]]+\\]") end t) |
| 4751 | (todos-insert-with-overlays done-item) | 4872 | (if (eq first 'first) |
| 4752 | (let ((todos-show-with-done t)) | 4873 | (setq first |
| 4753 | (todos-category-select) | 4874 | ;; FIXME: make this a user option? |
| 4754 | (goto-char opoint))) | 4875 | (when (y-or-n-p "Omit comment from restored item? ") |
| 4755 | (set-marker orig-mrk nil))))) | 4876 | 'omit)) |
| 4877 | t) | ||
| 4878 | (when (eq first 'omit) | ||
| 4879 | (delete-region (match-beginning 0) (match-end 0)) | ||
| 4880 | (setq end (point)))) | ||
| 4881 | (setq item (concat item (buffer-substring start end) | ||
| 4882 | (when marked "\n"))) | ||
| 4883 | (todos-remove-item) | ||
| 4884 | (unless marked (throw 'done nil))) | ||
| 4885 | (todos-forward-item)))) | ||
| 4886 | (if marked | ||
| 4887 | (progn | ||
| 4888 | (remove-overlays (point-min) (point-max) | ||
| 4889 | 'before-string todos-item-mark) | ||
| 4890 | (setq todos-categories-with-marks | ||
| 4891 | (assq-delete-all cat todos-categories-with-marks)) | ||
| 4892 | ;; Insert undone items that were marked at end of todo item list. | ||
| 4893 | (widen) | ||
| 4894 | (re-search-forward (concat "^" (regexp-quote todos-category-done)) | ||
| 4895 | nil t) | ||
| 4896 | (forward-line -1) | ||
| 4897 | (insert item) | ||
| 4898 | (todos-update-count 'todo item-count) | ||
| 4899 | (todos-update-count 'done (- item-count)) | ||
| 4900 | (when diary-count (todos-update-count 'diary diary-count)) | ||
| 4901 | (todos-update-categories-sexp)) | ||
| 4902 | ;; With an unmarked undone item, prompt for its priority. If user | ||
| 4903 | ;; cancels before setting new priority, then leave the done item | ||
| 4904 | ;; unchanged. | ||
| 4905 | (unwind-protect | ||
| 4906 | (progn | ||
| 4907 | (todos-set-item-priority item (todos-current-category) t) | ||
| 4908 | (setq undone t) | ||
| 4909 | (todos-update-count 'todo 1) | ||
| 4910 | (todos-update-count 'done -1) | ||
| 4911 | (and (todos-diary-item-p) (todos-update-count 'diary 1)) | ||
| 4912 | (todos-update-categories-sexp)) | ||
| 4913 | (unless undone | ||
| 4914 | (widen) | ||
| 4915 | (goto-char orig-mrk) | ||
| 4916 | (todos-insert-with-overlays done-item) | ||
| 4917 | (let ((todos-show-with-done t)) | ||
| 4918 | (todos-category-select) | ||
| 4919 | (goto-char opoint))) | ||
| 4920 | (set-marker orig-mrk nil))))))) | ||
| 4756 | 4921 | ||
| 4757 | (defun todos-archive-done-item (&optional all) | 4922 | (defun todos-archive-done-item (&optional all) |
| 4758 | "Archive at least one done item in this category. | 4923 | "Archive at least one done item in this category. |
| @@ -4996,31 +5161,24 @@ archive, the archive file is deleted." | |||
| 4996 | 5161 | ||
| 4997 | ;;; todos.el ends here | 5162 | ;;; todos.el ends here |
| 4998 | 5163 | ||
| 4999 | ;; --------------------------------------------------------------------------- | ||
| 5000 | |||
| 5001 | ;; FIXME: remove when part of Emacs | 5164 | ;; FIXME: remove when part of Emacs |
| 5165 | ;; --------------------------------------------------------------------------- | ||
| 5002 | (add-to-list 'auto-mode-alist '("\\.todo\\'" . todos-mode)) | 5166 | (add-to-list 'auto-mode-alist '("\\.todo\\'" . todos-mode)) |
| 5003 | (add-to-list 'auto-mode-alist '("\\.toda\\'" . todos-archive-mode)) | 5167 | (add-to-list 'auto-mode-alist '("\\.toda\\'" . todos-archive-mode)) |
| 5004 | 5168 | ||
| 5005 | ;;; Addition to calendar.el | 5169 | ;;; Addition to calendar.el |
| 5006 | ;; FIXME: autoload when key-binding is defined in calendar.el | 5170 | ;; FIXME: autoload when key-binding is defined in calendar.el |
| 5007 | (defun todos-insert-item-from-calendar () | 5171 | (defun todos-insert-item-from-calendar (&optional arg) |
| 5008 | "" | 5172 | "" |
| 5009 | (interactive) | 5173 | (interactive "P") |
| 5010 | ;; FIXME: todos-current-todos-file is nil here, better to solicit Todos | 5174 | (setq todos-date-from-calendar |
| 5011 | ;; file? todos-global-current-todos-file is nil if no Todos file has been | 5175 | (calendar-date-string (calendar-cursor-to-date t) t t)) |
| 5012 | ;; visited | 5176 | (calendar-exit) |
| 5013 | (pop-to-buffer (file-name-nondirectory todos-global-current-todos-file)) | ||
| 5014 | (todos-show) | 5177 | (todos-show) |
| 5015 | ;; FIXME: this now calls todos-set-date-from-calendar | 5178 | (todos-insert-item arg nil nil todos-date-from-calendar)) |
| 5016 | (todos-insert-item t 'calendar)) | ||
| 5017 | 5179 | ||
| 5018 | ;; FIXME: calendar is loaded before todos | 5180 | (define-key calendar-mode-map "it" 'todos-insert-item-from-calendar) |
| 5019 | ;; (add-hook 'calendar-load-hook | ||
| 5020 | ;; (lambda () | ||
| 5021 | (define-key calendar-mode-map "it" 'todos-insert-item-from-calendar);)) | ||
| 5022 | 5181 | ||
| 5023 | ;; --------------------------------------------------------------------------- | ||
| 5024 | ;;; necessitated adaptations to diary-lib.el | 5182 | ;;; necessitated adaptations to diary-lib.el |
| 5025 | 5183 | ||
| 5026 | ;; (defun diary-goto-entry (button) | 5184 | ;; (defun diary-goto-entry (button) |