diff --git a/ChangeLog b/ChangeLog index f9cf0700c..b12216b1d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2008-03-28 Carsten Dominik + + * lisp/org.el (org-auto-repeat-maybe): Make sure that repeating + dates are pushed into the future, and that the shift is at least + one interval, never 0. + + * lisp/org-agenda.el (org-agenda-deadline-leaders): Allow a + function value for the deadline leader. + (org-agenda-get-deadlines): Deal with new function value. + + * lisp/org.el (org-update-checkbox-count): Fix bug with checkbox + counting. + (org-add-note): New command. + (org-add-log-setup): Renamed from `org-add-log-maybe'. + (org-log-note-headings): New entry for plain notes (i.e. notes not + related to state changes or clocking). + 2008-03-27 Carsten Dominik * org-agenda.el: New file, split out from org.el. diff --git a/Makefile b/Makefile index 0f793b09c..ca78f9f1e 100644 --- a/Makefile +++ b/Makefile @@ -225,14 +225,18 @@ relup: make upload_release make upload_manual -clean: +cleanelc: rm -f $(ELCFILES) +cleandoc: (cd doc; rm -f org.pdf org org.html orgcard.pdf orgcard.ps) - rm -f *~ */*~ */*/*~ (cd doc; rm -f *.aux *.cp *.cps *.dvi *.fn *.fns *.ky *.kys *.pg *.pgs) (cd doc; rm -f *.toc *.tp *.tps *.vr *.vrs *.log *.html *.ps) (cd doc; rm -f orgcard_letter.tex orgcard_letter.pdf) - rm -rf doc/manual + (cd doc; rm -rf manual) +clean: + make cleanelc + make cleandoc + rm -f *~ */*~ */*/*~ rm -rf RELEASEDIR .el.elc: diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el index 36a45e8fc..69446ebbb 100644 --- a/lisp/org-agenda.el +++ b/lisp/org-agenda.el @@ -31,13 +31,13 @@ ;;; Code: +(require 'org) (eval-when-compile -; (require 'cl) -; (require 'gnus-sum) (require 'calendar)) (declare-function add-to-diary-list "diary-lib" (date string specifier &optional marker globcolor literal)) +(declare-function calendar-absolute-from-iso "cal-iso" (date)) (declare-function calendar-astro-date-string "cal-julian" (&optional date)) (declare-function calendar-bahai-date-string "cal-bahai" (&optional date)) (declare-function calendar-check-holidays "holidays" (date)) @@ -821,7 +821,9 @@ is (was)." :group 'org-agenda-line-format :type '(list (string :tag "Deadline today ") - (string :tag "Deadline relative"))) + (choice :tag "Deadline relative" + (string :tag "Format string") + (function)))) (defcustom org-agenda-remove-times-when-in-prefix t "Non-nil means, remove duplicate time specifications in agenda items. @@ -999,6 +1001,8 @@ The following commands are available: (org-defkey org-agenda-mode-map "w" 'org-agenda-week-view) (org-defkey org-agenda-mode-map "m" 'org-agenda-month-view) (org-defkey org-agenda-mode-map "y" 'org-agenda-year-view) +(org-defkey org-agenda-mode-map "\C-c\C-z" 'org-agenda-add-note) +(org-defkey org-agenda-mode-map "z" 'org-agenda-add-note) (org-defkey org-agenda-mode-map [(shift right)] 'org-agenda-date-later) (org-defkey org-agenda-mode-map [(shift left)] 'org-agenda-date-earlier) (org-defkey org-agenda-mode-map [?\C-c ?\C-x (right)] 'org-agenda-date-later) @@ -1084,6 +1088,7 @@ The following commands are available: ["Cycle TODO" org-agenda-todo t] ["Archive subtree" org-agenda-archive t] ["Delete subtree" org-agenda-kill t] + ["Add note" org-agenda-add-note t] "--" ["Goto Today" org-agenda-goto-today (org-agenda-check-type nil 'agenda 'timeline)] ["Next Dates" org-agenda-later (org-agenda-check-type nil 'agenda)] @@ -3237,8 +3242,10 @@ the documentation of `org-diary'." (setq txt (org-format-agenda-item (if (= diff 0) (car org-agenda-deadline-leaders) - (format (nth 1 org-agenda-deadline-leaders) - diff)) + (if (functionp (nth 1 org-agenda-deadline-leaders)) + (funcall (nth 1 org-agenda-deadline-leaders) diff date) + (format (nth 1 org-agenda-deadline-leaders) + diff))) head category tags timestr)))) (setq txt org-agenda-no-heading-message)) (when txt @@ -3396,46 +3403,6 @@ FRACTION is what fraction of the head-warning time has passed." ;;; Agenda presentation and sorting -(defconst org-plain-time-of-day-regexp - (concat - "\\(\\<[012]?[0-9]" - "\\(\\(:\\([0-5][0-9]\\([AaPp][Mm]\\)?\\)\\)\\|\\([AaPp][Mm]\\)\\)\\>\\)" - "\\(--?" - "\\(\\<[012]?[0-9]" - "\\(\\(:\\([0-5][0-9]\\([AaPp][Mm]\\)?\\)\\)\\|\\([AaPp][Mm]\\)\\)\\>\\)" - "\\)?") - "Regular expression to match a plain time or time range. -Examples: 11:45 or 8am-13:15 or 2:45-2:45pm. After a match, the following -groups carry important information: -0 the full match -1 the first time, range or not -8 the second time, if it is a range.") - -(defconst org-plain-time-extension-regexp - (concat - "\\(\\<[012]?[0-9]" - "\\(\\(:\\([0-5][0-9]\\([AaPp][Mm]\\)?\\)\\)\\|\\([AaPp][Mm]\\)\\)\\>\\)" - "\\+\\([0-9]+\\)\\(:\\([0-5][0-9]\\)\\)?") - "Regular expression to match a time range like 13:30+2:10 = 13:30-15:40. -Examples: 11:45 or 8am-13:15 or 2:45-2:45pm. After a match, the following -groups carry important information: -0 the full match -7 hours of duration -9 minutes of duration") - -(defconst org-stamp-time-of-day-regexp - (concat - "<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} +\\sw+ +\\)" - "\\([012][0-9]:[0-5][0-9]\\(-\\([012][0-9]:[0-5][0-9]\\)\\)?[^\n\r>]*?\\)>" - "\\(--?" - "<\\1\\([012][0-9]:[0-5][0-9]\\)>\\)?") - "Regular expression to match a timestamp time or time range. -After a match, the following groups carry important information: -0 the full match -1 date plus weekday, for backreferencing to make sure both times on same day -2 the first time, range or not -4 the second time, if it is a range.") - (defvar org-prefix-has-time nil "A flag, set by `org-compile-prefix-format'. The flag is set if the currently compiled format contains a `%t'.") @@ -4433,6 +4400,25 @@ the same tree node, and the headline of the tree node in the Org-mode file." (org-agenda-change-all-lines newhead hdmarker 'fixface)) (move-to-column col)))) +(defun org-agenda-add-note (&optional arg) + "Add a time-stamped note to the entry at point." + (interactive "P") + (org-agenda-check-no-diary) + (let* ((marker (or (get-text-property (point) 'org-marker) + (org-agenda-error))) + (buffer (marker-buffer marker)) + (pos (marker-position marker)) + (hdmarker (get-text-property (point) 'org-hd-marker)) + (inhibit-read-only t)) + (with-current-buffer buffer + (widen) + (goto-char pos) + (org-show-context 'agenda) + (save-excursion + (and (outline-next-heading) + (org-flag-heading nil))) ; show the next heading + (org-add-note)))) + (defun org-agenda-change-all-lines (newhead hdmarker &optional fixface) "Change all lines in the agenda buffer which match HDMARKER. The new content of the line will be NEWHEAD (as modified by diff --git a/lisp/org.el b/lisp/org.el index aa0a09949..3caa123d2 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -1530,10 +1530,11 @@ When nil, only the date will be recorded." :type 'boolean) (defcustom org-log-note-headings - '((done . "CLOSING NOTE %t") + '((done . "CLOSING NOTE %t") (state . "State %-12s %t") + (note . "Note taken on %t") (clock-out . "")) - "Headings for notes added when clocking out or closing TODO items. + "Headings for notes added to entries. The value is an alist, with the car being a symbol indicating the note context, and the cdr is the heading to be used. The heading may also be the empty string. @@ -1548,8 +1549,12 @@ empty string. (cons (const :tag "Heading when changing todo state (todo sequence only)" state) string) + (cons (const :tag "Heading when just taking a note" note) string) (cons (const :tag "Heading when clocking out" clock-out) string))) +(unless (assq 'note org-log-note-headings) + (push '(note . "%t") org-log-note-headings)) + (defcustom org-log-states-order-reversed t "Non-nil means, the latest state change note will be directly after heading. When nil, the notes will be orderer according to time." @@ -2914,6 +2919,46 @@ Also put tags into group 4 if tags are present.") "Matches a line with planning or clock info.") (make-variable-buffer-local 'org-planning-or-clock-line-re) +(defconst org-plain-time-of-day-regexp + (concat + "\\(\\<[012]?[0-9]" + "\\(\\(:\\([0-5][0-9]\\([AaPp][Mm]\\)?\\)\\)\\|\\([AaPp][Mm]\\)\\)\\>\\)" + "\\(--?" + "\\(\\<[012]?[0-9]" + "\\(\\(:\\([0-5][0-9]\\([AaPp][Mm]\\)?\\)\\)\\|\\([AaPp][Mm]\\)\\)\\>\\)" + "\\)?") + "Regular expression to match a plain time or time range. +Examples: 11:45 or 8am-13:15 or 2:45-2:45pm. After a match, the following +groups carry important information: +0 the full match +1 the first time, range or not +8 the second time, if it is a range.") + +(defconst org-plain-time-extension-regexp + (concat + "\\(\\<[012]?[0-9]" + "\\(\\(:\\([0-5][0-9]\\([AaPp][Mm]\\)?\\)\\)\\|\\([AaPp][Mm]\\)\\)\\>\\)" + "\\+\\([0-9]+\\)\\(:\\([0-5][0-9]\\)\\)?") + "Regular expression to match a time range like 13:30+2:10 = 13:30-15:40. +Examples: 11:45 or 8am-13:15 or 2:45-2:45pm. After a match, the following +groups carry important information: +0 the full match +7 hours of duration +9 minutes of duration") + +(defconst org-stamp-time-of-day-regexp + (concat + "<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} +\\sw+ +\\)" + "\\([012][0-9]:[0-5][0-9]\\(-\\([012][0-9]:[0-5][0-9]\\)\\)?[^\n\r>]*?\\)>" + "\\(--?" + "<\\1\\([012][0-9]:[0-5][0-9]\\)>\\)?") + "Regular expression to match a timestamp time or time range. +After a match, the following groups carry important information: +0 the full match +1 date plus weekday, for backreferencing to make sure both times on same day +2 the first time, range or not +4 the second time, if it is a range.") + (defconst org-startup-options '(("fold" org-startup-folded t) ("overview" org-startup-folded t) @@ -5512,7 +5557,7 @@ the whole buffer." (org-beginning-of-item) (setq curr-ind (org-get-indentation)) (setq next-ind curr-ind) - (while (= curr-ind next-ind) + (while (and (org-at-item-p) (= curr-ind next-ind)) (save-excursion (end-of-line) (setq eline (point))) (if (re-search-forward re-box eline t) (if (member (match-string 2) '("[ ]" "[-]")) @@ -8450,10 +8495,10 @@ For calling through lisp, arg is also interpreted in the following way: ;; It is now done, and it was not done before (org-add-planning-info 'closed (org-current-time)) (if (and (not dolog) (eq 'note org-log-done)) - (org-add-log-maybe 'done state 'findpos 'note))) + (org-add-log-setup 'done state 'findpos 'note))) (when (and state dolog) ;; This is a non-nil state, and we need to log it - (org-add-log-maybe 'state state 'findpos dolog))) + (org-add-log-setup 'state state 'findpos dolog))) ;; Fixup tag positioning (and org-auto-align-tags (not org-setting-tags) (org-set-tags nil t)) (run-hooks 'org-after-todo-state-change-hook) @@ -8634,7 +8679,7 @@ This function is run automatically after each state change to a DONE state." (default-value 'post-command-hook))) (eq org-log-note-purpose 'done))) ;; Make sure a note is taken; - (org-add-log-maybe 'state (or done-word (car org-done-keywords)) + (org-add-log-setup 'state (or done-word (car org-done-keywords)) 'findpos org-log-repeat)) (org-back-to-heading t) (org-add-planning-info nil nil 'closed) @@ -8661,12 +8706,12 @@ This function is run automatically after each state change to a DONE state." (- (time-to-days (current-time)) (time-to-days time)) 'day)) ((equal (match-string 1 ts) "+") - (while (< (time-to-days time) (time-to-days (current-time))) + (while (or (= nshift 0) + (<= (time-to-days time) (time-to-days (current-time)))) (when (= (incf nshift) nshiftmax) (or (y-or-n-p (message "%d repeater intervals were not enough to shift date past today. Continue? " nshift)) (error "Abort"))) (org-timestamp-change n (cdr (assoc what whata))) - (sit-for .0001) ;; so we can watch the date shifting (org-at-timestamp-p t) (setq ts (match-string 1)) (setq time (save-match-data (org-time-string-to-time ts)))) @@ -8835,7 +8880,13 @@ be removed." "Message to be displayed after a log note has been stored. The auto-repeater uses this.") -(defun org-add-log-maybe (&optional purpose state findpos how) +(defun org-add-note () + "Add a note to the current entry. +This is done in the same way as adding a state change note." + (interactive) + (org-add-log-setup 'note nil t nil)) + +(defun org-add-log-setup (&optional purpose state findpos how) "Set up the post command hook to take a note. If this is about to TODO state change, the new state is expected in STATE. When FINDPOS is non-nil, find the correct position for the note in @@ -8885,7 +8936,9 @@ the current entry. If not, assume that it can be inserted at point." ((eq org-log-note-purpose 'done) "closed todo item") ((eq org-log-note-purpose 'state) (format "state change to \"%s\"" org-log-note-state)) - (t (error "This should not happen"))))) + ((eq org-log-note-purpose 'note) + "this entry") + (t (error "This should not happen"))))) (org-set-local 'org-finish-function 'org-store-log-note))) (defvar org-note-abort nil) ; dynamically scoped @@ -12399,7 +12452,7 @@ in the timestamp determines what will be changed." (setq time0 (org-parse-time-string ts)) (when (and (eq org-ts-what 'minute) (eq current-prefix-arg nil)) - (setq n (* dm (org-no-warnings (signum n)))) + (setq n (* dm (cond ((> n 0) 1) ((< n 0) -1) (t 0)))) (when (not (= 0 (setq rem (% (nth 1 time0) dm)))) (setcar (cdr time0) (+ (nth 1 time0) (if (> n 0) (- rem) (- dm rem)))))) @@ -12662,7 +12715,7 @@ If there is no running clock, throw an error, unless FAIL-QUIETLY is set." (insert " => " (format "%2d:%02d" h m)) (move-marker org-clock-marker nil) (when org-log-note-clock-out - (org-add-log-maybe 'clock-out)) + (org-add-log-setup 'clock-out)) (when org-mode-line-timer (cancel-timer org-mode-line-timer) (setq org-mode-line-timer nil)) @@ -13771,7 +13824,7 @@ The images can be removed again with \\[org-ctrl-c-ctrl-c]." (org-defkey org-mode-map "\C-c\C-o" 'org-open-at-point) (org-defkey org-mode-map "\C-c%" 'org-mark-ring-push) (org-defkey org-mode-map "\C-c&" 'org-mark-ring-goto) -(org-defkey org-mode-map "\C-c\C-z" 'org-time-stamp) ; Alternative binding +(org-defkey org-mode-map "\C-c\C-z" 'org-add-note) ; Alternative binding (org-defkey org-mode-map "\C-c." 'org-time-stamp) ; Minor-mode reserved (org-defkey org-mode-map "\C-c!" 'org-time-stamp-inactive) ; Minor-mode r. (org-defkey org-mode-map "\C-c," 'org-priority) ; Minor-mode reserved