planner-calendar: Make « and » work
[planner-el.git] / planner-timeclock.el
blob4e5dc3f03cc7c4e45fb6248fc88fe81e45d63240
1 ;;; planner-timeclock.el --- Timeclock integration for the Emacs Planner
3 ;; Copyright (C) 2001, 2004, 2005, 2006 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.wjsullivan.net/PlannerMode.html
10 ;; This file is part of Planner. It is not part of GNU Emacs.
12 ;; Planner is free software; you can redistribute it and/or modify it
13 ;; under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
17 ;; Planner is distributed in the hope that it will be useful, but
18 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ;; General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with Planner; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 ;; Boston, MA 02110-1301, USA.
27 ;;; Commentary:
29 ;; This module allows you to clock in and clock out of your projects.
30 ;; You can also generate reports with the <timeclock-report> tag.
32 ;; timeclock.el is part of GNU Emacs. If you are using XEmacs, please
33 ;; use the version in the contrib directory -- otherwise, just use the
34 ;; version that comes with Emacs.
36 ;;; Contributors:
38 ;; Yann Hodique helped port this to Muse.
40 ;; Peter K. Lee helped fix an issue involving planner-multi.el with
41 ;; the Muse port.
43 ;;; Code:
45 (require 'planner)
46 (require 'timeclock)
47 (require 'easymenu)
48 (require 'advice)
50 (defvar planner-timeclock-current-task nil
51 "Description of current task.")
53 (defalias 'planner-timeclock-in 'planner-task-in-progress)
55 (defadvice timeclock-out (after planner-timeclock activate)
56 "Clear `planner-timeclock-current-task.'"
57 (setq planner-timeclock-current-task nil))
59 (defun planner-timeclock-plan-string (task-info &optional plans)
60 "Return the string for the plan part of the timeclock entry for TASK-INFO."
61 (let ((plan-link (if (featurep 'planner-multi)
62 ;; Remove the date links
63 (planner-multi-make-link
64 (or (planner-multi-filter-links
65 planner-date-regexp
66 (if plans
67 (planner-multi-split plans)
68 (planner-multi-task-link-as-list task-info))
70 (list (planner-task-plan task-info))))
71 (planner-make-link
72 (or plans (planner-task-plan task-info))))))
73 (if plan-link
74 (concat plan-link ": ")
75 "")))
77 (defun planner-timeclock-task-marked (old-status new-status)
78 "Clock out if the task description matches the one clocked in."
79 (cond
80 ((string= new-status "X")
81 (when (and planner-timeclock-current-task
82 (string= (planner-task-description (planner-current-task-info))
83 planner-timeclock-current-task)
84 (timeclock-currently-in-p))
85 (timeclock-out 1)))
86 ((string= new-status "P")
87 (when (and planner-timeclock-current-task
88 (string= (planner-task-description (planner-current-task-info))
89 planner-timeclock-current-task)
90 (timeclock-currently-in-p))
91 (timeclock-out)))
92 ((string= new-status "o")
93 (let* ((task-info (planner-current-task-info))
94 (project (concat
95 (planner-timeclock-plan-string task-info)
96 (planner-task-description task-info))))
97 (if (timeclock-currently-in-p)
98 (timeclock-change nil project)
99 (timeclock-in nil project))
100 (setq planner-timeclock-current-task (planner-task-description task-info)))))
102 (add-hook 'planner-mark-task-hook 'planner-timeclock-task-marked)
104 (defadvice planner-replan-task (around planner-timeclock activate)
105 "Edit the clocked in task as well."
106 (let ((info (planner-current-task-info)))
107 ad-do-it
108 (with-current-buffer (find-file-noselect timeclock-file)
109 (goto-char (point-min))
110 (while (re-search-forward
111 (concat
112 "^. [^ \n]+ [^ \n]+ "
113 "\\(" (regexp-quote (planner-timeclock-plan-string info)) "\\)"
114 (regexp-quote (planner-task-description info)) "$") nil t)
115 (replace-match
116 (save-match-data (planner-timeclock-plan-string nil (ad-get-arg 0)))
117 t t nil 1))
118 (save-buffer)
119 (kill-buffer (current-buffer)))))
121 (defadvice planner-edit-task-description (around planner-timeclock activate)
122 "Update the timelog as well. Warning! Do not have duplicate tasks!"
123 (let ((info (planner-current-task-info)))
124 (when (string= (planner-task-description info) planner-timeclock-current-task)
125 (setq planner-timeclock-current-task (ad-get-arg 0)))
126 ad-do-it
127 (with-current-buffer (find-file-noselect timeclock-file)
128 (goto-char (point-min))
129 (while (re-search-forward
130 (concat
131 "^. [^ \n]+ [^ \n]+ "
132 (regexp-quote (planner-timeclock-plan-string info))
133 "\\("
134 (regexp-quote (planner-task-description info))
135 "\\)$")
136 nil t)
137 (replace-match (ad-get-arg 0) t t nil 1))
138 (save-buffer)
139 (kill-buffer (current-buffer)))))
141 ;;;###autoload
142 (defun planner-colors-timeclock-report-tag (beg end)
143 "Replace the region BEG to END with a timeclock report, colorizing
144 the result."
145 (require 'timeclock)
146 (add-text-properties
147 beg end (list 'display
148 (with-temp-buffer
149 (timeclock-generate-report muse-publishing-p)
150 (buffer-string)))))
152 (defun planner-publish-timeclock-report-tag (beg end)
153 "Replace the region BEG to END with a timeclock report."
154 (require 'timeclock)
155 (delete-region beg end)
156 (timeclock-generate-report muse-publishing-p)
157 (add-text-properties beg (point) '(read-only t)))
159 (add-hook 'muse-colors-markup-tags
160 '("timeclock-report" nil nil nil
161 planner-colors-timeclock-report-tag))
163 (add-hook 'muse-publish-markup-tags
164 '("timeclock-report" nil nil nil
165 planner-publish-timeclock-report-tag))
167 (defun planner-timeclock-task-plan (info)
168 "Return the first plan page associated with INFO."
169 (car (elt info 0)))
171 (defun planner-timeclock-task-plan-as-list (info)
172 "Return all the plan pages associated with INFO."
173 (elt info 0))
175 (defun planner-timeclock-task-description (info)
176 "Return the descrption associated with INFO."
177 (elt info 1))
179 (defun planner-timeclock-task-length (info)
180 "Return the length associated with INFO."
181 (elt info 2))
183 (defun planner-timeclock-task-info (entry)
184 "Parse ENTRY and return a list of the form (plan task length).
185 See `timeclock-log-data' for the format of ENTRY. Note that the
186 project field in `timeclock-log-data' is 'project: task' here."
187 (let ((project (if (stringp entry) entry (timeclock-entry-project entry)))
188 plan task)
189 (when project
190 (with-planner
191 (cond
192 ;; No plan, just the task
193 ((string-match "^\\s-*:\\s-+" project)
194 (setq task (substring project (match-end 0))))
195 ;; Single link
196 ((string-match (concat "^\\(" muse-explicit-link-regexp "\\): ") project)
197 (setq plan (list (match-string 1 project)))
198 (setq task (substring project (match-end 0))))
199 ;; Multi link
200 ((and
201 (featurep 'planner-multi)
202 (string-match
203 (concat "^\\(\\(?:" muse-explicit-link-regexp "\\)"
204 "\\(?:" planner-multi-separator
205 "\\(?:" muse-explicit-link-regexp "\\)\\)*\\): ")
206 project))
207 (setq task (substring project (match-end 0)))
208 (setq plan (planner-multi-split (match-string 1 project))))
209 ;; Nothing whatsoever.
210 (t (setq task project)))
211 (list plan task (unless (stringp entry) (timeclock-entry-length entry)))))))
213 (condition-case nil
214 (let ((map planner-mode-map))
215 (define-key map "\C-c\C-i" 'planner-task-in-progress)
216 (define-key map "\C-c\C-o" 'timeclock-out)
217 (if (featurep 'xemacs)
218 (progn
219 (define-key map (kbd "C-c C-T C-i") 'planner-task-in-progress)
220 (define-key map (kbd "C-c C-T C-o") 'timeclock-out))
221 (define-key map (kbd "C-c C-S-t C-i") 'planner-task-in-progress)
222 (define-key map (kbd "C-c C-S-t C-o") 'timeclock-out)))
223 (error (message "Could not bind timeclock keys in planner mode")))
225 (condition-case nil
226 ;; XEmacs seems to be missing this function in some released
227 ;; versions of XEmacs21.
228 (if (fboundp 'easy-menu-create-menu)
229 (easy-menu-add-item planner-mode-map
230 '("Planner")
231 (easy-menu-create-menu
232 "Timeclock"
233 '(["Clock into a task" planner-timeclock-in]
234 ["Clock out" timeclock-out])))
235 (easy-menu-add-item planner-menu
236 '("Timeclock")
237 ["Clock into a task" planner-timeclock-in t]
238 "Plan")
239 (easy-menu-add-item planner-menu
240 '("Timeclock")
241 ["Clock out" timeclock-out t]
242 "Plan")))
244 (provide 'planner-timeclock)
246 ;;; planner-timeclock.el ends here