# HG changeset patch # User Carsten Dominik # Date 1116588288 0 # Node ID 153ddc124932f56835a8feb8d8192588a09ee765 # Parent bea5728f69fafe504f468fc39231cbef424f7877 (org-agenda-toggle-time-grid): New command. (org-agenda-use-time-grid, org-agenda-time-grid): New options. (org-agenda-add-time-grid-maybe): New function. (org-agenda): Call `org-agenda-add-time-grid-maybe'. (org-table-create): `dotimes' instead of `mapcar'. (org-xor): Simplified implementation. (org-agenda): `inhibit-redisplay' turned on. (org-agenda-change-all-lines): Use `org-format-agenda-item' to get a consistent line after a state change. (org-agenda-remove-times-when-in-prefix): New option. (org-prefix-has-time): New variable. (org-parse-time-string): Optional argument NODEFAULT. (org-format-agenda-item): Parse items for time-of-day specifications and move these into the prefix if possible. (org-agenda-priority): Get current heading, not previous heading during agenda remote editing. diff -r bea5728f69fa -r 153ddc124932 lisp/textmodes/org.el --- a/lisp/textmodes/org.el Fri May 20 11:18:22 2005 +0000 +++ b/lisp/textmodes/org.el Fri May 20 11:24:48 2005 +0000 @@ -5,7 +5,7 @@ ;; Author: Carsten Dominik ;; Keywords: outlines, hypermedia, calendar ;; Homepage: http://www.astro.uva.nl/~dominik/Tools/org/ -;; Version: 3.08 +;; Version: 3.09 ;; ;; This file is part of GNU Emacs. ;; @@ -31,27 +31,30 @@ ;; project planning with a fast and effective plain-text system. ;; ;; Org-mode develops organizational tasks around a NOTES file that contains -;; information about projects as plain text. Org-mode is implemented on -;; top of outline-mode - ideal to keep the content of large files well -;; structured. It supports ToDo items, deadlines and time stamps, which -;; magically appear in the diary listing of the Emacs calendar. Tables are -;; easily created with a built-in table editor. Plain text URL-like links -;; connect to websites, emails (VM,RMAIL,WANDERLUST), Usenet messages (Gnus), -;; BBDB entries, and any files related to the projects. For printing and -;; sharing of notes, an Org-mode file (or a part of it) can be exported as -;; a structured ASCII file, or as HTML. +;; information about projects as plain text. Org-mode is implemented on top +;; of outline-mode - ideal to keep the content of large files well structured. +;; It supports ToDo items, deadlines and time stamps, which can be extracted +;; to create a daily/weekly agenda that also integrates the diary of the Emacs +;; calendar. Tables are easily created with a built-in table editor. Plain +;; text URL-like links connect to websites, emails (VM, RMAIL, WANDERLUST), +;; Usenet messages (Gnus), BBDB entries, and any files related to the +;; projects. For printing and sharing of notes, an Org-mode file (or a part +;; of it) can be exported as a structured ASCII file, or as HTML. ;; ;; Installation ;; ------------ -;; The instruction below assume that you have downloaded Org-mode from the -;; web. If Org-mode is part of the Emacs distribution or an XEmacs package, -;; you only need to add to .emacs the last three lines of Lisp code listed -;; below, i.e. the `auto-mode-alist' modification and the global key bindings. +;; If Org-mode is part of the Emacs distribution or an XEmacs package, you +;; only need to copy the following lines to your .emacs file. The last two +;; lines define *global* keys for the commands `org-store-link' and +;; `org-agenda' - please choose suitable keys yourself. ;; -;; Byte-compile org.el and put it on your load path. Then copy the -;; following lines into .emacs. The last two lines define *global* -;; keys for the commands `org-store-link' and `org-agenda' - please -;; choose suitable keys yourself. +;; (add-to-list 'auto-mode-alist '("\\.org$" . org-mode)) +;; (define-key global-map "\C-cl" 'org-store-link) +;; (define-key global-map "\C-ca" 'org-agenda) +;; +;; If you have downloaded Org-mode from the Web, you must byte-compile +;; org.el and put it on your load path. In addition to the Emacs Lisp +;; lines above, you also need to add the following lines to .emacs: ;; ;; (autoload 'org-mode "org" "Org mode" t) ;; (autoload 'org-diary "org" "Diary entries from Org mode") @@ -59,12 +62,9 @@ ;; (autoload 'org-store-link "org" "Store a link to the current location" t) ;; (autoload 'orgtbl-mode "org" "Org tables as a minor mode" t) ;; (autoload 'turn-on-orgtbl "org" "Org tables as a minor mode") -;; (add-to-list 'auto-mode-alist '("\\.org$" . org-mode)) -;; (define-key global-map "\C-cl" 'org-store-link) -;; (define-key global-map "\C-ca" 'org-agenda) ;; -;; This will put all files with extension ".org" into Org-mode. As an -;; alternative, make the first line of a file look like this: +;; This setup will put all files with extension ".org" into Org-mode. As +;; an alternative, make the first line of a file look like this: ;; ;; MY PROJECTS -*- mode: org; -*- ;; @@ -73,12 +73,18 @@ ;; ;; Documentation ;; ------------- -;; The documentation of Org-mode can be found in the TeXInfo file. -;; The distribution also contains a PDF version of it. At the homepage -;; of Org-mode, you can read the same text online as HTML. +;; The documentation of Org-mode can be found in the TeXInfo file. The +;; distribution also contains a PDF version of it. At the homepage of +;; Org-mode, you can read the same text online as HTML. There is also an +;; excellent reference card made by Philip Rooke. ;; ;; Changes: ;; ------- +;; Version 3.09 +;; - Time-of-day specifications in agenda are extracted and placed +;; into the prefix. Timed entries can be placed into a time grid for +;; day. +;; ;; Version 3.08 ;; - "|" no longer allowed as part of a link, to allow links in tables. ;; - The prefix of items in the agenda buffer can be configured. @@ -136,7 +142,7 @@ ;; warnings about upcoming deadlines/overdue scheduled items. ;; That functionality is now limited to the (multifile) agenda. ;; - When reading a date, the calendar can be manipulated with keys. -;; - Link support for RMAIL and Wanderlust (from planner.el, untested) +;; - Link support for RMAIL and Wanderlust (from planner.el, untested). ;; - Minor bug fixes and documentation improvements. ;;; Code: @@ -148,16 +154,15 @@ ;;; Customization variables -(defvar org-version "3.08" +(defvar org-version "3.09" "The version number of the file org.el.") (defun org-version () (interactive) (message "Org-mode version %s" org-version)) -;; The following two constants are for compatibility with different -;; Emacs versions (Emacs versus XEmacs) and with different versions of -;; outline.el. All the compatibility code in org.el is based on these two -;; constants. +;; The following two constants are for compatibility with different Emacs +;; versions (Emacs versus XEmacs) and with different versions of outline.el. +;; The compatibility code in org.el is based on these two constants. (defconst org-xemacs-p (featurep 'xemacs) "Are we running xemacs?") (defconst org-noutline-p (featurep 'noutline) @@ -570,7 +575,7 @@ the sequence given in `org-agenda-files'. Within each category sort by priority. -Leaving out the `category-keep' would mean that items will be sorted across +Leaving out `category-keep' would mean that items will be sorted across categories by priority." :group 'org-agenda :type '(repeat @@ -583,7 +588,7 @@ (const priority-up) (const priority-down)))) -(defcustom org-agenda-prefix-format " %-12:c% s" +(defcustom org-agenda-prefix-format " %-12:c%?-12t% s" "Format specification for the prefix of items in the agenda buffer. This format works similar to a printf format, with the following meaning: @@ -593,22 +598,39 @@ format HH:MM %s Scheduling/Deadline information, a short string -In addition to the normal printf field modifiers like field width and -padding instructions, in this format you can also add an additional -punctuation or whitespace character just before the final format letter. -This character will be appended to the field value if the value is not -empty. For example, the format \"%-12:c\" leads to \"Diary: \" if -the category is \"Diary\". If the category were be empty, no additional -colon would be interted. - -Including `%t' in the format string leads to a double time specification -because the headline/diary item will contain the time specification as -well. However, using `%t' in the format will result in a canonical 24 -hour time specification at a consistent position in the prefix, while the -time specification in the headline/diary item may be at any position and in -various formats. -Example: - (setq org-agenda-prefix-format \" %-12:c% t% s\")" +All specifiers work basically like the standard `%s' of printf, but may +contain two additional characters: A question mark just after the `%' and +a whitespace/punctuation character just before the final letter. + +If the first character after `%' is a question mark, the entire field +will only be included if the corresponding value applies to the +current entry. This is useful for fields which should have fixed +width when present, but zero width when absent. For example, +\"%?-12t\" will result in a 12 character time field if a time of the +day is specified, but will completely disappear in entries which do +not contain a time. + +If there is punctuation or whitespace character just before the final +format letter, this character will be appended to the field value if +the value is not empty. For example, the format \"%-12:c\" leads to +\"Diary: \" if the category is \"Diary\". If the category were be +empty, no additional colon would be interted. + +The default value of this option is \" %-12:c%?-12t% s\", meaning: +- Indent the line with two space characters +- Give the category in a 12 chars wide field, padded with whitespace on + the right (because of `-'). Append a colon if there is a category + (because of `:'). +- If there is a time-of-day, put it into a 12 chars wide field. If no + time, don't put in an empty field, just skip it (because of '?'). +- Finally, put the scheduling information and append a whitespace. + +As another example, if you don't want the time-of-day of entries in +the prefix, you could use: + + (setq org-agenda-prefix-format \" %-11:c% s\") + +See also the variable `org-agenda-remove-times-when-in-prefix'." :type 'string :group 'org-agenda) @@ -618,13 +640,64 @@ :group 'org-agenda) (defvar org-prefix-format-compiled nil - "The compiled version of `org-???-prefix-format'.") + "The compiled version of the most recently used prefix format. +Depending on which command was used last, this may be the compiled version +of `org-agenda-prefix-format' or `org-timeline-prefix-format'.") + +(defcustom org-agenda-use-time-grid t + "Non-nil means, show a time grid in the agenda schedule. +A time grid is a set of lines for specific times (like every two hours between +8:00 and 20:00. The items scheduled for a day at specific times are +sorted in between these lines. +For deails about when the grid will be shown, and what it will look like, see +the variable `org-agenda-time-grid'." + :group 'org-agenda + :type 'boolean) + +(defcustom org-agenda-time-grid + '((daily today require-timed) + "----------------" + (800 1000 1200 1400 1600 1800 2000)) + + "FIXME: document" + :group 'org-agenda + :type + '(list + (set :greedy t :tag "Grid Display Options" + (const :tag "Show grid in single day agenda display" daily) + (const :tag "Show grid in weekly agenda display" weekly) + (const :tag "Always show grid for today" today) + (const :tag "Show grid only if any timed entries are present" + require-timed) + (const :tag "Skip grid times already present in an entry" + remove-match)) + (string :tag "Grid String") + (repeat :tag "Grid Times" (integer :tag "Time")))) + +(defcustom org-agenda-remove-times-when-in-prefix t + "Non-nil means, remove duplicate time specifications in agenda items. +When the format `org-agenda-prefix-format' contains a `%t' specifier, a +time-of-day specification in a headline or diary entry is extracted and +placed into the prefix. If this option is non-nil, the original specification +\(a timestamp or -range, or just a plain time(range) specification like +11:30-4pm) will be removed for agenda display. This makes the agenda less +cluttered. +The option can be t or nil. It may also be the symbol `beg', indicating +that the time should only be removed what it is located at the beginning of +the headline/diary entry." + :group 'org-agenda + :type '(choice + (const :tag "Always" t) + (const :tag "Never" nil) + (const :tag "When at beginning of entry" beg))) (defcustom org-sort-agenda-notime-is-late t "Non-nil means, items without time are considered late. This is only relevant for sorting. When t, items which have no explicit time like 15:30 will be considered as 24:01, i.e. later than any items which -do have a time. When nil, the default time is before 0:00." +do have a time. When nil, the default time is before 0:00. You can use this +option to decide if the schedule for today should come before or after timeless +agenda entries." :group 'org-agenda :type 'boolean) @@ -1370,6 +1443,14 @@ "Face used for tables." :group 'org-faces) +(defface org-time-grid-face ;; font-lock-variable-name-face + '((((type tty) (class color)) (:foreground "yellow" :weight light)) + (((class color) (background light)) (:foreground "DarkGoldenrod")) + (((class color) (background dark)) (:foreground "LightGoldenrod")) + (t (:bold t :italic t))) + "Face used for level 2 headlines." + :group 'org-faces) + (defvar org-level-faces '( org-level-1-face @@ -1496,7 +1577,7 @@ (save-excursion (goto-char (point-min)) (insert " -*- mode: org -*-\n\n"))) - (run-hooks 'org-mode-hook) ;FIXME: Should be run-mode-hooks. + (run-hooks 'org-mode-hook) (unless org-inhibit-startup (if org-startup-with-deadline-check (call-interactively 'org-check-deadlines) @@ -1565,7 +1646,7 @@ (save-excursion (org-back-to-heading t) (- (match-end 0) (match-beginning 0)))) - + (defvar org-font-lock-keywords nil) (defun org-set-font-lock-defaults () @@ -2844,13 +2925,17 @@ (defun org-time-string-to-time (s) (apply 'encode-time (org-parse-time-string s))) -(defun org-parse-time-string (s) +(defun org-parse-time-string (s &optional nodefault) "Parse the standard Org-mode time string. -This should be a lot faster than the normal `parse-time-string'." +This should be a lot faster than the normal `parse-time-string'. +If time is not given, defaults to 0:00. However, with optional NODEFAULT, +hour and minute fields will be nil if not given." (if (string-match org-ts-regexp1 s) (list 0 - (string-to-number (or (match-string 8 s) "0")) - (string-to-number (or (match-string 7 s) "0")) + (if (or (match-beginning 8) (not nodefault)) + (string-to-number (or (match-string 8 s) "0"))) + (if (or (match-beginning 7) (not nodefault)) + (string-to-number (or (match-string 7 s) "0"))) (string-to-number (match-string 4 s)) (string-to-number (match-string 3 s)) (string-to-number (match-string 2 s)) @@ -3056,6 +3141,7 @@ (define-key org-agenda-mode-map "f" 'org-agenda-follow-mode) (define-key org-agenda-mode-map "d" 'org-agenda-toggle-diary) +(define-key org-agenda-mode-map "g" 'org-agenda-toggle-time-grid) (define-key org-agenda-mode-map "r" 'org-agenda-redo) (define-key org-agenda-mode-map "q" 'org-agenda-quit) (define-key org-agenda-mode-map "x" 'org-agenda-exit) @@ -3115,7 +3201,7 @@ ["Decrease Priority" org-agenda-priority-down t] ["Show Priority" org-agenda-show-priority t]) "--" - ["Rebuild" org-agenda-redo t] + ["Rebuild buffer" org-agenda-redo t] ["Goto Today" org-agenda-goto-today t] ["Next Dates" org-agenda-later (local-variable-p 'starting-day)] ["Previous Dates" org-agenda-earlier (local-variable-p 'starting-day)] @@ -3124,6 +3210,8 @@ (local-variable-p 'starting-day)] ["Include Diary" org-agenda-toggle-diary :style toggle :selected org-agenda-include-diary :active t] + ["Use Time Grid" org-agenda-toggle-time-grid + :style toggle :selected org-agenda-use-time-grid :active t] "--" ["New Diary Entry" org-agenda-diary-entry t] ("Calendar Commands" @@ -3294,11 +3382,13 @@ (d (- nt n1))) (- sd (+ (if (< d 0) 7 0) d))))) (day-numbers (list start)) - s e rtn rtnall file date d start-pos end-pos) + (inhibit-redisplay t) + s e rtn rtnall file date d start-pos end-pos todayp nd) (setq org-agenda-redo-command (list 'org-agenda include-all start-day ndays)) ;; Make the list of days - (setq ndays (or ndays org-agenda-ndays)) + (setq ndays (or ndays org-agenda-ndays) + nd ndays) (while (> ndays 1) (push (1+ (car day-numbers)) day-numbers) (setq ndays (1- ndays))) @@ -3324,11 +3414,15 @@ rtn (org-agenda-get-day-entries file date :todo)) (setq rtnall (append rtnall rtn)))) - (if rtnall (insert (org-finalize-agenda-entries rtnall) "\n"))) + (when rtnall + (insert "ALL CURRENTLY OPEN TODO ITEMS:\n") + (add-text-properties (point-min) (1- (point)) + (list 'face 'org-link-face)) + (insert (org-finalize-agenda-entries rtnall) "\n"))) (while (setq d (pop day-numbers)) (setq date (calendar-gregorian-from-absolute d) s (point)) - (if (or (= d today) + (if (or (setq todayp (= d today)) (and (not start-pos) (= d sd))) (setq start-pos (point)) (if (and start-pos (not end-pos)) @@ -3347,14 +3441,18 @@ (setq rtnall (append rtnall rtn)))) (if (or rtnall org-agenda-show-all-dates) (progn - (insert (format "%-9s %2d %-9s %4d\n" + (insert (format "%-9s %2d %s %4d\n" (calendar-day-name date) (extract-calendar-day date) (calendar-month-name (extract-calendar-month date)) (extract-calendar-year date))) (put-text-property s (1- (point)) 'face 'org-link-face) - (if rtnall (insert (org-finalize-agenda-entries rtnall) "\n")) + (if rtnall (insert + (org-finalize-agenda-entries ;; FIXME: condition needed + (org-agenda-add-time-grid-maybe + rtnall nd todayp)) + "\n")) (put-text-property s (1- (point)) 'day d)))) (goto-char (point-min)) (setq buffer-read-only t) @@ -3502,6 +3600,15 @@ (message "Diary inclusion turned %s" (if org-agenda-include-diary "on" "off"))) +(defun org-agenda-toggle-time-grid () + "Toggle follow mode in an agenda buffer." + (interactive) + (setq org-agenda-use-time-grid (not org-agenda-use-time-grid)) + (org-agenda-redo) + (org-agenda-set-mode-name) + (message "Time-grid turned %s" + (if org-agenda-use-time-grid "on" "off"))) + (defun org-agenda-set-mode-name () "Set the mode name to indicate all the small mode settings." (setq mode-name @@ -3509,7 +3616,8 @@ (if (equal org-agenda-ndays 1) " Day" "") (if (equal org-agenda-ndays 7) " Week" "") (if org-agenda-follow-mode " Follow" "") - (if org-agenda-include-diary " Diary" ""))) + (if org-agenda-include-diary " Diary" "") + (if org-agenda-use-time-grid " Grid" ""))) (force-mode-line-update)) (defun org-agenda-post-command-hook () @@ -3524,7 +3632,7 @@ "Get the (Emacs Calendar) diary entries for DATE." (let* ((fancy-diary-buffer "*temporary-fancy-diary-buffer*") (diary-display-hook '(fancy-diary-display)) - (list-diary-entries-hook + (list-diary-entries-hook (cons 'org-diary-default-entry list-diary-entries-hook)) entries (org-disable-diary t)) @@ -3551,7 +3659,7 @@ (setq entries (mapcar (lambda (x) - (setq x (org-format-agenda-item "" x "Diary")) + (setq x (org-format-agenda-item "" x "Diary" 'time)) ;; Extend the text properties to the beginning of the line (add-text-properties 0 (length x) @@ -3764,7 +3872,7 @@ arg results rtn) (if (not buffer) ;; If file does not exist, make sure an error message ends up in diary - (format "ORG-AGENDA-ERROR: No such org-file %s" file) + (list (format "ORG-AGENDA-ERROR: No such org-file %s" file)) (with-current-buffer buffer (unless (eq major-mode 'org-mode) (error "Agenda file %s is not in `org-mode'" file)) @@ -3796,8 +3904,8 @@ ((and (eq arg :deadline) (equal date (calendar-current-date))) (setq rtn (org-agenda-get-deadlines)) - (setq results (append results rtn)))))))))) - results)) + (setq results (append results rtn)))))))) + results)))) (defun org-entry-is-done-p () "Is the current entry marked DONE?" @@ -3876,7 +3984,7 @@ (list 0 0 0 (nth 1 date) (car date) (nth 2 date)))) 0 11))) marker hdmarker deadlinep scheduledp donep tmp priority - ee txt) + ee txt timestr) (goto-char (point-min)) (while (re-search-forward regexp nil t) (if (not (save-match-data (org-at-date-range-p))) @@ -3886,9 +3994,13 @@ (- (match-beginning 0) org-ds-keyword-length)) (match-beginning 0)) + timestr (buffer-substring (match-beginning 0) (point-at-eol)) deadlinep (string-match org-deadline-regexp tmp) scheduledp (string-match org-scheduled-regexp tmp) donep (org-entry-is-done-p)) + (if (string-match ">" timestr) + ;; substring should only run to end of time stamp + (setq timestr (substring timestr 0 (match-end 0)))) (save-excursion (if (re-search-backward "\\(^\\|\r\\)\\*+" nil t) (progn @@ -3899,7 +4011,7 @@ (format "%s%s" (if deadlinep "Deadline: " "") (if scheduledp "Scheduled: " "")) - (match-string 1)))) + (match-string 1) nil timestr))) (setq txt org-agenda-no-heading-message)) (setq priority (org-get-priority txt)) (add-text-properties @@ -4044,10 +4156,11 @@ (abbreviate-file-name (buffer-file-name))))) (regexp org-tr-regexp) (d0 (calendar-absolute-from-gregorian date)) - marker hdmarker ee txt d1 d2 s1 s2) + marker hdmarker ee txt d1 d2 s1 s2 timestr) (goto-char (point-min)) (while (re-search-forward regexp nil t) - (setq s1 (match-string 1) + (setq timestr (match-string 0) + s1 (match-string 1) s2 (match-string 2) d1 (time-to-days (org-time-string-to-time s1)) d2 (time-to-days (org-time-string-to-time s2))) @@ -4062,9 +4175,9 @@ (goto-char (match-end 1)) (looking-at "\\*+[ \t]*\\([^\r\n]+\\)") (setq txt (org-format-agenda-item - (format "(%d/%d): " + (format (if (= d1 d2) "" "(%d/%d): ") (1+ (- d0 d1)) (1+ (- d2 d1))) - (match-string 1)))) + (match-string 1) nil (if (= d0 d1) timestr)))) (setq txt org-agenda-no-heading-message)) (add-text-properties 0 (length txt) (append (list 'org-marker marker @@ -4077,66 +4190,187 @@ ;; Sort the entries by expiration date. (nreverse ee))) -(defun org-format-agenda-item (prefix txt &optional category) + + +(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-stamp-time-of-day-regexp + (concat + "<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} +[a-zA-Z]+ +\\)" + "\\([012][0-9]:[0-5][0-9]\\)>" + "\\(--?" + "<\\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'.") + +(defun org-format-agenda-item (extra txt &optional category dotime noprefix) "Format TXT to be inserted into the agenda buffer. -In particular, this indents the line and adds a category." - (let* ((category (or category - org-category - (file-name-sans-extension - (file-name-nondirectory (buffer-file-name))))) - (extra prefix) - (time-of-day (org-get-time-of-day txt)) - (t1 (if time-of-day (concat "0" (int-to-string time-of-day)) "0000")) - (time (if time-of-day - (concat (substring t1 -4 -2) - ":" (substring t1 -2)) - "")) - rtn) - (if (symbolp category) (setq category (symbol-name category))) - (setq rtn (concat (eval org-prefix-format-compiled) txt)) - (add-text-properties - 0 (length rtn) (list 'category (downcase category) - 'prefix-length (- (length rtn) (length txt)) - 'time-of-day time-of-day) - rtn) - rtn)) - +In particular, it adds the prefix and corresponding text properties. EXTRA +must be a string and replaces the `%s' specifier in the prefix format. +CATEGORY (string, symbol or nil) may be used to overule the default +category taken from local variable or file name. It will replace the `%c' +specifier in the format. DOTIME, when non-nil, indicates that a +time-of-day should be extracted from TXT for sorting of this entry, and for +the `%t' specifier in the format. When DOTIME is a string, this string is +searched for a time before TXT is. NOPREFIX is a flag and indicates that +only the correctly processes TXT should be returned - this is used by +`org-agenda-change-all-lines'." + (save-match-data + ;; Diary entries sometimes have extra whitespace at the beginning + (if (string-match "^ +" txt) (setq txt (replace-match "" nil nil txt))) + (let* ((category (or category + org-category + (if (buffer-file-name) + (file-name-sans-extension + (file-name-nondirectory (buffer-file-name))) + ""))) + time ;; needed for the eval of the prefix format + (ts (if dotime (concat (if (stringp dotime) dotime "") txt))) + (time-of-day (and dotime (org-get-time-of-day ts))) + stamp plain s0 s1 s2 rtn) + (when (and dotime time-of-day org-prefix-has-time) + ;; Extract starting and ending time and move them to prefix + (when (or (setq stamp (string-match org-stamp-time-of-day-regexp ts)) + (setq plain (string-match org-plain-time-of-day-regexp ts))) + (setq s0 (match-string 0 ts) + s1 (match-string (if plain 1 2) ts) + s2 (match-string (if plain 8 4) ts)) + + ;; If the times are in TXT (not in DOTIMES), and the prefix will list + ;; them, we might want to remove them there to avoid duplication. + ;; The user can turn this off with a variable. + (if (and org-agenda-remove-times-when-in-prefix (or stamp plain) + (string-match (concat (regexp-quote s0) " *") txt) + (if (eq org-agenda-remove-times-when-in-prefix 'beg) + (= (match-beginning 0) 0) + t)) + (setq txt (replace-match "" nil nil txt)))) + ;; Normalize the time(s) to 24 hour + (if s1 (setq s1 (org-get-time-of-day s1 'string))) + (if s2 (setq s2 (org-get-time-of-day s2 'string)))) + + ;; Create the final string + (if noprefix + (setq rtn txt) + ;; Prepare the variables needed in the eval of the compiled format + (setq time (cond (s2 (concat s1 "-" s2)) + (s1 (concat s1 "......")) + (t "")) + extra (or extra "") + category (if (symbolp category) (symbol-name category) category)) + ;; Evaluate the compiled format + (setq rtn (concat (eval org-prefix-format-compiled) txt))) + + ;; And finally add the text properties + (add-text-properties + 0 (length rtn) (list 'category (downcase category) + 'prefix-length (- (length rtn) (length txt)) + 'time-of-day time-of-day + 'dotime dotime) + rtn) + rtn))) + +(defun org-agenda-add-time-grid-maybe (list ndays todayp) + (catch 'exit + (cond ((not org-agenda-use-time-grid) (throw 'exit list)) + ((and todayp (member 'today (car org-agenda-time-grid)))) + ((and (= ndays 1) (member 'daily (car org-agenda-time-grid)))) + ((member 'weekly (car org-agenda-time-grid))) + (t (throw 'exit list))) + (let* ((have (delq nil (mapcar + (lambda (x) (get-text-property 1 'time-of-day x)) + list))) + (string (nth 1 org-agenda-time-grid)) + (gridtimes (nth 2 org-agenda-time-grid)) + (req (car org-agenda-time-grid)) + (remove (member 'remove-match req)) + new time) + (if (and (member 'require-timed req) (not have)) + ;; don't show empty grid + (throw 'exit list)) + (while (setq time (pop gridtimes)) + (unless (and remove (member time have)) + (setq time (int-to-string time)) + (push (org-format-agenda-item + nil string "" ;; FIXME: put a category? + (concat (substring time 0 -2) ":" (substring time -2))) + new) + (put-text-property + 1 (length (car new)) 'face 'org-time-grid-face (car new)))) + (if (member 'time-up org-agenda-sorting-strategy) + (append new list) + (append list new))))) + (defun org-compile-prefix-format (format) "Compile the prefix format into a Lisp form that can be evaluated. The resulting form is returned and stored in the variable `org-prefix-format-compiled'." - (let ((start 0) varform vars (s format) c) - (while (string-match "%\\([-+]?[0-9.]*\\)\\([ .;,:!?=|/<>]?\\)\\([cts]\\)" + (setq org-prefix-has-time nil) + (let ((start 0) varform vars var (s format) c f opt) + (while (string-match "%\\(\\?\\)?\\([-+]?[0-9.]*\\)\\([ .;,:!?=|/<>]?\\)\\([cts]\\)" s start) - (setq var (cdr (assoc (match-string 3 s) + (setq var (cdr (assoc (match-string 4 s) '(("c" . category) ("t" . time) ("s" . extra)))) - c (match-string 2 s) + c (or (match-string 3 s) "") + opt (match-beginning 1) start (1+ (match-beginning 0))) - (if (= (length c) 1) - (setq varform `(if (equal "" ,var) "" (concat ,var ,c))) - (setq varform var)) - (setq s (replace-match "%\\1s" t nil s)) + (if (equal var 'time) (setq org-prefix-has-time t)) + (setq f (concat "%" (match-string 2 s) "s")) + (if opt + (setq varform + `(if (equal "" ,var) + "" + (format ,f (if (equal "" ,var) "" (concat ,var ,c))))) + (setq varform `(format ,f (if (equal ,var "") "" (concat ,var ,c))))) + (setq s (replace-match "%s" t nil s)) (push varform vars)) (setq vars (nreverse vars)) (setq org-prefix-format-compiled `(format ,s ,@vars)))) -(defun org-get-time-of-day (s) +(defun org-get-time-of-day (s &optional string) "Check string S for a time of day. If found, return it as a military time number between 0 and 2400. -If not found, return nil." +If not found, return nil. +The optional STRING argument forces conversion into a 5 character wide string +HH:MM." (save-match-data - (when (or - (string-match - "\\<\\([012]?[0-9]\\)\\(:\\([0-5][0-9]\\)\\)\\([AaPp][Mm]\\)?\\>" s) - (string-match - "\\<\\([012]?[0-9]\\)\\(:\\([0-5][0-9]\\)\\)?\\([AaPp][Mm]\\)\\>" s)) - (+ (* 100 (+ (string-to-number (match-string 1 s)) - (if (and (match-beginning 4) - (equal (downcase (match-string 4 s)) "pm")) - 12 0))) - (if (match-beginning 3) - (string-to-number (match-string 3 s)) - 0))))) + (when + (or + (string-match + "\\<\\([012]?[0-9]\\)\\(:\\([0-5][0-9]\\)\\)\\([AaPp][Mm]\\)?\\> *" s) + (string-match + "\\<\\([012]?[0-9]\\)\\(:\\([0-5][0-9]\\)\\)?\\([AaPp][Mm]\\)\\> *" s)) + (let* ((t0 (+ (* 100 + (+ (string-to-number (match-string 1 s)) + (if (and (match-beginning 4) + (equal (downcase (match-string 4 s)) "pm")) + 12 0))) + (if (match-beginning 3) + (string-to-number (match-string 3 s)) + 0))) + (t1 (concat " " (int-to-string t0)))) + (if string (concat (substring t1 -4 -2) ":" (substring t1 -2)) t0))))) (defun org-finalize-agenda-entries (list) "Sort and concatenate the agenda items." @@ -4295,23 +4529,30 @@ (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. HDMARKER is checked with -`equal' against all `org-hd-marker' text properties in the file." - (let* (props m pl undone-face done-face) +The new content of the line will be NEWHEAD (as modified by +`org-format-agenda-item'). HDMARKER is checked with +`equal' against all `org-hd-marker' text properties in the file. +If FIXFACE is non-nil, the face of each item is modified acording to +the new TODO state." + (let* (props m pl undone-face done-face finish new dotime) +; (setq newhead (org-format-agenda-item "x" newhead "x" nil 'noprefix)) (save-excursion (goto-char (point-max)) (beginning-of-line 1) - (while (not (bobp)) + (while (not finish) + (setq finish (bobp)) (when (and (setq m (get-text-property (point) 'org-hd-marker)) (equal m hdmarker)) (setq props (text-properties-at (point)) + dotime (get-text-property (point) 'dotime) + new (org-format-agenda-item "x" newhead "x" dotime 'noprefix) pl (get-text-property (point) 'prefix-length) undone-face (get-text-property (point) 'undone-face) done-face (get-text-property (point) 'done-face)) (move-to-column pl) (if (looking-at ".*") (progn - (replace-match newhead t t) + (replace-match new t t) (beginning-of-line 1) (add-text-properties (point-at-bol) (point-at-eol) props) (if fixface @@ -4355,6 +4596,7 @@ (and (outline-next-heading) (org-flag-heading nil))) ; show the next heading (funcall 'org-priority force-direction) + (end-of-line 1) (setq newhead (org-get-heading))) (org-agenda-change-all-lines newhead hdmarker) (beginning-of-line 1))) @@ -4519,7 +4761,7 @@ "ISO: " (calendar-iso-date-string date) "\n" "Day of Yr: " (calendar-day-of-year-string date) "\n" "Julian: " (calendar-julian-date-string date) "\n" - "Astronomic: " (calendar-astro-date-string date) + "Astron. JD: " (calendar-astro-date-string date) " (Julian date number at noon UTC)\n" "Hebrew: " (calendar-hebrew-date-string date) " (until sunset)\n" "Islamic: " (calendar-islamic-date-string date) " (until sunset)\n" @@ -4865,10 +5107,11 @@ ((fboundp 'gnus-group-name) (gnus-group-name)) (t "???")))) - (setq link (concat (if (org-xor arg org-usenet-links-prefer-google) - "http://groups.google.com/groups?group=" - "gnus:") - group)))) + (setq link (concat + (if (org-xor arg org-usenet-links-prefer-google) + "http://groups.google.com/groups?group=" + "gnus:") + group)))) ((memq major-mode '(gnus-summary-mode gnus-article-mode)) (and (eq major-mode 'gnus-article-mode) (gnus-article-show-summary)) @@ -4919,9 +5162,7 @@ (defun org-xor (a b) "Exclusive or." - ;; (if a (not b) b) - (or (and a (not b)) - (and b (not a)))) + (if a (not b) b)) (defun org-get-header (header) "Find a header field in the current buffer." @@ -5217,7 +5458,8 @@ (point-at-bol) (point))) (beginning-of-line 1) (newline)) - (mapcar (lambda (x) (insert line)) (make-list rows t)) + ;; (mapcar (lambda (x) (insert line)) (make-list rows t)) + (dotimes (i rows) (insert line)) (goto-char pos) (if (> rows 1) ;; Insert a hline after the first row. @@ -5285,8 +5527,7 @@ (unless (or (not (file-exists-p file)) (y-or-n-p (format "Overwrite file %s? " file))) (error "Abort")) - (save-excursion - (find-file file) + (with-current-buffer (find-file-noselect file) (setq buf (current-buffer)) (erase-buffer) (fundamental-mode) @@ -5406,12 +5647,18 @@ (setq rfmt (concat rfmt "\n") hfmt (concat (substring hfmt 0 -1) "|\n")) ;; Produce the new table - (while lines - (setq l (pop lines)) - (if l - (setq new (concat new (apply 'format rfmt - (append (pop fields) emptystrings)))) - (setq new (concat new hfmt)))) + ;;(while lines + ;; (setq l (pop lines)) + ;; (if l + ;; (setq new (concat new (apply 'format rfmt + ;; (append (pop fields) emptystrings)))) + ;; (setq new (concat new hfmt)))) + (setq new (mapconcat + (lambda (l) + (if l (apply 'format rfmt + (append (pop fields) emptystrings)) + hfmt)) + lines "")) ;; Replace the old one (delete-region beg end) (move-marker end nil) @@ -8480,9 +8727,7 @@ (get-char-property (point) 'invisible)) (save-excursion (skip-chars-backward "^\r\n") - (if (bobp) - nil - (equal (char-before) ?\r))))) + (equal (char-before) ?\r)))) (defun org-back-to-heading (&optional invisible-ok) "Move to previous heading line, or beg of this line if it's a heading.