1 ;;; planner-timeclock.el --- Timeclock integration for the Emacs Planner
3 ;; Copyright (C) 2001, 2004, 2005 Free Software Foundation, Inc.
4 ;; Parts copyright (C) 2005 Peter K. Lee
6 ;; Author: John Wiegley <johnw@gnu.org>
7 ;; Keywords: planner, timeclock
8 ;; URL: http://www.plannerlove.com/
10 ;; This file is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; This file is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to
22 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 ;; Boston, MA 02110-1301, USA.
27 ;; This module allows you to clock in and clock out of your projects.
28 ;; You can also generate reports with the <timeclock-report> tag.
30 ;; timeclock.el is part of GNU Emacs. If you are using XEmacs, please
31 ;; use the version in the contrib directory -- otherwise, just use the
32 ;; version that comes with Emacs.
36 ;; Yann Hodique helped port this to Muse.
38 ;; Peter K. Lee helped fix an issue involving planner-multi.el with
45 (require 'planner-schedule
)
49 (defvar planner-timeclock-current-task nil
50 "Description of current task.")
52 (defalias 'planner-timeclock-in
'planner-task-in-progress
)
54 (defadvice timeclock-out
(after planner-timeclock activate
)
55 "Clear `planner-timeclock-current-task.'"
56 (setq planner-timeclock-current-task nil
))
58 (defun planner-timeclock-plan-string (task-info &optional plans
)
59 "Return the string for the plan part of the timeclock entry for TASK-INFO."
60 (let ((plan-link (if (featurep 'planner-multi
)
61 ;; Remove the date links
62 (planner-multi-make-link
63 (or (planner-multi-filter-links
66 (planner-multi-split plans
)
67 (planner-multi-task-link-as-list task-info
))
69 (list (planner-task-plan task-info
))))
71 (or plans
(planner-task-plan task-info
))))))
73 (concat plan-link
": ")
76 (defun planner-timeclock-task-marked (old-status new-status
)
77 "Clock out if the task description matches the one clocked in."
79 ((string= new-status
"X")
80 (when (and planner-timeclock-current-task
81 (string= (planner-task-description (planner-current-task-info))
82 planner-timeclock-current-task
)
83 (timeclock-currently-in-p))
85 ((string= new-status
"P")
86 (when (and planner-timeclock-current-task
87 (string= (planner-task-description (planner-current-task-info))
88 planner-timeclock-current-task
)
89 (timeclock-currently-in-p))
91 ((string= new-status
"o")
92 (let* ((task-info (planner-current-task-info))
94 (planner-timeclock-plan-string task-info
)
95 (planner-task-description task-info
))))
96 (if (timeclock-currently-in-p)
97 (timeclock-change nil project
)
98 (timeclock-in nil project
))
99 (setq planner-timeclock-current-task
(planner-task-description task-info
)))))
101 (add-hook 'planner-mark-task-hook
'planner-timeclock-task-marked
)
103 (defadvice planner-replan-task
(around planner-timeclock activate
)
104 "Edit the clocked in task as well."
105 (let ((info (planner-current-task-info)))
107 (with-current-buffer (find-file-noselect timeclock-file
)
108 (goto-char (point-min))
109 (while (re-search-forward
112 "\\(" (regexp-quote (planner-timeclock-plan-string info
)) "\\)"
113 (regexp-quote (planner-task-description info
)) "$") nil t
)
115 (save-match-data (planner-timeclock-plan-string nil
(ad-get-arg 0)))
118 (kill-buffer (current-buffer)))))
120 (defadvice planner-edit-task-description
(around planner-timeclock activate
)
121 "Update the timelog as well. Warning! Do not have duplicate tasks!"
122 (let ((info (planner-current-task-info)))
123 (when (string= (planner-task-description info
) planner-timeclock-current-task
)
124 (setq planner-timeclock-current-task
(ad-get-arg 0)))
126 (with-current-buffer (find-file-noselect timeclock-file
)
127 (goto-char (point-min))
128 (while (re-search-forward
131 (regexp-quote (planner-timeclock-plan-string info
))
133 (regexp-quote (planner-task-description info
))
136 (replace-match (ad-get-arg 0) t t nil
1))
138 (kill-buffer (current-buffer)))))
141 (defun planner-timeclock-report-tag (beg end highlight-p
)
142 "Replace the region BEG to END with a timeclock report.
143 If HIGHLIGHT-P is non-nil, the output is also displayed."
147 beg end
(list 'display
149 (timeclock-generate-report muse-publishing-p
)
151 (delete-region beg end
)
152 (timeclock-generate-report muse-publishing-p
)
153 (add-text-properties beg
(point) '(read-only t
))))
155 (add-to-list 'planner-markup-tags
156 '("timeclock-report" nil nil t planner-timeclock-report-tag
))
157 (planner-option-customized 'planner-markup-tags planner-markup-tags
)
159 (defun planner-timeclock-task-plan (info)
160 "Return the first plan page associated with INFO."
163 (defun planner-timeclock-task-plan-as-list (info)
164 "Return all the plan pages associated with INFO."
167 (defun planner-timeclock-task-description (info)
168 "Return the descrption associated with INFO."
171 (defun planner-timeclock-task-length (info)
172 "Return the length associated with INFO."
175 (defun planner-timeclock-task-info (entry)
176 "Parse ENTRY and return a list of the form (plan task length).
177 See `timeclock-log-data' for the format of ENTRY. Note that the
178 project field in `timeclock-log-data' is 'project: task' here."
179 (let ((project (if (stringp entry
) entry
(timeclock-entry-project entry
)))
184 ;; No plan, just the task
185 ((string-match "^\\s-*:\\s-+" project
)
186 (setq task
(substring project
(match-end 0))))
188 ((string-match (concat "^\\(" muse-explicit-link-regexp
"\\): ") project
)
189 (setq plan
(list (match-string 1 project
)))
190 (setq task
(substring project
(match-end 0))))
193 (featurep 'planner-multi
)
195 (concat "^\\(\\(?:" muse-explicit-link-regexp
"\\)"
196 "\\(?:" planner-multi-separator
197 "\\(?:" muse-explicit-link-regexp
"\\)\\)*\\): ")
199 (setq task
(substring project
(match-end 0)))
200 (setq plan
(planner-multi-split (match-string 1 project
))))
201 ;; Nothing whatsoever.
202 (t (setq task project
)))
203 (list plan task
(unless (stringp entry
) (timeclock-entry-length entry
)))))))
206 (let ((map planner-mode-map
))
207 (define-key map
"\C-c\C-i" 'planner-task-in-progress
)
208 (define-key map
"\C-c\C-o" 'timeclock-out
)
209 (if (featurep 'xemacs
)
211 (define-key map
(kbd "C-c C-T C-i") 'planner-task-in-progress
)
212 (define-key map
(kbd "C-c C-T C-o") 'timeclock-out
))
213 (define-key map
(kbd "C-c C-S-t C-i") 'planner-task-in-progress
)
214 (define-key map
(kbd "C-c C-S-t C-o") 'timeclock-out
)))
215 (error (message "Could not bind timeclock keys in planner mode")))
218 ;; XEmacs seems to be missing this function in some released
219 ;; versions of XEmacs21.
220 (if (fboundp 'easy-menu-create-menu
)
221 (easy-menu-add-item planner-mode-map
223 (easy-menu-create-menu
225 '(["Clock into a task" planner-timeclock-in
]
226 ["Clock out" timeclock-out
])))
227 (easy-menu-add-item planner-menu
229 ["Clock into a task" planner-timeclock-in t
]
231 (easy-menu-add-item planner-menu
233 ["Clock out" timeclock-out t
]
236 (provide 'planner-timeclock
)
238 ;;; planner-timeclock.el ends here