1 ;;; planner-multi.el --- Multiple page support for planner.el
3 ;; Copyright (C) 2004, 2005 Free Software Foundation, Inc.
5 ;; Author: Sandra Jean Chua (Sacha) <sacha AT free.net.ph>
7 ;; This file is part of Planner. It is not part of GNU Emacs.
9 ;; Planner is free software; you can redistribute it and/or modify it
10 ;; under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; Planner is distributed in the hope that it will be useful, but
15 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 ;; General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with Planner; see the file COPYING. If not, write to the
21 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 ;; Boston, MA 02110-1301, USA.
24 ;; TODO: completion that makes sense!
28 ;; After (require 'planner-multi), you should be able to create tasks
29 ;; on multiple pages using M-x planner-create-task-from-buffer or M-x
30 ;; planner-create-task. Notes should also work if you have at least
31 ;; remember--dev--1.0--patch-21.
35 ;; Jim Ottaway made this work better with planner-appt.el and fixed
38 ;; Yann Hodique helped in porting this to use Muse rather than
41 ;; Sergey Vlasov provided a bugfix for lists with dates in them.
43 ;; Marco Gidde provided a patch that fixes a problem with extraneous
46 ;; Seth Falcon contributed a small improvement to
47 ;; `planner-multi-replan-task'.
53 (message "Could not load crm; cumbersome completing read used")))
57 (defcustom planner-multi-separator
" "
58 "String that separates multiple page references.
59 For best results, this should be something recognized by
60 `muse-link-at-point' so that links are highlighted separately."
64 (defcustom planner-multi-copy-tasks-to-page nil
65 "If non-nil, planner page to automatically copy tasks to. Example: TaskPool.
66 You can specify multiple pages by separating them with `planner-multi-separator',
67 such as \"[[AllTasksByProject][p]] [[AllTasksByContext][c]]\"."
69 :group
'planner-multi
)
71 (defcustom planner-multi-date-links-last-p t
72 "If non-nil, put day-page links after other pages on the task line.
73 Otherwise, put them before other pages on the task line."
75 :group
'planner-multi
)
77 ;;;_+ Utility functions
79 (defun planner-multi-next-link (current link-list
)
80 "Return the item following CURRENT in LINK-LIST."
81 (setq current
(or current
(planner-page-name)))
84 (while (and (car search
) (null found
))
85 (when (string= (planner-link-base (car search
))
87 (setq found
(car (cdr search
))))
88 (setq search
(cdr search
)))
89 (or found
(car link-list
))))
91 (defun planner-multi-make-link (list)
92 "Create a text link for LIST."
93 (mapconcat 'planner-make-link
95 planner-multi-separator
))
97 (defun planner-multi-link-member (page links
)
98 "Return non-nil if PAGE is a member of LINKS."
100 (while (and (car links
) (null found
))
101 (when (string= (planner-link-base (car links
))
103 (setq found
(car links
)))
104 (setq links
(cdr links
)))
107 (defun planner-multi-link-delete (page links
)
108 "Delete PAGE from LINKS."
111 (unless (string= (planner-link-base (car links
))
113 (setq results
(cons (car links
) results
)))
114 (setq links
(cdr links
)))
117 (defun planner-multi-split (string)
118 "Return a list of links in STRING."
119 ;; "[[foo bar]] baz [[qux quux]]" should return ("[[foo bar]]" "baz" "[[qux quux]]"), so a simple split-string won't do
123 (len (length string
)))
126 (concat (if (= (aref string pos
) ?\
[)
127 "\\(\\[\\[.+?\\]\\]\\)"
129 (regexp-quote planner-multi-separator
)
133 (add-to-list 'list
(match-string 1 string
) t
)
134 (setq pos
(match-end 0)))
135 (add-to-list 'list
(substring string pos
) t
)
142 (defadvice planner-task-info-from-string
(after planner-multi activate
)
143 "Store split task strings in position 5 (`planner-task-link')."
144 (when ad-return-value
145 (let ((sub-list (cdr (cdr (cdr (cdr (cdr ad-return-value
))))))
147 (when (car (cdr sub-list
))
148 (setq links
(planner-multi-split (car (cdr sub-list
)))))
149 (unless (planner-multi-link-member (planner-page-name) links
)
150 (add-to-list 'links
(planner-page-name)))
151 (setcar sub-list links
))))
153 (defadvice planner-task-link
(around planner-multi activate
)
154 "Return the next link of a task for compatibility with old code."
155 (setq ad-return-value
156 (if (listp (nth 5 info
))
157 (let ((link (planner-multi-next-link (planner-page-name) (nth 5 info
))))
158 (when link
(planner-link-base link
)))
161 (defadvice planner-task-plan
(around planner-multi activate
)
162 "Return the first plan of a task for compatibility with old code."
163 (if (and (nth 5 info
) (listp (nth 5 info
)))
164 (let ((current (nth 5 info
)))
166 (unless (string-match planner-date-regexp
(planner-link-base (car current
)))
167 (setq ad-return-value
(planner-link-base (car current
)))
169 (setq current
(cdr current
))))
172 (defun planner-multi-task-date (info)
173 "Return the date assigned to a task given INFO."
174 (or (let ((date (nth 8 info
)))
176 (string-match planner-date-regexp date
)
178 (let ((links (nth 5 info
))
182 (let ((page (planner-link-base (car links
))))
183 (if (string-match planner-date-regexp page
)
184 (setq found-date page
186 (setq links
(cdr links
)))))
188 (defalias 'planner-task-date
'planner-multi-task-date
)
190 (defun planner-multi-task-link-as-list (info)
191 "Return the page links of INFO as a list."
192 (if (listp (nth 5 info
))
194 (list (nth 5 info
))))
195 (defalias 'planner-task-link-as-list
'planner-multi-task-link-as-list
)
197 (defun planner-multi-task-xref (page)
198 "Copy the current task to PAGE."
199 (interactive (list (planner-make-link (planner-read-name (planner-file-alist)))))
200 (let* ((info (planner-current-task-info))
201 (links (planner-multi-task-link-as-list info
)))
203 (setq page
(planner-multi-split page
)))
205 (unless (planner-multi-link-member (car page
) links
)
206 (add-to-list 'links
(car page
) t
))
207 (setq page
(cdr page
)))
209 (planner-multi-link-delete (planner-task-date info
) links
))))
213 (defun planner-multi-copy-or-move-task (&optional date force
)
214 "Move the current task to DATE.
215 If this is the original task, it copies it instead of moving.
216 Most of the time, the original should be kept in a planning file,
217 but this is not required. If FORCE is non-nil, the task is moved
218 regardless of status. It also works for creating tasks from a
219 Note. Use `planner-replan-task' if you want to change the plan
220 page in order to get better completion.
221 This function is the most complex aspect of planner.el."
222 (interactive (list (let ((planner-expand-name-favor-future-p
223 (or planner-expand-name-favor-future-p
224 planner-task-dates-favor-future-p
)))
227 (let ((info (planner-current-task-info))
228 (case-fold-search nil
))
230 (string-match planner-date-regexp date
))
231 (if (and (planner-task-link-text info
)
232 (or (listp (planner-task-link-text info
))
234 (concat "^\\(" muse-explicit-link-regexp
235 "\\|[1-9][0-9][0-9][0-9]\\.[0-9]+\\.[0-9]+"
237 "\\(" (regexp-quote planner-multi-separator
)
238 "\\(" muse-explicit-link-regexp
239 "\\|[1-9][0-9][0-9][0-9]\\.[0-9]+\\.[0-9]+"
241 (planner-task-link-text info
))))
243 (when (equal date
(planner-page-name))
244 (error "Cannot move a task back to the same day"))
246 (when (equal (planner-task-date info
) date
)
247 (error "Cannot move a task back to the same day"))
248 (when (equal (planner-task-status info
) "X")
249 (error "Cannot reschedule a completed task"))
250 (when (equal (planner-task-status info
) "C")
251 (error "Cannot reschedule a cancelled task")))
253 (with-planner-update-setup
254 ;; Delete from date page
255 (let ((old-date (planner-task-date info
))
256 (links (planner-multi-split
257 (planner-task-link-text info
))))
259 (planner-find-file (planner-link-base old-date
))
260 (planner-find-task info
)
261 (delete-region (planner-line-beginning-position)
263 (1+ (planner-line-end-position)))))
265 (setq links
(planner-multi-link-delete
266 (planner-task-date info
) links
))
267 (planner-find-file (planner-link-base (car links
)))
268 (when date
(setq links
(cons date links
)))
269 (if (planner-find-task info
)
270 (delete-region (planner-line-beginning-position)
272 (1+ (planner-line-end-position))))
273 (planner-seek-task-creation-point))
274 (insert (planner-format-task info nil nil nil nil
275 (planner-multi-make-link links
))
278 ;; Update all linked tasks
279 (planner-update-task))))
280 (planner-copy-or-move-task-basic date force
))
281 (when (planner-replan-task date
) t
))))
282 (defalias 'planner-copy-or-move-task
'planner-multi-copy-or-move-task
)
284 (defun planner-multi-task-string (info page-name links
&optional new-pages
)
285 "Return task line for INFO on PAGE-NAME with LINKS, a list of pages to link to.
286 If non-nil, PAGES should be a list of the `planner-link-base's of LINKS."
287 ;; Set up the new links list for easy testing
288 (setq new-pages
(mapcar 'planner-link-base links
))
290 ;; If this is a no-link task
291 ((and (= (length new-pages
) 1)
292 (string= (car new-pages
) page-name
))
293 (planner-format-task info nil nil nil nil
"" ""))
294 ;; If this is a standard singly-linked task (date, plan)
295 ((and (= (length new-pages
) 2)
296 (string-match planner-date-regexp
(car new-pages
))
297 (not (string-match planner-date-regexp
(cadr new-pages
))))
298 (planner-format-task info nil nil nil nil
300 (if (string-match planner-date-regexp page-name
)
303 ;; If this is a standard singly-linked task (plan, date)
304 ((and (= (length new-pages
) 2)
305 (not (string-match planner-date-regexp
(car new-pages
)))
306 (string-match planner-date-regexp
(cadr new-pages
)))
307 (planner-format-task info nil nil nil nil
309 (if (string-match planner-date-regexp page-name
)
313 (t (planner-format-task info nil nil nil nil
314 (planner-make-link new-pages
)))))
316 (defun planner-multi-replan-task (pages)
317 "Change or assign the plan page for the current task.
318 PAGES is the new plan page for the task. Use
319 `planner-copy-or-move-task' if you want to change the date.
320 With a prefix, provide the current link text for editing."
323 (planner-file-alist) nil
324 ;; The list of planner pages associated with this task
325 (when current-prefix-arg
326 (planner-multi-make-link
327 (planner-multi-filter-links
329 (planner-multi-task-link-as-list
330 (planner-current-task-info))
332 (when (stringp pages
) (setq pages
(planner-multi-split pages
)))
333 (let* ((info (planner-current-task-info))
335 ;; Pages the task is currently assigned to
336 (planner-multi-task-link-as-list info
))
338 date
) ; Pages from page-name
339 ;; Add dates back to the list of pages
340 (setq date
(planner-task-date info
))
341 (when date
(add-to-list 'pages date planner-multi-date-links-last-p
))
343 (error "Cannot replan this task; it will disappear"))
344 (with-planner-update-setup
345 ;; Set up the new links list for easy testing
346 (setq new-pages
(mapcar 'planner-link-base pages
))
347 ;; Map over the old pages, removing if not in the new one and
350 (planner-find-file (car old-pages
))
352 ((planner-find-task info
)
353 (delete-region (planner-line-beginning-position)
354 (1+ (planner-line-end-position))))
355 ((member (planner-page-name) new-pages
)
356 (planner-seek-task-creation-point)))
357 (when (member (planner-page-name) new-pages
)
358 (insert (planner-multi-task-string info
(planner-page-name) pages new-pages
) "\n"))
359 (setq old-pages
(cdr old-pages
)))
360 ;; Map over any new pages that were not included
361 (setq old-pages
(mapcar 'planner-link-base
(planner-multi-task-link-as-list info
)))
362 (mapcar (lambda (page)
363 (unless (member page old-pages
)
364 (planner-find-file page
)
365 (if (planner-find-task info
)
366 (delete-region (planner-line-beginning-position)
367 (1+ (planner-line-end-position)))
368 (planner-seek-task-creation-point))
369 (insert (planner-multi-task-string info
(planner-page-name) pages new-pages
) "\n")))
372 (defalias 'planner-replan-task
'planner-multi-replan-task
)
374 (defun planner-multi-update-task ()
375 "Update the current task's priority and status on the linked page.
376 Tasks are considered the same if they have the same description.
377 This function allows you to force a task to be recreated if it
378 disappeared from the associated page.
380 Note that the text of the task must not change. If you want to be able
381 to update the task description, see planner-id.el."
383 (let* ((info (planner-current-task-info))
384 (links (planner-multi-task-link-as-list info
))
385 (new-pages (mapcar 'planner-link-base links
)))
387 (with-planner-update-setup
389 (planner-find-file (planner-link-base (car links
)))
390 (if (planner-find-task info
)
391 ;; Already there, so update only if changed
392 (unless (planner-tasks-equal-p info
393 (planner-current-task-info))
394 (delete-region (planner-line-beginning-position)
395 (planner-line-end-position))
396 (insert (planner-multi-task-string info
(planner-page-name)
397 (planner-multi-task-link-as-list info
)
399 ;; Not yet there, so add it
400 (planner-seek-task-creation-point)
401 (insert (planner-multi-task-string info
(planner-page-name)
402 (planner-multi-task-link-as-list info
)
404 (setq links
(cdr links
))))))
405 (defalias 'planner-update-task
'planner-multi-update-task
)
407 (defun planner-multi-tasks-equal-p (task-a task-b
)
408 "Return t if TASK-A and TASK-B are equivalent.
409 This is true if they have the same value for priority, status,
410 description, and links."
411 (and (string= (or (planner-task-priority task-a
) "")
412 (or (planner-task-priority task-b
) ""))
413 (string= (or (planner-task-status task-a
) "")
414 (or (planner-task-status task-b
) ""))
415 (string= (or (planner-task-description task-a
) "")
416 (or (planner-task-description task-b
) ""))
417 (if (or (string-match (regexp-quote planner-multi-separator
)
418 (or (planner-task-link-text task-a
) ""))
419 (string-match (regexp-quote planner-multi-separator
)
420 (or (planner-task-link-text task-b
) "")))
421 (string= (planner-task-link-text task-a
)
422 (planner-task-link-text task-b
))
423 (and (string= (or (planner-task-plan task-a
) "")
424 (or (planner-task-plan task-b
) ""))
425 (string= (or (planner-task-date task-a
) "")
426 (or (planner-task-date task-b
) ""))))))
427 (defalias 'planner-tasks-equal-p
'planner-multi-tasks-equal-p
)
429 (defadvice planner-delete-task
(around planner-multi activate
)
430 "Remove this note from all linked pages."
431 ;; Delete the current note.
432 (save-window-excursion
433 (let ((info (planner-current-task-info))
435 (setq links
(planner-multi-task-link-as-list info
))
436 (if (<= (length links
) 1)
439 (planner-find-file (planner-link-base (car links
)))
440 (when (planner-find-task info
)
441 (delete-region (planner-line-beginning-position)
442 (min (point-max) (1+ (planner-line-end-position)))))
443 (setq links
(cdr links
)))))))
445 (defadvice planner-make-link
(around planner-multi activate
)
446 "Escape separately if using `planner-multi-separator'."
449 (let ((case-fold-search nil
))
452 (setq ad-return-value
(mapconcat 'muse-make-link
454 planner-multi-separator
)))
455 ((string-match (regexp-quote planner-multi-separator
) link
)
456 (setq ad-return-value
(mapconcat 'muse-make-link
457 (planner-multi-split link
)
458 planner-multi-separator
)))
461 (defun planner-multi-filter-links (regexp links-list
&optional nonmatch
)
462 "Return a list of links matching REGEXP in LINKS-LIST.
463 If NONMATCH is non-nil, return non-matching links instead."
465 (mapcar (lambda (item)
466 (when (funcall (if nonmatch
'not
'null
)
467 (string-match regexp item
))
471 (defun planner-multi-create-task-from-info (info &optional priority number status description link-text date plan
)
472 "Create a task in the date and plan pages based on INFO.
473 Optional arguments PRIORITY, NUMBER, STATUS, DESCRIPTION,
474 LINK-TEXT, DATE, and PLAN override those in INFO.
475 Create task on multiple pages if necessary."
476 (setq link-text
(or link-text
(planner-task-link-text info
)))
477 (when planner-multi-copy-tasks-to-page
478 (if (and link-text planner-multi-copy-tasks-to-page
)
479 (unless (string-match (regexp-quote (planner-link-base planner-multi-copy-tasks-to-page
)) link-text
)
480 (setq link-text
(concat link-text planner-multi-separator planner-multi-copy-tasks-to-page
)))
481 (setq link-text planner-multi-copy-tasks-to-page
)))
483 (string-match (regexp-quote planner-multi-separator
) link-text
))
485 ;; Create the task on all pages.
486 (let ((list (planner-multi-split (or link-text
(planner-task-link-text info
))))
487 (link-text (or link-text
(planner-task-link-text info
))))
488 (when (or date
(planner-task-date info
))
489 (setq list
(planner-multi-filter-links
493 (add-to-list 'list
(or date
(planner-task-date info
))
494 planner-multi-date-links-last-p
)
495 (setq link-text
(planner-multi-make-link list
)))
496 (save-window-excursion
498 (planner-find-file (planner-link-base (car list
)))
499 (planner-seek-task-creation-point)
500 (insert (planner-format-task info priority number status description link-text
)
502 (setq list
(cdr list
)))
504 (run-hooks 'planner-create-task-hook
))))
505 (planner-create-task-from-info-basic info priority number status
506 description link-text date plan
)))
507 (setq planner-create-task-from-info-function
'planner-multi-create-task-from-info
)
509 (defun planner-multi-task-delete-this-page ()
510 "Remove this task from the current page."
512 (let ((info (planner-current-task-info)))
513 (planner-multi-replan-task
514 (planner-multi-link-delete
515 (planner-task-date info
)
516 (planner-multi-link-delete (planner-page-name)
517 (planner-multi-task-link-as-list
518 (planner-current-task-info)))))))
521 (defun planner-multi-edit-task-description (description)
522 "Update multiple pages."
524 (let* ((info (planner-current-task-info))
525 (planner-task-history
527 (planner-task-description info
))))
528 (unless info
(error "No task on current line"))
529 (read-string "New description: "
530 (cons (planner-task-description info
)
532 '(planner-task-history .
1)
533 (planner-task-description info
)))))
534 (let ((point (point)))
535 (with-planner-update-setup
536 (let* ((info (planner-current-task-info))
537 (newinfo (planner-current-task-info))
538 (links (planner-multi-task-link-as-list info
))
539 (new-pages (mapcar 'planner-link-base links
)))
540 (setcar (cdr (cdr (cdr (cdr newinfo
))))
543 (planner-find-file (planner-link-base (car links
)))
544 (if (planner-find-task info
)
545 ;; Already there, so update only if changed
546 (delete-region (planner-line-beginning-position)
548 (1+ (planner-line-end-position))))
549 ;; Not yet there, so add it
550 (planner-seek-task-creation-point))
551 (insert (planner-multi-task-string
552 newinfo
(planner-page-name)
553 (planner-multi-task-link-as-list info
)
556 (setq links
(cdr links
)))))
558 (defalias 'planner-edit-task-description
'planner-multi-edit-task-description
)
560 ;; Todo: copy-or-move
564 (defadvice planner-current-note-info
(after planner-multi activate
)
565 "Store split note strings in position 4 (`planner-note-link')."
566 (let ((sub-list (cdr (cdr (cdr (cdr ad-return-value
))))))
567 (when (and (car sub-list
) (string-match (regexp-quote planner-multi-separator
) (car sub-list
)))
568 (setcar sub-list
(planner-multi-split (car sub-list
))))))
570 (defadvice planner-note-link
(around planner-multi activate
)
571 "Return the next link of a note for compatibility with old code."
572 (setq ad-return-value
573 (if (and (nth 4 note-info
) (listp (nth 4 (ad-get-arg 0))))
574 (planner-multi-next-link (planner-page-name) (nth 4 (ad-get-arg 0)))
577 (defadvice planner-note-link-text
(around planner-multi activate
)
578 "Return the link text of a note for compatibility with old code."
579 (setq ad-return-value
580 (if (and (nth 4 note-info
) (listp (nth 4 (ad-get-arg 0))))
581 (planner-make-link (nth 4 note-info
))
584 (defun planner-multi-note-link-as-list (info)
585 "Return the page links of INFO as a list."
586 (if (listp (nth 4 info
))
588 (list (nth 4 info
))))
590 (defadvice planner-update-note
(around planner-multi activate
)
591 "Copy the text from this note to the linked notes, if any."
593 (let ((info (planner-current-note-info))
595 (if (= (length (planner-multi-note-link-as-list info
)) 1)
597 (save-window-excursion
599 (planner-narrow-to-note)
600 (goto-char (point-min))
601 (skip-chars-forward ".#0-9")
602 (setq body
(buffer-substring-no-properties (point) (point-max))))
603 (let ((links (planner-multi-note-link-as-list info
)))
606 (planner-visit-link (planner-link-target (car links
)))
608 (when (planner-narrow-to-note)
609 (goto-char (point-min))
610 (skip-chars-forward ".#0-9")
611 (delete-region (point) (point-max))
613 (setq links
(cdr links
))))))))
615 (defun planner-multi-note-xref (page)
616 "Copy the current note to PAGE."
617 (interactive (list (planner-read-name (planner-file-alist))))
618 (let ((case-fold-search nil
))
619 ;; Modify the current note
620 (save-window-excursion
621 (when (planner-current-note-info)
623 (save-window-excursion
624 (setq note-num
(planner-create-note page
)))
626 (unless (looking-at "^.#[0-9]")
627 (re-search-backward "^.#[0-9]" nil t
))
629 ;; Already a multilink
630 ((re-search-forward (concat
632 "\\(" muse-explicit-link-regexp
"\\)"
634 (regexp-quote planner-multi-separator
)
636 muse-explicit-link-regexp
637 "\\)\\)+)") (planner-line-end-position) t
)
639 (insert planner-multi-separator
640 (planner-make-link (concat page
"#"
641 (number-to-string note-num
)))))
643 ((re-search-forward (concat "(\\("
644 muse-explicit-link-regexp
646 (planner-line-end-position) t
)
648 (insert planner-multi-separator
649 (planner-make-link (concat page
"#"
650 (number-to-string note-num
)))
651 planner-multi-separator
652 (planner-make-link (concat (planner-page-name) "#"
653 (planner-note-anchor (planner-current-note-info))))))
656 (re-search-forward "\\s-*$" (planner-line-end-position) t
)
660 (planner-make-link (concat page
"#"
661 (number-to-string note-num
))))
664 (planner-update-note))))))
666 (defadvice planner-replan-note
(around planner-multi activate
)
667 "Allow multiple pages."
668 (if (string-match (regexp-quote planner-multi-separator
) page
)
669 (save-window-excursion
671 (let* ((info (planner-current-note-info))
672 (links (planner-multi-note-link-as-list info
))
673 (new-links (planner-multi-split page
))
674 (old-anchor (planner-make-link
675 (concat (planner-note-page info
) "#"
676 (planner-note-anchor info
))))
679 (old-pages (mapcar 'planner-link-base links
)))
680 (when (and (not old-pages
)
681 (string-match planner-date-regexp
(planner-note-page info
)))
682 (setq old-pages
(list (planner-note-page info
)))
683 (add-to-list 'new-links old-anchor
))
685 (setq cursor new-links
)
687 (setq current-page
(planner-link-base (car cursor
)))
688 (when (not (member current-page old-pages
))
692 (planner-link-base (car cursor
))
693 (planner-create-note current-page
)))))
694 (setq cursor
(cdr cursor
)))
695 ;; Delete from pages removed from list
698 (unless (member (planner-link-base (car cursor
)) old-pages
)
699 (planner-visit-link (planner-link-target (car links
)))
701 (when (planner-narrow-to-note)
702 (delete-region (point-min) (point-max)))))
703 (setq cursor
(cdr cursor
)))
704 ;; Update the current note
705 (planner-visit-link (concat (planner-note-page info
)
706 "#" (planner-note-anchor info
)))
707 (delete-region (1+ (point)) (planner-line-end-position))
708 (insert " " (planner-format-note info
"" nil nil
(planner-make-link
710 (planner-update-note))))
713 (defun planner-multi-note-delete ()
714 "Remove this note from all linked pages."
716 ;; Delete the current note.
717 (save-window-excursion
718 (let ((info (planner-current-note-info))
721 (setq links
(planner-multi-note-link-as-list info
))
723 (planner-visit-link (planner-link-target (car links
)))
725 (planner-narrow-to-note)
726 (delete-region (point-min) (point-max)))
727 (setq links
(cdr links
)))))))
729 (defun planner-multi-note-delete-this-page ()
730 "Remove this note from the current page."
732 ;; Delete the current note.
733 (save-window-excursion
734 (let* ((info (planner-current-note-info))
739 (planner-multi-link-delete
741 (planner-multi-note-link-as-list info
)))
742 (setq new-link
(planner-multi-make-link new-link-list
))
744 (planner-narrow-to-note)
745 (delete-region (point-min) (point-max)))
747 (planner-visit-link (planner-link-target (car new-link-list
)))
748 (when (re-search-forward (concat
750 "\\(" muse-explicit-link-regexp
"\\)"
752 (regexp-quote planner-multi-separator
)
754 muse-explicit-link-regexp
756 (planner-line-end-position)
759 (if (string= new-link
"") ""
760 (concat "(" new-link
")"))
762 (setq new-link-list
(cdr new-link-list
)))))))
764 (defalias 'planner-multi-xref-note
'planner-multi-note-xref
)
765 (defalias 'planner-multi-delete-note
'planner-multi-note-delete
)
766 (defalias 'planner-multi-delete-note-this-page
'planner-multi-note-delete-this-page
)
767 (defalias 'planner-multi-xref-task
'planner-multi-task-xref
)
768 (defalias 'planner-multi-delete-task-this-page
'planner-multi-task-delete-this-page
)
770 (defun planner-multi-read-name (file-alist &optional prompt initial
)
771 "Read multiple pages, completing based on FILE-ALIST.
772 If PROMPT is specified, use that instead of \"Page:\"."
773 (let* ((minibuffer-prompt-properties
775 (copy-sequence minibuffer-prompt-properties
)
777 (map (copy-keymap minibuffer-local-completion-map
))
778 (completion-ignore-case t
)
779 (crm-separator (regexp-quote planner-multi-separator
))
780 (prompt (format "%s(default: %s) "
781 (or prompt
"Page: ") planner-default-page
))
785 (define-key minibuffer-local-completion-map
786 planner-multi-separator
'self-insert-command
)
788 (if (fboundp 'completing-read-multiple
)
789 (completing-read-multiple
790 prompt file-alist nil nil initial
791 'planner-history-list
792 planner-default-page
)
794 (read-string prompt initial
'planner-history-list
795 planner-default-page
))))
798 (string= (car str
) "")) planner-default-page
)
799 ((string= (car str
) "nil") nil
)
800 (t (mapconcat 'identity str planner-multi-separator
))))
801 (setq minibuffer-local-completion-map map
))))
803 (defun planner-multi-read-name-multiple-prompts (file-alist prompt initial
)
804 "Read multiple pages, completing based on FILE-ALIST.
805 If PROMPT is specified, use that instead of \"Page:\".
806 Enter one plan page at a time. To end input, type \"nil\"."
813 (planner-read-name-single file-alist
814 (concat "nil to stop. " (or prompt
"Page: "))
816 (setq planner-default-page nil initial nil
)
818 (add-to-list 'list input t
)
820 (setq planner-default-page
(planner-multi-make-link list
))))
822 (if (fboundp 'completing-read-multiple
)
823 (setq planner-read-name-function
'planner-multi-read-name
)
824 (setq planner-read-name-function
'planner-multi-read-name-multiple-prompts
))
827 (defun planner-multi-remove-task-from-pool (old-status new-status
)
828 "Remove completed tasks from `planner-multi-copy-tasks-to-page' if that still leaves them linked."
829 (when (and planner-multi-copy-tasks-to-page
830 (or (string= new-status
"C")
831 (string= new-status
"X")))
832 (let ((info (planner-current-task-info)))
833 (when (planner-task-link-text info
)
834 ;; If it is linked to TaskPool _and_ at least one other thing
836 ((string-match planner-multi-separator
(planner-task-link-text info
))
838 (mapcar 'planner-link-base
(planner-multi-split planner-multi-copy-tasks-to-page
)))
842 (mapcar (lambda (item)
843 (unless (member (planner-link-base item
) remove-from
)
844 (planner-link-base item
)))
845 (planner-multi-task-link-as-list info
))))
847 (planner-replan-task (mapconcat 'identity new-links planner-multi-separator
)))
848 ;; Make sure we are on the same task
849 (when (string= (planner-page-name) planner-multi-copy-tasks-to-page
)
850 (planner-find-file (car new-links
))
851 (planner-find-task info
))))
852 ;; Else if it has a date and is linked to TaskPool
853 ((and (planner-task-date info
)
854 (string= (planner-task-plan info
) planner-multi-copy-tasks-to-page
)
855 (save-excursion (planner-replan-task nil
))
856 (when (string= (planner-page-name) planner-multi-copy-tasks-to-page
)
857 (planner-find-file (planner-task-date info
))
858 (planner-find-task info
))))))))
861 (add-hook 'planner-mark-task-hook
'planner-multi-remove-task-from-pool t
)
863 (provide 'planner-multi
)
865 ;;; planner-multi.el ends here