From bd2ceb1b545cd75b682b21f5f1dbdbf497cb48ed Mon Sep 17 00:00:00 2001 From: Carsten Dominik Date: Thu, 31 Jan 2008 11:33:26 +0100 Subject: [PATCH] Release 4.59 --- org | 410 +- org-install.el | 1 + org.el | 15352 ++++++++++++++++++++++++----------------------- org.pdf | Bin 638838 -> 638748 bytes org.texi | 26 +- orgcard.pdf | Bin 59693 -> 59989 bytes orgcard.tex | 7 +- 7 files changed, 7956 insertions(+), 7840 deletions(-) diff --git a/org b/org index d657fb2e7..76a1f0d66 100644 --- a/org +++ b/org @@ -5,7 +5,7 @@ START-INFO-DIR-ENTRY * Org Mode: (org). outline-based notes management and organizer END-INFO-DIR-ENTRY - This manual is for Org-mode (version 4.58). + This manual is for Org-mode (version 4.59). Copyright (C) 2004, 2005, 2006 Free Software Foundation @@ -27,7 +27,7 @@ File: org, Node: Top, Next: Introduction, Prev: (dir), Up: (dir) Org Mode Manual *************** -This manual is for Org-mode (version 4.58). +This manual is for Org-mode (version 4.59). Copyright (C) 2004, 2005, 2006 Free Software Foundation @@ -540,11 +540,10 @@ the visibility in the buffer. trees::) or an agenda command (*note Agenda commands::). `C-c C-x b' - Show the current subtree in an indirect buffer(3), in a separate, - dedicated frame. With positive numerical prefix N, go up to level - N before selecting the subtree. With negative prefix -N, go up N - levels. With `C-u' prefix, don't use the dedicated frame, but - another, new frame. + Show the current subtree in an indirect buffer(3). With numerical + prefix ARG, go up to this level and then take that tree. If ARG + is negative, go up that many levels. With `C-u' prefix, do not + remove the previously used indirect buffer. When Emacs first visits an Org-mode file, the global state is set to OVERVIEW, i.e. only the top level headlines are visible. This can be @@ -564,7 +563,7 @@ basis by adding one of the following lines anywhere in the buffer: (3) The indirect buffer (*note Indirect Buffers: (emacs)Indirect Buffers.) will contain the entire buffer, but will be narrowed to the current tree. Editing the indirect buffer will also change the -original buffer, but without affecting visibility in that buffer . +original buffer, but without affecting visibility in that buffer.  File: org, Node: Motion, Next: Structure editing, Prev: Visibility cycling, Up: Document structure @@ -3397,10 +3396,9 @@ View/GoTo org file `b' Display the entire subtree of the current item in an indirect - buffer, in a separate, dedicated frame. With positive numerical - prefix N, go up to level N before selecting the subtree. With - negative prefix -N, go up N levels. With `C-u' prefix, don't use - the dedicated frame, but another, new frame. + buffer. With numerical prefix ARG, go up to this level and then + take that tree. If ARG is negative, go up that many levels. With + `C-u' prefix, do not remove the previously used indirect buffer. `l' Toggle Logbook mode. In Logbook mode, entries that where marked @@ -5366,7 +5364,7 @@ Index * agenda: Weekly/Daily agenda. (line 6) * agenda dispatcher: Agenda dispatcher. (line 6) * agenda files: Agenda files. (line 6) -* agenda files, removing buffers: Agenda commands. (line 231) +* agenda files, removing buffers: Agenda commands. (line 230) * agenda views: Agenda views. (line 6) * agenda views, custom: Custom agenda views. (line 6) * agenda, batch production: Batch processing. (line 6) @@ -5394,7 +5392,7 @@ Index * calculations, in tables <1>: Table calculations. (line 6) * calculations, in tables: Built-in table editor. (line 141) -* calendar commands, from agenda: Agenda commands. (line 192) +* calendar commands, from agenda: Agenda commands. (line 191) * calendar integration: Weekly/Daily agenda. (line 24) * calendar, for selecting date: The date/time prompt. (line 26) @@ -5448,13 +5446,13 @@ Index * DEADLINE keyword: Time stamps. (line 53) * deadlines: Time stamps. (line 6) * demotion, of subtrees: Structure editing. (line 6) -* diary entries, creating from agenda: Agenda commands. (line 199) +* diary entries, creating from agenda: Agenda commands. (line 198) * diary integration: Weekly/Daily agenda. (line 24) * dictionary word completion: Completion. (line 6) * directories, for publishing: Sources and destinations. (line 6) * dispatching agenda commands: Agenda dispatcher. (line 6) -* display changing, in agenda: Agenda commands. (line 66) +* display changing, in agenda: Agenda commands. (line 65) * document structure: Document structure. (line 6) * DONE, final TODO keyword: Per file keywords. (line 20) * editing tables: Tables. (line 6) @@ -5614,8 +5612,8 @@ Index * regular expressions, with tags search: Tag searches. (line 63) * remember.el <1>: Cooperation. (line 33) * remember.el: Remember. (line 6) -* remote editing, from agenda: Agenda commands. (line 107) -* remote editing, undo: Agenda commands. (line 110) +* remote editing, from agenda: Agenda commands. (line 106) +* remote editing, undo: Agenda commands. (line 109) * richer text: Enhancing text. (line 6) * RMAIL links: External links. (line 6) * SCHEDULED keyword: Time stamps. (line 40) @@ -5713,7 +5711,7 @@ Index * tty keybindings: TTY keys. (line 6) * types as TODO keywords: TODO types. (line 6) * underlined text: Enhancing text. (line 15) -* undoing remote-editing events: Agenda commands. (line 110) +* undoing remote-editing events: Agenda commands. (line 109) * URL links: External links. (line 6) * USENET links: External links. (line 6) * variables, for customization: Customization. (line 6) @@ -5737,23 +5735,23 @@ Key Index [index] * Menu: -* $: Agenda commands. (line 124) +* $: Agenda commands. (line 123) * ': CDLaTeX mode. (line 43) -* +: Agenda commands. (line 146) -* ,: Agenda commands. (line 138) -* -: Agenda commands. (line 152) -* .: Agenda commands. (line 101) -* :: Agenda commands. (line 132) +* +: Agenda commands. (line 145) +* ,: Agenda commands. (line 137) +* -: Agenda commands. (line 151) +* .: Agenda commands. (line 100) +* :: Agenda commands. (line 131) * <: The date/time prompt. (line 29) -* : Agenda commands. (line 98) +* : Agenda commands. (line 97) * <1>: Agenda commands. (line 41) * <2>: Setting tags. (line 76) * <3>: The date/time prompt. (line 54) * : Built-in table editor. (line 64) -* : Agenda commands. (line 93) +* : Agenda commands. (line 92) * <1>: Agenda commands. (line 28) * : Setting tags. (line 73) * <1>: CDLaTeX mode. (line 23) @@ -5763,20 +5761,20 @@ Key Index (line 57) * <5>: Plain lists. (line 37) * : Visibility cycling. (line 10) -* > <1>: Agenda commands. (line 174) +* > <1>: Agenda commands. (line 173) * >: The date/time prompt. (line 30) * ^: CDLaTeX mode. (line 33) * _: CDLaTeX mode. (line 33) * `: CDLaTeX mode. (line 39) -* a: Agenda commands. (line 135) +* a: Agenda commands. (line 134) * b: Agenda commands. (line 51) -* C: Agenda commands. (line 214) -* c: Agenda commands. (line 192) +* C: Agenda commands. (line 213) +* c: Agenda commands. (line 191) * C-#: Built-in table editor. (line 161) * C-,: Agenda files. (line 18) -* C-_: Agenda commands. (line 110) +* C-_: Agenda commands. (line 109) * C-a a L: Timeline. (line 10) * C-c !: Creating timestamps. (line 21) * C-c #: Checkboxes. (line 56) @@ -5843,7 +5841,7 @@ Key Index * C-c C-c <7>: Built-in table editor. (line 54) * C-c C-c: Plain lists. (line 74) -* C-c C-d <1>: Agenda commands. (line 159) +* C-c C-d <1>: Agenda commands. (line 158) * C-c C-d: Creating timestamps. (line 37) * C-c C-e: Exporting. (line 19) * C-c C-e a: ASCII export. (line 9) @@ -5871,7 +5869,7 @@ Key Index * C-c C-q: Built-in table editor. (line 125) * C-c C-r: Visibility cycling. (line 32) -* C-c C-s <1>: Agenda commands. (line 156) +* C-c C-s <1>: Agenda commands. (line 155) * C-c C-s: Creating timestamps. (line 48) * C-c C-t <1>: Clocking work time. (line 26) * C-c C-t: TODO basics. (line 13) @@ -5881,7 +5879,7 @@ Key Index * C-c C-x b: Visibility cycling. (line 38) * C-c C-x C-a: ARCHIVE tag. (line 28) * C-c C-x C-b: Checkboxes. (line 38) -* C-c C-x C-c: Agenda commands. (line 221) +* C-c C-x C-c: Agenda commands. (line 220) * C-c C-x C-d: Clocking work time. (line 34) * C-c C-x C-i: Clocking work time. (line 12) * C-c C-x C-k: Structure editing. (line 39) @@ -5908,7 +5906,7 @@ Key Index * C-c |: Built-in table editor. (line 40) * C-c ~: table.el. (line 18) -* C-k: Agenda commands. (line 118) +* C-k: Agenda commands. (line 117) * C-TAB: ARCHIVE tag. (line 38) * C-u C-c $: Moving subtrees. (line 12) * C-u C-c .: Creating timestamps. (line 16) @@ -5918,16 +5916,16 @@ Key Index * C-u C-c C-x C-a: ARCHIVE tag. (line 31) * C-u C-c C-x C-u <1>: Dynamic blocks. (line 22) * C-u C-c C-x C-u: Clocking work time. (line 69) -* D: Agenda commands. (line 75) -* d: Agenda commands. (line 72) +* D: Agenda commands. (line 74) +* d: Agenda commands. (line 71) * f: Agenda commands. (line 44) -* g: Agenda commands. (line 79) -* H: Agenda commands. (line 218) -* i: Agenda commands. (line 199) -* I: Agenda commands. (line 179) -* l: Agenda commands. (line 58) +* g: Agenda commands. (line 78) +* H: Agenda commands. (line 217) +* i: Agenda commands. (line 198) +* I: Agenda commands. (line 178) +* l: Agenda commands. (line 57) * L: Agenda commands. (line 32) -* M: Agenda commands. (line 205) +* M: Agenda commands. (line 204) * M-: Built-in table editor. (line 82) * M- <1>: Built-in table editor. @@ -5975,29 +5973,29 @@ Key Index * mouse-3 <1>: Agenda commands. (line 28) * mouse-3: Handling links. (line 77) * n: Agenda commands. (line 19) -* O: Agenda commands. (line 181) -* o: Agenda commands. (line 66) -* P: Agenda commands. (line 143) +* O: Agenda commands. (line 180) +* o: Agenda commands. (line 65) +* P: Agenda commands. (line 142) * p: Agenda commands. (line 20) -* q: Agenda commands. (line 228) -* r <1>: Agenda commands. (line 83) +* q: Agenda commands. (line 227) +* r <1>: Agenda commands. (line 82) * r: Global TODO list. (line 20) -* S: Agenda commands. (line 209) -* s: Agenda commands. (line 90) -* S- <1>: Agenda commands. (line 152) +* S: Agenda commands. (line 208) +* s: Agenda commands. (line 89) +* S- <1>: Agenda commands. (line 151) * S- <2>: The date/time prompt. (line 42) * S- <3>: Creating timestamps. (line 58) * S- <4>: Priorities. (line 25) * S-: Plain lists. (line 55) -* S- <1>: Agenda commands. (line 170) +* S- <1>: Agenda commands. (line 169) * S- <2>: The date/time prompt. (line 39) * S- <3>: Creating timestamps. (line 53) * S-: TODO basics. (line 20) * S-: Built-in table editor. (line 176) -* S- <1>: Agenda commands. (line 162) +* S- <1>: Agenda commands. (line 161) * S- <2>: The date/time prompt. (line 36) * S- <3>: Creating timestamps. (line 53) @@ -6005,17 +6003,17 @@ Key Index * S- <1>: Built-in table editor. (line 61) * S-: Visibility cycling. (line 22) -* S- <1>: Agenda commands. (line 146) +* S- <1>: Agenda commands. (line 145) * S- <2>: The date/time prompt. (line 45) * S- <3>: Creating timestamps. (line 58) * S- <4>: Priorities. (line 25) * S-: Plain lists. (line 55) -* T: Agenda commands. (line 127) -* t: Agenda commands. (line 114) -* w: Agenda commands. (line 69) -* x: Agenda commands. (line 231) -* X: Agenda commands. (line 184) +* T: Agenda commands. (line 126) +* t: Agenda commands. (line 113) +* w: Agenda commands. (line 68) +* x: Agenda commands. (line 230) +* X: Agenda commands. (line 183)  @@ -6030,151 +6028,151 @@ Node: Document structure18484 Node: Outlines19258 Node: Headlines19918 Node: Visibility cycling20541 -Ref: Visibility cycling-Footnote-122614 -Ref: Visibility cycling-Footnote-222672 -Ref: Visibility cycling-Footnote-322722 -Node: Motion22992 -Node: Structure editing23776 -Node: Archiving26602 -Node: ARCHIVE tag27160 -Node: Moving subtrees28953 -Node: Sparse trees29994 -Ref: Sparse trees-Footnote-132125 -Ref: Sparse trees-Footnote-232217 -Node: Plain lists32332 -Ref: Plain lists-Footnote-135857 -Ref: Plain lists-Footnote-236214 -Node: Tables36398 -Node: Built-in table editor36946 -Node: Narrow columns44974 -Ref: Narrow columns-Footnote-146913 -Node: Table calculations46959 -Node: Formula syntax48279 -Ref: Formula syntax-Footnote-151184 -Node: Lisp formulas51484 -Node: Column formulas52273 -Node: Advanced features54035 -Node: Named-field formulas57289 -Node: Editing/debugging formulas57929 -Node: Appetizer59687 -Node: orgtbl-mode60790 -Node: table.el61281 -Node: Hyperlinks62258 -Node: Link format63031 -Node: Internal links64324 -Ref: Internal links-Footnote-166313 -Node: Radio targets66445 -Node: CamelCase links67160 -Node: External links67754 -Node: Handling links69885 -Ref: Handling links-Footnote-174537 -Ref: Handling links-Footnote-274774 -Node: Link abbreviations74848 -Node: Search options76527 -Ref: Search options-Footnote-178307 -Node: Custom searches78388 -Node: Remember79436 -Node: TODO items83130 -Node: TODO basics84112 -Node: TODO extensions85639 -Node: Workflow states86434 -Node: TODO types87302 -Ref: TODO types-Footnote-188960 -Node: Per file keywords89042 -Ref: Per file keywords-Footnote-190496 -Node: Priorities90697 -Node: Breaking down tasks91941 -Ref: Breaking down tasks-Footnote-192460 -Node: Checkboxes92556 -Node: Timestamps95311 -Node: Time stamps95772 -Ref: Time stamps-Footnote-199266 -Ref: Time stamps-Footnote-299382 -Node: Creating timestamps99537 -Node: The date/time prompt102163 -Ref: The date/time prompt-Footnote-1103929 -Node: Custom time format104035 -Node: Progress logging105594 -Node: Closing items106123 -Node: Clocking work time107027 -Ref: Clocking work time-Footnote-1110651 -Node: Tags110777 -Node: Tag inheritance111539 -Node: Setting tags112476 -Ref: Setting tags-Footnote-1116675 -Ref: Setting tags-Footnote-2116787 -Node: Tag searches116870 -Node: Agenda views119582 -Node: Agenda files121522 -Ref: Agenda files-Footnote-1122482 -Ref: Agenda files-Footnote-2122631 -Node: Agenda dispatcher122824 -Node: Built-in agenda views124515 -Node: Weekly/Daily agenda125093 -Node: Global TODO list127222 -Node: Matching headline tags129395 -Node: Timeline130466 -Node: Stuck projects131132 -Node: Presentation and sorting132831 -Node: Categories133622 -Node: Time-of-day specifications134286 -Node: Sorting of agenda items136257 -Node: Agenda commands137539 -Node: Custom agenda views144239 -Node: Storing searches144914 -Node: Block agenda146826 -Node: Setting Options148056 -Node: Batch processing150768 -Node: Embedded LaTeX151898 -Ref: Embedded LaTeX-Footnote-1152990 -Node: Math symbols153180 -Node: Subscripts and Superscripts153945 -Node: LaTeX fragments154789 -Ref: LaTeX fragments-Footnote-1156897 -Node: Processing LaTeX fragments157159 -Node: CDLaTeX mode158105 -Ref: CDLaTeX mode-Footnote-1160589 -Node: Exporting160737 -Node: ASCII export162051 -Node: HTML export163341 -Node: XOXO export166177 -Node: iCalendar export166616 -Node: Text interpretation168439 -Node: Comment lines168918 -Node: Enhancing text169389 -Node: Export options171081 -Node: Publishing172748 -Ref: Publishing-Footnote-1173544 -Node: Configuration173740 -Node: Project alist174458 -Node: Sources and destinations175524 -Node: Selecting files176254 -Node: Publishing action177002 -Node: Publishing options178235 -Node: Publishing links180387 -Node: Project page index181900 -Node: Sample configuration182678 -Node: Simple example183170 -Node: Complex example183843 -Node: Triggering publication185919 -Node: Miscellaneous186604 -Node: Completion187238 -Node: Customization188709 -Node: In-buffer settings189292 -Node: The very busy C-c C-c key192911 -Node: Clean view194555 -Node: TTY keys197132 -Node: Interaction198741 -Node: Cooperation199138 -Node: Conflicts201005 -Node: Bugs202597 -Node: Extensions and Hacking203991 -Node: Extensions204495 -Node: Dynamic blocks206282 -Node: Special agenda views208238 -Ref: Special agenda views-Footnote-1210519 -Node: History and Acknowledgments210779 -Node: Index215786 -Node: Key Index243028 +Ref: Visibility cycling-Footnote-122567 +Ref: Visibility cycling-Footnote-222625 +Ref: Visibility cycling-Footnote-322675 +Node: Motion22944 +Node: Structure editing23728 +Node: Archiving26554 +Node: ARCHIVE tag27112 +Node: Moving subtrees28905 +Node: Sparse trees29946 +Ref: Sparse trees-Footnote-132077 +Ref: Sparse trees-Footnote-232169 +Node: Plain lists32284 +Ref: Plain lists-Footnote-135809 +Ref: Plain lists-Footnote-236166 +Node: Tables36350 +Node: Built-in table editor36898 +Node: Narrow columns44926 +Ref: Narrow columns-Footnote-146865 +Node: Table calculations46911 +Node: Formula syntax48231 +Ref: Formula syntax-Footnote-151136 +Node: Lisp formulas51436 +Node: Column formulas52225 +Node: Advanced features53987 +Node: Named-field formulas57241 +Node: Editing/debugging formulas57881 +Node: Appetizer59639 +Node: orgtbl-mode60742 +Node: table.el61233 +Node: Hyperlinks62210 +Node: Link format62983 +Node: Internal links64276 +Ref: Internal links-Footnote-166265 +Node: Radio targets66397 +Node: CamelCase links67112 +Node: External links67706 +Node: Handling links69837 +Ref: Handling links-Footnote-174489 +Ref: Handling links-Footnote-274726 +Node: Link abbreviations74800 +Node: Search options76479 +Ref: Search options-Footnote-178259 +Node: Custom searches78340 +Node: Remember79388 +Node: TODO items83082 +Node: TODO basics84064 +Node: TODO extensions85591 +Node: Workflow states86386 +Node: TODO types87254 +Ref: TODO types-Footnote-188912 +Node: Per file keywords88994 +Ref: Per file keywords-Footnote-190448 +Node: Priorities90649 +Node: Breaking down tasks91893 +Ref: Breaking down tasks-Footnote-192412 +Node: Checkboxes92508 +Node: Timestamps95263 +Node: Time stamps95724 +Ref: Time stamps-Footnote-199218 +Ref: Time stamps-Footnote-299334 +Node: Creating timestamps99489 +Node: The date/time prompt102115 +Ref: The date/time prompt-Footnote-1103881 +Node: Custom time format103987 +Node: Progress logging105546 +Node: Closing items106075 +Node: Clocking work time106979 +Ref: Clocking work time-Footnote-1110603 +Node: Tags110729 +Node: Tag inheritance111491 +Node: Setting tags112428 +Ref: Setting tags-Footnote-1116627 +Ref: Setting tags-Footnote-2116739 +Node: Tag searches116822 +Node: Agenda views119534 +Node: Agenda files121474 +Ref: Agenda files-Footnote-1122434 +Ref: Agenda files-Footnote-2122583 +Node: Agenda dispatcher122776 +Node: Built-in agenda views124467 +Node: Weekly/Daily agenda125045 +Node: Global TODO list127174 +Node: Matching headline tags129347 +Node: Timeline130418 +Node: Stuck projects131084 +Node: Presentation and sorting132783 +Node: Categories133574 +Node: Time-of-day specifications134238 +Node: Sorting of agenda items136209 +Node: Agenda commands137491 +Node: Custom agenda views144144 +Node: Storing searches144819 +Node: Block agenda146731 +Node: Setting Options147961 +Node: Batch processing150673 +Node: Embedded LaTeX151803 +Ref: Embedded LaTeX-Footnote-1152895 +Node: Math symbols153085 +Node: Subscripts and Superscripts153850 +Node: LaTeX fragments154694 +Ref: LaTeX fragments-Footnote-1156802 +Node: Processing LaTeX fragments157064 +Node: CDLaTeX mode158010 +Ref: CDLaTeX mode-Footnote-1160494 +Node: Exporting160642 +Node: ASCII export161956 +Node: HTML export163246 +Node: XOXO export166082 +Node: iCalendar export166521 +Node: Text interpretation168344 +Node: Comment lines168823 +Node: Enhancing text169294 +Node: Export options170986 +Node: Publishing172653 +Ref: Publishing-Footnote-1173449 +Node: Configuration173645 +Node: Project alist174363 +Node: Sources and destinations175429 +Node: Selecting files176159 +Node: Publishing action176907 +Node: Publishing options178140 +Node: Publishing links180292 +Node: Project page index181805 +Node: Sample configuration182583 +Node: Simple example183075 +Node: Complex example183748 +Node: Triggering publication185824 +Node: Miscellaneous186509 +Node: Completion187143 +Node: Customization188614 +Node: In-buffer settings189197 +Node: The very busy C-c C-c key192816 +Node: Clean view194460 +Node: TTY keys197037 +Node: Interaction198646 +Node: Cooperation199043 +Node: Conflicts200910 +Node: Bugs202502 +Node: Extensions and Hacking203896 +Node: Extensions204400 +Node: Dynamic blocks206187 +Node: Special agenda views208143 +Ref: Special agenda views-Footnote-1210424 +Node: History and Acknowledgments210684 +Node: Index215691 +Node: Key Index242933  End Tag Table diff --git a/org-install.el b/org-install.el index bb6441e0d..167d16f7f 100644 --- a/org-install.el +++ b/org-install.el @@ -12,6 +12,7 @@ (autoload 'org-cycle-agenda-files "org" "Cycle through agenda-files." t) (autoload 'org-todo-list "org" "Produce global TODO list." t) (autoload 'org-tags-view "org" "Produce global TAGS agenda view." t) +(autoload 'org-agenda-list-stuck-projects "org" "List stuck projects." t) (autoload 'org-remember-annotation "org") (autoload 'org-remember-apply-template "org") (autoload 'org-remember-handler "org") diff --git a/org.el b/org.el index 7435b7937..0d165d4ae 100644 --- a/org.el +++ b/org.el @@ -5,7 +5,7 @@ ;; Author: Carsten Dominik ;; Keywords: outlines, hypermedia, calendar, wp ;; Homepage: http://www.astro.uva.nl/~dominik/Tools/org/ -;; Version: 4.58 +;; Version: 4.59 ;; ;; This file is part of GNU Emacs. ;; @@ -61,6 +61,9 @@ ;; ;; Recent changes ;; -------------- +;; Version 4.59 +;; - Cleanup code, bug fixes. +;; ;; Version 4.58 ;; - Full undo support in the agenda buffer. ;; - Listing stuck GTD projects (projects without any NEXT ACTIONS). @@ -124,6 +127,8 @@ ;; ;;; Code: +;;;; Require other packages + (eval-when-compile (require 'cl) (require 'gnus-sum) @@ -139,6 +144,8 @@ ;;;; Customization variables +;;; Version + (defvar org-version "4.58" "The version number of the file org.el.") (defun org-version () @@ -153,6 +160,8 @@ (get-text-property 0 'test (format "%s" x))) "Does format transport text properties?") +;;; The custom variables + (defgroup org nil "Outline-based notes management and organizer." :tag "Org" @@ -1819,23 +1828,22 @@ This is only effective if `org-agenda-window-setup' is `reorganize-frame'." :group 'org-agenda-windows :type 'boolean) - -(defcustom org-indirect-tree-new-frame 'dedicated +(defcustom org-indirect-buffer-display 'other-window "How should indirect tree buffers be displayed? This applies to indirect buffers created with the commands \\[org-tree-to-indirect-buffer] and \\[org-agenda-tree-to-indirect-buffer]. Valid values are: -nil Just display in another window. -t Use a new frame for each indirect buffer created in this way. -dedicated Create one new frame, and re-use it each time the command is - used. This also means that old indirect buffers will be - deleted when a new one is displayed. This is the default." +current-window Display in the current window +other-window Just display in another window. +dedicated-frame Create one new frame, and re-use it each time. +new-frame Make a new frame each time." :group 'org-structure :group 'org-agenda-windows :type '(choice - (const :tag "In current frame" nil) - (const :tag "Each time a new frame" t) - (const :tag "One dedicated frame" 'dedicated))) + (const :tag "In current window" current-window) + (const :tag "In current frame, other window" other-window) + (const :tag "Each time a new frame" new-frame) + (const :tag "One dedicated frame" dedicated-frame))) (defgroup org-agenda-daily/weekly nil "Options concerning the daily/weekly agenda." @@ -2638,7 +2646,7 @@ Changing this variable requires a restart of Emacs to take effect." "\\([" post (if stacked markers) "]\\|$\\)"))))) (defcustom org-emphasis-regexp-components - '(" \t(" " \t.,?;'\")" " \t\r\n," "." 1 nil) + '(" \t('\"" " \t.,?;'\")" " \t\r\n," "." 1 nil) "Components used to build the reqular expression for emphasis. This is a list with 6 entries. Terminology: In an emphasis string like \" *strong word* \", we call the initial space PREMATCH, the final @@ -2692,6 +2700,8 @@ Use customize to modify this, or restart Emacs after changing it." (string :tag "HTML start tag") (string :tag "HTML end tag")))) +;;; The faces + (defgroup org-faces nil "Faces in Org-mode." :tag "Org Faces" @@ -2946,7 +2956,8 @@ This face is only used if `org-fontify-done-headline' is set." (defconst org-n-levels (length org-level-faces)) -;; Variables for pre-computed regular expressions, all buffer local +;;; Variables for pre-computed regular expressions, all buffer local + (defvar org-done-string nil "The last string in `org-todo-keywords', indicating an item is DONE.") (make-variable-buffer-local 'org-done-string) @@ -3185,72 +3196,183 @@ Also put tags into group 4 if tags are present.") (org-set-font-lock-defaults))) -;;; Tell the compiler about dynamically scoped variables, or foreign vars -(defvar calc-embedded-close-formula) ; defined by the calc package -(defvar calc-embedded-open-formula) ; defined by the calc package -(defvar font-lock-unfontify-region-function) ; defined by font-lock.el -(defvar zmacs-regions) ; XEmacs regions -(defvar original-date) ; dynamically scoped in calendar -(defvar org-old-auto-fill-inhibit-regexp) ; local variable used by `orgtbl-mode' -(defvar orgtbl-mode-menu) ; defined when orgtbl mode get initialized -(defvar org-html-entities) ; defined later in this file -(defvar org-goto-start-pos) ; dynamically scoped parameter -(defvar org-time-was-given) ; dynamically scoped parameter -(defvar org-ts-what) ; dynamically scoped parameter -(defvar org-current-export-file) ; dynamically scoped parameter -(defvar org-current-export-dir) ; dynamically scoped parameter -(defvar mark-active) ; Emacs only, not available in XEmacs. -(defvar timecnt) ; dynamically scoped parameter -(defvar levels-open) ; dynamically scoped parameter -(defvar entry) ; dynamically scoped parameter -(defvar state) ; dynamically scoped into `org-after-todo-state-change-hook' -(defvar date) ; dynamically scoped parameter -(defvar description) ; dynamically scoped parameter -(defvar ans1) ; dynamically scoped parameter -(defvar ans2) ; dynamically scoped parameter -(defvar starting-day) ; local variable -(defvar include-all-loc) ; local variable -(defvar vm-message-pointer) ; from vm -(defvar vm-folder-directory) ; from vm -(defvar gnus-other-frame-object) ; from gnus -(defvar wl-summary-buffer-elmo-folder) ; from wanderlust -(defvar wl-summary-buffer-folder-name) ; from wanderlust -(defvar gnus-group-name) ; from gnus -(defvar gnus-article-current) ; from gnus -(defvar w3m-current-url) ; from w3m -(defvar w3m-current-title) ; from w3m -(defvar mh-progs) ; from MH-E -(defvar mh-current-folder) ; from MH-E -(defvar mh-show-folder-buffer) ; from MH-E -(defvar mh-index-folder) ; from MH-E -(defvar mh-searcher) ; from MH-E -(defvar org-selected-point) ; dynamically scoped parameter -(defvar calendar-mode-map) ; from calendar.el -(defvar last-arg) ; local variable -(defvar remember-save-after-remembering) ; from remember.el -(defvar remember-data-file) ; from remember.el -(defvar annotation) ; from remember.el, dynamically scoped in `remember-mode' -(defvar initial) ; from remember.el, dynamically scoped in `remember-mode' -(defvar orgtbl-mode) ; defined later in this file -(defvar Info-current-file) ; from info.el -(defvar Info-current-node) ; from info.el -(defvar texmathp-why) ; from texmathp.el -(defvar org-latex-regexps) + +;;; Some variables ujsed in various places + +(defvar org-window-configuration nil + "Used in various places to store a window configuration.") +(defvar org-finish-function nil + "Function to be called when `C-c C-c' is used. +This is for getting out of special buffers like remember.") + +;;; Foreign variables, to inform the compiler + +;; XEmacs only (defvar outline-mode-menu-heading) (defvar outline-mode-menu-show) (defvar outline-mode-menu-hide) -(defvar org-agenda-undo-list) ;; Defined later in this file -(defvar org-agenda-pending-undo-list) ;; Defined later in this file -(defvar org-agenda-overriding-header) ;; Defined later in this file +(defvar zmacs-regions) ; XEmacs regions +;; Emacs only +(defvar mark-active) -;;;; Define the mode +;; Packages that org-mode interacts with +(defvar calc-embedded-close-formula) +(defvar calc-embedded-open-formula) +(defvar font-lock-unfontify-region-function) +(defvar org-goto-start-pos) +(defvar vm-message-pointer) +(defvar vm-folder-directory) +(defvar wl-summary-buffer-elmo-folder) +(defvar wl-summary-buffer-folder-name) +(defvar gnus-other-frame-object) +(defvar gnus-group-name) +(defvar gnus-article-current) +(defvar w3m-current-url) +(defvar w3m-current-title) +(defvar mh-progs) +(defvar mh-current-folder) +(defvar mh-show-folder-buffer) +(defvar mh-index-folder) +(defvar mh-searcher) +(defvar calendar-mode-map) +(defvar Info-current-file) +(defvar Info-current-node) +(defvar texmathp-why) +(defvar remember-save-after-remembering) +(defvar remember-data-file) +(defvar annotation) ; from remember.el, dynamically scoped in `remember-mode' +(defvar initial) ; from remember.el, dynamically scoped in `remember-mode' +(defvar org-latex-regexps) + +(defvar original-date) ; dynamically scoped in calendar.el does scope this + +;; FIXME: Occasionally check by commenting these, to make sure +;; no other functions uses these, forgetting to let-bind them. +(defvar entry) +(defvar state) +(defvar date) +(defvar description) + + +;; Defined somewhere in this file, but used before definition. +(defvar orgtbl-mode-menu) ; defined when orgtbl mode get initialized +(defvar org-agenda-undo-list) +(defvar org-agenda-pending-undo-list) +(defvar org-agenda-overriding-header) +(defvar orgtbl-mode) +(defvar org-html-entities) +(defvar org-struct-menu) +(defvar org-org-menu) +(defvar org-tbl-menu) +(defvar org-agenda-keymap) +(defvar org-category-table) + +;;;; Emacs/XEmacs compatibility + +;; Overlay compatibility functions +(defun org-make-overlay (beg end &optional buffer) + (if (featurep 'xemacs) + (make-extent beg end buffer) + (make-overlay beg end buffer))) +(defun org-delete-overlay (ovl) + (if (featurep 'xemacs) (delete-extent ovl) (delete-overlay ovl))) +(defun org-detach-overlay (ovl) + (if (featurep 'xemacs) (detach-extent ovl) (delete-overlay ovl))) +(defun org-move-overlay (ovl beg end &optional buffer) + (if (featurep 'xemacs) + (set-extent-endpoints ovl beg end (or buffer (current-buffer))) + (move-overlay ovl beg end buffer))) +(defun org-overlay-put (ovl prop value) + (if (featurep 'xemacs) + (set-extent-property ovl prop value) + (overlay-put ovl prop value))) +(defun org-overlay-display (ovl text &optional face) + "Make overlay OVL display TEXT with face FACE." + (if (featurep 'xemacs) + (let ((gl (make-glyph text))) + (and face (set-glyph-face gl face)) + (set-extent-property ovl 'invisible t) + (set-extent-property ovl 'end-glyph gl)) + (overlay-put ovl 'display text) + (if face (overlay-put ovl 'face face)))) +(defun org-overlay-get (ovl prop) + (if (featurep 'xemacs) + (extent-property ovl prop) + (overlay-get ovl prop))) +(defun org-overlays-at (pos) + (if (featurep 'xemacs) (extents-at pos) (overlays-at pos))) +(defun org-overlays-in (&optional start end) + (if (featurep 'xemacs) + (extent-list nil start end) + (overlays-in start end))) +(defun org-overlay-start (o) + (if (featurep 'xemacs) (extent-start-position o) (overlay-start o))) +(defun org-overlay-end (o) + (if (featurep 'xemacs) (extent-end-position o) (overlay-end o))) +(defun org-find-overlays (prop &optional pos delete) + "Find all overlays specifying PROP at POS or point. +If DELETE is non-nil, delete all those overlays." + (let ((overlays (org-overlays-at (or pos (point)))) + ov found) + (while (setq ov (pop overlays)) + (if (org-overlay-get ov prop) + (if delete (org-delete-overlay ov) (push ov found)))) + found)) + +;; Region compatibility + +(defun org-add-hook (hook function &optional append local) + "Add-hook, compatible with both Emacsen." + (if (and local (featurep 'xemacs)) + (add-local-hook hook function append) + (add-hook hook function append local))) + +(defvar org-ignore-region nil + "To temporarily disable the active region.") + +(defun org-region-active-p () + "Is `transient-mark-mode' on and the region active? +Works on both Emacs and XEmacs." + (if org-ignore-region + nil + (if (featurep 'xemacs) + (and zmacs-regions (region-active-p)) + (and transient-mark-mode mark-active)))) + +;; Invisibility compatibility + +(defun org-add-to-invisibility-spec (arg) + "Add elements to `buffer-invisibility-spec'. +See documentation for `buffer-invisibility-spec' for the kind of elements +that can be added." + (cond + ((fboundp 'add-to-invisibility-spec) + (add-to-invisibility-spec arg)) + ((or (null buffer-invisibility-spec) (eq buffer-invisibility-spec t)) + (setq buffer-invisibility-spec (list arg))) + (t + (setq buffer-invisibility-spec + (cons arg buffer-invisibility-spec))))) + +(defun org-remove-from-invisibility-spec (arg) + "Remove elements from `buffer-invisibility-spec'." + (if (fboundp 'remove-from-invisibility-spec) + (remove-from-invisibility-spec arg) + (if (consp buffer-invisibility-spec) + (setq buffer-invisibility-spec + (delete arg buffer-invisibility-spec))))) + +(defun org-in-invisibility-spec-p (arg) + "Is ARG a member of `buffer-invisibility-spec'?" + (if (consp buffer-invisibility-spec) + (member arg buffer-invisibility-spec) + nil)) + +;;;; Define the Org-mode (if (and (not (keymapp outline-mode-map)) (featurep 'allout)) (error "Conflict with outdated version of allout.el. Load org.el before allout.el, or ugrade to newer allout, for example by switching to Emacs 22.")) -(defvar org-struct-menu) ; defined later in this file -(defvar org-org-menu) ; defined later in this file -(defvar org-tbl-menu) ; defined later in this file ;; We use a before-change function to check if a table might need ;; an update. @@ -3379,7 +3501,7 @@ that will be added to PLIST. Returns the string that was modified." (put 'org-add-props 'lisp-indent-function 2) -;;;; Font-Lock stuff +;;;; Font-Lock stuff, including the activators (defvar org-mouse-map (make-sparse-keymap)) (define-key org-mouse-map @@ -3554,10 +3676,6 @@ We use a macro so that the test can happen at compilation time." (defun org-activate-dates (limit) "Run through the buffer and add overlays to dates." -; (if (re-search-forward org-tsr-regexp limit t) -; (if (re-search-forward -; (if org-display-custom-times org-ts-regexp-both org-tsr-regexp-both) -; limit t) (if (re-search-forward org-tsr-regexp-both limit t) (progn (add-text-properties (match-beginning 0) (match-end 0) @@ -3771,7 +3889,9 @@ between words." rear-nonsticky t invisible t intangible t)))) -;;;; Visibility cycling +;;;; Visibility cycling, including org-goto and indirect buffer + +;;; Cycling (defvar org-cycle-global-status nil) (make-variable-buffer-local 'org-cycle-global-status) @@ -4007,6 +4127,8 @@ Optional argument N means, put the headline into the Nth line of the window." (beginning-of-line) (recenter (prefix-numeric-value N)))) +;;; Org-goto + (defvar org-goto-window-configuration nil) (defvar org-goto-marker nil) (defvar org-goto-map (make-sparse-keymap)) @@ -4064,6 +4186,8 @@ to the new location, making it and the headline hierarchy above it visible." (org-show-context 'org-goto))) (error "Quit")))) +(defvar org-selected-point nil) ; dynamically scoped parameter + (defun org-get-location (buf help) "Let the user select a location in the Org-mode buffer BUF. This function uses a recursive edit. It returns the selected position @@ -4146,18 +4270,20 @@ or nil." (defun org-tree-to-indirect-buffer (&optional arg) "Create indirect buffer and narrow it to current subtree. -With numerical prefix arg ARG, go up to this level and then take that tree. +With numerical prefix ARG, go up to this level and then take that tree. If ARG is negative, go up that many levels. -With a C-u prefix, make a separate frame for this tree (i.e. don't use the -dedicated frame)." +Normally this command removes the indirect buffer previously made +with this command. However, when called with a C-u prefix, the last buffer +is kept so that you can work with several indirect buffers at the same time. +If `org-indirect-buffer-display' is `dedicated-frame', the C-u prefix also +requests that a new frame be made for the new buffer, so that the dedicated +frame is not changed." (interactive "P") (let ((cbuf (current-buffer)) + (cwin (selected-window)) (pos (point)) (bname (buffer-name (current-buffer))) - (org-indirect-tree-new-frame - (if (equal arg '(4)) t org-indirect-tree-new-frame)) - beg end level heading) - + beg end level heading ibuf) (save-excursion (org-back-to-heading t) (when (numberp arg) @@ -4168,37 +4294,39 @@ dedicated frame)." (setq beg (point) heading (org-get-heading)) (org-end-of-subtree t) (setq end (point))) + (if (and (not arg) + (buffer-live-p org-last-indirect-buffer)) + (kill-buffer org-last-indirect-buffer)) + (setq ibuf (org-get-indirect-buffer cbuf) + org-last-indirect-buffer ibuf) (cond - ((eq org-indirect-tree-new-frame 'dedicated) + ((or (eq org-indirect-buffer-display 'new-frame) + (and arg (eq org-indirect-buffer-display 'dedicated-frame))) + (select-frame (make-frame)) + (delete-other-windows) + (switch-to-buffer ibuf) + (org-set-frame-title heading)) + ((eq org-indirect-buffer-display 'dedicated-frame) (raise-frame (select-frame (or (and org-indirect-dedicated-frame (frame-live-p org-indirect-dedicated-frame) org-indirect-dedicated-frame) (setq org-indirect-dedicated-frame (make-frame))))) (delete-other-windows) - (if (equal cbuf (buffer-base-buffer)) - ;; Re-use this buffer - (widen) - ;; clean up from last time - (if (buffer-base-buffer (current-buffer)) - (kill-buffer (current-buffer))) - (if (buffer-live-p org-last-indirect-buffer) - (kill-buffer org-last-indirect-buffer)) - ;; make and select the new indirect buffer - (switch-to-buffer - (setq org-last-indirect-buffer (org-get-indirect-buffer cbuf)))) + (switch-to-buffer ibuf) (org-set-frame-title (concat "Indirect: " heading))) - ((eq org-indirect-tree-new-frame t) - (select-frame (make-frame)) - (delete-other-windows) - (switch-to-buffer (org-get-indirect-buffer cbuf)) - (org-set-frame-title heading)) - (t (pop-to-buffer (org-get-indirect-buffer cbuf)))) + ((eq org-indirect-buffer-display 'current-window) + (switch-to-buffer ibuf)) + ((eq org-indirect-buffer-display 'other-window) + (pop-to-buffer ibuf)) + (t (error "Invalid value."))) (if (featurep 'xemacs) (save-excursion (org-mode) (turn-on-font-lock))) (narrow-to-region beg end) (show-all) - (goto-char pos))) + (goto-char pos) + (debug) + (and (window-live-p cwin) (select-window cwin)))) (defun org-get-indirect-buffer (&optional buffer) (setq buffer (or buffer (current-buffer))) @@ -4216,10 +4344,9 @@ dedicated frame)." (unless (featurep 'xemacs) (modify-frame-parameters (selected-frame) (list (cons 'name title))))) -;;;; Promotion, Demotion, Inserting new headlines +;;;; Structure editing -(defvar org-ignore-region nil - "To temporarily disable the active region.") +;;; Inserting headlines (defun org-insert-heading (&optional force-heading) "Insert a new heading or item with same depth at point. @@ -4254,49 +4381,6 @@ the current headline." (unless (= (point) pos) (just-one-space) (backward-delete-char 1)) (run-hooks 'org-insert-heading-hook))))) -(defun org-in-item-p () - "It the cursor inside a plain list item. -Does not have to be the first line." - (save-excursion - (condition-case nil - (progn - (org-beginning-of-item) - (org-at-item-p) - t) - (error nil)))) - -(defun org-insert-item (&optional checkbox) - "Insert a new item at the current level. -Return t when things worked, nil when we are not in an item." - (when (save-excursion - (condition-case nil - (progn - (org-beginning-of-item) - (org-at-item-p) - (if (org-invisible-p) (error "Invisible item")) - t) - (error nil))) - (let* ((bul (match-string 0)) - (eow (save-excursion (beginning-of-line 1) (looking-at "[ \t]*") - (match-end 0))) - (blank (cdr (assq 'plain-list-item org-blank-before-new-entry))) - pos) - (cond - ((and (org-at-item-p) (<= (point) eow)) - ;; before the bullet - (beginning-of-line 1) - (open-line (if blank 2 1))) - ((<= (point) eow) - (beginning-of-line 1)) - (t (newline (if blank 2 1)))) - (insert bul (if checkbox "[ ]" "")) - (just-one-space) - (setq pos (point)) - (end-of-line 1) - (unless (= (point) pos) (just-one-space) (backward-delete-char 1))) - (org-maybe-renumber-ordered-list) - (and checkbox (org-update-checkbox-count-maybe)) - t)) (defun org-insert-todo-heading (arg) "Insert a new heading with the same level and TODO state as current heading. @@ -4315,6 +4399,8 @@ state (TODO by default). Also with prefix arg, force first state." (insert (car org-todo-keywords) " ") (insert (match-string 2) " ")))) +;;; Promotion and Demotion + (defun org-promote-subtree () "Promote the entire subtree. See also `org-promote'." @@ -4403,120 +4489,6 @@ in the region." (and org-auto-align-tags (org-set-tags nil t)) (if org-adapt-indentation (org-fixup-indentation diff)))) -(defun org-sort (with-case) - "Call `org-sort-entries' or `org-table-sort-lines', depending on context." - (interactive "P") - (if (org-at-table-p) - (org-call-with-arg 'org-table-sort-lines with-case) - (org-call-with-arg 'org-sort-entries with-case))) - -(defun org-sort-entries (&optional with-case sorting-type) - "Sort entries on a certain level of an outline tree. -If there is an active region, the entries in the region are sorted. -If not, the children of the entry at point are sorted. - -Sorting can be alphabetically, numerically, and by date/time as given by -the first time stamp in the entry. The command prompts for the sorting -type unless it has been given to the function through the SORTING-TYPE -argument, which needs to a character, any of (?n ?N ?a ?A ?t ?T). - -Comparing entries ignores case by default. However, with an optional argument -WITH-CASE, the sorting considers case as well. With two prefix arguments -`C-u C-u', sorting is case-sensitive and duplicate entries will be removed." - (interactive "P") - (let ((unique (equal with-case '(16))) - start beg end entries stars re re2 p nentries (nremoved 0) last txt) - - ;; Find beginning and end of region to sort - (if (org-region-active-p) - (progn - ;; we will sort the region - (setq end (region-end)) - (goto-char (1- (setq start (region-beginning))))) - ;; we will sort the children of the current headline - (setq start (point) end (org-end-of-subtree)) - (goto-char start) - (show-subtree)) - (outline-next-heading) ; this is the first heading to be included - (setq beg (point)) - (if (>= (point) end) (error "Nothing to sort")) - (looking-at "\\(\\*+\\)") - (setq stars (match-string 1) - re (concat "^" (regexp-quote stars) " +") - re2 (concat "^" (regexp-quote (substring stars 0 -1)) "[^*]") - txt (buffer-substring beg end)) - (if (not (equal (substring txt -1) "\n")) (setq txt (concat txt "\n"))) - (if (string-match re2 txt) - (error "Region to sort contains a level above the first entry")) - ;; Make a list that can be sorted. - ;; The car is the string for comparison, the cdr is the subtree - (message "Sorting entries...") - (setq entries - (mapcar - (lambda (x) - (string-match "^.*\\(\n.*\\)?" x) ; take two lines - (cons (match-string 0 x) x)) - (org-split-string txt re))) - - ;; Sort the list - (setq entries (org-do-sort - entries - (if (org-region-active-p) "region" "children") - with-case sorting-type)) - - ;; Delete the old stuff - (goto-char beg) - (kill-region beg end) - (setq nentries (length entries)) - ;; Insert the sorted entries, and remove duplicates if this is required - (while (setq p (pop entries)) - (if (and unique (equal last (setq last (org-trim (cdr p))))) - (setq nremoved (1+ nremoved)) ; same entry as before, skip it - (insert stars " " (cdr p)))) - (goto-char start) - (message "Sorting entries...done (%d entries%s)" - nentries - (if unique (format ", %d duplicates removed" nremoved) "")))) - -(defun org-do-sort (table what &optional with-case sorting-type) - "Sort TABLE of WHAT according to SORTING-TYPE. -The user will be prompted for the SORTING-TYPE if the call to this -function does not specify it. WHAT is only for the prompt, to indicate -what is being sorted. The sorting key will be extracted from -the car of the elements of the table. -If WITH-CASE is non-nil, the sorting will be case-sensitive." - (unless sorting-type - (message - "Sort %s:[a]lphabetically [n]umerically [t]ime. A/N/T means reversed:" - what) - (setq sorting-type (read-char-exclusive))) - (let ((dcst (downcase sorting-type)) - extractfun comparefun) - ;; Define the appropriate functions - (cond - ((= dcst ?n) - (setq extractfun 'string-to-number - comparefun (if (= dcst sorting-type) '< '>))) - ((= dcst ?a) - (setq extractfun (if with-case 'identity 'downcase) - comparefun (if (= dcst sorting-type) - 'string< - (lambda (a b) (and (not (string< a b)) - (not (string= a b))))))) - ((= dcst ?t) - (setq extractfun - (lambda (x) - (if (string-match org-ts-regexp x) - (time-to-seconds - (org-time-string-to-time (match-string 0 x))) - 0)) - comparefun (if (= dcst sorting-type) '< '>))) - (t (error "Invalid sorting type `%c'" sorting-type))) - - (sort (mapcar (lambda (x) (cons (funcall extractfun (car x)) (cdr x))) - table) - (lambda (a b) (funcall comparefun (car a) (car b)))))) - (defun org-map-tree (fun) "Call FUN for every heading underneath the current one." (org-back-to-heading) @@ -4563,6 +4535,47 @@ would end up with no indentation after the change, nothing at all is done." (indent-to (+ diff col)))) (move-marker end nil)))) +(defun org-convert-to-odd-levels () + "Convert an org-mode file with all levels allowed to one with odd levels. +This will leave level 1 alone, convert level 2 to level 3, level 3 to +level 5 etc." + (interactive) + (when (yes-or-no-p "Are you sure you want to globally change levels to odd? ") + (let ((org-odd-levels-only nil) n) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "^\\*\\*+" nil t) + (setq n (1- (length (match-string 0)))) + (while (>= (setq n (1- n)) 0) + (org-demote)) + (end-of-line 1)))))) + + +(defun org-convert-to-oddeven-levels () + "Convert an org-mode file with only odd levels to one with odd and even levels. +This promotes level 3 to level 2, level 5 to level 3 etc. If the file contains a +section with an even level, conversion would destroy the structure of the file. An error +is signaled in this case." + (interactive) + (goto-char (point-min)) + ;; First check if there are no even levels + (when (re-search-forward "^\\(\\*\\*\\)+[^*]" nil t) + (org-show-context t) + (error "Not all levels are odd in this file. Conversion not possible.")) + (when (yes-or-no-p "Are you sure you want to globally change levels to odd-even? ") + (let ((org-odd-levels-only nil) n) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "^\\*\\*+" nil t) + (setq n (/ (length (match-string 0)) 2)) + (while (>= (setq n (1- n)) 0) + (org-promote)) + (end-of-line 1)))))) + +(defun org-tr-level (n) + "Make N odd if required." + (if org-odd-levels-only (1+ (/ n 2)) n)) + ;;; Vertical tree motion, cutting and pasting of subtrees (defun org-move-subtree-up (&optional arg) @@ -4772,7 +4785,126 @@ If optional TXT is given, check this string instead of the current kill." (progn (org-back-to-heading) (point)) (progn (org-end-of-subtree t) (point))))) -;;;; Plain list items + +;;; Outline Sorting + +(defun org-sort (with-case) + "Call `org-sort-entries' or `org-table-sort-lines', depending on context." + (interactive "P") + (if (org-at-table-p) + (org-call-with-arg 'org-table-sort-lines with-case) + (org-call-with-arg 'org-sort-entries with-case))) + +(defun org-sort-entries (&optional with-case sorting-type) + "Sort entries on a certain level of an outline tree. +If there is an active region, the entries in the region are sorted. +If not, the children of the entry at point are sorted. + +Sorting can be alphabetically, numerically, and by date/time as given by +the first time stamp in the entry. The command prompts for the sorting +type unless it has been given to the function through the SORTING-TYPE +argument, which needs to a character, any of (?n ?N ?a ?A ?t ?T). + +Comparing entries ignores case by default. However, with an optional argument +WITH-CASE, the sorting considers case as well. With two prefix arguments +`C-u C-u', sorting is case-sensitive and duplicate entries will be removed." + (interactive "P") + (let ((unique (equal with-case '(16))) + start beg end entries stars re re2 p nentries (nremoved 0) last txt) + + ;; Find beginning and end of region to sort + (if (org-region-active-p) + (progn + ;; we will sort the region + (setq end (region-end)) + (goto-char (1- (setq start (region-beginning))))) + ;; we will sort the children of the current headline + (setq start (point) end (org-end-of-subtree)) + (goto-char start) + (show-subtree)) + (outline-next-heading) ; this is the first heading to be included + (setq beg (point)) + (if (>= (point) end) (error "Nothing to sort")) + (looking-at "\\(\\*+\\)") + (setq stars (match-string 1) + re (concat "^" (regexp-quote stars) " +") + re2 (concat "^" (regexp-quote (substring stars 0 -1)) "[^*]") + txt (buffer-substring beg end)) + (if (not (equal (substring txt -1) "\n")) (setq txt (concat txt "\n"))) + (if (string-match re2 txt) + (error "Region to sort contains a level above the first entry")) + ;; Make a list that can be sorted. + ;; The car is the string for comparison, the cdr is the subtree + (message "Sorting entries...") + (setq entries + (mapcar + (lambda (x) + (string-match "^.*\\(\n.*\\)?" x) ; take two lines + (cons (match-string 0 x) x)) + (org-split-string txt re))) + + ;; Sort the list + (setq entries (org-do-sort + entries + (if (org-region-active-p) "region" "children") + with-case sorting-type)) + + ;; Delete the old stuff + (goto-char beg) + (kill-region beg end) + (setq nentries (length entries)) + ;; Insert the sorted entries, and remove duplicates if this is required + (while (setq p (pop entries)) + (if (and unique (equal last (setq last (org-trim (cdr p))))) + (setq nremoved (1+ nremoved)) ; same entry as before, skip it + (insert stars " " (cdr p)))) + (goto-char start) + (message "Sorting entries...done (%d entries%s)" + nentries + (if unique (format ", %d duplicates removed" nremoved) "")))) + +(defun org-do-sort (table what &optional with-case sorting-type) + "Sort TABLE of WHAT according to SORTING-TYPE. +The user will be prompted for the SORTING-TYPE if the call to this +function does not specify it. WHAT is only for the prompt, to indicate +what is being sorted. The sorting key will be extracted from +the car of the elements of the table. +If WITH-CASE is non-nil, the sorting will be case-sensitive." + (unless sorting-type + (message + "Sort %s:[a]lphabetically [n]umerically [t]ime. A/N/T means reversed:" + what) + (setq sorting-type (read-char-exclusive))) + (let ((dcst (downcase sorting-type)) + extractfun comparefun) + ;; Define the appropriate functions + (cond + ((= dcst ?n) + (setq extractfun 'string-to-number + comparefun (if (= dcst sorting-type) '< '>))) + ((= dcst ?a) + (setq extractfun (if with-case 'identity 'downcase) + comparefun (if (= dcst sorting-type) + 'string< + (lambda (a b) (and (not (string< a b)) + (not (string= a b))))))) + ((= dcst ?t) + (setq extractfun + (lambda (x) + (if (string-match org-ts-regexp x) + (time-to-seconds + (org-time-string-to-time (match-string 0 x))) + 0)) + comparefun (if (= dcst sorting-type) '< '>))) + (t (error "Invalid sorting type `%c'" sorting-type))) + + (sort (mapcar (lambda (x) (cons (funcall extractfun (car x)) (cdr x))) + table) + (lambda (a b) (funcall comparefun (car a) (car b)))))) + +;;;; Plain list items, including checkboxes + +;;; Plain list items (defun org-at-item-p () "Is point in a line starting a hand-formatted item?" @@ -4786,6 +4918,53 @@ If optional TXT is given, check this string instead of the current kill." ((= llt ?\)) "\\([ \t]*\\([-+]\\|\\([0-9]+)\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)") (t (error "Invalid value of `org-plain-list-ordered-item-terminator'"))))))) + +(defun org-in-item-p () + "It the cursor inside a plain list item. +Does not have to be the first line." + (save-excursion + (condition-case nil + (progn + (org-beginning-of-item) + (org-at-item-p) + t) + (error nil)))) + +(defun org-insert-item (&optional checkbox) + "Insert a new item at the current level. +Return t when things worked, nil when we are not in an item." + (when (save-excursion + (condition-case nil + (progn + (org-beginning-of-item) + (org-at-item-p) + (if (org-invisible-p) (error "Invisible item")) + t) + (error nil))) + (let* ((bul (match-string 0)) + (eow (save-excursion (beginning-of-line 1) (looking-at "[ \t]*") + (match-end 0))) + (blank (cdr (assq 'plain-list-item org-blank-before-new-entry))) + pos) + (cond + ((and (org-at-item-p) (<= (point) eow)) + ;; before the bullet + (beginning-of-line 1) + (open-line (if blank 2 1))) + ((<= (point) eow) + (beginning-of-line 1)) + (t (newline (if blank 2 1)))) + (insert bul (if checkbox "[ ]" "")) + (just-one-space) + (setq pos (point)) + (end-of-line 1) + (unless (= (point) pos) (just-one-space) (backward-delete-char 1))) + (org-maybe-renumber-ordered-list) + (and checkbox (org-update-checkbox-count-maybe)) + t)) + +;;; Checkboxes + (defun org-at-item-checkbox-p () "Is point at a line starting a plain-list item with a checklet?" (and (org-at-item-p) @@ -5156,7 +5335,7 @@ with something like \"1.\" or \"2)\"." (indent-to-column (+ ind1 arg)) (beginning-of-line 2))))) -;;; Archiving +;;;; Archiving (defun org-archive-subtree (&optional find-done) "Move the current subtree to the archive. @@ -5382,6777 +5561,17 @@ the children that do not contain any open TODO items." (and set (beginning-of-line 1)) (message "Subtree %s" (if set "archived" "unarchived"))))) -(defvar org-agenda-multi nil) ; dynammically scoped -(defvar org-agenda-buffer-name "*Org Agenda*") -(defvar org-pre-agenda-window-conf nil) -(defun org-prepare-agenda () - (if org-agenda-multi - (progn - (setq buffer-read-only nil) - (goto-char (point-max)) - (unless (= (point) 1) - (insert "\n" (make-string (window-width) ?=) "\n")) - (narrow-to-region (point) (point-max))) - (org-agenda-maybe-reset-markers 'force) - (org-prepare-agenda-buffers (org-agenda-files)) - (let* ((abuf (get-buffer-create org-agenda-buffer-name)) - (awin (get-buffer-window abuf))) - (cond - ((equal (current-buffer) abuf) nil) - (awin (select-window awin)) - ((not (setq org-pre-agenda-window-conf (current-window-configuration)))) - ((equal org-agenda-window-setup 'current-window) - (switch-to-buffer abuf)) - ((equal org-agenda-window-setup 'other-window) - (switch-to-buffer-other-window abuf)) - ((equal org-agenda-window-setup 'other-frame) - (switch-to-buffer-other-frame abuf)) - ((equal org-agenda-window-setup 'reorganize-frame) - (delete-other-windows) - (switch-to-buffer-other-window abuf)))) - (setq buffer-read-only nil) - (erase-buffer) - (org-agenda-mode)) - (setq buffer-read-only nil)) - -(defun org-finalize-agenda () - "Finishing touch for the agenda buffer, called just before displaying it." - (unless org-agenda-multi - (org-agenda-align-tags) - (save-excursion - (let ((buffer-read-only)) - (goto-char (point-min)) - (while (org-activate-bracket-links (point-max)) - (add-text-properties (match-beginning 0) (match-end 0) - '(face org-link)))) - (run-hooks 'org-finalize-agenda-hook)))) - -(defun org-prepare-agenda-buffers (files) - "Create buffers for all agenda files, protect archived trees and comments." - (interactive) - (let ((pa '(:org-archived t)) - (pc '(:org-comment t)) - (pall '(:org-archived t :org-comment t)) - (rea (concat ":" org-archive-tag ":")) - bmp file re) - (save-excursion - (while (setq file (pop files)) - (org-check-agenda-file file) - (set-buffer (org-get-agenda-file-buffer file)) - (widen) - (setq bmp (buffer-modified-p)) - (save-excursion - (remove-text-properties (point-min) (point-max) pall) - (when org-agenda-skip-archived-trees - (goto-char (point-min)) - (while (re-search-forward rea nil t) - (if (org-on-heading-p) - (add-text-properties (point-at-bol) (org-end-of-subtree t) pa)))) - (goto-char (point-min)) - (setq re (concat "^\\*+ +" org-comment-string "\\>")) - (while (re-search-forward re nil t) - (add-text-properties - (match-beginning 0) (org-end-of-subtree t) pc))) - (set-buffer-modified-p bmp))))) - -(defvar org-agenda-skip-function nil - "Function to be called at each match during agenda construction. -If this function return nil, the current match should not be skipped. -Otherwise, the function must return a position from where the search -should be continued. -Never set this variable using `setq' or so, because then it will apply -to all future agenda commands. Instead, bind it with `let' to scope -it dynamically into the agenda-constructing command.") - -(defun org-agenda-skip () - "Throw to `:skip' in places that should be skipped." - (let ((p (point-at-bol)) to) - (and org-agenda-skip-archived-trees - (get-text-property p :org-archived) - (org-end-of-subtree t) - (throw :skip t)) - (and (get-text-property p :org-comment) - (org-end-of-subtree t) - (throw :skip t)) - (if (equal (char-after p) ?#) (throw :skip t)) - (when (and (functionp org-agenda-skip-function) - (setq to (save-excursion - (save-match-data - (funcall org-agenda-skip-function))))) - (goto-char to) - (throw :skip t)))) - -;;;; Dynamic blocks - -(defun org-find-dblock (name) - "Find the first dynamic block with name NAME in the buffer. -If not found, stay at current position and return nil." - (let (pos) - (save-excursion - (goto-char (point-min)) - (setq pos (and (re-search-forward (concat "^#\\+BEGIN:[ \t]+" name "\\>") - nil t) - (match-beginning 0)))) - (if pos (goto-char pos)) - pos)) - -(defconst org-dblock-start-re - "^#\\+BEGIN:[ \t]+\\(\\S-+\\)\\([ \t]+\\(.*\\)\\)?" - "Matches the startline of a dynamic block, with parameters.") - -(defconst org-dblock-end-re "^#\\+END\\([: \t\r\n]\\|$\\)" - "Matches the end of a dyhamic block.") - -(defun org-create-dblock (plist) - "Create a dynamic block section, with parameters taken from PLIST. -PLIST must containe a :name entry which is used as name of the block." - (unless (bolp) (newline)) - (let ((name (plist-get plist :name))) - (insert "#+BEGIN: " name) - (while plist - (if (eq (car plist) :name) - (setq plist (cddr plist)) - (insert " " (prin1-to-string (pop plist))))) - (insert "\n\n#+END:\n") - (beginning-of-line -2))) - -(defun org-prepare-dblock () - "Prepare dynamic block for refresh. -This empties the block, puts the cursor at the insert position and returns -the property list including an extra property :name with the block name." - (unless (looking-at org-dblock-start-re) - (error "Not at a dynamic block")) - (let* ((begdel (1+ (match-end 0))) - (name (match-string 1)) - (params (append (list :name name) - (read (concat "(" (match-string 3) ")"))))) - (unless (re-search-forward org-dblock-end-re nil t) - (error "Dynamic block not terminated")) - (delete-region begdel (match-beginning 0)) - (goto-char begdel) - (open-line 1) - params)) - -(defun org-map-dblocks (&optional command) - "Apply COMMAND to all dynamic blocks in the current buffer. -If COMMAND is not given, use `org-update-dblock'." - (let ((cmd (or command 'org-update-dblock)) - pos) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward org-dblock-start-re nil t) - (goto-char (setq pos (match-beginning 0))) - (condition-case nil - (funcall cmd) - (error (message "Error during update of dynamic block"))) - (goto-char pos) - (unless (re-search-forward org-dblock-end-re nil t) - (error "Dynamic block not terminated")))))) - -(defun org-dblock-update (&optional arg) - "User command for updating dynamic blocks. -Update the dynamic block at point. With prefix ARG, update all dynamic -blocks in the buffer." - (interactive "P") - (if arg - (org-update-all-dblocks) - (or (looking-at org-dblock-start-re) - (org-beginning-of-dblock)) - (org-update-dblock))) - -(defun org-update-dblock () - "Update the dynamic block at point -This means to empty the block, parse for parameters and then call -the correct writing function." - (let* ((pos (point)) - (params (org-prepare-dblock)) - (name (plist-get params :name)) - (cmd (intern (concat "org-dblock-write:" name)))) - (funcall cmd params) - (goto-char pos))) - -(defun org-beginning-of-dblock () - "Find the beginning of the dynamic block at point. -Error if there is no scuh block at point." - (let ((pos (point)) - beg) - (end-of-line 1) - (if (and (re-search-backward org-dblock-start-re nil t) - (setq beg (match-beginning 0)) - (re-search-forward org-dblock-end-re nil t) - (> (match-end 0) pos)) - (goto-char beg) - (goto-char pos) - (error "Not in a dynamic block")))) - -(defun org-update-all-dblocks () - "Update all dynamic blocks in the buffer. -This function can be used in a hook." - (when (org-mode-p) - (org-map-dblocks 'org-update-dblock))) - - -;;;; Completion - -(defun org-complete (&optional arg) - "Perform completion on word at point. -At the beginning of a headline, this completes TODO keywords as given in -`org-todo-keywords'. -If the current word is preceded by a backslash, completes the TeX symbols -that are supported for HTML support. -If the current word is preceded by \"#+\", completes special words for -setting file options. -In the line after \"#+STARTUP:, complete valid keywords.\" -At all other locations, this simply calls `ispell-complete-word'." - (interactive "P") - (catch 'exit - (let* ((end (point)) - (beg1 (save-excursion - (skip-chars-backward "a-zA-Z_@0-9") - (point))) - (beg (save-excursion - (skip-chars-backward "a-zA-Z0-9_:$") - (point))) - (confirm (lambda (x) (stringp (car x)))) - (camel (equal (char-before beg) ?*)) - (tag (equal (char-before beg1) ?:)) - (texp (equal (char-before beg) ?\\)) - (link (equal (char-before beg) ?\[)) - (opt (equal (buffer-substring (max (point-at-bol) (- beg 2)) - beg) - "#+")) - (startup (string-match "^#\\+STARTUP:.*" - (buffer-substring (point-at-bol) (point)))) - (completion-ignore-case opt) - (type nil) - (tbl nil) - (table (cond - (opt - (setq type :opt) - (mapcar (lambda (x) - (string-match "^#\\+\\(\\([A-Z_]+:?\\).*\\)" x) - (cons (match-string 2 x) (match-string 1 x))) - (org-split-string (org-get-current-options) "\n"))) - (startup - (setq type :startup) - org-startup-options) - (link (append org-link-abbrev-alist-local - org-link-abbrev-alist)) - (texp - (setq type :tex) - org-html-entities) - ((string-match "\\`\\*+[ \t]*\\'" - (buffer-substring (point-at-bol) beg)) - (setq type :todo) - (mapcar 'list org-todo-keywords)) - (camel - (setq type :camel) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward org-todo-line-regexp nil t) - (push (list - (if org-file-link-context-use-camel-case - (org-make-org-heading-camel (match-string 3) t) - (org-make-org-heading-search-string - (match-string 3) t))) - tbl))) - tbl) - (tag (setq type :tag beg beg1) - (or org-tag-alist (org-get-buffer-tags))) - (t (progn (ispell-complete-word arg) (throw 'exit nil))))) - (pattern (buffer-substring-no-properties beg end)) - (completion (try-completion pattern table confirm))) - (cond ((eq completion t) - (if (equal type :opt) - (insert (substring (cdr (assoc (upcase pattern) table)) - (length pattern))) - (if (equal type :tag) (insert ":")))) - ((null completion) - (message "Can't find completion for \"%s\"" pattern) - (ding)) - ((not (string= pattern completion)) - (delete-region beg end) - (if (string-match " +$" completion) - (setq completion (replace-match "" t t completion))) - (insert completion) - (if (get-buffer-window "*Completions*") - (delete-window (get-buffer-window "*Completions*"))) - (if (assoc completion table) - (if (eq type :todo) (insert " ") - (if (eq type :tag) (insert ":")))) - (if (and (equal type :opt) (assoc completion table)) - (message "%s" (substitute-command-keys - "Press \\[org-complete] again to insert example settings")))) - (t - (message "Making completion list...") - (let ((list (sort (all-completions pattern table confirm) - 'string<))) - (with-output-to-temp-buffer "*Completions*" - (condition-case nil - ;; Protection needed for XEmacs and emacs 21 - (display-completion-list list pattern) - (error (display-completion-list list))))) - (message "Making completion list...%s" "done")))))) - -;;;; Comments, TODO and DEADLINE - -(defun org-toggle-comment () - "Change the COMMENT state of an entry." - (interactive) - (save-excursion - (org-back-to-heading) - (if (looking-at (concat outline-regexp - "\\( +\\<" org-comment-string "\\>\\)")) - (replace-match "" t t nil 1) - (if (looking-at outline-regexp) - (progn - (goto-char (match-end 0)) - (insert " " org-comment-string)))))) - -(defvar org-last-todo-state-is-todo nil - "This is non-nil when the last TODO state change led to a TODO state. -If the last change removed the TODO tag or switched to DONE, then -this is nil.") - -(defun org-todo (&optional arg) - "Change the TODO state of an item. -The state of an item is given by a keyword at the start of the heading, -like - *** TODO Write paper - *** DONE Call mom - -The different keywords are specified in the variable `org-todo-keywords'. -By default the available states are \"TODO\" and \"DONE\". -So for this example: when the item starts with TODO, it is changed to DONE. -When it starts with DONE, the DONE is removed. And when neither TODO nor -DONE are present, add TODO at the beginning of the heading. - -With prefix arg, use completion to determine the new state. With numeric -prefix arg, switch to that state." - (interactive "P") - (save-excursion - (org-back-to-heading) - (if (looking-at outline-regexp) (goto-char (match-end 0))) - (or (looking-at (concat " +" org-todo-regexp " *")) - (looking-at " *")) - (let* ((this (match-string 1)) - (completion-ignore-case t) - (member (member this org-todo-keywords)) - (tail (cdr member)) - (state (cond - ((equal arg '(4)) - ;; Read a state with completion - (completing-read "State: " (mapcar (lambda(x) (list x)) - org-todo-keywords) - nil t)) - ((eq arg 'right) - (if this - (if tail (car tail) nil) - (car org-todo-keywords))) - ((eq arg 'left) - (if (equal member org-todo-keywords) - nil - (if this - (nth (- (length org-todo-keywords) (length tail) 2) - org-todo-keywords) - org-done-string))) - (arg - ;; user requests a specific state - (nth (1- (prefix-numeric-value arg)) - org-todo-keywords)) - ((null member) (car org-todo-keywords)) - ((null tail) nil) ;; -> first entry - ((eq org-todo-interpretation 'sequence) - (car tail)) - ((memq org-todo-interpretation '(type priority)) - (if (eq this-command last-command) - (car tail) - (if (> (length tail) 0) org-done-string nil))) - (t nil))) - (next (if state (concat " " state " ") " "))) - (replace-match next t t) - (setq org-last-todo-state-is-todo - (not (equal state org-done-string))) - (when org-log-done - (if (equal state org-done-string) - (org-add-planning-info 'closed (org-current-time) 'scheduled) - (if (not this) - (org-add-planning-info nil nil 'closed)))) - ;; Fixup tag positioning - (and org-auto-align-tags (org-set-tags nil t)) - (run-hooks 'org-after-todo-state-change-hook))) - ;; Fixup cursor location if close to the keyword - (if (and (outline-on-heading-p) - (not (bolp)) - (save-excursion (beginning-of-line 1) - (looking-at org-todo-line-regexp)) - (< (point) (+ 2 (or (match-end 2) (match-end 1))))) - (progn - (goto-char (or (match-end 2) (match-end 1))) - (just-one-space)))) - -(defun org-show-todo-tree (arg) - "Make a compact tree which shows all headlines marked with TODO. -The tree will show the lines where the regexp matches, and all higher -headlines above the match. -With \\[universal-argument] prefix, also show the DONE entries. -With a numeric prefix N, construct a sparse tree for the Nth element -of `org-todo-keywords'." - (interactive "P") - (let ((case-fold-search nil) - (kwd-re - (cond ((null arg) org-not-done-regexp) - ((equal arg '(4)) org-todo-regexp) - ((<= (prefix-numeric-value arg) (length org-todo-keywords)) - (regexp-quote (nth (1- (prefix-numeric-value arg)) - org-todo-keywords))) - (t (error "Invalid prefix argument: %s" arg))))) - (message "%d TODO entries found" - (org-occur (concat "^" outline-regexp " +" kwd-re ))))) - -(defun org-deadline () - "Insert the DEADLINE: string to make a deadline. -A timestamp is also inserted - use \\[org-timestamp-up] and \\[org-timestamp-down] -to modify it to the correct date." - (interactive) - (org-add-planning-info 'deadline nil 'closed)) - -(defun org-schedule () - "Insert the SCHEDULED: string to schedule a TODO item. -A timestamp is also inserted - use \\[org-timestamp-up] and \\[org-timestamp-down] -to modify it to the correct date." - (interactive) - (org-add-planning-info 'scheduled nil 'closed)) - -(defun org-add-planning-info (what &optional time &rest remove) - "Insert new timestamp with keyword in the line directly after the headline. -WHAT indicates what kind of time stamp to add. TIME indicated the time to use. -If non is given, the user is prompted for a date. -REMOVE indicates what kind of entries to remove. An old WHAT entry will also -be removed." - (interactive) - (when what (setq time (or time (org-read-date nil 'to-time)))) - (when (and org-insert-labeled-timestamps-at-point - (member what '(scheduled deadline))) - (insert - (if (eq what 'scheduled) org-scheduled-string org-deadline-string) " ") - (org-insert-time-stamp time) - (setq what nil)) - (save-excursion - (save-restriction - (let (col list elt ts buffer-invisibility-spec) - (org-back-to-heading t) - (looking-at (concat outline-regexp "\\( *\\)[^\r\n]*")) - (goto-char (match-end 1)) - (setq col (current-column)) - (goto-char (1+ (match-end 0))) - (if (and (not (looking-at outline-regexp)) - (looking-at (concat "[^\r\n]*?" org-keyword-time-regexp - "[^\r\n]*")) - (not (equal (match-string 1) org-clock-string))) - (narrow-to-region (match-beginning 0) (match-end 0)) - (insert "\n") - (backward-char 1) - (narrow-to-region (point) (point)) - (indent-to-column col)) - ;; Check if we have to remove something. - (setq list (cons what remove)) - (while list - (setq elt (pop list)) - (goto-char (point-min)) - (when (or (and (eq elt 'scheduled) - (re-search-forward org-scheduled-time-regexp nil t)) - (and (eq elt 'deadline) - (re-search-forward org-deadline-time-regexp nil t)) - (and (eq elt 'closed) - (re-search-forward org-closed-time-regexp nil t))) - (replace-match "") - (if (looking-at "--+<[^>]+>") (replace-match "")) - (if (looking-at " +") (replace-match "")))) - (goto-char (point-max)) - (when what - (insert - (if (not (equal (char-before) ?\ )) " " "") - (cond ((eq what 'scheduled) org-scheduled-string) - ((eq what 'deadline) org-deadline-string) - ((eq what 'closed) org-closed-string)) - " ") - (org-insert-time-stamp time nil (eq what 'closed)) - (end-of-line 1) - (and (eq what 'closed) (org-add-log-maybe 'done))) - (goto-char (point-min)) - (widen) - (if (looking-at "[ \t]+\r?\n") - (replace-match "")) - ts)))) - -(defvar org-log-note-marker (make-marker)) -(defvar org-log-note-purpose nil) -(defvar org-log-note-window-configuration nil) - -(defun org-add-log-maybe (&optional purpose) - (when (and (listp org-log-done) - (memq purpose org-log-done)) - (move-marker org-log-note-marker (point)) - (setq org-log-note-purpose purpose) - (add-hook 'post-command-hook 'org-add-log-note 'append))) - -(defun org-add-log-note (&optional purpose) - "Pop up a window for taking a note, and add this note later at point." - (remove-hook 'post-command-hook 'org-add-log-note) - (setq org-log-note-window-configuration (current-window-configuration)) - (delete-other-windows) - (switch-to-buffer (marker-buffer org-log-note-marker)) - (goto-char org-log-note-marker) - (switch-to-buffer-other-window "*Org Note*") - (erase-buffer) - (org-mode) - (insert (format "# Insert note for %s, finish with C-c C-c.\n\n" - (cond - ((eq org-log-note-purpose 'clock-out) "stopped clock") - ((eq org-log-note-purpose 'done) "closed todo item") - (t (error "This should not happen"))))) - (org-set-local 'org-finish-function 'org-store-log-note)) - -(defun org-store-log-note () - "Finish taking a log note, and insert it to where it belongs." - (let ((txt (buffer-string)) - (note (cdr (assq org-log-note-purpose org-log-note-headings))) - lines ind) - (kill-buffer (current-buffer)) - (if (string-match "^#.*\n[ \t\\n]*" txt) - (setq txt (replace-match "" t t txt))) - (when (string-match "\\S-" txt) - (if (string-match "\\s-+\\'" txt) - (setq txt (replace-match "" t t txt))) - (setq lines (org-split-string txt "\n")) - (when (and note (string-match "\\S-" note)) - (setq note - (org-replace-escapes - note - (list (cons "%u" user-login-name) - (cons "%U" user-full-name) - (cons "%t" (format-time-string - (org-time-stamp-format 'long 'inactive) - (current-time)))))) - (push note lines)) - (save-excursion - (set-buffer (marker-buffer org-log-note-marker)) - (save-excursion - (goto-char org-log-note-marker) - (if (not (bolp)) (newline)) - (indent-relative t) - (setq ind (concat (buffer-substring (point-at-bol) (point)) " ")) - (insert " - " (pop lines)) - (while lines - (insert "\n" ind (pop lines)))))) - (set-window-configuration org-log-note-window-configuration))) - -(defvar org-occur-highlights nil) -(make-variable-buffer-local 'org-occur-highlights) - -(defun org-occur (regexp &optional keep-previous callback) - "Make a compact tree which shows all matches of REGEXP. -The tree will show the lines where the regexp matches, and all higher -headlines above the match. It will also show the heading after the match, -to make sure editing the matching entry is easy. -If KEEP-PREVIOUS is non-nil, highlighting and exposing done by a previous -call to `org-occur' will be kept, to allow stacking of calls to this -command. -If CALLBACK is non-nil, it is a function which is called to confirm -that the match should indeed be shown." - (interactive "sRegexp: \nP") - (or keep-previous (org-remove-occur-highlights nil nil t)) - (let ((cnt 0)) - (save-excursion - (goto-char (point-min)) - (if (or (not keep-previous) ; do not want to keep - (not org-occur-highlights)) ; no previous matches - ;; hide everything - (org-overview)) - (while (re-search-forward regexp nil t) - (when (or (not callback) - (save-match-data (funcall callback))) - (setq cnt (1+ cnt)) - (org-highlight-new-match (match-beginning 0) (match-end 0)) - (org-show-context 'occur-tree)))) - (when org-remove-highlights-with-change - (org-add-hook 'before-change-functions 'org-remove-occur-highlights - nil 'local)) - (unless org-sparse-tree-open-archived-trees - (org-hide-archived-subtrees (point-min) (point-max))) - (run-hooks 'org-occur-hook) - (if (interactive-p) - (message "%d match(es) for regexp %s" cnt regexp)) - cnt)) - -(defun org-show-context (&optional key siblings) - "Make sure point and context and visible. -How much context is shown depends upon the variables -`org-show-hierarchy-above' and `org-show-following-heading'. -When SIBLINGS is non-nil, show all siblings on each hierarchy level." - (let ((heading-p (org-on-heading-p t)) - (hierarchy-p (org-get-alist-option org-show-hierarchy-above key)) - (following-p (org-get-alist-option org-show-following-heading key))) - (catch 'exit - ;; Show heading or entry text - (if heading-p - (org-flag-heading nil) ; only show the heading - (and (or (org-invisible-p) (org-invisible-p2)) - (org-show-hidden-entry))) ; show entire entry - (when following-p - ;; Show next sibling, or heading below text - (save-excursion - (and (if heading-p (org-goto-sibling) (outline-next-heading)) - (org-flag-heading nil)))) - (when hierarchy-p - ;; show all higher headings, possibly with siblings - (save-excursion - (while (and (condition-case nil - (progn (org-up-heading-all 1) t) - (error nil)) - (not (bobp))) - (org-flag-heading nil) - (when siblings - (save-excursion - (while (org-goto-sibling) (org-flag-heading nil))) - (save-excursion - (while (org-goto-sibling 'previous) - (org-flag-heading nil)))))))))) - -(defun org-reveal (&optional siblings) - "Show current entry, hierarchy above it, and the following headline. -This can be used to show a consistent set of context around locations -exposed with `org-show-hierarchy-above' or `org-show-following-heading' -not t for the search context. - -With optional argument SIBLINGS, on each level of the hierarchy all -siblings are shown. This repairs the tree structure so what it would -look like when opend with successive calls to `org-cycle'." - (interactive "P") - (let ((org-show-hierarchy-above t) - (org-show-following-heading t)) - (org-show-context nil siblings))) - -;; Overlay compatibility functions -(defun org-make-overlay (beg end &optional buffer) - (if (featurep 'xemacs) - (make-extent beg end buffer) - (make-overlay beg end buffer))) -(defun org-delete-overlay (ovl) - (if (featurep 'xemacs) (delete-extent ovl) (delete-overlay ovl))) -(defun org-detach-overlay (ovl) - (if (featurep 'xemacs) (detach-extent ovl) (delete-overlay ovl))) -(defun org-move-overlay (ovl beg end &optional buffer) - (if (featurep 'xemacs) - (set-extent-endpoints ovl beg end (or buffer (current-buffer))) - (move-overlay ovl beg end buffer))) -(defun org-overlay-put (ovl prop value) - (if (featurep 'xemacs) - (set-extent-property ovl prop value) - (overlay-put ovl prop value))) -(defun org-overlay-display (ovl text &optional face) - "Make overlay OVL display TEXT with face FACE." - (if (featurep 'xemacs) - (let ((gl (make-glyph text))) - (and face (set-glyph-face gl face)) - (set-extent-property ovl 'invisible t) - (set-extent-property ovl 'end-glyph gl)) - (overlay-put ovl 'display text) - (if face (overlay-put ovl 'face face)))) -(defun org-overlay-get (ovl prop) - (if (featurep 'xemacs) - (extent-property ovl prop) - (overlay-get ovl prop))) -(defun org-overlays-at (pos) - (if (featurep 'xemacs) (extents-at pos) (overlays-at pos))) -(defun org-overlays-in (&optional start end) - (if (featurep 'xemacs) - (extent-list nil start end) - (overlays-in start end))) -(defun org-overlay-start (o) - (if (featurep 'xemacs) (extent-start-position o) (overlay-start o))) -(defun org-overlay-end (o) - (if (featurep 'xemacs) (extent-end-position o) (overlay-end o))) -(defun org-find-overlays (prop &optional pos delete) - "Find all overlays specifying PROP at POS or point. -If DELETE is non-nil, delete all those overlays." - (let ((overlays (org-overlays-at (or pos (point)))) - ov found) - (while (setq ov (pop overlays)) - (if (org-overlay-get ov prop) - (if delete (org-delete-overlay ov) (push ov found)))) - found)) - -(defun org-highlight-new-match (beg end) - "Highlight from BEG to END and mark the highlight is an occur headline." - (let ((ov (org-make-overlay beg end))) - (org-overlay-put ov 'face 'secondary-selection) - (push ov org-occur-highlights))) - -(defvar org-inhibit-highlight-removal nil) -(defun org-remove-occur-highlights (&optional beg end noremove) - "Remove the occur highlights from the buffer. -BEG and END are ignored. If NOREMOVE is nil, remove this function -from the `before-change-functions' in the current buffer." - (interactive) - (unless org-inhibit-highlight-removal - (mapc 'org-delete-overlay org-occur-highlights) - (setq org-occur-highlights nil) - (unless noremove - (remove-hook 'before-change-functions - 'org-remove-occur-highlights 'local)))) - -;;;; Priorities - -(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z]\\)\\] ?\\)" - "Regular expression matching the priority indicator.") - -(defvar org-remove-priority-next-time nil) - -(defun org-priority-up () - "Increase the priority of the current item." - (interactive) - (org-priority 'up)) - -(defun org-priority-down () - "Decrease the priority of the current item." - (interactive) - (org-priority 'down)) - -(defun org-priority (&optional action) - "Change the priority of an item by ARG. -ACTION can be set, up, or down." - (interactive) - (setq action (or action 'set)) - (let (current new news have remove) - (save-excursion - (org-back-to-heading) - (if (looking-at org-priority-regexp) - (setq current (string-to-char (match-string 2)) - have t) - (setq current org-default-priority)) - (cond - ((eq action 'set) - (message "Priority A-%c, SPC to remove: " org-lowest-priority) - (setq new (read-char-exclusive)) - (cond ((equal new ?\ ) (setq remove t)) - ((or (< (upcase new) ?A) (> (upcase new) org-lowest-priority)) - (error "Priority must be between `%c' and `%c'" - ?A org-lowest-priority)))) - ((eq action 'up) - (setq new (1- current))) - ((eq action 'down) - (setq new (1+ current))) - (t (error "Invalid action"))) - (setq new (min (max ?A (upcase new)) org-lowest-priority)) - (setq news (format "%c" new)) - (if have - (if remove - (replace-match "" t t nil 1) - (replace-match news t t nil 2)) - (if remove - (error "No priority cookie found in line") - (looking-at org-todo-line-regexp) - (if (match-end 2) - (progn - (goto-char (match-end 2)) - (insert " [#" news "]")) - (goto-char (match-beginning 3)) - (insert "[#" news "] "))))) - (if remove - (message "Priority removed") - (message "Priority of current item set to %s" news)))) - - -(defun org-get-priority (s) - "Find priority cookie and return priority." - (save-match-data - (if (not (string-match org-priority-regexp s)) - (* 1000 (- org-lowest-priority org-default-priority)) - (* 1000 (- org-lowest-priority - (string-to-char (match-string 2 s))))))) - -;;;; Timestamps - -(defvar org-last-changed-timestamp nil) - -(defun org-time-stamp (arg) - "Prompt for a date/time and insert a time stamp. -If the user specifies a time like HH:MM, or if this command is called -with a prefix argument, the time stamp will contain date and time. -Otherwise, only the date will be included. All parts of a date not -specified by the user will be filled in from the current date/time. -So if you press just return without typing anything, the time stamp -will represent the current date/time. If there is already a timestamp -at the cursor, it will be modified." - (interactive "P") - (let (org-time-was-given time) - (cond - ((and (org-at-timestamp-p) - (eq last-command 'org-time-stamp) - (eq this-command 'org-time-stamp)) - (insert "--") - (setq time (let ((this-command this-command)) - (org-read-date arg 'totime))) - (org-insert-time-stamp time (or org-time-was-given arg))) - ((org-at-timestamp-p) - (setq time (let ((this-command this-command)) - (org-read-date arg 'totime))) - (when (org-at-timestamp-p) ; just to get the match data - (replace-match "") - (setq org-last-changed-timestamp - (org-insert-time-stamp time (or org-time-was-given arg)))) - (message "Timestamp updated")) - (t - (setq time (let ((this-command this-command)) - (org-read-date arg 'totime))) - (org-insert-time-stamp time (or org-time-was-given arg)))))) - -(defun org-time-stamp-inactive (&optional arg) - "Insert an inactive time stamp. -An inactive time stamp is enclosed in square brackets instead of angle -brackets. It is inactive in the sense that it does not trigger agenda entries, -does not link to the calendar and cannot be changed with the S-cursor keys. -So these are more for recording a certain time/date." - (interactive "P") - (let (org-time-was-given time) - (setq time (org-read-date arg 'totime)) - (org-insert-time-stamp time (or org-time-was-given arg) 'inactive))) - -(defvar org-date-ovl (org-make-overlay 1 1)) -(org-overlay-put org-date-ovl 'face 'org-warning) -(org-detach-overlay org-date-ovl) - -(defun org-read-date (&optional with-time to-time from-string) - "Read a date and make things smooth for the user. -The prompt will suggest to enter an ISO date, but you can also enter anything -which will at least partially be understood by `parse-time-string'. -Unrecognized parts of the date will default to the current day, month, year, -hour and minute. For example, - 3-2-5 --> 2003-02-05 - feb 15 --> currentyear-02-15 - sep 12 9 --> 2009-09-12 - 12:45 --> today 12:45 - 22 sept 0:34 --> currentyear-09-22 0:34 - 12 --> currentyear-currentmonth-12 - Fri --> nearest Friday (today or later) - +4 --> four days from today (only if +N is the only thing given) - etc. -The function understands only English month and weekday abbreviations, -but this can be configured with the variables `parse-time-months' and -`parse-time-weekdays'. - -While prompting, a calendar is popped up - you can also select the -date with the mouse (button 1). The calendar shows a period of three -months. To scroll it to other months, use the keys `>' and `<'. -If you don't like the calendar, turn it off with - \(setq org-popup-calendar-for-date-prompt nil) - -With optional argument TO-TIME, the date will immediately be converted -to an internal time. -With an optional argument WITH-TIME, the prompt will suggest to also -insert a time. Note that when WITH-TIME is not set, you can still -enter a time, and this function will inform the calling routine about -this change. The calling routine may then choose to change the format -used to insert the time stamp into the buffer to include the time." - (require 'parse-time) - (let* ((org-time-stamp-rounding-minutes - (if (equal with-time '(16)) 0 org-time-stamp-rounding-minutes)) - (ct (org-current-time)) - (default-time - ;; Default time is either today, or, when entering a range, - ;; the range start. - (if (save-excursion - (re-search-backward - (concat org-ts-regexp "--?-?\\=") ; 1-3 minuses - (- (point) 20) t)) - (apply - 'encode-time - (mapcar (lambda(x) (or x 0)) - (parse-time-string (match-string 1)))) - ct)) - (calendar-move-hook nil) - (view-diary-entries-initially nil) - (view-calendar-holidays-initially nil) - (timestr (format-time-string - (if with-time "%Y-%m-%d %H:%M" "%Y-%m-%d") default-time)) - (prompt (format "YYYY-MM-DD [%s]: " timestr)) - ans ans1 ans2 (deltadays 0) - second minute hour day month year tl wday wday1) - - (cond - (from-string (setq ans from-string)) - (org-popup-calendar-for-date-prompt - (save-excursion - (save-window-excursion - (calendar) - (calendar-forward-day (- (time-to-days default-time) - (calendar-absolute-from-gregorian - (calendar-current-date)))) - (org-eval-in-calendar nil) - (let* ((old-map (current-local-map)) - (map (copy-keymap calendar-mode-map)) - (minibuffer-local-map (copy-keymap minibuffer-local-map))) - (define-key map (kbd "RET") 'org-calendar-select) - (define-key map (if (featurep 'xemacs) [button1] [mouse-1]) - 'org-calendar-select-mouse) - (define-key map (if (featurep 'xemacs) [button2] [mouse-2]) - 'org-calendar-select-mouse) - (define-key minibuffer-local-map [(meta shift left)] - (lambda () (interactive) - (org-eval-in-calendar '(calendar-backward-month 1)))) - (define-key minibuffer-local-map [(meta shift right)] - (lambda () (interactive) - (org-eval-in-calendar '(calendar-forward-month 1)))) - (define-key minibuffer-local-map [(shift up)] - (lambda () (interactive) - (org-eval-in-calendar '(calendar-backward-week 1)))) - (define-key minibuffer-local-map [(shift down)] - (lambda () (interactive) - (org-eval-in-calendar '(calendar-forward-week 1)))) - (define-key minibuffer-local-map [(shift left)] - (lambda () (interactive) - (org-eval-in-calendar '(calendar-backward-day 1)))) - (define-key minibuffer-local-map [(shift right)] - (lambda () (interactive) - (org-eval-in-calendar '(calendar-forward-day 1)))) - (define-key minibuffer-local-map ">" - (lambda () (interactive) - (org-eval-in-calendar '(scroll-calendar-left 1)))) - (define-key minibuffer-local-map "<" - (lambda () (interactive) - (org-eval-in-calendar '(scroll-calendar-right 1)))) - (unwind-protect - (progn - (use-local-map map) - (setq ans (read-string prompt "" nil nil)) - (if (not (string-match "\\S-" ans)) (setq ans nil)) - (setq ans (or ans1 ans ans2))) - (use-local-map old-map)))))) - (t ; Naked prompt only - (setq ans (read-string prompt "" nil timestr)))) - (org-detach-overlay org-date-ovl) - - (if (string-match "^[ \t]*[-+][0-9]+[ \t]*$" ans) - (setq deltadays (string-to-number ans) ans "")) - - (if (string-match - "^ *\\(\\([0-9]+\\)-\\)?\\([0-1]?[0-9]\\)-\\([0-3]?[0-9]\\)\\([^-0-9]\\|$\\)" ans) - (progn - (setq year (if (match-end 2) - (string-to-number (match-string 2 ans)) - (string-to-number (format-time-string "%Y"))) - month (string-to-number (match-string 3 ans)) - day (string-to-number (match-string 4 ans))) - (if (< year 100) (setq year (+ 2000 year))) - (setq ans (replace-match (format "%04d-%02d-%02d\\5" year month day) - t nil ans)))) - (setq tl (parse-time-string ans) - year (or (nth 5 tl) (string-to-number (format-time-string "%Y" ct))) - month (or (nth 4 tl) (string-to-number (format-time-string "%m" ct))) - day (or (nth 3 tl) (string-to-number (format-time-string "%d" ct))) - hour (or (nth 2 tl) (string-to-number (format-time-string "%H" ct))) - minute (or (nth 1 tl) (string-to-number (format-time-string "%M" ct))) - second (or (nth 0 tl) 0) - wday (nth 6 tl)) - (setq day (+ day deltadays)) - (when (and wday (not (nth 3 tl))) - ;; Weekday was given, but no day, so pick that day in the week - ;; on or after the derived date. - (setq wday1 (nth 6 (decode-time (encode-time 0 0 0 day month year)))) - (unless (equal wday wday1) - (setq day (+ day (% (- wday wday1 -7) 7))))) - (if (and (boundp 'org-time-was-given) - (nth 2 tl)) - (setq org-time-was-given t)) - (if (< year 100) (setq year (+ 2000 year))) - (if to-time - (encode-time second minute hour day month year) - (if (or (nth 1 tl) (nth 2 tl)) - (format "%04d-%02d-%02d %02d:%02d" year month day hour minute) - (format "%04d-%02d-%02d" year month day))))) - -(defun org-eval-in-calendar (form) - "Eval FORM in the calendar window and return to current window. -Also, store the cursor date in variable ans2." - (let ((sw (selected-window))) - (select-window (get-buffer-window "*Calendar*")) - (eval form) - (when (calendar-cursor-to-date) - (let* ((date (calendar-cursor-to-date)) - (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))) - (setq ans2 (format-time-string "%Y-%m-%d" time)))) - (org-move-overlay org-date-ovl (1- (point)) (1+ (point)) (current-buffer)) - (select-window sw))) - -(defun org-calendar-select () - "Return to `org-read-date' with the date currently selected. -This is used by `org-read-date' in a temporary keymap for the calendar buffer." - (interactive) - (when (calendar-cursor-to-date) - (let* ((date (calendar-cursor-to-date)) - (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))) - (setq ans1 (format-time-string "%Y-%m-%d" time))) - (if (active-minibuffer-window) (exit-minibuffer)))) - -(defun org-insert-time-stamp (time &optional with-hm inactive pre post) - "Insert a date stamp for the date given by the internal TIME. -WITH-HM means, use the stamp format that includes the time of the day. -INACTIVE means use square brackets instead of angular ones, so that the -stamp will not contribute to the agenda. -PRE and POST are optional strings to be inserted before and after the -stamp. -The command returns the inserted time stamp." - (let ((fmt (funcall (if with-hm 'cdr 'car) org-time-stamp-formats)) - stamp) - (if inactive (setq fmt (concat "[" (substring fmt 1 -1) "]"))) - (insert (or pre "")) - (insert (setq stamp (format-time-string fmt time))) - (insert (or post "")) - stamp)) - -(defun org-toggle-time-stamp-overlays () - "Toggle the use of custom time stamp formats." - (interactive) - (setq org-display-custom-times (not org-display-custom-times)) - (unless org-display-custom-times - (let ((p (point-min)) (bmp (buffer-modified-p))) - (while (setq p (next-single-property-change p 'display)) - (if (and (get-text-property p 'display) - (eq (get-text-property p 'face) 'org-date)) - (remove-text-properties - p (setq p (next-single-property-change p 'display)) - '(display t)))) - (set-buffer-modified-p bmp))) - (if (featurep 'xemacs) - (remove-text-properties (point-min) (point-max) '(end-glyph t))) - (org-restart-font-lock) - (setq org-table-may-need-update t) - (if org-display-custom-times - (message "Time stamps are overlayed with custom format") - (message "Time stamp overlays removed"))) - -(defun org-display-custom-time (beg end) - "Overlay modified time stamp format over timestamp between BED and END." - (let* ((t1 (save-match-data - (org-parse-time-string (buffer-substring beg end) t))) - (w1 (- end beg)) - (with-hm (and (nth 1 t1) (nth 2 t1))) - (inactive (= (char-before (1- beg)) ?\[)) - (tf (funcall (if with-hm 'cdr 'car) org-time-stamp-custom-formats)) - (time (org-fix-decoded-time t1)) - (str (org-add-props - (format-time-string - (substring tf 1 -1) (apply 'encode-time time)) - nil 'mouse-face 'highlight)) - (w2 (length str))) - (if (not (= w2 w1)) - (add-text-properties (1+ beg) (+ 2 beg) - (list 'org-dwidth t 'org-dwidth-n (- w1 w2)))) - (if (featurep 'xemacs) - (progn - (put-text-property beg end 'invisible t) - (put-text-property beg end 'end-glyph (make-glyph str))) - (put-text-property beg end 'display str)))) - -(defun org-translate-time (string) - "Translate all timestamps in STRING to custom format. -But do this only if the variable `org-display-custom-times' is set." - (when org-display-custom-times - (save-match-data - (let* ((start 0) - (re org-ts-regexp-both) - t1 with-hm inactive tf time str beg end) - (while (setq start (string-match re string start)) - (setq beg (match-beginning 0) - end (match-end 0) - t1 (save-match-data - (org-parse-time-string (substring string beg end) t)) - with-hm (and (nth 1 t1) (nth 2 t1)) - inactive (equal (substring string beg (1+ beg)) "[") - tf (funcall (if with-hm 'cdr 'car) - org-time-stamp-custom-formats) - time (org-fix-decoded-time t1) - str (format-time-string - (concat - (if inactive "[" "<") (substring tf 1 -1) - (if inactive "]" ">")) - (apply 'encode-time time)) - string (replace-match str t t string) - start (+ start (length str))))))) - string) - -(defun org-fix-decoded-time (time) - "Set 0 instead of nil for the first 6 elements of time. -Don't touch the rest." - (let ((n 0)) - (mapcar (lambda (x) (if (< (setq n (1+ n)) 7) (or x 0) x)) time))) - -(defun org-days-to-time (timestamp-string) - "Difference between TIMESTAMP-STRING and now in days." - (- (time-to-days (org-time-string-to-time timestamp-string)) - (time-to-days (current-time)))) - -(defun org-deadline-close (timestamp-string &optional ndays) - "Is the time in TIMESTAMP-STRING close to the current date?" - (and (< (org-days-to-time timestamp-string) - (or ndays org-deadline-warning-days)) - (not (org-entry-is-done-p)))) - -(defun org-calendar-select-mouse (ev) - "Return to `org-read-date' with the date currently selected. -This is used by `org-read-date' in a temporary keymap for the calendar buffer." - (interactive "e") - (mouse-set-point ev) - (when (calendar-cursor-to-date) - (let* ((date (calendar-cursor-to-date)) - (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))) - (setq ans1 (format-time-string "%Y-%m-%d" time))) - (if (active-minibuffer-window) (exit-minibuffer)))) - -(defun org-check-deadlines (ndays) - "Check if there are any deadlines due or past due. -A deadline is considered due if it happens within `org-deadline-warning-days' -days from today's date. If the deadline appears in an entry marked DONE, -it is not shown. The prefix arg NDAYS can be used to test that many -days. If the prefix is a raw \\[universal-argument] prefix, all deadlines are shown." - (interactive "P") - (let* ((org-warn-days - (cond - ((equal ndays '(4)) 100000) - (ndays (prefix-numeric-value ndays)) - (t org-deadline-warning-days))) - (case-fold-search nil) - (regexp (concat "\\<" org-deadline-string " *<\\([^>]+\\)>")) - (callback - (lambda () (org-deadline-close (match-string 1) org-warn-days)))) - - (message "%d deadlines past-due or due within %d days" - (org-occur regexp nil callback) - org-warn-days))) - -(defun org-evaluate-time-range (&optional to-buffer) - "Evaluate a time range by computing the difference between start and end. -Normally the result is just printed in the echo area, but with prefix arg -TO-BUFFER, the result is inserted just after the date stamp into the buffer. -If the time range is actually in a table, the result is inserted into the -next column. -For time difference computation, a year is assumed to be exactly 365 -days in order to avoid rounding problems." - (interactive "P") - (or - (org-clock-update-time-maybe) - (save-excursion - (unless (org-at-date-range-p) - (goto-char (point-at-bol)) - (re-search-forward org-tr-regexp (point-at-eol) t)) - (if (not (org-at-date-range-p)) - (error "Not at a time-stamp range, and none found in current line"))) - (let* ((ts1 (match-string 1)) - (ts2 (match-string 2)) - (havetime (or (> (length ts1) 15) (> (length ts2) 15))) - (match-end (match-end 0)) - (time1 (org-time-string-to-time ts1)) - (time2 (org-time-string-to-time ts2)) - (t1 (time-to-seconds time1)) - (t2 (time-to-seconds time2)) - (diff (abs (- t2 t1))) - (negative (< (- t2 t1) 0)) - ;; (ys (floor (* 365 24 60 60))) - (ds (* 24 60 60)) - (hs (* 60 60)) - (fy "%dy %dd %02d:%02d") - (fy1 "%dy %dd") - (fd "%dd %02d:%02d") - (fd1 "%dd") - (fh "%02d:%02d") - y d h m align) - (if havetime - (setq ; y (floor (/ diff ys)) diff (mod diff ys) - y 0 - d (floor (/ diff ds)) diff (mod diff ds) - h (floor (/ diff hs)) diff (mod diff hs) - m (floor (/ diff 60))) - (setq ; y (floor (/ diff ys)) diff (mod diff ys) - y 0 - d (floor (+ (/ diff ds) 0.5)) - h 0 m 0)) - (if (not to-buffer) - (message (org-make-tdiff-string y d h m)) - (when (org-at-table-p) - (goto-char match-end) - (setq align t) - (and (looking-at " *|") (goto-char (match-end 0)))) - (if (looking-at - "\\( *-? *[0-9]+y\\)?\\( *[0-9]+d\\)? *[0-9][0-9]:[0-9][0-9]") - (replace-match "")) - (if negative (insert " -")) - (if (> y 0) (insert " " (format (if havetime fy fy1) y d h m)) - (if (> d 0) (insert " " (format (if havetime fd fd1) d h m)) - (insert " " (format fh h m)))) - (if align (org-table-align)) - (message "Time difference inserted"))))) - -(defun org-make-tdiff-string (y d h m) - (let ((fmt "") - (l nil)) - (if (> y 0) (setq fmt (concat fmt "%d year" (if (> y 1) "s" "") " ") - l (push y l))) - (if (> d 0) (setq fmt (concat fmt "%d day" (if (> d 1) "s" "") " ") - l (push d l))) - (if (> h 0) (setq fmt (concat fmt "%d hour" (if (> h 1) "s" "") " ") - l (push h l))) - (if (> m 0) (setq fmt (concat fmt "%d minute" (if (> m 1) "s" "") " ") - l (push m l))) - (apply 'format fmt (nreverse l)))) - -(defun org-time-string-to-time (s) - (apply 'encode-time (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'. -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 - (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)) - nil nil nil) - (make-list 9 0))) - -(defun org-timestamp-up (&optional arg) - "Increase the date item at the cursor by one. -If the cursor is on the year, change the year. If it is on the month or -the day, change that. -With prefix ARG, change by that many units." - (interactive "p") - (org-timestamp-change (prefix-numeric-value arg))) - -(defun org-timestamp-down (&optional arg) - "Decrease the date item at the cursor by one. -If the cursor is on the year, change the year. If it is on the month or -the day, change that. -With prefix ARG, change by that many units." - (interactive "p") - (org-timestamp-change (- (prefix-numeric-value arg)))) - -(defun org-timestamp-up-day (&optional arg) - "Increase the date in the time stamp by one day. -With prefix ARG, change that many days." - (interactive "p") - (if (and (not (org-at-timestamp-p t)) - (org-on-heading-p)) - (org-todo 'up) - (org-timestamp-change (prefix-numeric-value arg) 'day))) - -(defun org-timestamp-down-day (&optional arg) - "Decrease the date in the time stamp by one day. -With prefix ARG, change that many days." - (interactive "p") - (if (and (not (org-at-timestamp-p t)) - (org-on-heading-p)) - (org-todo 'down) - (org-timestamp-change (- (prefix-numeric-value arg)) 'day))) - -(defsubst org-pos-in-match-range (pos n) - (and (match-beginning n) - (<= (match-beginning n) pos) - (>= (match-end n) pos))) - -(defun org-at-timestamp-p (&optional inactive-ok) - "Determine if the cursor is in or at a timestamp." - (interactive) - (let* ((tsr (if inactive-ok org-ts-regexp3 org-ts-regexp2)) - (pos (point)) - (ans (or (looking-at tsr) - (save-excursion - (skip-chars-backward "^[<\n\r\t") - (if (> (point) 1) (backward-char 1)) - (and (looking-at tsr) - (> (- (match-end 0) pos) -1)))))) - (and (boundp 'org-ts-what) - (setq org-ts-what - (cond - ((org-pos-in-match-range pos 2) 'year) - ((org-pos-in-match-range pos 3) 'month) - ((org-pos-in-match-range pos 7) 'hour) - ((org-pos-in-match-range pos 8) 'minute) - ((or (org-pos-in-match-range pos 4) - (org-pos-in-match-range pos 5)) 'day) - (t 'day)))) - ans)) - -(defun org-timestamp-change (n &optional what) - "Change the date in the time stamp at point. -The date will be changed by N times WHAT. WHAT can be `day', `month', -`year', `minute', `second'. If WHAT is not given, the cursor position -in the timestamp determines what will be changed." - (let ((pos (point)) - with-hm inactive - org-ts-what - ts time time0) - (if (not (org-at-timestamp-p t)) - (error "Not at a timestamp")) - (if (and (not what) (not (eq org-ts-what 'day)) - org-display-custom-times - (get-text-property (point) 'display) - (not (get-text-property (1- (point)) 'display))) - (setq org-ts-what 'day)) - (setq org-ts-what (or what org-ts-what) - with-hm (<= (abs (- (cdr org-ts-lengths) - (- (match-end 0) (match-beginning 0)))) - 1) - inactive (= (char-after (match-beginning 0)) ?\[) - ts (match-string 0)) - (replace-match "") - (setq time0 (org-parse-time-string ts)) - (setq time - (apply 'encode-time - (append - (list (or (car time0) 0)) - (list (+ (if (eq org-ts-what 'minute) n 0) (nth 1 time0))) - (list (+ (if (eq org-ts-what 'hour) n 0) (nth 2 time0))) - (list (+ (if (eq org-ts-what 'day) n 0) (nth 3 time0))) - (list (+ (if (eq org-ts-what 'month) n 0) (nth 4 time0))) - (list (+ (if (eq org-ts-what 'year) n 0) (nth 5 time0))) - (nthcdr 6 time0)))) - (if (eq what 'calendar) - (let ((cal-date - (save-excursion - (save-match-data - (set-buffer "*Calendar*") - (calendar-cursor-to-date))))) - (setcar (nthcdr 4 time0) (nth 0 cal-date)) ; month - (setcar (nthcdr 3 time0) (nth 1 cal-date)) ; day - (setcar (nthcdr 5 time0) (nth 2 cal-date)) ; year - (setcar time0 (or (car time0) 0)) - (setcar (nthcdr 1 time0) (or (nth 1 time0) 0)) - (setcar (nthcdr 2 time0) (or (nth 1 time0) 0)) - (setq time (apply 'encode-time time0)))) - (setq org-last-changed-timestamp - (org-insert-time-stamp time with-hm inactive)) - (org-clock-update-time-maybe) - (goto-char pos) - ;; Try to recenter the calendar window, if any - (if (and org-calendar-follow-timestamp-change - (get-buffer-window "*Calendar*" t) - (memq org-ts-what '(day month year))) - (org-recenter-calendar (time-to-days time))))) - -(defun org-recenter-calendar (date) - "If the calendar is visible, recenter it to DATE." - (let* ((win (selected-window)) - (cwin (get-buffer-window "*Calendar*" t)) - (calendar-move-hook nil)) - (when cwin - (select-window cwin) - (calendar-goto-date (if (listp date) date - (calendar-gregorian-from-absolute date))) - (select-window win)))) - -(defun org-goto-calendar (&optional arg) - "Go to the Emacs calendar at the current date. -If there is a time stamp in the current line, go to that date. -A prefix ARG can be used to force the current date." - (interactive "P") - (let ((tsr org-ts-regexp) diff - (calendar-move-hook nil) - (view-calendar-holidays-initially nil) - (view-diary-entries-initially nil)) - (if (or (org-at-timestamp-p) - (save-excursion - (beginning-of-line 1) - (looking-at (concat ".*" tsr)))) - (let ((d1 (time-to-days (current-time))) - (d2 (time-to-days - (org-time-string-to-time (match-string 1))))) - (setq diff (- d2 d1)))) - (calendar) - (calendar-goto-today) - (if (and diff (not arg)) (calendar-forward-day diff)))) - -(defun org-date-from-calendar () - "Insert time stamp corresponding to cursor date in *Calendar* buffer. -If there is already a time stamp at the cursor position, update it." - (interactive) - (org-timestamp-change 0 'calendar)) - -;;; The clock for measuring work time. - -(defvar org-clock-marker (make-marker) - "Marker recording the last clock-in.") - -(defun org-clock-in () - "Start the clock on the current item. -If necessary, clock-out of the currently active clock." - (interactive) - (org-clock-out t) - (let (ts) - (save-excursion - (org-back-to-heading t) - (beginning-of-line 2) - (if (and (looking-at (concat "[ \t]*" org-keyword-time-regexp)) - (not (equal (match-string 1) org-clock-string))) - (beginning-of-line 1)) - (insert "\n") (backward-char 1) - (indent-relative) - (insert org-clock-string " ") - (setq ts (org-insert-time-stamp (current-time) 'with-hm 'inactive)) - (move-marker org-clock-marker (point)) - (message "Clock started at %s" ts)))) - -(defun org-clock-out (&optional fail-quietly) - "Stop the currently running clock. -If there is no running clock, throw an error, unless FAIL-QUIETLY is set." - (interactive) - (catch 'exit - (if (not (marker-buffer org-clock-marker)) - (if fail-quietly (throw 'exit t) (error "No active clock"))) - (let (ts te s h m) - (save-excursion - (set-buffer (marker-buffer org-clock-marker)) - (goto-char org-clock-marker) - (beginning-of-line 1) - (if (and (looking-at (concat "[ \t]*" org-keyword-time-regexp)) - (equal (match-string 1) org-clock-string)) - (setq ts (match-string 2)) - (if fail-quietly (throw 'exit nil) (error "Clock start time is gone"))) - (goto-char org-clock-marker) - (insert "--") - (setq te (org-insert-time-stamp (current-time) 'with-hm 'inactive)) - (setq s (- (time-to-seconds (apply 'encode-time (org-parse-time-string te))) - (time-to-seconds (apply 'encode-time (org-parse-time-string ts)))) - h (floor (/ s 3600)) - s (- s (* 3600 h)) - m (floor (/ s 60)) - s (- s (* 60 s))) - (insert " => " (format "%2d:%02d" h m)) - (move-marker org-clock-marker nil) - (org-add-log-maybe 'clock-out) - (message "Clock stopped at %s after HH:MM = %d:%02d" te h m))))) - -(defun org-clock-cancel () - "Cancel the running clock be removing the start timestamp." - (interactive) - (if (not (marker-buffer org-clock-marker)) - (error "No active clock")) - (save-excursion - (set-buffer (marker-buffer org-clock-marker)) - (goto-char org-clock-marker) - (delete-region (1- (point-at-bol)) (point-at-eol))) - (message "Clock canceled")) - -(defvar org-clock-file-total-minutes nil - "Holds the file total time in minutes, after a call to `org-clock-sum'.") - (make-variable-buffer-local 'org-clock-file-total-minutes) - -(defun org-clock-sum (&optional tstart tend) - "Sum the times for each subtree. -Puts the resulting times in minutes as a text property on each headline." - (interactive) - (let* ((bmp (buffer-modified-p)) - (re (concat "^\\(\\*+\\)[ \t]\\|^[ \t]*" - org-clock-string - "[ \t]*\\(\\[.*?\\]\\)-+\\(\\[.*?\\]\\)")) - (lmax 30) - (ltimes (make-vector lmax 0)) - (t1 0) - (level 0) - ts te dt - time) - (remove-text-properties (point-min) (point-max) '(:org-clock-minutes t)) - (save-excursion - (goto-char (point-max)) - (while (re-search-backward re nil t) - (if (match-end 2) - ;; A time - (setq ts (match-string 2) - te (match-string 3) - ts (time-to-seconds - (apply 'encode-time (org-parse-time-string ts))) - te (time-to-seconds - (apply 'encode-time (org-parse-time-string te))) - ts (if tstart (max ts tstart) ts) - te (if tend (min te tend) te) - dt (- te ts) - t1 (if (> dt 0) (+ t1 (floor (/ dt 60))) t1)) - ;; A headline - (setq level (- (match-end 1) (match-beginning 1))) - (when (or (> t1 0) (> (aref ltimes level) 0)) - (loop for l from 0 to level do - (aset ltimes l (+ (aref ltimes l) t1))) - (setq t1 0 time (aref ltimes level)) - (loop for l from level to (1- lmax) do - (aset ltimes l 0)) - (goto-char (match-beginning 0)) - (put-text-property (point) (point-at-eol) :org-clock-minutes time)))) - (setq org-clock-file-total-minutes (aref ltimes 0))) - (set-buffer-modified-p bmp))) - -(defun org-clock-display (&optional total-only) - "Show subtree times in the entire buffer. -If TOTAL-ONLY is non-nil, only show the total time for the entire file -in the echo area." - (interactive) - (org-remove-clock-overlays) - (let (time h m p) - (org-clock-sum) - (unless total-only - (save-excursion - (goto-char (point-min)) - (while (setq p (next-single-property-change (point) :org-clock-minutes)) - (goto-char p) - (when (setq time (get-text-property p :org-clock-minutes)) - (org-put-clock-overlay time (funcall outline-level)))) - (setq h (/ org-clock-file-total-minutes 60) - m (- org-clock-file-total-minutes (* 60 h))) - ;; Arrange to remove the overlays upon next change. - (when org-remove-highlights-with-change - (org-add-hook 'before-change-functions 'org-remove-clock-overlays - nil 'local)))) - (message "Total file time: %d:%02d (%d hours and %d minutes)" h m h m))) - -(defvar org-clock-overlays nil) -(make-variable-buffer-local 'org-clock-overlays) - -(defun org-put-clock-overlay (time &optional level) - "Put an overlays on the current line, displaying TIME. -If LEVEL is given, prefix time with a corresponding number of stars. -This creates a new overlay and stores it in `org-clock-overlays', so that it -will be easy to remove." - (let* ((c 60) (h (floor (/ time 60))) (m (- time (* 60 h))) - (l (if level (org-get-legal-level level 0) 0)) - (off 0) - ov tx) - (move-to-column c) - (unless (eolp) (skip-chars-backward "^ \t")) - (skip-chars-backward " \t") - (setq ov (org-make-overlay (1- (point)) (point-at-eol)) - tx (concat (buffer-substring (1- (point)) (point)) - (make-string (+ off (max 0 (- c (current-column)))) ?.) - (org-add-props (format "%s %2d:%02d%s" - (make-string l ?*) h m - (make-string (- 10 l) ?\ )) - '(face secondary-selection)) - "")) - (if (not (featurep 'xemacs)) - (org-overlay-put ov 'display tx) - (org-overlay-put ov 'invisible t) - (org-overlay-put ov 'end-glyph (make-glyph tx))) - (push ov org-clock-overlays))) - -(defun org-remove-clock-overlays (&optional beg end noremove) - "Remove the occur highlights from the buffer. -BEG and END are ignored. If NOREMOVE is nil, remove this function -from the `before-change-functions' in the current buffer." - (interactive) - (unless org-inhibit-highlight-removal - (mapc 'org-delete-overlay org-clock-overlays) - (setq org-clock-overlays nil) - (unless noremove - (remove-hook 'before-change-functions - 'org-remove-clock-overlays 'local)))) - -(defun org-clock-out-if-current () - "Clock out if the current entry contains the running clock. -This is used to stop the clock after a TODO entry is marked DONE." - (when (and (equal state org-done-string) - (equal (marker-buffer org-clock-marker) (current-buffer)) - (< (point) org-clock-marker) - (> (save-excursion (outline-next-heading) (point)) - org-clock-marker)) - ;; Clock out, but don't accept a logging message for this. - (let ((org-log-done (if (and (listp org-log-done) - (member 'clock-out org-log-done)) - '(done) - org-log-done))) - (org-clock-out)))) - -(add-hook 'org-after-todo-state-change-hook - 'org-clock-out-if-current) - -(defun org-check-running-clock () - "Check if the current buffer contains the running clock. -If yes, offer to stop it and to save the buffer with the changes." - (when (and (equal (marker-buffer org-clock-marker) (current-buffer)) - (y-or-n-p (format "Clock-out in buffer %s before killing it? " - (buffer-name)))) - (org-clock-out) - (when (y-or-n-p "Save changed buffer?") - (save-buffer)))) - -(defun org-clock-report () - "Create a table containing a report about clocked time. -If the buffer contains lines -#+BEGIN: clocktable :maxlevel 3 :emphasize nil - -#+END: clocktable -then the table will be inserted between these lines, replacing whatever -is was there before. If these lines are not in the buffer, the table -is inserted at point, surrounded by the special lines. -The BEGIN line can contain parameters. Allowed are: -:maxlevel The maximum level to be included in the table. Default is 3. -:emphasize t/nil, if levell 1 and level 2 should be bold/italic in the table." - (interactive) - (org-remove-clock-overlays) - (unless (org-find-dblock "clocktable") - (org-create-dblock (list :name "clocktable" - :maxlevel 2 :emphasize nil))) - (org-update-dblock)) - -(defun org-clock-update-time-maybe () - "If this is a CLOCK line, update it and return t. -Otherwise, return nil." - (interactive) - (save-excursion - (beginning-of-line 1) - (skip-chars-forward " \t") - (when (looking-at org-clock-string) - (let ((re (concat "[ \t]*" org-clock-string - " *[[<]\\([^]>]+\\)[]>]-+[[<]\\([^]>]+\\)[]>]" - "\\([ \t]*=>.*\\)?")) - ts te h m s) - (if (not (looking-at re)) - nil - (and (match-end 3) (delete-region (match-beginning 3) (match-end 3))) - (end-of-line 1) - (setq ts (match-string 1) - te (match-string 2)) - (setq s (- (time-to-seconds - (apply 'encode-time (org-parse-time-string te))) - (time-to-seconds - (apply 'encode-time (org-parse-time-string ts)))) - h (floor (/ s 3600)) - s (- s (* 3600 h)) - m (floor (/ s 60)) - s (- s (* 60 s))) - (insert " => " (format "%2d:%02d" h m)) - t))))) - -(defun org-clock-special-range (key &optional time as-strings) - "Return two times bordering a special time range. -Key is a symbol specifying the range and can be one of `today', `yesterday', -`thisweek', `lastweek', `thismonth', `lastmonth', `thisyear', `lastyear'. -A week starts Monday 0:00 and ends Sunday 24:00. -The range is determined relative to TIME. TIME defaults to the current time. -The return value is a cons cell with two internal times like the ones -returned by `current time' or `encode-time'. if AS-STRINGS is non-nil, -the returned times will be formatted strings." - (let* ((tm (decode-time (or time (current-time)))) - (s 0) (m (nth 1 tm)) (h (nth 2 tm)) - (d (nth 3 tm)) (month (nth 4 tm)) (y (nth 5 tm)) - (dow (nth 6 tm)) - s1 m1 h1 d1 month1 y1 diff ts te fm) - (cond - ((eq key 'today) - (setq h 0 m 0 h1 24 m1 0)) - ((eq key 'yesterday) - (setq d (1- d) h 0 m 0 h1 24 m1 0)) - ((eq key 'thisweek) - (setq diff (if (= dow 0) 6 (1- dow)) - m 0 h 0 d (- d diff) d1 (+ 7 d))) - ((eq key 'lastweek) - (setq diff (+ 7 (if (= dow 0) 6 (1- dow))) - m 0 h 0 d (- d diff) d1 (+ 7 d))) - ((eq key 'thismonth) - (setq d 1 h 0 m 0 d1 1 month1 (1+ month) h1 0 m1 0)) - ((eq key 'lastmonth) - (setq d 1 h 0 m 0 d1 1 month (1- month) month1 (1+ month) h1 0 m1 0)) - ((eq key 'thisyear) - (setq m 0 h 0 d 1 month 1 y1 (1+ y))) - ((eq key 'lastyear) - (setq m 0 h 0 d 1 month 1 y (1- y) y1 (1+ y))) - (t (error "No such time block %s" key))) - (setq ts (encode-time s m h d month y) - te (encode-time (or s1 s) (or m1 m) (or h1 h) - (or d1 d) (or month1 month) (or y1 y))) - (setq fm (cdr org-time-stamp-formats)) - (if as-strings - (cons (format-time-string fm ts) (format-time-string fm te)) - (cons ts te)))) - -(defun org-dblock-write:clocktable (params) - "Write the standard clocktable." - (let ((hlchars '((1 . "*") (2 . ?/))) - (emph nil) - (ins (make-marker)) - ipos time h m p level hlc hdl maxlevel - ts te cc block) - (setq maxlevel (or (plist-get params :maxlevel) 3) - emph (plist-get params :emphasize) - ts (plist-get params :tstart) - te (plist-get params :tend) - block (plist-get params :block)) - (when block - (setq cc (org-clock-special-range block nil t) - ts (car cc) te (cdr cc))) - (if ts (setq ts (time-to-seconds - (apply 'encode-time (org-parse-time-string ts))))) - (if te (setq te (time-to-seconds - (apply 'encode-time (org-parse-time-string te))))) - (move-marker ins (point)) - (setq ipos (point)) - ;; FIXME: does not yet use org-insert-time-stamp - (insert-before-markers "Clock summary at [" - (substring - (format-time-string (cdr org-time-stamp-formats)) - 1 -1) - "]." - (if block - (format " Considered range is /%s/." block) - "") - "\n\n|L|Headline|Time|\n") - (org-clock-sum ts te) - (setq h (/ org-clock-file-total-minutes 60) - m (- org-clock-file-total-minutes (* 60 h))) - (insert-before-markers "|-\n|0|" "*Total file time*| " - (format "*%d:%02d*" h m) - "|\n") - (goto-char (point-min)) - (while (setq p (next-single-property-change (point) :org-clock-minutes)) - (goto-char p) - (when (setq time (get-text-property p :org-clock-minutes)) - (save-excursion - (beginning-of-line 1) - (when (and (looking-at "\\(\\*+\\)[ \t]+\\(.*?\\)\\([ \t]+:[0-9a-zA-Z_@:]+:\\)?[ \t]*$") - (setq level (- (match-end 1) (match-beginning 1))) - (<= level maxlevel)) - (setq hlc (if emph (or (cdr (assoc level hlchars)) "") "") - hdl (match-string 2) - h (/ time 60) - m (- time (* 60 h))) - (goto-char ins) - (if (= level 1) (insert-before-markers "|-\n")) - (insert-before-markers - "| " (int-to-string level) "|" hlc hdl hlc " |" - (make-string (1- level) ?|) - hlc - (format "%d:%02d" h m) - hlc - " |\n"))))) - (goto-char ins) - (backward-delete-char 1) - (goto-char ipos) - (skip-chars-forward "^|") - (org-table-align))) - -(defun org-collect-clock-time-entries () - "Return an internal list with clocking information. -This list has one entry for each CLOCK interval. -FIXME: describe the elements." - (interactive) - (let ((re (concat "^[ \t]*" org-clock-string - " *\\[\\(.*?\\)\\]--\\[\\(.*?\\)\\]")) - rtn beg end next cont level title total closedp leafp - clockpos titlepos h m donep) - (save-excursion - (org-clock-sum) - (goto-char (point-min)) - (while (re-search-forward re nil t) - (setq clockpos (match-beginning 0) - beg (match-string 1) end (match-string 2) - cont (match-end 0)) - (setq beg (apply 'encode-time (org-parse-time-string beg)) - end (apply 'encode-time (org-parse-time-string end))) - (org-back-to-heading t) - (setq donep (org-entry-is-done-p)) - (setq titlepos (point) - total (or (get-text-property (1+ (point)) :org-clock-minutes) 0) - h (/ total 60) m (- total (* 60 h)) - total (cons h m)) - (looking-at "\\(\\*+\\) +\\(.*\\)") - (setq level (- (match-end 1) (match-beginning 1)) - title (org-match-string-no-properties 2)) - (save-excursion (outline-next-heading) (setq next (point))) - (setq closedp (re-search-forward org-closed-time-regexp next t)) - (goto-char next) - (setq leafp (and (looking-at "^\\*+ ") - (<= (- (match-end 0) (point)) level))) - (push (list beg end clockpos closedp donep - total title titlepos level leafp) - rtn) - (goto-char cont))) - (nreverse rtn))) - -;;;; Agenda, and Diary Integration - -;;; Define the mode - -(defvar org-agenda-mode-map (make-sparse-keymap) - "Keymap for `org-agenda-mode'.") - -(defvar org-agenda-menu) ; defined later in this file. -(defvar org-agenda-follow-mode nil) -(defvar org-agenda-show-log nil) -(defvar org-agenda-redo-command nil) -(defvar org-agenda-mode-hook nil) -(defvar org-agenda-type nil) -(defvar org-agenda-force-single-file nil) - -(defun org-agenda-mode () - "Mode for time-sorted view on action items in Org-mode files. - -The following commands are available: - -\\{org-agenda-mode-map}" - (interactive) - (kill-all-local-variables) - (setq org-agenda-undo-list nil - org-agenda-pending-undo-list nil) - (setq major-mode 'org-agenda-mode) - (setq mode-name "Org-Agenda") - (use-local-map org-agenda-mode-map) - (easy-menu-add org-agenda-menu) - (if org-startup-truncated (setq truncate-lines t)) - (org-add-hook 'post-command-hook 'org-agenda-post-command-hook nil 'local) - (org-add-hook 'pre-command-hook 'org-unhighlight nil 'local) - (unless org-agenda-keep-modes - (setq org-agenda-follow-mode org-agenda-start-with-follow-mode - org-agenda-show-log nil)) - (easy-menu-change - '("Agenda") "Agenda Files" - (append - (list - (vector - (if (get 'org-agenda-files 'org-restrict) - "Restricted to single file" - "Edit File List") - '(org-edit-agenda-file-list) - (not (get 'org-agenda-files 'org-restrict))) - "--") - (mapcar 'org-file-menu-entry (org-agenda-files)))) - (org-agenda-set-mode-name) - (apply - (if (fboundp 'run-mode-hooks) 'run-mode-hooks 'run-hooks) - (list 'org-agenda-mode-hook))) - -(substitute-key-definition 'undo 'org-agenda-undo - org-agenda-mode-map global-map) -(define-key org-agenda-mode-map "\C-i" 'org-agenda-goto) -(define-key org-agenda-mode-map [(tab)] 'org-agenda-goto) -(define-key org-agenda-mode-map "\C-m" 'org-agenda-switch-to) -(define-key org-agenda-mode-map "\C-k" 'org-agenda-kill) -(define-key org-agenda-mode-map "\C-c$" 'org-agenda-archive) -(define-key org-agenda-mode-map "$" 'org-agenda-archive) -(define-key org-agenda-mode-map "\C-c\C-o" 'org-agenda-open-link) -(define-key org-agenda-mode-map " " 'org-agenda-show) -(define-key org-agenda-mode-map "\C-c\C-t" 'org-agenda-todo) -(define-key org-agenda-mode-map "\C-c\C-xb" 'org-agenda-tree-to-indirect-buffer) -(define-key org-agenda-mode-map "b" 'org-agenda-tree-to-indirect-buffer) -(define-key org-agenda-mode-map "o" 'delete-other-windows) -(define-key org-agenda-mode-map "L" 'org-agenda-recenter) -(define-key org-agenda-mode-map "t" 'org-agenda-todo) -(define-key org-agenda-mode-map "a" 'org-agenda-toggle-archive-tag) -(define-key org-agenda-mode-map ":" 'org-agenda-set-tags) -(define-key org-agenda-mode-map "." 'org-agenda-goto-today) -(define-key org-agenda-mode-map "d" 'org-agenda-day-view) -(define-key org-agenda-mode-map "w" 'org-agenda-week-view) -(define-key org-agenda-mode-map (org-key 'S-right) 'org-agenda-date-later) -(define-key org-agenda-mode-map (org-key 'S-left) 'org-agenda-date-earlier) -(define-key org-agenda-mode-map [?\C-c ?\C-x (right)] 'org-agenda-date-later) -(define-key org-agenda-mode-map [?\C-c ?\C-x (left)] 'org-agenda-date-earlier) - -(define-key org-agenda-mode-map ">" 'org-agenda-date-prompt) -(define-key org-agenda-mode-map "\C-c\C-s" 'org-agenda-schedule) -(define-key org-agenda-mode-map "\C-c\C-d" 'org-agenda-deadline) -(let ((l '(1 2 3 4 5 6 7 8 9 0))) - (while l (define-key org-agenda-mode-map - (int-to-string (pop l)) 'digit-argument))) - -(define-key org-agenda-mode-map "f" 'org-agenda-follow-mode) -(define-key org-agenda-mode-map "l" 'org-agenda-log-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) -(define-key org-agenda-mode-map "s" 'org-save-all-org-buffers) -(define-key org-agenda-mode-map "P" 'org-agenda-show-priority) -(define-key org-agenda-mode-map "T" 'org-agenda-show-tags) -(define-key org-agenda-mode-map "n" 'next-line) -(define-key org-agenda-mode-map "p" 'previous-line) -(define-key org-agenda-mode-map "\C-n" 'org-agenda-next-date-line) -(define-key org-agenda-mode-map "\C-p" 'org-agenda-previous-date-line) -(define-key org-agenda-mode-map "," 'org-agenda-priority) -(define-key org-agenda-mode-map "\C-c," 'org-agenda-priority) -(define-key org-agenda-mode-map "i" 'org-agenda-diary-entry) -(define-key org-agenda-mode-map "c" 'org-agenda-goto-calendar) -(eval-after-load "calendar" - '(define-key calendar-mode-map org-calendar-to-agenda-key - 'org-calendar-goto-agenda)) -(define-key org-agenda-mode-map "C" 'org-agenda-convert-date) -(define-key org-agenda-mode-map "m" 'org-agenda-phases-of-moon) -(define-key org-agenda-mode-map "M" 'org-agenda-phases-of-moon) -(define-key org-agenda-mode-map "S" 'org-agenda-sunrise-sunset) -(define-key org-agenda-mode-map "h" 'org-agenda-holidays) -(define-key org-agenda-mode-map "H" 'org-agenda-holidays) -(define-key org-agenda-mode-map "+" 'org-agenda-priority-up) -(define-key org-agenda-mode-map "I" 'org-agenda-clock-in) -(define-key org-agenda-mode-map "O" 'org-agenda-clock-out) -(define-key org-agenda-mode-map "X" 'org-agenda-clock-cancel) -(define-key org-agenda-mode-map "-" 'org-agenda-priority-down) -(define-key org-agenda-mode-map (org-key 'S-up) 'org-agenda-priority-up) -(define-key org-agenda-mode-map (org-key 'S-down) 'org-agenda-priority-down) -(define-key org-agenda-mode-map [?\C-c ?\C-x (up)] 'org-agenda-priority-up) -(define-key org-agenda-mode-map [?\C-c ?\C-x (down)] 'org-agenda-priority-down) -(define-key org-agenda-mode-map [(right)] 'org-agenda-later) -(define-key org-agenda-mode-map [(left)] 'org-agenda-earlier) -(define-key org-agenda-mode-map "\C-c\C-x\C-c" 'org-export-icalendar-combine-agenda-files) -(defvar org-agenda-keymap (copy-keymap org-agenda-mode-map) - "Local keymap for agenda entries from Org-mode.") - -(define-key org-agenda-keymap - (if (featurep 'xemacs) [(button2)] [(mouse-2)]) 'org-agenda-goto-mouse) -(define-key org-agenda-keymap - (if (featurep 'xemacs) [(button3)] [(mouse-3)]) 'org-agenda-show-mouse) -(when org-agenda-mouse-1-follows-link - (define-key org-agenda-keymap [follow-link] 'mouse-face)) -(easy-menu-define org-agenda-menu org-agenda-mode-map "Agenda menu" - '("Agenda" - ("Agenda Files") - "--" - ["Show" org-agenda-show t] - ["Go To (other window)" org-agenda-goto t] - ["Go To (this window)" org-agenda-switch-to t] - ["Follow Mode" org-agenda-follow-mode - :style toggle :selected org-agenda-follow-mode :active t] - ["Tree to indirect frame" org-agenda-tree-to-indirect-buffer t] - "--" - ["Cycle TODO" org-agenda-todo t] - ["Archive subtree" org-agenda-archive t] - ["Delete subtree" org-agenda-kill 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)] - ["Previous Dates" org-agenda-earlier (org-agenda-check-type nil 'agenda)] - "--" - ("Tags" - ["Show all Tags" org-agenda-show-tags t] - ["Set Tags" org-agenda-set-tags t]) - ("Date/Schedule" - ["Schedule" org-agenda-schedule t] - ["Set Deadline" org-agenda-deadline t] - "--" - ["Change date +1 day" org-agenda-date-later (org-agenda-check-type nil 'agenda 'timeline)] - ["Change date -1 day" org-agenda-date-earlier (org-agenda-check-type nil 'agenda 'timeline)] - ["Change date to ..." org-agenda-date-prompt (org-agenda-check-type nil 'agenda 'timeline)]) - ("Priority" - ["Set Priority" org-agenda-priority t] - ["Increase Priority" org-agenda-priority-up t] - ["Decrease Priority" org-agenda-priority-down t] - ["Show Priority" org-agenda-show-priority t]) - ("Calendar/Diary" - ["New Diary Entry" org-agenda-diary-entry (org-agenda-check-type nil 'agenda 'timeline)] - ["Goto Calendar" org-agenda-goto-calendar (org-agenda-check-type nil 'agenda 'timeline)] - ["Phases of the Moon" org-agenda-phases-of-moon (org-agenda-check-type nil 'agenda 'timeline)] - ["Sunrise/Sunset" org-agenda-sunrise-sunset (org-agenda-check-type nil 'agenda 'timeline)] - ["Holidays" org-agenda-holidays (org-agenda-check-type nil 'agenda 'timeline)] - ["Convert" org-agenda-convert-date (org-agenda-check-type nil 'agenda 'timeline)] - "--" - ["Create iCalendar file" org-export-icalendar-combine-agenda-files t]) - "--" - ("View" - ["Day View" org-agenda-day-view :active (org-agenda-check-type nil 'agenda) - :style radio :selected (equal org-agenda-ndays 1)] - ["Week View" org-agenda-week-view :active (org-agenda-check-type nil 'agenda) - :style radio :selected (equal org-agenda-ndays 7)] - "--" - ["Show Logbook entries" org-agenda-log-mode - :style toggle :selected org-agenda-show-log :active (org-agenda-check-type nil 'agenda 'timeline)] - ["Include Diary" org-agenda-toggle-diary - :style toggle :selected org-agenda-include-diary :active (org-agenda-check-type nil 'agenda)] - ["Use Time Grid" org-agenda-toggle-time-grid - :style toggle :selected org-agenda-use-time-grid :active (org-agenda-check-type nil 'agenda)]) - ["Rebuild buffer" org-agenda-redo t] - ["Save all Org-mode Buffers" org-save-all-org-buffers t] - "--" - ["Undo Remote Editing" org-agenda-undo org-agenda-undo-list] - "--" - ["Quit" org-agenda-quit t] - ["Exit and Release Buffers" org-agenda-exit t] - )) - -;;; Agenda undo - -(defvar org-agenda-allow-remote-undo t - "Non-nil means, allow remote undo from the agenda buffer.") -(defvar org-agenda-undo-list nil - "List of undoable operations in the agenda since last refresh.") -(defvar org-agenda-undo-has-started-in nil - "Buffers that have already seen `undo-start' in the current undo sequence.") -(defvar org-agenda-pending-undo-list nil - "In a series of undo commands, this is the list of remaning undo items.") - -(defmacro org-with-remote-undo (_buffer &rest _body) - "Execute BODY while recording undo information in two buffers." - (declare (indent 1) (debug t)) - `(let ((_cline (org-current-line)) - (_cmd this-command) - (_buf1 (current-buffer)) - (_buf2 ,_buffer) - (_undo1 buffer-undo-list) - (_undo2 (with-current-buffer ,_buffer buffer-undo-list)) - _c1 _c2) - ,@_body - (when org-agenda-allow-remote-undo - (setq _c1 (org-verify-change-for-undo - _undo1 (with-current-buffer _buf1 buffer-undo-list)) - _c2 (org-verify-change-for-undo - _undo2 (with-current-buffer _buf2 buffer-undo-list))) - (when (or _c1 _c2) - ;; make sure there are undo boundaries - (and _c1 (with-current-buffer _buf1 (undo-boundary))) - (and _c2 (with-current-buffer _buf2 (undo-boundary))) - ;; remember which buffer to undo - (push (list _cmd _cline _buf1 _c1 _buf2 _c2) - org-agenda-undo-list))))) - -(defun org-agenda-undo () - "Undo a remote editing step in the agenda. -This undoes changes both in the agenda buffer and in the remote buffer -that have been changed along." - (interactive) - (or org-agenda-allow-remote-undo - (error "Check the variable `org-agenda-allow-remote-undo' to activate remote undo.")) - (if (not (eq this-command last-command)) - (setq org-agenda-undo-has-started-in nil - org-agenda-pending-undo-list org-agenda-undo-list)) - (if (not org-agenda-pending-undo-list) - (error "No further undo information")) - (let* ((entry (pop org-agenda-pending-undo-list)) - buf line cmd rembuf) - (setq cmd (pop entry) line (pop entry)) - (setq rembuf (nth 2 entry)) - (org-with-remote-undo rembuf - (while (bufferp (setq buf (pop entry))) - (if (pop entry) - (with-current-buffer buf - (let ((last-undo-buffer buf) - buffer-read-only) - (unless (memq buf org-agenda-undo-has-started-in) - (push buf org-agenda-undo-has-started-in) - (make-local-variable 'pending-undo-list) - (undo-start)) - (while (and pending-undo-list - (listp pending-undo-list) - (not (car pending-undo-list))) - (pop pending-undo-list)) - (undo-more 1)))))) - (goto-line line) - (message "`%s' undone (buffer %s)" cmd (buffer-name rembuf)))) - -(defun org-verify-change-for-undo (l1 l2) - "Verify that a real change occurred between the undo lists L1 and L2." - (while (and l1 (listp l1) (null (car l1))) (pop l1)) - (while (and l2 (listp l2) (null (car l2))) (pop l2)) - (not (eq l1 l2))) - -;;; Agenda dispatch - -(defvar org-agenda-restrict nil) -(defvar org-agenda-restrict-begin (make-marker)) -(defvar org-agenda-restrict-end (make-marker)) -(defvar org-agenda-last-dispatch-buffer nil) - -;;;###autoload -(defun org-agenda (arg) - "Dispatch agenda commands to collect entries to the agenda buffer. -Prompts for a character to select a command. Any prefix arg will be passed -on to the selected command. The default selections are: -g -a Call `org-agenda-list' to display the agenda for current day or week. -t Call `org-todo-list' to display the global todo list. -T Call `org-todo-list' to display the global todo list, select only - entries with a specific TODO keyword (the user gets a prompt). -m Call `org-tags-view' to display headlines with tags matching - a condition (the user is prompted for the condition). -M Like `m', but select only TODO entries, no ordinary headlines. -l Create a timeeline for the current buffer. - -More commands can be added by configuring the variable -`org-agenda-custom-commands'. In particular, specific tags and TODO keyword -searches can be pre-defined in this way. - -If the current buffer is in Org-mode and visiting a file, you can also -first press `1' to indicate that the agenda should be temporarily (until the -next use of \\[org-agenda]) restricted to the current file." - (interactive "P") - (catch 'exit - (let* ((buf (current-buffer)) - (bfn (buffer-file-name (buffer-base-buffer))) - (restrict-ok (and bfn (org-mode-p))) - (custom org-agenda-custom-commands) - c entry key type match lprops header) - ;; Turn off restriction - (put 'org-agenda-files 'org-restrict nil) - (setq org-agenda-restrict nil) - (move-marker org-agenda-restrict-begin nil) - (move-marker org-agenda-restrict-end nil) - ;; Remember where this call originated - (setq org-agenda-last-dispatch-buffer (current-buffer)) - (save-window-excursion - (delete-other-windows) - (switch-to-buffer-other-window " *Agenda Commands*") - (erase-buffer) - (insert (eval-when-compile - (let ((header -"Press key for an agenda command: --------------------------------- C Configure custom agenda commands -a Agenda for current week or day -t List of all TODO entries T Entries with special TODO kwd -m Match a TAGS query M Like m, but only TODO entries -L Timeline for current buffer # List stuck projects (!=configure) -") - (start 0)) - (while (string-match "\\(^\\| \\|(\\)\\(\\S-\\)\\( \\|=\\)" header start) - (setq start (match-end 0)) - (add-text-properties (match-beginning 2) (match-end 2) - '(face bold) header)) - header))) - (while (setq entry (pop custom)) - (setq key (car entry) type (nth 1 entry) match (nth 2 entry)) - (insert (format "\n%-4s%-14s: %s" - (org-add-props (copy-sequence key) - '(face bold)) - (cond - ((stringp type) type) - ((eq type 'tags) "Tags query") - ((eq type 'todo) "TODO keyword") - ((eq type 'tags-tree) "Tags tree") - ((eq type 'todo-tree) "TODO kwd tree") - ((eq type 'occur-tree) "Occur tree") - ((functionp type) (symbol-name type)) - (t "???")) - (if (stringp match) - (org-add-props match nil 'face 'org-warning) - (format "set of %d commands" (+ -2 (length entry))))))) - (if restrict-ok - (insert "\n" - (org-add-props "1 Restrict call to current buffer 0 Restrict call to region or subtree" nil 'face 'org-table))) - - (goto-char (point-min)) - (if (fboundp 'fit-window-to-buffer) (fit-window-to-buffer)) - (message "Press key for agenda command%s" - (if restrict-ok ", or [1] or [0] to restrict" "")) - (setq c (read-char-exclusive)) - (message "") - (when (memq c '(?L ?1 ?0)) - (if restrict-ok - (put 'org-agenda-files 'org-restrict (list bfn)) - (error "Cannot restrict agenda to current buffer")) - (with-current-buffer " *Agenda Commands*" - (goto-char (point-max)) - (delete-region (point-at-bol) (point)) - (goto-char (point-min))) - (when (eq c ?0) - (setq org-agenda-restrict t) - (with-current-buffer buf - (if (org-region-active-p) - (progn - (move-marker org-agenda-restrict-begin (region-beginning)) - (move-marker org-agenda-restrict-end (region-end))) - (save-excursion - (org-back-to-heading t) - (move-marker org-agenda-restrict-begin (point)) - (move-marker org-agenda-restrict-end - (progn (org-end-of-subtree t))))))) - (unless (eq c ?L) - (message "Press key for agenda command%s" - (if restrict-ok " (restricted to current file)" "")) - (setq c (read-char-exclusive))) - (message ""))) - (require 'calendar) ; FIXME: can we avoid this for some commands? - ;; For example the todo list should not need it (but does...) - (cond - ((setq entry (assoc (char-to-string c) org-agenda-custom-commands)) - (if (symbolp (nth 1 entry)) - (progn - (setq type (nth 1 entry) match (nth 2 entry) lprops (nth 3 entry) - lprops (nth 3 entry)) - (cond - ((eq type 'tags) - (org-let lprops '(org-tags-view current-prefix-arg match))) - ((eq type 'tags-todo) - (org-let lprops '(org-tags-view '(4) match))) - ((eq type 'todo) - (org-let lprops '(org-todo-list match))) - ((eq type 'tags-tree) - (org-check-for-org-mode) - (org-let lprops '(org-tags-sparse-tree current-prefix-arg match))) - ((eq type 'todo-tree) - (org-check-for-org-mode) - (org-let lprops - '(org-occur (concat "^" outline-regexp "[ \t]*" - (regexp-quote match) "\\>")))) - ((eq type 'occur-tree) - (org-check-for-org-mode) - (org-let lprops '(org-occur match))) - ((fboundp type) - (org-let lprops '(funcall type match))) - (t (error "Invalid custom agenda command type %s" type)))) - (org-run-agenda-series (cddr entry)))) - ((equal c ?C) (customize-variable 'org-agenda-custom-commands)) - ((equal c ?a) (call-interactively 'org-agenda-list)) - ((equal c ?t) (call-interactively 'org-todo-list)) - ((equal c ?T) (org-call-with-arg 'org-todo-list (or arg '(4)))) - ((equal c ?m) (call-interactively 'org-tags-view)) - ((equal c ?M) (org-call-with-arg 'org-tags-view (or arg '(4)))) - ((equal c ?L) - (unless restrict-ok - (error "This is not an Org-mode file")) - (org-call-with-arg 'org-timeline arg)) - ((equal c ?#) (call-interactively 'org-agenda-list-stuck-projects)) - ((equal c ?!) (customize-variable 'org-stuck-projects)) - (t (error "Invalid key")))))) - -;; FIXME: what is the meaning of WINDOW????? -(defun org-run-agenda-series (series &optional window) - (org-prepare-agenda) - (let* ((org-agenda-multi t) - (redo (list 'org-run-agenda-series (list 'quote series))) - (org-select-agenda-window t) - (cmds (car series)) - (gprops (nth 1 series)) - match ;; The byte compiler incorrectly complains about this. Keep it! - cmd type lprops) - (while (setq cmd (pop cmds)) - (setq type (car cmd) match (nth 1 cmd) lprops (nth 2 cmd)) - (cond - ((eq type 'agenda) - (call-interactively 'org-agenda-list)) - ((eq type 'alltodo) - (call-interactively 'org-todo-list)) - ((eq type 'tags) - (org-let2 gprops lprops - '(org-tags-view current-prefix-arg match))) - ((eq type 'tags-todo) - (org-let2 gprops lprops - '(org-tags-view '(4) match))) - ((eq type 'todo) - (org-let2 gprops lprops - '(org-todo-list match))) - ((fboundp type) - (org-let2 gprops lprops - '(funcall type match))) - (t (error "Invalid type in command series")))) - (widen) - (setq org-agenda-redo-command redo) - (goto-char (point-min))) - (org-finalize-agenda)) - -;;;###autoload -(defmacro org-batch-agenda (cmd-key &rest parameters) - "Run an agenda command in batch mode, send result to STDOUT. -CMD-KEY is a string that is also a key in `org-agenda-custom-commands'. -Paramters are alternating variable names and values that will be bound -before running the agenda command." - (let (pars) - (while parameters - (push (list (pop parameters) (if parameters (pop parameters))) pars)) - (flet ((read-char-exclusive () (string-to-char cmd-key))) - (eval (list 'let (nreverse pars) '(org-agenda nil)))) - (set-buffer "*Org Agenda*") - (princ (buffer-string)))) - -(defmacro org-no-read-only (&rest body) - "Inhibit read-only for BODY." - `(let ((inhibit-read-only t)) ,@body)) - -(defun org-check-for-org-mode () - "Make sure current buffer is in org-mode. Error if not." - (or (org-mode-p) - (error "Cannot execute org-mode agenda command on buffer in %s." - major-mode))) - -(defun org-fit-agenda-window () - "Fit the window to the buffer size." - (and org-fit-agenda-window - (memq org-agenda-window-setup '(reorganize-frame)) - (fboundp 'fit-window-to-buffer) - (fit-window-to-buffer nil (/ (* (frame-height) 3) 4) - (/ (frame-height) 2)))) - -(defun org-agenda-files (&optional unrestricted) - "Get the list of agenda files. -Optional UNRESTRICTED means return the full list even if a restriction -is currently in place." - (cond - ((and (not unrestricted) (get 'org-agenda-files 'org-restrict))) - ((stringp org-agenda-files) (org-read-agenda-file-list)) - ((listp org-agenda-files) org-agenda-files) - (t (error "Invalid value of `org-agenda-files'")))) - -(defvar org-window-configuration) - -(defun org-edit-agenda-file-list () - "Edit the list of agenda files. -Depending on setup, this either uses customize to edit the variable -`org-agenda-files', or it visits the file that is holding the list. In the -latter case, the buffer is set up in a way that saving it automatically kills -the buffer and restores the previous window configuration." - (interactive) - (if (stringp org-agenda-files) - (let ((cw (current-window-configuration))) - (find-file org-agenda-files) - (org-set-local 'org-window-configuration cw) - (org-add-hook 'after-save-hook - (lambda () - (set-window-configuration - (prog1 org-window-configuration - (kill-buffer (current-buffer)))) - (org-install-agenda-files-menu) - (message "New agenda file list installed")) - nil 'local) - (message (substitute-command-keys - "Edit list and finish with \\[save-buffer]"))) - (customize-variable 'org-agenda-files))) - -(defun org-store-new-agenda-file-list (list) - "Set new value for the agenda file list and save it correcly." - (if (stringp org-agenda-files) - (let ((f org-agenda-files) b) - (while (setq b (find-buffer-visiting f)) (kill-buffer b)) - (with-temp-file f - (insert (mapconcat 'identity list "\n") "\n"))) - (let ((org-mode-hook nil) (default-major-mode 'fundamental-mode)) - (setq org-agenda-files list) - (customize-save-variable 'org-agenda-files org-agenda-files)))) - -(defun org-read-agenda-file-list () - "Read the list of agenda files from a file." - (when (stringp org-agenda-files) - (with-temp-buffer - (insert-file-contents org-agenda-files) - (org-split-string (buffer-string) "[ \t\r\n]*?[\r\n][ \t\r\n]*")))) - -(defvar org-agenda-markers nil - "List of all currently active markers created by `org-agenda'.") -(defvar org-agenda-last-marker-time (time-to-seconds (current-time)) - "Creation time of the last agenda marker.") - -(defun org-agenda-new-marker (&optional pos) - "Return a new agenda marker. -Org-mode keeps a list of these markers and resets them when they are -no longer in use." - (let ((m (copy-marker (or pos (point))))) - (setq org-agenda-last-marker-time (time-to-seconds (current-time))) - (push m org-agenda-markers) - m)) - -(defun org-agenda-maybe-reset-markers (&optional force) - "Reset markers created by `org-agenda'. But only if they are old enough." - (if (or (and force (not org-agenda-multi)) - (> (- (time-to-seconds (current-time)) - org-agenda-last-marker-time) - 5)) - (while org-agenda-markers - (move-marker (pop org-agenda-markers) nil)))) - -(defvar org-agenda-new-buffers nil - "Buffers created to visit agenda files.") - -(defun org-get-agenda-file-buffer (file) - "Get a buffer visiting FILE. If the buffer needs to be created, add -it to the list of buffers which might be released later." - (let ((buf (find-buffer-visiting file))) - (if buf - buf ; just return it - ;; Make a new buffer and remember it - (setq buf (find-file-noselect file)) - (if buf (push buf org-agenda-new-buffers)) - buf))) - -(defun org-release-buffers (blist) - "Release all buffers in list, asking the user for confirmation when needed. -When a buffer is unmodified, it is just killed. When modified, it is saved -\(if the user agrees) and then killed." - (let (buf file) - (while (setq buf (pop blist)) - (setq file (buffer-file-name buf)) - (when (and (buffer-modified-p buf) - file - (y-or-n-p (format "Save file %s? " file))) - (with-current-buffer buf (save-buffer))) - (kill-buffer buf)))) - -(defun org-timeline (&optional include-all) - "Show a time-sorted view of the entries in the current org file. -Only entries with a time stamp of today or later will be listed. With -\\[universal-argument] prefix, all unfinished TODO items will also be shown, -under the current date. -If the buffer contains an active region, only check the region for -dates." - (interactive "P") - (require 'calendar) - (org-compile-prefix-format 'timeline) - (org-set-sorting-strategy 'timeline) - (let* ((dopast t) - (dotodo include-all) - (doclosed org-agenda-show-log) - (entry buffer-file-name) - (date (calendar-current-date)) - (win (selected-window)) - (pos1 (point)) - (beg (if (org-region-active-p) (region-beginning) (point-min))) - (end (if (org-region-active-p) (region-end) (point-max))) - (day-numbers (org-get-all-dates beg end 'no-ranges - t doclosed ; always include today - org-timeline-show-empty-dates)) - (today (time-to-days (current-time))) - (past t) - args - s e rtn d emptyp) - (setq org-agenda-redo-command - (list 'progn - (list 'switch-to-buffer-other-window (current-buffer)) - (list 'org-timeline (list 'quote include-all)))) - (if (not dopast) - ;; Remove past dates from the list of dates. - (setq day-numbers (delq nil (mapcar (lambda(x) - (if (>= x today) x nil)) - day-numbers)))) - (org-prepare-agenda) - (if doclosed (push :closed args)) - (push :timestamp args) - (if dotodo (push :todo args)) - (while (setq d (pop day-numbers)) - (if (and (listp d) (eq (car d) :omitted)) - (progn - (setq s (point)) - (insert (format "\n[... %d empty days omitted]\n\n" (cdr d))) - (put-text-property s (1- (point)) 'face 'org-level-3)) - (if (listp d) (setq d (car d) emptyp t) (setq emptyp nil)) - (if (and (>= d today) - dopast - past) - (progn - (setq past nil) - (insert (make-string 79 ?-) "\n"))) - (setq date (calendar-gregorian-from-absolute d)) - (setq s (point)) - (setq rtn (and (not emptyp) - (apply 'org-agenda-get-day-entries - entry date args))) - (if (or rtn (equal d today) org-timeline-show-empty-dates) - (progn - (insert (calendar-day-name date) " " - (number-to-string (extract-calendar-day date)) " " - (calendar-month-name (extract-calendar-month date)) " " - (number-to-string (extract-calendar-year date)) "\n") - (put-text-property s (1- (point)) 'face - 'org-level-3) - (if (equal d today) - (put-text-property s (1- (point)) 'org-today t)) - (and rtn (insert (org-finalize-agenda-entries rtn) "\n")) - (put-text-property s (1- (point)) 'day d))))) - (goto-char (point-min)) - (goto-char (or (text-property-any (point-min) (point-max) 'org-today t) - (point-min))) - (add-text-properties (point-min) (point-max) '(org-agenda-type timeline)) - (org-finalize-agenda) - (setq buffer-read-only t) - (when (not org-select-agenda-window) - (select-window win) - (goto-char pos1)))) - -(defvar org-agenda-overriding-arguments nil) ; dynamically scoped parameter -(defvar org-agenda-last-arguments nil - "The arguments of the previous call to org-agenda") - -;;;###autoload -(defun org-agenda-list (&optional include-all start-day ndays) - "Produce a weekly view from all files in variable `org-agenda-files'. -The view will be for the current week, but from the overview buffer you -will be able to go to other weeks. -With one \\[universal-argument] prefix argument INCLUDE-ALL, all unfinished TODO items will -also be shown, under the current date. -With two \\[universal-argument] prefix argument INCLUDE-ALL, all TODO entries marked DONE -on the days are also shown. See the variable `org-log-done' for how -to turn on logging. -START-DAY defaults to TODAY, or to the most recent match for the weekday -given in `org-agenda-start-on-weekday'. -NDAYS defaults to `org-agenda-ndays'." - (interactive "P") - (if org-agenda-overriding-arguments - (setq include-all (car org-agenda-overriding-arguments) - start-day (nth 1 org-agenda-overriding-arguments) - ndays (nth 2 org-agenda-overriding-arguments))) - (setq org-agenda-last-arguments (list include-all start-day ndays)) - (org-compile-prefix-format 'agenda) - (org-set-sorting-strategy 'agenda) - (require 'calendar) - (let* ((org-agenda-start-on-weekday - (if (or (equal ndays 1) - (and (null ndays) (equal 1 org-agenda-ndays))) - nil org-agenda-start-on-weekday)) - (thefiles (org-agenda-files)) - (files thefiles) - (win (selected-window)) - (today (time-to-days (current-time))) - (sd (or start-day today)) - (start (if (or (null org-agenda-start-on-weekday) - (< org-agenda-ndays 7)) - sd - (let* ((nt (calendar-day-of-week - (calendar-gregorian-from-absolute sd))) - (n1 org-agenda-start-on-weekday) - (d (- nt n1))) - (- sd (+ (if (< d 0) 7 0) d))))) - (day-numbers (list start)) - (inhibit-redisplay t) - s e rtn rtnall file date d start-pos end-pos todayp nd) - (setq org-agenda-redo-command - (list 'org-agenda-list (list 'quote include-all) start-day ndays)) - ;; Make the list of days - (setq ndays (or ndays org-agenda-ndays) - nd ndays) - (while (> ndays 1) - (push (1+ (car day-numbers)) day-numbers) - (setq ndays (1- ndays))) - (setq day-numbers (nreverse day-numbers)) - (org-prepare-agenda) - (org-set-local 'starting-day (car day-numbers)) - (org-set-local 'include-all-loc include-all) - (when (and (or include-all org-agenda-include-all-todo) - (member today day-numbers)) - (setq files thefiles - rtnall nil) - (while (setq file (pop files)) - (catch 'nextfile - (org-check-agenda-file file) - (setq date (calendar-gregorian-from-absolute today) - rtn (org-agenda-get-day-entries - file date :todo)) - (setq rtnall (append rtnall rtn)))) - (when rtnall - (insert "ALL CURRENTLY OPEN TODO ITEMS:\n") - (add-text-properties (point-min) (1- (point)) - (list 'face 'org-level-3)) - (insert (org-finalize-agenda-entries rtnall) "\n"))) - (setq s (point)) - (insert (if (= nd 7) "Week-" "Day-") "agenda:\n") - (add-text-properties s (1- (point)) (list 'face 'org-level-3)) - (while (setq d (pop day-numbers)) - (setq date (calendar-gregorian-from-absolute d) - s (point)) - (if (or (setq todayp (= d today)) - (and (not start-pos) (= d sd))) - (setq start-pos (point)) - (if (and start-pos (not end-pos)) - (setq end-pos (point)))) - (setq files thefiles - rtnall nil) - (while (setq file (pop files)) - (catch 'nextfile - (org-check-agenda-file file) - (if org-agenda-show-log - (setq rtn (org-agenda-get-day-entries - file date - :deadline :scheduled :timestamp :closed)) - (setq rtn (org-agenda-get-day-entries - file date - :deadline :scheduled :timestamp))) - (setq rtnall (append rtnall rtn)))) - (if org-agenda-include-diary - (progn - (require 'diary-lib) - (setq rtn (org-get-entries-from-diary date)) - (setq rtnall (append rtnall rtn)))) - (if (or rtnall org-agenda-show-all-dates) - (progn - (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-level-3) - (if todayp (put-text-property s (1- (point)) 'org-today t)) - - (if rtnall (insert - (org-finalize-agenda-entries - (org-agenda-add-time-grid-maybe - rtnall nd todayp)) - "\n")) - (put-text-property s (1- (point)) 'day d)))) - (goto-char (point-min)) - (org-fit-agenda-window) - (unless (and (pos-visible-in-window-p (point-min)) - (pos-visible-in-window-p (point-max))) - (goto-char (1- (point-max))) - (recenter -1) - (if (not (pos-visible-in-window-p (or start-pos 1))) - (progn - (goto-char (or start-pos 1)) - (recenter 1)))) - (goto-char (or start-pos 1)) - (add-text-properties (point-min) (point-max) '(org-agenda-type agenda)) - (org-finalize-agenda) - (setq buffer-read-only t) - (if (not org-select-agenda-window) (select-window win)) - (message ""))) - -(defvar org-select-this-todo-keyword nil) - -;;;###autoload -(defun org-todo-list (arg) - "Show all TODO entries from all agenda file in a single list. -The prefix arg can be used to select a specific TODO keyword and limit -the list to these. When using \\[universal-argument], you will be prompted -for a keyword. A numeric prefix directly selects the Nth keyword in -`org-todo-keywords'." - (interactive "P") - (require 'calendar) - (org-compile-prefix-format 'todo) - (org-set-sorting-strategy 'todo) - (let* ((today (time-to-days (current-time))) - (date (calendar-gregorian-from-absolute today)) - (win (selected-window)) - (kwds org-todo-keywords) - (completion-ignore-case t) - (org-select-this-todo-keyword - (if (stringp arg) arg - (and arg (integerp arg) (> arg 0) - (nth (1- arg) org-todo-keywords)))) - rtn rtnall files file pos) - (when (equal arg '(4)) - (setq org-select-this-todo-keyword - (completing-read "Keyword: " (mapcar 'list org-todo-keywords) - nil t))) - (and (equal 0 arg) (setq org-select-this-todo-keyword nil)) - (org-prepare-agenda) - (org-set-local 'last-arg arg) - (org-set-local 'org-todo-keywords kwds) - (setq org-agenda-redo-command - '(org-todo-list (or current-prefix-arg last-arg))) - (setq files (org-agenda-files) - rtnall nil) - (while (setq file (pop files)) - (catch 'nextfile - (org-check-agenda-file file) - (setq rtn (org-agenda-get-day-entries file date :todo)) - (setq rtnall (append rtnall rtn)))) - (if org-agenda-overriding-header - (insert (org-add-props (copy-sequence org-agenda-overriding-header) - nil 'face 'org-level-3) "\n") - (insert "Global list of TODO items of type: ") - (add-text-properties (point-min) (1- (point)) - (list 'face 'org-level-3)) - (setq pos (point)) - (insert (or org-select-this-todo-keyword "ALL") "\n") - (add-text-properties pos (1- (point)) (list 'face 'org-warning)) - (setq pos (point)) - (unless org-agenda-multi - (insert - "Available with `N r': (0)ALL " - (let ((n 0)) - (mapconcat (lambda (x) - (format "(%d)%s" (setq n (1+ n)) x)) - org-todo-keywords " ")) - "\n")) - (add-text-properties pos (1- (point)) (list 'face 'org-level-3))) - (when rtnall - (insert (org-finalize-agenda-entries rtnall) "\n")) - (goto-char (point-min)) - (org-fit-agenda-window) - (add-text-properties (point-min) (point-max) '(org-agenda-type todo)) - (org-finalize-agenda) - (setq buffer-read-only t) - (if (not org-select-agenda-window) (select-window win)))) - -;;; Finding stuck projects -(defvar org-agenda-skip-regexp nil - "Regular expression used in skipping subtrees for the agenda. -This is basically a temporary global variable that can be set and then -used by user-defined selections using `org-agenda-skip-function'.") - -(defvar org-agenda-overriding-header nil - "When this is set during todo and tags searches, will replace header.") - -(defun org-agenda-skip-subtree-when-regexp-matches () - "Checks if the current subtree contains match for `org-agenda-skip-regexp'. -If yes, it returns the end position of this tree, causing agenda commands -to skip this subtree. This is a function that can be put into -`org-agenda-skip-function' for the duration of a command." - (save-match-data - (let ((end (save-excursion (org-end-of-subtree t))) - skip) - (save-excursion - (setq skip (re-search-forward org-agenda-skip-regexp end t))) - (and skip end)))) - -(defun org-agenda-list-stuck-projects (match) - "Create agenda view for projects that are stuck. -Stuck projects are project that have no next actions. For the definitions -of what a project is and how to check if it stuck, customize the variable -`org-stuck-projects'. -MATCH is being ignored." - (interactive) - (let* ((org-agenda-skip-function 'org-agenda-skip-subtree-when-regexp-matches) - (org-agenda-overriding-header "List of stuck projects: ") - (matcher (nth 0 org-stuck-projects)) - (todo (nth 1 org-stuck-projects)) - (tags (nth 2 org-stuck-projects)) - (todo-re (concat "^\\*+[ \t]+\\(" - (mapconcat 'identity todo "\\|") - "\\)\\>")) - (tags-re (concat "^\\*+.*:\\(" - (mapconcat 'identity tags "\\|") - "\\):[a-zA-Z0-9_@:]*[ \t]*$"))) - - (setq org-agenda-skip-regexp - (cond - ((and todo tags) - (concat todo-re "\\|" tags-re)) - (todo todo-re) - (tags tags-re) - (t (error "No information how to identify unstuck projects")))) - (org-tags-view nil matcher))) - -(defun org-check-agenda-file (file) - "Make sure FILE exists. If not, ask user what to do." - (when (not (file-exists-p file)) - (message "non-existent file %s. [R]emove from list or [A]bort?" - (abbreviate-file-name file)) - (let ((r (downcase (read-char-exclusive)))) - (cond - ((equal r ?r) - (org-remove-file file) - (throw 'nextfile t)) - (t (error "Abort")))))) - -(defun org-agenda-check-type (error &rest types) - "Check if agenda buffer is of allowed type. -If ERROR is non-nil, throw an error, otherwise just return nil." - (if (memq org-agenda-type types) - t - (if error - (error "Not allowed in %s-type agenda buffers" org-agenda-type) - nil))) - -(defun org-agenda-quit () - "Exit agenda by removing the window or the buffer." - (interactive) - (let ((buf (current-buffer))) - (if (not (one-window-p)) (delete-window)) - (kill-buffer buf) - (org-agenda-maybe-reset-markers 'force)) - ;; Maybe restore the pre-agenda window configuration. - (and org-agenda-restore-windows-after-quit - (not (eq org-agenda-window-setup 'other-frame)) - org-pre-agenda-window-conf - (set-window-configuration org-pre-agenda-window-conf))) - -(defun org-agenda-exit () - "Exit agenda by removing the window or the buffer. -Also kill all Org-mode buffers which have been loaded by `org-agenda'. -Org-mode buffers visited directly by the user will not be touched." - (interactive) - (org-release-buffers org-agenda-new-buffers) - (setq org-agenda-new-buffers nil) - (org-agenda-quit)) - -(defun org-save-all-org-buffers () - "Save all Org-mode buffers without user confirmation." - (interactive) - (message "Saving all Org-mode buffers...") - (save-some-buffers t 'org-mode-p) - (message "Saving all Org-mode buffers... done")) - -(defun org-agenda-redo () - "Rebuild Agenda. -When this is the global TODO list, a prefix argument will be interpreted." - (interactive) - (let* ((org-agenda-keep-modes t) - (line (org-current-line)) - (window-line (- line (org-current-line (window-start))))) - (message "Rebuilding agenda buffer...") - (eval org-agenda-redo-command) - (setq org-agenda-undo-list nil - org-agenda-pending-undo-list nil) - (message "Rebuilding agenda buffer...done") - (goto-line line) - (recenter window-line))) - -(defun org-agenda-goto-today () - "Go to today." - (interactive) - (org-agenda-check-type t 'timeline 'agenda) - (let ((tdpos (text-property-any (point-min) (point-max) 'org-today t))) - (cond - (tdpos (goto-char tdpos)) - ((eq org-agenda-type 'agenda) - (let ((org-agenda-overriding-arguments org-agenda-last-arguments)) - (setf (nth 1 org-agenda-overriding-arguments) nil) - (org-agenda-redo) - (org-agenda-find-today-or-agenda))) - (t (error "Cannot find today"))))) - -(defun org-agenda-find-today-or-agenda () - (goto-char - (or (text-property-any (point-min) (point-max) 'org-today t) - (text-property-any (point-min) (point-max) 'org-agenda-type 'agenda) - (point-min)))) - -(defun org-agenda-later (arg) - "Go forward in time by `org-agenda-ndays' days. -With prefix ARG, go forward that many times `org-agenda-ndays'." - (interactive "p") - (org-agenda-check-type t 'agenda) - (let ((org-agenda-overriding-arguments - (list (car org-agenda-last-arguments) - (+ starting-day (* arg org-agenda-ndays)) - nil t))) - (org-agenda-redo) - (org-agenda-find-today-or-agenda))) - -(defun org-agenda-earlier (arg) - "Go back in time by `org-agenda-ndays' days. -With prefix ARG, go back that many times `org-agenda-ndays'." - (interactive "p") - (org-agenda-check-type t 'agenda) - (let ((org-agenda-overriding-arguments - (list (car org-agenda-last-arguments) - (- starting-day (* arg org-agenda-ndays)) - nil t))) - (org-agenda-redo) - (org-agenda-find-today-or-agenda))) - -(defun org-agenda-week-view () - "Switch to weekly view for agenda." - (interactive) - (org-agenda-check-type t 'agenda) - (if (= org-agenda-ndays 7) - (error "This is already the week view")) - (setq org-agenda-ndays 7) - (let ((org-agenda-overriding-arguments - (list (car org-agenda-last-arguments) - (or (get-text-property (point) 'day) - starting-day) - nil t))) - (org-agenda-redo) - (org-agenda-find-today-or-agenda)) - (org-agenda-set-mode-name) - (message "Switched to week view")) - -(defun org-agenda-day-view () - "Switch to daily view for agenda." - (interactive) - (org-agenda-check-type t 'agenda) - (if (= org-agenda-ndays 1) - (error "This is already the day view")) - (setq org-agenda-ndays 1) - (let ((org-agenda-overriding-arguments - (list (car org-agenda-last-arguments) - (or (get-text-property (point) 'day) - starting-day) - nil t))) - (org-agenda-redo) - (org-agenda-find-today-or-agenda)) - (org-agenda-set-mode-name) - (message "Switched to day view")) - -(defun org-agenda-next-date-line (&optional arg) - "Jump to the next line indicating a date in agenda buffer." - (interactive "p") - (org-agenda-check-type t 'agenda 'timeline) - (beginning-of-line 1) - (if (looking-at "^\\S-") (forward-char 1)) - (if (not (re-search-forward "^\\S-" nil t arg)) - (progn - (backward-char 1) - (error "No next date after this line in this buffer"))) - (goto-char (match-beginning 0))) - -(defun org-agenda-previous-date-line (&optional arg) - "Jump to the previous line indicating a date in agenda buffer." - (interactive "p") - (org-agenda-check-type t 'agenda 'timeline) - (beginning-of-line 1) - (if (not (re-search-backward "^\\S-" nil t arg)) - (error "No previous date before this line in this buffer"))) - -;; Initialize the highlight -(defvar org-hl (org-make-overlay 1 1)) -(org-overlay-put org-hl 'face 'highlight) - -(defun org-highlight (begin end &optional buffer) - "Highlight a region with overlay." - (funcall (if (featurep 'xemacs) 'set-extent-endpoints 'move-overlay) - org-hl begin end (or buffer (current-buffer)))) - -(defun org-unhighlight () - "Detach overlay INDEX." - (funcall (if (featurep 'xemacs) 'detach-extent 'delete-overlay) org-hl)) - - -(defun org-agenda-follow-mode () - "Toggle follow mode in an agenda buffer." - (interactive) - (setq org-agenda-follow-mode (not org-agenda-follow-mode)) - (org-agenda-set-mode-name) - (message "Follow mode is %s" - (if org-agenda-follow-mode "on" "off"))) - -(defun org-agenda-log-mode () - "Toggle log mode in an agenda buffer." - (interactive) - (org-agenda-check-type t 'agenda 'timeline) - (setq org-agenda-show-log (not org-agenda-show-log)) - (org-agenda-set-mode-name) - (org-agenda-redo) - (message "Log mode is %s" - (if org-agenda-show-log "on" "off"))) - -(defun org-agenda-toggle-diary () - "Toggle diary inclusion in an agenda buffer." - (interactive) - (org-agenda-check-type t 'agenda) - (setq org-agenda-include-diary (not org-agenda-include-diary)) - (org-agenda-redo) - (org-agenda-set-mode-name) - (message "Diary inclusion turned %s" - (if org-agenda-include-diary "on" "off"))) - -(defun org-agenda-toggle-time-grid () - "Toggle time grid in an agenda buffer." - (interactive) - (org-agenda-check-type t 'agenda) - (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 - (concat "Org-Agenda" - (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-use-time-grid " Grid" "") - (if org-agenda-show-log " Log" ""))) - (force-mode-line-update)) - -(defun org-agenda-post-command-hook () - (and (eolp) (not (bolp)) (backward-char 1)) - (setq org-agenda-type (get-text-property (point) 'org-agenda-type)) - (if (and org-agenda-follow-mode - (get-text-property (point) 'org-marker)) - (org-agenda-show))) - -(defvar org-disable-agenda-to-diary nil) ;Dynamically-scoped param. - -(defun org-get-entries-from-diary (date) - "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 - (cons 'org-diary-default-entry list-diary-entries-hook)) - (diary-file-name-prefix-function nil) ; turn this feature off - (diary-modify-entry-list-string-function 'org-modify-diary-entry-string) - entries - (org-disable-agenda-to-diary t)) - (save-excursion - (save-window-excursion - (list-diary-entries date 1))) ;; Keep this name for now, compatibility - (if (not (get-buffer fancy-diary-buffer)) - (setq entries nil) - (with-current-buffer fancy-diary-buffer - (setq buffer-read-only nil) - (if (= (point-max) 1) - ;; No entries - (setq entries nil) - ;; Omit the date and other unnecessary stuff - (org-agenda-cleanup-fancy-diary) - ;; Add prefix to each line and extend the text properties - (if (= (point-max) 1) - (setq entries nil) - (setq entries (buffer-substring (point-min) (- (point-max) 1))))) - (set-buffer-modified-p nil) - (kill-buffer fancy-diary-buffer))) - (when entries - (setq entries (org-split-string entries "\n")) - (setq entries - (mapcar - (lambda (x) - (setq x (org-format-agenda-item "" x "Diary" nil 'time)) - ;; Extend the text properties to the beginning of the line - (org-add-props x (text-properties-at (1- (length x)) x))) - entries))))) - -(defun org-agenda-cleanup-fancy-diary () - "Remove unwanted stuff in buffer created by `fancy-diary-display'. -This gets rid of the date, the underline under the date, and -the dummy entry installed by `org-mode' to ensure non-empty diary for each -date. It also removes lines that contain only whitespace." - (goto-char (point-min)) - (if (looking-at ".*?:[ \t]*") - (progn - (replace-match "") - (re-search-forward "\n=+$" nil t) - (replace-match "") - (while (re-search-backward "^ +\n?" nil t) (replace-match ""))) - (re-search-forward "\n=+$" nil t) - (delete-region (point-min) (min (point-max) (1+ (match-end 0))))) - (goto-char (point-min)) - (while (re-search-forward "^ +\n" nil t) - (replace-match "")) - (goto-char (point-min)) - (if (re-search-forward "^Org-mode dummy\n?" nil t) - (replace-match ""))) - -;; Make sure entries from the diary have the right text properties. -(eval-after-load "diary-lib" - '(if (boundp 'diary-modify-entry-list-string-function) - ;; We can rely on the hook, nothing to do - nil - ;; Hook not avaiable, must use advice to make this work - (defadvice add-to-diary-list (before org-mark-diary-entry activate) - "Make the position visible." - (if (and org-disable-agenda-to-diary ;; called from org-agenda - (stringp string) - buffer-file-name) - (setq string (org-modify-diary-entry-string string)))))) - -(defun org-modify-diary-entry-string (string) - "Add text properties to string, allowing org-mode to act on it." - (org-add-props string nil - 'mouse-face 'highlight - 'keymap org-agenda-keymap - 'help-echo (format "mouse-2 or RET jump to diary file %s" - (abbreviate-file-name buffer-file-name)) - 'org-agenda-diary-link t - 'org-marker (org-agenda-new-marker (point-at-bol)))) - -(defun org-diary-default-entry () - "Add a dummy entry to the diary. -Needed to avoid empty dates which mess up holiday display." - ;; Catch the error if dealing with the new add-to-diary-alist - (when org-disable-agenda-to-diary - (condition-case nil - (add-to-diary-list original-date "Org-mode dummy" "") - (error - (add-to-diary-list original-date "Org-mode dummy" "" nil))))) - -;;;###autoload -(defun org-cycle-agenda-files () - "Cycle through the files in `org-agenda-files'. -If the current buffer visits an agenda file, find the next one in the list. -If the current buffer does not, find the first agenda file." - (interactive) - (let* ((fs (org-agenda-files t)) - (files (append fs (list (car fs)))) - (tcf (if buffer-file-name (file-truename buffer-file-name))) - file) - (unless files (error "No agenda files")) - (catch 'exit - (while (setq file (pop files)) - (if (equal (file-truename file) tcf) - (when (car files) - (find-file (car files)) - (throw 'exit t)))) - (find-file (car fs))))) - -(defun org-agenda-file-to-end () - "Move/add the current file to the end of the agenda file list. -If the file is not present in the list, it is appended to the list. If it is -present, it is moved there." - (interactive) - (org-agenda-file-to-front 'to-end)) - -(defun org-agenda-file-to-front (&optional to-end) - "Move/add the current file to the top of the agenda file list. -If the file is not present in the list, it is added to the front. If it is -present, it is moved there. With optional argument TO-END, add/move to the -end of the list." - (interactive "P") - (let ((file-alist (mapcar (lambda (x) - (cons (file-truename x) x)) - (org-agenda-files t))) - (ctf (file-truename buffer-file-name)) - x had) - (setq x (assoc ctf file-alist) had x) - - (if (not x) (setq x (cons ctf (abbreviate-file-name buffer-file-name)))) - (if to-end - (setq file-alist (append (delq x file-alist) (list x))) - (setq file-alist (cons x (delq x file-alist)))) - (org-store-new-agenda-file-list (mapcar 'cdr file-alist)) - (org-install-agenda-files-menu) - (message "File %s to %s of agenda file list" - (if had "moved" "added") (if to-end "end" "front")))) - -(defun org-remove-file (&optional file) - "Remove current file from the list of files in variable `org-agenda-files'. -These are the files which are being checked for agenda entries. -Optional argument FILE means, use this file instead of the current." - (interactive) - (let* ((file (or file buffer-file-name)) - (true-file (file-truename file)) - (afile (abbreviate-file-name file)) - (files (delq nil (mapcar - (lambda (x) - (if (equal true-file - (file-truename x)) - nil x)) - (org-agenda-files t))))) - (if (not (= (length files) (length (org-agenda-files t)))) - (progn - (org-store-new-agenda-file-list files) - (org-install-agenda-files-menu) - (message "Removed file: %s" afile)) - (message "File was not in list: %s" afile)))) - -(defun org-file-menu-entry (file) - (vector file (list 'find-file file) t)) - -(defun org-get-all-dates (beg end &optional no-ranges force-today inactive empty) - "Return a list of all relevant day numbers from BEG to END buffer positions. -If NO-RANGES is non-nil, include only the start and end dates of a range, -not every single day in the range. If FORCE-TODAY is non-nil, make -sure that TODAY is included in the list. If INACTIVE is non-nil, also -inactive time stamps (those in square brackets) are included. -When EMPTY is non-nil, also include days without any entries." - (let ((re (if inactive org-ts-regexp-both org-ts-regexp)) - dates dates1 date day day1 day2 ts1 ts2) - (if force-today - (setq dates (list (time-to-days (current-time))))) - (save-excursion - (goto-char beg) - (while (re-search-forward re end t) - (setq day (time-to-days (org-time-string-to-time - (substring (match-string 1) 0 10)))) - (or (memq day dates) (push day dates))) - (unless no-ranges - (goto-char beg) - (while (re-search-forward org-tr-regexp end t) - (setq ts1 (substring (match-string 1) 0 10) - ts2 (substring (match-string 2) 0 10) - day1 (time-to-days (org-time-string-to-time ts1)) - day2 (time-to-days (org-time-string-to-time ts2))) - (while (< (setq day1 (1+ day1)) day2) - (or (memq day1 dates) (push day1 dates))))) - (setq dates (sort dates '<)) - (when empty - (while (setq day (pop dates)) - (setq day2 (car dates)) - (push day dates1) - (when (and day2 empty) - (if (or (eq empty t) - (and (numberp empty) (<= (- day2 day) empty))) - (while (< (setq day (1+ day)) day2) - (push (list day) dates1)) - (push (cons :omitted (- day2 day)) dates1)))) - (setq dates (nreverse dates1))) - dates))) - -;;;###autoload -(defun org-diary (&rest args) - "Return diary information from org-files. -This function can be used in a \"sexp\" diary entry in the Emacs calendar. -It accesses org files and extracts information from those files to be -listed in the diary. The function accepts arguments specifying what -items should be listed. The following arguments are allowed: - - :timestamp List the headlines of items containing a date stamp or - date range matching the selected date. Deadlines will - also be listed, on the expiration day. - - :deadline List any deadlines past due, or due within - `org-deadline-warning-days'. The listing occurs only - in the diary for *today*, not at any other date. If - an entry is marked DONE, it is no longer listed. - - :scheduled List all items which are scheduled for the given date. - The diary for *today* also contains items which were - scheduled earlier and are not yet marked DONE. - - :todo List all TODO items from the org-file. This may be a - long list - so this is not turned on by default. - Like deadlines, these entries only show up in the - diary for *today*, not at any other date. - -The call in the diary file should look like this: - - &%%(org-diary) ~/path/to/some/orgfile.org - -Use a separate line for each org file to check. Or, if you omit the file name, -all files listed in `org-agenda-files' will be checked automatically: - - &%%(org-diary) - -If you don't give any arguments (as in the example above), the default -arguments (:deadline :scheduled :timestamp) are used. So the example above may -also be written as - - &%%(org-diary :deadline :timestamp :scheduled) - -The function expects the lisp variables `entry' and `date' to be provided -by the caller, because this is how the calendar works. Don't use this -function from a program - use `org-agenda-get-day-entries' instead." - (org-agenda-maybe-reset-markers) - (org-compile-prefix-format 'agenda) - (org-set-sorting-strategy 'agenda) - (setq args (or args '(:deadline :scheduled :timestamp))) - (let* ((files (if (and entry (stringp entry) (string-match "\\S-" entry)) - (list entry) - (org-agenda-files t))) - file rtn results) - ;; If this is called during org-agenda, don't return any entries to - ;; the calendar. Org Agenda will list these entries itself. - (if org-disable-agenda-to-diary (setq files nil)) - (while (setq file (pop files)) - (setq rtn (apply 'org-agenda-get-day-entries file date args)) - (setq results (append results rtn))) - (if results - (concat (org-finalize-agenda-entries results) "\n")))) -(defvar org-category-table nil) -(defun org-get-category-table () - "Get the table of categories and positions in current buffer." - (let (tbl) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward "\\(^\\|\r\\)#\\+CATEGORY:[ \t]*\\(.*\\)" nil t) - (push (cons (point) (org-trim (match-string 2))) tbl))) - tbl)) -(defun org-get-category (&optional pos) - "Get the category applying to position POS." - (if (not org-category-table) - (cond - ((null org-category) - (setq org-category - (if buffer-file-name - (file-name-sans-extension - (file-name-nondirectory buffer-file-name)) - "???"))) - ((symbolp org-category) (symbol-name org-category)) - (t org-category)) - (let ((tbl org-category-table) - (pos (or pos (point)))) - (while (and tbl (> (caar tbl) pos)) - (pop tbl)) - (or (cdar tbl) (cdr (nth (1- (length org-category-table)) - org-category-table)))))) - -(defun org-agenda-get-day-entries (file date &rest args) - "Does the work for `org-diary' and `org-agenda'. -FILE is the path to a file to be checked for entries. DATE is date like -the one returned by `calendar-current-date'. ARGS are symbols indicating -which kind of entries should be extracted. For details about these, see -the documentation of `org-diary'." - (setq args (or args '(:deadline :scheduled :timestamp))) - (let* ((org-startup-with-deadline-check nil) - (org-startup-folded nil) - (org-startup-align-all-tables nil) - (buffer (if (file-exists-p file) - (org-get-agenda-file-buffer file) - (error "No such file %s" file))) - arg results rtn) - (if (not buffer) - ;; If file does not exist, make sure an error message ends up in diary - (list (format "ORG-AGENDA-ERROR: No such org-file %s" file)) - (with-current-buffer buffer - (unless (org-mode-p) - (error "Agenda file %s is not in `org-mode'" file)) - (setq org-category-table (org-get-category-table)) - (let ((case-fold-search nil)) - (save-excursion - (save-restriction - (if org-agenda-restrict - (narrow-to-region org-agenda-restrict-begin - org-agenda-restrict-end) - (widen)) - ;; The way we repeatedly append to `results' makes it O(n^2) :-( - (while (setq arg (pop args)) - (cond - ((and (eq arg :todo) - (equal date (calendar-current-date))) - (setq rtn (org-agenda-get-todos)) - (setq results (append results rtn))) - ((eq arg :timestamp) - (setq rtn (org-agenda-get-blocks)) - (setq results (append results rtn)) - (setq rtn (org-agenda-get-timestamps)) - (setq results (append results rtn))) - ((eq arg :scheduled) - (setq rtn (org-agenda-get-scheduled)) - (setq results (append results rtn))) - ((eq arg :closed) - (setq rtn (org-agenda-get-closed)) - (setq results (append results rtn))) - ((and (eq arg :deadline) - (equal date (calendar-current-date))) - (setq rtn (org-agenda-get-deadlines)) - (setq results (append results rtn)))))))) - results)))) - -(defun org-entry-is-done-p () - "Is the current entry marked DONE?" - (save-excursion - (and (re-search-backward "[\r\n]\\*" nil t) - (looking-at org-nl-done-regexp)))) - -(defun org-at-date-range-p (&optional inactive-ok) - "Is the cursor inside a date range?" - (interactive) - (save-excursion - (catch 'exit - (let ((pos (point))) - (skip-chars-backward "^[<\r\n") - (skip-chars-backward "<[") - (and (looking-at (if inactive-ok org-tr-regexp-both org-tr-regexp)) - (>= (match-end 0) pos) - (throw 'exit t)) - (skip-chars-backward "^<[\r\n") - (skip-chars-backward "<[") - (and (looking-at (if inactive-ok org-tr-regexp-both org-tr-regexp)) - (>= (match-end 0) pos) - (throw 'exit t))) - nil))) - -(defun org-agenda-get-todos () - "Return the TODO information for agenda display." - (let* ((props (list 'face nil - 'done-face 'org-done - 'org-not-done-regexp org-not-done-regexp - 'mouse-face 'highlight - 'keymap org-agenda-keymap - 'help-echo - (format "mouse-2 or RET jump to org file %s" - (abbreviate-file-name buffer-file-name)))) - (regexp (concat "[\n\r]\\*+ *\\(" - (if org-select-this-todo-keyword - (concat "\\<\\(" org-select-this-todo-keyword - "\\)\\>") - org-not-done-regexp) - "[^\n\r]*\\)")) - (deadline-re (concat ".*\\(\n[^*].*\\)?" org-deadline-time-regexp)) - (sched-re (concat ".*\\(\n[^*].*\\)?" org-scheduled-time-regexp)) -; FIXME why was this wrong? (sched-re (concat ".*\n?.*?" org-scheduled-time-regexp)) - marker priority category tags - ee txt) - (goto-char (point-min)) - (while (re-search-forward regexp nil t) - (catch :skip - (save-match-data - (beginning-of-line) - (when (or (and org-agenda-todo-ignore-scheduled - (looking-at sched-re)) - (and org-agenda-todo-ignore-deadlines - (looking-at deadline-re) - (org-deadline-close (match-string 2)))) - - ;; FIXME: the following test also happens below, but we need it here - (or org-agenda-todo-list-sublevels (org-end-of-subtree 'invisible)) - (throw :skip nil))) - (org-agenda-skip) - (goto-char (match-beginning 1)) - (setq marker (org-agenda-new-marker (1+ (match-beginning 0))) - category (org-get-category) - tags (org-get-tags-at (point)) - txt (org-format-agenda-item "" (match-string 1) category tags) - priority - (+ (org-get-priority txt) - (if org-todo-kwd-priority-p - (- org-todo-kwd-max-priority -2 - (length - (member (match-string 2) org-todo-keywords))) - 1))) - (org-add-props txt props - 'org-marker marker 'org-hd-marker marker - 'priority priority 'category category) - (push txt ee) - (if org-agenda-todo-list-sublevels - (goto-char (match-end 1)) - (org-end-of-subtree 'invisible)))) - (nreverse ee))) - -(defconst org-agenda-no-heading-message - "No heading for this item in buffer or region.") - -(defun org-agenda-get-timestamps () - "Return the date stamp information for agenda display." - (let* ((props (list 'face nil - 'org-not-done-regexp org-not-done-regexp - 'mouse-face 'highlight - 'keymap org-agenda-keymap - 'help-echo - (format "mouse-2 or RET jump to org file %s" - (abbreviate-file-name buffer-file-name)))) - (regexp (regexp-quote - (substring - (format-time-string - (car org-time-stamp-formats) - (apply 'encode-time ; DATE bound by calendar - (list 0 0 0 (nth 1 date) (car date) (nth 2 date)))) - 0 11))) - marker hdmarker deadlinep scheduledp donep tmp priority category - ee txt timestr tags) - (goto-char (point-min)) - (while (re-search-forward regexp nil t) - (catch :skip - (and (save-match-data (org-at-date-range-p)) (throw :skip nil)) - (org-agenda-skip) - (setq marker (org-agenda-new-marker (match-beginning 0)) - category (org-get-category (match-beginning 0)) - tmp (buffer-substring (max (point-min) - (- (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)) - (and org-agenda-skip-scheduled-if-done - scheduledp donep - (throw :skip t)) - (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 - (goto-char (match-end 1)) - (setq hdmarker (org-agenda-new-marker) - tags (org-get-tags-at)) - (looking-at "\\*+[ \t]*\\([^\r\n]+\\)") - (setq txt (org-format-agenda-item - (format "%s%s" - (if deadlinep "Deadline: " "") - (if scheduledp "Scheduled: " "")) - (match-string 1) category tags timestr))) - (setq txt org-agenda-no-heading-message)) - (setq priority (org-get-priority txt)) - (org-add-props txt props - 'org-marker marker 'org-hd-marker hdmarker) - (if deadlinep - (org-add-props txt nil - 'face (if donep 'org-done 'org-warning) - 'undone-face 'org-warning 'done-face 'org-done - 'category category 'priority (+ 100 priority)) - (if scheduledp - (org-add-props txt nil - 'face 'org-scheduled-today - 'undone-face 'org-scheduled-today 'done-face 'org-done - 'category category 'priority (+ 99 priority)) - (org-add-props txt nil 'priority priority 'category category))) - (push txt ee)) - (outline-next-heading))) - (nreverse ee))) - -(defun org-agenda-get-closed () - "Return the logged TODO entries for agenda display." - (let* ((props (list 'mouse-face 'highlight - 'org-not-done-regexp org-not-done-regexp - 'keymap org-agenda-keymap - 'help-echo - (format "mouse-2 or RET jump to org file %s" - (abbreviate-file-name buffer-file-name)))) - (regexp (concat - "\\<\\(" org-closed-string "\\|" org-clock-string "\\) *\\[" - (regexp-quote - (substring - (format-time-string - (car org-time-stamp-formats) - (apply 'encode-time ; DATE bound by calendar - (list 0 0 0 (nth 1 date) (car date) (nth 2 date)))) - 1 11)))) - marker hdmarker priority category tags closedp - ee txt timestr) - (goto-char (point-min)) - (while (re-search-forward regexp nil t) - (catch :skip - (org-agenda-skip) - (setq marker (org-agenda-new-marker (match-beginning 0)) - closedp (equal (match-string 1) org-closed-string) - category (org-get-category (match-beginning 0)) - timestr (buffer-substring (match-beginning 0) (point-at-eol)) - ;; 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 - (goto-char (match-end 1)) - (setq hdmarker (org-agenda-new-marker) - tags (org-get-tags-at)) - (looking-at "\\*+[ \t]*\\([^\r\n]+\\)") - (setq txt (org-format-agenda-item - (if closedp "Closed: " "Clocked: ") - (match-string 1) category tags timestr))) - (setq txt org-agenda-no-heading-message)) - (setq priority 100000) - (org-add-props txt props - 'org-marker marker 'org-hd-marker hdmarker 'face 'org-done - 'priority priority 'category category - 'undone-face 'org-warning 'done-face 'org-done) - (push txt ee)) - (outline-next-heading))) - (nreverse ee))) - -(defun org-agenda-get-deadlines () - "Return the deadline information for agenda display." - (let* ((wdays org-deadline-warning-days) - (props (list 'mouse-face 'highlight - 'org-not-done-regexp org-not-done-regexp - 'keymap org-agenda-keymap - 'help-echo - (format "mouse-2 or RET jump to org file %s" - (abbreviate-file-name buffer-file-name)))) - (regexp org-deadline-time-regexp) - (todayp (equal date (calendar-current-date))) ; DATE bound by calendar - (d1 (calendar-absolute-from-gregorian date)) ; DATE bound by calendar - d2 diff pos pos1 category tags - ee txt head face) - (goto-char (point-min)) - (while (re-search-forward regexp nil t) - (catch :skip - (org-agenda-skip) - (setq pos (1- (match-beginning 1)) - d2 (time-to-days - (org-time-string-to-time (match-string 1))) - diff (- d2 d1)) - ;; When to show a deadline in the calendar: - ;; If the expiration is within wdays warning time. - ;; Past-due deadlines are only shown on the current date - (if (and (< diff wdays) todayp (not (= diff 0))) - (save-excursion - (setq category (org-get-category)) - (if (re-search-backward "\\(^\\|\r\\)\\*+[ \t]*" nil t) - (progn - (goto-char (match-end 0)) - (setq pos1 (match-end 1)) - (setq tags (org-get-tags-at pos1)) - (setq head (buffer-substring-no-properties - (point) - (progn (skip-chars-forward "^\r\n") - (point)))) - (if (string-match org-looking-at-done-regexp head) - (setq txt nil) - (setq txt (org-format-agenda-item - (format "In %3d d.: " diff) head category tags)))) - (setq txt org-agenda-no-heading-message)) - (when txt - (setq face (cond ((<= diff 0) 'org-warning) - ((<= diff 5) 'org-upcoming-deadline) - (t nil))) - (org-add-props txt props - 'org-marker (org-agenda-new-marker pos) - 'org-hd-marker (org-agenda-new-marker pos1) - 'priority (+ (- 10 diff) (org-get-priority txt)) - 'category category - 'face face 'undone-face face 'done-face 'org-done) - (push txt ee)))))) - ee)) - -(defun org-agenda-get-scheduled () - "Return the scheduled information for agenda display." - (let* ((props (list 'face 'org-scheduled-previously - 'org-not-done-regexp org-not-done-regexp - 'undone-face 'org-scheduled-previously - 'done-face 'org-done - 'mouse-face 'highlight - 'keymap org-agenda-keymap - 'help-echo - (format "mouse-2 or RET jump to org file %s" - (abbreviate-file-name buffer-file-name)))) - (regexp org-scheduled-time-regexp) - (todayp (equal date (calendar-current-date))) ; DATE bound by calendar - (d1 (calendar-absolute-from-gregorian date)) ; DATE bound by calendar - d2 diff pos pos1 category tags donep - ee txt head) - (goto-char (point-min)) - (while (re-search-forward regexp nil t) - (catch :skip - (org-agenda-skip) - (setq pos (1- (match-beginning 1)) - d2 (time-to-days - (org-time-string-to-time (match-string 1))) - diff (- d2 d1)) - ;; When to show a scheduled item in the calendar: - ;; If it is on or past the date. - (if (and (< diff 0) todayp) - (save-excursion - (setq category (org-get-category)) - (if (re-search-backward "\\(^\\|\r\\)\\*+[ \t]*" nil t) - (progn - (goto-char (match-end 0)) - (setq pos1 (match-end 1)) - (setq tags (org-get-tags-at)) - (setq head (buffer-substring-no-properties - (point) - (progn (skip-chars-forward "^\r\n") (point)))) - (if (string-match org-looking-at-done-regexp head) - (setq txt nil) - (setq txt (org-format-agenda-item - (format "Sched.%2dx: " (- 1 diff)) head - category tags)))) - (setq txt org-agenda-no-heading-message)) - (when txt - (org-add-props txt props - 'org-marker (org-agenda-new-marker pos) - 'org-hd-marker (org-agenda-new-marker pos1) - 'priority (+ (- 5 diff) (org-get-priority txt)) - 'category category) - (push txt ee)))))) - ee)) - -(defun org-agenda-get-blocks () - "Return the date-range information for agenda display." - (let* ((props (list 'face nil - 'org-not-done-regexp org-not-done-regexp - 'mouse-face 'highlight - 'keymap org-agenda-keymap - 'help-echo - (format "mouse-2 or RET jump to org file %s" - (abbreviate-file-name buffer-file-name)))) - (regexp org-tr-regexp) - (d0 (calendar-absolute-from-gregorian date)) - marker hdmarker ee txt d1 d2 s1 s2 timestr category tags pos) - (goto-char (point-min)) - (while (re-search-forward regexp nil t) - (catch :skip - (org-agenda-skip) - (setq pos (point)) - (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))) - (if (and (> (- d0 d1) -1) (> (- d2 d0) -1)) - ;; Only allow days between the limits, because the normal - ;; date stamps will catch the limits. - (save-excursion - (setq marker (org-agenda-new-marker (point))) - (setq category (org-get-category)) - (if (re-search-backward "\\(^\\|\r\\)\\*+" nil t) - (progn - (setq hdmarker (org-agenda-new-marker (match-end 1))) - (goto-char (match-end 1)) - (setq tags (org-get-tags-at)) - (looking-at "\\*+[ \t]*\\([^\r\n]+\\)") - (setq txt (org-format-agenda-item - (format (if (= d1 d2) "" "(%d/%d): ") - (1+ (- d0 d1)) (1+ (- d2 d1))) - (match-string 1) category tags - (if (= d0 d1) timestr)))) - (setq txt org-agenda-no-heading-message)) - (org-add-props txt props - 'org-marker marker 'org-hd-marker hdmarker - 'priority (org-get-priority txt) 'category category) - (push txt ee))) - (goto-char pos))) - ;; Sort the entries by expiration date. - (nreverse ee))) - -;; FIXME: should I allow spaces around the dash? -(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\\} +\\sw+ +\\)" - "\\([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'.") -(defvar org-prefix-has-tag 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 tags dotime - noprefix) - "Format TXT to be inserted into the agenda buffer. -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 overrule 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'. TAGS can be the tags of the headline." - (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)) - ""))) - (tag (if tags (nth (1- (length tags)) tags) "")) - 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 t))) - (if s2 (setq s2 (org-get-time-of-day s2 'string t)))) - - (when (string-match "\\([ \t]+\\)\\(:[a-zA-Z_@0-9:]+:\\)[ \t]*$" txt) - ;; Tags are in the string - (if (or (eq org-agenda-remove-tags-when-in-prefix t) - (and org-agenda-remove-tags-when-in-prefix - org-prefix-has-tag)) - (setq txt (replace-match "" t t txt)) - (setq txt (replace-match - (concat (make-string (max (- 50 (length txt)) 1) ?\ ) - (match-string 2 txt)) - t t txt)))) - - ;; 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 - (org-add-props rtn nil - 'category (downcase category) 'tags tags - 'prefix-length (- (length rtn) (length txt)) - 'time-of-day time-of-day - 'dotime dotime)))) - -(defvar org-agenda-sorting-strategy) -(defvar org-agenda-sorting-strategy-selected nil) - -(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 "" nil - (concat (substring time 0 -2) ":" (substring time -2))) - new) - (put-text-property - 1 (length (car new)) 'face 'org-time-grid (car new)))) - (if (member 'time-up org-agenda-sorting-strategy-selected) - (append new list) - (append list new))))) - -(defun org-compile-prefix-format (key) - "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'." - (setq org-prefix-has-time nil org-prefix-has-tag nil) - (let ((s (cond - ((stringp org-agenda-prefix-format) - org-agenda-prefix-format) - ((assq key org-agenda-prefix-format) - (cdr (assq key org-agenda-prefix-format))) - (t " %-12:c%?-12t% s"))) - (start 0) - varform vars var e c f opt) - (while (string-match "%\\(\\?\\)?\\([-+]?[0-9.]*\\)\\([ .;,:!?=|/<>]?\\)\\([cts]\\)" - s start) - (setq var (cdr (assoc (match-string 4 s) - '(("c" . category) ("t" . time) ("s" . extra) - ("T" . tag)))) - c (or (match-string 3 s) "") - opt (match-beginning 1) - start (1+ (match-beginning 0))) - (if (equal var 'time) (setq org-prefix-has-time t)) - (if (equal var 'tag) (setq org-prefix-has-tag 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-set-sorting-strategy (key) - (if (symbolp (car org-agenda-sorting-strategy)) - ;; the old format - (setq org-agenda-sorting-strategy-selected org-agenda-sorting-strategy) - (setq org-agenda-sorting-strategy-selected - (or (cdr (assq key org-agenda-sorting-strategy)) - (cdr (assq 'agenda org-agenda-sorting-strategy)) - '(time-up category-keep priority-down))))) - -(defun org-get-time-of-day (s &optional string mod24) - "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. -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)) - (let* ((h (string-to-number (match-string 1 s))) - (m (if (match-end 3) (string-to-number (match-string 3 s)) 0)) - (ampm (if (match-end 4) (downcase (match-string 4 s)))) - (am-p (equal ampm "am")) - (h1 (cond ((not ampm) h) - ((= h 12) (if am-p 0 12)) - (t (+ h (if am-p 0 12))))) - (h2 (if (and string mod24 (not (and (= m 0) (= h1 24)))) - (mod h1 24) h1)) - (t0 (+ (* 100 h2) m)) - (t1 (concat (if (>= h1 24) "+" " ") - (if (< t0 100) "0" "") - (if (< t0 10) "0" "") - (int-to-string t0)))) - (if string (concat (substring t1 -4 -2) ":" (substring t1 -2)) t0))))) - -(defun org-finalize-agenda-entries (list &optional nosort) - "Sort and concatenate the agenda items." - (setq list (mapcar 'org-agenda-highlight-todo list)) - (if nosort - list - (mapconcat 'identity (sort list 'org-entries-lessp) "\n"))) - -(defun org-agenda-highlight-todo (x) - (let (re pl) - (if (eq x 'line) - (save-excursion - (beginning-of-line 1) - (setq re (get-text-property (point) 'org-not-done-regexp)) - (goto-char (+ (point) (or (get-text-property (point) 'prefix-length) 0))) - (and (looking-at (concat "[ \t]*\\.*" re)) - (add-text-properties (match-beginning 0) (match-end 0) - '(face org-todo)))) - (setq re (concat (get-text-property 0 'org-not-done-regexp x)) - pl (get-text-property 0 'prefix-length x)) - (and re (equal (string-match (concat "\\(\\.*\\)" re) x (or pl 0)) pl) - (add-text-properties (or (match-end 1) (match-end 0)) (match-end 0) - '(face org-todo) x)) - x))) - -(defsubst org-cmp-priority (a b) - "Compare the priorities of string A and B." - (let ((pa (or (get-text-property 1 'priority a) 0)) - (pb (or (get-text-property 1 'priority b) 0))) - (cond ((> pa pb) +1) - ((< pa pb) -1) - (t nil)))) - -(defsubst org-cmp-category (a b) - "Compare the string values of categories of strings A and B." - (let ((ca (or (get-text-property 1 'category a) "")) - (cb (or (get-text-property 1 'category b) ""))) - (cond ((string-lessp ca cb) -1) - ((string-lessp cb ca) +1) - (t nil)))) - -(defsubst org-cmp-tag (a b) - "Compare the string values of categories of strings A and B." - (let ((ta (car (last (get-text-property 1 'tags a)))) - (tb (car (last (get-text-property 1 'tags b))))) - (cond ((not ta) +1) - ((not tb) -1) - ((string-lessp ta tb) -1) - ((string-lessp tb ta) +1) - (t nil)))) - -(defsubst org-cmp-time (a b) - "Compare the time-of-day values of strings A and B." - (let* ((def (if org-sort-agenda-notime-is-late 9901 -1)) - (ta (or (get-text-property 1 'time-of-day a) def)) - (tb (or (get-text-property 1 'time-of-day b) def))) - (cond ((< ta tb) -1) - ((< tb ta) +1) - (t nil)))) - -(defun org-entries-lessp (a b) - "Predicate for sorting agenda entries." - ;; The following variables will be used when the form is evaluated. - (let* ((time-up (org-cmp-time a b)) - (time-down (if time-up (- time-up) nil)) - (priority-up (org-cmp-priority a b)) - (priority-down (if priority-up (- priority-up) nil)) - (category-up (org-cmp-category a b)) - (category-down (if category-up (- category-up) nil)) - (category-keep (if category-up +1 nil)) - (tag-up (org-cmp-tag a b)) - (tag-down (if tag-up (- tag-up) nil))) - (cdr (assoc - (eval (cons 'or org-agenda-sorting-strategy-selected)) - '((-1 . t) (1 . nil) (nil . nil)))))) - -(defun org-agenda-show-priority () - "Show the priority of the current item. -This priority is composed of the main priority given with the [#A] cookies, -and by additional input from the age of a schedules or deadline entry." - (interactive) - (let* ((pri (get-text-property (point-at-bol) 'priority))) - (message "Priority is %d" (if pri pri -1000)))) - -(defun org-agenda-show-tags () - "Show the tags applicable to the current item." - (interactive) - (let* ((tags (get-text-property (point-at-bol) 'tags))) - (if tags - (message "Tags are :%s:" - (org-no-properties (mapconcat 'identity tags ":"))) - (message "No tags associated with this line")))) - -(defun org-agenda-goto (&optional highlight) - "Go to the Org-mode file which contains the item at point." - (interactive) - (let* ((marker (or (get-text-property (point) 'org-marker) - (org-agenda-error))) - (buffer (marker-buffer marker)) - (pos (marker-position marker))) - (switch-to-buffer-other-window buffer) - (widen) - (goto-char pos) - (when (org-mode-p) - (org-show-context 'agenda) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil)))) ; show the next heading - (and highlight (org-highlight (point-at-bol) (point-at-eol))))) - -(defun org-agenda-kill () - "Kill the entry or subtree belonging to the current agenda entry." - (interactive) - (let* ((marker (or (get-text-property (point) 'org-marker) - (org-agenda-error))) - (hdmarker (get-text-property (point) 'org-hd-marker)) - (buffer (marker-buffer marker)) - (pos (marker-position marker)) - dbeg dend (n 0) conf) - (org-with-remote-undo buffer - (with-current-buffer buffer - (save-excursion - (goto-char pos) - (if (org-mode-p) - (setq dbeg (progn (org-back-to-heading t) (point)) - dend (org-end-of-subtree t)) - (setq dbeg (point-at-bol) - dend (min (point-max) (1+ (point-at-eol))))) - (goto-char dbeg) - (while (re-search-forward "^[ \t]*\\S-" dend t) (setq n (1+ n))))) - (setq conf (or (eq t org-agenda-confirm-kill) - (and (numberp org-agenda-confirm-kill) - (> n org-agenda-confirm-kill)))) - (and conf - (not (y-or-n-p - (format "Delete entry with %d lines in buffer \"%s\"? " - n (buffer-name buffer)))) - (error "Abort")) - (org-remove-subtree-entries-from-agenda buffer dbeg dend) - (with-current-buffer buffer (delete-region dbeg dend)) - (message "Agenda item and source killed")))) - -(defun org-agenda-archive () - "Kill the entry or subtree belonging to the current agenda entry." - (interactive) - (let* ((marker (or (get-text-property (point) 'org-marker) - (org-agenda-error))) - (hdmarker (get-text-property (point) 'org-hd-marker)) - (buffer (marker-buffer marker)) - (pos (marker-position marker)) - dbeg dend txt n conf) - (org-with-remote-undo buffer - (with-current-buffer buffer - (if (org-mode-p) - (save-excursion - (goto-char pos) - (org-remove-subtree-entries-from-agenda) - (org-back-to-heading t) - (org-archive-subtree)) - (error "Archiving works only in Org-mode files")))))) - -(defun org-remove-subtree-entries-from-agenda (&optional buf beg end) - "Remove all lines in the agenda that correspond to a given subtree. -The subtree is the one in buffer BUF, starting at BEG and ending at END. -If this information is not given, the function uses the tree at point." - (let ((buf (or buf (current-buffer))) m p) - (save-excursion - (unless (and beg end) - (org-back-to-heading t) - (setq beg (point)) - (org-end-of-subtree t) - (setq end (point))) - (set-buffer (get-buffer org-agenda-buffer-name)) - (save-excursion - (goto-char (point-max)) - (beginning-of-line 1) - (while (not (bobp)) - (when (and (setq m (get-text-property (point) 'org-marker)) - (equal buf (marker-buffer m)) - (setq p (marker-position m)) - (>= p beg) - (<= p end)) - (let (buffer-read-only) - (delete-region (point-at-bol) (1+ (point-at-eol))))) - (beginning-of-line 0)))))) - -(defun org-agenda-open-link () - "Follow the link in the current line, if any." - (interactive) - (let ((eol (point-at-eol))) - (save-excursion - (if (or (re-search-forward org-bracket-link-regexp eol t) - (re-search-forward org-angle-link-re eol t) - (re-search-forward org-plain-link-re eol t)) - (call-interactively 'org-open-at-point) - (error "No link in current line"))))) - -(defun org-agenda-switch-to (&optional delete-other-windows) - "Go to the Org-mode file which contains the item at point." - (interactive) - (let* ((marker (or (get-text-property (point) 'org-marker) - (org-agenda-error))) - (buffer (marker-buffer marker)) - (pos (marker-position marker))) - (switch-to-buffer buffer) - (and delete-other-windows (delete-other-windows)) - (widen) - (goto-char pos) - (when (org-mode-p) - (org-show-context 'agenda) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil)))))) ; show the next heading - -(defun org-agenda-goto-mouse (ev) - "Go to the Org-mode file which contains the item at the mouse click." - (interactive "e") - (mouse-set-point ev) - (org-agenda-goto)) - -(defun org-agenda-show () - "Display the Org-mode file which contains the item at point." - (interactive) - (let ((win (selected-window))) - (org-agenda-goto t) - (select-window win))) - -(defun org-agenda-recenter (arg) - "Display the Org-mode file which contains the item at point and recenter." - (interactive "P") - (let ((win (selected-window))) - (org-agenda-goto t) - (recenter arg) - (select-window win))) - -(defun org-agenda-show-mouse (ev) - "Display the Org-mode file which contains the item at the mouse click." - (interactive "e") - (mouse-set-point ev) - (org-agenda-show)) - -(defun org-agenda-check-no-diary () - "Check if the entry is a diary link and abort if yes." - (if (get-text-property (point) 'org-agenda-diary-link) - (org-agenda-error))) - -(defun org-agenda-error () - (error "Command not allowed in this line")) - -(defun org-agenda-tree-to-indirect-buffer () - "Show the subtree corresponding to the current entry in an indirect buffer. -This calls the command `org-tree-to-indirect-buffer' from the original -Org-mode buffer. -With numerical prefix arg ARG, go up to this level and then take that tree. -With a C-u prefix, make a separate frame for this tree (i.e. don't use the -dedicated frame)." - (interactive) - (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))) - (with-current-buffer buffer - (save-excursion - (goto-char pos) - (org-tree-to-indirect-buffer))))) - -(defvar org-last-heading-marker (make-marker) - "Marker pointing to the headline that last changed its TODO state -by a remote command from the agenda.") - -(defun org-agenda-todo (&optional arg) - "Cycle TODO state of line at point, also in Org-mode file. -This changes the line at point, all other lines in the agenda referring to -the same tree node, and the headline of the tree node in the Org-mode file." - (interactive "P") - (org-agenda-check-no-diary) - (let* ((col (current-column)) - (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)) - (buffer-read-only nil) - newhead) - (org-with-remote-undo buffer - (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-todo arg) - (and (bolp) (forward-char 1)) - (setq newhead (org-get-heading)) - (save-excursion - (org-back-to-heading) - (move-marker org-last-heading-marker (point)))) - (beginning-of-line 1) - (save-excursion - (org-agenda-change-all-lines newhead hdmarker 'fixface)) - (move-to-column col)))) - -(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 -`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* ((buffer-read-only nil) - props m pl undone-face done-face finish new dotime cat tags) - (save-excursion - (goto-char (point-max)) - (beginning-of-line 1) - (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) - cat (get-text-property (point) 'category) - tags (get-text-property (point) 'tags) - new (org-format-agenda-item "x" newhead cat tags 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) - (cond - ((equal new "") - (beginning-of-line 1) - (and (looking-at ".*\n?") (replace-match ""))) - ((looking-at ".*") - (replace-match new t t) - (beginning-of-line 1) - (add-text-properties (point-at-bol) (point-at-eol) props) - (when fixface - (add-text-properties - (point-at-bol) (point-at-eol) - (list 'face - (if org-last-todo-state-is-todo - undone-face done-face)))) - (org-agenda-highlight-todo 'line) - (beginning-of-line 1)) - (t (error "Line update did not work")))) - (beginning-of-line 0))) - (org-finalize-agenda))) - -(defun org-agenda-align-tags (&optional line) - "Align all tags in agenda items to `org-agenda-align-tags-to-column'." - (let ((buffer-read-only)) - (save-excursion - (goto-char (if line (point-at-bol) (point-min))) - (while (re-search-forward "\\([ \t]+\\):[a-zA-Z0-9_@:]+:[ \t]*$" - (if line (point-at-eol) nil) t) - (delete-region (match-beginning 1) (match-end 1)) - (goto-char (match-beginning 1)) - (insert (org-add-props - (make-string (max 1 (- org-agenda-align-tags-to-column - (current-column))) ?\ ) - (text-properties-at (point)))))))) - -(defun org-agenda-priority-up () - "Increase the priority of line at point, also in Org-mode file." - (interactive) - (org-agenda-priority 'up)) - -(defun org-agenda-priority-down () - "Decrease the priority of line at point, also in Org-mode file." - (interactive) - (org-agenda-priority 'down)) - -(defun org-agenda-priority (&optional force-direction) - "Set the priority of line at point, also in Org-mode file. -This changes the line at point, all other lines in the agenda referring to -the same tree node, and the headline of the tree node in the Org-mode file." - (interactive) - (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)) - (buffer-read-only nil) - newhead) - (org-with-remote-undo buffer - (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 - (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)))) - -(defun org-get-tags-at (&optional pos) - "Get a list of all headline tags applicable at POS. -POS defaults to point. If tags are inherited, the list contains -the targets in the same sequence as the headlines appear, i.e. -the tags of the current headline come last." - (interactive) - (let (tags) - (save-excursion - (goto-char (or pos (point))) - (save-match-data - (org-back-to-heading t) - (condition-case nil - (while t - (if (looking-at "[^\r\n]+?:\\([a-zA-Z_@0-9:]+\\):[ \t]*\\([\n\r]\\|\\'\\)") - (setq tags (append (org-split-string - (org-match-string-no-properties 1) ":") - tags))) - (or org-use-tag-inheritance (error "")) - (org-up-heading-all 1)) - (error nil)))) - tags)) - -;; FIXME: should fix the tags property of the agenda line. -(defun org-agenda-set-tags () - "Set tags for the current headline." - (interactive) - (org-agenda-check-no-diary) - (org-agenda-show) ;;; FIXME This is a stupid hack and should not be needed - (let* ((hdmarker (or (get-text-property (point) 'org-hd-marker) - (org-agenda-error))) - (buffer (marker-buffer hdmarker)) - (pos (marker-position hdmarker)) - (buffer-read-only nil) - newhead) - (org-with-remote-undo buffer - (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 - (call-interactively 'org-set-tags) - (end-of-line 1) - (setq newhead (org-get-heading))) - (org-agenda-change-all-lines newhead hdmarker) - (beginning-of-line 1)))) - -(defun org-agenda-toggle-archive-tag () - "Toggle the archive tag for the current entry." - (interactive) - (org-agenda-check-no-diary) - (org-agenda-show) ;;; FIXME This is a stupid hack and should not be needed - (let* ((hdmarker (or (get-text-property (point) 'org-hd-marker) - (org-agenda-error))) - (buffer (marker-buffer hdmarker)) - (pos (marker-position hdmarker)) - (buffer-read-only nil) - newhead) - (org-with-remote-undo buffer - (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 - (call-interactively 'org-toggle-archive-tag) - (end-of-line 1) - (setq newhead (org-get-heading))) - (org-agenda-change-all-lines newhead hdmarker) - (beginning-of-line 1)))) - -(defun org-agenda-date-later (arg &optional what) - "Change the date of this item to one day later." - (interactive "p") - (org-agenda-check-type t 'agenda 'timeline) - (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))) - (org-with-remote-undo buffer - (with-current-buffer buffer - (widen) - (goto-char pos) - (if (not (org-at-timestamp-p)) - (error "Cannot find time stamp")) - (org-timestamp-change arg (or what 'day))) - (org-agenda-show-new-time marker org-last-changed-timestamp)) - (message "Time stamp changed to %s" org-last-changed-timestamp))) - -(defun org-agenda-date-earlier (arg &optional what) - "Change the date of this item to one day earlier." - (interactive "p") - (org-agenda-date-later (- arg) what)) - -(defun org-agenda-show-new-time (marker stamp) - "Show new date stamp via text properties." - ;; We use text properties to make this undoable - (let ((buffer-read-only nil) - ovs ov) - (setq stamp (concat " => " stamp)) - (save-excursion - (goto-char (point-max)) - (while (not (bobp)) - (when (equal marker (get-text-property (point) 'org-marker)) - (move-to-column (- (window-width) (length stamp)) t) - (if (featurep 'xemacs) - ;; Use `duplicable' property to trigger undo recording - (let ((ex (make-extent nil nil)) - (gl (make-glyph stamp))) - (set-glyph-face gl 'secondary-selection) - (set-extent-properties - ex (list 'invisible t 'end-glyph gl 'duplicable t)) - (insert-extent ex (1- (point)) (point-at-eol))) - (add-text-properties - (1- (point)) (point-at-eol) - (list 'display (org-add-props stamp nil - 'face 'secondary-selection)))) - (beginning-of-line 1)) - (beginning-of-line 0))))) - -(defun org-agenda-date-prompt (arg) - "Change the date of this item. Date is prompted for, with default today. -The prefix ARG is passed to the `org-time-stamp' command and can therefore -be used to request time specification in the time stamp." - (interactive "P") - (org-agenda-check-type t 'agenda 'timeline) - (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))) - (org-with-remote-undo buffer - (with-current-buffer buffer - (widen) - (goto-char pos) - (if (not (org-at-timestamp-p)) - (error "Cannot find time stamp")) - (org-time-stamp arg) - (message "Time stamp changed to %s" org-last-changed-timestamp))))) - -(defun org-agenda-schedule (arg) - "Schedule the item at point." - (interactive "P") - (org-agenda-check-type t 'agenda 'timeline 'todo 'tags) - (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)) - (org-insert-labeled-timestamps-at-point nil) - ts) - (org-with-remote-undo buffer - (with-current-buffer buffer - (widen) - (goto-char pos) - (setq ts (org-schedule)) - (message "Item scheduled for %s" ts))))) - -(defun org-agenda-deadline (arg) - "Schedule the item at point." - (interactive "P") - (org-agenda-check-type t 'agenda 'timeline 'todo 'tags) - (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)) - (org-insert-labeled-timestamps-at-point nil) - ts) - (org-with-remote-undo buffer - (with-current-buffer buffer - (widen) - (goto-char pos) - (setq ts (org-deadline)) - (message "Deadline for this item set to %s" ts))))) - -(defun org-get-heading () - "Return the heading of the current entry, without the stars." - (save-excursion - (and (memq (char-before) '(?\n ?\r)) (skip-chars-forward "^\n\r")) - (if (and (re-search-backward "[\r\n]\\*" nil t) - (looking-at "[\r\n]\\*+[ \t]+\\([^\r\n]*\\)")) - (match-string 1) - ""))) - -(defun org-agenda-clock-in (&optional arg) - "Start the clock on the currently selected item." - (interactive "P") - (org-agenda-check-no-diary) - (let* ((marker (or (get-text-property (point) 'org-marker) - (org-agenda-error))) - (pos (marker-position marker))) - (org-with-remote-undo (marker-buffer marker) - (with-current-buffer (marker-buffer marker) - (widen) - (goto-char pos) - (org-clock-in))))) - -(defun org-agenda-clock-out (&optional arg) - "Stop the currently running clock." - (interactive "P") - (unless (marker-buffer org-clock-marker) - (error "No running clock")) - (org-with-remote-undo (marker-buffer org-clock-marker) - (org-clock-out))) - -(defun org-agenda-clock-cancel (&optional arg) - "Cancel the currently running clock." - (interactive "P") - (unless (marker-buffer org-clock-marker) - (error "No running clock")) - (org-with-remote-undo (marker-buffer org-clock-marker) - (org-clock-cancel))) - -(defun org-agenda-diary-entry () - "Make a diary entry, like the `i' command from the calendar. -All the standard commands work: block, weekly etc." - (interactive) - (org-agenda-check-type t 'agenda 'timeline) - (require 'diary-lib) - (let* ((char (progn - (message "Diary entry: [d]ay [w]eekly [m]onthly [y]early [a]nniversary [b]lock [c]yclic") - (read-char-exclusive))) - (cmd (cdr (assoc char - '((?d . insert-diary-entry) - (?w . insert-weekly-diary-entry) - (?m . insert-monthly-diary-entry) - (?y . insert-yearly-diary-entry) - (?a . insert-anniversary-diary-entry) - (?b . insert-block-diary-entry) - (?c . insert-cyclic-diary-entry))))) - (oldf (symbol-function 'calendar-cursor-to-date)) -; (buf (get-file-buffer (substitute-in-file-name diary-file))) - (point (point)) - (mark (or (mark t) (point)))) - (unless cmd - (error "No command associated with <%c>" char)) - (unless (and (get-text-property point 'day) - (or (not (equal ?b char)) - (get-text-property mark 'day))) - (error "Don't know which date to use for diary entry")) - ;; We implement this by hacking the `calendar-cursor-to-date' function - ;; and the `calendar-mark-ring' variable. Saves a lot of code. - (let ((calendar-mark-ring - (list (calendar-gregorian-from-absolute - (or (get-text-property mark 'day) - (get-text-property point 'day)))))) - (unwind-protect - (progn - (fset 'calendar-cursor-to-date - (lambda (&optional error) - (calendar-gregorian-from-absolute - (get-text-property point 'day)))) - (call-interactively cmd)) - (fset 'calendar-cursor-to-date oldf))))) - - -(defun org-agenda-execute-calendar-command (cmd) - "Execute a calendar command from the agenda, with the date associated to -the cursor position." - (org-agenda-check-type t 'agenda 'timeline) - (require 'diary-lib) - (unless (get-text-property (point) 'day) - (error "Don't know which date to use for calendar command")) - (let* ((oldf (symbol-function 'calendar-cursor-to-date)) - (point (point)) - (date (calendar-gregorian-from-absolute - (get-text-property point 'day))) - (displayed-day (extract-calendar-day date)) - (displayed-month (extract-calendar-month date)) - (displayed-year (extract-calendar-year date))) - (unwind-protect - (progn - (fset 'calendar-cursor-to-date - (lambda (&optional error) - (calendar-gregorian-from-absolute - (get-text-property point 'day)))) - (call-interactively cmd)) - (fset 'calendar-cursor-to-date oldf)))) - -(defun org-agenda-phases-of-moon () - "Display the phases of the moon for the 3 months around the cursor date." - (interactive) - (org-agenda-execute-calendar-command 'calendar-phases-of-moon)) - -(defun org-agenda-holidays () - "Display the holidays for the 3 months around the cursor date." - (interactive) - (org-agenda-execute-calendar-command 'list-calendar-holidays)) - -(defun org-agenda-sunrise-sunset (arg) - "Display sunrise and sunset for the cursor date. -Latitude and longitude can be specified with the variables -`calendar-latitude' and `calendar-longitude'. When called with prefix -argument, latitude and longitude will be prompted for." - (interactive "P") - (let ((calendar-longitude (if arg nil calendar-longitude)) - (calendar-latitude (if arg nil calendar-latitude)) - (calendar-location-name - (if arg "the given coordinates" calendar-location-name))) - (org-agenda-execute-calendar-command 'calendar-sunrise-sunset))) - -(defun org-agenda-goto-calendar () - "Open the Emacs calendar with the date at the cursor." - (interactive) - (org-agenda-check-type t 'agenda 'timeline) - (let* ((day (or (get-text-property (point) 'day) - (error "Don't know which date to open in calendar"))) - (date (calendar-gregorian-from-absolute day)) - (calendar-move-hook nil) - (view-calendar-holidays-initially nil) - (view-diary-entries-initially nil)) - (calendar) - (calendar-goto-date date))) - -(defun org-calendar-goto-agenda () - "Compute the Org-mode agenda for the calendar date displayed at the cursor. -This is a command that has to be installed in `calendar-mode-map'." - (interactive) - (org-agenda-list nil (calendar-absolute-from-gregorian - (calendar-cursor-to-date)) - nil)) - -(defun org-agenda-convert-date () - (interactive) - (org-agenda-check-type t 'agenda 'timeline) - (let ((day (get-text-property (point) 'day)) - date s) - (unless day - (error "Don't know which date to convert")) - (setq date (calendar-gregorian-from-absolute day)) - (setq s (concat - "Gregorian: " (calendar-date-string date) "\n" - "ISO: " (calendar-iso-date-string date) "\n" - "Day of Yr: " (calendar-day-of-year-string date) "\n" - "Julian: " (calendar-julian-date-string date) "\n" - "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" - "French: " (calendar-french-date-string date) "\n" - "Mayan: " (calendar-mayan-date-string date) "\n" - "Coptic: " (calendar-coptic-date-string date) "\n" - "Ethiopic: " (calendar-ethiopic-date-string date) "\n" - "Persian: " (calendar-persian-date-string date) "\n" - "Chinese: " (calendar-chinese-date-string date) "\n")) - (with-output-to-temp-buffer "*Dates*" - (princ s)) - (if (fboundp 'fit-window-to-buffer) - (fit-window-to-buffer (get-buffer-window "*Dates*"))))) - -;;;; Tags - -(defun org-scan-tags (action matcher &optional todo-only) - "Scan headline tags with inheritance and produce output ACTION. -ACTION can be `sparse-tree' or `agenda'. MATCHER is a Lisp form to be -evaluated, testing if a given set of tags qualifies a headline for -inclusion. When TODO-ONLY is non-nil, only lines with a TODO keyword -are included in the output." - (let* ((re (concat "[\n\r]" outline-regexp " *\\(\\<\\(" - (mapconcat 'regexp-quote - (nreverse (cdr (reverse org-todo-keywords))) - "\\|") - "\\>\\)\\)? *\\(.*?\\)\\(:[A-Za-z_@0-9:]+:\\)?[ \t]*$")) - (props (list 'face nil - 'done-face 'org-done - 'undone-face nil - 'mouse-face 'highlight - 'org-not-done-regexp org-not-done-regexp - 'keymap org-agenda-keymap - 'help-echo - (format "mouse-2 or RET jump to org file %s" - (abbreviate-file-name buffer-file-name)))) - (case-fold-search nil) - lspos - tags tags-list tags-alist (llast 0) rtn level category i txt - todo marker) - (save-excursion - (goto-char (point-min)) - (when (eq action 'sparse-tree) (org-overview)) - (while (re-search-forward re nil t) - (catch :skip - (and (eq action 'agenda) (org-agenda-skip)) - (setq todo (if (match-end 1) (match-string 2)) - tags (if (match-end 4) (match-string 4))) - (goto-char (setq lspos (1+ (match-beginning 0)))) - (setq level (funcall outline-level) - category (org-get-category)) - (setq i llast llast level) - ;; remove tag lists from same and sublevels - (while (>= i level) - (when (setq entry (assoc i tags-alist)) - (setq tags-alist (delete entry tags-alist))) - (setq i (1- i))) - ;; add the nex tags - (when tags - (setq tags (mapcar 'downcase (org-split-string tags ":")) - tags-alist - (cons (cons level tags) tags-alist))) - ;; compile tags for current headline - (setq tags-list - (if org-use-tag-inheritance - (apply 'append (mapcar 'cdr tags-alist)) - tags)) - (when (and (or (not todo-only) todo) - (eval matcher) - (or (not org-agenda-skip-archived-trees) - (not (member org-archive-tag tags-list)))) - ;; list this headline - (if (eq action 'sparse-tree) - (progn - (org-show-context 'tags-tree)) - (setq txt (org-format-agenda-item - "" - (concat - (if org-tags-match-list-sublevels - (make-string (1- level) ?.) "") - (org-get-heading)) - category tags-list)) - (goto-char lspos) - (setq marker (org-agenda-new-marker)) - (org-add-props txt props - 'org-marker marker 'org-hd-marker marker 'category category) - (push txt rtn)) - ;; if we are to skip sublevels, jump to end of subtree - (or org-tags-match-list-sublevels (org-end-of-subtree t)))))) - (when (and (eq action 'sparse-tree) - (not org-sparse-tree-open-archived-trees)) - (org-hide-archived-subtrees (point-min) (point-max))) - (nreverse rtn))) - -(defvar todo-only) ;; dynamically scoped - -(defun org-tags-sparse-tree (&optional todo-only match) - "Create a sparse tree according to tags string MATCH. -MATCH can contain positive and negative selection of tags, like -\"+WORK+URGENT-WITHBOSS\". -If optional argument TODO_ONLY is non-nil, only select lines that are -also TODO lines." - (interactive "P") - (org-scan-tags 'sparse-tree (cdr (org-make-tags-matcher match)) todo-only)) - -;; FIXME: implement search for a specific level. -(defun org-make-tags-matcher (match) - "Create the TAGS//TODO matcher form for the selection string MATCH." - ;; todo-only is scoped dynamically into this function, and the function - ;; may change it it the matcher asksk for it. - (unless match - ;; Get a new match request, with completion - (setq org-last-tags-completion-table - (or org-tag-alist - org-last-tags-completion-table)) - (setq match (completing-read - "Match: " 'org-tags-completion-function nil nil nil - 'org-tags-history))) ; FIXME: Separate history for this? - - ;; Parse the string and create a lisp form - (let ((match0 match) - (re "^&?\\([-+:]\\)?\\({[^}]+}\\|LEVEL=\\([0-9]+\\)\\|[A-Za-z_@0-9]+\\)") - minus tag mm - tagsmatch todomatch tagsmatcher todomatcher kwd matcher - orterms term orlist re-p level-p) - (if (string-match "/+" match) - ;; match contains also a todo-matching request - (progn - (setq tagsmatch (substring match 0 (match-beginning 0)) - todomatch (substring match (match-end 0))) - (if (string-match "^!" todomatch) - (setq todo-only t todomatch (substring todomatch 1))) - (if (string-match "^\\s-*$" todomatch) - (setq todomatch nil))) - ;; only matching tags - (setq tagsmatch match todomatch nil)) - - ;; Make the tags matcher - (if (or (not tagsmatch) (not (string-match "\\S-" tagsmatch))) - (setq tagsmatcher t) - (setq orterms (org-split-string tagsmatch "|") orlist nil) - (while (setq term (pop orterms)) - (while (and (equal (substring term -1) "\\") orterms) - (setq term (concat term "|" (pop orterms)))) ; repair bad split - (while (string-match re term) - (setq minus (and (match-end 1) - (equal (match-string 1 term) "-")) - tag (match-string 2 term) - re-p (equal (string-to-char tag) ?{) - level-p (match-end 3) - mm (cond - (re-p `(org-match-any-p ,(substring tag 1 -1) tags-list)) - (level-p `(= level ,(string-to-number - (match-string 3 term)))) - (t `(member ,(downcase tag) tags-list))) - mm (if minus (list 'not mm) mm) - term (substring term (match-end 0))) - (push mm tagsmatcher)) - (push (if (> (length tagsmatcher) 1) - (cons 'and tagsmatcher) - (car tagsmatcher)) - orlist) - (setq tagsmatcher nil)) - (setq tagsmatcher (if (> (length orlist) 1) (cons 'or orlist) (car orlist)))) - - ;; Make the todo matcher - (if (or (not todomatch) (not (string-match "\\S-" todomatch))) - (setq todomatcher t) - (setq orterms (org-split-string todomatch "|") orlist nil) - (while (setq term (pop orterms)) - (while (string-match re term) - (setq minus (and (match-end 1) - (equal (match-string 1 term) "-")) - kwd (match-string 2 term) - re-p (equal (string-to-char kwd) ?{) - term (substring term (match-end 0)) - mm (if re-p - `(string-match ,(substring kwd 1 -1) todo) - (list 'equal 'todo kwd)) - mm (if minus (list 'not mm) mm)) - (push mm todomatcher)) - (push (if (> (length todomatcher) 1) - (cons 'and todomatcher) - (car todomatcher)) - orlist) - (setq todomatcher nil)) - (setq todomatcher (if (> (length orlist) 1) - (cons 'or orlist) (car orlist)))) - - ;; Return the string and lisp forms of the matcher - (setq matcher (if todomatcher - (list 'and tagsmatcher todomatcher) - tagsmatcher)) - (cons match0 matcher))) - -(defun org-match-any-p (re list) - "Does re match any element of list?" - (setq list (mapcar (lambda (x) (string-match re x)) list)) - (delq nil list)) - -;;;###autoload -(defun org-tags-view (&optional todo-only match) - "Show all headlines for all `org-agenda-files' matching a TAGS criterion. -The prefix arg TODO-ONLY limits the search to TODO entries." - (interactive "P") - (org-compile-prefix-format 'tags) - (org-set-sorting-strategy 'tags) - (let* ((org-tags-match-list-sublevels - (if todo-only t org-tags-match-list-sublevels)) - (win (selected-window)) - (completion-ignore-case t) - rtn rtnall files file pos matcher - buffer) - (setq matcher (org-make-tags-matcher match) - match (car matcher) matcher (cdr matcher)) - (org-prepare-agenda) - (setq org-agenda-redo-command - (list 'org-tags-view (list 'quote todo-only) - (list 'if 'current-prefix-arg nil match))) - (setq files (org-agenda-files) - rtnall nil) - (while (setq file (pop files)) - (catch 'nextfile - (org-check-agenda-file file) - (setq buffer (if (file-exists-p file) - (org-get-agenda-file-buffer file) - (error "No such file %s" file))) - (if (not buffer) - ;; If file does not exist, merror message to agenda - (setq rtn (list - (format "ORG-AGENDA-ERROR: No such org-file %s" file)) - rtnall (append rtnall rtn)) - (with-current-buffer buffer - (unless (org-mode-p) - (error "Agenda file %s is not in `org-mode'" file)) - (setq org-category-table (org-get-category-table)) - (save-excursion - (save-restriction - (if org-agenda-restrict - (narrow-to-region org-agenda-restrict-begin - org-agenda-restrict-end) - (widen)) - (setq rtn (org-scan-tags 'agenda matcher todo-only)) - (setq rtnall (append rtnall rtn)))))))) - (if org-agenda-overriding-header - (insert (org-add-props (copy-sequence org-agenda-overriding-header) - nil 'face 'org-level-3) "\n") - (insert "Headlines with TAGS match: ") - (add-text-properties (point-min) (1- (point)) - (list 'face 'org-level-3)) - (setq pos (point)) - (insert match "\n") - (add-text-properties pos (1- (point)) (list 'face 'org-warning)) - (setq pos (point)) - (unless org-agenda-multi - (insert "Press `C-u r' to search again with new search string\n")) - (add-text-properties pos (1- (point)) (list 'face 'org-level-3))) - (when rtnall - (insert (org-finalize-agenda-entries rtnall) "\n")) - (goto-char (point-min)) - (org-fit-agenda-window) - (add-text-properties (point-min) (point-max) '(org-agenda-type tags)) - (org-finalize-agenda) - (setq buffer-read-only t) - (if (not org-select-agenda-window) (select-window win)))) - -(defvar org-add-colon-after-tag-completion nil) ;; dynamically skoped param -(defvar org-tags-overlay (org-make-overlay 1 1)) -(org-detach-overlay org-tags-overlay) - -(defun org-set-tags (&optional arg just-align) - "Set the tags for the current headline. -With prefix ARG, realign all tags in headings in the current buffer." - (interactive "P") - (let* ((re (concat "^" outline-regexp)) - (current (org-get-tags)) - table current-tags inherited-tags ; computed below when needed - tags p0 c0 c1 rpl) - (if arg - (save-excursion - (goto-char (point-min)) - (let (buffer-invisibility-spec) ; Emacs 21 compatibility - (while (re-search-forward re nil t) - (org-set-tags nil t) - (end-of-line 1))) - (message "All tags realigned to column %d" org-tags-column)) - (if just-align - (setq tags current) - ;; Get a new set of tags from the user - (setq table (or org-tag-alist (org-get-buffer-tags)) - org-last-tags-completion-table table - current-tags (org-split-string current ":") - inherited-tags (nreverse - (nthcdr (length current-tags) - (nreverse (org-get-tags-at)))) - tags - (if (or (eq t org-use-fast-tag-selection) - (and org-use-fast-tag-selection - (delq nil (mapcar 'cdr table)))) - (org-fast-tag-selection current-tags inherited-tags table) - (let ((org-add-colon-after-tag-completion t)) - (org-trim - (completing-read "Tags: " 'org-tags-completion-function - nil nil current 'org-tags-history))))) - (while (string-match "[-+&]+" tags) - ;; No boolean logic, just a list - (setq tags (replace-match ":" t t tags)))) - (if (string-match "\\`[\t ]*\\'" tags) - (setq tags "") - (unless (string-match ":$" tags) (setq tags (concat tags ":"))) - (unless (string-match "^:" tags) (setq tags (concat ":" tags)))) - - ;; Insert new tags at the correct column - (beginning-of-line 1) - (if (re-search-forward - (concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$") - (point-at-eol) t) - (progn - (if (equal tags "") - (setq rpl "") - (goto-char (match-beginning 0)) - (setq c0 (current-column) p0 (point) - c1 (max (1+ c0) (if (> org-tags-column 0) - org-tags-column - (- (- org-tags-column) (length tags)))) - rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags))) - (replace-match rpl) - (and (not (featurep 'xemacs)) c0 (tabify p0 (point))) - tags) - (error "Tags alignment failed"))))) - -(defun org-tags-completion-function (string predicate &optional flag) - (let (s1 s2 rtn (ctable org-last-tags-completion-table) - (confirm (lambda (x) (stringp (car x))))) - (if (string-match "^\\(.*[-+:&|]\\)\\([^-+:&|]*\\)$" string) - (setq s1 (match-string 1 string) - s2 (match-string 2 string)) - (setq s1 "" s2 string)) - (cond - ((eq flag nil) - ;; try completion - (setq rtn (try-completion s2 ctable confirm)) - (if (stringp rtn) - (concat s1 s2 (substring rtn (length s2)) - (if (and org-add-colon-after-tag-completion - (assoc rtn ctable)) - ":" ""))) - ) - ((eq flag t) - ;; all-completions - (all-completions s2 ctable confirm) - ) - ((eq flag 'lambda) - ;; exact match? - (assoc s2 ctable))) - )) - -(defun org-fast-tag-insert (kwd tags face &optional end) - "Insert KDW, and the TAGS, the latter with face FACE. Also inser END." - (insert (format "%-12s" (concat kwd ":")) - (org-add-props (mapconcat 'identity tags " ") nil 'face face) - (or end ""))) - -(defun org-fast-tag-show-exit (flag) - (save-excursion - (goto-line 3) - (if (re-search-forward "[ \t]+Next change exits" (point-at-eol) t) - (replace-match "")) - (when flag - (end-of-line 1) - (move-to-column (- (window-width) 19) t) - (insert (org-add-props " Next change exits" nil 'face 'org-warning))))) - -(defun org-set-current-tags-overlay (current prefix) - (let ((s (concat ":" (mapconcat 'identity current ":") ":"))) - (if (featurep 'xemacs) - (org-overlay-display org-tags-overlay (concat prefix s) - 'secondary-selection) - (put-text-property 0 (length s) 'face '(secondary-selection org-tag) s) - (org-overlay-display org-tags-overlay (concat prefix s))))) - -(defun org-fast-tag-selection (current inherited table) - "Fast tag selection with single keys. -CURRENT is the current list of tags in the headline, INHERITED is the -list of inherited tags, and TABLE is an alist of tags and corresponding keys, -possibly with grouping information. -If the keys are nil, a-z are automatically assigned. -Returns the new tags string, or nil to not change the current settings." - (let* ((maxlen (apply 'max (mapcar - (lambda (x) - (if (stringp (car x)) (string-width (car x)) 0)) - table))) - (buf (current-buffer)) - (buffer-tags nil) - (fwidth (+ maxlen 3 1 3)) - (ncol (/ (- (window-width) 4) fwidth)) - (i-face 'org-done) - (c-face 'org-tag) - tg cnt e c char c1 c2 ntable tbl rtn - ov-start ov-end ov-prefix - (exit-after-next org-fast-tag-selection-single-key) - groups ingroup) - (save-excursion - (beginning-of-line 1) - (if (looking-at ".*[ \t]\\(:[A-Za-z_@0-9:]+:\\)[ \t]*\\(\r\\|$\\)") - (setq ov-start (match-beginning 1) - ov-end (match-end 1) - ov-prefix "") - (setq ov-start (1- (point-at-eol)) - ov-end (1+ ov-start)) - (skip-chars-forward "^\n\r") - (setq ov-prefix - (concat - (buffer-substring (1- (point)) (point)) - (if (> (current-column) org-tags-column) - " " - (make-string (- org-tags-column (current-column)) ?\ )))))) - (org-move-overlay org-tags-overlay ov-start ov-end) - (save-window-excursion - ;; FIXME: would it be better to keep the other windows? - (delete-other-windows) - (split-window-vertically) - (switch-to-buffer-other-window (get-buffer-create " *Org tags*")) - (erase-buffer) - (org-fast-tag-insert "Inherited" inherited i-face "\n") - (org-fast-tag-insert "Current" current c-face "\n\n") - (org-fast-tag-show-exit exit-after-next) - (org-set-current-tags-overlay current ov-prefix) - (setq tbl table char ?a cnt 0) - (while (setq e (pop tbl)) - (cond - ((equal e '(:startgroup)) - (push '() groups) (setq ingroup t) - (when (not (= cnt 0)) - (setq cnt 0) - (insert "\n")) - (insert "{ ")) - ((equal e '(:endgroup)) - (setq ingroup nil cnt 0) - (insert "}\n")) - (t - (setq tg (car e) c2 nil) - (if (cdr e) - (setq c (cdr e)) - ;; automatically assign a character. - (setq c1 (string-to-char - (downcase (substring - tg (if (= (string-to-char tg) ?@) 1 0))))) - (if (or (rassoc c1 ntable) (rassoc c1 table)) - (while (or (rassoc char ntable) (rassoc char table)) - (setq char (1+ char))) - (setq c2 c1)) - (setq c (or c2 char))) - (if ingroup (push tg (car groups))) - (setq tg (org-add-props tg nil 'face - (cond - ((member tg current) c-face) - ((member tg inherited) i-face) - (t nil)))) - (if (and (= cnt 0) (not ingroup)) (insert " ")) - (insert "[" c "] " tg (make-string - (- fwidth 4 (length tg)) ?\ )) - (push (cons tg c) ntable) - (when (= (setq cnt (1+ cnt)) ncol) - (insert "\n") - (if ingroup (insert " ")) - (setq cnt 0))))) - (setq ntable (nreverse ntable)) - (insert "\n") - (goto-char (point-min)) - (if (fboundp 'fit-window-to-buffer) (fit-window-to-buffer)) - (setq rtn - (catch 'exit - (while t - (message "[a-z..]:Toggle [SPC]:clear [RET]:accept [TAB]:free [C-c]: multi%s" - (if groups " [!] no groups" "")) - (setq c (let ((inhibit-quit t)) (read-char-exclusive))) - (cond - ((= c ?\r) (throw 'exit t)) - ((= c ?!) - (setq groups nil) - (goto-char (point-min)) - (while (re-search-forward "[{}]" nil t) (replace-match " "))) - ((= c ?\C-c) - (org-fast-tag-show-exit - (setq exit-after-next (not exit-after-next)))) - ((or (= c ?\C-g) - (and (= c ?q) (not (rassoc c ntable)))) - (org-detach-overlay org-tags-overlay) - (setq quit-flag t)) - ((= c ?\ ) - (setq current nil) - (if exit-after-next (setq exit-after-next 'now))) - ((= c ?\t) - (condition-case nil - (setq tg (completing-read - "Tag: " - (or buffer-tags - (with-current-buffer buf - (org-get-buffer-tags))))) - (quit (setq tg ""))) - (when (string-match "\\S-" tg) - (add-to-list 'buffer-tags (list tg)) - (if (member tg current) - (setq current (delete tg current)) - (push tg current))) - (if exit-after-next (setq exit-after-next 'now))) - ((setq e (rassoc c ntable) tg (car e)) - (if (member tg current) - (setq current (delete tg current)) - (loop for g in groups do - (if (member tg g) - (mapcar (lambda (x) - (setq current (delete x current))) - g))) - (push tg current)) - (if exit-after-next (setq exit-after-next 'now)))) - - ;; Create a sorted list - (setq current - (sort current - (lambda (a b) - (assoc b (cdr (memq (assoc a ntable) ntable)))))) - (if (eq exit-after-next 'now) (throw 'exit t)) - (goto-char (point-min)) - (beginning-of-line 2) - (delete-region (point) (point-at-eol)) - (org-fast-tag-insert "Current" current c-face) - (org-set-current-tags-overlay current ov-prefix) - (while (re-search-forward "\\[.\\] \\([a-zA-Z0-9_@]+\\)" nil t) - (setq tg (match-string 1)) - (add-text-properties (match-beginning 1) (match-end 1) - (list 'face - (cond - ((member tg current) c-face) - ((member tg inherited) i-face) - (t nil))))) - (goto-char (point-min))))) - (org-detach-overlay org-tags-overlay) - (if rtn - (mapconcat 'identity current ":") - nil)))) - -(defun org-get-tags () - "Get the TAGS string in the current headline." - (unless (org-on-heading-p t) - (error "Not on a heading")) - (save-excursion - (beginning-of-line 1) - (if (looking-at ".*[ \t]\\(:[A-Za-z_@0-9:]+:\\)[ \t]*\\(\r\\|$\\)") - (org-match-string-no-properties 1) - ""))) - -(defun org-get-buffer-tags () - "Get a table of all tags used in the buffer, for completion." - (let (tags) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward "[ \t]:\\([A-Za-z_@0-9:]+\\):[ \t\r\n]" nil t) - (mapc (lambda (x) (add-to-list 'tags x)) - (org-split-string (org-match-string-no-properties 1) ":")))) - (mapcar 'list tags))) - -;;;; Link Stuff - -(defvar org-create-file-search-functions nil - "List of functions to construct the right search string for a file link. -These functions are called in turn with point at the location to -which the link should point. - -A function in the hook should first test if it would like to -handle this file type, for example by checking the major-mode or -the file extension. If it decides not to handle this file, it -should just return nil to give other functions a chance. If it -does handle the file, it must return the search string to be used -when following the link. The search string will be part of the -file link, given after a double colon, and `org-open-at-point' -will automatically search for it. If special measures must be -taken to make the search successful, another function should be -added to the companion hook `org-execute-file-search-functions', -which see. - -A function in this hook may also use `setq' to set the variable -`description' to provide a suggestion for the descriptive text to -be used for this link when it gets inserted into an Org-mode -buffer with \\[org-insert-link].") - -(defvar org-execute-file-search-functions nil - "List of functions to execute a file search triggered by a link. - -Functions added to this hook must accept a single argument, the -search string that was part of the file link, the part after the -double colon. The function must first check if it would like to -handle this search, for example by checking the major-mode or the -file extension. If it decides not to handle this search, it -should just return nil to give other functions a chance. If it -does handle the search, it must return a non-nil value to keep -other functions from trying. - -Each function can access the current prefix argument through the -variable `current-prefix-argument'. Note that a single prefix is -used to force opening a link in Emacs, so it may be good to only -use a numeric or double prefix to guide the search function. - -In case this is needed, a function in this hook can also restore -the window configuration before `org-open-at-point' was called using: - - (set-window-configuration org-window-config-before-follow-link)") - -(defun org-find-file-at-mouse (ev) - "Open file link or URL at mouse." - (interactive "e") - (mouse-set-point ev) - (org-open-at-point 'in-emacs)) - -(defun org-open-at-mouse (ev) - "Open file link or URL at mouse." - (interactive "e") - (mouse-set-point ev) - (org-open-at-point)) - -(defvar org-window-config-before-follow-link nil - "The window configuration before following a link. -This is saved in case the need arises to restore it.") - -;; FIXME: IN-EMACS is used for many purposes, maybe rename this argument??? -(defun org-open-at-point (&optional in-emacs) - "Open link at or after point. -If there is no link at point, this function will search forward up to -the end of the current subtree. -Normally, files will be opened by an appropriate application. If the -optional argument IN-EMACS is non-nil, Emacs will visit the file." - (interactive "P") - (setq org-window-config-before-follow-link (current-window-configuration)) - (org-remove-occur-highlights nil nil t) - (if (org-at-timestamp-p t) - (org-follow-timestamp-link) - (let (type path link line search (pos (point))) - (catch 'match - (save-excursion - (skip-chars-forward "^]\n\r") - (when (and (re-search-backward "\\[\\[" nil t) - (looking-at org-bracket-link-regexp) - (<= (match-beginning 0) pos) - (>= (match-end 0) pos)) - (setq link (org-link-unescape (org-match-string-no-properties 1))) - (while (string-match " *\n *" link) - (setq link (replace-match " " t t link))) - (setq link (org-link-expand-abbrev link)) - (if (string-match org-link-re-with-space2 link) - (setq type (match-string 1 link) - path (match-string 2 link)) - (setq type "thisfile" - path link)) - (throw 'match t))) - - (when (get-text-property (point) 'org-linked-text) - (setq type "thisfile" - pos (if (get-text-property (1+ (point)) 'org-linked-text) - (1+ (point)) (point)) - path (buffer-substring - (previous-single-property-change pos 'org-linked-text) - (next-single-property-change pos 'org-linked-text))) - (throw 'match t)) - - (save-excursion - (skip-chars-backward (concat "^[]" org-non-link-chars " ")) - (if (equal (char-before) ?<) (backward-char 1)) - (when (or (looking-at org-angle-link-re) - (looking-at org-plain-link-re) - (and (or (re-search-forward org-angle-link-re (point-at-eol) t) - (re-search-forward org-plain-link-re (point-at-eol) t)) - (<= (match-beginning 0) pos) - (>= (match-end 0) pos))) - (setq type (match-string 1) - path (match-string 2)) - (throw 'match t))) - (save-excursion - (skip-chars-backward "^ \t\n\r") - (when (looking-at "\\(:[A-Za-z_@0-9:]+\\):[ \t\r\n]") - (setq type "tags" - path (match-string 1)) - (while (string-match ":" path) - (setq path (replace-match "+" t t path))) - (throw 'match t))) - (save-excursion - (skip-chars-backward "a-zA-Z_") - (when (and (memq 'camel org-activate-links) - (looking-at org-camel-regexp)) - (setq type "camel" path (match-string 0)) - (if (equal (char-before) ?*) - (setq path (concat "*" path)))) - (throw 'match t))) - (unless path - (error "No link found")) - ;; Remove any trailing spaces in path - (if (string-match " +\\'" path) - (setq path (replace-match "" t t path))) - - (cond - - ((equal type "mailto") - (let ((cmd (car org-link-mailto-program)) - (args (cdr org-link-mailto-program)) args1 - (address path) (subject "") a) - (if (string-match "\\(.*\\)::\\(.*\\)" path) - (setq address (match-string 1 path) - subject (org-link-escape (match-string 2 path)))) - (while args - (cond - ((not (stringp (car args))) (push (pop args) args1)) - (t (setq a (pop args)) - (if (string-match "%a" a) - (setq a (replace-match address t t a))) - (if (string-match "%s" a) - (setq a (replace-match subject t t a))) - (push a args1)))) - (apply cmd (nreverse args1)))) - - ((member type '("http" "https" "ftp" "news")) - (browse-url (concat type ":" path))) - - ((string= type "tags") - (org-tags-view in-emacs path)) - ((or (string= type "camel") - (string= type "thisfile")) - (if in-emacs - (switch-to-buffer-other-window - (org-get-buffer-for-internal-link (current-buffer))) - (org-mark-ring-push)) - (org-link-search - path - (cond ((equal in-emacs '(4)) 'occur) - ((equal in-emacs '(16)) 'org-occur) - (t nil)))) - - ((string= type "file") - (if (string-match "::\\([0-9]+\\)\\'" path) - (setq line (string-to-number (match-string 1 path)) - path (substring path 0 (match-beginning 0))) - (if (string-match "::\\(.+\\)\\'" path) - (setq search (match-string 1 path) - path (substring path 0 (match-beginning 0))))) - (org-open-file path in-emacs line search)) - - ((string= type "news") - (org-follow-gnus-link path)) - - ((string= type "bbdb") - (org-follow-bbdb-link path)) - - ((string= type "info") - (org-follow-info-link path)) - - ((string= type "gnus") - (let (group article) - (if (not (string-match "\\`\\([^#]+\\)\\(#\\(.*\\)\\)?" path)) - (error "Error in Gnus link")) - (setq group (match-string 1 path) - article (match-string 3 path)) - (org-follow-gnus-link group article))) - - ((string= type "vm") - (let (folder article) - (if (not (string-match "\\`\\([^#]+\\)\\(#\\(.*\\)\\)?" path)) - (error "Error in VM link")) - (setq folder (match-string 1 path) - article (match-string 3 path)) - ;; in-emacs is the prefix arg, will be interpreted as read-only - (org-follow-vm-link folder article in-emacs))) - - ((string= type "wl") - (let (folder article) - (if (not (string-match "\\`\\([^#]+\\)\\(#\\(.*\\)\\)?" path)) - (error "Error in Wanderlust link")) - (setq folder (match-string 1 path) - article (match-string 3 path)) - (org-follow-wl-link folder article))) - - ((string= type "mhe") - (let (folder article) - (if (not (string-match "\\`\\([^#]+\\)\\(#\\(.*\\)\\)?" path)) - (error "Error in MHE link")) - (setq folder (match-string 1 path) - article (match-string 3 path)) - (org-follow-mhe-link folder article))) - - ((string= type "rmail") - (let (folder article) - (if (not (string-match "\\`\\([^#]+\\)\\(#\\(.*\\)\\)?" path)) - (error "Error in RMAIL link")) - (setq folder (match-string 1 path) - article (match-string 3 path)) - (org-follow-rmail-link folder article))) - - ((string= type "shell") - (let ((cmd path)) - (while (string-match "@{" cmd) ; FIXME: not needed for [[]] links - (setq cmd (replace-match "<" t t cmd))) - (while (string-match "@}" cmd) ; FIXME: not needed for [[]] links - (setq cmd (replace-match ">" t t cmd))) - (if (or (not org-confirm-shell-link-function) - (funcall org-confirm-shell-link-function - (format "Execute \"%s\" in shell? " - (org-add-props cmd nil - 'face 'org-warning)))) - (progn - (message "Executing %s" cmd) - (shell-command cmd)) - (error "Abort")))) - - ((string= type "elisp") - (let ((cmd path)) - (if (or (not org-confirm-elisp-link-function) - (funcall org-confirm-elisp-link-function - (format "Execute \"%s\" as elisp? " - (org-add-props cmd nil - 'face 'org-warning)))) - (message "%s => %s" cmd (eval (read cmd))) - (error "Abort")))) - - (t - (browse-url-at-point)))))) - -(defun org-link-expand-abbrev (link) - "Apply replacements as defined in `org-link-abbrev-alist." - (if (string-match "^\\([a-zA-Z]+\\)\\(::\\(.*\\)\\)?$" link) - (let* ((key (match-string 1 link)) - (as (or (assoc key org-link-abbrev-alist-local) - (assoc key org-link-abbrev-alist))) - (tag (and (match-end 2) (match-string 3 link))) - rpl) - (if (not as) - link - (setq rpl (cdr as)) - (cond - ((symbolp rpl) (funcall rpl tag)) - ((string-match "%s" rpl) (replace-match (or tag "") t t rpl)) - (t (concat rpl tag))))) - link)) - -(defun org-link-search (s &optional type) - "Search for a link search option. -When S is a CamelCaseWord, search for a target, or for a sentence containing -the words. If S is surrounded by forward slashes, it is interpreted as a -regular expression. In org-mode files, this will create an `org-occur' -sparse tree. In ordinary files, `occur' will be used to list matches. -If the current buffer is in `dired-mode', grep will be used to search -in all files." - (let ((case-fold-search t) - (s0 (mapconcat 'identity (org-split-string s "[ \t\r\n]+") " ")) - (pos (point)) - (pre "") (post "") - words re0 re1 re2 re3 re4 re5 re2a reall camel) - (cond - ;; First check if there are any special - ((run-hook-with-args-until-success 'org-execute-file-search-functions s)) - ;; Now try the builtin stuff - ((save-excursion - (goto-char (point-min)) - (and - (re-search-forward - (concat "<<" (regexp-quote s0) ">>") nil t) - (setq pos (match-beginning 0)))) - ;; There is an exact target for this - (goto-char pos)) - ((string-match "^/\\(.*\\)/$" s) - ;; A regular expression - (cond - ((org-mode-p) - (org-occur (match-string 1 s))) - ;;((eq major-mode 'dired-mode) - ;; (grep (concat "grep -n -e '" (match-string 1 s) "' *"))) - (t (org-do-occur (match-string 1 s))))) - ((or (setq camel (string-match (concat "^" org-camel-regexp "$") s)) - t) - ;; A camel or a normal search string - (when (equal (string-to-char s) ?*) - ;; Anchor on headlines, post may include tags. - (setq pre "^\\*+[ \t]*\\(?:\\sw+\\)?[ \t]*" - post "[ \t]*\\(?:[ \t]+:[a-zA-Z_@0-9:+]:[ \t]*\\)?$" - s (substring s 1))) - (remove-text-properties - 0 (length s) - '(face nil mouse-face nil keymap nil fontified nil) s) - ;; Make a series of regular expressions to find a match - (setq words - (if camel - (org-camel-to-words s) - (org-split-string s "[ \n\r\t]+")) - re0 (concat "\\(<<" (regexp-quote s0) ">>\\)") - re2 (concat "[ \t\r\n]\\(" (mapconcat 'downcase words "[ \t]+") "\\)[ \t\r\n]") - re2a (concat "[ \t\r\n]\\(" (mapconcat 'downcase words "[ \t\r\n]+") "\\)[ \t\r\n]") - re4 (concat "[^a-zA-Z_]\\(" (mapconcat 'downcase words "[^a-zA-Z_\r\n]+") "\\)[^a-zA-Z_]") - re1 (concat pre re2 post) - re3 (concat pre re4 post) - re5 (concat pre ".*" re4) - re2 (concat pre re2) - re2a (concat pre re2a) - re4 (concat pre re4) - reall (concat "\\(" re0 "\\)\\|\\(" re1 "\\)\\|\\(" re2 - "\\)\\|\\(" re3 "\\)\\|\\(" re4 "\\)\\|\\(" - re5 "\\)" - )) - (cond - ((eq type 'org-occur) (org-occur reall)) - ((eq type 'occur) (org-do-occur (downcase reall) 'cleanup)) - (t (goto-char (point-min)) - (if (or (org-search-not-link re0 nil t) - (org-search-not-link re1 nil t) - (org-search-not-link re2 nil t) - (org-search-not-link re2a nil t) - (org-search-not-link re3 nil t) - (org-search-not-link re4 nil t) - (org-search-not-link re5 nil t) - ) - (goto-char (match-beginning 1)) - (goto-char pos) - (error "No match"))))) - (t - ;; Normal string-search - (goto-char (point-min)) - (if (search-forward s nil t) - (goto-char (match-beginning 0)) - (error "No match")))) - (and (org-mode-p) (org-show-context 'link-search)))) - -(defun org-search-not-link (&rest args) - "Execute `re-search-forward', but only accept matches that are not a link." - (catch 'exit - (let (p1) - (while (apply 're-search-forward args) - (setq p1 (point)) - (if (not (save-match-data - (and (re-search-backward "\\[\\[" nil t) - (looking-at org-bracket-link-regexp) - (<= (match-beginning 0) p1) - (>= (match-end 0) p1)))) - (progn (goto-char (match-end 0)) - (throw 'exit (point))) - (goto-char (match-end 0))))))) - -(defun org-get-buffer-for-internal-link (buffer) - "Return a buffer to be used for displaying the link target of internal links." - (cond - ((not org-display-internal-link-with-indirect-buffer) - buffer) - ((string-match "(Clone)$" (buffer-name buffer)) - (message "Buffer is already a clone, not making another one") - ;; we also do not modify visibility in this case - buffer) - (t ; make a new indirect buffer for displaying the link - (let* ((bn (buffer-name buffer)) - (ibn (concat bn "(Clone)")) - (ib (or (get-buffer ibn) (make-indirect-buffer buffer ibn 'clone)))) - (with-current-buffer ib (org-overview)) - ib)))) - -(defun org-do-occur (regexp &optional cleanup) - "Call the Emacs command `occur'. -If CLEANUP is non-nil, remove the printout of the regular expression -in the *Occur* buffer. This is useful if the regex is long and not useful -to read." - (occur regexp) - (when cleanup - (let ((cwin (selected-window)) win beg end) - (when (setq win (get-buffer-window "*Occur*")) - (select-window win)) - (goto-char (point-min)) - (when (re-search-forward "match[a-z]+" nil t) - (setq beg (match-end 0)) - (if (re-search-forward "^[ \t]*[0-9]+" nil t) - (setq end (1- (match-beginning 0))))) - (and beg end (let ((buffer-read-only)) (delete-region beg end))) - (goto-char (point-min)) - (select-window cwin)))) - -(defvar org-mark-ring nil - "Mark ring for positions before jumps in Org-mode.") -(defvar org-mark-ring-last-goto nil - "Last position in the mark ring used to go back.") -;; Fill and close the ring -(setq org-mark-ring nil org-mark-ring-last-goto nil) ;; in case file is reloaded -(loop for i from 1 to org-mark-ring-length do - (push (make-marker) org-mark-ring)) -(setcdr (nthcdr (1- org-mark-ring-length) org-mark-ring) - org-mark-ring) - -(defun org-mark-ring-push (&optional pos buffer) - "Put the current position or POS into the mark ring and rotate it." - (interactive) - (setq pos (or pos (point))) - (setq org-mark-ring (nthcdr (1- org-mark-ring-length) org-mark-ring)) - (move-marker (car org-mark-ring) - (or pos (point)) - (or buffer (current-buffer))) - (message - (substitute-command-keys - "Position saved to mark ring, go back with \\[org-mark-ring-goto]."))) - -(defun org-mark-ring-goto (&optional n) - "Jump to the previous position in the mark ring. -With prefix arg N, jump back that many stored positions. When -called several times in succession, walk through the entire ring. -Org-mode commands jumping to a different position in the current file, -or to another Org-mode file, automatically push the old position -onto the ring." - (interactive "p") - (let (p m) - (if (eq last-command this-command) - (setq p (nthcdr n (or org-mark-ring-last-goto org-mark-ring))) - (setq p org-mark-ring)) - (setq org-mark-ring-last-goto p) - (setq m (car p)) - (switch-to-buffer (marker-buffer m)) - (goto-char m) - (if (or (org-invisible-p) (org-invisible-p2)) (org-show-context 'mark-goto)))) - -(defun org-camel-to-words (s) - "Split \"CamelCaseWords\" to (\"Camel\" \"Case\" \"Words\")." - (let ((case-fold-search nil) - words) - (while (string-match "[a-z][A-Z]" s) - (push (substring s 0 (1+ (match-beginning 0))) words) - (setq s (substring s (1+ (match-beginning 0))))) - (nreverse (cons s words)))) - -(defun org-remove-angle-brackets (s) - (if (equal (substring s 0 1) "<") (setq s (substring s 1))) - (if (equal (substring s -1) ">") (setq s (substring s 0 -1))) - s) -(defun org-add-angle-brackets (s) - (if (equal (substring s 0 1) "<") nil (setq s (concat "<" s))) - (if (equal (substring s -1) ">") nil (setq s (concat s ">"))) - s) - -(defun org-follow-timestamp-link () - (cond - ((org-at-date-range-p t) - (let ((org-agenda-start-on-weekday) - (t1 (match-string 1)) - (t2 (match-string 2))) - (setq t1 (time-to-days (org-time-string-to-time t1)) - t2 (time-to-days (org-time-string-to-time t2))) - (org-agenda-list nil t1 (1+ (- t2 t1))))) - ((org-at-timestamp-p t) - (org-agenda-list nil (time-to-days (org-time-string-to-time - (substring (match-string 1) 0 10))) - 1)) - (t (error "This should not happen")))) - - -(defun org-follow-bbdb-link (name) - "Follow a BBDB link to NAME." - (require 'bbdb) - (let ((inhibit-redisplay t) - (bbdb-electric-p nil)) - (catch 'exit - ;; Exact match on name - (bbdb-name (concat "\\`" name "\\'") nil) - (if (< 0 (buffer-size (get-buffer "*BBDB*"))) (throw 'exit nil)) - ;; Exact match on name - (bbdb-company (concat "\\`" name "\\'") nil) - (if (< 0 (buffer-size (get-buffer "*BBDB*"))) (throw 'exit nil)) - ;; Partial match on name - (bbdb-name name nil) - (if (< 0 (buffer-size (get-buffer "*BBDB*"))) (throw 'exit nil)) - ;; Partial match on company - (bbdb-company name nil) - (if (< 0 (buffer-size (get-buffer "*BBDB*"))) (throw 'exit nil)) - ;; General match including network address and notes - (bbdb name nil) - (when (= 0 (buffer-size (get-buffer "*BBDB*"))) - (delete-window (get-buffer-window "*BBDB*")) - (error "No matching BBDB record"))))) - - -(defun org-follow-info-link (name) - "Follow an info file & node link to NAME." - (if (or (string-match "\\(.*\\)::?\\(.*\\)" name) - (string-match "\\(.*\\)" name)) - (progn - (require 'info) - (if (match-string 2 name) ; If there isn't a node, choose "Top" - (Info-find-node (match-string 1 name) (match-string 2 name)) - (Info-find-node (match-string 1 name) "Top"))) - (message (concat "Could not open: " name)))) - -(defun org-follow-gnus-link (&optional group article) - "Follow a Gnus link to GROUP and ARTICLE." - (require 'gnus) - (funcall (cdr (assq 'gnus org-link-frame-setup))) - (if gnus-other-frame-object (select-frame gnus-other-frame-object)) - (if group (gnus-fetch-group group)) - (if article - (or (gnus-summary-goto-article article nil 'force) - (if (fboundp 'gnus-summary-insert-cached-articles) - (progn - (gnus-summary-insert-cached-articles) - (gnus-summary-goto-article article nil 'force)) - (message "Message could not be found."))))) - -(defun org-follow-vm-link (&optional folder article readonly) - "Follow a VM link to FOLDER and ARTICLE." - (require 'vm) - (setq article (org-add-angle-brackets article)) - (if (string-match "^//\\([a-zA-Z]+@\\)?\\([^:]+\\):\\(.*\\)" folder) - ;; ange-ftp or efs or tramp access - (let ((user (or (match-string 1 folder) (user-login-name))) - (host (match-string 2 folder)) - (file (match-string 3 folder))) - (cond - ((featurep 'tramp) - ;; use tramp to access the file - (if (featurep 'xemacs) - (setq folder (format "[%s@%s]%s" user host file)) - (setq folder (format "/%s@%s:%s" user host file)))) - (t - ;; use ange-ftp or efs - (require (if (featurep 'xemacs) 'efs 'ange-ftp)) - (setq folder (format "/%s@%s:%s" user host file)))))) - (when folder - (funcall (cdr (assq 'vm org-link-frame-setup)) folder readonly) - (sit-for 0.1) - (when article - (vm-select-folder-buffer) - (widen) - (let ((case-fold-search t)) - (goto-char (point-min)) - (if (not (re-search-forward - (concat "^" "message-id: *" (regexp-quote article)))) - (error "Could not find the specified message in this folder")) - (vm-isearch-update) - (vm-isearch-narrow) - (vm-beginning-of-message) - (vm-summarize))))) - -(defun org-follow-wl-link (folder article) - "Follow a Wanderlust link to FOLDER and ARTICLE." - (setq article (org-add-angle-brackets article)) - (wl-summary-goto-folder-subr folder 'no-sync t nil t nil nil) - (if article (wl-summary-jump-to-msg-by-message-id article)) - (wl-summary-redisplay)) - -(defun org-follow-rmail-link (folder article) - "Follow an RMAIL link to FOLDER and ARTICLE." - (setq article (org-add-angle-brackets article)) - (let (message-number) - (save-excursion - (save-window-excursion - (rmail (if (string= folder "RMAIL") rmail-file-name folder)) - (setq message-number - (save-restriction - (widen) - (goto-char (point-max)) - (if (re-search-backward - (concat "^Message-ID:\\s-+" (regexp-quote - (or article ""))) - nil t) - (rmail-what-message)))))) - (if message-number - (progn - (rmail (if (string= folder "RMAIL") rmail-file-name folder)) - (rmail-show-message message-number) - message-number) - (error "Message not found")))) - -;;; mh-e integration based on planner-mode -(defun org-mhe-get-message-real-folder () - "Return the name of the current message real folder, so if you use -sequences, it will now work." - (save-excursion - (let* ((folder - (if (equal major-mode 'mh-folder-mode) - mh-current-folder - ;; Refer to the show buffer - mh-show-folder-buffer)) - (end-index - (if (boundp 'mh-index-folder) - (min (length mh-index-folder) (length folder)))) - ) - ;; a simple test on mh-index-data does not work, because - ;; mh-index-data is always nil in a show buffer. - (if (and (boundp 'mh-index-folder) - (string= mh-index-folder (substring folder 0 end-index))) - (if (equal major-mode 'mh-show-mode) - (save-window-excursion - (when (buffer-live-p (get-buffer folder)) - (progn - (pop-to-buffer folder) - (org-mhe-get-message-folder-from-index) - ) - )) - (org-mhe-get-message-folder-from-index) - ) - folder - ) - ))) - -(defun org-mhe-get-message-folder-from-index () - "Returns the name of the message folder in a index folder buffer." - (save-excursion - (mh-index-previous-folder) - (re-search-forward "^\\(+.*\\)$" nil t) - (message (match-string 1)))) - -(defun org-mhe-get-message-folder () - "Return the name of the current message folder. Be careful if you -use sequences." - (save-excursion - (if (equal major-mode 'mh-folder-mode) - mh-current-folder - ;; Refer to the show buffer - mh-show-folder-buffer))) - -(defun org-mhe-get-message-num () - "Return the number of the current message. Be careful if you -use sequences." - (save-excursion - (if (equal major-mode 'mh-folder-mode) - (mh-get-msg-num nil) - ;; Refer to the show buffer - (mh-show-buffer-message-number)))) - -(defun org-mhe-get-header (header) - "Return a header of the message in folder mode. This will create a -show buffer for the corresponding message. If you have a more clever -idea..." - (let* ((folder (org-mhe-get-message-folder)) - (num (org-mhe-get-message-num)) - (buffer (get-buffer-create (concat "show-" folder))) - (header-field)) - (with-current-buffer buffer - (mh-display-msg num folder) - (if (equal major-mode 'mh-folder-mode) - (mh-header-display) - (mh-show-header-display)) - (set-buffer buffer) - (setq header-field (mh-get-header-field header)) - (if (equal major-mode 'mh-folder-mode) - (mh-show) - (mh-show-show)) - header-field))) - -(defun org-follow-mhe-link (folder article) - "Follow an MHE link to FOLDER and ARTICLE. -If ARTICLE is nil FOLDER is shown. If the configuration variable -`org-mhe-search-all-folders' is t and `mh-searcher' is pick, -ARTICLE is searched in all folders. Indexed searches (swish++, -namazu, and others supported by MH-E) will always search in all -folders." - (require 'mh-e) - (require 'mh-search) - (require 'mh-utils) - (mh-find-path) - (if (not article) - (mh-visit-folder (mh-normalize-folder-name folder)) - (setq article (org-add-angle-brackets article)) - (mh-search-choose) - (if (equal mh-searcher 'pick) - (progn - (mh-search folder (list "--message-id" article)) - (when (and org-mhe-search-all-folders - (not (org-mhe-get-message-real-folder))) - (kill-this-buffer) - (mh-search "+" (list "--message-id" article)))) - (mh-search "+" article)) - (if (org-mhe-get-message-real-folder) - (mh-show-msg 1) - (kill-this-buffer) - (error "Message not found")))) - -;;; BibTeX links - -;; Use the custom search meachnism to construct and use search strings for -;; file links to BibTeX database entries. - -(defun org-create-file-search-in-bibtex () - "Create the search string and description for a BibTeX database entry." - (when (eq major-mode 'bibtex-mode) - ;; yes, we want to construct this search string. - ;; Make a good description for this entry, using names, year and the title - ;; Put it into the `description' variable which is dynamically scoped. - (let ((bibtex-autokey-names 1) - (bibtex-autokey-names-stretch 1) - (bibtex-autokey-name-case-convert-function 'identity) - (bibtex-autokey-name-separator " & ") - (bibtex-autokey-additional-names " et al.") - (bibtex-autokey-year-length 4) - (bibtex-autokey-name-year-separator " ") - (bibtex-autokey-titlewords 3) - (bibtex-autokey-titleword-separator " ") - (bibtex-autokey-titleword-case-convert-function 'identity) - (bibtex-autokey-titleword-length 'infty) - (bibtex-autokey-year-title-separator ": ")) - (setq description (bibtex-generate-autokey))) - ;; Now parse the entry, get the key and return it. - (save-excursion - (bibtex-beginning-of-entry) - (cdr (assoc "=key=" (bibtex-parse-entry)))))) - -(defun org-execute-file-search-in-bibtex (s) - "Find the link search string S as a key for a database entry." - (when (eq major-mode 'bibtex-mode) - ;; Yes, we want to do the search in this file. - ;; We construct a regexp that searches for "@entrytype{" followed by the key - (goto-char (point-min)) - (and (re-search-forward (concat "@[a-zA-Z]+[ \t\n]*{[ \t\n]*" - (regexp-quote s) "[ \t\n]*,") nil t) - (goto-char (match-beginning 0))) - (if (and (match-beginning 0) (equal current-prefix-arg '(16))) - ;; Use double prefix to indicate that any web link should be browsed - (let ((b (current-buffer)) (p (point))) - ;; Restore the window configuration because we just use the web link - (set-window-configuration org-window-config-before-follow-link) - (save-excursion (set-buffer b) (goto-char p) - (bibtex-url))) - (recenter 0)) ; Move entry start to beginning of window - ;; return t to indicate that the search is done. - t)) - -;; Finally add the functions to the right hooks. -(add-hook 'org-create-file-search-functions 'org-create-file-search-in-bibtex) -(add-hook 'org-execute-file-search-functions 'org-execute-file-search-in-bibtex) - -;; end of Bibtex link setup - -(defun org-upgrade-old-links (&optional query-description) - "Transfer old <...> style links to new [[...]] style links. -With arg query-description, ask at each match for a description text to use -for this link." - (interactive (list (y-or-n-p "Would you like to be queried for a description at each link?"))) - (save-excursion - (goto-char (point-min)) - (let ((re (concat "\\([^[]\\)<\\(" - "\\(" (mapconcat 'identity org-link-types "\\|") - "\\):" - "[^" org-non-link-chars "]+\\)>")) - l1 l2 (cnt 0)) - (while (re-search-forward re nil t) - (setq cnt (1+ cnt) - l1 (org-match-string-no-properties 2) - l2 (save-match-data (org-link-escape l1))) - (when query-description (setq l1 (read-string "Desc: " l1))) - (if (equal l1 l2) - (replace-match (concat (match-string 1) "[[" l1 "]]") t t) - (replace-match (concat (match-string 1) "[[" l2 "][" l1 "]]") t t))) - (message "%d matches have beed treated" cnt)))) - -(defun org-open-file (path &optional in-emacs line search) - "Open the file at PATH. -First, this expands any special file name abbreviations. Then the -configuration variable `org-file-apps' is checked if it contains an -entry for this file type, and if yes, the corresponding command is launched. -If no application is found, Emacs simply visits the file. -With optional argument IN-EMACS, Emacs will visit the file. -Optional LINE specifies a line to go to, optional SEARCH a string to -search for. If LINE or SEARCH is given, the file will always be -opened in Emacs. -If the file does not exist, an error is thrown." - (setq in-emacs (or in-emacs line search)) - (let* ((file (if (equal path "") - buffer-file-name - (substitute-in-file-name (expand-file-name path)))) - (apps (append org-file-apps (org-default-apps))) - (remp (and (assq 'remote apps) (org-file-remote-p file))) - (dirp (if remp nil (file-directory-p file))) - (dfile (downcase file)) - (old-buffer (current-buffer)) - (old-pos (point)) - (old-mode major-mode) - ext cmd) - (if (string-match "^.*\\.\\([a-zA-Z0-9]+\\.gz\\)$" dfile) - (setq ext (match-string 1 dfile)) - (if (string-match "^.*\\.\\([a-zA-Z0-9]+\\)$" dfile) - (setq ext (match-string 1 dfile)))) - (if in-emacs - (setq cmd 'emacs) - (setq cmd (or (and remp (cdr (assoc 'remote apps))) - (and dirp (cdr (assoc 'directory apps))) - (cdr (assoc ext apps)) - (cdr (assoc t apps))))) - (when (eq cmd 'mailcap) - (require 'mailcap) - (mailcap-parse-mailcaps) - (let* ((mime-type (mailcap-extension-to-mime (or ext ""))) - (command (mailcap-mime-info mime-type))) - (if (stringp command) - (setq cmd command) - (setq cmd 'emacs)))) - (if (and (not (eq cmd 'emacs)) ; Emacs has not problems with non-ex files - (not (file-exists-p file)) - (not org-open-non-existing-files)) - (error "No such file: %s" file)) - (cond - ((and (stringp cmd) (not (string-match "^\\s-*$" cmd))) - ;; Remove quotes around the file name - we'll use shell-quote-argument. - (if (string-match "['\"]%s['\"]" cmd) - (setq cmd (replace-match "%s" t t cmd))) - (setq cmd (format cmd (shell-quote-argument file))) - (save-window-excursion - (shell-command (concat cmd " &")))) - ((or (stringp cmd) - (eq cmd 'emacs)) -; (unless (equal (file-truename file) (file-truename (or buffer-file-name ""))) -; (funcall (cdr (assq 'file org-link-frame-setup)) file)) - (funcall (cdr (assq 'file org-link-frame-setup)) file) - (if line (goto-line line) - (if search (org-link-search search)))) - ((consp cmd) - (eval cmd)) - (t (funcall (cdr (assq 'file org-link-frame-setup)) file))) - (and (org-mode-p) (eq old-mode 'org-mode) - (or (not (equal old-buffer (current-buffer))) - (not (equal old-pos (point)))) - (org-mark-ring-push old-pos old-buffer)))) - -(defun org-default-apps () - "Return the default applications for this operating system." - (cond - ((eq system-type 'darwin) - org-file-apps-defaults-macosx) - ((eq system-type 'windows-nt) - org-file-apps-defaults-windowsnt) - (t org-file-apps-defaults-gnu))) - -(defun org-expand-file-name (path) - "Replace special path abbreviations and expand the file name." - (expand-file-name path)) - -(defvar ange-ftp-name-format) ; to silence the XEmacs compiler. -(defun org-file-remote-p (file) - "Test whether FILE specifies a location on a remote system. -Return non-nil if the location is indeed remote. - -For example, the filename \"/user@host:/foo\" specifies a location -on the system \"/user@host:\"." - (cond ((fboundp 'file-remote-p) - (file-remote-p file)) - ((fboundp 'tramp-handle-file-remote-p) - (tramp-handle-file-remote-p file)) - ((and (boundp 'ange-ftp-name-format) - (string-match (car ange-ftp-name-format) file)) - t) - (t nil))) - -(defvar org-insert-link-history nil - "Minibuffer history for links inserted with `org-insert-link'.") - -(defvar org-stored-links nil - "Contains the links stored with `org-store-link'.") - -;;;###autoload -(defun org-store-link (arg) - "\\Store an org-link to the current location. -This link can later be inserted into an org-buffer with -\\[org-insert-link]. -For some link types, a prefix arg is interpreted: -For links to usenet articles, arg negates `org-usenet-links-prefer-google'. -For file links, arg negates `org-context-in-file-links'." - (interactive "P") - (let (link cpltxt desc description search txt (pos (point))) - (cond - - ((eq major-mode 'bbdb-mode) - (setq cpltxt (concat - "bbdb:" - (or (bbdb-record-name (bbdb-current-record)) - (bbdb-record-company (bbdb-current-record)))) - link (org-make-link cpltxt))) - - ((eq major-mode 'Info-mode) - (setq link (org-make-link "info:" - (file-name-nondirectory Info-current-file) - ":" Info-current-node)) - (setq cpltxt (concat (file-name-nondirectory Info-current-file) - ":" Info-current-node))) - - ((eq major-mode 'calendar-mode) - (let ((cd (calendar-cursor-to-date))) - (setq link - (format-time-string - (car org-time-stamp-formats) - (apply 'encode-time - (list 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd) - nil nil nil)))))) - - ((or (eq major-mode 'vm-summary-mode) - (eq major-mode 'vm-presentation-mode)) - (and (eq major-mode 'vm-presentation-mode) (vm-summarize)) - (vm-follow-summary-cursor) - (save-excursion - (vm-select-folder-buffer) - (let* ((message (car vm-message-pointer)) - (folder buffer-file-name) - (subject (vm-su-subject message)) - (author (vm-su-full-name message)) - (message-id (vm-su-message-id message))) - (setq message-id (org-remove-angle-brackets message-id)) - (setq folder (abbreviate-file-name folder)) - (if (string-match (concat "^" (regexp-quote vm-folder-directory)) - folder) - (setq folder (replace-match "" t t folder))) - (setq cpltxt (concat author " on: " subject)) - (setq link (org-make-link "vm:" folder "#" message-id))))) - - ((eq major-mode 'wl-summary-mode) - (let* ((msgnum (wl-summary-message-number)) - (message-id (elmo-message-field wl-summary-buffer-elmo-folder - msgnum 'message-id)) - (wl-message-entity - (if (fboundp 'elmo-message-entity) - (elmo-message-entity - wl-summary-buffer-elmo-folder msgnum) - (elmo-msgdb-overview-get-entity - msgnum (wl-summary-buffer-msgdb)))) - (author (wl-summary-line-from)) ; FIXME: correct? - (subject "???")) ; FIXME: - (setq message-id (org-remove-angle-brackets message-id)) - (setq cpltxt (concat author " on: " subject)) - (setq link (org-make-link "wl:" wl-summary-buffer-folder-name - "#" message-id)))) - - ((or (equal major-mode 'mh-folder-mode) - (equal major-mode 'mh-show-mode)) - (let ((from-header (org-mhe-get-header "From:")) - (to-header (org-mhe-get-header "To:")) - (subject (org-mhe-get-header "Subject:"))) - (setq cpltxt (concat from-header " on: " subject)) - (setq link (org-make-link "mhe:" (org-mhe-get-message-real-folder) "#" - (org-remove-angle-brackets - (org-mhe-get-header "Message-Id:")))))) - - ((eq major-mode 'rmail-mode) - (save-excursion - (save-restriction - (rmail-narrow-to-non-pruned-header) - (let ((folder buffer-file-name) - (message-id (mail-fetch-field "message-id")) - (author (mail-fetch-field "from")) - (subject (mail-fetch-field "subject"))) - (setq message-id (org-remove-angle-brackets message-id)) - (setq cpltxt (concat author " on: " subject)) - (setq link (org-make-link "rmail:" folder "#" message-id)))))) - - ((eq major-mode 'gnus-group-mode) - (let ((group (cond ((fboundp 'gnus-group-group-name) ; depending on Gnus - (gnus-group-group-name)) ; version - ((fboundp 'gnus-group-name) - (gnus-group-name)) - (t "???")))) - (setq cpltxt (concat - (if (org-xor arg org-usenet-links-prefer-google) - "http://groups.google.com/groups?group=" - "gnus:") - group) - link (org-make-link cpltxt)))) - - ((memq major-mode '(gnus-summary-mode gnus-article-mode)) - (require 'gnus-sum) ; FIXME: I don't think this is needed, actually - (and (eq major-mode 'gnus-article-mode) (gnus-article-show-summary)) - (gnus-summary-beginning-of-article) - (let* ((group (car gnus-article-current)) - (article (cdr gnus-article-current)) - (header (gnus-summary-article-header article)) - (author (mail-header-from header)) - (message-id (mail-header-id header)) - (date (mail-header-date header)) - (subject (gnus-summary-subject-string))) - (setq cpltxt (concat author " on: " subject)) - (if (org-xor arg org-usenet-links-prefer-google) - (setq link - (concat - cpltxt "\n " - (format "http://groups.google.com/groups?as_umsgid=%s" - (org-fixup-message-id-for-http message-id)))) - (setq link (org-make-link "gnus:" group - "#" (number-to-string article)))))) - - ((eq major-mode 'w3-mode) - (setq cpltxt (url-view-url t) - link (org-make-link cpltxt))) - ((eq major-mode 'w3m-mode) - (setq cpltxt (or w3m-current-title w3m-current-url) - link (org-make-link w3m-current-url))) - - ((setq search (run-hook-with-args-until-success - 'org-create-file-search-functions)) - (setq link (concat "file:" (abbreviate-file-name buffer-file-name) - "::" search)) - (setq cpltxt (or description link))) - - ((eq major-mode 'image-mode) - (setq cpltxt (concat "file:" - (abbreviate-file-name buffer-file-name)) - link (org-make-link cpltxt))) - - ((eq major-mode 'dired-mode) - ;; link to the file in the current line - (setq cpltxt (concat "file:" - (abbreviate-file-name - (expand-file-name - (dired-get-filename nil t)))) - link (org-make-link cpltxt))) - - ((and buffer-file-name (org-mode-p)) - ;; Just link to current headline - (setq cpltxt (concat "file:" - (abbreviate-file-name buffer-file-name))) - ;; Add a context search string - (when (org-xor org-context-in-file-links arg) - ;; Check if we are on a target - (if (save-excursion - (skip-chars-forward "^>\n\r") - (and (re-search-backward "<<" nil t) - (looking-at "<<\\(.*?\\)>>") - (<= (match-beginning 0) pos) - (>= (match-end 0) pos))) - (setq cpltxt (concat cpltxt "::" (match-string 1))) - (setq txt (cond - ((org-on-heading-p) nil) - ((org-region-active-p) - (buffer-substring (region-beginning) (region-end))) - (t (buffer-substring (point-at-bol) (point-at-eol))))) - (when (or (null txt) (string-match "\\S-" txt)) - (setq cpltxt - (concat cpltxt "::" - (if org-file-link-context-use-camel-case - (org-make-org-heading-camel txt) - (org-make-org-heading-search-string txt))) - desc "NONE")))) - (if (string-match "::\\'" cpltxt) - (setq cpltxt (substring cpltxt 0 -2))) - (setq link (org-make-link cpltxt))) - - (buffer-file-name - ;; Just link to this file here. - (setq cpltxt (concat "file:" - (abbreviate-file-name buffer-file-name))) - ;; Add a context string - (when (org-xor org-context-in-file-links arg) - (setq txt (if (org-region-active-p) - (buffer-substring (region-beginning) (region-end)) - (buffer-substring (point-at-bol) (point-at-eol)))) - ;; Only use search option if there is some text. - (when (string-match "\\S-" txt) - (setq cpltxt - (concat cpltxt "::" - (if org-file-link-context-use-camel-case - (org-make-org-heading-camel txt) - (org-make-org-heading-search-string txt))) - desc "NONE"))) - (setq link (org-make-link cpltxt))) - - ((interactive-p) - (error "Cannot link to a buffer which is not visiting a file")) - - (t (setq link nil))) - - (if (consp link) (setq cpltxt (car link) link (cdr link))) - (setq link (or link cpltxt) - desc (or desc cpltxt)) - (if (equal desc "NONE") (setq desc nil)) - - (if (and (interactive-p) link) - (progn - (setq org-stored-links - (cons (list cpltxt link desc) org-stored-links)) - (message "Stored: %s" (or cpltxt link))) - (org-make-link-string link desc)))) - -(defun org-make-org-heading-search-string (&optional string heading) - "Make search string for STRING or current headline." - (interactive) - (let ((s (or string (org-get-heading)))) - (unless (and string (not heading)) - ;; We are using a headline, clean up garbage in there. - (if (string-match org-todo-regexp s) - (setq s (replace-match "" t t s))) - (if (string-match ":[a-zA-Z_@0-9:]+:[ \t]*$" s) - (setq s (replace-match "" t t s))) - (setq s (org-trim s)) - (if (string-match (concat "^\\(" org-quote-string "\\|" - org-comment-string "\\)") s) - (setq s (replace-match "" t t s))) - (while (string-match org-ts-regexp s) - (setq s (replace-match "" t t s)))) - (while (string-match "[^a-zA-Z_0-9 \t]+" s) - (setq s (replace-match " " t t s))) - (or string (setq s (concat "*" s))) ; Add * for headlines - (mapconcat 'identity (org-split-string s "[ \t]+") " "))) - -(defun org-make-org-heading-camel (&optional string heading) - "Make a CamelCase string for STRING or the current headline." - (interactive) - (let ((s (or string (org-get-heading)))) - (unless (and string (not heading)) - ;; We are using a headline, clean up garbage in there. - (if (string-match org-todo-regexp s) - (setq s (replace-match "" t t s))) - (if (string-match ":[a-zA-Z_@0-9:]+:[ \t]*$" s) - (setq s (replace-match "" t t s))) - (setq s (org-trim s)) - (if (string-match (concat "^\\(" org-quote-string "\\|" - org-comment-string "\\)") s) - (setq s (replace-match "" t t s))) - (while (string-match org-ts-regexp s) - (setq s (replace-match "" t t s)))) - (while (string-match "[^a-zA-Z_ \t]+" s) - (setq s (replace-match " " t t s))) - (or string (setq s (concat "*" s))) ; Add * for headlines - (mapconcat 'capitalize (org-split-string s "[ \t]+") ""))) - -(defun org-make-link (&rest strings) - "Concatenate STRINGS, format resulting string with `org-link-format'." - (format org-link-format (apply 'concat strings))) - -(defun org-make-link-string (link &optional description) - "Make a link with brackets, consisting of LINK and DESCRIPTION." - (if (eq org-link-style 'plain) - (if (equal description link) - link - (concat description "\n" link)) - (when (stringp description) - ;; Remove brackets from the description, they are fatal. - (while (string-match "\\[\\|\\]" description) - (setq description (replace-match "" t t description)))) - (when (equal (org-link-escape link) description) - ;; No description needed, it is identical - (setq description nil)) - (when (and (not description) - (not (equal link (org-link-escape link)))) - (setq description link)) - (concat "[[" (org-link-escape link) "]" - (if description (concat "[" description "]") "") - "]"))) - -(defconst org-link-escape-chars '(("[" . "%5B") ("]" . "%5D") (" " . "%20")) - "Association list of escapes for some characters problematic in links.") - -(defun org-link-escape (text) - "Escape charaters in TEXT that are problematic for links." - (when text - (let ((re (mapconcat (lambda (x) (regexp-quote (car x))) - org-link-escape-chars "\\|"))) - (while (string-match re text) - (setq text - (replace-match - (cdr (assoc (match-string 0 text) org-link-escape-chars)) - t t text))) - text))) - -(defun org-link-unescape (text) - "Reverse the action of `org-link-escape'." - (when text - (let ((re (mapconcat (lambda (x) (regexp-quote (cdr x))) - org-link-escape-chars "\\|"))) - (while (string-match re text) - (setq text - (replace-match - (car (rassoc (match-string 0 text) org-link-escape-chars)) - t t text))) - text))) - -(defun org-xor (a b) - "Exclusive or." - (if a (not b) b)) - -(defun org-get-header (header) - "Find a header field in the current buffer." - (save-excursion - (goto-char (point-min)) - (let ((case-fold-search t) s) - (cond - ((eq header 'from) - (if (re-search-forward "^From:\\s-+\\(.*\\)" nil t) - (setq s (match-string 1))) - (while (string-match "\"" s) - (setq s (replace-match "" t t s))) - (if (string-match "[<(].*" s) - (setq s (replace-match "" t t s)))) - ((eq header 'message-id) - (if (re-search-forward "^message-id:\\s-+\\(.*\\)" nil t) - (setq s (match-string 1)))) - ((eq header 'subject) - (if (re-search-forward "^subject:\\s-+\\(.*\\)" nil t) - (setq s (match-string 1))))) - (if (string-match "\\`[ \t\]+" s) (setq s (replace-match "" t t s))) - (if (string-match "[ \t\]+\\'" s) (setq s (replace-match "" t t s))) - s))) - - -(defun org-fixup-message-id-for-http (s) - "Replace special characters in a message id, so it can be used in an http query." - (while (string-match "<" s) - (setq s (replace-match "%3C" t t s))) - (while (string-match ">" s) - (setq s (replace-match "%3E" t t s))) - (while (string-match "@" s) - (setq s (replace-match "%40" t t s))) - s) - -(defun org-insert-link (&optional complete-file) - "Insert a link. At the prompt, enter the link. - -Completion can be used to select a link previously stored with -`org-store-link'. When the empty string is entered (i.e. if you just -press RET at the prompt), the link defaults to the most recently -stored link. As SPC triggers completion in the minibuffer, you need to -use M-SPC or C-q SPC to force the insertion of a space character. - -You will also be prompted for a description, and if one is given, it will -be displayed in the buffer instead of the link. - -If there is already a link at point, this command will allow you to edit link -and description parts. - -With a \\[universal-argument] prefix, prompts for a file to link to. The file name can be -selected using completion. The path to the file will be relative to -the current directory if the file is in the current directory or a -subdirectory. Otherwise, the link will be the absolute path as -completed in the minibuffer (i.e. normally ~/path/to/file). - -With two \\[universal-argument] prefixes, enforce an absolute path even if the file -is in the current directory or below. -With three \\[universal-argument] prefixes, negate the meaning of -`org-keep-stored-link-after-insertion'." - (interactive "P") - (let (link desc entry remove file (pos (point))) - (cond - ((save-excursion - (skip-chars-forward "^]\n\r") - (and (re-search-backward "\\[\\[" nil t) - (looking-at org-bracket-link-regexp) - (<= (match-beginning 0) pos) - (>= (match-end 0) pos))) - ;; We do have a link at point, and we are going to edit it. - (setq remove (list (match-beginning 0) (match-end 0))) - (setq desc (if (match-end 3) (org-match-string-no-properties 3))) - (setq link (read-string "Link: " - (org-link-unescape - (org-match-string-no-properties 1))))) - ((equal complete-file '(4)) - ;; Completing read for file names. - (setq file (read-file-name "File: ")) - (let ((pwd (file-name-as-directory (expand-file-name "."))) - (pwd1 (file-name-as-directory (abbreviate-file-name - (expand-file-name "."))))) - (cond - ((equal complete-file '(16)) - (setq link (org-make-link - "file:" - (abbreviate-file-name (expand-file-name file))))) - ((string-match (concat "^" (regexp-quote pwd1) "\\(.+\\)") file) - (setq link (org-make-link "file:" (match-string 1 file)))) - ((string-match (concat "^" (regexp-quote pwd) "\\(.+\\)") - (expand-file-name file)) - (setq link (org-make-link - "file:" (match-string 1 (expand-file-name file))))) - (t (setq link (org-make-link "file:" file)))))) - (t - ;; Read link, with completion for stored links. - (setq link (org-completing-read - "Link: " org-stored-links nil nil nil - org-insert-link-history - (or (car (car org-stored-links))))) - (setq entry (assoc link org-stored-links)) - (if (funcall (if (equal complete-file '(64)) 'not 'identity) - (not org-keep-stored-link-after-insertion)) - (setq org-stored-links (delq (assoc link org-stored-links) - org-stored-links))) - (setq link (if entry (nth 1 entry) link) - desc (or desc (nth 2 entry))))) - - (if (string-match org-plain-link-re link) - ;; URL-like link, normalize the use of angular brackets. - (setq link (org-make-link (org-remove-angle-brackets link)))) - - ;; Check if we are linking to the current file with a search option - ;; If yes, simplify the link by using only the search option. - (when (and buffer-file-name - (string-match "\\]+\\)" link)) - (let* ((path (match-string 1 link)) - (case-fold-search nil) - (search (match-string 2 link))) - (save-match-data - (if (equal (file-truename buffer-file-name) (file-truename path)) - ;; We are linking to this same file, with a search option - (setq link search))))) - - ;; Check if we can/should use a relative path. If yes, simplify the link - (when (string-match "\\/=Store -RET at beg-of-buf -> Append to file as level 2 headline -RET on headline -> Store as sublevel entry to current headline -/ -> before/after current headline, same headings level") - -;;;###autoload -(defun org-remember-apply-template () - "Initialize *remember* buffer with template, invoke `org-mode'. -This function should be placed into `remember-mode-hook' and in fact requires -to be run from that hook to fucntion properly." - (if org-remember-templates - - (let* ((entry (if (= (length org-remember-templates) 1) - (cdar org-remember-templates) - (message "Select template: %s" - (mapconcat - (lambda (x) (char-to-string (car x))) - org-remember-templates " ")) - (cdr (assoc (read-char-exclusive) org-remember-templates)))) - (tpl (car entry)) - (file (if (consp (cdr entry)) (nth 1 entry))) - (v-t (format-time-string (car org-time-stamp-formats) (org-current-time))) - (v-T (format-time-string (cdr org-time-stamp-formats) (org-current-time))) - (v-u (concat "[" (substring v-t 1 -1) "]")) - (v-U (concat "[" (substring v-T 1 -1) "]")) - (v-a annotation) ; defined in `remember-mode' - (v-i initial) ; defined in `remember-mode' - (v-n user-full-name) - ) - (unless tpl (setq tpl "") (message "No template") (ding)) - (insert tpl) (goto-char (point-min)) - (while (re-search-forward "%\\([tTuTai]\\)" nil t) - (when (and initial (equal (match-string 0) "%i")) - (save-match-data - (let* ((lead (buffer-substring - (point-at-bol) (match-beginning 0)))) - (setq v-i (mapconcat 'identity - (org-split-string initial "\n") - (concat "\n" lead)))))) - (replace-match - (or (eval (intern (concat "v-" (match-string 1)))) "") - t t)) - (let ((org-startup-folded nil) - (org-startup-with-deadline-check nil)) - (org-mode)) - (if (and file (string-match "\\S-" file) (not (file-directory-p file))) - (org-set-local 'org-default-notes-file file)) - (goto-char (point-min)) - (if (re-search-forward "%\\?" nil t) (replace-match ""))) - (let ((org-startup-folded nil) - (org-startup-with-deadline-check nil)) - (org-mode))) - (org-set-local 'org-finish-function 'remember-buffer)) - -;;;###autoload -(defun org-remember-handler () - "Store stuff from remember.el into an org file. -First prompts for an org file. If the user just presses return, the value -of `org-default-notes-file' is used. -Then the command offers the headings tree of the selected file in order to -file the text at a specific location. -You can either immediately press RET to get the note appended to the -file, or you can use vertical cursor motion and visibility cycling (TAB) to -find a better place. Then press RET or or in insert the note. - -Key Cursor position Note gets inserted ------------------------------------------------------------------------------ -RET buffer-start as level 2 heading at end of file -RET on headline as sublevel of the heading at cursor -RET no heading at cursor position, level taken from context. - Or use prefix arg to specify level manually. - on headline as same level, before current heading - on headline as same level, after current heading - -So the fastest way to store the note is to press RET RET to append it to -the default file. This way your current train of thought is not -interrupted, in accordance with the principles of remember.el. But with -little extra effort, you can push it directly to the correct location. - -Before being stored away, the function ensures that the text has a -headline, i.e. a first line that starts with a \"*\". If not, a headline -is constructed from the current date and some additional data. - -If the variable `org-adapt-indentation' is non-nil, the entire text is -also indented so that it starts in the same column as the headline -\(i.e. after the stars). - -See also the variable `org-reverse-note-order'." - (catch 'quit - (let* ((txt (buffer-substring (point-min) (point-max))) - (fastp current-prefix-arg) - (file (if fastp org-default-notes-file (org-get-org-file))) - (visiting (find-buffer-visiting file)) - (org-startup-with-deadline-check nil) - (org-startup-folded nil) - (org-startup-align-all-tables nil) - spos level indent reversed) - ;; Modify text so that it becomes a nice subtree which can be inserted - ;; into an org tree. - (let* ((lines (split-string txt "\n")) - first) - ;; remove empty lines at the beginning - (while (and lines (string-match "^[ \t]*\n" (car lines))) - (setq lines (cdr lines))) - (setq first (car lines) lines (cdr lines)) - (if (string-match "^\\*+" first) - ;; Is already a headline - (setq indent nil) - ;; We need to add a headline: Use time and first buffer line - (setq lines (cons first lines) - first (concat "* " (current-time-string) - " (" (remember-buffer-desc) ")") - indent " ")) - (if (and org-adapt-indentation indent) - (setq lines (mapcar (lambda (x) (concat indent x)) lines))) - (setq txt (concat first "\n" - (mapconcat 'identity lines "\n")))) - ;; Find the file - (if (not visiting) - (find-file-noselect file)) - (with-current-buffer (get-file-buffer file) - (save-excursion (and (goto-char (point-min)) - (not (re-search-forward "^\\* " nil t)) - (insert "\n* Notes\n"))) - (setq reversed (org-notes-order-reversed-p)) - (save-excursion - (save-restriction - (widen) - ;; Ask the User for a location - (setq spos (if fastp 1 (org-get-location - (current-buffer) - org-remember-help))) - (if (not spos) (throw 'quit nil)) ; return nil to show we did - ; not handle this note - (goto-char spos) - (cond ((and (bobp) (not reversed)) - ;; Put it at the end, as level 2 - (save-restriction - (widen) - (goto-char (point-max)) - (if (not (bolp)) (newline)) - (org-paste-subtree 2 txt))) - ((and (bobp) reversed) - ;; Put it at the start, as level 1 - (save-restriction - (widen) - (goto-char (point-min)) - (re-search-forward "^\\*" nil t) - (beginning-of-line 1) - (org-paste-subtree 1 txt))) - ((and (org-on-heading-p nil) (not current-prefix-arg)) - ;; Put it below this entry, at the beg/end of the subtree - (org-back-to-heading) - (setq level (funcall outline-level)) - (if reversed - (outline-end-of-heading) - (outline-end-of-subtree)) - (if (not (bolp)) (newline)) - (beginning-of-line 1) - (org-paste-subtree (org-get-legal-level level 1) txt)) - (t - ;; Put it right there, with automatic level determined by - ;; org-paste-subtree or from prefix arg - (org-paste-subtree current-prefix-arg txt))) - (when remember-save-after-remembering - (save-buffer) - (if (not visiting) (kill-buffer (current-buffer))))))))) - t) ;; return t to indicate that we took care of this note. - -(defun org-get-org-file () - "Read a filename, with default directory `org-directory'." - (let ((default (or org-default-notes-file remember-data-file))) - (read-file-name (format "File name [%s]: " default) - (file-name-as-directory org-directory) - default))) - -(defun org-notes-order-reversed-p () - "Check if the current file should receive notes in reversed order." - (cond - ((not org-reverse-note-order) nil) - ((eq t org-reverse-note-order) t) - ((not (listp org-reverse-note-order)) nil) - (t (catch 'exit - (let ((all org-reverse-note-order) - entry) - (while (setq entry (pop all)) - (if (string-match (car entry) buffer-file-name) - (throw 'exit (cdr entry)))) - nil))))) ;;;; Tables +;;; The table editor + ;; Watch out: Here we are talking about two different kind of tables. ;; Most of the code is for the tables created with the Org-mode table editor. ;; Sometimes, we talk about tables created and edited with the table.el ;; Emacs package. We call the former org-type tables, and the latter ;; table.el-type tables. - (defun org-before-change-function (beg end) "Every change indicates that a table might need an update." (setq org-table-may-need-update t)) @@ -13423,6 +6842,8 @@ and end of string." (re-search-forward org-table-any-border-regexp nil 1)))) (message "Mapping tables: done")) +(defvar org-timecnt) ; dynamically scoped parameter + (defun org-table-sum (&optional beg end nlast) "Sum numbers in region of current table column. The result will be displayed in the echo area, and will be available @@ -13440,7 +6861,7 @@ numbers are added as such. If NLAST is a number, only the NLAST fields will actually be summed." (interactive) (save-excursion - (let (col (timecnt 0) diff h m s org-table-clip) + (let (col (org-timecnt 0) diff h m s org-table-clip) (cond ((and beg end)) ; beg and end given explicitly ((org-region-active-p) @@ -13468,7 +6889,7 @@ If NLAST is a number, only the NLAST fields will actually be summed." (numbers (delq nil (mapcar 'org-table-get-number-for-summing items1))) (res (apply '+ numbers)) - (sres (if (= timecnt 0) + (sres (if (= org-timecnt 0) (format "%g" res) (setq diff (* 3600 res) h (floor (/ diff 3600)) diff (mod diff 3600) @@ -13498,7 +6919,7 @@ If NLAST is a number, only the NLAST fields will actually be summed." (let ((h (string-to-number (or (match-string 1 s) "0"))) (m (string-to-number (or (match-string 2 s) "0"))) (s (string-to-number (or (match-string 4 s) "0")))) - (if (boundp 'timecnt) (setq timecnt (1+ timecnt))) + (if (boundp 'org-timecnt) (setq org-timecnt (1+ org-timecnt))) (* 1.0 (+ h (/ m 60.0) (/ s 3600.0))))) ((equal n 0) nil) (t n)))) @@ -14242,7 +7663,7 @@ With prefix ARG, apply the new formulas to the table." (goto-char pos) (message "Formula editing aborted without installing changes"))) -;;;; The orgtbl minor mode +;;; The orgtbl minor mode ;; Define a minor mode which can be used in other modes in order to ;; integrate the org-mode table editor. @@ -14283,6 +7704,9 @@ table editor in arbitrary modes.") "Unconditionally turn on `orgtbl-mode'." (orgtbl-mode 1)) +(defvar org-old-auto-fill-inhibit-regexp nil + "Local variable used by `orgtbl-mode'") + ;;;###autoload (defun orgtbl-mode (&optional arg) "The `org-mode' table editor as a minor mode for use in other modes." @@ -14521,8 +7945,7054 @@ overwritten, and the table is not marked as requiring realignment." (interactive "p") (self-insert-command N)) +;;;; Link Stuff + +;;; Link abbreviations + +(defun org-link-expand-abbrev (link) + "Apply replacements as defined in `org-link-abbrev-alist." + (if (string-match "^\\([a-zA-Z]+\\)\\(::\\(.*\\)\\)?$" link) + (let* ((key (match-string 1 link)) + (as (or (assoc key org-link-abbrev-alist-local) + (assoc key org-link-abbrev-alist))) + (tag (and (match-end 2) (match-string 3 link))) + rpl) + (if (not as) + link + (setq rpl (cdr as)) + (cond + ((symbolp rpl) (funcall rpl tag)) + ((string-match "%s" rpl) (replace-match (or tag "") t t rpl)) + (t (concat rpl tag))))) + link)) + +;;; Storing and inserting links + +(defvar org-insert-link-history nil + "Minibuffer history for links inserted with `org-insert-link'.") + +(defvar org-stored-links nil + "Contains the links stored with `org-store-link'.") + +;;;###autoload +(defun org-store-link (arg) + "\\Store an org-link to the current location. +This link can later be inserted into an org-buffer with +\\[org-insert-link]. +For some link types, a prefix arg is interpreted: +For links to usenet articles, arg negates `org-usenet-links-prefer-google'. +For file links, arg negates `org-context-in-file-links'." + (interactive "P") + (let (link cpltxt desc description search txt (pos (point))) + (cond + + ((eq major-mode 'bbdb-mode) + (setq cpltxt (concat + "bbdb:" + (or (bbdb-record-name (bbdb-current-record)) + (bbdb-record-company (bbdb-current-record)))) + link (org-make-link cpltxt))) + + ((eq major-mode 'Info-mode) + (setq link (org-make-link "info:" + (file-name-nondirectory Info-current-file) + ":" Info-current-node)) + (setq cpltxt (concat (file-name-nondirectory Info-current-file) + ":" Info-current-node))) + + ((eq major-mode 'calendar-mode) + (let ((cd (calendar-cursor-to-date))) + (setq link + (format-time-string + (car org-time-stamp-formats) + (apply 'encode-time + (list 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd) + nil nil nil)))))) + + ((or (eq major-mode 'vm-summary-mode) + (eq major-mode 'vm-presentation-mode)) + (and (eq major-mode 'vm-presentation-mode) (vm-summarize)) + (vm-follow-summary-cursor) + (save-excursion + (vm-select-folder-buffer) + (let* ((message (car vm-message-pointer)) + (folder buffer-file-name) + (subject (vm-su-subject message)) + (author (vm-su-full-name message)) + (message-id (vm-su-message-id message))) + (setq message-id (org-remove-angle-brackets message-id)) + (setq folder (abbreviate-file-name folder)) + (if (string-match (concat "^" (regexp-quote vm-folder-directory)) + folder) + (setq folder (replace-match "" t t folder))) + (setq cpltxt (concat author " on: " subject)) + (setq link (org-make-link "vm:" folder "#" message-id))))) + + ((eq major-mode 'wl-summary-mode) + (let* ((msgnum (wl-summary-message-number)) + (message-id (elmo-message-field wl-summary-buffer-elmo-folder + msgnum 'message-id)) + (wl-message-entity + (if (fboundp 'elmo-message-entity) + (elmo-message-entity + wl-summary-buffer-elmo-folder msgnum) + (elmo-msgdb-overview-get-entity + msgnum (wl-summary-buffer-msgdb)))) + (author (wl-summary-line-from)) ; FIXME: correct? + (subject "???")) ; FIXME: + (setq message-id (org-remove-angle-brackets message-id)) + (setq cpltxt (concat author " on: " subject)) + (setq link (org-make-link "wl:" wl-summary-buffer-folder-name + "#" message-id)))) + + ((or (equal major-mode 'mh-folder-mode) + (equal major-mode 'mh-show-mode)) + (let ((from-header (org-mhe-get-header "From:")) + (to-header (org-mhe-get-header "To:")) + (subject (org-mhe-get-header "Subject:"))) + (setq cpltxt (concat from-header " on: " subject)) + (setq link (org-make-link "mhe:" (org-mhe-get-message-real-folder) "#" + (org-remove-angle-brackets + (org-mhe-get-header "Message-Id:")))))) + + ((eq major-mode 'rmail-mode) + (save-excursion + (save-restriction + (rmail-narrow-to-non-pruned-header) + (let ((folder buffer-file-name) + (message-id (mail-fetch-field "message-id")) + (author (mail-fetch-field "from")) + (subject (mail-fetch-field "subject"))) + (setq message-id (org-remove-angle-brackets message-id)) + (setq cpltxt (concat author " on: " subject)) + (setq link (org-make-link "rmail:" folder "#" message-id)))))) + + ((eq major-mode 'gnus-group-mode) + (let ((group (cond ((fboundp 'gnus-group-group-name) ; depending on Gnus + (gnus-group-group-name)) ; version + ((fboundp 'gnus-group-name) + (gnus-group-name)) + (t "???")))) + (setq cpltxt (concat + (if (org-xor arg org-usenet-links-prefer-google) + "http://groups.google.com/groups?group=" + "gnus:") + group) + link (org-make-link cpltxt)))) + + ((memq major-mode '(gnus-summary-mode gnus-article-mode)) + (require 'gnus-sum) ; FIXME: I don't think this is needed, actually + (and (eq major-mode 'gnus-article-mode) (gnus-article-show-summary)) + (gnus-summary-beginning-of-article) + (let* ((group (car gnus-article-current)) + (article (cdr gnus-article-current)) + (header (gnus-summary-article-header article)) + (author (mail-header-from header)) + (message-id (mail-header-id header)) + (date (mail-header-date header)) + (subject (gnus-summary-subject-string))) + (setq cpltxt (concat author " on: " subject)) + (if (org-xor arg org-usenet-links-prefer-google) + (setq link + (concat + cpltxt "\n " + (format "http://groups.google.com/groups?as_umsgid=%s" + (org-fixup-message-id-for-http message-id)))) + (setq link (org-make-link "gnus:" group + "#" (number-to-string article)))))) + + ((eq major-mode 'w3-mode) + (setq cpltxt (url-view-url t) + link (org-make-link cpltxt))) + ((eq major-mode 'w3m-mode) + (setq cpltxt (or w3m-current-title w3m-current-url) + link (org-make-link w3m-current-url))) + + ((setq search (run-hook-with-args-until-success + 'org-create-file-search-functions)) + (setq link (concat "file:" (abbreviate-file-name buffer-file-name) + "::" search)) + (setq cpltxt (or description link))) + + ((eq major-mode 'image-mode) + (setq cpltxt (concat "file:" + (abbreviate-file-name buffer-file-name)) + link (org-make-link cpltxt))) + + ((eq major-mode 'dired-mode) + ;; link to the file in the current line + (setq cpltxt (concat "file:" + (abbreviate-file-name + (expand-file-name + (dired-get-filename nil t)))) + link (org-make-link cpltxt))) + + ((and buffer-file-name (org-mode-p)) + ;; Just link to current headline + (setq cpltxt (concat "file:" + (abbreviate-file-name buffer-file-name))) + ;; Add a context search string + (when (org-xor org-context-in-file-links arg) + ;; Check if we are on a target + (if (save-excursion + (skip-chars-forward "^>\n\r") + (and (re-search-backward "<<" nil t) + (looking-at "<<\\(.*?\\)>>") + (<= (match-beginning 0) pos) + (>= (match-end 0) pos))) + (setq cpltxt (concat cpltxt "::" (match-string 1))) + (setq txt (cond + ((org-on-heading-p) nil) + ((org-region-active-p) + (buffer-substring (region-beginning) (region-end))) + (t (buffer-substring (point-at-bol) (point-at-eol))))) + (when (or (null txt) (string-match "\\S-" txt)) + (setq cpltxt + (concat cpltxt "::" + (if org-file-link-context-use-camel-case + (org-make-org-heading-camel txt) + (org-make-org-heading-search-string txt))) + desc "NONE")))) + (if (string-match "::\\'" cpltxt) + (setq cpltxt (substring cpltxt 0 -2))) + (setq link (org-make-link cpltxt))) + + (buffer-file-name + ;; Just link to this file here. + (setq cpltxt (concat "file:" + (abbreviate-file-name buffer-file-name))) + ;; Add a context string + (when (org-xor org-context-in-file-links arg) + (setq txt (if (org-region-active-p) + (buffer-substring (region-beginning) (region-end)) + (buffer-substring (point-at-bol) (point-at-eol)))) + ;; Only use search option if there is some text. + (when (string-match "\\S-" txt) + (setq cpltxt + (concat cpltxt "::" + (if org-file-link-context-use-camel-case + (org-make-org-heading-camel txt) + (org-make-org-heading-search-string txt))) + desc "NONE"))) + (setq link (org-make-link cpltxt))) + + ((interactive-p) + (error "Cannot link to a buffer which is not visiting a file")) + + (t (setq link nil))) + + (if (consp link) (setq cpltxt (car link) link (cdr link))) + (setq link (or link cpltxt) + desc (or desc cpltxt)) + (if (equal desc "NONE") (setq desc nil)) + + (if (and (interactive-p) link) + (progn + (setq org-stored-links + (cons (list cpltxt link desc) org-stored-links)) + (message "Stored: %s" (or cpltxt link))) + (org-make-link-string link desc)))) + +(defun org-make-org-heading-search-string (&optional string heading) + "Make search string for STRING or current headline." + (interactive) + (let ((s (or string (org-get-heading)))) + (unless (and string (not heading)) + ;; We are using a headline, clean up garbage in there. + (if (string-match org-todo-regexp s) + (setq s (replace-match "" t t s))) + (if (string-match ":[a-zA-Z_@0-9:]+:[ \t]*$" s) + (setq s (replace-match "" t t s))) + (setq s (org-trim s)) + (if (string-match (concat "^\\(" org-quote-string "\\|" + org-comment-string "\\)") s) + (setq s (replace-match "" t t s))) + (while (string-match org-ts-regexp s) + (setq s (replace-match "" t t s)))) + (while (string-match "[^a-zA-Z_0-9 \t]+" s) + (setq s (replace-match " " t t s))) + (or string (setq s (concat "*" s))) ; Add * for headlines + (mapconcat 'identity (org-split-string s "[ \t]+") " "))) + +(defun org-make-org-heading-camel (&optional string heading) + "Make a CamelCase string for STRING or the current headline." + (interactive) + (let ((s (or string (org-get-heading)))) + (unless (and string (not heading)) + ;; We are using a headline, clean up garbage in there. + (if (string-match org-todo-regexp s) + (setq s (replace-match "" t t s))) + (if (string-match ":[a-zA-Z_@0-9:]+:[ \t]*$" s) + (setq s (replace-match "" t t s))) + (setq s (org-trim s)) + (if (string-match (concat "^\\(" org-quote-string "\\|" + org-comment-string "\\)") s) + (setq s (replace-match "" t t s))) + (while (string-match org-ts-regexp s) + (setq s (replace-match "" t t s)))) + (while (string-match "[^a-zA-Z_ \t]+" s) + (setq s (replace-match " " t t s))) + (or string (setq s (concat "*" s))) ; Add * for headlines + (mapconcat 'capitalize (org-split-string s "[ \t]+") ""))) + +(defun org-make-link (&rest strings) + "Concatenate STRINGS, format resulting string with `org-link-format'." + (format org-link-format (apply 'concat strings))) + +(defun org-make-link-string (link &optional description) + "Make a link with brackets, consisting of LINK and DESCRIPTION." + (if (eq org-link-style 'plain) + (if (equal description link) + link + (concat description "\n" link)) + (when (stringp description) + ;; Remove brackets from the description, they are fatal. + (while (string-match "\\[\\|\\]" description) + (setq description (replace-match "" t t description)))) + (when (equal (org-link-escape link) description) + ;; No description needed, it is identical + (setq description nil)) + (when (and (not description) + (not (equal link (org-link-escape link)))) + (setq description link)) + (concat "[[" (org-link-escape link) "]" + (if description (concat "[" description "]") "") + "]"))) + +(defconst org-link-escape-chars '(("[" . "%5B") ("]" . "%5D") (" " . "%20")) + "Association list of escapes for some characters problematic in links.") + +(defun org-link-escape (text) + "Escape charaters in TEXT that are problematic for links." + (when text + (let ((re (mapconcat (lambda (x) (regexp-quote (car x))) + org-link-escape-chars "\\|"))) + (while (string-match re text) + (setq text + (replace-match + (cdr (assoc (match-string 0 text) org-link-escape-chars)) + t t text))) + text))) + +(defun org-link-unescape (text) + "Reverse the action of `org-link-escape'." + (when text + (let ((re (mapconcat (lambda (x) (regexp-quote (cdr x))) + org-link-escape-chars "\\|"))) + (while (string-match re text) + (setq text + (replace-match + (car (rassoc (match-string 0 text) org-link-escape-chars)) + t t text))) + text))) + +(defun org-xor (a b) + "Exclusive or." + (if a (not b) b)) + +(defun org-get-header (header) + "Find a header field in the current buffer." + (save-excursion + (goto-char (point-min)) + (let ((case-fold-search t) s) + (cond + ((eq header 'from) + (if (re-search-forward "^From:\\s-+\\(.*\\)" nil t) + (setq s (match-string 1))) + (while (string-match "\"" s) + (setq s (replace-match "" t t s))) + (if (string-match "[<(].*" s) + (setq s (replace-match "" t t s)))) + ((eq header 'message-id) + (if (re-search-forward "^message-id:\\s-+\\(.*\\)" nil t) + (setq s (match-string 1)))) + ((eq header 'subject) + (if (re-search-forward "^subject:\\s-+\\(.*\\)" nil t) + (setq s (match-string 1))))) + (if (string-match "\\`[ \t\]+" s) (setq s (replace-match "" t t s))) + (if (string-match "[ \t\]+\\'" s) (setq s (replace-match "" t t s))) + s))) + + +(defun org-fixup-message-id-for-http (s) + "Replace special characters in a message id, so it can be used in an http query." + (while (string-match "<" s) + (setq s (replace-match "%3C" t t s))) + (while (string-match ">" s) + (setq s (replace-match "%3E" t t s))) + (while (string-match "@" s) + (setq s (replace-match "%40" t t s))) + s) + +(defun org-insert-link (&optional complete-file) + "Insert a link. At the prompt, enter the link. + +Completion can be used to select a link previously stored with +`org-store-link'. When the empty string is entered (i.e. if you just +press RET at the prompt), the link defaults to the most recently +stored link. As SPC triggers completion in the minibuffer, you need to +use M-SPC or C-q SPC to force the insertion of a space character. + +You will also be prompted for a description, and if one is given, it will +be displayed in the buffer instead of the link. + +If there is already a link at point, this command will allow you to edit link +and description parts. + +With a \\[universal-argument] prefix, prompts for a file to link to. The file name can be +selected using completion. The path to the file will be relative to +the current directory if the file is in the current directory or a +subdirectory. Otherwise, the link will be the absolute path as +completed in the minibuffer (i.e. normally ~/path/to/file). + +With two \\[universal-argument] prefixes, enforce an absolute path even if the file +is in the current directory or below. +With three \\[universal-argument] prefixes, negate the meaning of +`org-keep-stored-link-after-insertion'." + (interactive "P") + (let (link desc entry remove file (pos (point))) + (cond + ((save-excursion + (skip-chars-forward "^]\n\r") + (and (re-search-backward "\\[\\[" nil t) + (looking-at org-bracket-link-regexp) + (<= (match-beginning 0) pos) + (>= (match-end 0) pos))) + ;; We do have a link at point, and we are going to edit it. + (setq remove (list (match-beginning 0) (match-end 0))) + (setq desc (if (match-end 3) (org-match-string-no-properties 3))) + (setq link (read-string "Link: " + (org-link-unescape + (org-match-string-no-properties 1))))) + ((equal complete-file '(4)) + ;; Completing read for file names. + (setq file (read-file-name "File: ")) + (let ((pwd (file-name-as-directory (expand-file-name "."))) + (pwd1 (file-name-as-directory (abbreviate-file-name + (expand-file-name "."))))) + (cond + ((equal complete-file '(16)) + (setq link (org-make-link + "file:" + (abbreviate-file-name (expand-file-name file))))) + ((string-match (concat "^" (regexp-quote pwd1) "\\(.+\\)") file) + (setq link (org-make-link "file:" (match-string 1 file)))) + ((string-match (concat "^" (regexp-quote pwd) "\\(.+\\)") + (expand-file-name file)) + (setq link (org-make-link + "file:" (match-string 1 (expand-file-name file))))) + (t (setq link (org-make-link "file:" file)))))) + (t + ;; Read link, with completion for stored links. + (setq link (org-completing-read + "Link: " org-stored-links nil nil nil + org-insert-link-history + (or (car (car org-stored-links))))) + (setq entry (assoc link org-stored-links)) + (if (funcall (if (equal complete-file '(64)) 'not 'identity) + (not org-keep-stored-link-after-insertion)) + (setq org-stored-links (delq (assoc link org-stored-links) + org-stored-links))) + (setq link (if entry (nth 1 entry) link) + desc (or desc (nth 2 entry))))) + + (if (string-match org-plain-link-re link) + ;; URL-like link, normalize the use of angular brackets. + (setq link (org-make-link (org-remove-angle-brackets link)))) + + ;; Check if we are linking to the current file with a search option + ;; If yes, simplify the link by using only the search option. + (when (and buffer-file-name + (string-match "\\]+\\)" link)) + (let* ((path (match-string 1 link)) + (case-fold-search nil) + (search (match-string 2 link))) + (save-match-data + (if (equal (file-truename buffer-file-name) (file-truename path)) + ;; We are linking to this same file, with a search option + (setq link search))))) + + ;; Check if we can/should use a relative path. If yes, simplify the link + (when (string-match "\\= (match-end 0) pos)) + (setq link (org-link-unescape (org-match-string-no-properties 1))) + (while (string-match " *\n *" link) + (setq link (replace-match " " t t link))) + (setq link (org-link-expand-abbrev link)) + (if (string-match org-link-re-with-space2 link) + (setq type (match-string 1 link) + path (match-string 2 link)) + (setq type "thisfile" + path link)) + (throw 'match t))) + + (when (get-text-property (point) 'org-linked-text) + (setq type "thisfile" + pos (if (get-text-property (1+ (point)) 'org-linked-text) + (1+ (point)) (point)) + path (buffer-substring + (previous-single-property-change pos 'org-linked-text) + (next-single-property-change pos 'org-linked-text))) + (throw 'match t)) + + (save-excursion + (skip-chars-backward (concat "^[]" org-non-link-chars " ")) + (if (equal (char-before) ?<) (backward-char 1)) + (when (or (looking-at org-angle-link-re) + (looking-at org-plain-link-re) + (and (or (re-search-forward org-angle-link-re (point-at-eol) t) + (re-search-forward org-plain-link-re (point-at-eol) t)) + (<= (match-beginning 0) pos) + (>= (match-end 0) pos))) + (setq type (match-string 1) + path (match-string 2)) + (throw 'match t))) + (save-excursion + (skip-chars-backward "^ \t\n\r") + (when (looking-at "\\(:[A-Za-z_@0-9:]+\\):[ \t\r\n]") + (setq type "tags" + path (match-string 1)) + (while (string-match ":" path) + (setq path (replace-match "+" t t path))) + (throw 'match t))) + (save-excursion + (skip-chars-backward "a-zA-Z_") + (when (and (memq 'camel org-activate-links) + (looking-at org-camel-regexp)) + (setq type "camel" path (match-string 0)) + (if (equal (char-before) ?*) + (setq path (concat "*" path)))) + (throw 'match t))) + (unless path + (error "No link found")) + ;; Remove any trailing spaces in path + (if (string-match " +\\'" path) + (setq path (replace-match "" t t path))) + + (cond + + ((equal type "mailto") + (let ((cmd (car org-link-mailto-program)) + (args (cdr org-link-mailto-program)) args1 + (address path) (subject "") a) + (if (string-match "\\(.*\\)::\\(.*\\)" path) + (setq address (match-string 1 path) + subject (org-link-escape (match-string 2 path)))) + (while args + (cond + ((not (stringp (car args))) (push (pop args) args1)) + (t (setq a (pop args)) + (if (string-match "%a" a) + (setq a (replace-match address t t a))) + (if (string-match "%s" a) + (setq a (replace-match subject t t a))) + (push a args1)))) + (apply cmd (nreverse args1)))) + + ((member type '("http" "https" "ftp" "news")) + (browse-url (concat type ":" path))) + + ((string= type "tags") + (org-tags-view in-emacs path)) + ((or (string= type "camel") + (string= type "thisfile")) + (if in-emacs + (switch-to-buffer-other-window + (org-get-buffer-for-internal-link (current-buffer))) + (org-mark-ring-push)) + (org-link-search + path + (cond ((equal in-emacs '(4)) 'occur) + ((equal in-emacs '(16)) 'org-occur) + (t nil)))) + + ((string= type "file") + (if (string-match "::\\([0-9]+\\)\\'" path) + (setq line (string-to-number (match-string 1 path)) + path (substring path 0 (match-beginning 0))) + (if (string-match "::\\(.+\\)\\'" path) + (setq search (match-string 1 path) + path (substring path 0 (match-beginning 0))))) + (org-open-file path in-emacs line search)) + + ((string= type "news") + (org-follow-gnus-link path)) + + ((string= type "bbdb") + (org-follow-bbdb-link path)) + + ((string= type "info") + (org-follow-info-link path)) + + ((string= type "gnus") + (let (group article) + (if (not (string-match "\\`\\([^#]+\\)\\(#\\(.*\\)\\)?" path)) + (error "Error in Gnus link")) + (setq group (match-string 1 path) + article (match-string 3 path)) + (org-follow-gnus-link group article))) + + ((string= type "vm") + (let (folder article) + (if (not (string-match "\\`\\([^#]+\\)\\(#\\(.*\\)\\)?" path)) + (error "Error in VM link")) + (setq folder (match-string 1 path) + article (match-string 3 path)) + ;; in-emacs is the prefix arg, will be interpreted as read-only + (org-follow-vm-link folder article in-emacs))) + + ((string= type "wl") + (let (folder article) + (if (not (string-match "\\`\\([^#]+\\)\\(#\\(.*\\)\\)?" path)) + (error "Error in Wanderlust link")) + (setq folder (match-string 1 path) + article (match-string 3 path)) + (org-follow-wl-link folder article))) + + ((string= type "mhe") + (let (folder article) + (if (not (string-match "\\`\\([^#]+\\)\\(#\\(.*\\)\\)?" path)) + (error "Error in MHE link")) + (setq folder (match-string 1 path) + article (match-string 3 path)) + (org-follow-mhe-link folder article))) + + ((string= type "rmail") + (let (folder article) + (if (not (string-match "\\`\\([^#]+\\)\\(#\\(.*\\)\\)?" path)) + (error "Error in RMAIL link")) + (setq folder (match-string 1 path) + article (match-string 3 path)) + (org-follow-rmail-link folder article))) + + ((string= type "shell") + (let ((cmd path)) + (while (string-match "@{" cmd) ; FIXME: not needed for [[]] links + (setq cmd (replace-match "<" t t cmd))) + (while (string-match "@}" cmd) ; FIXME: not needed for [[]] links + (setq cmd (replace-match ">" t t cmd))) + (if (or (not org-confirm-shell-link-function) + (funcall org-confirm-shell-link-function + (format "Execute \"%s\" in shell? " + (org-add-props cmd nil + 'face 'org-warning)))) + (progn + (message "Executing %s" cmd) + (shell-command cmd)) + (error "Abort")))) + + ((string= type "elisp") + (let ((cmd path)) + (if (or (not org-confirm-elisp-link-function) + (funcall org-confirm-elisp-link-function + (format "Execute \"%s\" as elisp? " + (org-add-props cmd nil + 'face 'org-warning)))) + (message "%s => %s" cmd (eval (read cmd))) + (error "Abort")))) + + (t + (browse-url-at-point)))))) + +;;; File search + +(defvar org-create-file-search-functions nil + "List of functions to construct the right search string for a file link. +These functions are called in turn with point at the location to +which the link should point. + +A function in the hook should first test if it would like to +handle this file type, for example by checking the major-mode or +the file extension. If it decides not to handle this file, it +should just return nil to give other functions a chance. If it +does handle the file, it must return the search string to be used +when following the link. The search string will be part of the +file link, given after a double colon, and `org-open-at-point' +will automatically search for it. If special measures must be +taken to make the search successful, another function should be +added to the companion hook `org-execute-file-search-functions', +which see. + +A function in this hook may also use `setq' to set the variable +`description' to provide a suggestion for the descriptive text to +be used for this link when it gets inserted into an Org-mode +buffer with \\[org-insert-link].") + +(defvar org-execute-file-search-functions nil + "List of functions to execute a file search triggered by a link. + +Functions added to this hook must accept a single argument, the +search string that was part of the file link, the part after the +double colon. The function must first check if it would like to +handle this search, for example by checking the major-mode or the +file extension. If it decides not to handle this search, it +should just return nil to give other functions a chance. If it +does handle the search, it must return a non-nil value to keep +other functions from trying. + +Each function can access the current prefix argument through the +variable `current-prefix-argument'. Note that a single prefix is +used to force opening a link in Emacs, so it may be good to only +use a numeric or double prefix to guide the search function. + +In case this is needed, a function in this hook can also restore +the window configuration before `org-open-at-point' was called using: + + (set-window-configuration org-window-config-before-follow-link)") + +(defun org-link-search (s &optional type) + "Search for a link search option. +When S is a CamelCaseWord, search for a target, or for a sentence containing +the words. If S is surrounded by forward slashes, it is interpreted as a +regular expression. In org-mode files, this will create an `org-occur' +sparse tree. In ordinary files, `occur' will be used to list matches. +If the current buffer is in `dired-mode', grep will be used to search +in all files." + (let ((case-fold-search t) + (s0 (mapconcat 'identity (org-split-string s "[ \t\r\n]+") " ")) + (pos (point)) + (pre "") (post "") + words re0 re1 re2 re3 re4 re5 re2a reall camel) + (cond + ;; First check if there are any special + ((run-hook-with-args-until-success 'org-execute-file-search-functions s)) + ;; Now try the builtin stuff + ((save-excursion + (goto-char (point-min)) + (and + (re-search-forward + (concat "<<" (regexp-quote s0) ">>") nil t) + (setq pos (match-beginning 0)))) + ;; There is an exact target for this + (goto-char pos)) + ((string-match "^/\\(.*\\)/$" s) + ;; A regular expression + (cond + ((org-mode-p) + (org-occur (match-string 1 s))) + ;;((eq major-mode 'dired-mode) + ;; (grep (concat "grep -n -e '" (match-string 1 s) "' *"))) + (t (org-do-occur (match-string 1 s))))) + ((or (setq camel (string-match (concat "^" org-camel-regexp "$") s)) + t) + ;; A camel or a normal search string + (when (equal (string-to-char s) ?*) + ;; Anchor on headlines, post may include tags. + (setq pre "^\\*+[ \t]*\\(?:\\sw+\\)?[ \t]*" + post "[ \t]*\\(?:[ \t]+:[a-zA-Z_@0-9:+]:[ \t]*\\)?$" + s (substring s 1))) + (remove-text-properties + 0 (length s) + '(face nil mouse-face nil keymap nil fontified nil) s) + ;; Make a series of regular expressions to find a match + (setq words + (if camel + (org-camel-to-words s) + (org-split-string s "[ \n\r\t]+")) + re0 (concat "\\(<<" (regexp-quote s0) ">>\\)") + re2 (concat "[ \t\r\n]\\(" (mapconcat 'downcase words "[ \t]+") "\\)[ \t\r\n]") + re2a (concat "[ \t\r\n]\\(" (mapconcat 'downcase words "[ \t\r\n]+") "\\)[ \t\r\n]") + re4 (concat "[^a-zA-Z_]\\(" (mapconcat 'downcase words "[^a-zA-Z_\r\n]+") "\\)[^a-zA-Z_]") + re1 (concat pre re2 post) + re3 (concat pre re4 post) + re5 (concat pre ".*" re4) + re2 (concat pre re2) + re2a (concat pre re2a) + re4 (concat pre re4) + reall (concat "\\(" re0 "\\)\\|\\(" re1 "\\)\\|\\(" re2 + "\\)\\|\\(" re3 "\\)\\|\\(" re4 "\\)\\|\\(" + re5 "\\)" + )) + (cond + ((eq type 'org-occur) (org-occur reall)) + ((eq type 'occur) (org-do-occur (downcase reall) 'cleanup)) + (t (goto-char (point-min)) + (if (or (org-search-not-link re0 nil t) + (org-search-not-link re1 nil t) + (org-search-not-link re2 nil t) + (org-search-not-link re2a nil t) + (org-search-not-link re3 nil t) + (org-search-not-link re4 nil t) + (org-search-not-link re5 nil t) + ) + (goto-char (match-beginning 1)) + (goto-char pos) + (error "No match"))))) + (t + ;; Normal string-search + (goto-char (point-min)) + (if (search-forward s nil t) + (goto-char (match-beginning 0)) + (error "No match")))) + (and (org-mode-p) (org-show-context 'link-search)))) + +(defun org-search-not-link (&rest args) + "Execute `re-search-forward', but only accept matches that are not a link." + (catch 'exit + (let (p1) + (while (apply 're-search-forward args) + (setq p1 (point)) + (if (not (save-match-data + (and (re-search-backward "\\[\\[" nil t) + (looking-at org-bracket-link-regexp) + (<= (match-beginning 0) p1) + (>= (match-end 0) p1)))) + (progn (goto-char (match-end 0)) + (throw 'exit (point))) + (goto-char (match-end 0))))))) + +(defun org-get-buffer-for-internal-link (buffer) + "Return a buffer to be used for displaying the link target of internal links." + (cond + ((not org-display-internal-link-with-indirect-buffer) + buffer) + ((string-match "(Clone)$" (buffer-name buffer)) + (message "Buffer is already a clone, not making another one") + ;; we also do not modify visibility in this case + buffer) + (t ; make a new indirect buffer for displaying the link + (let* ((bn (buffer-name buffer)) + (ibn (concat bn "(Clone)")) + (ib (or (get-buffer ibn) (make-indirect-buffer buffer ibn 'clone)))) + (with-current-buffer ib (org-overview)) + ib)))) + +(defun org-do-occur (regexp &optional cleanup) + "Call the Emacs command `occur'. +If CLEANUP is non-nil, remove the printout of the regular expression +in the *Occur* buffer. This is useful if the regex is long and not useful +to read." + (occur regexp) + (when cleanup + (let ((cwin (selected-window)) win beg end) + (when (setq win (get-buffer-window "*Occur*")) + (select-window win)) + (goto-char (point-min)) + (when (re-search-forward "match[a-z]+" nil t) + (setq beg (match-end 0)) + (if (re-search-forward "^[ \t]*[0-9]+" nil t) + (setq end (1- (match-beginning 0))))) + (and beg end (let ((buffer-read-only)) (delete-region beg end))) + (goto-char (point-min)) + (select-window cwin)))) + +;;; The mark ring for links jumps + +(defvar org-mark-ring nil + "Mark ring for positions before jumps in Org-mode.") +(defvar org-mark-ring-last-goto nil + "Last position in the mark ring used to go back.") +;; Fill and close the ring +(setq org-mark-ring nil org-mark-ring-last-goto nil) ;; in case file is reloaded +(loop for i from 1 to org-mark-ring-length do + (push (make-marker) org-mark-ring)) +(setcdr (nthcdr (1- org-mark-ring-length) org-mark-ring) + org-mark-ring) + +(defun org-mark-ring-push (&optional pos buffer) + "Put the current position or POS into the mark ring and rotate it." + (interactive) + (setq pos (or pos (point))) + (setq org-mark-ring (nthcdr (1- org-mark-ring-length) org-mark-ring)) + (move-marker (car org-mark-ring) + (or pos (point)) + (or buffer (current-buffer))) + (message + (substitute-command-keys + "Position saved to mark ring, go back with \\[org-mark-ring-goto]."))) + +(defun org-mark-ring-goto (&optional n) + "Jump to the previous position in the mark ring. +With prefix arg N, jump back that many stored positions. When +called several times in succession, walk through the entire ring. +Org-mode commands jumping to a different position in the current file, +or to another Org-mode file, automatically push the old position +onto the ring." + (interactive "p") + (let (p m) + (if (eq last-command this-command) + (setq p (nthcdr n (or org-mark-ring-last-goto org-mark-ring))) + (setq p org-mark-ring)) + (setq org-mark-ring-last-goto p) + (setq m (car p)) + (switch-to-buffer (marker-buffer m)) + (goto-char m) + (if (or (org-invisible-p) (org-invisible-p2)) (org-show-context 'mark-goto)))) + +(defun org-camel-to-words (s) + "Split \"CamelCaseWords\" to (\"Camel\" \"Case\" \"Words\")." + (let ((case-fold-search nil) + words) + (while (string-match "[a-z][A-Z]" s) + (push (substring s 0 (1+ (match-beginning 0))) words) + (setq s (substring s (1+ (match-beginning 0))))) + (nreverse (cons s words)))) + +(defun org-remove-angle-brackets (s) + (if (equal (substring s 0 1) "<") (setq s (substring s 1))) + (if (equal (substring s -1) ">") (setq s (substring s 0 -1))) + s) +(defun org-add-angle-brackets (s) + (if (equal (substring s 0 1) "<") nil (setq s (concat "<" s))) + (if (equal (substring s -1) ">") nil (setq s (concat s ">"))) + s) + +;;; Following specific links + +(defun org-follow-timestamp-link () + (cond + ((org-at-date-range-p t) + (let ((org-agenda-start-on-weekday) + (t1 (match-string 1)) + (t2 (match-string 2))) + (setq t1 (time-to-days (org-time-string-to-time t1)) + t2 (time-to-days (org-time-string-to-time t2))) + (org-agenda-list nil t1 (1+ (- t2 t1))))) + ((org-at-timestamp-p t) + (org-agenda-list nil (time-to-days (org-time-string-to-time + (substring (match-string 1) 0 10))) + 1)) + (t (error "This should not happen")))) + + +(defun org-follow-bbdb-link (name) + "Follow a BBDB link to NAME." + (require 'bbdb) + (let ((inhibit-redisplay t) + (bbdb-electric-p nil)) + (catch 'exit + ;; Exact match on name + (bbdb-name (concat "\\`" name "\\'") nil) + (if (< 0 (buffer-size (get-buffer "*BBDB*"))) (throw 'exit nil)) + ;; Exact match on name + (bbdb-company (concat "\\`" name "\\'") nil) + (if (< 0 (buffer-size (get-buffer "*BBDB*"))) (throw 'exit nil)) + ;; Partial match on name + (bbdb-name name nil) + (if (< 0 (buffer-size (get-buffer "*BBDB*"))) (throw 'exit nil)) + ;; Partial match on company + (bbdb-company name nil) + (if (< 0 (buffer-size (get-buffer "*BBDB*"))) (throw 'exit nil)) + ;; General match including network address and notes + (bbdb name nil) + (when (= 0 (buffer-size (get-buffer "*BBDB*"))) + (delete-window (get-buffer-window "*BBDB*")) + (error "No matching BBDB record"))))) + +(defun org-follow-info-link (name) + "Follow an info file & node link to NAME." + (if (or (string-match "\\(.*\\)::?\\(.*\\)" name) + (string-match "\\(.*\\)" name)) + (progn + (require 'info) + (if (match-string 2 name) ; If there isn't a node, choose "Top" + (Info-find-node (match-string 1 name) (match-string 2 name)) + (Info-find-node (match-string 1 name) "Top"))) + (message (concat "Could not open: " name)))) + +(defun org-follow-gnus-link (&optional group article) + "Follow a Gnus link to GROUP and ARTICLE." + (require 'gnus) + (funcall (cdr (assq 'gnus org-link-frame-setup))) + (if gnus-other-frame-object (select-frame gnus-other-frame-object)) + (if group (gnus-fetch-group group)) + (if article + (or (gnus-summary-goto-article article nil 'force) + (if (fboundp 'gnus-summary-insert-cached-articles) + (progn + (gnus-summary-insert-cached-articles) + (gnus-summary-goto-article article nil 'force)) + (message "Message could not be found."))))) + +(defun org-follow-vm-link (&optional folder article readonly) + "Follow a VM link to FOLDER and ARTICLE." + (require 'vm) + (setq article (org-add-angle-brackets article)) + (if (string-match "^//\\([a-zA-Z]+@\\)?\\([^:]+\\):\\(.*\\)" folder) + ;; ange-ftp or efs or tramp access + (let ((user (or (match-string 1 folder) (user-login-name))) + (host (match-string 2 folder)) + (file (match-string 3 folder))) + (cond + ((featurep 'tramp) + ;; use tramp to access the file + (if (featurep 'xemacs) + (setq folder (format "[%s@%s]%s" user host file)) + (setq folder (format "/%s@%s:%s" user host file)))) + (t + ;; use ange-ftp or efs + (require (if (featurep 'xemacs) 'efs 'ange-ftp)) + (setq folder (format "/%s@%s:%s" user host file)))))) + (when folder + (funcall (cdr (assq 'vm org-link-frame-setup)) folder readonly) + (sit-for 0.1) + (when article + (vm-select-folder-buffer) + (widen) + (let ((case-fold-search t)) + (goto-char (point-min)) + (if (not (re-search-forward + (concat "^" "message-id: *" (regexp-quote article)))) + (error "Could not find the specified message in this folder")) + (vm-isearch-update) + (vm-isearch-narrow) + (vm-beginning-of-message) + (vm-summarize))))) + +(defun org-follow-wl-link (folder article) + "Follow a Wanderlust link to FOLDER and ARTICLE." + (setq article (org-add-angle-brackets article)) + (wl-summary-goto-folder-subr folder 'no-sync t nil t nil nil) + (if article (wl-summary-jump-to-msg-by-message-id article)) + (wl-summary-redisplay)) + +(defun org-follow-rmail-link (folder article) + "Follow an RMAIL link to FOLDER and ARTICLE." + (setq article (org-add-angle-brackets article)) + (let (message-number) + (save-excursion + (save-window-excursion + (rmail (if (string= folder "RMAIL") rmail-file-name folder)) + (setq message-number + (save-restriction + (widen) + (goto-char (point-max)) + (if (re-search-backward + (concat "^Message-ID:\\s-+" (regexp-quote + (or article ""))) + nil t) + (rmail-what-message)))))) + (if message-number + (progn + (rmail (if (string= folder "RMAIL") rmail-file-name folder)) + (rmail-show-message message-number) + message-number) + (error "Message not found")))) + +;;; mh-e integration based on planner-mode +(defun org-mhe-get-message-real-folder () + "Return the name of the current message real folder, so if you use +sequences, it will now work." + (save-excursion + (let* ((folder + (if (equal major-mode 'mh-folder-mode) + mh-current-folder + ;; Refer to the show buffer + mh-show-folder-buffer)) + (end-index + (if (boundp 'mh-index-folder) + (min (length mh-index-folder) (length folder)))) + ) + ;; a simple test on mh-index-data does not work, because + ;; mh-index-data is always nil in a show buffer. + (if (and (boundp 'mh-index-folder) + (string= mh-index-folder (substring folder 0 end-index))) + (if (equal major-mode 'mh-show-mode) + (save-window-excursion + (when (buffer-live-p (get-buffer folder)) + (progn + (pop-to-buffer folder) + (org-mhe-get-message-folder-from-index) + ) + )) + (org-mhe-get-message-folder-from-index) + ) + folder + ) + ))) + +(defun org-mhe-get-message-folder-from-index () + "Returns the name of the message folder in a index folder buffer." + (save-excursion + (mh-index-previous-folder) + (re-search-forward "^\\(+.*\\)$" nil t) + (message (match-string 1)))) + +(defun org-mhe-get-message-folder () + "Return the name of the current message folder. Be careful if you +use sequences." + (save-excursion + (if (equal major-mode 'mh-folder-mode) + mh-current-folder + ;; Refer to the show buffer + mh-show-folder-buffer))) + +(defun org-mhe-get-message-num () + "Return the number of the current message. Be careful if you +use sequences." + (save-excursion + (if (equal major-mode 'mh-folder-mode) + (mh-get-msg-num nil) + ;; Refer to the show buffer + (mh-show-buffer-message-number)))) + +(defun org-mhe-get-header (header) + "Return a header of the message in folder mode. This will create a +show buffer for the corresponding message. If you have a more clever +idea..." + (let* ((folder (org-mhe-get-message-folder)) + (num (org-mhe-get-message-num)) + (buffer (get-buffer-create (concat "show-" folder))) + (header-field)) + (with-current-buffer buffer + (mh-display-msg num folder) + (if (equal major-mode 'mh-folder-mode) + (mh-header-display) + (mh-show-header-display)) + (set-buffer buffer) + (setq header-field (mh-get-header-field header)) + (if (equal major-mode 'mh-folder-mode) + (mh-show) + (mh-show-show)) + header-field))) + +(defun org-follow-mhe-link (folder article) + "Follow an MHE link to FOLDER and ARTICLE. +If ARTICLE is nil FOLDER is shown. If the configuration variable +`org-mhe-search-all-folders' is t and `mh-searcher' is pick, +ARTICLE is searched in all folders. Indexed searches (swish++, +namazu, and others supported by MH-E) will always search in all +folders." + (require 'mh-e) + (require 'mh-search) + (require 'mh-utils) + (mh-find-path) + (if (not article) + (mh-visit-folder (mh-normalize-folder-name folder)) + (setq article (org-add-angle-brackets article)) + (mh-search-choose) + (if (equal mh-searcher 'pick) + (progn + (mh-search folder (list "--message-id" article)) + (when (and org-mhe-search-all-folders + (not (org-mhe-get-message-real-folder))) + (kill-this-buffer) + (mh-search "+" (list "--message-id" article)))) + (mh-search "+" article)) + (if (org-mhe-get-message-real-folder) + (mh-show-msg 1) + (kill-this-buffer) + (error "Message not found")))) + +;;; BibTeX links + +;; Use the custom search meachnism to construct and use search strings for +;; file links to BibTeX database entries. + +(defun org-create-file-search-in-bibtex () + "Create the search string and description for a BibTeX database entry." + (when (eq major-mode 'bibtex-mode) + ;; yes, we want to construct this search string. + ;; Make a good description for this entry, using names, year and the title + ;; Put it into the `description' variable which is dynamically scoped. + (let ((bibtex-autokey-names 1) + (bibtex-autokey-names-stretch 1) + (bibtex-autokey-name-case-convert-function 'identity) + (bibtex-autokey-name-separator " & ") + (bibtex-autokey-additional-names " et al.") + (bibtex-autokey-year-length 4) + (bibtex-autokey-name-year-separator " ") + (bibtex-autokey-titlewords 3) + (bibtex-autokey-titleword-separator " ") + (bibtex-autokey-titleword-case-convert-function 'identity) + (bibtex-autokey-titleword-length 'infty) + (bibtex-autokey-year-title-separator ": ")) + (setq description (bibtex-generate-autokey))) + ;; Now parse the entry, get the key and return it. + (save-excursion + (bibtex-beginning-of-entry) + (cdr (assoc "=key=" (bibtex-parse-entry)))))) + +(defun org-execute-file-search-in-bibtex (s) + "Find the link search string S as a key for a database entry." + (when (eq major-mode 'bibtex-mode) + ;; Yes, we want to do the search in this file. + ;; We construct a regexp that searches for "@entrytype{" followed by the key + (goto-char (point-min)) + (and (re-search-forward (concat "@[a-zA-Z]+[ \t\n]*{[ \t\n]*" + (regexp-quote s) "[ \t\n]*,") nil t) + (goto-char (match-beginning 0))) + (if (and (match-beginning 0) (equal current-prefix-arg '(16))) + ;; Use double prefix to indicate that any web link should be browsed + (let ((b (current-buffer)) (p (point))) + ;; Restore the window configuration because we just use the web link + (set-window-configuration org-window-config-before-follow-link) + (save-excursion (set-buffer b) (goto-char p) + (bibtex-url))) + (recenter 0)) ; Move entry start to beginning of window + ;; return t to indicate that the search is done. + t)) + +;; Finally add the functions to the right hooks. +(add-hook 'org-create-file-search-functions 'org-create-file-search-in-bibtex) +(add-hook 'org-execute-file-search-functions 'org-execute-file-search-in-bibtex) + +;; end of Bibtex link setup + + +;; FIXME: This function can be removed, I think. +(defun org-upgrade-old-links (&optional query-description) + "Transfer old <...> style links to new [[...]] style links. +With arg query-description, ask at each match for a description text to use +for this link." + (interactive (list (y-or-n-p "Would you like to be queried for a description at each link?"))) + (save-excursion + (goto-char (point-min)) + (let ((re (concat "\\([^[]\\)<\\(" + "\\(" (mapconcat 'identity org-link-types "\\|") + "\\):" + "[^" org-non-link-chars "]+\\)>")) + l1 l2 (cnt 0)) + (while (re-search-forward re nil t) + (setq cnt (1+ cnt) + l1 (org-match-string-no-properties 2) + l2 (save-match-data (org-link-escape l1))) + (when query-description (setq l1 (read-string "Desc: " l1))) + (if (equal l1 l2) + (replace-match (concat (match-string 1) "[[" l1 "]]") t t) + (replace-match (concat (match-string 1) "[[" l2 "][" l1 "]]") t t))) + (message "%d matches have beed treated" cnt)))) + +;;; Following file links + +(defun org-open-file (path &optional in-emacs line search) + "Open the file at PATH. +First, this expands any special file name abbreviations. Then the +configuration variable `org-file-apps' is checked if it contains an +entry for this file type, and if yes, the corresponding command is launched. +If no application is found, Emacs simply visits the file. +With optional argument IN-EMACS, Emacs will visit the file. +Optional LINE specifies a line to go to, optional SEARCH a string to +search for. If LINE or SEARCH is given, the file will always be +opened in Emacs. +If the file does not exist, an error is thrown." + (setq in-emacs (or in-emacs line search)) + (let* ((file (if (equal path "") + buffer-file-name + (substitute-in-file-name (expand-file-name path)))) + (apps (append org-file-apps (org-default-apps))) + (remp (and (assq 'remote apps) (org-file-remote-p file))) + (dirp (if remp nil (file-directory-p file))) + (dfile (downcase file)) + (old-buffer (current-buffer)) + (old-pos (point)) + (old-mode major-mode) + ext cmd) + (if (string-match "^.*\\.\\([a-zA-Z0-9]+\\.gz\\)$" dfile) + (setq ext (match-string 1 dfile)) + (if (string-match "^.*\\.\\([a-zA-Z0-9]+\\)$" dfile) + (setq ext (match-string 1 dfile)))) + (if in-emacs + (setq cmd 'emacs) + (setq cmd (or (and remp (cdr (assoc 'remote apps))) + (and dirp (cdr (assoc 'directory apps))) + (cdr (assoc ext apps)) + (cdr (assoc t apps))))) + (when (eq cmd 'mailcap) + (require 'mailcap) + (mailcap-parse-mailcaps) + (let* ((mime-type (mailcap-extension-to-mime (or ext ""))) + (command (mailcap-mime-info mime-type))) + (if (stringp command) + (setq cmd command) + (setq cmd 'emacs)))) + (if (and (not (eq cmd 'emacs)) ; Emacs has not problems with non-ex files + (not (file-exists-p file)) + (not org-open-non-existing-files)) + (error "No such file: %s" file)) + (cond + ((and (stringp cmd) (not (string-match "^\\s-*$" cmd))) + ;; Remove quotes around the file name - we'll use shell-quote-argument. + (if (string-match "['\"]%s['\"]" cmd) + (setq cmd (replace-match "%s" t t cmd))) + (setq cmd (format cmd (shell-quote-argument file))) + (save-window-excursion + (shell-command (concat cmd " &")))) + ((or (stringp cmd) + (eq cmd 'emacs)) + (funcall (cdr (assq 'file org-link-frame-setup)) file) + (if line (goto-line line) + (if search (org-link-search search)))) + ((consp cmd) + (eval cmd)) + (t (funcall (cdr (assq 'file org-link-frame-setup)) file))) + (and (org-mode-p) (eq old-mode 'org-mode) + (or (not (equal old-buffer (current-buffer))) + (not (equal old-pos (point)))) + (org-mark-ring-push old-pos old-buffer)))) + +(defun org-default-apps () + "Return the default applications for this operating system." + (cond + ((eq system-type 'darwin) + org-file-apps-defaults-macosx) + ((eq system-type 'windows-nt) + org-file-apps-defaults-windowsnt) + (t org-file-apps-defaults-gnu))) + +(defun org-expand-file-name (path) + "Replace special path abbreviations and expand the file name." + (expand-file-name path)) + +(defvar ange-ftp-name-format) ; to silence the XEmacs compiler. +(defun org-file-remote-p (file) + "Test whether FILE specifies a location on a remote system. +Return non-nil if the location is indeed remote. + +For example, the filename \"/user@host:/foo\" specifies a location +on the system \"/user@host:\"." + (cond ((fboundp 'file-remote-p) + (file-remote-p file)) + ((fboundp 'tramp-handle-file-remote-p) + (tramp-handle-file-remote-p file)) + ((and (boundp 'ange-ftp-name-format) + (string-match (car ange-ftp-name-format) file)) + t) + (t nil))) + + +;;;; Hooks for remember.el + +;;;###autoload +(defun org-remember-annotation () + "Return a link to the current location as an annotation for remember.el. +If you are using Org-mode files as target for data storage with +remember.el, then the annotations should include a link compatible with the +conventions in Org-mode. This function returns such a link." + (org-store-link nil)) + +(defconst org-remember-help +"Select a destination location for the note. +UP/DOWN=headline TAB=cycle visibility [Q]uit RET//=Store +RET at beg-of-buf -> Append to file as level 2 headline +RET on headline -> Store as sublevel entry to current headline +/ -> before/after current headline, same headings level") + +;;;###autoload +(defun org-remember-apply-template () + "Initialize *remember* buffer with template, invoke `org-mode'. +This function should be placed into `remember-mode-hook' and in fact requires +to be run from that hook to fucntion properly." + (if org-remember-templates + + (let* ((entry (if (= (length org-remember-templates) 1) + (cdar org-remember-templates) + (message "Select template: %s" + (mapconcat + (lambda (x) (char-to-string (car x))) + org-remember-templates " ")) + (cdr (assoc (read-char-exclusive) org-remember-templates)))) + (tpl (car entry)) + (file (if (consp (cdr entry)) (nth 1 entry))) + (v-t (format-time-string (car org-time-stamp-formats) (org-current-time))) + (v-T (format-time-string (cdr org-time-stamp-formats) (org-current-time))) + (v-u (concat "[" (substring v-t 1 -1) "]")) + (v-U (concat "[" (substring v-T 1 -1) "]")) + (v-a annotation) ; defined in `remember-mode' + (v-i initial) ; defined in `remember-mode' + (v-n user-full-name) + ) + (unless tpl (setq tpl "") (message "No template") (ding)) + (insert tpl) (goto-char (point-min)) + (while (re-search-forward "%\\([tTuTai]\\)" nil t) + (when (and initial (equal (match-string 0) "%i")) + (save-match-data + (let* ((lead (buffer-substring + (point-at-bol) (match-beginning 0)))) + (setq v-i (mapconcat 'identity + (org-split-string initial "\n") + (concat "\n" lead)))))) + (replace-match + (or (eval (intern (concat "v-" (match-string 1)))) "") + t t)) + (let ((org-startup-folded nil) + (org-startup-with-deadline-check nil)) + (org-mode)) + (if (and file (string-match "\\S-" file) (not (file-directory-p file))) + (org-set-local 'org-default-notes-file file)) + (goto-char (point-min)) + (if (re-search-forward "%\\?" nil t) (replace-match ""))) + (let ((org-startup-folded nil) + (org-startup-with-deadline-check nil)) + (org-mode))) + (org-set-local 'org-finish-function 'remember-buffer)) + +;;;###autoload +(defun org-remember-handler () + "Store stuff from remember.el into an org file. +First prompts for an org file. If the user just presses return, the value +of `org-default-notes-file' is used. +Then the command offers the headings tree of the selected file in order to +file the text at a specific location. +You can either immediately press RET to get the note appended to the +file, or you can use vertical cursor motion and visibility cycling (TAB) to +find a better place. Then press RET or or in insert the note. + +Key Cursor position Note gets inserted +----------------------------------------------------------------------------- +RET buffer-start as level 2 heading at end of file +RET on headline as sublevel of the heading at cursor +RET no heading at cursor position, level taken from context. + Or use prefix arg to specify level manually. + on headline as same level, before current heading + on headline as same level, after current heading + +So the fastest way to store the note is to press RET RET to append it to +the default file. This way your current train of thought is not +interrupted, in accordance with the principles of remember.el. But with +little extra effort, you can push it directly to the correct location. + +Before being stored away, the function ensures that the text has a +headline, i.e. a first line that starts with a \"*\". If not, a headline +is constructed from the current date and some additional data. + +If the variable `org-adapt-indentation' is non-nil, the entire text is +also indented so that it starts in the same column as the headline +\(i.e. after the stars). + +See also the variable `org-reverse-note-order'." + (catch 'quit + (let* ((txt (buffer-substring (point-min) (point-max))) + (fastp current-prefix-arg) + (file (if fastp org-default-notes-file (org-get-org-file))) + (visiting (find-buffer-visiting file)) + (org-startup-with-deadline-check nil) + (org-startup-folded nil) + (org-startup-align-all-tables nil) + spos level indent reversed) + ;; Modify text so that it becomes a nice subtree which can be inserted + ;; into an org tree. + (let* ((lines (split-string txt "\n")) + first) + ;; remove empty lines at the beginning + (while (and lines (string-match "^[ \t]*\n" (car lines))) + (setq lines (cdr lines))) + (setq first (car lines) lines (cdr lines)) + (if (string-match "^\\*+" first) + ;; Is already a headline + (setq indent nil) + ;; We need to add a headline: Use time and first buffer line + (setq lines (cons first lines) + first (concat "* " (current-time-string) + " (" (remember-buffer-desc) ")") + indent " ")) + (if (and org-adapt-indentation indent) + (setq lines (mapcar (lambda (x) (concat indent x)) lines))) + (setq txt (concat first "\n" + (mapconcat 'identity lines "\n")))) + ;; Find the file + (if (not visiting) + (find-file-noselect file)) + (with-current-buffer (get-file-buffer file) + (save-excursion (and (goto-char (point-min)) + (not (re-search-forward "^\\* " nil t)) + (insert "\n* Notes\n"))) + (setq reversed (org-notes-order-reversed-p)) + (save-excursion + (save-restriction + (widen) + ;; Ask the User for a location + (setq spos (if fastp 1 (org-get-location + (current-buffer) + org-remember-help))) + (if (not spos) (throw 'quit nil)) ; return nil to show we did + ; not handle this note + (goto-char spos) + (cond ((and (bobp) (not reversed)) + ;; Put it at the end, as level 2 + (save-restriction + (widen) + (goto-char (point-max)) + (if (not (bolp)) (newline)) + (org-paste-subtree 2 txt))) + ((and (bobp) reversed) + ;; Put it at the start, as level 1 + (save-restriction + (widen) + (goto-char (point-min)) + (re-search-forward "^\\*" nil t) + (beginning-of-line 1) + (org-paste-subtree 1 txt))) + ((and (org-on-heading-p nil) (not current-prefix-arg)) + ;; Put it below this entry, at the beg/end of the subtree + (org-back-to-heading) + (setq level (funcall outline-level)) + (if reversed + (outline-end-of-heading) + (outline-end-of-subtree)) + (if (not (bolp)) (newline)) + (beginning-of-line 1) + (org-paste-subtree (org-get-legal-level level 1) txt)) + (t + ;; Put it right there, with automatic level determined by + ;; org-paste-subtree or from prefix arg + (org-paste-subtree current-prefix-arg txt))) + (when remember-save-after-remembering + (save-buffer) + (if (not visiting) (kill-buffer (current-buffer))))))))) + t) ;; return t to indicate that we took care of this note. + +(defun org-get-org-file () + "Read a filename, with default directory `org-directory'." + (let ((default (or org-default-notes-file remember-data-file))) + (read-file-name (format "File name [%s]: " default) + (file-name-as-directory org-directory) + default))) + +(defun org-notes-order-reversed-p () + "Check if the current file should receive notes in reversed order." + (cond + ((not org-reverse-note-order) nil) + ((eq t org-reverse-note-order) t) + ((not (listp org-reverse-note-order)) nil) + (t (catch 'exit + (let ((all org-reverse-note-order) + entry) + (while (setq entry (pop all)) + (if (string-match (car entry) buffer-file-name) + (throw 'exit (cdr entry)))) + nil))))) + +;;;; Dynamic blocks + +(defun org-find-dblock (name) + "Find the first dynamic block with name NAME in the buffer. +If not found, stay at current position and return nil." + (let (pos) + (save-excursion + (goto-char (point-min)) + (setq pos (and (re-search-forward (concat "^#\\+BEGIN:[ \t]+" name "\\>") + nil t) + (match-beginning 0)))) + (if pos (goto-char pos)) + pos)) + +(defconst org-dblock-start-re + "^#\\+BEGIN:[ \t]+\\(\\S-+\\)\\([ \t]+\\(.*\\)\\)?" + "Matches the startline of a dynamic block, with parameters.") + +(defconst org-dblock-end-re "^#\\+END\\([: \t\r\n]\\|$\\)" + "Matches the end of a dyhamic block.") + +(defun org-create-dblock (plist) + "Create a dynamic block section, with parameters taken from PLIST. +PLIST must containe a :name entry which is used as name of the block." + (unless (bolp) (newline)) + (let ((name (plist-get plist :name))) + (insert "#+BEGIN: " name) + (while plist + (if (eq (car plist) :name) + (setq plist (cddr plist)) + (insert " " (prin1-to-string (pop plist))))) + (insert "\n\n#+END:\n") + (beginning-of-line -2))) + +(defun org-prepare-dblock () + "Prepare dynamic block for refresh. +This empties the block, puts the cursor at the insert position and returns +the property list including an extra property :name with the block name." + (unless (looking-at org-dblock-start-re) + (error "Not at a dynamic block")) + (let* ((begdel (1+ (match-end 0))) + (name (match-string 1)) + (params (append (list :name name) + (read (concat "(" (match-string 3) ")"))))) + (unless (re-search-forward org-dblock-end-re nil t) + (error "Dynamic block not terminated")) + (delete-region begdel (match-beginning 0)) + (goto-char begdel) + (open-line 1) + params)) + +(defun org-map-dblocks (&optional command) + "Apply COMMAND to all dynamic blocks in the current buffer. +If COMMAND is not given, use `org-update-dblock'." + (let ((cmd (or command 'org-update-dblock)) + pos) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward org-dblock-start-re nil t) + (goto-char (setq pos (match-beginning 0))) + (condition-case nil + (funcall cmd) + (error (message "Error during update of dynamic block"))) + (goto-char pos) + (unless (re-search-forward org-dblock-end-re nil t) + (error "Dynamic block not terminated")))))) + +(defun org-dblock-update (&optional arg) + "User command for updating dynamic blocks. +Update the dynamic block at point. With prefix ARG, update all dynamic +blocks in the buffer." + (interactive "P") + (if arg + (org-update-all-dblocks) + (or (looking-at org-dblock-start-re) + (org-beginning-of-dblock)) + (org-update-dblock))) + +(defun org-update-dblock () + "Update the dynamic block at point +This means to empty the block, parse for parameters and then call +the correct writing function." + (let* ((pos (point)) + (params (org-prepare-dblock)) + (name (plist-get params :name)) + (cmd (intern (concat "org-dblock-write:" name)))) + (funcall cmd params) + (goto-char pos))) + +(defun org-beginning-of-dblock () + "Find the beginning of the dynamic block at point. +Error if there is no scuh block at point." + (let ((pos (point)) + beg) + (end-of-line 1) + (if (and (re-search-backward org-dblock-start-re nil t) + (setq beg (match-beginning 0)) + (re-search-forward org-dblock-end-re nil t) + (> (match-end 0) pos)) + (goto-char beg) + (goto-char pos) + (error "Not in a dynamic block")))) + +(defun org-update-all-dblocks () + "Update all dynamic blocks in the buffer. +This function can be used in a hook." + (when (org-mode-p) + (org-map-dblocks 'org-update-dblock))) + + +;;;; Completion + +(defun org-complete (&optional arg) + "Perform completion on word at point. +At the beginning of a headline, this completes TODO keywords as given in +`org-todo-keywords'. +If the current word is preceded by a backslash, completes the TeX symbols +that are supported for HTML support. +If the current word is preceded by \"#+\", completes special words for +setting file options. +In the line after \"#+STARTUP:, complete valid keywords.\" +At all other locations, this simply calls `ispell-complete-word'." + (interactive "P") + (catch 'exit + (let* ((end (point)) + (beg1 (save-excursion + (skip-chars-backward "a-zA-Z_@0-9") + (point))) + (beg (save-excursion + (skip-chars-backward "a-zA-Z0-9_:$") + (point))) + (confirm (lambda (x) (stringp (car x)))) + (camel (equal (char-before beg) ?*)) + (tag (equal (char-before beg1) ?:)) + (texp (equal (char-before beg) ?\\)) + (link (equal (char-before beg) ?\[)) + (opt (equal (buffer-substring (max (point-at-bol) (- beg 2)) + beg) + "#+")) + (startup (string-match "^#\\+STARTUP:.*" + (buffer-substring (point-at-bol) (point)))) + (completion-ignore-case opt) + (type nil) + (tbl nil) + (table (cond + (opt + (setq type :opt) + (mapcar (lambda (x) + (string-match "^#\\+\\(\\([A-Z_]+:?\\).*\\)" x) + (cons (match-string 2 x) (match-string 1 x))) + (org-split-string (org-get-current-options) "\n"))) + (startup + (setq type :startup) + org-startup-options) + (link (append org-link-abbrev-alist-local + org-link-abbrev-alist)) + (texp + (setq type :tex) + org-html-entities) + ((string-match "\\`\\*+[ \t]*\\'" + (buffer-substring (point-at-bol) beg)) + (setq type :todo) + (mapcar 'list org-todo-keywords)) + (camel + (setq type :camel) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward org-todo-line-regexp nil t) + (push (list + (if org-file-link-context-use-camel-case + (org-make-org-heading-camel (match-string 3) t) + (org-make-org-heading-search-string + (match-string 3) t))) + tbl))) + tbl) + (tag (setq type :tag beg beg1) + (or org-tag-alist (org-get-buffer-tags))) + (t (progn (ispell-complete-word arg) (throw 'exit nil))))) + (pattern (buffer-substring-no-properties beg end)) + (completion (try-completion pattern table confirm))) + (cond ((eq completion t) + (if (equal type :opt) + (insert (substring (cdr (assoc (upcase pattern) table)) + (length pattern))) + (if (equal type :tag) (insert ":")))) + ((null completion) + (message "Can't find completion for \"%s\"" pattern) + (ding)) + ((not (string= pattern completion)) + (delete-region beg end) + (if (string-match " +$" completion) + (setq completion (replace-match "" t t completion))) + (insert completion) + (if (get-buffer-window "*Completions*") + (delete-window (get-buffer-window "*Completions*"))) + (if (assoc completion table) + (if (eq type :todo) (insert " ") + (if (eq type :tag) (insert ":")))) + (if (and (equal type :opt) (assoc completion table)) + (message "%s" (substitute-command-keys + "Press \\[org-complete] again to insert example settings")))) + (t + (message "Making completion list...") + (let ((list (sort (all-completions pattern table confirm) + 'string<))) + (with-output-to-temp-buffer "*Completions*" + (condition-case nil + ;; Protection needed for XEmacs and emacs 21 + (display-completion-list list pattern) + (error (display-completion-list list))))) + (message "Making completion list...%s" "done")))))) + +;;;; TODO, DEADLINE, Comments + +(defun org-toggle-comment () + "Change the COMMENT state of an entry." + (interactive) + (save-excursion + (org-back-to-heading) + (if (looking-at (concat outline-regexp + "\\( +\\<" org-comment-string "\\>\\)")) + (replace-match "" t t nil 1) + (if (looking-at outline-regexp) + (progn + (goto-char (match-end 0)) + (insert " " org-comment-string)))))) + +(defvar org-last-todo-state-is-todo nil + "This is non-nil when the last TODO state change led to a TODO state. +If the last change removed the TODO tag or switched to DONE, then +this is nil.") + +(defun org-todo (&optional arg) + "Change the TODO state of an item. +The state of an item is given by a keyword at the start of the heading, +like + *** TODO Write paper + *** DONE Call mom + +The different keywords are specified in the variable `org-todo-keywords'. +By default the available states are \"TODO\" and \"DONE\". +So for this example: when the item starts with TODO, it is changed to DONE. +When it starts with DONE, the DONE is removed. And when neither TODO nor +DONE are present, add TODO at the beginning of the heading. + +With prefix arg, use completion to determine the new state. With numeric +prefix arg, switch to that state." + (interactive "P") + (save-excursion + (org-back-to-heading) + (if (looking-at outline-regexp) (goto-char (match-end 0))) + (or (looking-at (concat " +" org-todo-regexp " *")) + (looking-at " *")) + (let* ((this (match-string 1)) + (completion-ignore-case t) + (member (member this org-todo-keywords)) + (tail (cdr member)) + (state (cond + ((equal arg '(4)) + ;; Read a state with completion + (completing-read "State: " (mapcar (lambda(x) (list x)) + org-todo-keywords) + nil t)) + ((eq arg 'right) + (if this + (if tail (car tail) nil) + (car org-todo-keywords))) + ((eq arg 'left) + (if (equal member org-todo-keywords) + nil + (if this + (nth (- (length org-todo-keywords) (length tail) 2) + org-todo-keywords) + org-done-string))) + (arg + ;; user requests a specific state + (nth (1- (prefix-numeric-value arg)) + org-todo-keywords)) + ((null member) (car org-todo-keywords)) + ((null tail) nil) ;; -> first entry + ((eq org-todo-interpretation 'sequence) + (car tail)) + ((memq org-todo-interpretation '(type priority)) + (if (eq this-command last-command) + (car tail) + (if (> (length tail) 0) org-done-string nil))) + (t nil))) + (next (if state (concat " " state " ") " "))) + (replace-match next t t) + (setq org-last-todo-state-is-todo + (not (equal state org-done-string))) + (when org-log-done + (if (equal state org-done-string) + (org-add-planning-info 'closed (org-current-time) 'scheduled) + (if (not this) + (org-add-planning-info nil nil 'closed)))) + ;; Fixup tag positioning + (and org-auto-align-tags (org-set-tags nil t)) + (run-hooks 'org-after-todo-state-change-hook))) + ;; Fixup cursor location if close to the keyword + (if (and (outline-on-heading-p) + (not (bolp)) + (save-excursion (beginning-of-line 1) + (looking-at org-todo-line-regexp)) + (< (point) (+ 2 (or (match-end 2) (match-end 1))))) + (progn + (goto-char (or (match-end 2) (match-end 1))) + (just-one-space)))) + +(defun org-show-todo-tree (arg) + "Make a compact tree which shows all headlines marked with TODO. +The tree will show the lines where the regexp matches, and all higher +headlines above the match. +With \\[universal-argument] prefix, also show the DONE entries. +With a numeric prefix N, construct a sparse tree for the Nth element +of `org-todo-keywords'." + (interactive "P") + (let ((case-fold-search nil) + (kwd-re + (cond ((null arg) org-not-done-regexp) + ((equal arg '(4)) org-todo-regexp) + ((<= (prefix-numeric-value arg) (length org-todo-keywords)) + (regexp-quote (nth (1- (prefix-numeric-value arg)) + org-todo-keywords))) + (t (error "Invalid prefix argument: %s" arg))))) + (message "%d TODO entries found" + (org-occur (concat "^" outline-regexp " +" kwd-re ))))) + +(defun org-deadline () + "Insert the DEADLINE: string to make a deadline. +A timestamp is also inserted - use \\[org-timestamp-up] and \\[org-timestamp-down] +to modify it to the correct date." + (interactive) + (org-add-planning-info 'deadline nil 'closed)) + +(defun org-schedule () + "Insert the SCHEDULED: string to schedule a TODO item. +A timestamp is also inserted - use \\[org-timestamp-up] and \\[org-timestamp-down] +to modify it to the correct date." + (interactive) + (org-add-planning-info 'scheduled nil 'closed)) + +(defun org-add-planning-info (what &optional time &rest remove) + "Insert new timestamp with keyword in the line directly after the headline. +WHAT indicates what kind of time stamp to add. TIME indicated the time to use. +If non is given, the user is prompted for a date. +REMOVE indicates what kind of entries to remove. An old WHAT entry will also +be removed." + (interactive) + (when what (setq time (or time (org-read-date nil 'to-time)))) + (when (and org-insert-labeled-timestamps-at-point + (member what '(scheduled deadline))) + (insert + (if (eq what 'scheduled) org-scheduled-string org-deadline-string) " ") + (org-insert-time-stamp time) + (setq what nil)) + (save-excursion + (save-restriction + (let (col list elt ts buffer-invisibility-spec) + (org-back-to-heading t) + (looking-at (concat outline-regexp "\\( *\\)[^\r\n]*")) + (goto-char (match-end 1)) + (setq col (current-column)) + (goto-char (1+ (match-end 0))) + (if (and (not (looking-at outline-regexp)) + (looking-at (concat "[^\r\n]*?" org-keyword-time-regexp + "[^\r\n]*")) + (not (equal (match-string 1) org-clock-string))) + (narrow-to-region (match-beginning 0) (match-end 0)) + (insert "\n") + (backward-char 1) + (narrow-to-region (point) (point)) + (indent-to-column col)) + ;; Check if we have to remove something. + (setq list (cons what remove)) + (while list + (setq elt (pop list)) + (goto-char (point-min)) + (when (or (and (eq elt 'scheduled) + (re-search-forward org-scheduled-time-regexp nil t)) + (and (eq elt 'deadline) + (re-search-forward org-deadline-time-regexp nil t)) + (and (eq elt 'closed) + (re-search-forward org-closed-time-regexp nil t))) + (replace-match "") + (if (looking-at "--+<[^>]+>") (replace-match "")) + (if (looking-at " +") (replace-match "")))) + (goto-char (point-max)) + (when what + (insert + (if (not (equal (char-before) ?\ )) " " "") + (cond ((eq what 'scheduled) org-scheduled-string) + ((eq what 'deadline) org-deadline-string) + ((eq what 'closed) org-closed-string)) + " ") + (org-insert-time-stamp time nil (eq what 'closed)) + (end-of-line 1) + (and (eq what 'closed) (org-add-log-maybe 'done))) + (goto-char (point-min)) + (widen) + (if (looking-at "[ \t]+\r?\n") + (replace-match "")) + ts)))) + +(defvar org-log-note-marker (make-marker)) +(defvar org-log-note-purpose nil) +(defvar org-log-note-window-configuration nil) + +(defun org-add-log-maybe (&optional purpose) + (when (and (listp org-log-done) + (memq purpose org-log-done)) + (move-marker org-log-note-marker (point)) + (setq org-log-note-purpose purpose) + (add-hook 'post-command-hook 'org-add-log-note 'append))) + +(defun org-add-log-note (&optional purpose) + "Pop up a window for taking a note, and add this note later at point." + (remove-hook 'post-command-hook 'org-add-log-note) + (setq org-log-note-window-configuration (current-window-configuration)) + (delete-other-windows) + (switch-to-buffer (marker-buffer org-log-note-marker)) + (goto-char org-log-note-marker) + (switch-to-buffer-other-window "*Org Note*") + (erase-buffer) + (org-mode) + (insert (format "# Insert note for %s, finish with C-c C-c.\n\n" + (cond + ((eq org-log-note-purpose 'clock-out) "stopped clock") + ((eq org-log-note-purpose 'done) "closed todo item") + (t (error "This should not happen"))))) + (org-set-local 'org-finish-function 'org-store-log-note)) + +(defun org-store-log-note () + "Finish taking a log note, and insert it to where it belongs." + (let ((txt (buffer-string)) + (note (cdr (assq org-log-note-purpose org-log-note-headings))) + lines ind) + (kill-buffer (current-buffer)) + (if (string-match "^#.*\n[ \t\\n]*" txt) + (setq txt (replace-match "" t t txt))) + (when (string-match "\\S-" txt) + (if (string-match "\\s-+\\'" txt) + (setq txt (replace-match "" t t txt))) + (setq lines (org-split-string txt "\n")) + (when (and note (string-match "\\S-" note)) + (setq note + (org-replace-escapes + note + (list (cons "%u" user-login-name) + (cons "%U" user-full-name) + (cons "%t" (format-time-string + (org-time-stamp-format 'long 'inactive) + (current-time)))))) + (push note lines)) + (save-excursion + (set-buffer (marker-buffer org-log-note-marker)) + (save-excursion + (goto-char org-log-note-marker) + (if (not (bolp)) (newline)) + (indent-relative t) + (setq ind (concat (buffer-substring (point-at-bol) (point)) " ")) + (insert " - " (pop lines)) + (while lines + (insert "\n" ind (pop lines)))))) + (set-window-configuration org-log-note-window-configuration))) + +(defvar org-occur-highlights nil) +(make-variable-buffer-local 'org-occur-highlights) + +(defun org-occur (regexp &optional keep-previous callback) + "Make a compact tree which shows all matches of REGEXP. +The tree will show the lines where the regexp matches, and all higher +headlines above the match. It will also show the heading after the match, +to make sure editing the matching entry is easy. +If KEEP-PREVIOUS is non-nil, highlighting and exposing done by a previous +call to `org-occur' will be kept, to allow stacking of calls to this +command. +If CALLBACK is non-nil, it is a function which is called to confirm +that the match should indeed be shown." + (interactive "sRegexp: \nP") + (or keep-previous (org-remove-occur-highlights nil nil t)) + (let ((cnt 0)) + (save-excursion + (goto-char (point-min)) + (if (or (not keep-previous) ; do not want to keep + (not org-occur-highlights)) ; no previous matches + ;; hide everything + (org-overview)) + (while (re-search-forward regexp nil t) + (when (or (not callback) + (save-match-data (funcall callback))) + (setq cnt (1+ cnt)) + (org-highlight-new-match (match-beginning 0) (match-end 0)) + (org-show-context 'occur-tree)))) + (when org-remove-highlights-with-change + (org-add-hook 'before-change-functions 'org-remove-occur-highlights + nil 'local)) + (unless org-sparse-tree-open-archived-trees + (org-hide-archived-subtrees (point-min) (point-max))) + (run-hooks 'org-occur-hook) + (if (interactive-p) + (message "%d match(es) for regexp %s" cnt regexp)) + cnt)) + +(defun org-show-context (&optional key siblings) + "Make sure point and context and visible. +How much context is shown depends upon the variables +`org-show-hierarchy-above' and `org-show-following-heading'. +When SIBLINGS is non-nil, show all siblings on each hierarchy level." + (let ((heading-p (org-on-heading-p t)) + (hierarchy-p (org-get-alist-option org-show-hierarchy-above key)) + (following-p (org-get-alist-option org-show-following-heading key))) + (catch 'exit + ;; Show heading or entry text + (if heading-p + (org-flag-heading nil) ; only show the heading + (and (or (org-invisible-p) (org-invisible-p2)) + (org-show-hidden-entry))) ; show entire entry + (when following-p + ;; Show next sibling, or heading below text + (save-excursion + (and (if heading-p (org-goto-sibling) (outline-next-heading)) + (org-flag-heading nil)))) + (when hierarchy-p + ;; show all higher headings, possibly with siblings + (save-excursion + (while (and (condition-case nil + (progn (org-up-heading-all 1) t) + (error nil)) + (not (bobp))) + (org-flag-heading nil) + (when siblings + (save-excursion + (while (org-goto-sibling) (org-flag-heading nil))) + (save-excursion + (while (org-goto-sibling 'previous) + (org-flag-heading nil)))))))))) + +(defun org-reveal (&optional siblings) + "Show current entry, hierarchy above it, and the following headline. +This can be used to show a consistent set of context around locations +exposed with `org-show-hierarchy-above' or `org-show-following-heading' +not t for the search context. + +With optional argument SIBLINGS, on each level of the hierarchy all +siblings are shown. This repairs the tree structure so what it would +look like when opend with successive calls to `org-cycle'." + (interactive "P") + (let ((org-show-hierarchy-above t) + (org-show-following-heading t)) + (org-show-context nil siblings))) + + +(defun org-highlight-new-match (beg end) + "Highlight from BEG to END and mark the highlight is an occur headline." + (let ((ov (org-make-overlay beg end))) + (org-overlay-put ov 'face 'secondary-selection) + (push ov org-occur-highlights))) + +(defvar org-inhibit-highlight-removal nil) +(defun org-remove-occur-highlights (&optional beg end noremove) + "Remove the occur highlights from the buffer. +BEG and END are ignored. If NOREMOVE is nil, remove this function +from the `before-change-functions' in the current buffer." + (interactive) + (unless org-inhibit-highlight-removal + (mapc 'org-delete-overlay org-occur-highlights) + (setq org-occur-highlights nil) + (unless noremove + (remove-hook 'before-change-functions + 'org-remove-occur-highlights 'local)))) + +;;;; Priorities + +(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z]\\)\\] ?\\)" + "Regular expression matching the priority indicator.") + +(defvar org-remove-priority-next-time nil) + +(defun org-priority-up () + "Increase the priority of the current item." + (interactive) + (org-priority 'up)) + +(defun org-priority-down () + "Decrease the priority of the current item." + (interactive) + (org-priority 'down)) + +(defun org-priority (&optional action) + "Change the priority of an item by ARG. +ACTION can be set, up, or down." + (interactive) + (setq action (or action 'set)) + (let (current new news have remove) + (save-excursion + (org-back-to-heading) + (if (looking-at org-priority-regexp) + (setq current (string-to-char (match-string 2)) + have t) + (setq current org-default-priority)) + (cond + ((eq action 'set) + (message "Priority A-%c, SPC to remove: " org-lowest-priority) + (setq new (read-char-exclusive)) + (cond ((equal new ?\ ) (setq remove t)) + ((or (< (upcase new) ?A) (> (upcase new) org-lowest-priority)) + (error "Priority must be between `%c' and `%c'" + ?A org-lowest-priority)))) + ((eq action 'up) + (setq new (1- current))) + ((eq action 'down) + (setq new (1+ current))) + (t (error "Invalid action"))) + (setq new (min (max ?A (upcase new)) org-lowest-priority)) + (setq news (format "%c" new)) + (if have + (if remove + (replace-match "" t t nil 1) + (replace-match news t t nil 2)) + (if remove + (error "No priority cookie found in line") + (looking-at org-todo-line-regexp) + (if (match-end 2) + (progn + (goto-char (match-end 2)) + (insert " [#" news "]")) + (goto-char (match-beginning 3)) + (insert "[#" news "] "))))) + (if remove + (message "Priority removed") + (message "Priority of current item set to %s" news)))) + + +(defun org-get-priority (s) + "Find priority cookie and return priority." + (save-match-data + (if (not (string-match org-priority-regexp s)) + (* 1000 (- org-lowest-priority org-default-priority)) + (* 1000 (- org-lowest-priority + (string-to-char (match-string 2 s))))))) + +;;;; Tags + +(defun org-scan-tags (action matcher &optional todo-only) + "Scan headline tags with inheritance and produce output ACTION. +ACTION can be `sparse-tree' or `agenda'. MATCHER is a Lisp form to be +evaluated, testing if a given set of tags qualifies a headline for +inclusion. When TODO-ONLY is non-nil, only lines with a TODO keyword +are included in the output." + (let* ((re (concat "[\n\r]" outline-regexp " *\\(\\<\\(" + (mapconcat 'regexp-quote + (nreverse (cdr (reverse org-todo-keywords))) + "\\|") + "\\>\\)\\)? *\\(.*?\\)\\(:[A-Za-z_@0-9:]+:\\)?[ \t]*$")) + (props (list 'face nil + 'done-face 'org-done + 'undone-face nil + 'mouse-face 'highlight + 'org-not-done-regexp org-not-done-regexp + 'keymap org-agenda-keymap + 'help-echo + (format "mouse-2 or RET jump to org file %s" + (abbreviate-file-name buffer-file-name)))) + (case-fold-search nil) + lspos + tags tags-list tags-alist (llast 0) rtn level category i txt + todo marker entry) + (save-excursion + (goto-char (point-min)) + (when (eq action 'sparse-tree) (org-overview)) + (while (re-search-forward re nil t) + (catch :skip + (and (eq action 'agenda) (org-agenda-skip)) + (setq todo (if (match-end 1) (match-string 2)) + tags (if (match-end 4) (match-string 4))) + (goto-char (setq lspos (1+ (match-beginning 0)))) + (setq level (funcall outline-level) + category (org-get-category)) + (setq i llast llast level) + ;; remove tag lists from same and sublevels + (while (>= i level) + (when (setq entry (assoc i tags-alist)) + (setq tags-alist (delete entry tags-alist))) + (setq i (1- i))) + ;; add the nex tags + (when tags + (setq tags (mapcar 'downcase (org-split-string tags ":")) + tags-alist + (cons (cons level tags) tags-alist))) + ;; compile tags for current headline + (setq tags-list + (if org-use-tag-inheritance + (apply 'append (mapcar 'cdr tags-alist)) + tags)) + (when (and (or (not todo-only) todo) + (eval matcher) + (or (not org-agenda-skip-archived-trees) + (not (member org-archive-tag tags-list)))) + ;; list this headline + (if (eq action 'sparse-tree) + (progn + (org-show-context 'tags-tree)) + (setq txt (org-format-agenda-item + "" + (concat + (if org-tags-match-list-sublevels + (make-string (1- level) ?.) "") + (org-get-heading)) + category tags-list)) + (goto-char lspos) + (setq marker (org-agenda-new-marker)) + (org-add-props txt props + 'org-marker marker 'org-hd-marker marker 'category category) + (push txt rtn)) + ;; if we are to skip sublevels, jump to end of subtree + (or org-tags-match-list-sublevels (org-end-of-subtree t)))))) + (when (and (eq action 'sparse-tree) + (not org-sparse-tree-open-archived-trees)) + (org-hide-archived-subtrees (point-min) (point-max))) + (nreverse rtn))) + +(defvar todo-only) ;; dynamically scoped + +(defun org-tags-sparse-tree (&optional todo-only match) + "Create a sparse tree according to tags string MATCH. +MATCH can contain positive and negative selection of tags, like +\"+WORK+URGENT-WITHBOSS\". +If optional argument TODO_ONLY is non-nil, only select lines that are +also TODO lines." + (interactive "P") + (org-scan-tags 'sparse-tree (cdr (org-make-tags-matcher match)) todo-only)) + +;; FIXME: implement search for a specific level. +(defun org-make-tags-matcher (match) + "Create the TAGS//TODO matcher form for the selection string MATCH." + ;; todo-only is scoped dynamically into this function, and the function + ;; may change it it the matcher asksk for it. + (unless match + ;; Get a new match request, with completion + (setq org-last-tags-completion-table + (or org-tag-alist + org-last-tags-completion-table)) + (setq match (completing-read + "Match: " 'org-tags-completion-function nil nil nil + 'org-tags-history))) ; FIXME: Separate history for this? + + ;; Parse the string and create a lisp form + (let ((match0 match) + (re "^&?\\([-+:]\\)?\\({[^}]+}\\|LEVEL=\\([0-9]+\\)\\|[A-Za-z_@0-9]+\\)") + minus tag mm + tagsmatch todomatch tagsmatcher todomatcher kwd matcher + orterms term orlist re-p level-p) + (if (string-match "/+" match) + ;; match contains also a todo-matching request + (progn + (setq tagsmatch (substring match 0 (match-beginning 0)) + todomatch (substring match (match-end 0))) + (if (string-match "^!" todomatch) + (setq todo-only t todomatch (substring todomatch 1))) + (if (string-match "^\\s-*$" todomatch) + (setq todomatch nil))) + ;; only matching tags + (setq tagsmatch match todomatch nil)) + + ;; Make the tags matcher + (if (or (not tagsmatch) (not (string-match "\\S-" tagsmatch))) + (setq tagsmatcher t) + (setq orterms (org-split-string tagsmatch "|") orlist nil) + (while (setq term (pop orterms)) + (while (and (equal (substring term -1) "\\") orterms) + (setq term (concat term "|" (pop orterms)))) ; repair bad split + (while (string-match re term) + (setq minus (and (match-end 1) + (equal (match-string 1 term) "-")) + tag (match-string 2 term) + re-p (equal (string-to-char tag) ?{) + level-p (match-end 3) + mm (cond + (re-p `(org-match-any-p ,(substring tag 1 -1) tags-list)) + (level-p `(= level ,(string-to-number + (match-string 3 term)))) + (t `(member ,(downcase tag) tags-list))) + mm (if minus (list 'not mm) mm) + term (substring term (match-end 0))) + (push mm tagsmatcher)) + (push (if (> (length tagsmatcher) 1) + (cons 'and tagsmatcher) + (car tagsmatcher)) + orlist) + (setq tagsmatcher nil)) + (setq tagsmatcher (if (> (length orlist) 1) (cons 'or orlist) (car orlist)))) + + ;; Make the todo matcher + (if (or (not todomatch) (not (string-match "\\S-" todomatch))) + (setq todomatcher t) + (setq orterms (org-split-string todomatch "|") orlist nil) + (while (setq term (pop orterms)) + (while (string-match re term) + (setq minus (and (match-end 1) + (equal (match-string 1 term) "-")) + kwd (match-string 2 term) + re-p (equal (string-to-char kwd) ?{) + term (substring term (match-end 0)) + mm (if re-p + `(string-match ,(substring kwd 1 -1) todo) + (list 'equal 'todo kwd)) + mm (if minus (list 'not mm) mm)) + (push mm todomatcher)) + (push (if (> (length todomatcher) 1) + (cons 'and todomatcher) + (car todomatcher)) + orlist) + (setq todomatcher nil)) + (setq todomatcher (if (> (length orlist) 1) + (cons 'or orlist) (car orlist)))) + + ;; Return the string and lisp forms of the matcher + (setq matcher (if todomatcher + (list 'and tagsmatcher todomatcher) + tagsmatcher)) + (cons match0 matcher))) + +(defun org-match-any-p (re list) + "Does re match any element of list?" + (setq list (mapcar (lambda (x) (string-match re x)) list)) + (delq nil list)) + +(defvar org-add-colon-after-tag-completion nil) ;; dynamically skoped param +(defvar org-tags-overlay (org-make-overlay 1 1)) +(org-detach-overlay org-tags-overlay) + +(defun org-set-tags (&optional arg just-align) + "Set the tags for the current headline. +With prefix ARG, realign all tags in headings in the current buffer." + (interactive "P") + (let* ((re (concat "^" outline-regexp)) + (current (org-get-tags)) + table current-tags inherited-tags ; computed below when needed + tags p0 c0 c1 rpl) + (if arg + (save-excursion + (goto-char (point-min)) + (let (buffer-invisibility-spec) ; Emacs 21 compatibility + (while (re-search-forward re nil t) + (org-set-tags nil t) + (end-of-line 1))) + (message "All tags realigned to column %d" org-tags-column)) + (if just-align + (setq tags current) + ;; Get a new set of tags from the user + (setq table (or org-tag-alist (org-get-buffer-tags)) + org-last-tags-completion-table table + current-tags (org-split-string current ":") + inherited-tags (nreverse + (nthcdr (length current-tags) + (nreverse (org-get-tags-at)))) + tags + (if (or (eq t org-use-fast-tag-selection) + (and org-use-fast-tag-selection + (delq nil (mapcar 'cdr table)))) + (org-fast-tag-selection current-tags inherited-tags table) + (let ((org-add-colon-after-tag-completion t)) + (org-trim + (completing-read "Tags: " 'org-tags-completion-function + nil nil current 'org-tags-history))))) + (while (string-match "[-+&]+" tags) + ;; No boolean logic, just a list + (setq tags (replace-match ":" t t tags)))) + (if (string-match "\\`[\t ]*\\'" tags) + (setq tags "") + (unless (string-match ":$" tags) (setq tags (concat tags ":"))) + (unless (string-match "^:" tags) (setq tags (concat ":" tags)))) + + ;; Insert new tags at the correct column + (beginning-of-line 1) + (if (re-search-forward + (concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$") + (point-at-eol) t) + (progn + (if (equal tags "") + (setq rpl "") + (goto-char (match-beginning 0)) + (setq c0 (current-column) p0 (point) + c1 (max (1+ c0) (if (> org-tags-column 0) + org-tags-column + (- (- org-tags-column) (length tags)))) + rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags))) + (replace-match rpl) + (and (not (featurep 'xemacs)) c0 (tabify p0 (point))) + tags) + (error "Tags alignment failed"))))) + +(defun org-tags-completion-function (string predicate &optional flag) + (let (s1 s2 rtn (ctable org-last-tags-completion-table) + (confirm (lambda (x) (stringp (car x))))) + (if (string-match "^\\(.*[-+:&|]\\)\\([^-+:&|]*\\)$" string) + (setq s1 (match-string 1 string) + s2 (match-string 2 string)) + (setq s1 "" s2 string)) + (cond + ((eq flag nil) + ;; try completion + (setq rtn (try-completion s2 ctable confirm)) + (if (stringp rtn) + (concat s1 s2 (substring rtn (length s2)) + (if (and org-add-colon-after-tag-completion + (assoc rtn ctable)) + ":" ""))) + ) + ((eq flag t) + ;; all-completions + (all-completions s2 ctable confirm) + ) + ((eq flag 'lambda) + ;; exact match? + (assoc s2 ctable))) + )) + +(defun org-fast-tag-insert (kwd tags face &optional end) + "Insert KDW, and the TAGS, the latter with face FACE. Also inser END." + (insert (format "%-12s" (concat kwd ":")) + (org-add-props (mapconcat 'identity tags " ") nil 'face face) + (or end ""))) + +(defun org-fast-tag-show-exit (flag) + (save-excursion + (goto-line 3) + (if (re-search-forward "[ \t]+Next change exits" (point-at-eol) t) + (replace-match "")) + (when flag + (end-of-line 1) + (move-to-column (- (window-width) 19) t) + (insert (org-add-props " Next change exits" nil 'face 'org-warning))))) + +(defun org-set-current-tags-overlay (current prefix) + (let ((s (concat ":" (mapconcat 'identity current ":") ":"))) + (if (featurep 'xemacs) + (org-overlay-display org-tags-overlay (concat prefix s) + 'secondary-selection) + (put-text-property 0 (length s) 'face '(secondary-selection org-tag) s) + (org-overlay-display org-tags-overlay (concat prefix s))))) + +(defun org-fast-tag-selection (current inherited table) + "Fast tag selection with single keys. +CURRENT is the current list of tags in the headline, INHERITED is the +list of inherited tags, and TABLE is an alist of tags and corresponding keys, +possibly with grouping information. +If the keys are nil, a-z are automatically assigned. +Returns the new tags string, or nil to not change the current settings." + (let* ((maxlen (apply 'max (mapcar + (lambda (x) + (if (stringp (car x)) (string-width (car x)) 0)) + table))) + (buf (current-buffer)) + (buffer-tags nil) + (fwidth (+ maxlen 3 1 3)) + (ncol (/ (- (window-width) 4) fwidth)) + (i-face 'org-done) + (c-face 'org-tag) + tg cnt e c char c1 c2 ntable tbl rtn + ov-start ov-end ov-prefix + (exit-after-next org-fast-tag-selection-single-key) + groups ingroup) + (save-excursion + (beginning-of-line 1) + (if (looking-at ".*[ \t]\\(:[A-Za-z_@0-9:]+:\\)[ \t]*\\(\r\\|$\\)") + (setq ov-start (match-beginning 1) + ov-end (match-end 1) + ov-prefix "") + (setq ov-start (1- (point-at-eol)) + ov-end (1+ ov-start)) + (skip-chars-forward "^\n\r") + (setq ov-prefix + (concat + (buffer-substring (1- (point)) (point)) + (if (> (current-column) org-tags-column) + " " + (make-string (- org-tags-column (current-column)) ?\ )))))) + (org-move-overlay org-tags-overlay ov-start ov-end) + (save-window-excursion + ;; FIXME: would it be better to keep the other windows? + (delete-other-windows) + (split-window-vertically) + (switch-to-buffer-other-window (get-buffer-create " *Org tags*")) + (erase-buffer) + (org-fast-tag-insert "Inherited" inherited i-face "\n") + (org-fast-tag-insert "Current" current c-face "\n\n") + (org-fast-tag-show-exit exit-after-next) + (org-set-current-tags-overlay current ov-prefix) + (setq tbl table char ?a cnt 0) + (while (setq e (pop tbl)) + (cond + ((equal e '(:startgroup)) + (push '() groups) (setq ingroup t) + (when (not (= cnt 0)) + (setq cnt 0) + (insert "\n")) + (insert "{ ")) + ((equal e '(:endgroup)) + (setq ingroup nil cnt 0) + (insert "}\n")) + (t + (setq tg (car e) c2 nil) + (if (cdr e) + (setq c (cdr e)) + ;; automatically assign a character. + (setq c1 (string-to-char + (downcase (substring + tg (if (= (string-to-char tg) ?@) 1 0))))) + (if (or (rassoc c1 ntable) (rassoc c1 table)) + (while (or (rassoc char ntable) (rassoc char table)) + (setq char (1+ char))) + (setq c2 c1)) + (setq c (or c2 char))) + (if ingroup (push tg (car groups))) + (setq tg (org-add-props tg nil 'face + (cond + ((member tg current) c-face) + ((member tg inherited) i-face) + (t nil)))) + (if (and (= cnt 0) (not ingroup)) (insert " ")) + (insert "[" c "] " tg (make-string + (- fwidth 4 (length tg)) ?\ )) + (push (cons tg c) ntable) + (when (= (setq cnt (1+ cnt)) ncol) + (insert "\n") + (if ingroup (insert " ")) + (setq cnt 0))))) + (setq ntable (nreverse ntable)) + (insert "\n") + (goto-char (point-min)) + (if (fboundp 'fit-window-to-buffer) (fit-window-to-buffer)) + (setq rtn + (catch 'exit + (while t + (message "[a-z..]:Toggle [SPC]:clear [RET]:accept [TAB]:free [C-c]: multi%s" + (if groups " [!] no groups" "")) + (setq c (let ((inhibit-quit t)) (read-char-exclusive))) + (cond + ((= c ?\r) (throw 'exit t)) + ((= c ?!) + (setq groups nil) + (goto-char (point-min)) + (while (re-search-forward "[{}]" nil t) (replace-match " "))) + ((= c ?\C-c) + (org-fast-tag-show-exit + (setq exit-after-next (not exit-after-next)))) + ((or (= c ?\C-g) + (and (= c ?q) (not (rassoc c ntable)))) + (org-detach-overlay org-tags-overlay) + (setq quit-flag t)) + ((= c ?\ ) + (setq current nil) + (if exit-after-next (setq exit-after-next 'now))) + ((= c ?\t) + (condition-case nil + (setq tg (completing-read + "Tag: " + (or buffer-tags + (with-current-buffer buf + (org-get-buffer-tags))))) + (quit (setq tg ""))) + (when (string-match "\\S-" tg) + (add-to-list 'buffer-tags (list tg)) + (if (member tg current) + (setq current (delete tg current)) + (push tg current))) + (if exit-after-next (setq exit-after-next 'now))) + ((setq e (rassoc c ntable) tg (car e)) + (if (member tg current) + (setq current (delete tg current)) + (loop for g in groups do + (if (member tg g) + (mapcar (lambda (x) + (setq current (delete x current))) + g))) + (push tg current)) + (if exit-after-next (setq exit-after-next 'now)))) + + ;; Create a sorted list + (setq current + (sort current + (lambda (a b) + (assoc b (cdr (memq (assoc a ntable) ntable)))))) + (if (eq exit-after-next 'now) (throw 'exit t)) + (goto-char (point-min)) + (beginning-of-line 2) + (delete-region (point) (point-at-eol)) + (org-fast-tag-insert "Current" current c-face) + (org-set-current-tags-overlay current ov-prefix) + (while (re-search-forward "\\[.\\] \\([a-zA-Z0-9_@]+\\)" nil t) + (setq tg (match-string 1)) + (add-text-properties (match-beginning 1) (match-end 1) + (list 'face + (cond + ((member tg current) c-face) + ((member tg inherited) i-face) + (t nil))))) + (goto-char (point-min))))) + (org-detach-overlay org-tags-overlay) + (if rtn + (mapconcat 'identity current ":") + nil)))) + +(defun org-get-tags () + "Get the TAGS string in the current headline." + (unless (org-on-heading-p t) + (error "Not on a heading")) + (save-excursion + (beginning-of-line 1) + (if (looking-at ".*[ \t]\\(:[A-Za-z_@0-9:]+:\\)[ \t]*\\(\r\\|$\\)") + (org-match-string-no-properties 1) + ""))) + +(defun org-get-buffer-tags () + "Get a table of all tags used in the buffer, for completion." + (let (tags) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "[ \t]:\\([A-Za-z_@0-9:]+\\):[ \t\r\n]" nil t) + (mapc (lambda (x) (add-to-list 'tags x)) + (org-split-string (org-match-string-no-properties 1) ":")))) + (mapcar 'list tags))) + +;;;; Timestamps + +(defvar org-last-changed-timestamp nil) +(defvar org-time-was-given) ; dynamically scoped parameter +(defvar org-ts-what) ; dynamically scoped parameter + +(defun org-time-stamp (arg) + "Prompt for a date/time and insert a time stamp. +If the user specifies a time like HH:MM, or if this command is called +with a prefix argument, the time stamp will contain date and time. +Otherwise, only the date will be included. All parts of a date not +specified by the user will be filled in from the current date/time. +So if you press just return without typing anything, the time stamp +will represent the current date/time. If there is already a timestamp +at the cursor, it will be modified." + (interactive "P") + (let (org-time-was-given time) + (cond + ((and (org-at-timestamp-p) + (eq last-command 'org-time-stamp) + (eq this-command 'org-time-stamp)) + (insert "--") + (setq time (let ((this-command this-command)) + (org-read-date arg 'totime))) + (org-insert-time-stamp time (or org-time-was-given arg))) + ((org-at-timestamp-p) + (setq time (let ((this-command this-command)) + (org-read-date arg 'totime))) + (when (org-at-timestamp-p) ; just to get the match data + (replace-match "") + (setq org-last-changed-timestamp + (org-insert-time-stamp time (or org-time-was-given arg)))) + (message "Timestamp updated")) + (t + (setq time (let ((this-command this-command)) + (org-read-date arg 'totime))) + (org-insert-time-stamp time (or org-time-was-given arg)))))) + +(defun org-time-stamp-inactive (&optional arg) + "Insert an inactive time stamp. +An inactive time stamp is enclosed in square brackets instead of angle +brackets. It is inactive in the sense that it does not trigger agenda entries, +does not link to the calendar and cannot be changed with the S-cursor keys. +So these are more for recording a certain time/date." + (interactive "P") + (let (org-time-was-given time) + (setq time (org-read-date arg 'totime)) + (org-insert-time-stamp time (or org-time-was-given arg) 'inactive))) + +(defvar org-date-ovl (org-make-overlay 1 1)) +(org-overlay-put org-date-ovl 'face 'org-warning) +(org-detach-overlay org-date-ovl) + +(defvar org-ans1) ; dynamically scoped parameter +(defvar org-ans2) ; dynamically scoped parameter + +(defun org-read-date (&optional with-time to-time from-string) + "Read a date and make things smooth for the user. +The prompt will suggest to enter an ISO date, but you can also enter anything +which will at least partially be understood by `parse-time-string'. +Unrecognized parts of the date will default to the current day, month, year, +hour and minute. For example, + 3-2-5 --> 2003-02-05 + feb 15 --> currentyear-02-15 + sep 12 9 --> 2009-09-12 + 12:45 --> today 12:45 + 22 sept 0:34 --> currentyear-09-22 0:34 + 12 --> currentyear-currentmonth-12 + Fri --> nearest Friday (today or later) + +4 --> four days from today (only if +N is the only thing given) + etc. +The function understands only English month and weekday abbreviations, +but this can be configured with the variables `parse-time-months' and +`parse-time-weekdays'. + +While prompting, a calendar is popped up - you can also select the +date with the mouse (button 1). The calendar shows a period of three +months. To scroll it to other months, use the keys `>' and `<'. +If you don't like the calendar, turn it off with + \(setq org-popup-calendar-for-date-prompt nil) + +With optional argument TO-TIME, the date will immediately be converted +to an internal time. +With an optional argument WITH-TIME, the prompt will suggest to also +insert a time. Note that when WITH-TIME is not set, you can still +enter a time, and this function will inform the calling routine about +this change. The calling routine may then choose to change the format +used to insert the time stamp into the buffer to include the time." + (require 'parse-time) + (let* ((org-time-stamp-rounding-minutes + (if (equal with-time '(16)) 0 org-time-stamp-rounding-minutes)) + (ct (org-current-time)) + (default-time + ;; Default time is either today, or, when entering a range, + ;; the range start. + (if (save-excursion + (re-search-backward + (concat org-ts-regexp "--?-?\\=") ; 1-3 minuses + (- (point) 20) t)) + (apply + 'encode-time + (mapcar (lambda(x) (or x 0)) + (parse-time-string (match-string 1)))) + ct)) + (calendar-move-hook nil) + (view-diary-entries-initially nil) + (view-calendar-holidays-initially nil) + (timestr (format-time-string + (if with-time "%Y-%m-%d %H:%M" "%Y-%m-%d") default-time)) + (prompt (format "YYYY-MM-DD [%s]: " timestr)) + ans org-ans1 org-ans2 (deltadays 0) + second minute hour day month year tl wday wday1) + + (cond + (from-string (setq ans from-string)) + (org-popup-calendar-for-date-prompt + (save-excursion + (save-window-excursion + (calendar) + (calendar-forward-day (- (time-to-days default-time) + (calendar-absolute-from-gregorian + (calendar-current-date)))) + (org-eval-in-calendar nil) + (let* ((old-map (current-local-map)) + (map (copy-keymap calendar-mode-map)) + (minibuffer-local-map (copy-keymap minibuffer-local-map))) + (define-key map (kbd "RET") 'org-calendar-select) + (define-key map (if (featurep 'xemacs) [button1] [mouse-1]) + 'org-calendar-select-mouse) + (define-key map (if (featurep 'xemacs) [button2] [mouse-2]) + 'org-calendar-select-mouse) + (define-key minibuffer-local-map [(meta shift left)] + (lambda () (interactive) + (org-eval-in-calendar '(calendar-backward-month 1)))) + (define-key minibuffer-local-map [(meta shift right)] + (lambda () (interactive) + (org-eval-in-calendar '(calendar-forward-month 1)))) + (define-key minibuffer-local-map [(shift up)] + (lambda () (interactive) + (org-eval-in-calendar '(calendar-backward-week 1)))) + (define-key minibuffer-local-map [(shift down)] + (lambda () (interactive) + (org-eval-in-calendar '(calendar-forward-week 1)))) + (define-key minibuffer-local-map [(shift left)] + (lambda () (interactive) + (org-eval-in-calendar '(calendar-backward-day 1)))) + (define-key minibuffer-local-map [(shift right)] + (lambda () (interactive) + (org-eval-in-calendar '(calendar-forward-day 1)))) + (define-key minibuffer-local-map ">" + (lambda () (interactive) + (org-eval-in-calendar '(scroll-calendar-left 1)))) + (define-key minibuffer-local-map "<" + (lambda () (interactive) + (org-eval-in-calendar '(scroll-calendar-right 1)))) + (unwind-protect + (progn + (use-local-map map) + (setq ans (read-string prompt "" nil nil)) + (if (not (string-match "\\S-" ans)) (setq ans nil)) + (setq ans (or org-ans1 ans org-ans2))) + (use-local-map old-map)))))) + (t ; Naked prompt only + (setq ans (read-string prompt "" nil timestr)))) + (org-detach-overlay org-date-ovl) + + (if (string-match "^[ \t]*[-+][0-9]+[ \t]*$" ans) + (setq deltadays (string-to-number ans) ans "")) + + (if (string-match + "^ *\\(\\([0-9]+\\)-\\)?\\([0-1]?[0-9]\\)-\\([0-3]?[0-9]\\)\\([^-0-9]\\|$\\)" ans) + (progn + (setq year (if (match-end 2) + (string-to-number (match-string 2 ans)) + (string-to-number (format-time-string "%Y"))) + month (string-to-number (match-string 3 ans)) + day (string-to-number (match-string 4 ans))) + (if (< year 100) (setq year (+ 2000 year))) + (setq ans (replace-match (format "%04d-%02d-%02d\\5" year month day) + t nil ans)))) + (setq tl (parse-time-string ans) + year (or (nth 5 tl) (string-to-number (format-time-string "%Y" ct))) + month (or (nth 4 tl) (string-to-number (format-time-string "%m" ct))) + day (or (nth 3 tl) (string-to-number (format-time-string "%d" ct))) + hour (or (nth 2 tl) (string-to-number (format-time-string "%H" ct))) + minute (or (nth 1 tl) (string-to-number (format-time-string "%M" ct))) + second (or (nth 0 tl) 0) + wday (nth 6 tl)) + (setq day (+ day deltadays)) + (when (and wday (not (nth 3 tl))) + ;; Weekday was given, but no day, so pick that day in the week + ;; on or after the derived date. + (setq wday1 (nth 6 (decode-time (encode-time 0 0 0 day month year)))) + (unless (equal wday wday1) + (setq day (+ day (% (- wday wday1 -7) 7))))) + (if (and (boundp 'org-time-was-given) + (nth 2 tl)) + (setq org-time-was-given t)) + (if (< year 100) (setq year (+ 2000 year))) + (if to-time + (encode-time second minute hour day month year) + (if (or (nth 1 tl) (nth 2 tl)) + (format "%04d-%02d-%02d %02d:%02d" year month day hour minute) + (format "%04d-%02d-%02d" year month day))))) + +(defun org-eval-in-calendar (form) + "Eval FORM in the calendar window and return to current window. +Also, store the cursor date in variable org-ans2." + (let ((sw (selected-window))) + (select-window (get-buffer-window "*Calendar*")) + (eval form) + (when (calendar-cursor-to-date) + (let* ((date (calendar-cursor-to-date)) + (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))) + (setq org-ans2 (format-time-string "%Y-%m-%d" time)))) + (org-move-overlay org-date-ovl (1- (point)) (1+ (point)) (current-buffer)) + (select-window sw))) + +(defun org-calendar-select () + "Return to `org-read-date' with the date currently selected. +This is used by `org-read-date' in a temporary keymap for the calendar buffer." + (interactive) + (when (calendar-cursor-to-date) + (let* ((date (calendar-cursor-to-date)) + (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))) + (setq org-ans1 (format-time-string "%Y-%m-%d" time))) + (if (active-minibuffer-window) (exit-minibuffer)))) + +(defun org-insert-time-stamp (time &optional with-hm inactive pre post) + "Insert a date stamp for the date given by the internal TIME. +WITH-HM means, use the stamp format that includes the time of the day. +INACTIVE means use square brackets instead of angular ones, so that the +stamp will not contribute to the agenda. +PRE and POST are optional strings to be inserted before and after the +stamp. +The command returns the inserted time stamp." + (let ((fmt (funcall (if with-hm 'cdr 'car) org-time-stamp-formats)) + stamp) + (if inactive (setq fmt (concat "[" (substring fmt 1 -1) "]"))) + (insert (or pre "")) + (insert (setq stamp (format-time-string fmt time))) + (insert (or post "")) + stamp)) + +(defun org-toggle-time-stamp-overlays () + "Toggle the use of custom time stamp formats." + (interactive) + (setq org-display-custom-times (not org-display-custom-times)) + (unless org-display-custom-times + (let ((p (point-min)) (bmp (buffer-modified-p))) + (while (setq p (next-single-property-change p 'display)) + (if (and (get-text-property p 'display) + (eq (get-text-property p 'face) 'org-date)) + (remove-text-properties + p (setq p (next-single-property-change p 'display)) + '(display t)))) + (set-buffer-modified-p bmp))) + (if (featurep 'xemacs) + (remove-text-properties (point-min) (point-max) '(end-glyph t))) + (org-restart-font-lock) + (setq org-table-may-need-update t) + (if org-display-custom-times + (message "Time stamps are overlayed with custom format") + (message "Time stamp overlays removed"))) + +(defun org-display-custom-time (beg end) + "Overlay modified time stamp format over timestamp between BED and END." + (let* ((t1 (save-match-data + (org-parse-time-string (buffer-substring beg end) t))) + (w1 (- end beg)) + (with-hm (and (nth 1 t1) (nth 2 t1))) + (inactive (= (char-before (1- beg)) ?\[)) + (tf (funcall (if with-hm 'cdr 'car) org-time-stamp-custom-formats)) + (time (org-fix-decoded-time t1)) + (str (org-add-props + (format-time-string + (substring tf 1 -1) (apply 'encode-time time)) + nil 'mouse-face 'highlight)) + (w2 (length str))) + (if (not (= w2 w1)) + (add-text-properties (1+ beg) (+ 2 beg) + (list 'org-dwidth t 'org-dwidth-n (- w1 w2)))) + (if (featurep 'xemacs) + (progn + (put-text-property beg end 'invisible t) + (put-text-property beg end 'end-glyph (make-glyph str))) + (put-text-property beg end 'display str)))) + +(defun org-translate-time (string) + "Translate all timestamps in STRING to custom format. +But do this only if the variable `org-display-custom-times' is set." + (when org-display-custom-times + (save-match-data + (let* ((start 0) + (re org-ts-regexp-both) + t1 with-hm inactive tf time str beg end) + (while (setq start (string-match re string start)) + (setq beg (match-beginning 0) + end (match-end 0) + t1 (save-match-data + (org-parse-time-string (substring string beg end) t)) + with-hm (and (nth 1 t1) (nth 2 t1)) + inactive (equal (substring string beg (1+ beg)) "[") + tf (funcall (if with-hm 'cdr 'car) + org-time-stamp-custom-formats) + time (org-fix-decoded-time t1) + str (format-time-string + (concat + (if inactive "[" "<") (substring tf 1 -1) + (if inactive "]" ">")) + (apply 'encode-time time)) + string (replace-match str t t string) + start (+ start (length str))))))) + string) + +(defun org-fix-decoded-time (time) + "Set 0 instead of nil for the first 6 elements of time. +Don't touch the rest." + (let ((n 0)) + (mapcar (lambda (x) (if (< (setq n (1+ n)) 7) (or x 0) x)) time))) + +(defun org-days-to-time (timestamp-string) + "Difference between TIMESTAMP-STRING and now in days." + (- (time-to-days (org-time-string-to-time timestamp-string)) + (time-to-days (current-time)))) + +(defun org-deadline-close (timestamp-string &optional ndays) + "Is the time in TIMESTAMP-STRING close to the current date?" + (and (< (org-days-to-time timestamp-string) + (or ndays org-deadline-warning-days)) + (not (org-entry-is-done-p)))) + +(defun org-calendar-select-mouse (ev) + "Return to `org-read-date' with the date currently selected. +This is used by `org-read-date' in a temporary keymap for the calendar buffer." + (interactive "e") + (mouse-set-point ev) + (when (calendar-cursor-to-date) + (let* ((date (calendar-cursor-to-date)) + (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))) + (setq org-ans1 (format-time-string "%Y-%m-%d" time))) + (if (active-minibuffer-window) (exit-minibuffer)))) + +(defun org-check-deadlines (ndays) + "Check if there are any deadlines due or past due. +A deadline is considered due if it happens within `org-deadline-warning-days' +days from today's date. If the deadline appears in an entry marked DONE, +it is not shown. The prefix arg NDAYS can be used to test that many +days. If the prefix is a raw \\[universal-argument] prefix, all deadlines are shown." + (interactive "P") + (let* ((org-warn-days + (cond + ((equal ndays '(4)) 100000) + (ndays (prefix-numeric-value ndays)) + (t org-deadline-warning-days))) + (case-fold-search nil) + (regexp (concat "\\<" org-deadline-string " *<\\([^>]+\\)>")) + (callback + (lambda () (org-deadline-close (match-string 1) org-warn-days)))) + + (message "%d deadlines past-due or due within %d days" + (org-occur regexp nil callback) + org-warn-days))) + +(defun org-evaluate-time-range (&optional to-buffer) + "Evaluate a time range by computing the difference between start and end. +Normally the result is just printed in the echo area, but with prefix arg +TO-BUFFER, the result is inserted just after the date stamp into the buffer. +If the time range is actually in a table, the result is inserted into the +next column. +For time difference computation, a year is assumed to be exactly 365 +days in order to avoid rounding problems." + (interactive "P") + (or + (org-clock-update-time-maybe) + (save-excursion + (unless (org-at-date-range-p) + (goto-char (point-at-bol)) + (re-search-forward org-tr-regexp (point-at-eol) t)) + (if (not (org-at-date-range-p)) + (error "Not at a time-stamp range, and none found in current line"))) + (let* ((ts1 (match-string 1)) + (ts2 (match-string 2)) + (havetime (or (> (length ts1) 15) (> (length ts2) 15))) + (match-end (match-end 0)) + (time1 (org-time-string-to-time ts1)) + (time2 (org-time-string-to-time ts2)) + (t1 (time-to-seconds time1)) + (t2 (time-to-seconds time2)) + (diff (abs (- t2 t1))) + (negative (< (- t2 t1) 0)) + ;; (ys (floor (* 365 24 60 60))) + (ds (* 24 60 60)) + (hs (* 60 60)) + (fy "%dy %dd %02d:%02d") + (fy1 "%dy %dd") + (fd "%dd %02d:%02d") + (fd1 "%dd") + (fh "%02d:%02d") + y d h m align) + (if havetime + (setq ; y (floor (/ diff ys)) diff (mod diff ys) + y 0 + d (floor (/ diff ds)) diff (mod diff ds) + h (floor (/ diff hs)) diff (mod diff hs) + m (floor (/ diff 60))) + (setq ; y (floor (/ diff ys)) diff (mod diff ys) + y 0 + d (floor (+ (/ diff ds) 0.5)) + h 0 m 0)) + (if (not to-buffer) + (message (org-make-tdiff-string y d h m)) + (when (org-at-table-p) + (goto-char match-end) + (setq align t) + (and (looking-at " *|") (goto-char (match-end 0)))) + (if (looking-at + "\\( *-? *[0-9]+y\\)?\\( *[0-9]+d\\)? *[0-9][0-9]:[0-9][0-9]") + (replace-match "")) + (if negative (insert " -")) + (if (> y 0) (insert " " (format (if havetime fy fy1) y d h m)) + (if (> d 0) (insert " " (format (if havetime fd fd1) d h m)) + (insert " " (format fh h m)))) + (if align (org-table-align)) + (message "Time difference inserted"))))) + +(defun org-make-tdiff-string (y d h m) + (let ((fmt "") + (l nil)) + (if (> y 0) (setq fmt (concat fmt "%d year" (if (> y 1) "s" "") " ") + l (push y l))) + (if (> d 0) (setq fmt (concat fmt "%d day" (if (> d 1) "s" "") " ") + l (push d l))) + (if (> h 0) (setq fmt (concat fmt "%d hour" (if (> h 1) "s" "") " ") + l (push h l))) + (if (> m 0) (setq fmt (concat fmt "%d minute" (if (> m 1) "s" "") " ") + l (push m l))) + (apply 'format fmt (nreverse l)))) + +(defun org-time-string-to-time (s) + (apply 'encode-time (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'. +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 + (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)) + nil nil nil) + (make-list 9 0))) + +(defun org-timestamp-up (&optional arg) + "Increase the date item at the cursor by one. +If the cursor is on the year, change the year. If it is on the month or +the day, change that. +With prefix ARG, change by that many units." + (interactive "p") + (org-timestamp-change (prefix-numeric-value arg))) + +(defun org-timestamp-down (&optional arg) + "Decrease the date item at the cursor by one. +If the cursor is on the year, change the year. If it is on the month or +the day, change that. +With prefix ARG, change by that many units." + (interactive "p") + (org-timestamp-change (- (prefix-numeric-value arg)))) + +(defun org-timestamp-up-day (&optional arg) + "Increase the date in the time stamp by one day. +With prefix ARG, change that many days." + (interactive "p") + (if (and (not (org-at-timestamp-p t)) + (org-on-heading-p)) + (org-todo 'up) + (org-timestamp-change (prefix-numeric-value arg) 'day))) + +(defun org-timestamp-down-day (&optional arg) + "Decrease the date in the time stamp by one day. +With prefix ARG, change that many days." + (interactive "p") + (if (and (not (org-at-timestamp-p t)) + (org-on-heading-p)) + (org-todo 'down) + (org-timestamp-change (- (prefix-numeric-value arg)) 'day))) + +(defsubst org-pos-in-match-range (pos n) + (and (match-beginning n) + (<= (match-beginning n) pos) + (>= (match-end n) pos))) + +(defun org-at-timestamp-p (&optional inactive-ok) + "Determine if the cursor is in or at a timestamp." + (interactive) + (let* ((tsr (if inactive-ok org-ts-regexp3 org-ts-regexp2)) + (pos (point)) + (ans (or (looking-at tsr) + (save-excursion + (skip-chars-backward "^[<\n\r\t") + (if (> (point) 1) (backward-char 1)) + (and (looking-at tsr) + (> (- (match-end 0) pos) -1)))))) + (and (boundp 'org-ts-what) + (setq org-ts-what + (cond + ((org-pos-in-match-range pos 2) 'year) + ((org-pos-in-match-range pos 3) 'month) + ((org-pos-in-match-range pos 7) 'hour) + ((org-pos-in-match-range pos 8) 'minute) + ((or (org-pos-in-match-range pos 4) + (org-pos-in-match-range pos 5)) 'day) + (t 'day)))) + ans)) + +(defun org-timestamp-change (n &optional what) + "Change the date in the time stamp at point. +The date will be changed by N times WHAT. WHAT can be `day', `month', +`year', `minute', `second'. If WHAT is not given, the cursor position +in the timestamp determines what will be changed." + (let ((pos (point)) + with-hm inactive + org-ts-what + ts time time0) + (if (not (org-at-timestamp-p t)) + (error "Not at a timestamp")) + (if (and (not what) (not (eq org-ts-what 'day)) + org-display-custom-times + (get-text-property (point) 'display) + (not (get-text-property (1- (point)) 'display))) + (setq org-ts-what 'day)) + (setq org-ts-what (or what org-ts-what) + with-hm (<= (abs (- (cdr org-ts-lengths) + (- (match-end 0) (match-beginning 0)))) + 1) + inactive (= (char-after (match-beginning 0)) ?\[) + ts (match-string 0)) + (replace-match "") + (setq time0 (org-parse-time-string ts)) + (setq time + (apply 'encode-time + (append + (list (or (car time0) 0)) + (list (+ (if (eq org-ts-what 'minute) n 0) (nth 1 time0))) + (list (+ (if (eq org-ts-what 'hour) n 0) (nth 2 time0))) + (list (+ (if (eq org-ts-what 'day) n 0) (nth 3 time0))) + (list (+ (if (eq org-ts-what 'month) n 0) (nth 4 time0))) + (list (+ (if (eq org-ts-what 'year) n 0) (nth 5 time0))) + (nthcdr 6 time0)))) + (if (eq what 'calendar) + (let ((cal-date + (save-excursion + (save-match-data + (set-buffer "*Calendar*") + (calendar-cursor-to-date))))) + (setcar (nthcdr 4 time0) (nth 0 cal-date)) ; month + (setcar (nthcdr 3 time0) (nth 1 cal-date)) ; day + (setcar (nthcdr 5 time0) (nth 2 cal-date)) ; year + (setcar time0 (or (car time0) 0)) + (setcar (nthcdr 1 time0) (or (nth 1 time0) 0)) + (setcar (nthcdr 2 time0) (or (nth 1 time0) 0)) + (setq time (apply 'encode-time time0)))) + (setq org-last-changed-timestamp + (org-insert-time-stamp time with-hm inactive)) + (org-clock-update-time-maybe) + (goto-char pos) + ;; Try to recenter the calendar window, if any + (if (and org-calendar-follow-timestamp-change + (get-buffer-window "*Calendar*" t) + (memq org-ts-what '(day month year))) + (org-recenter-calendar (time-to-days time))))) + +(defun org-recenter-calendar (date) + "If the calendar is visible, recenter it to DATE." + (let* ((win (selected-window)) + (cwin (get-buffer-window "*Calendar*" t)) + (calendar-move-hook nil)) + (when cwin + (select-window cwin) + (calendar-goto-date (if (listp date) date + (calendar-gregorian-from-absolute date))) + (select-window win)))) + +(defun org-goto-calendar (&optional arg) + "Go to the Emacs calendar at the current date. +If there is a time stamp in the current line, go to that date. +A prefix ARG can be used to force the current date." + (interactive "P") + (let ((tsr org-ts-regexp) diff + (calendar-move-hook nil) + (view-calendar-holidays-initially nil) + (view-diary-entries-initially nil)) + (if (or (org-at-timestamp-p) + (save-excursion + (beginning-of-line 1) + (looking-at (concat ".*" tsr)))) + (let ((d1 (time-to-days (current-time))) + (d2 (time-to-days + (org-time-string-to-time (match-string 1))))) + (setq diff (- d2 d1)))) + (calendar) + (calendar-goto-today) + (if (and diff (not arg)) (calendar-forward-day diff)))) + +(defun org-date-from-calendar () + "Insert time stamp corresponding to cursor date in *Calendar* buffer. +If there is already a time stamp at the cursor position, update it." + (interactive) + (org-timestamp-change 0 'calendar)) + +;;; The clock for measuring work time. + +(defvar org-clock-marker (make-marker) + "Marker recording the last clock-in.") + +(defun org-clock-in () + "Start the clock on the current item. +If necessary, clock-out of the currently active clock." + (interactive) + (org-clock-out t) + (let (ts) + (save-excursion + (org-back-to-heading t) + (beginning-of-line 2) + (if (and (looking-at (concat "[ \t]*" org-keyword-time-regexp)) + (not (equal (match-string 1) org-clock-string))) + (beginning-of-line 1)) + (insert "\n") (backward-char 1) + (indent-relative) + (insert org-clock-string " ") + (setq ts (org-insert-time-stamp (current-time) 'with-hm 'inactive)) + (move-marker org-clock-marker (point) (buffer-base-buffer)) + (message "Clock started at %s" ts)))) + +(defun org-clock-out (&optional fail-quietly) + "Stop the currently running clock. +If there is no running clock, throw an error, unless FAIL-QUIETLY is set." + (interactive) + (catch 'exit + (if (not (marker-buffer org-clock-marker)) + (if fail-quietly (throw 'exit t) (error "No active clock"))) + (let (ts te s h m) + (save-excursion + (set-buffer (marker-buffer org-clock-marker)) + (goto-char org-clock-marker) + (beginning-of-line 1) + (if (and (looking-at (concat "[ \t]*" org-keyword-time-regexp)) + (equal (match-string 1) org-clock-string)) + (setq ts (match-string 2)) + (if fail-quietly (throw 'exit nil) (error "Clock start time is gone"))) + (goto-char org-clock-marker) + (insert "--") + (setq te (org-insert-time-stamp (current-time) 'with-hm 'inactive)) + (setq s (- (time-to-seconds (apply 'encode-time (org-parse-time-string te))) + (time-to-seconds (apply 'encode-time (org-parse-time-string ts)))) + h (floor (/ s 3600)) + s (- s (* 3600 h)) + m (floor (/ s 60)) + s (- s (* 60 s))) + (insert " => " (format "%2d:%02d" h m)) + (move-marker org-clock-marker nil) + (org-add-log-maybe 'clock-out) + (message "Clock stopped at %s after HH:MM = %d:%02d" te h m))))) + +(defun org-clock-cancel () + "Cancel the running clock be removing the start timestamp." + (interactive) + (if (not (marker-buffer org-clock-marker)) + (error "No active clock")) + (save-excursion + (set-buffer (marker-buffer org-clock-marker)) + (goto-char org-clock-marker) + (delete-region (1- (point-at-bol)) (point-at-eol))) + (message "Clock canceled")) + +(defvar org-clock-file-total-minutes nil + "Holds the file total time in minutes, after a call to `org-clock-sum'.") + (make-variable-buffer-local 'org-clock-file-total-minutes) + +(defun org-clock-sum (&optional tstart tend) + "Sum the times for each subtree. +Puts the resulting times in minutes as a text property on each headline." + (interactive) + (let* ((bmp (buffer-modified-p)) + (re (concat "^\\(\\*+\\)[ \t]\\|^[ \t]*" + org-clock-string + "[ \t]*\\(\\[.*?\\]\\)-+\\(\\[.*?\\]\\)")) + (lmax 30) + (ltimes (make-vector lmax 0)) + (t1 0) + (level 0) + ts te dt + time) + (remove-text-properties (point-min) (point-max) '(:org-clock-minutes t)) + (save-excursion + (goto-char (point-max)) + (while (re-search-backward re nil t) + (if (match-end 2) + ;; A time + (setq ts (match-string 2) + te (match-string 3) + ts (time-to-seconds + (apply 'encode-time (org-parse-time-string ts))) + te (time-to-seconds + (apply 'encode-time (org-parse-time-string te))) + ts (if tstart (max ts tstart) ts) + te (if tend (min te tend) te) + dt (- te ts) + t1 (if (> dt 0) (+ t1 (floor (/ dt 60))) t1)) + ;; A headline + (setq level (- (match-end 1) (match-beginning 1))) + (when (or (> t1 0) (> (aref ltimes level) 0)) + (loop for l from 0 to level do + (aset ltimes l (+ (aref ltimes l) t1))) + (setq t1 0 time (aref ltimes level)) + (loop for l from level to (1- lmax) do + (aset ltimes l 0)) + (goto-char (match-beginning 0)) + (put-text-property (point) (point-at-eol) :org-clock-minutes time)))) + (setq org-clock-file-total-minutes (aref ltimes 0))) + (set-buffer-modified-p bmp))) + +(defun org-clock-display (&optional total-only) + "Show subtree times in the entire buffer. +If TOTAL-ONLY is non-nil, only show the total time for the entire file +in the echo area." + (interactive) + (org-remove-clock-overlays) + (let (time h m p) + (org-clock-sum) + (unless total-only + (save-excursion + (goto-char (point-min)) + (while (setq p (next-single-property-change (point) :org-clock-minutes)) + (goto-char p) + (when (setq time (get-text-property p :org-clock-minutes)) + (org-put-clock-overlay time (funcall outline-level)))) + (setq h (/ org-clock-file-total-minutes 60) + m (- org-clock-file-total-minutes (* 60 h))) + ;; Arrange to remove the overlays upon next change. + (when org-remove-highlights-with-change + (org-add-hook 'before-change-functions 'org-remove-clock-overlays + nil 'local)))) + (message "Total file time: %d:%02d (%d hours and %d minutes)" h m h m))) + +(defvar org-clock-overlays nil) +(make-variable-buffer-local 'org-clock-overlays) + +(defun org-put-clock-overlay (time &optional level) + "Put an overlays on the current line, displaying TIME. +If LEVEL is given, prefix time with a corresponding number of stars. +This creates a new overlay and stores it in `org-clock-overlays', so that it +will be easy to remove." + (let* ((c 60) (h (floor (/ time 60))) (m (- time (* 60 h))) + (l (if level (org-get-legal-level level 0) 0)) + (off 0) + ov tx) + (move-to-column c) + (unless (eolp) (skip-chars-backward "^ \t")) + (skip-chars-backward " \t") + (setq ov (org-make-overlay (1- (point)) (point-at-eol)) + tx (concat (buffer-substring (1- (point)) (point)) + (make-string (+ off (max 0 (- c (current-column)))) ?.) + (org-add-props (format "%s %2d:%02d%s" + (make-string l ?*) h m + (make-string (- 10 l) ?\ )) + '(face secondary-selection)) + "")) + (if (not (featurep 'xemacs)) + (org-overlay-put ov 'display tx) + (org-overlay-put ov 'invisible t) + (org-overlay-put ov 'end-glyph (make-glyph tx))) + (push ov org-clock-overlays))) + +(defun org-remove-clock-overlays (&optional beg end noremove) + "Remove the occur highlights from the buffer. +BEG and END are ignored. If NOREMOVE is nil, remove this function +from the `before-change-functions' in the current buffer." + (interactive) + (unless org-inhibit-highlight-removal + (mapc 'org-delete-overlay org-clock-overlays) + (setq org-clock-overlays nil) + (unless noremove + (remove-hook 'before-change-functions + 'org-remove-clock-overlays 'local)))) + +(defun org-clock-out-if-current () + "Clock out if the current entry contains the running clock. +This is used to stop the clock after a TODO entry is marked DONE." + (when (and (equal state org-done-string) + (equal (marker-buffer org-clock-marker) (current-buffer)) + (< (point) org-clock-marker) + (> (save-excursion (outline-next-heading) (point)) + org-clock-marker)) + ;; Clock out, but don't accept a logging message for this. + (let ((org-log-done (if (and (listp org-log-done) + (member 'clock-out org-log-done)) + '(done) + org-log-done))) + (org-clock-out)))) + +(add-hook 'org-after-todo-state-change-hook + 'org-clock-out-if-current) + +(defun org-check-running-clock () + "Check if the current buffer contains the running clock. +If yes, offer to stop it and to save the buffer with the changes." + (when (and (equal (marker-buffer org-clock-marker) (current-buffer)) + (y-or-n-p (format "Clock-out in buffer %s before killing it? " + (buffer-name)))) + (org-clock-out) + (when (y-or-n-p "Save changed buffer?") + (save-buffer)))) + +(defun org-clock-report () + "Create a table containing a report about clocked time. +If the buffer contains lines +#+BEGIN: clocktable :maxlevel 3 :emphasize nil + +#+END: clocktable +then the table will be inserted between these lines, replacing whatever +is was there before. If these lines are not in the buffer, the table +is inserted at point, surrounded by the special lines. +The BEGIN line can contain parameters. Allowed are: +:maxlevel The maximum level to be included in the table. Default is 3. +:emphasize t/nil, if levell 1 and level 2 should be bold/italic in the table." + (interactive) + (org-remove-clock-overlays) + (unless (org-find-dblock "clocktable") + (org-create-dblock (list :name "clocktable" + :maxlevel 2 :emphasize nil))) + (org-update-dblock)) + +(defun org-clock-update-time-maybe () + "If this is a CLOCK line, update it and return t. +Otherwise, return nil." + (interactive) + (save-excursion + (beginning-of-line 1) + (skip-chars-forward " \t") + (when (looking-at org-clock-string) + (let ((re (concat "[ \t]*" org-clock-string + " *[[<]\\([^]>]+\\)[]>]-+[[<]\\([^]>]+\\)[]>]" + "\\([ \t]*=>.*\\)?")) + ts te h m s) + (if (not (looking-at re)) + nil + (and (match-end 3) (delete-region (match-beginning 3) (match-end 3))) + (end-of-line 1) + (setq ts (match-string 1) + te (match-string 2)) + (setq s (- (time-to-seconds + (apply 'encode-time (org-parse-time-string te))) + (time-to-seconds + (apply 'encode-time (org-parse-time-string ts)))) + h (floor (/ s 3600)) + s (- s (* 3600 h)) + m (floor (/ s 60)) + s (- s (* 60 s))) + (insert " => " (format "%2d:%02d" h m)) + t))))) + +(defun org-clock-special-range (key &optional time as-strings) + "Return two times bordering a special time range. +Key is a symbol specifying the range and can be one of `today', `yesterday', +`thisweek', `lastweek', `thismonth', `lastmonth', `thisyear', `lastyear'. +A week starts Monday 0:00 and ends Sunday 24:00. +The range is determined relative to TIME. TIME defaults to the current time. +The return value is a cons cell with two internal times like the ones +returned by `current time' or `encode-time'. if AS-STRINGS is non-nil, +the returned times will be formatted strings." + (let* ((tm (decode-time (or time (current-time)))) + (s 0) (m (nth 1 tm)) (h (nth 2 tm)) + (d (nth 3 tm)) (month (nth 4 tm)) (y (nth 5 tm)) + (dow (nth 6 tm)) + s1 m1 h1 d1 month1 y1 diff ts te fm) + (cond + ((eq key 'today) + (setq h 0 m 0 h1 24 m1 0)) + ((eq key 'yesterday) + (setq d (1- d) h 0 m 0 h1 24 m1 0)) + ((eq key 'thisweek) + (setq diff (if (= dow 0) 6 (1- dow)) + m 0 h 0 d (- d diff) d1 (+ 7 d))) + ((eq key 'lastweek) + (setq diff (+ 7 (if (= dow 0) 6 (1- dow))) + m 0 h 0 d (- d diff) d1 (+ 7 d))) + ((eq key 'thismonth) + (setq d 1 h 0 m 0 d1 1 month1 (1+ month) h1 0 m1 0)) + ((eq key 'lastmonth) + (setq d 1 h 0 m 0 d1 1 month (1- month) month1 (1+ month) h1 0 m1 0)) + ((eq key 'thisyear) + (setq m 0 h 0 d 1 month 1 y1 (1+ y))) + ((eq key 'lastyear) + (setq m 0 h 0 d 1 month 1 y (1- y) y1 (1+ y))) + (t (error "No such time block %s" key))) + (setq ts (encode-time s m h d month y) + te (encode-time (or s1 s) (or m1 m) (or h1 h) + (or d1 d) (or month1 month) (or y1 y))) + (setq fm (cdr org-time-stamp-formats)) + (if as-strings + (cons (format-time-string fm ts) (format-time-string fm te)) + (cons ts te)))) + +(defun org-dblock-write:clocktable (params) + "Write the standard clocktable." + (let ((hlchars '((1 . "*") (2 . ?/))) + (emph nil) + (ins (make-marker)) + ipos time h m p level hlc hdl maxlevel + ts te cc block) + (setq maxlevel (or (plist-get params :maxlevel) 3) + emph (plist-get params :emphasize) + ts (plist-get params :tstart) + te (plist-get params :tend) + block (plist-get params :block)) + (when block + (setq cc (org-clock-special-range block nil t) + ts (car cc) te (cdr cc))) + (if ts (setq ts (time-to-seconds + (apply 'encode-time (org-parse-time-string ts))))) + (if te (setq te (time-to-seconds + (apply 'encode-time (org-parse-time-string te))))) + (move-marker ins (point)) + (setq ipos (point)) + ;; FIXME: does not yet use org-insert-time-stamp + (insert-before-markers "Clock summary at [" + (substring + (format-time-string (cdr org-time-stamp-formats)) + 1 -1) + "]." + (if block + (format " Considered range is /%s/." block) + "") + "\n\n|L|Headline|Time|\n") + (org-clock-sum ts te) + (setq h (/ org-clock-file-total-minutes 60) + m (- org-clock-file-total-minutes (* 60 h))) + (insert-before-markers "|-\n|0|" "*Total file time*| " + (format "*%d:%02d*" h m) + "|\n") + (goto-char (point-min)) + (while (setq p (next-single-property-change (point) :org-clock-minutes)) + (goto-char p) + (when (setq time (get-text-property p :org-clock-minutes)) + (save-excursion + (beginning-of-line 1) + (when (and (looking-at "\\(\\*+\\)[ \t]+\\(.*?\\)\\([ \t]+:[0-9a-zA-Z_@:]+:\\)?[ \t]*$") + (setq level (- (match-end 1) (match-beginning 1))) + (<= level maxlevel)) + (setq hlc (if emph (or (cdr (assoc level hlchars)) "") "") + hdl (match-string 2) + h (/ time 60) + m (- time (* 60 h))) + (goto-char ins) + (if (= level 1) (insert-before-markers "|-\n")) + (insert-before-markers + "| " (int-to-string level) "|" hlc hdl hlc " |" + (make-string (1- level) ?|) + hlc + (format "%d:%02d" h m) + hlc + " |\n"))))) + (goto-char ins) + (backward-delete-char 1) + (goto-char ipos) + (skip-chars-forward "^|") + (org-table-align))) + +(defun org-collect-clock-time-entries () + "Return an internal list with clocking information. +This list has one entry for each CLOCK interval. +FIXME: describe the elements." + (interactive) + (let ((re (concat "^[ \t]*" org-clock-string + " *\\[\\(.*?\\)\\]--\\[\\(.*?\\)\\]")) + rtn beg end next cont level title total closedp leafp + clockpos titlepos h m donep) + (save-excursion + (org-clock-sum) + (goto-char (point-min)) + (while (re-search-forward re nil t) + (setq clockpos (match-beginning 0) + beg (match-string 1) end (match-string 2) + cont (match-end 0)) + (setq beg (apply 'encode-time (org-parse-time-string beg)) + end (apply 'encode-time (org-parse-time-string end))) + (org-back-to-heading t) + (setq donep (org-entry-is-done-p)) + (setq titlepos (point) + total (or (get-text-property (1+ (point)) :org-clock-minutes) 0) + h (/ total 60) m (- total (* 60 h)) + total (cons h m)) + (looking-at "\\(\\*+\\) +\\(.*\\)") + (setq level (- (match-end 1) (match-beginning 1)) + title (org-match-string-no-properties 2)) + (save-excursion (outline-next-heading) (setq next (point))) + (setq closedp (re-search-forward org-closed-time-regexp next t)) + (goto-char next) + (setq leafp (and (looking-at "^\\*+ ") + (<= (- (match-end 0) (point)) level))) + (push (list beg end clockpos closedp donep + total title titlepos level leafp) + rtn) + (goto-char cont))) + (nreverse rtn))) + +;;;; Agenda, and Diary Integration + +;;; Define the Org-agenda-mode + +(defvar org-agenda-mode-map (make-sparse-keymap) + "Keymap for `org-agenda-mode'.") + +(defvar org-agenda-menu) ; defined later in this file. +(defvar org-agenda-follow-mode nil) +(defvar org-agenda-show-log nil) +(defvar org-agenda-redo-command nil) +(defvar org-agenda-mode-hook nil) +(defvar org-agenda-type nil) +(defvar org-agenda-force-single-file nil) + +(defun org-agenda-mode () + "Mode for time-sorted view on action items in Org-mode files. + +The following commands are available: + +\\{org-agenda-mode-map}" + (interactive) + (kill-all-local-variables) + (setq org-agenda-undo-list nil + org-agenda-pending-undo-list nil) + (setq major-mode 'org-agenda-mode) + (setq mode-name "Org-Agenda") + (use-local-map org-agenda-mode-map) + (easy-menu-add org-agenda-menu) + (if org-startup-truncated (setq truncate-lines t)) + (org-add-hook 'post-command-hook 'org-agenda-post-command-hook nil 'local) + (org-add-hook 'pre-command-hook 'org-unhighlight nil 'local) + (unless org-agenda-keep-modes + (setq org-agenda-follow-mode org-agenda-start-with-follow-mode + org-agenda-show-log nil)) + (easy-menu-change + '("Agenda") "Agenda Files" + (append + (list + (vector + (if (get 'org-agenda-files 'org-restrict) + "Restricted to single file" + "Edit File List") + '(org-edit-agenda-file-list) + (not (get 'org-agenda-files 'org-restrict))) + "--") + (mapcar 'org-file-menu-entry (org-agenda-files)))) + (org-agenda-set-mode-name) + (apply + (if (fboundp 'run-mode-hooks) 'run-mode-hooks 'run-hooks) + (list 'org-agenda-mode-hook))) + +(substitute-key-definition 'undo 'org-agenda-undo + org-agenda-mode-map global-map) +(define-key org-agenda-mode-map "\C-i" 'org-agenda-goto) +(define-key org-agenda-mode-map [(tab)] 'org-agenda-goto) +(define-key org-agenda-mode-map "\C-m" 'org-agenda-switch-to) +(define-key org-agenda-mode-map "\C-k" 'org-agenda-kill) +(define-key org-agenda-mode-map "\C-c$" 'org-agenda-archive) +(define-key org-agenda-mode-map "$" 'org-agenda-archive) +(define-key org-agenda-mode-map "\C-c\C-o" 'org-agenda-open-link) +(define-key org-agenda-mode-map " " 'org-agenda-show) +(define-key org-agenda-mode-map "\C-c\C-t" 'org-agenda-todo) +(define-key org-agenda-mode-map "\C-c\C-xb" 'org-agenda-tree-to-indirect-buffer) +(define-key org-agenda-mode-map "b" 'org-agenda-tree-to-indirect-buffer) +(define-key org-agenda-mode-map "o" 'delete-other-windows) +(define-key org-agenda-mode-map "L" 'org-agenda-recenter) +(define-key org-agenda-mode-map "t" 'org-agenda-todo) +(define-key org-agenda-mode-map "a" 'org-agenda-toggle-archive-tag) +(define-key org-agenda-mode-map ":" 'org-agenda-set-tags) +(define-key org-agenda-mode-map "." 'org-agenda-goto-today) +(define-key org-agenda-mode-map "d" 'org-agenda-day-view) +(define-key org-agenda-mode-map "w" 'org-agenda-week-view) +(define-key org-agenda-mode-map (org-key 'S-right) 'org-agenda-date-later) +(define-key org-agenda-mode-map (org-key 'S-left) 'org-agenda-date-earlier) +(define-key org-agenda-mode-map [?\C-c ?\C-x (right)] 'org-agenda-date-later) +(define-key org-agenda-mode-map [?\C-c ?\C-x (left)] 'org-agenda-date-earlier) + +(define-key org-agenda-mode-map ">" 'org-agenda-date-prompt) +(define-key org-agenda-mode-map "\C-c\C-s" 'org-agenda-schedule) +(define-key org-agenda-mode-map "\C-c\C-d" 'org-agenda-deadline) +(let ((l '(1 2 3 4 5 6 7 8 9 0))) + (while l (define-key org-agenda-mode-map + (int-to-string (pop l)) 'digit-argument))) + +(define-key org-agenda-mode-map "f" 'org-agenda-follow-mode) +(define-key org-agenda-mode-map "l" 'org-agenda-log-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) +(define-key org-agenda-mode-map "s" 'org-save-all-org-buffers) +(define-key org-agenda-mode-map "P" 'org-agenda-show-priority) +(define-key org-agenda-mode-map "T" 'org-agenda-show-tags) +(define-key org-agenda-mode-map "n" 'next-line) +(define-key org-agenda-mode-map "p" 'previous-line) +(define-key org-agenda-mode-map "\C-n" 'org-agenda-next-date-line) +(define-key org-agenda-mode-map "\C-p" 'org-agenda-previous-date-line) +(define-key org-agenda-mode-map "," 'org-agenda-priority) +(define-key org-agenda-mode-map "\C-c," 'org-agenda-priority) +(define-key org-agenda-mode-map "i" 'org-agenda-diary-entry) +(define-key org-agenda-mode-map "c" 'org-agenda-goto-calendar) +(eval-after-load "calendar" + '(define-key calendar-mode-map org-calendar-to-agenda-key + 'org-calendar-goto-agenda)) +(define-key org-agenda-mode-map "C" 'org-agenda-convert-date) +(define-key org-agenda-mode-map "m" 'org-agenda-phases-of-moon) +(define-key org-agenda-mode-map "M" 'org-agenda-phases-of-moon) +(define-key org-agenda-mode-map "S" 'org-agenda-sunrise-sunset) +(define-key org-agenda-mode-map "h" 'org-agenda-holidays) +(define-key org-agenda-mode-map "H" 'org-agenda-holidays) +(define-key org-agenda-mode-map "+" 'org-agenda-priority-up) +(define-key org-agenda-mode-map "I" 'org-agenda-clock-in) +(define-key org-agenda-mode-map "O" 'org-agenda-clock-out) +(define-key org-agenda-mode-map "X" 'org-agenda-clock-cancel) +(define-key org-agenda-mode-map "-" 'org-agenda-priority-down) +(define-key org-agenda-mode-map (org-key 'S-up) 'org-agenda-priority-up) +(define-key org-agenda-mode-map (org-key 'S-down) 'org-agenda-priority-down) +(define-key org-agenda-mode-map [?\C-c ?\C-x (up)] 'org-agenda-priority-up) +(define-key org-agenda-mode-map [?\C-c ?\C-x (down)] 'org-agenda-priority-down) +(define-key org-agenda-mode-map [(right)] 'org-agenda-later) +(define-key org-agenda-mode-map [(left)] 'org-agenda-earlier) +(define-key org-agenda-mode-map "\C-c\C-x\C-c" 'org-export-icalendar-combine-agenda-files) +(defvar org-agenda-keymap (copy-keymap org-agenda-mode-map) + "Local keymap for agenda entries from Org-mode.") + +(define-key org-agenda-keymap + (if (featurep 'xemacs) [(button2)] [(mouse-2)]) 'org-agenda-goto-mouse) +(define-key org-agenda-keymap + (if (featurep 'xemacs) [(button3)] [(mouse-3)]) 'org-agenda-show-mouse) +(when org-agenda-mouse-1-follows-link + (define-key org-agenda-keymap [follow-link] 'mouse-face)) +(easy-menu-define org-agenda-menu org-agenda-mode-map "Agenda menu" + '("Agenda" + ("Agenda Files") + "--" + ["Show" org-agenda-show t] + ["Go To (other window)" org-agenda-goto t] + ["Go To (this window)" org-agenda-switch-to t] + ["Follow Mode" org-agenda-follow-mode + :style toggle :selected org-agenda-follow-mode :active t] + ["Tree to indirect frame" org-agenda-tree-to-indirect-buffer t] + "--" + ["Cycle TODO" org-agenda-todo t] + ["Archive subtree" org-agenda-archive t] + ["Delete subtree" org-agenda-kill 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)] + ["Previous Dates" org-agenda-earlier (org-agenda-check-type nil 'agenda)] + "--" + ("Tags" + ["Show all Tags" org-agenda-show-tags t] + ["Set Tags" org-agenda-set-tags t]) + ("Date/Schedule" + ["Schedule" org-agenda-schedule t] + ["Set Deadline" org-agenda-deadline t] + "--" + ["Change date +1 day" org-agenda-date-later (org-agenda-check-type nil 'agenda 'timeline)] + ["Change date -1 day" org-agenda-date-earlier (org-agenda-check-type nil 'agenda 'timeline)] + ["Change date to ..." org-agenda-date-prompt (org-agenda-check-type nil 'agenda 'timeline)]) + ("Priority" + ["Set Priority" org-agenda-priority t] + ["Increase Priority" org-agenda-priority-up t] + ["Decrease Priority" org-agenda-priority-down t] + ["Show Priority" org-agenda-show-priority t]) + ("Calendar/Diary" + ["New Diary Entry" org-agenda-diary-entry (org-agenda-check-type nil 'agenda 'timeline)] + ["Goto Calendar" org-agenda-goto-calendar (org-agenda-check-type nil 'agenda 'timeline)] + ["Phases of the Moon" org-agenda-phases-of-moon (org-agenda-check-type nil 'agenda 'timeline)] + ["Sunrise/Sunset" org-agenda-sunrise-sunset (org-agenda-check-type nil 'agenda 'timeline)] + ["Holidays" org-agenda-holidays (org-agenda-check-type nil 'agenda 'timeline)] + ["Convert" org-agenda-convert-date (org-agenda-check-type nil 'agenda 'timeline)] + "--" + ["Create iCalendar file" org-export-icalendar-combine-agenda-files t]) + "--" + ("View" + ["Day View" org-agenda-day-view :active (org-agenda-check-type nil 'agenda) + :style radio :selected (equal org-agenda-ndays 1)] + ["Week View" org-agenda-week-view :active (org-agenda-check-type nil 'agenda) + :style radio :selected (equal org-agenda-ndays 7)] + "--" + ["Show Logbook entries" org-agenda-log-mode + :style toggle :selected org-agenda-show-log :active (org-agenda-check-type nil 'agenda 'timeline)] + ["Include Diary" org-agenda-toggle-diary + :style toggle :selected org-agenda-include-diary :active (org-agenda-check-type nil 'agenda)] + ["Use Time Grid" org-agenda-toggle-time-grid + :style toggle :selected org-agenda-use-time-grid :active (org-agenda-check-type nil 'agenda)]) + ["Rebuild buffer" org-agenda-redo t] + ["Save all Org-mode Buffers" org-save-all-org-buffers t] + "--" + ["Undo Remote Editing" org-agenda-undo org-agenda-undo-list] + "--" + ["Quit" org-agenda-quit t] + ["Exit and Release Buffers" org-agenda-exit t] + )) + +;;; Agenda undo + +(defvar org-agenda-allow-remote-undo t + "Non-nil means, allow remote undo from the agenda buffer.") +(defvar org-agenda-undo-list nil + "List of undoable operations in the agenda since last refresh.") +(defvar org-agenda-undo-has-started-in nil + "Buffers that have already seen `undo-start' in the current undo sequence.") +(defvar org-agenda-pending-undo-list nil + "In a series of undo commands, this is the list of remaning undo items.") + +(defmacro org-with-remote-undo (_buffer &rest _body) + "Execute BODY while recording undo information in two buffers." + (declare (indent 1) (debug t)) + `(let ((_cline (org-current-line)) + (_cmd this-command) + (_buf1 (current-buffer)) + (_buf2 ,_buffer) + (_undo1 buffer-undo-list) + (_undo2 (with-current-buffer ,_buffer buffer-undo-list)) + _c1 _c2) + ,@_body + (when org-agenda-allow-remote-undo + (setq _c1 (org-verify-change-for-undo + _undo1 (with-current-buffer _buf1 buffer-undo-list)) + _c2 (org-verify-change-for-undo + _undo2 (with-current-buffer _buf2 buffer-undo-list))) + (when (or _c1 _c2) + ;; make sure there are undo boundaries + (and _c1 (with-current-buffer _buf1 (undo-boundary))) + (and _c2 (with-current-buffer _buf2 (undo-boundary))) + ;; remember which buffer to undo + (push (list _cmd _cline _buf1 _c1 _buf2 _c2) + org-agenda-undo-list))))) + +(defun org-agenda-undo () + "Undo a remote editing step in the agenda. +This undoes changes both in the agenda buffer and in the remote buffer +that have been changed along." + (interactive) + (or org-agenda-allow-remote-undo + (error "Check the variable `org-agenda-allow-remote-undo' to activate remote undo.")) + (if (not (eq this-command last-command)) + (setq org-agenda-undo-has-started-in nil + org-agenda-pending-undo-list org-agenda-undo-list)) + (if (not org-agenda-pending-undo-list) + (error "No further undo information")) + (let* ((entry (pop org-agenda-pending-undo-list)) + buf line cmd rembuf) + (setq cmd (pop entry) line (pop entry)) + (setq rembuf (nth 2 entry)) + (org-with-remote-undo rembuf + (while (bufferp (setq buf (pop entry))) + (if (pop entry) + (with-current-buffer buf + (let ((last-undo-buffer buf) + buffer-read-only) + (unless (memq buf org-agenda-undo-has-started-in) + (push buf org-agenda-undo-has-started-in) + (make-local-variable 'pending-undo-list) + (undo-start)) + (while (and pending-undo-list + (listp pending-undo-list) + (not (car pending-undo-list))) + (pop pending-undo-list)) + (undo-more 1)))))) + (goto-line line) + (message "`%s' undone (buffer %s)" cmd (buffer-name rembuf)))) + +(defun org-verify-change-for-undo (l1 l2) + "Verify that a real change occurred between the undo lists L1 and L2." + (while (and l1 (listp l1) (null (car l1))) (pop l1)) + (while (and l2 (listp l2) (null (car l2))) (pop l2)) + (not (eq l1 l2))) + +;;; Agenda dispatch + +(defvar org-agenda-restrict nil) +(defvar org-agenda-restrict-begin (make-marker)) +(defvar org-agenda-restrict-end (make-marker)) +(defvar org-agenda-last-dispatch-buffer nil) + +;;;###autoload +(defun org-agenda (arg) + "Dispatch agenda commands to collect entries to the agenda buffer. +Prompts for a character to select a command. Any prefix arg will be passed +on to the selected command. The default selections are: +g +a Call `org-agenda-list' to display the agenda for current day or week. +t Call `org-todo-list' to display the global todo list. +T Call `org-todo-list' to display the global todo list, select only + entries with a specific TODO keyword (the user gets a prompt). +m Call `org-tags-view' to display headlines with tags matching + a condition (the user is prompted for the condition). +M Like `m', but select only TODO entries, no ordinary headlines. +l Create a timeeline for the current buffer. + +More commands can be added by configuring the variable +`org-agenda-custom-commands'. In particular, specific tags and TODO keyword +searches can be pre-defined in this way. + +If the current buffer is in Org-mode and visiting a file, you can also +first press `1' to indicate that the agenda should be temporarily (until the +next use of \\[org-agenda]) restricted to the current file." + (interactive "P") + (catch 'exit + (let* ((buf (current-buffer)) + (bfn (buffer-file-name (buffer-base-buffer))) + (restrict-ok (and bfn (org-mode-p))) + (custom org-agenda-custom-commands) + c entry key type match lprops header) + ;; Turn off restriction + (put 'org-agenda-files 'org-restrict nil) + (setq org-agenda-restrict nil) + (move-marker org-agenda-restrict-begin nil) + (move-marker org-agenda-restrict-end nil) + ;; Remember where this call originated + (setq org-agenda-last-dispatch-buffer (current-buffer)) + (save-window-excursion + (delete-other-windows) + (switch-to-buffer-other-window " *Agenda Commands*") + (erase-buffer) + (insert (eval-when-compile + (let ((header +"Press key for an agenda command: +-------------------------------- C Configure custom agenda commands +a Agenda for current week or day +t List of all TODO entries T Entries with special TODO kwd +m Match a TAGS query M Like m, but only TODO entries +L Timeline for current buffer # List stuck projects (!=configure) +") + (start 0)) + (while (string-match "\\(^\\| \\|(\\)\\(\\S-\\)\\( \\|=\\)" header start) + (setq start (match-end 0)) + (add-text-properties (match-beginning 2) (match-end 2) + '(face bold) header)) + header))) + (while (setq entry (pop custom)) + (setq key (car entry) type (nth 1 entry) match (nth 2 entry)) + (insert (format "\n%-4s%-14s: %s" + (org-add-props (copy-sequence key) + '(face bold)) + (cond + ((stringp type) type) + ((eq type 'tags) "Tags query") + ((eq type 'todo) "TODO keyword") + ((eq type 'tags-tree) "Tags (TODO)") + ((eq type 'tags-tree) "Tags tree") + ((eq type 'todo-tree) "TODO kwd tree") + ((eq type 'occur-tree) "Occur tree") + ((functionp type) (symbol-name type)) + (t "???")) + (if (stringp match) + (org-add-props match nil 'face 'org-warning) + (format "set of %d commands" (+ -2 (length entry))))))) + (if restrict-ok + (insert "\n" + (org-add-props "1 Restrict call to current buffer 0 Restrict call to region or subtree" nil 'face 'org-table))) + + (goto-char (point-min)) + (if (fboundp 'fit-window-to-buffer) (fit-window-to-buffer)) + (message "Press key for agenda command%s" + (if restrict-ok ", or [1] or [0] to restrict" "")) + (setq c (read-char-exclusive)) + (message "") + (when (memq c '(?L ?1 ?0)) + (if restrict-ok + (put 'org-agenda-files 'org-restrict (list bfn)) + (error "Cannot restrict agenda to current buffer")) + (with-current-buffer " *Agenda Commands*" + (goto-char (point-max)) + (delete-region (point-at-bol) (point)) + (goto-char (point-min))) + (when (eq c ?0) + (setq org-agenda-restrict t) + (with-current-buffer buf + (if (org-region-active-p) + (progn + (move-marker org-agenda-restrict-begin (region-beginning)) + (move-marker org-agenda-restrict-end (region-end))) + (save-excursion + (org-back-to-heading t) + (move-marker org-agenda-restrict-begin (point)) + (move-marker org-agenda-restrict-end + (progn (org-end-of-subtree t))))))) + (unless (eq c ?L) + (message "Press key for agenda command%s" + (if restrict-ok " (restricted to current file)" "")) + (setq c (read-char-exclusive))) + (message ""))) + (require 'calendar) ; FIXME: can we avoid this for some commands? + ;; For example the todo list should not need it (but does...) + (cond + ((setq entry (assoc (char-to-string c) org-agenda-custom-commands)) + (if (symbolp (nth 1 entry)) + (progn + (setq type (nth 1 entry) match (nth 2 entry) lprops (nth 3 entry) + lprops (nth 3 entry)) + (cond + ((eq type 'tags) + (org-let lprops '(org-tags-view current-prefix-arg match))) + ((eq type 'tags-todo) + (org-let lprops '(org-tags-view '(4) match))) + ((eq type 'todo) + (org-let lprops '(org-todo-list match))) + ((eq type 'tags-tree) + (org-check-for-org-mode) + (org-let lprops '(org-tags-sparse-tree current-prefix-arg match))) + ((eq type 'todo-tree) + (org-check-for-org-mode) + (org-let lprops + '(org-occur (concat "^" outline-regexp "[ \t]*" + (regexp-quote match) "\\>")))) + ((eq type 'occur-tree) + (org-check-for-org-mode) + (org-let lprops '(org-occur match))) + ((fboundp type) + (org-let lprops '(funcall type match))) + (t (error "Invalid custom agenda command type %s" type)))) + (org-run-agenda-series (cddr entry)))) + ((equal c ?C) (customize-variable 'org-agenda-custom-commands)) + ((equal c ?a) (call-interactively 'org-agenda-list)) + ((equal c ?t) (call-interactively 'org-todo-list)) + ((equal c ?T) (org-call-with-arg 'org-todo-list (or arg '(4)))) + ((equal c ?m) (call-interactively 'org-tags-view)) + ((equal c ?M) (org-call-with-arg 'org-tags-view (or arg '(4)))) + ((equal c ?L) + (unless restrict-ok + (error "This is not an Org-mode file")) + (org-call-with-arg 'org-timeline arg)) + ((equal c ?#) (call-interactively 'org-agenda-list-stuck-projects)) + ((equal c ?!) (customize-variable 'org-stuck-projects)) + (t (error "Invalid key")))))) + +;; FIXME: what is the meaning of WINDOW????? +(defun org-run-agenda-series (series &optional window) + (org-prepare-agenda) + (let* ((org-agenda-multi t) + (redo (list 'org-run-agenda-series (list 'quote series))) + (org-select-agenda-window t) + (cmds (car series)) + (gprops (nth 1 series)) + match ;; The byte compiler incorrectly complains about this. Keep it! + cmd type lprops) + (while (setq cmd (pop cmds)) + (setq type (car cmd) match (nth 1 cmd) lprops (nth 2 cmd)) + (cond + ((eq type 'agenda) + (call-interactively 'org-agenda-list)) + ((eq type 'alltodo) + (call-interactively 'org-todo-list)) + ((eq type 'tags) + (org-let2 gprops lprops + '(org-tags-view current-prefix-arg match))) + ((eq type 'tags-todo) + (org-let2 gprops lprops + '(org-tags-view '(4) match))) + ((eq type 'todo) + (org-let2 gprops lprops + '(org-todo-list match))) + ((fboundp type) + (org-let2 gprops lprops + '(funcall type match))) + (t (error "Invalid type in command series")))) + (widen) + (setq org-agenda-redo-command redo) + (goto-char (point-min))) + (org-finalize-agenda)) + +;;;###autoload +(defmacro org-batch-agenda (cmd-key &rest parameters) + "Run an agenda command in batch mode, send result to STDOUT. +CMD-KEY is a string that is also a key in `org-agenda-custom-commands'. +Paramters are alternating variable names and values that will be bound +before running the agenda command." + (let (pars) + (while parameters + (push (list (pop parameters) (if parameters (pop parameters))) pars)) + (flet ((read-char-exclusive () (string-to-char cmd-key))) + (eval (list 'let (nreverse pars) '(org-agenda nil)))) + (set-buffer "*Org Agenda*") + (princ (buffer-string)))) + +(defmacro org-no-read-only (&rest body) + "Inhibit read-only for BODY." + `(let ((inhibit-read-only t)) ,@body)) + +(defun org-check-for-org-mode () + "Make sure current buffer is in org-mode. Error if not." + (or (org-mode-p) + (error "Cannot execute org-mode agenda command on buffer in %s." + major-mode))) + +(defun org-fit-agenda-window () + "Fit the window to the buffer size." + (and org-fit-agenda-window + (memq org-agenda-window-setup '(reorganize-frame)) + (fboundp 'fit-window-to-buffer) + (fit-window-to-buffer nil (/ (* (frame-height) 3) 4) + (/ (frame-height) 2)))) + +;;; Agenda file list + +(defun org-agenda-files (&optional unrestricted) + "Get the list of agenda files. +Optional UNRESTRICTED means return the full list even if a restriction +is currently in place." + (cond + ((and (not unrestricted) (get 'org-agenda-files 'org-restrict))) + ((stringp org-agenda-files) (org-read-agenda-file-list)) + ((listp org-agenda-files) org-agenda-files) + (t (error "Invalid value of `org-agenda-files'")))) + +(defun org-edit-agenda-file-list () + "Edit the list of agenda files. +Depending on setup, this either uses customize to edit the variable +`org-agenda-files', or it visits the file that is holding the list. In the +latter case, the buffer is set up in a way that saving it automatically kills +the buffer and restores the previous window configuration." + (interactive) + (if (stringp org-agenda-files) + (let ((cw (current-window-configuration))) + (find-file org-agenda-files) + (org-set-local 'org-window-configuration cw) + (org-add-hook 'after-save-hook + (lambda () + (set-window-configuration + (prog1 org-window-configuration + (kill-buffer (current-buffer)))) + (org-install-agenda-files-menu) + (message "New agenda file list installed")) + nil 'local) + (message (substitute-command-keys + "Edit list and finish with \\[save-buffer]"))) + (customize-variable 'org-agenda-files))) + +(defun org-store-new-agenda-file-list (list) + "Set new value for the agenda file list and save it correcly." + (if (stringp org-agenda-files) + (let ((f org-agenda-files) b) + (while (setq b (find-buffer-visiting f)) (kill-buffer b)) + (with-temp-file f + (insert (mapconcat 'identity list "\n") "\n"))) + (let ((org-mode-hook nil) (default-major-mode 'fundamental-mode)) + (setq org-agenda-files list) + (customize-save-variable 'org-agenda-files org-agenda-files)))) + +(defun org-read-agenda-file-list () + "Read the list of agenda files from a file." + (when (stringp org-agenda-files) + (with-temp-buffer + (insert-file-contents org-agenda-files) + (org-split-string (buffer-string) "[ \t\r\n]*?[\r\n][ \t\r\n]*")))) + + +;;;###autoload +(defun org-cycle-agenda-files () + "Cycle through the files in `org-agenda-files'. +If the current buffer visits an agenda file, find the next one in the list. +If the current buffer does not, find the first agenda file." + (interactive) + (let* ((fs (org-agenda-files t)) + (files (append fs (list (car fs)))) + (tcf (if buffer-file-name (file-truename buffer-file-name))) + file) + (unless files (error "No agenda files")) + (catch 'exit + (while (setq file (pop files)) + (if (equal (file-truename file) tcf) + (when (car files) + (find-file (car files)) + (throw 'exit t)))) + (find-file (car fs))))) + +(defun org-agenda-file-to-end () + "Move/add the current file to the end of the agenda file list. +If the file is not present in the list, it is appended to the list. If it is +present, it is moved there." + (interactive) + (org-agenda-file-to-front 'to-end)) + +(defun org-agenda-file-to-front (&optional to-end) + "Move/add the current file to the top of the agenda file list. +If the file is not present in the list, it is added to the front. If it is +present, it is moved there. With optional argument TO-END, add/move to the +end of the list." + (interactive "P") + (let ((file-alist (mapcar (lambda (x) + (cons (file-truename x) x)) + (org-agenda-files t))) + (ctf (file-truename buffer-file-name)) + x had) + (setq x (assoc ctf file-alist) had x) + + (if (not x) (setq x (cons ctf (abbreviate-file-name buffer-file-name)))) + (if to-end + (setq file-alist (append (delq x file-alist) (list x))) + (setq file-alist (cons x (delq x file-alist)))) + (org-store-new-agenda-file-list (mapcar 'cdr file-alist)) + (org-install-agenda-files-menu) + (message "File %s to %s of agenda file list" + (if had "moved" "added") (if to-end "end" "front")))) + +(defun org-remove-file (&optional file) + "Remove current file from the list of files in variable `org-agenda-files'. +These are the files which are being checked for agenda entries. +Optional argument FILE means, use this file instead of the current." + (interactive) + (let* ((file (or file buffer-file-name)) + (true-file (file-truename file)) + (afile (abbreviate-file-name file)) + (files (delq nil (mapcar + (lambda (x) + (if (equal true-file + (file-truename x)) + nil x)) + (org-agenda-files t))))) + (if (not (= (length files) (length (org-agenda-files t)))) + (progn + (org-store-new-agenda-file-list files) + (org-install-agenda-files-menu) + (message "Removed file: %s" afile)) + (message "File was not in list: %s" afile)))) + +(defun org-file-menu-entry (file) + (vector file (list 'find-file file) t)) + +(defun org-check-agenda-file (file) + "Make sure FILE exists. If not, ask user what to do." + (when (not (file-exists-p file)) + (message "non-existent file %s. [R]emove from list or [A]bort?" + (abbreviate-file-name file)) + (let ((r (downcase (read-char-exclusive)))) + (cond + ((equal r ?r) + (org-remove-file file) + (throw 'nextfile t)) + (t (error "Abort")))))) + +;;; Agenda prepare and finalize + +(defvar org-agenda-multi nil) ; dynammically scoped +(defvar org-agenda-buffer-name "*Org Agenda*") +(defvar org-pre-agenda-window-conf nil) +(defun org-prepare-agenda () + (if org-agenda-multi + (progn + (setq buffer-read-only nil) + (goto-char (point-max)) + (unless (= (point) 1) + (insert "\n" (make-string (window-width) ?=) "\n")) + (narrow-to-region (point) (point-max))) + (org-agenda-maybe-reset-markers 'force) + (org-prepare-agenda-buffers (org-agenda-files)) + (let* ((abuf (get-buffer-create org-agenda-buffer-name)) + (awin (get-buffer-window abuf))) + (cond + ((equal (current-buffer) abuf) nil) + (awin (select-window awin)) + ((not (setq org-pre-agenda-window-conf (current-window-configuration)))) + ((equal org-agenda-window-setup 'current-window) + (switch-to-buffer abuf)) + ((equal org-agenda-window-setup 'other-window) + (switch-to-buffer-other-window abuf)) + ((equal org-agenda-window-setup 'other-frame) + (switch-to-buffer-other-frame abuf)) + ((equal org-agenda-window-setup 'reorganize-frame) + (delete-other-windows) + (switch-to-buffer-other-window abuf)))) + (setq buffer-read-only nil) + (erase-buffer) + (org-agenda-mode)) + (setq buffer-read-only nil)) + +(defun org-finalize-agenda () + "Finishing touch for the agenda buffer, called just before displaying it." + (unless org-agenda-multi + (org-agenda-align-tags) + (save-excursion + (let ((buffer-read-only)) + (goto-char (point-min)) + (while (org-activate-bracket-links (point-max)) + (add-text-properties (match-beginning 0) (match-end 0) + '(face org-link)))) + (run-hooks 'org-finalize-agenda-hook)))) + +(defun org-prepare-agenda-buffers (files) + "Create buffers for all agenda files, protect archived trees and comments." + (interactive) + (let ((pa '(:org-archived t)) + (pc '(:org-comment t)) + (pall '(:org-archived t :org-comment t)) + (rea (concat ":" org-archive-tag ":")) + bmp file re) + (save-excursion + (while (setq file (pop files)) + (org-check-agenda-file file) + (set-buffer (org-get-agenda-file-buffer file)) + (widen) + (setq bmp (buffer-modified-p)) + (save-excursion + (remove-text-properties (point-min) (point-max) pall) + (when org-agenda-skip-archived-trees + (goto-char (point-min)) + (while (re-search-forward rea nil t) + (if (org-on-heading-p) + (add-text-properties (point-at-bol) (org-end-of-subtree t) pa)))) + (goto-char (point-min)) + (setq re (concat "^\\*+ +" org-comment-string "\\>")) + (while (re-search-forward re nil t) + (add-text-properties + (match-beginning 0) (org-end-of-subtree t) pc))) + (set-buffer-modified-p bmp))))) + +(defvar org-agenda-skip-function nil + "Function to be called at each match during agenda construction. +If this function return nil, the current match should not be skipped. +Otherwise, the function must return a position from where the search +should be continued. +Never set this variable using `setq' or so, because then it will apply +to all future agenda commands. Instead, bind it with `let' to scope +it dynamically into the agenda-constructing command.") + +(defun org-agenda-skip () + "Throw to `:skip' in places that should be skipped." + (let ((p (point-at-bol)) to) + (and org-agenda-skip-archived-trees + (get-text-property p :org-archived) + (org-end-of-subtree t) + (throw :skip t)) + (and (get-text-property p :org-comment) + (org-end-of-subtree t) + (throw :skip t)) + (if (equal (char-after p) ?#) (throw :skip t)) + (when (and (functionp org-agenda-skip-function) + (setq to (save-excursion + (save-match-data + (funcall org-agenda-skip-function))))) + (goto-char to) + (throw :skip t)))) + +(defvar org-agenda-markers nil + "List of all currently active markers created by `org-agenda'.") +(defvar org-agenda-last-marker-time (time-to-seconds (current-time)) + "Creation time of the last agenda marker.") + +(defun org-agenda-new-marker (&optional pos) + "Return a new agenda marker. +Org-mode keeps a list of these markers and resets them when they are +no longer in use." + (let ((m (copy-marker (or pos (point))))) + (setq org-agenda-last-marker-time (time-to-seconds (current-time))) + (push m org-agenda-markers) + m)) + +(defun org-agenda-maybe-reset-markers (&optional force) + "Reset markers created by `org-agenda'. But only if they are old enough." + (if (or (and force (not org-agenda-multi)) + (> (- (time-to-seconds (current-time)) + org-agenda-last-marker-time) + 5)) + (while org-agenda-markers + (move-marker (pop org-agenda-markers) nil)))) + +(defvar org-agenda-new-buffers nil + "Buffers created to visit agenda files.") + +(defun org-get-agenda-file-buffer (file) + "Get a buffer visiting FILE. If the buffer needs to be created, add +it to the list of buffers which might be released later." + (let ((buf (find-buffer-visiting file))) + (if buf + buf ; just return it + ;; Make a new buffer and remember it + (setq buf (find-file-noselect file)) + (if buf (push buf org-agenda-new-buffers)) + buf))) + +(defun org-release-buffers (blist) + "Release all buffers in list, asking the user for confirmation when needed. +When a buffer is unmodified, it is just killed. When modified, it is saved +\(if the user agrees) and then killed." + (let (buf file) + (while (setq buf (pop blist)) + (setq file (buffer-file-name buf)) + (when (and (buffer-modified-p buf) + file + (y-or-n-p (format "Save file %s? " file))) + (with-current-buffer buf (save-buffer))) + (kill-buffer buf)))) + +(defvar org-category-table nil) +(defun org-get-category-table () + "Get the table of categories and positions in current buffer." + (let (tbl) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "\\(^\\|\r\\)#\\+CATEGORY:[ \t]*\\(.*\\)" nil t) + (push (cons (point) (org-trim (match-string 2))) tbl))) + tbl)) + +(defun org-get-category (&optional pos) + "Get the category applying to position POS." + (if (not org-category-table) + (cond + ((null org-category) + (setq org-category + (if buffer-file-name + (file-name-sans-extension + (file-name-nondirectory buffer-file-name)) + "???"))) + ((symbolp org-category) (symbol-name org-category)) + (t org-category)) + (let ((tbl org-category-table) + (pos (or pos (point)))) + (while (and tbl (> (caar tbl) pos)) + (pop tbl)) + (or (cdar tbl) (cdr (nth (1- (length org-category-table)) + org-category-table)))))) +;;; Agenda timeline + +(defun org-timeline (&optional include-all) + "Show a time-sorted view of the entries in the current org file. +Only entries with a time stamp of today or later will be listed. With +\\[universal-argument] prefix, all unfinished TODO items will also be shown, +under the current date. +If the buffer contains an active region, only check the region for +dates." + (interactive "P") + (require 'calendar) + (org-compile-prefix-format 'timeline) + (org-set-sorting-strategy 'timeline) + (let* ((dopast t) + (dotodo include-all) + (doclosed org-agenda-show-log) + (entry buffer-file-name) + (date (calendar-current-date)) + (win (selected-window)) + (pos1 (point)) + (beg (if (org-region-active-p) (region-beginning) (point-min))) + (end (if (org-region-active-p) (region-end) (point-max))) + (day-numbers (org-get-all-dates beg end 'no-ranges + t doclosed ; always include today + org-timeline-show-empty-dates)) + (today (time-to-days (current-time))) + (past t) + args + s e rtn d emptyp) + (setq org-agenda-redo-command + (list 'progn + (list 'switch-to-buffer-other-window (current-buffer)) + (list 'org-timeline (list 'quote include-all)))) + (if (not dopast) + ;; Remove past dates from the list of dates. + (setq day-numbers (delq nil (mapcar (lambda(x) + (if (>= x today) x nil)) + day-numbers)))) + (org-prepare-agenda) + (if doclosed (push :closed args)) + (push :timestamp args) + (if dotodo (push :todo args)) + (while (setq d (pop day-numbers)) + (if (and (listp d) (eq (car d) :omitted)) + (progn + (setq s (point)) + (insert (format "\n[... %d empty days omitted]\n\n" (cdr d))) + (put-text-property s (1- (point)) 'face 'org-level-3)) + (if (listp d) (setq d (car d) emptyp t) (setq emptyp nil)) + (if (and (>= d today) + dopast + past) + (progn + (setq past nil) + (insert (make-string 79 ?-) "\n"))) + (setq date (calendar-gregorian-from-absolute d)) + (setq s (point)) + (setq rtn (and (not emptyp) + (apply 'org-agenda-get-day-entries + entry date args))) + (if (or rtn (equal d today) org-timeline-show-empty-dates) + (progn + (insert (calendar-day-name date) " " + (number-to-string (extract-calendar-day date)) " " + (calendar-month-name (extract-calendar-month date)) " " + (number-to-string (extract-calendar-year date)) "\n") + (put-text-property s (1- (point)) 'face + 'org-level-3) + (if (equal d today) + (put-text-property s (1- (point)) 'org-today t)) + (and rtn (insert (org-finalize-agenda-entries rtn) "\n")) + (put-text-property s (1- (point)) 'day d))))) + (goto-char (point-min)) + (goto-char (or (text-property-any (point-min) (point-max) 'org-today t) + (point-min))) + (add-text-properties (point-min) (point-max) '(org-agenda-type timeline)) + (org-finalize-agenda) + (setq buffer-read-only t) + (when (not org-select-agenda-window) + (select-window win) + (goto-char pos1)))) +(defun org-get-all-dates (beg end &optional no-ranges force-today inactive empty) + "Return a list of all relevant day numbers from BEG to END buffer positions. +If NO-RANGES is non-nil, include only the start and end dates of a range, +not every single day in the range. If FORCE-TODAY is non-nil, make +sure that TODAY is included in the list. If INACTIVE is non-nil, also +inactive time stamps (those in square brackets) are included. +When EMPTY is non-nil, also include days without any entries." + (let ((re (if inactive org-ts-regexp-both org-ts-regexp)) + dates dates1 date day day1 day2 ts1 ts2) + (if force-today + (setq dates (list (time-to-days (current-time))))) + (save-excursion + (goto-char beg) + (while (re-search-forward re end t) + (setq day (time-to-days (org-time-string-to-time + (substring (match-string 1) 0 10)))) + (or (memq day dates) (push day dates))) + (unless no-ranges + (goto-char beg) + (while (re-search-forward org-tr-regexp end t) + (setq ts1 (substring (match-string 1) 0 10) + ts2 (substring (match-string 2) 0 10) + day1 (time-to-days (org-time-string-to-time ts1)) + day2 (time-to-days (org-time-string-to-time ts2))) + (while (< (setq day1 (1+ day1)) day2) + (or (memq day1 dates) (push day1 dates))))) + (setq dates (sort dates '<)) + (when empty + (while (setq day (pop dates)) + (setq day2 (car dates)) + (push day dates1) + (when (and day2 empty) + (if (or (eq empty t) + (and (numberp empty) (<= (- day2 day) empty))) + (while (< (setq day (1+ day)) day2) + (push (list day) dates1)) + (push (cons :omitted (- day2 day)) dates1)))) + (setq dates (nreverse dates1))) + dates))) + +;;; Agenda Daily/Weekly + +(defvar org-agenda-overriding-arguments nil) ; dynamically scoped parameter +(defvar org-agenda-last-arguments nil + "The arguments of the previous call to org-agenda") +(defvar org-starting-day nil) ; local variable in the agenda buffer +(defvar org-include-all-loc nil) ; local variable + + +;;;###autoload +(defun org-agenda-list (&optional include-all start-day ndays) + "Produce a weekly view from all files in variable `org-agenda-files'. +The view will be for the current week, but from the overview buffer you +will be able to go to other weeks. +With one \\[universal-argument] prefix argument INCLUDE-ALL, all unfinished TODO items will +also be shown, under the current date. +With two \\[universal-argument] prefix argument INCLUDE-ALL, all TODO entries marked DONE +on the days are also shown. See the variable `org-log-done' for how +to turn on logging. +START-DAY defaults to TODAY, or to the most recent match for the weekday +given in `org-agenda-start-on-weekday'. +NDAYS defaults to `org-agenda-ndays'." + (interactive "P") + (if org-agenda-overriding-arguments + (setq include-all (car org-agenda-overriding-arguments) + start-day (nth 1 org-agenda-overriding-arguments) + ndays (nth 2 org-agenda-overriding-arguments))) + (setq org-agenda-last-arguments (list include-all start-day ndays)) + (org-compile-prefix-format 'agenda) + (org-set-sorting-strategy 'agenda) + (require 'calendar) + (let* ((org-agenda-start-on-weekday + (if (or (equal ndays 1) + (and (null ndays) (equal 1 org-agenda-ndays))) + nil org-agenda-start-on-weekday)) + (thefiles (org-agenda-files)) + (files thefiles) + (win (selected-window)) + (today (time-to-days (current-time))) + (sd (or start-day today)) + (start (if (or (null org-agenda-start-on-weekday) + (< org-agenda-ndays 7)) + sd + (let* ((nt (calendar-day-of-week + (calendar-gregorian-from-absolute sd))) + (n1 org-agenda-start-on-weekday) + (d (- nt n1))) + (- sd (+ (if (< d 0) 7 0) d))))) + (day-numbers (list start)) + (inhibit-redisplay t) + s e rtn rtnall file date d start-pos end-pos todayp nd) + (setq org-agenda-redo-command + (list 'org-agenda-list (list 'quote include-all) start-day ndays)) + ;; Make the list of days + (setq ndays (or ndays org-agenda-ndays) + nd ndays) + (while (> ndays 1) + (push (1+ (car day-numbers)) day-numbers) + (setq ndays (1- ndays))) + (setq day-numbers (nreverse day-numbers)) + (org-prepare-agenda) + (org-set-local 'org-starting-day (car day-numbers)) + (org-set-local 'org-include-all-loc include-all) + (when (and (or include-all org-agenda-include-all-todo) + (member today day-numbers)) + (setq files thefiles + rtnall nil) + (while (setq file (pop files)) + (catch 'nextfile + (org-check-agenda-file file) + (setq date (calendar-gregorian-from-absolute today) + rtn (org-agenda-get-day-entries + file date :todo)) + (setq rtnall (append rtnall rtn)))) + (when rtnall + (insert "ALL CURRENTLY OPEN TODO ITEMS:\n") + (add-text-properties (point-min) (1- (point)) + (list 'face 'org-level-3)) + (insert (org-finalize-agenda-entries rtnall) "\n"))) + (setq s (point)) + (insert (if (= nd 7) "Week-" "Day-") "agenda:\n") + (add-text-properties s (1- (point)) (list 'face 'org-level-3)) + (while (setq d (pop day-numbers)) + (setq date (calendar-gregorian-from-absolute d) + s (point)) + (if (or (setq todayp (= d today)) + (and (not start-pos) (= d sd))) + (setq start-pos (point)) + (if (and start-pos (not end-pos)) + (setq end-pos (point)))) + (setq files thefiles + rtnall nil) + (while (setq file (pop files)) + (catch 'nextfile + (org-check-agenda-file file) + (if org-agenda-show-log + (setq rtn (org-agenda-get-day-entries + file date + :deadline :scheduled :timestamp :closed)) + (setq rtn (org-agenda-get-day-entries + file date + :deadline :scheduled :timestamp))) + (setq rtnall (append rtnall rtn)))) + (if org-agenda-include-diary + (progn + (require 'diary-lib) + (setq rtn (org-get-entries-from-diary date)) + (setq rtnall (append rtnall rtn)))) + (if (or rtnall org-agenda-show-all-dates) + (progn + (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-level-3) + (if todayp (put-text-property s (1- (point)) 'org-today t)) + + (if rtnall (insert + (org-finalize-agenda-entries + (org-agenda-add-time-grid-maybe + rtnall nd todayp)) + "\n")) + (put-text-property s (1- (point)) 'day d)))) + (goto-char (point-min)) + (org-fit-agenda-window) + (unless (and (pos-visible-in-window-p (point-min)) + (pos-visible-in-window-p (point-max))) + (goto-char (1- (point-max))) + (recenter -1) + (if (not (pos-visible-in-window-p (or start-pos 1))) + (progn + (goto-char (or start-pos 1)) + (recenter 1)))) + (goto-char (or start-pos 1)) + (add-text-properties (point-min) (point-max) '(org-agenda-type agenda)) + (org-finalize-agenda) + (setq buffer-read-only t) + (if (not org-select-agenda-window) (select-window win)) + (message ""))) + +;;; Agenda TODO list + +(defvar org-select-this-todo-keyword nil) +(defvar org-last-arg nil) + +;;;###autoload +(defun org-todo-list (arg) + "Show all TODO entries from all agenda file in a single list. +The prefix arg can be used to select a specific TODO keyword and limit +the list to these. When using \\[universal-argument], you will be prompted +for a keyword. A numeric prefix directly selects the Nth keyword in +`org-todo-keywords'." + (interactive "P") + (require 'calendar) + (org-compile-prefix-format 'todo) + (org-set-sorting-strategy 'todo) + (let* ((today (time-to-days (current-time))) + (date (calendar-gregorian-from-absolute today)) + (win (selected-window)) + (kwds org-todo-keywords) + (completion-ignore-case t) + (org-select-this-todo-keyword + (if (stringp arg) arg + (and arg (integerp arg) (> arg 0) + (nth (1- arg) org-todo-keywords)))) + rtn rtnall files file pos) + (when (equal arg '(4)) + (setq org-select-this-todo-keyword + (completing-read "Keyword: " (mapcar 'list org-todo-keywords) + nil t))) + (and (equal 0 arg) (setq org-select-this-todo-keyword nil)) + (org-prepare-agenda) + (org-set-local 'org-last-arg arg) + (org-set-local 'org-todo-keywords kwds) + (setq org-agenda-redo-command + '(org-todo-list (or current-prefix-arg org-last-arg))) + (setq files (org-agenda-files) + rtnall nil) + (while (setq file (pop files)) + (catch 'nextfile + (org-check-agenda-file file) + (setq rtn (org-agenda-get-day-entries file date :todo)) + (setq rtnall (append rtnall rtn)))) + (if org-agenda-overriding-header + (insert (org-add-props (copy-sequence org-agenda-overriding-header) + nil 'face 'org-level-3) "\n") + (insert "Global list of TODO items of type: ") + (add-text-properties (point-min) (1- (point)) + (list 'face 'org-level-3)) + (setq pos (point)) + (insert (or org-select-this-todo-keyword "ALL") "\n") + (add-text-properties pos (1- (point)) (list 'face 'org-warning)) + (setq pos (point)) + (unless org-agenda-multi + (insert + "Available with `N r': (0)ALL " + (let ((n 0)) + (mapconcat (lambda (x) + (format "(%d)%s" (setq n (1+ n)) x)) + org-todo-keywords " ")) + "\n")) + (add-text-properties pos (1- (point)) (list 'face 'org-level-3))) + (when rtnall + (insert (org-finalize-agenda-entries rtnall) "\n")) + (goto-char (point-min)) + (org-fit-agenda-window) + (add-text-properties (point-min) (point-max) '(org-agenda-type todo)) + (org-finalize-agenda) + (setq buffer-read-only t) + (if (not org-select-agenda-window) (select-window win)))) + +;;; Agenda tags match + +;;;###autoload +(defun org-tags-view (&optional todo-only match) + "Show all headlines for all `org-agenda-files' matching a TAGS criterion. +The prefix arg TODO-ONLY limits the search to TODO entries." + (interactive "P") + (org-compile-prefix-format 'tags) + (org-set-sorting-strategy 'tags) + (let* ((org-tags-match-list-sublevels + (if todo-only t org-tags-match-list-sublevels)) + (win (selected-window)) + (completion-ignore-case t) + rtn rtnall files file pos matcher + buffer) + (setq matcher (org-make-tags-matcher match) + match (car matcher) matcher (cdr matcher)) + (org-prepare-agenda) + (setq org-agenda-redo-command + (list 'org-tags-view (list 'quote todo-only) + (list 'if 'current-prefix-arg nil match))) + (setq files (org-agenda-files) + rtnall nil) + (while (setq file (pop files)) + (catch 'nextfile + (org-check-agenda-file file) + (setq buffer (if (file-exists-p file) + (org-get-agenda-file-buffer file) + (error "No such file %s" file))) + (if (not buffer) + ;; If file does not exist, merror message to agenda + (setq rtn (list + (format "ORG-AGENDA-ERROR: No such org-file %s" file)) + rtnall (append rtnall rtn)) + (with-current-buffer buffer + (unless (org-mode-p) + (error "Agenda file %s is not in `org-mode'" file)) + (setq org-category-table (org-get-category-table)) + (save-excursion + (save-restriction + (if org-agenda-restrict + (narrow-to-region org-agenda-restrict-begin + org-agenda-restrict-end) + (widen)) + (setq rtn (org-scan-tags 'agenda matcher todo-only)) + (setq rtnall (append rtnall rtn)))))))) + (if org-agenda-overriding-header + (insert (org-add-props (copy-sequence org-agenda-overriding-header) + nil 'face 'org-level-3) "\n") + (insert "Headlines with TAGS match: ") + (add-text-properties (point-min) (1- (point)) + (list 'face 'org-level-3)) + (setq pos (point)) + (insert match "\n") + (add-text-properties pos (1- (point)) (list 'face 'org-warning)) + (setq pos (point)) + (unless org-agenda-multi + (insert "Press `C-u r' to search again with new search string\n")) + (add-text-properties pos (1- (point)) (list 'face 'org-level-3))) + (when rtnall + (insert (org-finalize-agenda-entries rtnall) "\n")) + (goto-char (point-min)) + (org-fit-agenda-window) + (add-text-properties (point-min) (point-max) '(org-agenda-type tags)) + (org-finalize-agenda) + (setq buffer-read-only t) + (if (not org-select-agenda-window) (select-window win)))) + +;;; Agenda Finding stuck projects + +(defvar org-agenda-skip-regexp nil + "Regular expression used in skipping subtrees for the agenda. +This is basically a temporary global variable that can be set and then +used by user-defined selections using `org-agenda-skip-function'.") + +(defvar org-agenda-overriding-header nil + "When this is set during todo and tags searches, will replace header.") + +(defun org-agenda-skip-subtree-when-regexp-matches () + "Checks if the current subtree contains match for `org-agenda-skip-regexp'. +If yes, it returns the end position of this tree, causing agenda commands +to skip this subtree. This is a function that can be put into +`org-agenda-skip-function' for the duration of a command." + (save-match-data + (let ((end (save-excursion (org-end-of-subtree t))) + skip) + (save-excursion + (setq skip (re-search-forward org-agenda-skip-regexp end t))) + (and skip end)))) + +(defun org-agenda-list-stuck-projects (&rest ignore) + "Create agenda view for projects that are stuck. +Stuck projects are project that have no next actions. For the definitions +of what a project is and how to check if it stuck, customize the variable +`org-stuck-projects'. +MATCH is being ignored." + (interactive) + (let* ((org-agenda-skip-function 'org-agenda-skip-subtree-when-regexp-matches) + (org-agenda-overriding-header "List of stuck projects: ") + (matcher (nth 0 org-stuck-projects)) + (todo (nth 1 org-stuck-projects)) + (tags (nth 2 org-stuck-projects)) + (todo-re (concat "^\\*+[ \t]+\\(" + (mapconcat 'identity todo "\\|") + "\\)\\>")) + (tags-re (concat "^\\*+.*:\\(" + (mapconcat 'identity tags "\\|") + "\\):[a-zA-Z0-9_@:]*[ \t]*$"))) + + (setq org-agenda-skip-regexp + (cond + ((and todo tags) + (concat todo-re "\\|" tags-re)) + (todo todo-re) + (tags tags-re) + (t (error "No information how to identify unstuck projects")))) + (org-tags-view nil matcher))) + + +;;; Diary integration + +(defvar org-disable-agenda-to-diary nil) ;Dynamically-scoped param. + +(defun org-get-entries-from-diary (date) + "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 + (cons 'org-diary-default-entry list-diary-entries-hook)) + (diary-file-name-prefix-function nil) ; turn this feature off + (diary-modify-entry-list-string-function 'org-modify-diary-entry-string) + entries + (org-disable-agenda-to-diary t)) + (save-excursion + (save-window-excursion + (list-diary-entries date 1))) ;; Keep this name for now, compatibility + (if (not (get-buffer fancy-diary-buffer)) + (setq entries nil) + (with-current-buffer fancy-diary-buffer + (setq buffer-read-only nil) + (if (= (point-max) 1) + ;; No entries + (setq entries nil) + ;; Omit the date and other unnecessary stuff + (org-agenda-cleanup-fancy-diary) + ;; Add prefix to each line and extend the text properties + (if (= (point-max) 1) + (setq entries nil) + (setq entries (buffer-substring (point-min) (- (point-max) 1))))) + (set-buffer-modified-p nil) + (kill-buffer fancy-diary-buffer))) + (when entries + (setq entries (org-split-string entries "\n")) + (setq entries + (mapcar + (lambda (x) + (setq x (org-format-agenda-item "" x "Diary" nil 'time)) + ;; Extend the text properties to the beginning of the line + (org-add-props x (text-properties-at (1- (length x)) x))) + entries))))) + +(defun org-agenda-cleanup-fancy-diary () + "Remove unwanted stuff in buffer created by `fancy-diary-display'. +This gets rid of the date, the underline under the date, and +the dummy entry installed by `org-mode' to ensure non-empty diary for each +date. It also removes lines that contain only whitespace." + (goto-char (point-min)) + (if (looking-at ".*?:[ \t]*") + (progn + (replace-match "") + (re-search-forward "\n=+$" nil t) + (replace-match "") + (while (re-search-backward "^ +\n?" nil t) (replace-match ""))) + (re-search-forward "\n=+$" nil t) + (delete-region (point-min) (min (point-max) (1+ (match-end 0))))) + (goto-char (point-min)) + (while (re-search-forward "^ +\n" nil t) + (replace-match "")) + (goto-char (point-min)) + (if (re-search-forward "^Org-mode dummy\n?" nil t) + (replace-match ""))) + +;; Make sure entries from the diary have the right text properties. +(eval-after-load "diary-lib" + '(if (boundp 'diary-modify-entry-list-string-function) + ;; We can rely on the hook, nothing to do + nil + ;; Hook not avaiable, must use advice to make this work + (defadvice add-to-diary-list (before org-mark-diary-entry activate) + "Make the position visible." + (if (and org-disable-agenda-to-diary ;; called from org-agenda + (stringp string) + buffer-file-name) + (setq string (org-modify-diary-entry-string string)))))) + +(defun org-modify-diary-entry-string (string) + "Add text properties to string, allowing org-mode to act on it." + (org-add-props string nil + 'mouse-face 'highlight + 'keymap org-agenda-keymap + 'help-echo (format "mouse-2 or RET jump to diary file %s" + (abbreviate-file-name buffer-file-name)) + 'org-agenda-diary-link t + 'org-marker (org-agenda-new-marker (point-at-bol)))) + +(defun org-diary-default-entry () + "Add a dummy entry to the diary. +Needed to avoid empty dates which mess up holiday display." + ;; Catch the error if dealing with the new add-to-diary-alist + (when org-disable-agenda-to-diary + (condition-case nil + (add-to-diary-list original-date "Org-mode dummy" "") + (error + (add-to-diary-list original-date "Org-mode dummy" "" nil))))) + +;;;###autoload +(defun org-diary (&rest args) + "Return diary information from org-files. +This function can be used in a \"sexp\" diary entry in the Emacs calendar. +It accesses org files and extracts information from those files to be +listed in the diary. The function accepts arguments specifying what +items should be listed. The following arguments are allowed: + + :timestamp List the headlines of items containing a date stamp or + date range matching the selected date. Deadlines will + also be listed, on the expiration day. + + :deadline List any deadlines past due, or due within + `org-deadline-warning-days'. The listing occurs only + in the diary for *today*, not at any other date. If + an entry is marked DONE, it is no longer listed. + + :scheduled List all items which are scheduled for the given date. + The diary for *today* also contains items which were + scheduled earlier and are not yet marked DONE. + + :todo List all TODO items from the org-file. This may be a + long list - so this is not turned on by default. + Like deadlines, these entries only show up in the + diary for *today*, not at any other date. + +The call in the diary file should look like this: + + &%%(org-diary) ~/path/to/some/orgfile.org + +Use a separate line for each org file to check. Or, if you omit the file name, +all files listed in `org-agenda-files' will be checked automatically: + + &%%(org-diary) + +If you don't give any arguments (as in the example above), the default +arguments (:deadline :scheduled :timestamp) are used. So the example above may +also be written as + + &%%(org-diary :deadline :timestamp :scheduled) + +The function expects the lisp variables `entry' and `date' to be provided +by the caller, because this is how the calendar works. Don't use this +function from a program - use `org-agenda-get-day-entries' instead." + (org-agenda-maybe-reset-markers) + (org-compile-prefix-format 'agenda) + (org-set-sorting-strategy 'agenda) + (setq args (or args '(:deadline :scheduled :timestamp))) + (let* ((files (if (and entry (stringp entry) (string-match "\\S-" entry)) + (list entry) + (org-agenda-files t))) + file rtn results) + ;; If this is called during org-agenda, don't return any entries to + ;; the calendar. Org Agenda will list these entries itself. + (if org-disable-agenda-to-diary (setq files nil)) + (while (setq file (pop files)) + (setq rtn (apply 'org-agenda-get-day-entries file date args)) + (setq results (append results rtn))) + (if results + (concat (org-finalize-agenda-entries results) "\n")))) + +;;; Agenda entry finders + +(defun org-agenda-get-day-entries (file date &rest args) + "Does the work for `org-diary' and `org-agenda'. +FILE is the path to a file to be checked for entries. DATE is date like +the one returned by `calendar-current-date'. ARGS are symbols indicating +which kind of entries should be extracted. For details about these, see +the documentation of `org-diary'." + (setq args (or args '(:deadline :scheduled :timestamp))) + (let* ((org-startup-with-deadline-check nil) + (org-startup-folded nil) + (org-startup-align-all-tables nil) + (buffer (if (file-exists-p file) + (org-get-agenda-file-buffer file) + (error "No such file %s" file))) + arg results rtn) + (if (not buffer) + ;; If file does not exist, make sure an error message ends up in diary + (list (format "ORG-AGENDA-ERROR: No such org-file %s" file)) + (with-current-buffer buffer + (unless (org-mode-p) + (error "Agenda file %s is not in `org-mode'" file)) + (setq org-category-table (org-get-category-table)) + (let ((case-fold-search nil)) + (save-excursion + (save-restriction + (if org-agenda-restrict + (narrow-to-region org-agenda-restrict-begin + org-agenda-restrict-end) + (widen)) + ;; The way we repeatedly append to `results' makes it O(n^2) :-( + (while (setq arg (pop args)) + (cond + ((and (eq arg :todo) + (equal date (calendar-current-date))) + (setq rtn (org-agenda-get-todos)) + (setq results (append results rtn))) + ((eq arg :timestamp) + (setq rtn (org-agenda-get-blocks)) + (setq results (append results rtn)) + (setq rtn (org-agenda-get-timestamps)) + (setq results (append results rtn))) + ((eq arg :scheduled) + (setq rtn (org-agenda-get-scheduled)) + (setq results (append results rtn))) + ((eq arg :closed) + (setq rtn (org-agenda-get-closed)) + (setq results (append results rtn))) + ((and (eq arg :deadline) + (equal date (calendar-current-date))) + (setq rtn (org-agenda-get-deadlines)) + (setq results (append results rtn)))))))) + results)))) + +(defun org-entry-is-done-p () + "Is the current entry marked DONE?" + (save-excursion + (and (re-search-backward "[\r\n]\\*" nil t) + (looking-at org-nl-done-regexp)))) + +(defun org-at-date-range-p (&optional inactive-ok) + "Is the cursor inside a date range?" + (interactive) + (save-excursion + (catch 'exit + (let ((pos (point))) + (skip-chars-backward "^[<\r\n") + (skip-chars-backward "<[") + (and (looking-at (if inactive-ok org-tr-regexp-both org-tr-regexp)) + (>= (match-end 0) pos) + (throw 'exit t)) + (skip-chars-backward "^<[\r\n") + (skip-chars-backward "<[") + (and (looking-at (if inactive-ok org-tr-regexp-both org-tr-regexp)) + (>= (match-end 0) pos) + (throw 'exit t))) + nil))) + +(defun org-agenda-get-todos () + "Return the TODO information for agenda display." + (let* ((props (list 'face nil + 'done-face 'org-done + 'org-not-done-regexp org-not-done-regexp + 'mouse-face 'highlight + 'keymap org-agenda-keymap + 'help-echo + (format "mouse-2 or RET jump to org file %s" + (abbreviate-file-name buffer-file-name)))) + (regexp (concat "[\n\r]\\*+ *\\(" + (if org-select-this-todo-keyword + (concat "\\<\\(" org-select-this-todo-keyword + "\\)\\>") + org-not-done-regexp) + "[^\n\r]*\\)")) + (deadline-re (concat ".*\\(\n[^*].*\\)?" org-deadline-time-regexp)) + (sched-re (concat ".*\\(\n[^*].*\\)?" org-scheduled-time-regexp)) +; FIXME why was this wrong? (sched-re (concat ".*\n?.*?" org-scheduled-time-regexp)) + marker priority category tags + ee txt) + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (catch :skip + (save-match-data + (beginning-of-line) + (when (or (and org-agenda-todo-ignore-scheduled + (looking-at sched-re)) + (and org-agenda-todo-ignore-deadlines + (looking-at deadline-re) + (org-deadline-close (match-string 2)))) + + ;; FIXME: the following test also happens below, but we need it here + (or org-agenda-todo-list-sublevels (org-end-of-subtree 'invisible)) + (throw :skip nil))) + (org-agenda-skip) + (goto-char (match-beginning 1)) + (setq marker (org-agenda-new-marker (1+ (match-beginning 0))) + category (org-get-category) + tags (org-get-tags-at (point)) + txt (org-format-agenda-item "" (match-string 1) category tags) + priority + (+ (org-get-priority txt) + (if org-todo-kwd-priority-p + (- org-todo-kwd-max-priority -2 + (length + (member (match-string 2) org-todo-keywords))) + 1))) + (org-add-props txt props + 'org-marker marker 'org-hd-marker marker + 'priority priority 'category category) + (push txt ee) + (if org-agenda-todo-list-sublevels + (goto-char (match-end 1)) + (org-end-of-subtree 'invisible)))) + (nreverse ee))) + +(defconst org-agenda-no-heading-message + "No heading for this item in buffer or region.") + +(defun org-agenda-get-timestamps () + "Return the date stamp information for agenda display." + (let* ((props (list 'face nil + 'org-not-done-regexp org-not-done-regexp + 'mouse-face 'highlight + 'keymap org-agenda-keymap + 'help-echo + (format "mouse-2 or RET jump to org file %s" + (abbreviate-file-name buffer-file-name)))) + (regexp (regexp-quote + (substring + (format-time-string + (car org-time-stamp-formats) + (apply 'encode-time ; DATE bound by calendar + (list 0 0 0 (nth 1 date) (car date) (nth 2 date)))) + 0 11))) + marker hdmarker deadlinep scheduledp donep tmp priority category + ee txt timestr tags) + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (catch :skip + (and (save-match-data (org-at-date-range-p)) (throw :skip nil)) + (org-agenda-skip) + (setq marker (org-agenda-new-marker (match-beginning 0)) + category (org-get-category (match-beginning 0)) + tmp (buffer-substring (max (point-min) + (- (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)) + (and org-agenda-skip-scheduled-if-done + scheduledp donep + (throw :skip t)) + (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 + (goto-char (match-end 1)) + (setq hdmarker (org-agenda-new-marker) + tags (org-get-tags-at)) + (looking-at "\\*+[ \t]*\\([^\r\n]+\\)") + (setq txt (org-format-agenda-item + (format "%s%s" + (if deadlinep "Deadline: " "") + (if scheduledp "Scheduled: " "")) + (match-string 1) category tags timestr))) + (setq txt org-agenda-no-heading-message)) + (setq priority (org-get-priority txt)) + (org-add-props txt props + 'org-marker marker 'org-hd-marker hdmarker) + (if deadlinep + (org-add-props txt nil + 'face (if donep 'org-done 'org-warning) + 'undone-face 'org-warning 'done-face 'org-done + 'category category 'priority (+ 100 priority)) + (if scheduledp + (org-add-props txt nil + 'face 'org-scheduled-today + 'undone-face 'org-scheduled-today 'done-face 'org-done + 'category category 'priority (+ 99 priority)) + (org-add-props txt nil 'priority priority 'category category))) + (push txt ee)) + (outline-next-heading))) + (nreverse ee))) + +(defun org-agenda-get-closed () + "Return the logged TODO entries for agenda display." + (let* ((props (list 'mouse-face 'highlight + 'org-not-done-regexp org-not-done-regexp + 'keymap org-agenda-keymap + 'help-echo + (format "mouse-2 or RET jump to org file %s" + (abbreviate-file-name buffer-file-name)))) + (regexp (concat + "\\<\\(" org-closed-string "\\|" org-clock-string "\\) *\\[" + (regexp-quote + (substring + (format-time-string + (car org-time-stamp-formats) + (apply 'encode-time ; DATE bound by calendar + (list 0 0 0 (nth 1 date) (car date) (nth 2 date)))) + 1 11)))) + marker hdmarker priority category tags closedp + ee txt timestr) + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (catch :skip + (org-agenda-skip) + (setq marker (org-agenda-new-marker (match-beginning 0)) + closedp (equal (match-string 1) org-closed-string) + category (org-get-category (match-beginning 0)) + timestr (buffer-substring (match-beginning 0) (point-at-eol)) + ;; 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 + (goto-char (match-end 1)) + (setq hdmarker (org-agenda-new-marker) + tags (org-get-tags-at)) + (looking-at "\\*+[ \t]*\\([^\r\n]+\\)") + (setq txt (org-format-agenda-item + (if closedp "Closed: " "Clocked: ") + (match-string 1) category tags timestr))) + (setq txt org-agenda-no-heading-message)) + (setq priority 100000) + (org-add-props txt props + 'org-marker marker 'org-hd-marker hdmarker 'face 'org-done + 'priority priority 'category category + 'undone-face 'org-warning 'done-face 'org-done) + (push txt ee)) + (outline-next-heading))) + (nreverse ee))) + +(defun org-agenda-get-deadlines () + "Return the deadline information for agenda display." + (let* ((wdays org-deadline-warning-days) + (props (list 'mouse-face 'highlight + 'org-not-done-regexp org-not-done-regexp + 'keymap org-agenda-keymap + 'help-echo + (format "mouse-2 or RET jump to org file %s" + (abbreviate-file-name buffer-file-name)))) + (regexp org-deadline-time-regexp) + (todayp (equal date (calendar-current-date))) ; DATE bound by calendar + (d1 (calendar-absolute-from-gregorian date)) ; DATE bound by calendar + d2 diff pos pos1 category tags + ee txt head face) + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (catch :skip + (org-agenda-skip) + (setq pos (1- (match-beginning 1)) + d2 (time-to-days + (org-time-string-to-time (match-string 1))) + diff (- d2 d1)) + ;; When to show a deadline in the calendar: + ;; If the expiration is within wdays warning time. + ;; Past-due deadlines are only shown on the current date + (if (and (< diff wdays) todayp (not (= diff 0))) + (save-excursion + (setq category (org-get-category)) + (if (re-search-backward "\\(^\\|\r\\)\\*+[ \t]*" nil t) + (progn + (goto-char (match-end 0)) + (setq pos1 (match-end 1)) + (setq tags (org-get-tags-at pos1)) + (setq head (buffer-substring-no-properties + (point) + (progn (skip-chars-forward "^\r\n") + (point)))) + (if (string-match org-looking-at-done-regexp head) + (setq txt nil) + (setq txt (org-format-agenda-item + (format "In %3d d.: " diff) head category tags)))) + (setq txt org-agenda-no-heading-message)) + (when txt + (setq face (cond ((<= diff 0) 'org-warning) + ((<= diff 5) 'org-upcoming-deadline) + (t nil))) + (org-add-props txt props + 'org-marker (org-agenda-new-marker pos) + 'org-hd-marker (org-agenda-new-marker pos1) + 'priority (+ (- 10 diff) (org-get-priority txt)) + 'category category + 'face face 'undone-face face 'done-face 'org-done) + (push txt ee)))))) + ee)) + +(defun org-agenda-get-scheduled () + "Return the scheduled information for agenda display." + (let* ((props (list 'face 'org-scheduled-previously + 'org-not-done-regexp org-not-done-regexp + 'undone-face 'org-scheduled-previously + 'done-face 'org-done + 'mouse-face 'highlight + 'keymap org-agenda-keymap + 'help-echo + (format "mouse-2 or RET jump to org file %s" + (abbreviate-file-name buffer-file-name)))) + (regexp org-scheduled-time-regexp) + (todayp (equal date (calendar-current-date))) ; DATE bound by calendar + (d1 (calendar-absolute-from-gregorian date)) ; DATE bound by calendar + d2 diff pos pos1 category tags donep + ee txt head) + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (catch :skip + (org-agenda-skip) + (setq pos (1- (match-beginning 1)) + d2 (time-to-days + (org-time-string-to-time (match-string 1))) + diff (- d2 d1)) + ;; When to show a scheduled item in the calendar: + ;; If it is on or past the date. + (if (and (< diff 0) todayp) + (save-excursion + (setq category (org-get-category)) + (if (re-search-backward "\\(^\\|\r\\)\\*+[ \t]*" nil t) + (progn + (goto-char (match-end 0)) + (setq pos1 (match-end 1)) + (setq tags (org-get-tags-at)) + (setq head (buffer-substring-no-properties + (point) + (progn (skip-chars-forward "^\r\n") (point)))) + (if (string-match org-looking-at-done-regexp head) + (setq txt nil) + (setq txt (org-format-agenda-item + (format "Sched.%2dx: " (- 1 diff)) head + category tags)))) + (setq txt org-agenda-no-heading-message)) + (when txt + (org-add-props txt props + 'org-marker (org-agenda-new-marker pos) + 'org-hd-marker (org-agenda-new-marker pos1) + 'priority (+ (- 5 diff) (org-get-priority txt)) + 'category category) + (push txt ee)))))) + ee)) + +(defun org-agenda-get-blocks () + "Return the date-range information for agenda display." + (let* ((props (list 'face nil + 'org-not-done-regexp org-not-done-regexp + 'mouse-face 'highlight + 'keymap org-agenda-keymap + 'help-echo + (format "mouse-2 or RET jump to org file %s" + (abbreviate-file-name buffer-file-name)))) + (regexp org-tr-regexp) + (d0 (calendar-absolute-from-gregorian date)) + marker hdmarker ee txt d1 d2 s1 s2 timestr category tags pos) + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (catch :skip + (org-agenda-skip) + (setq pos (point)) + (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))) + (if (and (> (- d0 d1) -1) (> (- d2 d0) -1)) + ;; Only allow days between the limits, because the normal + ;; date stamps will catch the limits. + (save-excursion + (setq marker (org-agenda-new-marker (point))) + (setq category (org-get-category)) + (if (re-search-backward "\\(^\\|\r\\)\\*+" nil t) + (progn + (setq hdmarker (org-agenda-new-marker (match-end 1))) + (goto-char (match-end 1)) + (setq tags (org-get-tags-at)) + (looking-at "\\*+[ \t]*\\([^\r\n]+\\)") + (setq txt (org-format-agenda-item + (format (if (= d1 d2) "" "(%d/%d): ") + (1+ (- d0 d1)) (1+ (- d2 d1))) + (match-string 1) category tags + (if (= d0 d1) timestr)))) + (setq txt org-agenda-no-heading-message)) + (org-add-props txt props + 'org-marker marker 'org-hd-marker hdmarker + 'priority (org-get-priority txt) 'category category) + (push txt ee))) + (goto-char pos))) + ;; Sort the entries by expiration date. + (nreverse ee))) + +;;; Agenda presentation and sorting + +;; FIXME: should I allow spaces around the dash? +(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\\} +\\sw+ +\\)" + "\\([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'.") +(defvar org-prefix-has-tag 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 tags dotime + noprefix) + "Format TXT to be inserted into the agenda buffer. +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 overrule 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'. TAGS can be the tags of the headline." + (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)) + ""))) + (tag (if tags (nth (1- (length tags)) tags) "")) + 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 t))) + (if s2 (setq s2 (org-get-time-of-day s2 'string t)))) + + (when (string-match "\\([ \t]+\\)\\(:[a-zA-Z_@0-9:]+:\\)[ \t]*$" txt) + ;; Tags are in the string + (if (or (eq org-agenda-remove-tags-when-in-prefix t) + (and org-agenda-remove-tags-when-in-prefix + org-prefix-has-tag)) + (setq txt (replace-match "" t t txt)) + (setq txt (replace-match + (concat (make-string (max (- 50 (length txt)) 1) ?\ ) + (match-string 2 txt)) + t t txt)))) + + ;; 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 + (org-add-props rtn nil + 'category (downcase category) 'tags tags + 'prefix-length (- (length rtn) (length txt)) + 'time-of-day time-of-day + 'dotime dotime)))) + +(defvar org-agenda-sorting-strategy) +(defvar org-agenda-sorting-strategy-selected nil) + +(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 "" nil + (concat (substring time 0 -2) ":" (substring time -2))) + new) + (put-text-property + 1 (length (car new)) 'face 'org-time-grid (car new)))) + (if (member 'time-up org-agenda-sorting-strategy-selected) + (append new list) + (append list new))))) + +(defun org-compile-prefix-format (key) + "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'." + (setq org-prefix-has-time nil org-prefix-has-tag nil) + (let ((s (cond + ((stringp org-agenda-prefix-format) + org-agenda-prefix-format) + ((assq key org-agenda-prefix-format) + (cdr (assq key org-agenda-prefix-format))) + (t " %-12:c%?-12t% s"))) + (start 0) + varform vars var e c f opt) + (while (string-match "%\\(\\?\\)?\\([-+]?[0-9.]*\\)\\([ .;,:!?=|/<>]?\\)\\([cts]\\)" + s start) + (setq var (cdr (assoc (match-string 4 s) + '(("c" . category) ("t" . time) ("s" . extra) + ("T" . tag)))) + c (or (match-string 3 s) "") + opt (match-beginning 1) + start (1+ (match-beginning 0))) + (if (equal var 'time) (setq org-prefix-has-time t)) + (if (equal var 'tag) (setq org-prefix-has-tag 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-set-sorting-strategy (key) + (if (symbolp (car org-agenda-sorting-strategy)) + ;; the old format + (setq org-agenda-sorting-strategy-selected org-agenda-sorting-strategy) + (setq org-agenda-sorting-strategy-selected + (or (cdr (assq key org-agenda-sorting-strategy)) + (cdr (assq 'agenda org-agenda-sorting-strategy)) + '(time-up category-keep priority-down))))) + +(defun org-get-time-of-day (s &optional string mod24) + "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. +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)) + (let* ((h (string-to-number (match-string 1 s))) + (m (if (match-end 3) (string-to-number (match-string 3 s)) 0)) + (ampm (if (match-end 4) (downcase (match-string 4 s)))) + (am-p (equal ampm "am")) + (h1 (cond ((not ampm) h) + ((= h 12) (if am-p 0 12)) + (t (+ h (if am-p 0 12))))) + (h2 (if (and string mod24 (not (and (= m 0) (= h1 24)))) + (mod h1 24) h1)) + (t0 (+ (* 100 h2) m)) + (t1 (concat (if (>= h1 24) "+" " ") + (if (< t0 100) "0" "") + (if (< t0 10) "0" "") + (int-to-string t0)))) + (if string (concat (substring t1 -4 -2) ":" (substring t1 -2)) t0))))) + +(defun org-finalize-agenda-entries (list &optional nosort) + "Sort and concatenate the agenda items." + (setq list (mapcar 'org-agenda-highlight-todo list)) + (if nosort + list + (mapconcat 'identity (sort list 'org-entries-lessp) "\n"))) + +(defun org-agenda-highlight-todo (x) + (let (re pl) + (if (eq x 'line) + (save-excursion + (beginning-of-line 1) + (setq re (get-text-property (point) 'org-not-done-regexp)) + (goto-char (+ (point) (or (get-text-property (point) 'prefix-length) 0))) + (and (looking-at (concat "[ \t]*\\.*" re)) + (add-text-properties (match-beginning 0) (match-end 0) + '(face org-todo)))) + (setq re (concat (get-text-property 0 'org-not-done-regexp x)) + pl (get-text-property 0 'prefix-length x)) + (and re (equal (string-match (concat "\\(\\.*\\)" re) x (or pl 0)) pl) + (add-text-properties (or (match-end 1) (match-end 0)) (match-end 0) + '(face org-todo) x)) + x))) + +(defsubst org-cmp-priority (a b) + "Compare the priorities of string A and B." + (let ((pa (or (get-text-property 1 'priority a) 0)) + (pb (or (get-text-property 1 'priority b) 0))) + (cond ((> pa pb) +1) + ((< pa pb) -1) + (t nil)))) + +(defsubst org-cmp-category (a b) + "Compare the string values of categories of strings A and B." + (let ((ca (or (get-text-property 1 'category a) "")) + (cb (or (get-text-property 1 'category b) ""))) + (cond ((string-lessp ca cb) -1) + ((string-lessp cb ca) +1) + (t nil)))) + +(defsubst org-cmp-tag (a b) + "Compare the string values of categories of strings A and B." + (let ((ta (car (last (get-text-property 1 'tags a)))) + (tb (car (last (get-text-property 1 'tags b))))) + (cond ((not ta) +1) + ((not tb) -1) + ((string-lessp ta tb) -1) + ((string-lessp tb ta) +1) + (t nil)))) + +(defsubst org-cmp-time (a b) + "Compare the time-of-day values of strings A and B." + (let* ((def (if org-sort-agenda-notime-is-late 9901 -1)) + (ta (or (get-text-property 1 'time-of-day a) def)) + (tb (or (get-text-property 1 'time-of-day b) def))) + (cond ((< ta tb) -1) + ((< tb ta) +1) + (t nil)))) + +(defun org-entries-lessp (a b) + "Predicate for sorting agenda entries." + ;; The following variables will be used when the form is evaluated. + (let* ((time-up (org-cmp-time a b)) + (time-down (if time-up (- time-up) nil)) + (priority-up (org-cmp-priority a b)) + (priority-down (if priority-up (- priority-up) nil)) + (category-up (org-cmp-category a b)) + (category-down (if category-up (- category-up) nil)) + (category-keep (if category-up +1 nil)) + (tag-up (org-cmp-tag a b)) + (tag-down (if tag-up (- tag-up) nil))) + (cdr (assoc + (eval (cons 'or org-agenda-sorting-strategy-selected)) + '((-1 . t) (1 . nil) (nil . nil)))))) + +;;; Agenda commands + +(defun org-agenda-check-type (error &rest types) + "Check if agenda buffer is of allowed type. +If ERROR is non-nil, throw an error, otherwise just return nil." + (if (memq org-agenda-type types) + t + (if error + (error "Not allowed in %s-type agenda buffers" org-agenda-type) + nil))) + +(defun org-agenda-quit () + "Exit agenda by removing the window or the buffer." + (interactive) + (let ((buf (current-buffer))) + (if (not (one-window-p)) (delete-window)) + (kill-buffer buf) + (org-agenda-maybe-reset-markers 'force)) + ;; Maybe restore the pre-agenda window configuration. + (and org-agenda-restore-windows-after-quit + (not (eq org-agenda-window-setup 'other-frame)) + org-pre-agenda-window-conf + (set-window-configuration org-pre-agenda-window-conf))) + +(defun org-agenda-exit () + "Exit agenda by removing the window or the buffer. +Also kill all Org-mode buffers which have been loaded by `org-agenda'. +Org-mode buffers visited directly by the user will not be touched." + (interactive) + (org-release-buffers org-agenda-new-buffers) + (setq org-agenda-new-buffers nil) + (org-agenda-quit)) + +(defun org-save-all-org-buffers () + "Save all Org-mode buffers without user confirmation." + (interactive) + (message "Saving all Org-mode buffers...") + (save-some-buffers t 'org-mode-p) + (message "Saving all Org-mode buffers... done")) + +(defun org-agenda-redo () + "Rebuild Agenda. +When this is the global TODO list, a prefix argument will be interpreted." + (interactive) + (let* ((org-agenda-keep-modes t) + (line (org-current-line)) + (window-line (- line (org-current-line (window-start))))) + (message "Rebuilding agenda buffer...") + (eval org-agenda-redo-command) + (setq org-agenda-undo-list nil + org-agenda-pending-undo-list nil) + (message "Rebuilding agenda buffer...done") + (goto-line line) + (recenter window-line))) + +(defun org-agenda-goto-today () + "Go to today." + (interactive) + (org-agenda-check-type t 'timeline 'agenda) + (let ((tdpos (text-property-any (point-min) (point-max) 'org-today t))) + (cond + (tdpos (goto-char tdpos)) + ((eq org-agenda-type 'agenda) + (let ((org-agenda-overriding-arguments org-agenda-last-arguments)) + (setf (nth 1 org-agenda-overriding-arguments) nil) + (org-agenda-redo) + (org-agenda-find-today-or-agenda))) + (t (error "Cannot find today"))))) + +(defun org-agenda-find-today-or-agenda () + (goto-char + (or (text-property-any (point-min) (point-max) 'org-today t) + (text-property-any (point-min) (point-max) 'org-agenda-type 'agenda) + (point-min)))) + +(defun org-agenda-later (arg) + "Go forward in time by `org-agenda-ndays' days. +With prefix ARG, go forward that many times `org-agenda-ndays'." + (interactive "p") + (org-agenda-check-type t 'agenda) + (let ((org-agenda-overriding-arguments + (list (car org-agenda-last-arguments) + (+ org-starting-day (* arg org-agenda-ndays)) + nil t))) + (org-agenda-redo) + (org-agenda-find-today-or-agenda))) + +(defun org-agenda-earlier (arg) + "Go back in time by `org-agenda-ndays' days. +With prefix ARG, go back that many times `org-agenda-ndays'." + (interactive "p") + (org-agenda-check-type t 'agenda) + (let ((org-agenda-overriding-arguments + (list (car org-agenda-last-arguments) + (- org-starting-day (* arg org-agenda-ndays)) + nil t))) + (org-agenda-redo) + (org-agenda-find-today-or-agenda))) + +(defun org-agenda-week-view () + "Switch to weekly view for agenda." + (interactive) + (org-agenda-check-type t 'agenda) + (if (= org-agenda-ndays 7) + (error "This is already the week view")) + (setq org-agenda-ndays 7) + (let ((org-agenda-overriding-arguments + (list (car org-agenda-last-arguments) + (or (get-text-property (point) 'day) + org-starting-day) + nil t))) + (org-agenda-redo) + (org-agenda-find-today-or-agenda)) + (org-agenda-set-mode-name) + (message "Switched to week view")) + +(defun org-agenda-day-view () + "Switch to daily view for agenda." + (interactive) + (org-agenda-check-type t 'agenda) + (if (= org-agenda-ndays 1) + (error "This is already the day view")) + (setq org-agenda-ndays 1) + (let ((org-agenda-overriding-arguments + (list (car org-agenda-last-arguments) + (or (get-text-property (point) 'day) + org-starting-day) + nil t))) + (org-agenda-redo) + (org-agenda-find-today-or-agenda)) + (org-agenda-set-mode-name) + (message "Switched to day view")) + +(defun org-agenda-next-date-line (&optional arg) + "Jump to the next line indicating a date in agenda buffer." + (interactive "p") + (org-agenda-check-type t 'agenda 'timeline) + (beginning-of-line 1) + (if (looking-at "^\\S-") (forward-char 1)) + (if (not (re-search-forward "^\\S-" nil t arg)) + (progn + (backward-char 1) + (error "No next date after this line in this buffer"))) + (goto-char (match-beginning 0))) + +(defun org-agenda-previous-date-line (&optional arg) + "Jump to the previous line indicating a date in agenda buffer." + (interactive "p") + (org-agenda-check-type t 'agenda 'timeline) + (beginning-of-line 1) + (if (not (re-search-backward "^\\S-" nil t arg)) + (error "No previous date before this line in this buffer"))) + +;; Initialize the highlight +(defvar org-hl (org-make-overlay 1 1)) +(org-overlay-put org-hl 'face 'highlight) + +(defun org-highlight (begin end &optional buffer) + "Highlight a region with overlay." + (funcall (if (featurep 'xemacs) 'set-extent-endpoints 'move-overlay) + org-hl begin end (or buffer (current-buffer)))) + +(defun org-unhighlight () + "Detach overlay INDEX." + (funcall (if (featurep 'xemacs) 'detach-extent 'delete-overlay) org-hl)) + + +(defun org-agenda-follow-mode () + "Toggle follow mode in an agenda buffer." + (interactive) + (setq org-agenda-follow-mode (not org-agenda-follow-mode)) + (org-agenda-set-mode-name) + (message "Follow mode is %s" + (if org-agenda-follow-mode "on" "off"))) + +(defun org-agenda-log-mode () + "Toggle log mode in an agenda buffer." + (interactive) + (org-agenda-check-type t 'agenda 'timeline) + (setq org-agenda-show-log (not org-agenda-show-log)) + (org-agenda-set-mode-name) + (org-agenda-redo) + (message "Log mode is %s" + (if org-agenda-show-log "on" "off"))) + +(defun org-agenda-toggle-diary () + "Toggle diary inclusion in an agenda buffer." + (interactive) + (org-agenda-check-type t 'agenda) + (setq org-agenda-include-diary (not org-agenda-include-diary)) + (org-agenda-redo) + (org-agenda-set-mode-name) + (message "Diary inclusion turned %s" + (if org-agenda-include-diary "on" "off"))) + +(defun org-agenda-toggle-time-grid () + "Toggle time grid in an agenda buffer." + (interactive) + (org-agenda-check-type t 'agenda) + (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 + (concat "Org-Agenda" + (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-use-time-grid " Grid" "") + (if org-agenda-show-log " Log" ""))) + (force-mode-line-update)) + +(defun org-agenda-post-command-hook () + (and (eolp) (not (bolp)) (backward-char 1)) + (setq org-agenda-type (get-text-property (point) 'org-agenda-type)) + (if (and org-agenda-follow-mode + (get-text-property (point) 'org-marker)) + (org-agenda-show))) + +(defun org-agenda-show-priority () + "Show the priority of the current item. +This priority is composed of the main priority given with the [#A] cookies, +and by additional input from the age of a schedules or deadline entry." + (interactive) + (let* ((pri (get-text-property (point-at-bol) 'priority))) + (message "Priority is %d" (if pri pri -1000)))) + +(defun org-agenda-show-tags () + "Show the tags applicable to the current item." + (interactive) + (let* ((tags (get-text-property (point-at-bol) 'tags))) + (if tags + (message "Tags are :%s:" + (org-no-properties (mapconcat 'identity tags ":"))) + (message "No tags associated with this line")))) + +(defun org-agenda-goto (&optional highlight) + "Go to the Org-mode file which contains the item at point." + (interactive) + (let* ((marker (or (get-text-property (point) 'org-marker) + (org-agenda-error))) + (buffer (marker-buffer marker)) + (pos (marker-position marker))) + (switch-to-buffer-other-window buffer) + (widen) + (goto-char pos) + (when (org-mode-p) + (org-show-context 'agenda) + (save-excursion + (and (outline-next-heading) + (org-flag-heading nil)))) ; show the next heading + (and highlight (org-highlight (point-at-bol) (point-at-eol))))) + +(defun org-agenda-kill () + "Kill the entry or subtree belonging to the current agenda entry." + (interactive) + (let* ((marker (or (get-text-property (point) 'org-marker) + (org-agenda-error))) + (hdmarker (get-text-property (point) 'org-hd-marker)) + (buffer (marker-buffer marker)) + (pos (marker-position marker)) + dbeg dend (n 0) conf) + (org-with-remote-undo buffer + (with-current-buffer buffer + (save-excursion + (goto-char pos) + (if (org-mode-p) + (setq dbeg (progn (org-back-to-heading t) (point)) + dend (org-end-of-subtree t)) + (setq dbeg (point-at-bol) + dend (min (point-max) (1+ (point-at-eol))))) + (goto-char dbeg) + (while (re-search-forward "^[ \t]*\\S-" dend t) (setq n (1+ n))))) + (setq conf (or (eq t org-agenda-confirm-kill) + (and (numberp org-agenda-confirm-kill) + (> n org-agenda-confirm-kill)))) + (and conf + (not (y-or-n-p + (format "Delete entry with %d lines in buffer \"%s\"? " + n (buffer-name buffer)))) + (error "Abort")) + (org-remove-subtree-entries-from-agenda buffer dbeg dend) + (with-current-buffer buffer (delete-region dbeg dend)) + (message "Agenda item and source killed")))) + +(defun org-agenda-archive () + "Kill the entry or subtree belonging to the current agenda entry." + (interactive) + (let* ((marker (or (get-text-property (point) 'org-marker) + (org-agenda-error))) + (hdmarker (get-text-property (point) 'org-hd-marker)) + (buffer (marker-buffer marker)) + (pos (marker-position marker)) + dbeg dend txt n conf) + (org-with-remote-undo buffer + (with-current-buffer buffer + (if (org-mode-p) + (save-excursion + (goto-char pos) + (org-remove-subtree-entries-from-agenda) + (org-back-to-heading t) + (org-archive-subtree)) + (error "Archiving works only in Org-mode files")))))) + +(defun org-remove-subtree-entries-from-agenda (&optional buf beg end) + "Remove all lines in the agenda that correspond to a given subtree. +The subtree is the one in buffer BUF, starting at BEG and ending at END. +If this information is not given, the function uses the tree at point." + (let ((buf (or buf (current-buffer))) m p) + (save-excursion + (unless (and beg end) + (org-back-to-heading t) + (setq beg (point)) + (org-end-of-subtree t) + (setq end (point))) + (set-buffer (get-buffer org-agenda-buffer-name)) + (save-excursion + (goto-char (point-max)) + (beginning-of-line 1) + (while (not (bobp)) + (when (and (setq m (get-text-property (point) 'org-marker)) + (equal buf (marker-buffer m)) + (setq p (marker-position m)) + (>= p beg) + (<= p end)) + (let (buffer-read-only) + (delete-region (point-at-bol) (1+ (point-at-eol))))) + (beginning-of-line 0)))))) + +(defun org-agenda-open-link () + "Follow the link in the current line, if any." + (interactive) + (let ((eol (point-at-eol))) + (save-excursion + (if (or (re-search-forward org-bracket-link-regexp eol t) + (re-search-forward org-angle-link-re eol t) + (re-search-forward org-plain-link-re eol t)) + (call-interactively 'org-open-at-point) + (error "No link in current line"))))) + +(defun org-agenda-switch-to (&optional delete-other-windows) + "Go to the Org-mode file which contains the item at point." + (interactive) + (let* ((marker (or (get-text-property (point) 'org-marker) + (org-agenda-error))) + (buffer (marker-buffer marker)) + (pos (marker-position marker))) + (switch-to-buffer buffer) + (and delete-other-windows (delete-other-windows)) + (widen) + (goto-char pos) + (when (org-mode-p) + (org-show-context 'agenda) + (save-excursion + (and (outline-next-heading) + (org-flag-heading nil)))))) ; show the next heading + +(defun org-agenda-goto-mouse (ev) + "Go to the Org-mode file which contains the item at the mouse click." + (interactive "e") + (mouse-set-point ev) + (org-agenda-goto)) + +(defun org-agenda-show () + "Display the Org-mode file which contains the item at point." + (interactive) + (let ((win (selected-window))) + (org-agenda-goto t) + (select-window win))) + +(defun org-agenda-recenter (arg) + "Display the Org-mode file which contains the item at point and recenter." + (interactive "P") + (let ((win (selected-window))) + (org-agenda-goto t) + (recenter arg) + (select-window win))) + +(defun org-agenda-show-mouse (ev) + "Display the Org-mode file which contains the item at the mouse click." + (interactive "e") + (mouse-set-point ev) + (org-agenda-show)) + +(defun org-agenda-check-no-diary () + "Check if the entry is a diary link and abort if yes." + (if (get-text-property (point) 'org-agenda-diary-link) + (org-agenda-error))) + +(defun org-agenda-error () + (error "Command not allowed in this line")) + +(defun org-agenda-tree-to-indirect-buffer () + "Show the subtree corresponding to the current entry in an indirect buffer. +This calls the command `org-tree-to-indirect-buffer' from the original +Org-mode buffer. +With numerical prefix arg ARG, go up to this level and then take that tree. +With a C-u prefix, make a separate frame for this tree (i.e. don't use the +dedicated frame)." + (interactive) + (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))) + (with-current-buffer buffer + (save-excursion + (goto-char pos) + (call-interactively 'org-tree-to-indirect-buffer))))) + +(defvar org-last-heading-marker (make-marker) + "Marker pointing to the headline that last changed its TODO state +by a remote command from the agenda.") + +(defun org-agenda-todo (&optional arg) + "Cycle TODO state of line at point, also in Org-mode file. +This changes the line at point, all other lines in the agenda referring to +the same tree node, and the headline of the tree node in the Org-mode file." + (interactive "P") + (org-agenda-check-no-diary) + (let* ((col (current-column)) + (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)) + (buffer-read-only nil) + newhead) + (org-with-remote-undo buffer + (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-todo arg) + (and (bolp) (forward-char 1)) + (setq newhead (org-get-heading)) + (save-excursion + (org-back-to-heading) + (move-marker org-last-heading-marker (point)))) + (beginning-of-line 1) + (save-excursion + (org-agenda-change-all-lines newhead hdmarker 'fixface)) + (move-to-column col)))) + +(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 +`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* ((buffer-read-only nil) + props m pl undone-face done-face finish new dotime cat tags) + (save-excursion + (goto-char (point-max)) + (beginning-of-line 1) + (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) + cat (get-text-property (point) 'category) + tags (get-text-property (point) 'tags) + new (org-format-agenda-item "x" newhead cat tags 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) + (cond + ((equal new "") + (beginning-of-line 1) + (and (looking-at ".*\n?") (replace-match ""))) + ((looking-at ".*") + (replace-match new t t) + (beginning-of-line 1) + (add-text-properties (point-at-bol) (point-at-eol) props) + (when fixface + (add-text-properties + (point-at-bol) (point-at-eol) + (list 'face + (if org-last-todo-state-is-todo + undone-face done-face)))) + (org-agenda-highlight-todo 'line) + (beginning-of-line 1)) + (t (error "Line update did not work")))) + (beginning-of-line 0))) + (org-finalize-agenda))) + +(defun org-agenda-align-tags (&optional line) + "Align all tags in agenda items to `org-agenda-align-tags-to-column'." + (let ((buffer-read-only)) + (save-excursion + (goto-char (if line (point-at-bol) (point-min))) + (while (re-search-forward "\\([ \t]+\\):[a-zA-Z0-9_@:]+:[ \t]*$" + (if line (point-at-eol) nil) t) + (delete-region (match-beginning 1) (match-end 1)) + (goto-char (match-beginning 1)) + (insert (org-add-props + (make-string (max 1 (- org-agenda-align-tags-to-column + (current-column))) ?\ ) + (text-properties-at (point)))))))) + +(defun org-agenda-priority-up () + "Increase the priority of line at point, also in Org-mode file." + (interactive) + (org-agenda-priority 'up)) + +(defun org-agenda-priority-down () + "Decrease the priority of line at point, also in Org-mode file." + (interactive) + (org-agenda-priority 'down)) + +(defun org-agenda-priority (&optional force-direction) + "Set the priority of line at point, also in Org-mode file. +This changes the line at point, all other lines in the agenda referring to +the same tree node, and the headline of the tree node in the Org-mode file." + (interactive) + (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)) + (buffer-read-only nil) + newhead) + (org-with-remote-undo buffer + (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 + (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)))) + +(defun org-get-tags-at (&optional pos) + "Get a list of all headline tags applicable at POS. +POS defaults to point. If tags are inherited, the list contains +the targets in the same sequence as the headlines appear, i.e. +the tags of the current headline come last." + (interactive) + (let (tags) + (save-excursion + (goto-char (or pos (point))) + (save-match-data + (org-back-to-heading t) + (condition-case nil + (while t + (if (looking-at "[^\r\n]+?:\\([a-zA-Z_@0-9:]+\\):[ \t]*\\([\n\r]\\|\\'\\)") + (setq tags (append (org-split-string + (org-match-string-no-properties 1) ":") + tags))) + (or org-use-tag-inheritance (error "")) + (org-up-heading-all 1)) + (error nil)))) + tags)) + +;; FIXME: should fix the tags property of the agenda line. +(defun org-agenda-set-tags () + "Set tags for the current headline." + (interactive) + (org-agenda-check-no-diary) + (org-agenda-show) ;;; FIXME This is a stupid hack and should not be needed + (let* ((hdmarker (or (get-text-property (point) 'org-hd-marker) + (org-agenda-error))) + (buffer (marker-buffer hdmarker)) + (pos (marker-position hdmarker)) + (buffer-read-only nil) + newhead) + (org-with-remote-undo buffer + (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 + (call-interactively 'org-set-tags) + (end-of-line 1) + (setq newhead (org-get-heading))) + (org-agenda-change-all-lines newhead hdmarker) + (beginning-of-line 1)))) + +(defun org-agenda-toggle-archive-tag () + "Toggle the archive tag for the current entry." + (interactive) + (org-agenda-check-no-diary) + (org-agenda-show) ;;; FIXME This is a stupid hack and should not be needed + (let* ((hdmarker (or (get-text-property (point) 'org-hd-marker) + (org-agenda-error))) + (buffer (marker-buffer hdmarker)) + (pos (marker-position hdmarker)) + (buffer-read-only nil) + newhead) + (org-with-remote-undo buffer + (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 + (call-interactively 'org-toggle-archive-tag) + (end-of-line 1) + (setq newhead (org-get-heading))) + (org-agenda-change-all-lines newhead hdmarker) + (beginning-of-line 1)))) + +(defun org-agenda-date-later (arg &optional what) + "Change the date of this item to one day later." + (interactive "p") + (org-agenda-check-type t 'agenda 'timeline) + (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))) + (org-with-remote-undo buffer + (with-current-buffer buffer + (widen) + (goto-char pos) + (if (not (org-at-timestamp-p)) + (error "Cannot find time stamp")) + (org-timestamp-change arg (or what 'day))) + (org-agenda-show-new-time marker org-last-changed-timestamp)) + (message "Time stamp changed to %s" org-last-changed-timestamp))) + +(defun org-agenda-date-earlier (arg &optional what) + "Change the date of this item to one day earlier." + (interactive "p") + (org-agenda-date-later (- arg) what)) + +(defun org-agenda-show-new-time (marker stamp) + "Show new date stamp via text properties." + ;; We use text properties to make this undoable + (let ((buffer-read-only nil) + ovs ov) + (setq stamp (concat " => " stamp)) + (save-excursion + (goto-char (point-max)) + (while (not (bobp)) + (when (equal marker (get-text-property (point) 'org-marker)) + (move-to-column (- (window-width) (length stamp)) t) + (if (featurep 'xemacs) + ;; Use `duplicable' property to trigger undo recording + (let ((ex (make-extent nil nil)) + (gl (make-glyph stamp))) + (set-glyph-face gl 'secondary-selection) + (set-extent-properties + ex (list 'invisible t 'end-glyph gl 'duplicable t)) + (insert-extent ex (1- (point)) (point-at-eol))) + (add-text-properties + (1- (point)) (point-at-eol) + (list 'display (org-add-props stamp nil + 'face 'secondary-selection)))) + (beginning-of-line 1)) + (beginning-of-line 0))))) + +(defun org-agenda-date-prompt (arg) + "Change the date of this item. Date is prompted for, with default today. +The prefix ARG is passed to the `org-time-stamp' command and can therefore +be used to request time specification in the time stamp." + (interactive "P") + (org-agenda-check-type t 'agenda 'timeline) + (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))) + (org-with-remote-undo buffer + (with-current-buffer buffer + (widen) + (goto-char pos) + (if (not (org-at-timestamp-p)) + (error "Cannot find time stamp")) + (org-time-stamp arg) + (message "Time stamp changed to %s" org-last-changed-timestamp))))) + +(defun org-agenda-schedule (arg) + "Schedule the item at point." + (interactive "P") + (org-agenda-check-type t 'agenda 'timeline 'todo 'tags) + (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)) + (org-insert-labeled-timestamps-at-point nil) + ts) + (org-with-remote-undo buffer + (with-current-buffer buffer + (widen) + (goto-char pos) + (setq ts (org-schedule)) + (message "Item scheduled for %s" ts))))) + +(defun org-agenda-deadline (arg) + "Schedule the item at point." + (interactive "P") + (org-agenda-check-type t 'agenda 'timeline 'todo 'tags) + (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)) + (org-insert-labeled-timestamps-at-point nil) + ts) + (org-with-remote-undo buffer + (with-current-buffer buffer + (widen) + (goto-char pos) + (setq ts (org-deadline)) + (message "Deadline for this item set to %s" ts))))) + +(defun org-get-heading () + "Return the heading of the current entry, without the stars." + (save-excursion + (and (memq (char-before) '(?\n ?\r)) (skip-chars-forward "^\n\r")) + (if (and (re-search-backward "[\r\n]\\*" nil t) + (looking-at "[\r\n]\\*+[ \t]+\\([^\r\n]*\\)")) + (match-string 1) + ""))) + +(defun org-agenda-clock-in (&optional arg) + "Start the clock on the currently selected item." + (interactive "P") + (org-agenda-check-no-diary) + (let* ((marker (or (get-text-property (point) 'org-marker) + (org-agenda-error))) + (pos (marker-position marker))) + (org-with-remote-undo (marker-buffer marker) + (with-current-buffer (marker-buffer marker) + (widen) + (goto-char pos) + (org-clock-in))))) + +(defun org-agenda-clock-out (&optional arg) + "Stop the currently running clock." + (interactive "P") + (unless (marker-buffer org-clock-marker) + (error "No running clock")) + (org-with-remote-undo (marker-buffer org-clock-marker) + (org-clock-out))) + +(defun org-agenda-clock-cancel (&optional arg) + "Cancel the currently running clock." + (interactive "P") + (unless (marker-buffer org-clock-marker) + (error "No running clock")) + (org-with-remote-undo (marker-buffer org-clock-marker) + (org-clock-cancel))) + +(defun org-agenda-diary-entry () + "Make a diary entry, like the `i' command from the calendar. +All the standard commands work: block, weekly etc." + (interactive) + (org-agenda-check-type t 'agenda 'timeline) + (require 'diary-lib) + (let* ((char (progn + (message "Diary entry: [d]ay [w]eekly [m]onthly [y]early [a]nniversary [b]lock [c]yclic") + (read-char-exclusive))) + (cmd (cdr (assoc char + '((?d . insert-diary-entry) + (?w . insert-weekly-diary-entry) + (?m . insert-monthly-diary-entry) + (?y . insert-yearly-diary-entry) + (?a . insert-anniversary-diary-entry) + (?b . insert-block-diary-entry) + (?c . insert-cyclic-diary-entry))))) + (oldf (symbol-function 'calendar-cursor-to-date)) +; (buf (get-file-buffer (substitute-in-file-name diary-file))) + (point (point)) + (mark (or (mark t) (point)))) + (unless cmd + (error "No command associated with <%c>" char)) + (unless (and (get-text-property point 'day) + (or (not (equal ?b char)) + (get-text-property mark 'day))) + (error "Don't know which date to use for diary entry")) + ;; We implement this by hacking the `calendar-cursor-to-date' function + ;; and the `calendar-mark-ring' variable. Saves a lot of code. + (let ((calendar-mark-ring + (list (calendar-gregorian-from-absolute + (or (get-text-property mark 'day) + (get-text-property point 'day)))))) + (unwind-protect + (progn + (fset 'calendar-cursor-to-date + (lambda (&optional error) + (calendar-gregorian-from-absolute + (get-text-property point 'day)))) + (call-interactively cmd)) + (fset 'calendar-cursor-to-date oldf))))) + + +(defun org-agenda-execute-calendar-command (cmd) + "Execute a calendar command from the agenda, with the date associated to +the cursor position." + (org-agenda-check-type t 'agenda 'timeline) + (require 'diary-lib) + (unless (get-text-property (point) 'day) + (error "Don't know which date to use for calendar command")) + (let* ((oldf (symbol-function 'calendar-cursor-to-date)) + (point (point)) + (date (calendar-gregorian-from-absolute + (get-text-property point 'day))) + (displayed-day (extract-calendar-day date)) + (displayed-month (extract-calendar-month date)) + (displayed-year (extract-calendar-year date))) + (unwind-protect + (progn + (fset 'calendar-cursor-to-date + (lambda (&optional error) + (calendar-gregorian-from-absolute + (get-text-property point 'day)))) + (call-interactively cmd)) + (fset 'calendar-cursor-to-date oldf)))) + +(defun org-agenda-phases-of-moon () + "Display the phases of the moon for the 3 months around the cursor date." + (interactive) + (org-agenda-execute-calendar-command 'calendar-phases-of-moon)) + +(defun org-agenda-holidays () + "Display the holidays for the 3 months around the cursor date." + (interactive) + (org-agenda-execute-calendar-command 'list-calendar-holidays)) + +(defun org-agenda-sunrise-sunset (arg) + "Display sunrise and sunset for the cursor date. +Latitude and longitude can be specified with the variables +`calendar-latitude' and `calendar-longitude'. When called with prefix +argument, latitude and longitude will be prompted for." + (interactive "P") + (let ((calendar-longitude (if arg nil calendar-longitude)) + (calendar-latitude (if arg nil calendar-latitude)) + (calendar-location-name + (if arg "the given coordinates" calendar-location-name))) + (org-agenda-execute-calendar-command 'calendar-sunrise-sunset))) + +(defun org-agenda-goto-calendar () + "Open the Emacs calendar with the date at the cursor." + (interactive) + (org-agenda-check-type t 'agenda 'timeline) + (let* ((day (or (get-text-property (point) 'day) + (error "Don't know which date to open in calendar"))) + (date (calendar-gregorian-from-absolute day)) + (calendar-move-hook nil) + (view-calendar-holidays-initially nil) + (view-diary-entries-initially nil)) + (calendar) + (calendar-goto-date date))) + +(defun org-calendar-goto-agenda () + "Compute the Org-mode agenda for the calendar date displayed at the cursor. +This is a command that has to be installed in `calendar-mode-map'." + (interactive) + (org-agenda-list nil (calendar-absolute-from-gregorian + (calendar-cursor-to-date)) + nil)) + +(defun org-agenda-convert-date () + (interactive) + (org-agenda-check-type t 'agenda 'timeline) + (let ((day (get-text-property (point) 'day)) + date s) + (unless day + (error "Don't know which date to convert")) + (setq date (calendar-gregorian-from-absolute day)) + (setq s (concat + "Gregorian: " (calendar-date-string date) "\n" + "ISO: " (calendar-iso-date-string date) "\n" + "Day of Yr: " (calendar-day-of-year-string date) "\n" + "Julian: " (calendar-julian-date-string date) "\n" + "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" + "French: " (calendar-french-date-string date) "\n" + "Mayan: " (calendar-mayan-date-string date) "\n" + "Coptic: " (calendar-coptic-date-string date) "\n" + "Ethiopic: " (calendar-ethiopic-date-string date) "\n" + "Persian: " (calendar-persian-date-string date) "\n" + "Chinese: " (calendar-chinese-date-string date) "\n")) + (with-output-to-temp-buffer "*Dates*" + (princ s)) + (if (fboundp 'fit-window-to-buffer) + (fit-window-to-buffer (get-buffer-window "*Dates*"))))) + + +;;;; Embedded LaTeX + +(defvar org-cdlatex-mode-map (make-sparse-keymap) + "Keymap for the minor `org-cdlatex-mode'.") + +(define-key org-cdlatex-mode-map "_" 'org-cdlatex-underscore-caret) +(define-key org-cdlatex-mode-map "^" 'org-cdlatex-underscore-caret) +(define-key org-cdlatex-mode-map "`" 'cdlatex-math-symbol) +(define-key org-cdlatex-mode-map "'" 'org-cdlatex-math-modify) +(define-key org-cdlatex-mode-map "\C-c{" 'cdlatex-environment) + +(defvar org-cdlatex-texmathp-advice-is-done nil + "Flag remembering if we have applied the advice to texmathp already.") + +(define-minor-mode org-cdlatex-mode + "Toggle the minor `org-cdlatex-mode'. +This mode supports entering LaTeX environment and math in LaTeX fragments +in Org-mode. +\\{org-cdlatex-mode-map}" + nil " OCDL" nil + (when org-cdlatex-mode (require 'cdlatex)) + (unless org-cdlatex-texmathp-advice-is-done + (setq org-cdlatex-texmathp-advice-is-done t) + (defadvice texmathp (around org-math-always-on activate) + "Always return t in org-mode buffers. +This is because we want to insert math symbols without dollars even outside +the LaTeX math segments. If Orgmode thinks that point is actually inside +en embedded LaTeX fragement, let texmathp do its job. +\\[org-cdlatex-mode-map]" + (interactive) + (let (p) + (cond + ((not (org-mode-p)) ad-do-it) + ((eq this-command 'cdlatex-math-symbol) + (setq ad-return-value t + texmathp-why '("cdlatex-math-symbol in org-mode" . 0))) + (t + (let ((p (org-inside-LaTeX-fragment-p))) + (if (and p (member (car p) (plist-get org-format-latex-options :matchers))) + (setq ad-return-value t + texmathp-why '("Org-mode embedded math" . 0)) + (if p ad-do-it))))))))) + +(defun turn-on-org-cdlatex () + "Unconditionally turn on `org-cdlatex-mode'." + (org-cdlatex-mode 1)) + +(defun org-inside-LaTeX-fragment-p () + "Test if point is inside a LaTeX fragment. +I.e. after a \\begin, \\(, \\[, $, or $$, without the corresponding closing +sequence appearing also before point. +Even though the matchers for math are configurable, this function assumes +that \\begin, \\(, \\[, and $$ are always used. Only the single dollar +delimiters are skipped when they have been removed by customization. +The return value is nil, or a cons cell with the delimiter and +and the position of this delimiter. + +This function does a reasonably good job, but can locally be fooled by +for example currency specifications. For example it will assume being in +inline math after \"$22.34\". The LaTeX fragment formatter will only format +fragments that are properly closed, but during editing, we have to live +with the uncertainty caused by missing closing delimiters. This function +looks only before point, not after." + (catch 'exit + (let ((pos (point)) + (dodollar (member "$" (plist-get org-format-latex-options :matchers))) + (lim (progn + (re-search-backward (concat "^\\(" paragraph-start "\\)") nil t) + (point))) + dd-on str (start 0) m re) + (goto-char pos) + (when dodollar + (setq str (concat (buffer-substring lim (point)) "\000 X$.") + re (nth 1 (assoc "$" org-latex-regexps))) + (while (string-match re str start) + (cond + ((= (match-end 0) (length str)) + (throw 'exit (cons "$" (+ lim (match-beginning 0))))) + ((= (match-end 0) (- (length str) 5)) + (throw 'exit nil)) + (t (setq start (match-end 0)))))) + (when (setq m (re-search-backward "\\(\\\\begin{[^}]*}\\|\\\\(\\|\\\\\\[\\)\\|\\(\\\\end{[^}]*}\\|\\\\)\\|\\\\\\]\\)\\|\\(\\$\\$\\)" lim t)) + (goto-char pos) + (and (match-beginning 1) (throw 'exit (cons (match-string 1) m))) + (and (match-beginning 2) (throw 'exit nil)) + ;; count $$ + (while (re-search-backward "\\$\\$" lim t) + (setq dd-on (not dd-on))) + (goto-char pos) + (if dd-on (cons "$$" m)))))) + + +(defun org-try-cdlatex-tab () + "Check if it makes sense to execute `cdlatex-tab', and do it if yes. +It makes sense to do so if `org-cdlatex-mode' is active and if the cursor is + - inside a LaTeX fragment, or + - after the first word in a line, where an abbreviation expansion could + insert a LaTeX environment." + (when org-cdlatex-mode + (cond + ((save-excursion + (skip-chars-backward "a-zA-Z0-9*") + (skip-chars-backward " \t") + (bolp)) + (cdlatex-tab) t) + ((org-inside-LaTeX-fragment-p) + (cdlatex-tab) t) + (t nil)))) + +(defun org-cdlatex-underscore-caret (&optional arg) + "Execute `cdlatex-sub-superscript' in LaTeX fragments. +Revert to the normal definition outside of these fragments." + (interactive "P") + (if (org-inside-LaTeX-fragment-p) + (call-interactively 'cdlatex-sub-superscript) + (let (org-cdlatex-mode) + (call-interactively (key-binding (vector last-input-event)))))) + +(defun org-cdlatex-math-modify (&optional arg) + "Execute `cdlatex-math-modify' in LaTeX fragments. +Revert to the normal definition outside of these fragments." + (interactive "P") + (if (org-inside-LaTeX-fragment-p) + (call-interactively 'cdlatex-math-modify) + (let (org-cdlatex-mode) + (call-interactively (key-binding (vector last-input-event)))))) + +(defvar org-latex-fragment-image-overlays nil + "List of overlays carrying the images of latex fragments.") +(make-variable-buffer-local 'org-latex-fragment-image-overlays) + +(defun org-remove-latex-fragment-image-overlays () + "Remove all overlays with LaTeX fragment images in current buffer." + (mapc 'org-delete-overlay org-latex-fragment-image-overlays) + (setq org-latex-fragment-image-overlays nil)) + +(defun org-preview-latex-fragment (&optional subtree) + "Preview the LaTeX fragment at point, or all locally or globally. +If the cursor is in a LaTeX fragment, create the image and overlay +it over the source code. If there is no fragment at point, display +all fragments in the current text, from one headline to the next. With +prefix SUBTREE, display all fragments in the current subtree. With a +double prefix `C-u C-u', or when the cursor is before the first headline, +display all fragments in the buffer. +The images can be removed again with \\[org-ctrl-c-ctrl-c]." + (interactive "P") + (org-remove-latex-fragment-image-overlays) + (save-excursion + (save-restriction + (let (beg end at msg) + (cond + ((or (equal subtree '(16)) + (not (save-excursion + (re-search-backward (concat "^" outline-regexp) nil t)))) + (setq beg (point-min) end (point-max) + msg "Creating images for buffer...%s")) + ((equal subtree '(4)) + (org-back-to-heading) + (setq beg (point) end (org-end-of-subtree t) + msg "Creating images for subtree...%s")) + (t + (if (setq at (org-inside-LaTeX-fragment-p)) + (goto-char (max (point-min) (- (cdr at) 2))) + (org-back-to-heading)) + (setq beg (point) end (progn (outline-next-heading) (point)) + msg (if at "Creating image...%s" + "Creating images for entry...%s")))) + (message msg "") + (narrow-to-region beg end) + (org-format-latex + (concat "ltxpng/" (file-name-sans-extension + (file-name-nondirectory + buffer-file-name))) + default-directory 'overlays msg at) + (message msg "done. Use `C-c C-c' to remove images."))))) + +(defvar org-latex-regexps + '(("begin" "^[ \t]*\\(\\\\begin{\\([a-zA-Z0-9\\*]+\\)[^\000]+?\\\\end{\\2}\\)" 1 t) + ;; ("$" "\\([ (]\\|^\\)\\(\\(\\([$]\\)\\([^ \r\n,.$].*?\\(\n.*?\\)\\{0,5\\}[^ \r\n,.$]\\)\\4\\)\\)\\([ .,?;:'\")]\\|$\\)" 2 nil) + ;; \000 in the following regex is needed for org-inside-LaTeX-fragment-p + ("$" "\\([^$]\\)\\(\\(\\$\\([^ \r\n,;.$][^$\n\r]*?\\(\n[^$\n\r]*?\\)\\{0,2\\}[^ \r\n,.$]\\)\\$\\)\\)\\([ .,?;:'\")\000]\\|$\\)" 2 nil) + ("\\(" "\\\\([^\000]*?\\\\)" 0 nil) + ("\\[" "\\\\\\[[^\000]*?\\\\\\]" 0 t) + ("$$" "\\$\\$[^\000]*?\\$\\$" 0 t)) + "Regular expressions for matching embedded LaTeX.") + +(defun org-format-latex (prefix &optional dir overlays msg at) + "Replace LaTeX fragments with links to an image, and produce images." + (if (and overlays (fboundp 'clear-image-cache)) (clear-image-cache)) + (let* ((prefixnodir (file-name-nondirectory prefix)) + (absprefix (expand-file-name prefix dir)) + (todir (file-name-directory absprefix)) + (opt org-format-latex-options) + (matchers (plist-get opt :matchers)) + (re-list org-latex-regexps) + (cnt 0) txt link beg end re e oldfiles + m n block linkfile movefile ov) + ;; Make sure the directory exists + (or (file-directory-p todir) (make-directory todir)) + ;; Check if there are old images files with this prefix, and remove them + (setq oldfiles (directory-files + todir 'full + (concat (regexp-quote prefixnodir) "_[0-9]+\\.png$"))) + (while oldfiles (delete-file (pop oldfiles))) + ;; Check the different regular expressions + (while (setq e (pop re-list)) + (setq m (car e) re (nth 1 e) n (nth 2 e) + block (if (nth 3 e) "\n\n" "")) + (when (member m matchers) + (goto-char (point-min)) + (while (re-search-forward re nil t) + (when (or (not at) (equal (cdr at) (match-beginning n))) + (setq txt (match-string n) + beg (match-beginning n) end (match-end n) + cnt (1+ cnt) + linkfile (format "%s_%04d.png" prefix cnt) + movefile (format "%s_%04d.png" absprefix cnt) + link (concat block "[[file:" linkfile "]]" block)) + (if msg (message msg cnt)) + (goto-char beg) + (org-create-formula-image + txt movefile opt) + (if overlays + (progn + (setq ov (org-make-overlay beg end)) + (if (featurep 'xemacs) + (progn + (org-overlay-put ov 'invisible t) + (org-overlay-put + ov 'end-glyph + (make-glyph (vector 'png :file movefile)))) + (org-overlay-put + ov 'display + (list 'image :type 'png :file movefile :ascent 'center))) + (push ov org-latex-fragment-image-overlays) + (goto-char end)) + (delete-region beg end) + (insert link)))))))) + +;; This function borrows from Ganesh Swami's latex2png.el +(defun org-create-formula-image (string tofile options) + (let* ((tmpdir (if (featurep 'xemacs) + (temp-directory) + temporary-file-directory)) + (texfilebase (make-temp-name + (expand-file-name "orgtex" tmpdir))) + +;(texfilebase (make-temp-file "orgtex")) +; (dummy (delete-file texfilebase)) + (texfile (concat texfilebase ".tex")) + (dvifile (concat texfilebase ".dvi")) + (pngfile (concat texfilebase ".png")) + (scale (number-to-string (* 1000 (or (plist-get options :scale) 1.0)))) + (fg (or (plist-get options :foreground) "Black")) + (bg (or (plist-get options :background) "Transparent"))) + (with-temp-file texfile + (insert "\\documentclass{article} +\\usepackage{fullpage} +\\usepackage{amssymb} +\\usepackage[usenames]{color} +\\usepackage{amsmath} +\\usepackage{latexsym} +\\usepackage[mathscr]{eucal} +\\pagestyle{empty} +\\begin{document}\n" string "\n\\end{document}\n")) + (let ((dir default-directory)) + (condition-case nil + (progn + (cd tmpdir) + (call-process "latex" nil nil nil texfile)) + (error nil)) + (cd dir)) + (if (not (file-exists-p dvifile)) + (progn (message "Failed to create dvi file from %s" texfile) nil) + (call-process "dvipng" nil nil nil + "-E" "-fg" fg "-bg" bg + "-x" scale "-y" scale "-T" "tight" + "-o" pngfile + dvifile) + (if (not (file-exists-p pngfile)) + (progn (message "Failed to create png file from %s" texfile) nil) + ;; Use the requested file name and clean up + (copy-file pngfile tofile 'replace) + (loop for e in '(".dvi" ".tex" ".aux" ".log" ".png") do + (delete-file (concat texfilebase e))) + pngfile)))) + ;;;; Exporting +;;; Variables, constants, and parameter plists + (defconst org-level-max 20) (defvar org-export-html-preamble nil @@ -14533,6 +15003,9 @@ overwritten, and the table is not marked as requiring realignment." "Should default preamble be inserted? Set by publishing functions.") (defvar org-export-html-auto-postamble t "Should default postamble be inserted? Set by publishing functions.") +(defvar org-current-export-file nil) ; dynamically scoped parameter +(defvar org-current-export-dir nil) ; dynamically scoped parameter + (defconst org-export-plist-vars '((:language . org-export-default-language) @@ -14711,8 +15184,6 @@ ones and overrule settings in the other lists." (call-interactively (cdr ass)) (error "No command associated with key %c" r1)))) -;;; ASCII - (defconst org-html-entities '(("nbsp") ("iexcl") @@ -15011,6 +15482,8 @@ The list contains HTML entities for Latin-1, Greek and other symbols. It is supplemented by a number of commonly used TeX macros with appropriate translations. There is currently no way for users to extend this.") +;;; General functions for all backends + (defun org-cleaned-string-for-export (string &rest parameters) "Cleanup a buffer substring so that links can be created safely." (interactive) @@ -15117,48 +15590,56 @@ translations. There is currently no way for users to extend this.") (a (assoc rtn alist))) (or (cdr a) rtn)))) -(defun org-convert-to-odd-levels () - "Convert an org-mode file with all levels allowed to one with odd levels. -This will leave level 1 alone, convert level 2 to level 3, level 3 to -level 5 etc." - (interactive) - (when (yes-or-no-p "Are you sure you want to globally change levels to odd? ") - (let ((org-odd-levels-only nil) n) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward "^\\*\\*+" nil t) - (setq n (1- (length (match-string 0)))) - (while (>= (setq n (1- n)) 0) - (org-demote)) - (end-of-line 1)))))) +;; Variable holding the vector with section numbers +(defvar org-section-numbers (make-vector org-level-max 0)) +(defun org-init-section-numbers () + "Initialize the vector for the section numbers." + (let* ((level -1) + (numbers (nreverse (org-split-string "" "\\."))) + (depth (1- (length org-section-numbers))) + (i depth) number-string) + (while (>= i 0) + (if (> i level) + (aset org-section-numbers i 0) + (setq number-string (or (car numbers) "0")) + (if (string-match "\\`[A-Z]\\'" number-string) + (aset org-section-numbers i + (- (string-to-char number-string) ?A -1)) + (aset org-section-numbers i (string-to-number number-string))) + (pop numbers)) + (setq i (1- i))))) -(defun org-convert-to-oddeven-levels () - "Convert an org-mode file with only odd levels to one with odd and even levels. -This promotes level 3 to level 2, level 5 to level 3 etc. If the file contains a -section with an even level, conversion would destroy the structure of the file. An error -is signaled in this case." - (interactive) - (goto-char (point-min)) - ;; First check if there are no even levels - (when (re-search-forward "^\\(\\*\\*\\)+[^*]" nil t) - (org-show-context t) - (error "Not all levels are odd in this file. Conversion not possible.")) - (when (yes-or-no-p "Are you sure you want to globally change levels to odd-even? ") - (let ((org-odd-levels-only nil) n) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward "^\\*\\*+" nil t) - (setq n (/ (length (match-string 0)) 2)) - (while (>= (setq n (1- n)) 0) - (org-promote)) - (end-of-line 1)))))) +(defun org-section-number (&optional level) + "Return a string with the current section number. +When LEVEL is non-nil, increase section numbers on that level." + (let* ((depth (1- (length org-section-numbers))) idx n (string "")) + (when level + (when (> level -1) + (aset org-section-numbers + level (1+ (aref org-section-numbers level)))) + (setq idx (1+ level)) + (while (<= idx depth) + (if (not (= idx 1)) + (aset org-section-numbers idx 0)) + (setq idx (1+ idx)))) + (setq idx 0) + (while (<= idx depth) + (setq n (aref org-section-numbers idx)) + (setq string (concat string (if (not (string= string "")) "." "") + (int-to-string n))) + (setq idx (1+ idx))) + (save-match-data + (if (string-match "\\`\\([@0]\\.\\)+" string) + (setq string (replace-match "" t nil string))) + (if (string-match "\\(\\.0\\)+\\'" string) + (setq string (replace-match "" t nil string)))) + string)) -(defun org-tr-level (n) - "Make N odd if required." - (if org-odd-levels-only (1+ (/ n 2)) n)) +;;; ASCII export (defvar org-last-level nil) ; dynamically scoped variable +(defvar org-levels-open nil) ; dynamically scoped parameter (defvar org-ascii-current-indentation nil) ; For communication (defun org-export-as-ascii (arg) @@ -15191,7 +15672,7 @@ underlined headlines. The default is 3." (file-name-nondirectory buffer-file-name)) ".txt")) (buffer (find-file-noselect filename)) - (levels-open (make-vector org-level-max nil)) + (org-levels-open (make-vector org-level-max nil)) (odd org-odd-levels-only) (date (format-time-string "%Y/%m/%d" (current-time))) (time (format-time-string "%X" (org-current-time))) @@ -15452,7 +15933,7 @@ command." (not (get-char-property s 'invisible)))) s)) -;;; HTML +;;; HTML export (defun org-get-current-options () "Return a string with current options as keyword options. @@ -15626,7 +16107,7 @@ org-mode's default settings, but still inferior to file-local settings." ".html")) (current-dir (file-name-directory buffer-file-name)) (buffer (find-file-noselect filename)) - (levels-open (make-vector org-level-max nil)) + (org-levels-open (make-vector org-level-max nil)) (date (format-time-string "%Y/%m/%d" (current-time))) (time (format-time-string "%X" (org-current-time))) (author (plist-get opt-plist :author)) @@ -16379,10 +16860,10 @@ When TITLE is nil, just close all open levels." (org-close-par-maybe) (let ((l (1+ (max level umax)))) (while (<= l org-level-max) - (if (aref levels-open (1- l)) + (if (aref org-levels-open (1- l)) (progn (org-html-level-close l) - (aset levels-open (1- l) nil))) + (aset org-levels-open (1- l) nil))) (setq l (1+ l))) (when title ;; If title is nil, this means this function is called to close @@ -16401,11 +16882,11 @@ When TITLE is nil, just close all open levels." t t title))) (if (> level umax) (progn - (if (aref levels-open (1- level)) + (if (aref org-levels-open (1- level)) (progn (org-close-li) (insert "
  • " title "
    \n")) - (aset levels-open (1- level) t) + (aset org-levels-open (1- level) t) (org-close-par-maybe) (insert "
      \n
    • " title "
      \n"))) (if org-export-with-section-numbers @@ -16422,52 +16903,7 @@ When TITLE is nil, just close all open levels." (org-close-li) (insert "
    ")) -;; Variable holding the vector with section numbers -(defvar org-section-numbers (make-vector org-level-max 0)) - -(defun org-init-section-numbers () - "Initialize the vector for the section numbers." - (let* ((level -1) - (numbers (nreverse (org-split-string "" "\\."))) - (depth (1- (length org-section-numbers))) - (i depth) number-string) - (while (>= i 0) - (if (> i level) - (aset org-section-numbers i 0) - (setq number-string (or (car numbers) "0")) - (if (string-match "\\`[A-Z]\\'" number-string) - (aset org-section-numbers i - (- (string-to-char number-string) ?A -1)) - (aset org-section-numbers i (string-to-number number-string))) - (pop numbers)) - (setq i (1- i))))) - -(defun org-section-number (&optional level) - "Return a string with the current section number. -When LEVEL is non-nil, increase section numbers on that level." - (let* ((depth (1- (length org-section-numbers))) idx n (string "")) - (when level - (when (> level -1) - (aset org-section-numbers - level (1+ (aref org-section-numbers level)))) - (setq idx (1+ level)) - (while (<= idx depth) - (if (not (= idx 1)) - (aset org-section-numbers idx 0)) - (setq idx (1+ idx)))) - (setq idx 0) - (while (<= idx depth) - (setq n (aref org-section-numbers idx)) - (setq string (concat string (if (not (string= string "")) "." "") - (int-to-string n))) - (setq idx (1+ idx))) - (save-match-data - (if (string-match "\\`\\([@0]\\.\\)+" string) - (setq string (replace-match "" t nil string))) - (if (string-match "\\(\\.0\\)+\\'" string) - (setq string (replace-match "" t nil string)))) - string)) - +;;; iCalendar export ;;;###autoload (defun org-export-icalendar-this-file () @@ -16477,6 +16913,175 @@ file, but with extension `.ics'." (interactive) (org-export-icalendar nil buffer-file-name)) +;;;###autoload +(defun org-export-icalendar-all-agenda-files () + "Export all files in `org-agenda-files' to iCalendar .ics files. +Each iCalendar file will be located in the same directory as the Org-mode +file, but with extension `.ics'." + (interactive) + (apply 'org-export-icalendar nil (org-agenda-files t))) + +;;;###autoload +(defun org-export-icalendar-combine-agenda-files () + "Export all files in `org-agenda-files' to a single combined iCalendar file. +The file is stored under the name `org-combined-agenda-icalendar-file'." + (interactive) + (apply 'org-export-icalendar t (org-agenda-files t))) + +(defun org-export-icalendar (combine &rest files) + "Create iCalendar files for all elements of FILES. +If COMBINE is non-nil, combine all calendar entries into a single large +file and store it under the name `org-combined-agenda-icalendar-file'." + (save-excursion + (let* ((dir (org-export-directory + :ical (list :publishing-directory + org-export-publishing-directory))) + file ical-file ical-buffer category started org-agenda-new-buffers) + + (when combine + (setq ical-file + (if (file-name-absolute-p org-combined-agenda-icalendar-file) + org-combined-agenda-icalendar-file + (expand-file-name org-combined-agenda-icalendar-file dir)) + ical-buffer (org-get-agenda-file-buffer ical-file)) + (set-buffer ical-buffer) (erase-buffer)) + (while (setq file (pop files)) + (catch 'nextfile + (org-check-agenda-file file) + (set-buffer (org-get-agenda-file-buffer file)) + (unless combine + (setq ical-file (concat (file-name-as-directory dir) + (file-name-sans-extension + (file-name-nondirectory buffer-file-name)) + ".ics")) + (setq ical-buffer (org-get-agenda-file-buffer ical-file)) + (with-current-buffer ical-buffer (erase-buffer))) + (setq category (or org-category + (file-name-sans-extension + (file-name-nondirectory buffer-file-name)))) + (if (symbolp category) (setq category (symbol-name category))) + (let ((standard-output ical-buffer)) + (if combine + (and (not started) (setq started t) + (org-start-icalendar-file org-icalendar-combined-name)) + (org-start-icalendar-file category)) + (org-print-icalendar-entries combine category) + (when (or (and combine (not files)) (not combine)) + (org-finish-icalendar-file) + (set-buffer ical-buffer) + (save-buffer) + (run-hooks 'org-after-save-iCalendar-file-hook))))) + (org-release-buffers org-agenda-new-buffers)))) + +(defvar org-after-save-iCalendar-file-hook nil + "Hook run after an iCalendar file has been saved. +The iCalendar buffer is still current when this hook is run. +A good way to use this is to tell a desktop calenndar application to re-read +the iCalendar file.") + + +;; FIXME: Strip down the links +(defun org-print-icalendar-entries (&optional combine category) + "Print iCalendar entries for the current Org-mode file to `standard-output'. +When COMBINE is non nil, add the category to each line." + (let ((re2 (concat "--?-?\\(" org-ts-regexp "\\)")) + (dts (org-ical-ts-to-string + (format-time-string (cdr org-time-stamp-formats) (current-time)) + "DTSTART")) + hd ts ts2 state (inc t) pos scheduledp deadlinep tmp pri) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward org-ts-regexp nil t) + (setq pos (match-beginning 0) + ts (match-string 0) + inc t + hd (org-get-heading)) + (if (looking-at re2) + (progn + (goto-char (match-end 0)) + (setq ts2 (match-string 1) inc nil)) + (setq ts2 ts + tmp (buffer-substring (max (point-min) + (- pos org-ds-keyword-length)) + pos) + deadlinep (string-match org-deadline-regexp tmp) + scheduledp (string-match org-scheduled-regexp tmp) + ;; donep (org-entry-is-done-p) + )) + (if (or (string-match org-tr-regexp hd) + (string-match org-ts-regexp hd)) + (setq hd (replace-match "" t t hd))) + (if deadlinep (setq hd (concat "DL: " hd))) + (if scheduledp (setq hd (concat "S: " hd))) + (princ (format "BEGIN:VEVENT +%s +%s +SUMMARY:%s +CATEGORIES:%s +END:VEVENT\n" + (org-ical-ts-to-string ts "DTSTART") + (org-ical-ts-to-string ts2 "DTEND" inc) + hd category))) + (when org-icalendar-include-todo + (goto-char (point-min)) + (while (re-search-forward org-todo-line-regexp nil t) + (setq state (match-string 1)) + (unless (equal state org-done-string) + (setq hd (match-string 3)) + (if (string-match org-priority-regexp hd) + (setq pri (string-to-char (match-string 2 hd)) + hd (concat (substring hd 0 (match-beginning 1)) + (substring hd (- (match-end 1))))) + (setq pri org-default-priority)) + (setq pri (floor (1+ (* 8. (/ (float (- org-lowest-priority pri)) + (- org-lowest-priority ?A)))))) + + (princ (format "BEGIN:VTODO +%s +SUMMARY:%s +CATEGORIES:%s +SEQUENCE:1 +PRIORITY:%d +END:VTODO\n" + dts hd category pri)))))))) + +(defun org-start-icalendar-file (name) + "Start an iCalendar file by inserting the header." + (let ((user user-full-name) + (name (or name "unknown")) + (timezone (cadr (current-time-zone)))) + (princ + (format "BEGIN:VCALENDAR +VERSION:2.0 +X-WR-CALNAME:%s +PRODID:-//%s//Emacs with Org-mode//EN +X-WR-TIMEZONE:%s +CALSCALE:GREGORIAN\n" name user timezone)))) + +(defun org-finish-icalendar-file () + "Finish an iCalendar file by inserting the END statement." + (princ "END:VCALENDAR\n")) + +(defun org-ical-ts-to-string (s keyword &optional inc) + "Take a time string S and convert it to iCalendar format. +KEYWORD is added in front, to make a complete line like DTSTART.... +When INC is non-nil, increase the hour by two (if time string contains +a time), or the day by one (if it does not contain a time)." + (let ((t1 (org-parse-time-string s 'nodefault)) + t2 fmt have-time time) + (if (and (car t1) (nth 1 t1) (nth 2 t1)) + (setq t2 t1 have-time t) + (setq t2 (org-parse-time-string s))) + (let ((s (car t2)) (mi (nth 1 t2)) (h (nth 2 t2)) + (d (nth 3 t2)) (m (nth 4 t2)) (y (nth 5 t2))) + (when inc + (if have-time (setq h (+ 2 h)) (setq d (1+ d)))) + (setq time (encode-time s mi h d m y))) + (setq fmt (if have-time ":%Y%m%dT%H%M%S" ";VALUE=DATE:%Y%m%d")) + (concat keyword (format-time-string fmt time)))) + +;;; XOXO export + (defun org-export-as-xoxo-insert-into (buffer &rest output) (with-current-buffer buffer (apply 'insert output))) @@ -16560,457 +17165,6 @@ The XOXO buffer is named *xoxo-*" (goto-char (point-min)) ))) -;;;###autoload -(defun org-export-icalendar-all-agenda-files () - "Export all files in `org-agenda-files' to iCalendar .ics files. -Each iCalendar file will be located in the same directory as the Org-mode -file, but with extension `.ics'." - (interactive) - (apply 'org-export-icalendar nil (org-agenda-files t))) - -;;;###autoload -(defun org-export-icalendar-combine-agenda-files () - "Export all files in `org-agenda-files' to a single combined iCalendar file. -The file is stored under the name `org-combined-agenda-icalendar-file'." - (interactive) - (apply 'org-export-icalendar t (org-agenda-files t))) - -(defun org-export-icalendar (combine &rest files) - "Create iCalendar files for all elements of FILES. -If COMBINE is non-nil, combine all calendar entries into a single large -file and store it under the name `org-combined-agenda-icalendar-file'." - (save-excursion - (let* ((dir (org-export-directory - :ical (list :publishing-directory - org-export-publishing-directory))) - file ical-file ical-buffer category started org-agenda-new-buffers) - - (when combine - (setq ical-file - (if (file-name-absolute-p org-combined-agenda-icalendar-file) - org-combined-agenda-icalendar-file - (expand-file-name org-combined-agenda-icalendar-file dir)) - ical-buffer (org-get-agenda-file-buffer ical-file)) - (set-buffer ical-buffer) (erase-buffer)) - (while (setq file (pop files)) - (catch 'nextfile - (org-check-agenda-file file) - (set-buffer (org-get-agenda-file-buffer file)) - (unless combine - (setq ical-file (concat (file-name-as-directory dir) - (file-name-sans-extension - (file-name-nondirectory buffer-file-name)) - ".ics")) - (setq ical-buffer (org-get-agenda-file-buffer ical-file)) - (with-current-buffer ical-buffer (erase-buffer))) - (setq category (or org-category - (file-name-sans-extension - (file-name-nondirectory buffer-file-name)))) - (if (symbolp category) (setq category (symbol-name category))) - (let ((standard-output ical-buffer)) - (if combine - (and (not started) (setq started t) - (org-start-icalendar-file org-icalendar-combined-name)) - (org-start-icalendar-file category)) - (org-print-icalendar-entries combine category) - (when (or (and combine (not files)) (not combine)) - (org-finish-icalendar-file) - (set-buffer ical-buffer) - (save-buffer) - (run-hooks 'org-after-save-iCalendar-file-hook))))) - (org-release-buffers org-agenda-new-buffers)))) - -(defvar org-after-save-iCalendar-file-hook nil - "Hook run after an iCalendar file has been saved. -The iCalendar buffer is still current when this hook is run. -A good way to use this is to tell a desktop calenndar application to re-read -the iCalendar file.") - -(defun org-print-icalendar-entries (&optional combine category) - "Print iCalendar entries for the current Org-mode file to `standard-output'. -When COMBINE is non nil, add the category to each line." - (let ((re2 (concat "--?-?\\(" org-ts-regexp "\\)")) - (dts (org-ical-ts-to-string - (format-time-string (cdr org-time-stamp-formats) (current-time)) - "DTSTART")) - hd ts ts2 state (inc t) pos scheduledp deadlinep tmp pri) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward org-ts-regexp nil t) - (setq pos (match-beginning 0) - ts (match-string 0) - inc t - hd (org-get-heading)) - (if (looking-at re2) - (progn - (goto-char (match-end 0)) - (setq ts2 (match-string 1) inc nil)) - (setq ts2 ts - tmp (buffer-substring (max (point-min) - (- pos org-ds-keyword-length)) - pos) - deadlinep (string-match org-deadline-regexp tmp) - scheduledp (string-match org-scheduled-regexp tmp) - ;; donep (org-entry-is-done-p) - )) - (if (or (string-match org-tr-regexp hd) - (string-match org-ts-regexp hd)) - (setq hd (replace-match "" t t hd))) - (if combine - (setq hd (concat hd " (category " category ")"))) - (if deadlinep (setq hd (concat "DL: " hd " This is a deadline"))) - (if scheduledp (setq hd (concat "S: " hd " Scheduled for this date"))) - (princ (format "BEGIN:VEVENT -%s -%s -SUMMARY:%s -END:VEVENT\n" - (org-ical-ts-to-string ts "DTSTART") - (org-ical-ts-to-string ts2 "DTEND" inc) - hd))) - (when org-icalendar-include-todo - (goto-char (point-min)) - (while (re-search-forward org-todo-line-regexp nil t) - (setq state (match-string 1)) - (unless (equal state org-done-string) - (setq hd (match-string 3)) - (if (string-match org-priority-regexp hd) - (setq pri (string-to-char (match-string 2 hd)) - hd (concat (substring hd 0 (match-beginning 1)) - (substring hd (- (match-end 1))))) - (setq pri org-default-priority)) - (setq pri (floor (1+ (* 8. (/ (float (- org-lowest-priority pri)) - (- org-lowest-priority ?A)))))) - - (princ (format "BEGIN:VTODO -%s -SUMMARY:%s -SEQUENCE:1 -PRIORITY:%d -END:VTODO\n" - dts hd pri)))))))) - -(defun org-start-icalendar-file (name) - "Start an iCalendar file by inserting the header." - (let ((user user-full-name) - (name (or name "unknown")) - (timezone (cadr (current-time-zone)))) - (princ - (format "BEGIN:VCALENDAR -VERSION:2.0 -X-WR-CALNAME:%s -PRODID:-//%s//Emacs with Org-mode//EN -X-WR-TIMEZONE:%s -CALSCALE:GREGORIAN\n" name user timezone)))) - -(defun org-finish-icalendar-file () - "Finish an iCalendar file by inserting the END statement." - (princ "END:VCALENDAR\n")) - -(defun org-ical-ts-to-string (s keyword &optional inc) - "Take a time string S and convert it to iCalendar format. -KEYWORD is added in front, to make a complete line like DTSTART.... -When INC is non-nil, increase the hour by two (if time string contains -a time), or the day by one (if it does not contain a time)." - (let ((t1 (org-parse-time-string s 'nodefault)) - t2 fmt have-time time) - (if (and (car t1) (nth 1 t1) (nth 2 t1)) - (setq t2 t1 have-time t) - (setq t2 (org-parse-time-string s))) - (let ((s (car t2)) (mi (nth 1 t2)) (h (nth 2 t2)) - (d (nth 3 t2)) (m (nth 4 t2)) (y (nth 5 t2))) - (when inc - (if have-time (setq h (+ 2 h)) (setq d (1+ d)))) - (setq time (encode-time s mi h d m y))) - (setq fmt (if have-time ":%Y%m%dT%H%M%S" ";VALUE=DATE:%Y%m%d")) - (concat keyword (format-time-string fmt time)))) - -;;;; LaTeX stuff - -(defvar org-cdlatex-mode-map (make-sparse-keymap) - "Keymap for the minor `org-cdlatex-mode'.") - -(define-key org-cdlatex-mode-map "_" 'org-cdlatex-underscore-caret) -(define-key org-cdlatex-mode-map "^" 'org-cdlatex-underscore-caret) -(define-key org-cdlatex-mode-map "`" 'cdlatex-math-symbol) -(define-key org-cdlatex-mode-map "'" 'org-cdlatex-math-modify) -(define-key org-cdlatex-mode-map "\C-c{" 'cdlatex-environment) - -(defvar org-cdlatex-texmathp-advice-is-done nil - "Flag remembering if we have applied the advice to texmathp already.") - -(define-minor-mode org-cdlatex-mode - "Toggle the minor `org-cdlatex-mode'. -This mode supports entering LaTeX environment and math in LaTeX fragments -in Org-mode. -\\{org-cdlatex-mode-map}" - nil " OCDL" nil - (when org-cdlatex-mode (require 'cdlatex)) - (unless org-cdlatex-texmathp-advice-is-done - (setq org-cdlatex-texmathp-advice-is-done t) - (defadvice texmathp (around org-math-always-on activate) - "Always return t in org-mode buffers. -This is because we want to insert math symbols without dollars even outside -the LaTeX math segments. If Orgmode thinks that point is actually inside -en embedded LaTeX fragement, let texmathp do its job. -\\[org-cdlatex-mode-map]" - (interactive) - (let (p) - (cond - ((not (org-mode-p)) ad-do-it) - ((eq this-command 'cdlatex-math-symbol) - (setq ad-return-value t - texmathp-why '("cdlatex-math-symbol in org-mode" . 0))) - (t - (let ((p (org-inside-LaTeX-fragment-p))) - (if (and p (member (car p) (plist-get org-format-latex-options :matchers))) - (setq ad-return-value t - texmathp-why '("Org-mode embedded math" . 0)) - (if p ad-do-it))))))))) - -(defun turn-on-org-cdlatex () - "Unconditionally turn on `org-cdlatex-mode'." - (org-cdlatex-mode 1)) - -(defun org-inside-LaTeX-fragment-p () - "Test if point is inside a LaTeX fragment. -I.e. after a \\begin, \\(, \\[, $, or $$, without the corresponding closing -sequence appearing also before point. -Even though the matchers for math are configurable, this function assumes -that \\begin, \\(, \\[, and $$ are always used. Only the single dollar -delimiters are skipped when they have been removed by customization. -The return value is nil, or a cons cell with the delimiter and -and the position of this delimiter. - -This function does a reasonably good job, but can locally be fooled by -for example currency specifications. For example it will assume being in -inline math after \"$22.34\". The LaTeX fragment formatter will only format -fragments that are properly closed, but during editing, we have to live -with the uncertainty caused by missing closing delimiters. This function -looks only before point, not after." - (catch 'exit - (let ((pos (point)) - (dodollar (member "$" (plist-get org-format-latex-options :matchers))) - (lim (progn - (re-search-backward (concat "^\\(" paragraph-start "\\)") nil t) - (point))) - dd-on str (start 0) m re) - (goto-char pos) - (when dodollar - (setq str (concat (buffer-substring lim (point)) "\000 X$.") - re (nth 1 (assoc "$" org-latex-regexps))) - (while (string-match re str start) - (cond - ((= (match-end 0) (length str)) - (throw 'exit (cons "$" (+ lim (match-beginning 0))))) - ((= (match-end 0) (- (length str) 5)) - (throw 'exit nil)) - (t (setq start (match-end 0)))))) - (when (setq m (re-search-backward "\\(\\\\begin{[^}]*}\\|\\\\(\\|\\\\\\[\\)\\|\\(\\\\end{[^}]*}\\|\\\\)\\|\\\\\\]\\)\\|\\(\\$\\$\\)" lim t)) - (goto-char pos) - (and (match-beginning 1) (throw 'exit (cons (match-string 1) m))) - (and (match-beginning 2) (throw 'exit nil)) - ;; count $$ - (while (re-search-backward "\\$\\$" lim t) - (setq dd-on (not dd-on))) - (goto-char pos) - (if dd-on (cons "$$" m)))))) - - -(defun org-try-cdlatex-tab () - "Check if it makes sense to execute `cdlatex-tab', and do it if yes. -It makes sense to do so if `org-cdlatex-mode' is active and if the cursor is - - inside a LaTeX fragment, or - - after the first word in a line, where an abbreviation expansion could - insert a LaTeX environment." - (when org-cdlatex-mode - (cond - ((save-excursion - (skip-chars-backward "a-zA-Z0-9*") - (skip-chars-backward " \t") - (bolp)) - (cdlatex-tab) t) - ((org-inside-LaTeX-fragment-p) - (cdlatex-tab) t) - (t nil)))) - -(defun org-cdlatex-underscore-caret (&optional arg) - "Execute `cdlatex-sub-superscript' in LaTeX fragments. -Revert to the normal definition outside of these fragments." - (interactive "P") - (if (org-inside-LaTeX-fragment-p) - (call-interactively 'cdlatex-sub-superscript) - (let (org-cdlatex-mode) - (call-interactively (key-binding (vector last-input-event)))))) - -(defun org-cdlatex-math-modify (&optional arg) - "Execute `cdlatex-math-modify' in LaTeX fragments. -Revert to the normal definition outside of these fragments." - (interactive "P") - (if (org-inside-LaTeX-fragment-p) - (call-interactively 'cdlatex-math-modify) - (let (org-cdlatex-mode) - (call-interactively (key-binding (vector last-input-event)))))) - -(defvar org-latex-fragment-image-overlays nil - "List of overlays carrying the images of latex fragments.") -(make-variable-buffer-local 'org-latex-fragment-image-overlays) - -(defun org-remove-latex-fragment-image-overlays () - "Remove all overlays with LaTeX fragment images in current buffer." - (mapc 'org-delete-overlay org-latex-fragment-image-overlays) - (setq org-latex-fragment-image-overlays nil)) - -(defun org-preview-latex-fragment (&optional subtree) - "Preview the LaTeX fragment at point, or all locally or globally. -If the cursor is in a LaTeX fragment, create the image and overlay -it over the source code. If there is no fragment at point, display -all fragments in the current text, from one headline to the next. With -prefix SUBTREE, display all fragments in the current subtree. With a -double prefix `C-u C-u', or when the cursor is before the first headline, -display all fragments in the buffer. -The images can be removed again with \\[org-ctrl-c-ctrl-c]." - (interactive "P") - (org-remove-latex-fragment-image-overlays) - (save-excursion - (save-restriction - (let (beg end at msg) - (cond - ((or (equal subtree '(16)) - (not (save-excursion - (re-search-backward (concat "^" outline-regexp) nil t)))) - (setq beg (point-min) end (point-max) - msg "Creating images for buffer...%s")) - ((equal subtree '(4)) - (org-back-to-heading) - (setq beg (point) end (org-end-of-subtree t) - msg "Creating images for subtree...%s")) - (t - (if (setq at (org-inside-LaTeX-fragment-p)) - (goto-char (max (point-min) (- (cdr at) 2))) - (org-back-to-heading)) - (setq beg (point) end (progn (outline-next-heading) (point)) - msg (if at "Creating image...%s" - "Creating images for entry...%s")))) - (message msg "") - (narrow-to-region beg end) - (org-format-latex - (concat "ltxpng/" (file-name-sans-extension - (file-name-nondirectory - buffer-file-name))) - default-directory 'overlays msg at) - (message msg "done. Use `C-c C-c' to remove images."))))) - -(defvar org-latex-regexps - '(("begin" "^[ \t]*\\(\\\\begin{\\([a-zA-Z0-9\\*]+\\)[^\000]+?\\\\end{\\2}\\)" 1 t) - ;; ("$" "\\([ (]\\|^\\)\\(\\(\\([$]\\)\\([^ \r\n,.$].*?\\(\n.*?\\)\\{0,5\\}[^ \r\n,.$]\\)\\4\\)\\)\\([ .,?;:'\")]\\|$\\)" 2 nil) - ;; \000 in the following regex is needed for org-inside-LaTeX-fragment-p - ("$" "\\([^$]\\)\\(\\(\\$\\([^ \r\n,;.$][^$\n\r]*?\\(\n[^$\n\r]*?\\)\\{0,2\\}[^ \r\n,.$]\\)\\$\\)\\)\\([ .,?;:'\")\000]\\|$\\)" 2 nil) - ("\\(" "\\\\([^\000]*?\\\\)" 0 nil) - ("\\[" "\\\\\\[[^\000]*?\\\\\\]" 0 t) - ("$$" "\\$\\$[^\000]*?\\$\\$" 0 t)) - "Regular expressions for matching embedded LaTeX.") - -(defun org-format-latex (prefix &optional dir overlays msg at) - "Replace LaTeX fragments with links to an image, and produce images." - (if (and overlays (fboundp 'clear-image-cache)) (clear-image-cache)) - (let* ((prefixnodir (file-name-nondirectory prefix)) - (absprefix (expand-file-name prefix dir)) - (todir (file-name-directory absprefix)) - (opt org-format-latex-options) - (matchers (plist-get opt :matchers)) - (re-list org-latex-regexps) - (cnt 0) txt link beg end re e oldfiles - m n block linkfile movefile ov) - ;; Make sure the directory exists - (or (file-directory-p todir) (make-directory todir)) - ;; Check if there are old images files with this prefix, and remove them - (setq oldfiles (directory-files - todir 'full - (concat (regexp-quote prefixnodir) "_[0-9]+\\.png$"))) - (while oldfiles (delete-file (pop oldfiles))) - ;; Check the different regular expressions - (while (setq e (pop re-list)) - (setq m (car e) re (nth 1 e) n (nth 2 e) - block (if (nth 3 e) "\n\n" "")) - (when (member m matchers) - (goto-char (point-min)) - (while (re-search-forward re nil t) - (when (or (not at) (equal (cdr at) (match-beginning n))) - (setq txt (match-string n) - beg (match-beginning n) end (match-end n) - cnt (1+ cnt) - linkfile (format "%s_%04d.png" prefix cnt) - movefile (format "%s_%04d.png" absprefix cnt) - link (concat block "[[file:" linkfile "]]" block)) - (if msg (message msg cnt)) - (goto-char beg) - (org-create-formula-image - txt movefile opt) - (if overlays - (progn - (setq ov (org-make-overlay beg end)) - (if (featurep 'xemacs) - (progn - (org-overlay-put ov 'invisible t) - (org-overlay-put - ov 'end-glyph - (make-glyph (vector 'png :file movefile)))) - (org-overlay-put - ov 'display - (list 'image :type 'png :file movefile :ascent 'center))) - (push ov org-latex-fragment-image-overlays) - (goto-char end)) - (delete-region beg end) - (insert link)))))))) - -;; This function borrows from Ganesh Swami's latex2png.el -(defun org-create-formula-image (string tofile options) - (let* ((tmpdir (if (featurep 'xemacs) - (temp-directory) - temporary-file-directory)) - (texfilebase (make-temp-name - (expand-file-name "orgtex" tmpdir))) - -;(texfilebase (make-temp-file "orgtex")) -; (dummy (delete-file texfilebase)) - (texfile (concat texfilebase ".tex")) - (dvifile (concat texfilebase ".dvi")) - (pngfile (concat texfilebase ".png")) - (scale (number-to-string (* 1000 (or (plist-get options :scale) 1.0)))) - (fg (or (plist-get options :foreground) "Black")) - (bg (or (plist-get options :background) "Transparent"))) - (with-temp-file texfile - (insert "\\documentclass{article} -\\usepackage{fullpage} -\\usepackage{amssymb} -\\usepackage[usenames]{color} -\\usepackage{amsmath} -\\usepackage{latexsym} -\\usepackage[mathscr]{eucal} -\\pagestyle{empty} -\\begin{document}\n" string "\n\\end{document}\n")) - (let ((dir default-directory)) - (condition-case nil - (progn - (cd tmpdir) - (call-process "latex" nil nil nil texfile)) - (error nil)) - (cd dir)) - (if (not (file-exists-p dvifile)) - (progn (message "Failed to create dvi file from %s" texfile) nil) - (call-process "dvipng" nil nil nil - "-E" "-fg" fg "-bg" bg - "-x" scale "-y" scale "-T" "tight" - "-o" pngfile - dvifile) - (if (not (file-exists-p pngfile)) - (progn (message "Failed to create png file from %s" texfile) nil) - ;; Use the requested file name and clean up - (copy-file pngfile tofile 'replace) - (loop for e in '(".dvi" ".tex" ".aux" ".log" ".png") do - (delete-file (concat texfilebase e))) - pngfile)))) ;;;; Key bindings @@ -17598,7 +17752,7 @@ See the individual commands for more information." ["Reveal Context" org-reveal t] ["Show All" show-all t] "--" - ["Subtree to indirect buffer" 'org-tree-to-indirect-buffer t]) + ["Subtree to indirect buffer" org-tree-to-indirect-buffer t]) "--" ["New Heading" org-insert-heading t] ("Navigate Headings" @@ -17798,6 +17952,9 @@ With optional NODE, go directly to that node." ;;;; Miscellaneous stuff + +;;; Generally useful functions + (defun org-context () "Return a list of contexts of the current cursor position. If several contexts apply, all are returned. @@ -17942,7 +18099,7 @@ return nil." (setq string (replace-match (cdr e) t t string)))) string)) -;;;; Paragraph filling stuff. +;;; Paragraph filling stuff. ;; We want this to be just right, so use the full arsenal. (defun org-set-autofill-regexps () @@ -17999,49 +18156,6 @@ work correctly." (make-string (- (match-end 0) (match-beginning 0)) ?\ )) (t nil))) -;; Functions needed for Emacs/XEmacs region compatibility - -(defun org-add-hook (hook function &optional append local) - "Add-hook, compatible with both Emacsen." - (if (and local (featurep 'xemacs)) - (add-local-hook hook function append) - (add-hook hook function append local))) - -(defun org-region-active-p () - "Is `transient-mark-mode' on and the region active? -Works on both Emacs and XEmacs." - (if org-ignore-region - nil - (if (featurep 'xemacs) - (and zmacs-regions (region-active-p)) - (and transient-mark-mode mark-active)))) - -(defun org-add-to-invisibility-spec (arg) - "Add elements to `buffer-invisibility-spec'. -See documentation for `buffer-invisibility-spec' for the kind of elements -that can be added." - (cond - ((fboundp 'add-to-invisibility-spec) - (add-to-invisibility-spec arg)) - ((or (null buffer-invisibility-spec) (eq buffer-invisibility-spec t)) - (setq buffer-invisibility-spec (list arg))) - (t - (setq buffer-invisibility-spec - (cons arg buffer-invisibility-spec))))) - -(defun org-remove-from-invisibility-spec (arg) - "Remove elements from `buffer-invisibility-spec'." - (if (fboundp 'remove-from-invisibility-spec) - (remove-from-invisibility-spec arg) - (if (consp buffer-invisibility-spec) - (setq buffer-invisibility-spec - (delete arg buffer-invisibility-spec))))) - -(defun org-in-invisibility-spec-p (arg) - "Is ARG a member of `buffer-invisibility-spec'?" - (if (consp buffer-invisibility-spec) - (member arg buffer-invisibility-spec) - nil)) (defun org-image-file-name-regexp () "Return regexp matching the file names of images." diff --git a/org.pdf b/org.pdf index d04fbf5eaa27ee36bdd48e78a09407fc3774ea21..1235b9dfc3fa35e1233602cae1475917aca21d8e 100644 GIT binary patch delta 101876 zcmV)EK)}ED{wAFMCXiAE3NM|72a#7Kf2HArG8tJDL2^+(?u0o`Hv*59Bg^^Zlg@7% z|M;v~UDvk!KX1g@b&b**2tLVXl~?D)O^p`s;TT2J?a!u~-_}F~*GJnd7}6WlCm@UXHz7iF0S` zwd3KnAEy3tzvIbzSo6I(;Ta79BK_wXAtn0u{0S&lX#TTP0Sf_tOK+P%5Wf3Ypj^60 zf$Xw?p*=N8UA5{FMGCX?{$^%pZ5$1M#!0m^x7JU? z#L0IvE!_K=5h}C+eE%!*q9Aa*BkSws>}nAi$7ny*sk2<8_aHF-JWZTs@l!3gRquM% zTD5aMqq4>gQnk;2YUZ&lRBP9$wqxGsZsYA**9#I=lq|l}kp_)N7kDMQ>H4Z|SUvPX z5~ZMGeD~M#J>=<`*f$}CziA)P=uZbq)n<$IKw44g2XpIa-v%jZ=E%NU#QF$}XMJq} z^+D@Sn}`)pK%3By!WcrMPM~#o$;km39mtr`EZihiT`AaqzP1Q{z|5Z9V6HMuwVX7q zYWKis3iy`|h#Ir^Z0x5=!bmNr_Jyj`?W(@V5^;f&h-;k8)u!X(4^+VdnK8tL!m~V= zQek$GbEvh#pjAI~)e49`8lp|s6j#(Na86B?W}))>&%rNu9u5U7H@rmZ!Pv4kHCG5rkBE7|g05P)mqqnl5>EI%h z?S~3vIm>#o$GX_L`i@RZ8A8-FzF`9Eg5Rv+TGFA;hG*gNOn2NeYzB4>O8r;9 zdUPYe)}cBh93)0bWunqa!j~st39C4^(II0SOU5m`^KM#>Gu?DCD1Q$1rIq1kV#$i8j{MbBSvWoUt``}tUbD({3CRUcHhwuxAnGcYD$np zaXc!>#5;E4C{{eqInO#uWcsV2K3Jba##&2}?Z2Z2oLjl*)4dw+JKZaHTuPw%;S7j> zalF0lxNajY1;l&1+|mV3)Z#1HEnf6|8k_fjzu_S>X%uTuSL&tf#U>z3KV;F8`ZhcE zf+o131!&RkWEZt8J9G@WG-F7NFBnFHrTENV5^{egWh+!g~A>Fb!EzawF>Y;rR97X_>uQ&i(@FLn5}b`UShhmtC>}EPqRL+c*-w z`&X!T4@1?O7=i>}Ic$}a^<(XDNwmd86161#`uE!}kQ6B?aV1HWOahHY zqtW>AbyH6~_^&6aH;IElr6!pyHfNr+em&Fj9tcxbr$x~cA9$O1<+@Nieb zU_bOi&k3iWXK&9gXRSS{}cqM(Mzi#n&!Q^6PKfOcIqw5sGN7OlT&a z3euE>RzpLyXH*dBNtAjj2~(Wue`lXQdy^#?_4W)$CX)v=P-)PyIe*i!ioqg&U7uZ_ z{in0pDsy|<$nIbgt28Ey?0h}TXVWBcE?yorE%B9~>Yrs=r;SY8bC<7GM5cv7K&BP! z(>=et0ZO?#P@!S3zH;xzzW2ceI+?JUhkXw!V&6=n7!3D*)c2yy?P(kOzMT)&lAQLv z6^&A*js97tb=t**jD zc$gC^2ej$2#dz>)gf#D#r>{-zZ`HPtXL&` zroJop9GD3>7Tda5=HiNMzRv6O14m2iP=#?MoAM1rJ94Ugi2pE%Cy}432z32W=vrlN zPut*Bb@TZu6@QN&IaQsgR@zH`nrW@J(W&xNqhc^S@&gqJS``QOF;;lpbYA^}H`!)a z%TAWEaVy!uU6tP#CCZ2doCCwhMLX*yJgGy#>BH`&X@n0d4t&AR1?zm}W(N!lW7UQO z##70sizdz1>ti#~up`q{r>D81Mcm^&IpK-%`A#nx0Dq|avEh!aA}?&0{JH}Oh@G-v z$#ZNBoqEMa7u#EwYLUeRtQl2QRYxX3riVDy(=;)TDC>{CDRKR4>k{n{-pM4ZJX?Yt zf_?3YrzK`+uO9`iHa`j-d@S-Rs}`tlg+EW&1Ad@%a(i;2nCAZGeFHd_~>K&-1 zaKr_TF|Mf)je}VDS-IJe6N1=-*bO}%myZst0kRNI!VnZCboe1?sISM=M9A$)jp^k; z6UB4BXbM~?TIDOSKIZ37*YFJMsl{Tp9TxW)gMT3Lpzl_?lA*=MgDC-ud&V3fmsaYu z%|l&iG&oE1)VRs$zD!mCHf{0bXX55^gEo_!aXWwsBLwNaT_M-!?Vjy15a0?XPA8%9 zloy0Yd~c=Pp4fOHLtXpc2T({<$Pb?`a1|N9^p_x^^%)+{1p|2#0Ef(o-Ukt|%_Qj? ze}7;Xtkh}ahq~-897fn!R1);*A@n_3>hcVrjZ?w>I5~_~&|cg2c~jYIAm_2gmPIo8 zOBl7p-zt|>iQ4Bl9O)E|6^sTt2)C@E0|eJ=3^y1gL?Pc}!GTp6*k^ETD+3@E!h!jS z2n1k9Gm+tRzwuPXf?ytc;9A3r?2+Ftu76PQ^|^$v4|k+a_rmuUaVz#o*^VC@YJyd* zike&2d54*6?qvKWseCI>*;kSB1I+b`!pF<<_OP929~qEQ@yDMKdDx%#>a2hspzV2Lk0u zQuuLYhr#9}`L!#i`McJ-z^#1p0UQFbZ&NLh`}cS6;JpR&^uq3%eb~oED9z6gFi>X9nk45L!f> zQ^h&9tiCO=z3yRVIX@EUzVQ7ye3WQl7v?#H+kjuVjp7w`{r3q?!ZS& z#&G0vY3Y-v{1T8W!PQbq;PbP!@m;r|C4kpeCI;8$K~iAthJPC@@Pi>I*q$Z> z@AAi{Gm2OC?38PFdX7KalYCG9zY{mLIGah#KYQYv=Pyo5G}*?I80cO1a>Dhuj2N-5 z`A=Fs>-z!AM((*gZQ09gfw=MY0o7;Kaz9>Ih#GDI92e&;MW+uquw8XMbu`3M)rc2?4!UTi`V zFh@P>qG2cpLPlIJY{@pjb3I13mIK60h6bM|;*#SGG`e}glW!%!_h&eE{M5koc z6ZZ}<1g@;m19RH=V}(AL?bGt;Wg)DQUOIlo6Hy@7wj?nx=zp@2rH^rb@wBkylA|2t zFf$xi{|tUsh)&kjQ?202pXe}Ek^h4wHml6-X&ctm+xdJ5)8J%Hy%p8Ui+-AAR@u1r z=Orrg;4;R<+tWvDe}M`U(=Yx>j_)&v4E&a!QpJQe(U&-{xPRH+R1L*_hN(5>2)=W1 z(-1`XNss5(9Df{beQHqTO%wWHaAKbg#80B*XY6yLH1PO?-n7b2>~l9?x6fC|rDw-2 z`_66tKEsY~vjI8+a?ju9bKW~<$?&kaYb$7_Y}jT{^Q$EnPLMyv`vo>cr?e|j`e`Kj z$U0v%mZtlu!Ga~>SjP1i93)UR9A8#+#S~l2dvNGna(^>o)xPA>I7k>T4jNvYsG;t< ziuO{VN@t9fPA(>bo{YcY^jAHCMYi2Y*4DMld-T?jI}JSt++CMyv4q(6z6L@-9x)@gm{69U7wp^QNsPiODY|Nb0}w zdm#)r2>YLrLgh!Hu!-02K*0x=mRuem|H!yhl&wegy%cbQ@|<=Fhw6JGb!w$UO+EYt z3myup@bk+;K0su0TCj}|FFXh(U{Cb74i5_-T7Uc$*E$h5E;BGVATu^JE;BedAT=~NlaWOzm){`+9+ThK_XaaFE;%wegWTAM+}HuP z+}Hx~XaO{r;Uxhoe_BhE+cpxu&#zGJiK-eR1PH$N=Gcxascf9JXOlzX14rUW%uwPi zNm-tMpMHU)BuXQvTyqcsG=YA8=x&al1@Nb5Sujh&P-QlqEjBlS%s$>|dGdk4A5naN zck|s3k)G*5WkEK(dl>I`k?s@N`bp8KsV z_U+s^iHS1L#`8~iKi#~&>(7mhd4Vycc#*R>wMvJUIO}RSIf}e?UC&MG?Rk`Mf!ifM zTko&k=82yiZMTNif}iWAT$M1~<`ol>h+EvNYheDF$trk+w_$dH>v@Oo)Z1HFje~>Z z+#$uX>||!$f1kDT{|vMR;);%GdHzojreT(}x2ajnc;fTFnBW1Pn|g!l1U30OR61hm zdr2}cM?1g}O%bKuJv{EV7y+-NgafvnC&niPM)Q|ewI(n|uAl~ZSmuW8m~e<4SWdMA!LRtyfeOMckaooFw#9@?cUS{`h2+~AqXmgQ$070s$pbe6W@ zofZy^l{R$V$9e}s-jT@kkpmcsO-k{G# zO#*Fl(j(d6mX*^xAZU$5+@Y(swWxkuhzqcHgc2UG(yIUm<@R*9WkKhlV>P`W0S$Ul zxn)xTG=w8)KqtslkY>d6f9kFTB{5lgO2$-g z`9^rnTi!+6S*o%)CY&c?3{+g)9&dkQ)#Lq`;B<& zfA=Dm##%CwR&hi;B`G^wIbo4dMJc%CpiHoVRui^`pp29W?byUmmreX@4?UmJwIJfy zsN!f29-_dx{zbEEI0NvE?4F&mdmN}V0Txmf=qt<)l0?Ry(J}Akk2X6tyE{!Qld;v8 z0I`|3-=1SE2BSvbh_8OHQ`0AzO4FD)e~UA7jqS4&+lL3X&q8IBvFU$9_$c<@y{qr$ zY3S8#H@JsgBm?M^^`}vyzh?=D{qWS>NCjRVnQ2o42xI|>7#bCuAnde8Wh;NQ z*O8^&VJ3oKxtM-^eU2q#RS;$0hNWSz6DtE20edN&7ppM15aB6EWyWTHQ?^ePe_h*@ zm7H7@;#@t?quzErXHdG1V9f!}ptRzPAs+Cl7kD*jIk3*9AP>&;^Y}KiwfBHJmDm01s zYk1jTEtu)5D-m2Vh+goEVY4&-8b+?L`0y6^zdOejl*uMZz7bc$W|Q6;1tBpO>GYDj z1|Z2OO?xLMa#??m!@avALcN6FhqzyMg0PcziDqU)Hm3?~^ zkUv}oa!x2#t4hi^uCS`gf3+dFd?SgLE`F#Oi@hHiv8jB!<~69bq9DL@la{5r<@c#D zxZDdH^fv-ts_5Nccc0Ei0 zt*iRMEH;U~J918#C9f>Ik{r{9ob?x7NAvO~LySe&biEJQ`_OVYQ-IDfbSJW9IWbioLuk^Ly?+v1gl+XQvh}E9cwTsAAju zZI?HlkGuL0=%#yre^W1uiO|qcS7zdcE3( zK@@;Fb|`4q6f#B}v6w!yUV5i^F z8mC60qjs*7Ry5^;TrJVwQ&XT%e&Nxp4@mg#mPf0ae|Jy(f7{6zJh0`bO3GLS0@;u; zlBfnjc_M!l#0V2J$`uo*IPMrmDdW|{0ZTk6+bQ9S6@()*0h}Aa8|2(XhB(LH{xBT4 zUKy06&pdM=26nKlPI^@yIh9T$xJ|`dUi-T#&&*U7hC=ex+gm!ZUxde{@Q8-3g|%kf zkrZW_Xb3HQe|{pj`;2{q>X|!eCWus$ga=xI>}KhjT^RJ}I&@{ZcGzjR_L>yE`ie^` zFy7DgigAQ-s(!?2-NP0lVQkakf`YY|Ucrk%r+o-Pj5Q^;?(n#7jYdZDJzNdK!zORo z@s_;w=KU{kr+{o-Rt~_xvq1=M35NkEe0j?&oGJv=f1weWL-pqOlQ5@kqKpnlI0rlX z%X&KnX;4@s#;Fp!4t!GH2vdZ@>(MfKCL2|7{wQHq*uu?x*I=;*sk+hGd z@mCUS6YpbD9M@W*jj6Y#Q}EJ|x(vV;Esj20Ix2recKYe^crP*!WDq^Ose%Q5{Nr~F zI1b-O@Gk9r_Xf-*67;F#r(5}GlcmZ=hoJG4yAZVcSS(~Ai|tIua5<*YnXj>q${&%P ze~NXy*V+6pbGr@ByZQ=KYct>}K9?JLS~VpGk(SFqyQ~K%@J>bRhHPD;&#RWJCL&?? zfoYc{qh9avTj1&SVWLc>(L|bz%?o767YrA=u{ns#Q)x}CA{)t$$oIsl`smJ*L+QXX z+iDF7y|tIpaf69bV8)TxTsFrC?l?9@e~!tqu~M?(!>E|S;ZZtFIabI;lYt7Dzj@}H zC%NKjtD1i)1CILa2+8|X1jW$6E4&KnE09 zIIl3~0f#LUxHrna$-0ph`C1EWAN`5%K}n>5lS|)&LKCUTfXcN|#$3~q*HCFCf7AUO zx0yfM?WoPvp9g7b6!5=MRK-JgmaiaGajd@;U&C%EjiwMgNZ@tISr&w68%?PStRDB7 zDBj5gV4VpoK;fB~?9#{BFv(O5;B~zbAS~>eFQ0C+OGa?#jf~Kgt2JfNXPLX&5?3*I z$r4$tQpkN@vc$=US*#$E!25k%e{FK4NuvlN<_H`luq2#>P+-)kR3D;fRo}awp#u#r z-f&p;Nl1qob`)@PB^lS%2pQA@+HFQf3oZ=*#$xIJT@F5}aGlrn|B8UHB&QNo2`J*L za%S8A9}mN(D~g$@>i?akV{-f_A8slEgALI4+xpn@7ef=f}*A@dRkCNpuEU{b`o5LB=fHz;aFL|I&DMGFWB3NCmQwMbPs zYNV}N3yR93B4`J!r>FR{{ngWXf9AX2efQmWzvo~VsBn=HOgNm$(3}eDx?GYEi;qCimhjwlcWqaYg%2?2s(fFP9|2#0EtMugpl z{Ad*eq;FwDr+wFohHMmX3OMp6od9oV`T&(u+rKd$MP z?+)p`Hm)Hl!=D63qt$Vc1#*UfEZv*B5We*$hXkq9^zQm`I7KRDw3>pz1rrNU@mrjX znn~&*5lC{%Ly zU@NDLoeGO)W#f{tepTt`Du!yl-SnVt+^^cg&zkG)+f31A=HJbIh)MTZKT0smf4yPzn6l9rkBj!0T=vcO1{|9G>ge(>JC(mTC%fk#OpfiirY1jw+5$q zB&N&z|CBbRa}esJS2a{4*sjQNb&K|R_`W%2ig`2a!pZ&3_u3OS9}Oatf0TT2zvg*G zO>%3vdUa*ZugicszB4+uu*k}^6d7sWm$95x2Jc0x`tb1 zG`LX#vXc#6uP*t{=&&v+NSOj38%4oex#C9Cvw*Dr_MtYvtQv51TS5z2f3GWdzg@Z^ zTNBXv$m{p8{K|WRhS}-5 z3APy@Zb_Y6AMVx|H?&r+xg*~tQdOSVbuICxq%`N^+T9l7HLp^gPdV0%ZqK;pbJ<4O z`s!KL#Ma>r^atkqaEJI1f2*z-EqYvEl6bDvLm{aw-acfkc%8cSCGs++>}SP1>uFET zl17C}$mdr>9E~PDkb>!Et@u>BJ_un88P*Vv$t#~1s3ee6Pe*8L+wk#pDC9M}6}e|=Og>k@b2O7pcf<6i`4Q-a(s))293 z_v!YE&gzj5m+W0Me{p|PM}5T7qRF!C5w=0a(0E&O!}!t7=c3q7X1m#ot=q02KJHpQ zu-_T4A>6q$(Gm7pdD4xqsl0U!8D_;ziOqLj2RI4C!31|OE zgU#N>h89s{LfiK*<`oq_mI_YA+YCBoQQhjb?rNdLhgtsV;OtgQ=lZgMt%Adyg+*0~ zZGi=Y7Cu1#7U75dFYuobP*MAi@6pa>`eN=F2wH?g3=jqbCz-hCeLeLBCByyXAQF~9k(F~@p{n2?#Jj20Z@ ziqONLePuwhAPAsiVr2ya10Y#>1~D-m90KZ##Go%ieGw1DMJ*M zAd1QiVt@_?8-PQ)d-ww4IubvMQ~)h+1P%#f6r*at?o3Yugz~{p&)1E}B7+XkV)U?BA|Gs(&)*7k`u5gF_MlM|oL! zc@U|P^!@9X(?1T=MZ+*~B$_l4O3DBfhl2(%kVb<0#C<6Y6`lp}-$aw@{JrL+$k}|1=ba5E?-;%OCAcsQWe{E9+ z19I*^MwQg=zcf`vKz|O3har(LBo0RK{-vvs^j~xikaPV-s(>8qZvv5?Sq||VlG3{W zhNJ^LenV1p~BbN#{GsQXZ&wSa`ycVNzX(0iT^$a z+S(Z6RT&T<16EK3l)#FBvZ~Ux|6OK{M*0vCh8F=Pd3pJB3V#YeRTu$>L!fq;W(bhzJS7{lKrk)w*98Tm-g zRNEbtWpc7CQ7;Mle?NtfZ4EMip|=(0b1QxHXvxoIDKoI?l-+KoNx&jj_;dIgYfJs> zkLyb0rmtq6mw%+#7xd(8_parciC?r@rkxTZd+g{}`#38?S;bB-6J;G8S$#%9$bv21 zrzA!(dfJ~Y)w^2KNOYB&3iM?3n znT%%iogqyf$HZ78ZJ5ry?kv~hE%DcLDSUM*3!0)!FXmP=zfi`4U2>HYMyn<*?c|ut z0%s(K5}N66kCy1+X1{Xd_Y$%W`E2K*i!yn&Tnc@{(Dxw$8P`5hCOb)<3{3re--+vH zO;>#>*ncZs8r)lL=N^4}ye5&G43s}(6Q7_0dJwQ$Nnd_>sV7gOn=9BRMk>@}R;544 zk0)gZ zH~VCWzFG2|gn(|rW9OYD%)52{!#;ZUwQT7Ah;{pH|Ktg~NPRy@&vw-S$E)KSSvty* zU~t$(_5#XE0bzZn{2N4`-{#;^&h}L&syKZQyunNpMkCvD&}Mpsqv?X`$rraPEx)Be zqkl};8_CZlB`|Vedkpa1Mc5R!?|9|V!i@&O_LtcprZa56nC+j z_{3BnXXL8LU8*ZLm9c}AlXsVchutX=sr`FZkvcl<5RUgz^^5nvlwUlsB)&=**p&BT z5r-69)Yix}-Ak0Rnfer{I#}JSGx5O#VSj$jiPGnJ^c^5}rdLIQ0b+YgIx}XY!6As} zAl;1C=^n3bVy_#g<$z!h$7s%RMBu>iANd@CzoJWdIx>pUBO(*D43GjUCEztADt0 z+eFCg(^5_>buvdj3#It7buy<<)4z?i71}bm$^_Dw!a_2eIF3xH zzPji=yMFdQ-If`1C;0J0I3D5-<6!FAYIBy?fIzF|FYMmAmNxFYU*uu)hkq{C z-|LZ3oLze=BdPwvg8EV6=I~iU#eaw!htO4ba(|De8yrhTAN-#>hlu3GLf)>+h>*XI z(V-pPP_5xJuQG0qe{k93MKs*#1NC`Nx7k*MZ)`Kgf@Sw$>U_kehE?qK$Q_kL2D3+4 zG5sJ2SlfN4Z%Betux;-sRKy}L*j0dBT5_S$4PnO0zrZpw!^pZ$NTxD18-L6qL|$e~ zi+-;<|B_Ksq3&iFS$Q`-OVkpxT_PDO+pLgJaW`@b+tAsY*8BYiI1QV7^5zK(BOTA- z#mdy@Vn$rFlzPV2TINukB{sMKb+8Ai7UVnC%5Ea*RX?u$&hX?F6W67+pY_i0~F)6J@$X)Q_R& zDKg)fFY(Wh2x(;*6kD#4;9rKuA!SFi?_e~dbvJ?a?=p^W_|gC2l6;JAn2}p zsQ$XA!To80-9~M%kGhVsvGFGoP9yjUOP;XjGNZF54BL5U<&z`g3NKrpGJmqEmt=DT z*Y;}9icQ`5T7S7R&nIMF2xV^J9QD3Z;&DUA?A^l;8R!7rZ14Nz(~0p%pxC^s38(a# z%}y3?vgYXrqm%|K-ppbJl2UsvqjAx^eCyKgCtQfr)_L0Z7BW!-QG)Mpo(pTa(h+yA zhI?U9hL>OL+%aDh&H05}wGo@zGvcY>s6rJQueYF~bARb0_0ffB&BC_<{w0-#xG(rC z(SVDg2)YA^d{`8hWUwPF)o`OrC9mV{a+{LFnOaPzm2h1317rf8CX0w`YKmmI+SV=@ zHeK)A#%AbjnSVRz&G|joi>1oTW!S||bCGmCsHV}Y=Iy?^1aD+FpW7yP!oAyWycVf_ z9ILc=oPU>C=1!M-`nlZ$+a)Xy1%zu#|KRkF{z?D5nZ;W*As2@1+xODq;m;JxFNH=$ zFRp(43RWm+S|cA3I&f!zj(v^KTvHo7;UZy=P{${4dsE{1FLAXp`b`O186OFTx}K70 zQ%6$Sc+zaycGEOFC2q6%EvUc@YUaI#qLss(4u6`AX5o1;lDTJAJKXxRbF-+%z;7$I z{p#-QOlFZ+Jh~#XEpED${N|Xrhe>dw<#41sUt zNq_H%&PUr!P)bQ?bf{ij$=s*HcYLL7ox&%qdk9tIG%I>gkB#8FUz@f|AG_Vp?5&Bx zWu-2E)W074W&XQT>V5aMXX9nQe3#qtY0u~P!^kefnIgya8p0Xd9-5}RI+@oxysJ)9 zS_Rr|D{eVlsrRlEvfv%AuzKt2df zeRt#OmbY}2N#U&N_nlbYLyfe_u&l8SW%kgu@sp6WG_X}Au4yQ!^{fVw-}Cz{<4Msd zGm|EVwr=ktFTF^RDno<0I`jNOlTcH}3Gb0&hQO(L)8h}cmET|CH0Dl`tqAhtx_=$m z*yC~t4o=p2Jcurd()}7_c7!8n@R|mf;&i)~C-#bv&-5j>+k0zHIm0(uiey7;HIX=m zoJpp85}PgX+$)FWGTdro7ec{xn$%}S<7rKFQ!VC#-9zMrbzSZ%+76&|A3y3@TFFjy zPLsuGGCkEf><-??aXB$qRFve{AAjwx_i?6}Rmbjqp1?+qRNgn`bh0babX{jHjK~|l z#UIE!JYx!D9a#BvZo_0^r#~!S2DTq8WjQicDnu8~ReKgwP`T=1A|O;qc;>|-sKz^a zpm;d5-lQL@OUANGNBRc@kBWwt6i60Iw{$~HT(}$G-wxemq(9EVnN*szkAKVB_@7S& zl#0|xir#X(b|7Bg&&>GaAvq8I6Q0vk$;mACVGilnqh(~?Cb`AF{5o))FV^CZv$-bc!Nsc+IKf3#IW6hNO>;Q&dXT8o z9|4KvMyvtVtmh*v8f@MvQcB}yhcYhlo|12SHTX8j=y*|;1|D&_{Z+%1r_npZjVd8m z&@AH$Plt8@_34H3w3P>M?mISL-fVkfJ%&tlTWje&Q)FJX=z}BYtbe~)!F1usK9c^P zdI5KE0IaUn8MxC}|B%0s*|;FU^rgUa;n;QsH$pdpvF`O`S>UsProiQwoBpOt9<@tS zVqb*epGLzY3rz7|-r`p^)OS5M>bLzU(qH-B9oD9L`I>2S$cLFuFW{g7h;%T((LO0L z+$oc{W49BXe`C(KRDa{CIku+nb-O%B)FbA~jdFpl!yya1r)4e|mQrp+X;&^?w>&dp zE0bl_b-l%nCD+I~U%Hwj`qW_pLPhZuUZ9EZ=$*_K6xttA9@5yRXBn^aDpWO~y04p8 zFL6`4SDWZ;-}PaMdge2QU5JSBv)QlC(iE>uWfFPVRP-6n$A6Bc_q-v(?jBQ&<6CCi zN3VO8`kpNVd$)uD6YeyLou!2Ftim0!Xx%ULA__Kc1W?2Erx`c>tW3cbSVK{<N@S-B_bm@6#B>59Y#9WQeAOe-wjlEvS5Jl`spC#V*#7{!f_ z#NG%kk1_tv^?zkoUk&MBh1zF&xlCEa#xvY#l^w0llP=#>p=V5;J9n2}W;DCFJUiul zue7Cww^#0niglx8EhkC>`?BE8eueYt~-z|EE={Wtrk1V8qo+g_6&P8 zm-8r&LH_^v`LDr$h5-zPfZ}{H-cXzu!~XzhF#`5=mv@;4g$7zmRYF)xmjapwYYkdT zRYF)xD??3GR53D_fguPOx00F$t_6QqT!KSzcXuo9?(XjH?(R@rio@pqKho~~yxe`Y zpH1H6J2Pjl>&$OX67rAoigbboAU&WM$kL9EiJplIAS|OO&BO>`qGv=TCl|H>>e`ur zEJbwffLs73PEIC(puG`*8JuV5VrArF1{VQ@K~~N-CPv0~04ibXe-&{61TBAnHYWPI zmH-)DJ7b^)xQD*3IY1GlZvwP)rUwX`n*$X7b%iZJ0cZ=faR3_7BQh}o3{3Rx0D3?p z6H7#fe>sw{Gz0-S{v&K)Z}nd(2cV5D*c5;YY?2xPwrBvdGy(*J+R>spwYJO5_|$im9r4rl|A0T}>oEdRY!1^6H4WPk=H z_7?x1m9Wz_H_;cgG%^PQ=s4+F+5aPKVk>6i1T>I0vC}sO80wna0{@8vEe-zN2zYq^ z*kDlmCMGTV_49w8>OV<&T@y>Y@6J|00OOzPf0*eHgJ)!8;sj7Ol$yGCV+{h0niBmbOJkOptl6sfiD4o2jdPf z1lb_|>rh$P0SpEpb8}srf5ra^v9kdf1pg2RfI;{VeE~3t{2@*NgXn(~2NQrn{0}h$ z7{2}?766099|9{#{ULv_1?fKo)|2@|U_IGC1lE)LZ~DRrV37YqU(}enfA`j7=$zm94Tf(FQztML8+9YoJU+dhM zvo;MA-+{ze82*14rY0rm@uyN{3w3XuTWE z=Ahed?nSt~Dzr1czbl)(}=!H%2EOg^GN%c0M*fnw{?xfpw~mrlbQ71DSOt8e0#7=iN)g zJvOl&0xo|>B@RCt>Oly++)m#y8$TPpWd>HL;C&`#e7M50_8L|#SgIZ*eS>Z{pCl5l9=1{)Xj1&W zR5yP_JW{N&j-gh$m+HGx7C%2Y*j&XS`&(fO8I6DM0A0jpD11hRrh-ni0O=0)z4$iq zPEQBf250wFjk?g7LPC4H*^r!DTK_(AzlWTWjq>XftkleI9f5V#v|H|8aXOKg*Zy)H z6CB3by_sP_oc%v#`qR*!};T>n|NK>M7TJ_t*gZ?>OO8Kf?-p( zPmqt7OoO%P(+!SYL^6Jo)yS|vr`9fzD3`X1=@N4@&HHDB$|4Qq!~^Ti3l?<`#<8B4 z{ktlM9t02^A_^0~{K|I1yFp}y?dNV8JgG{?U#K=@f~iJz_&M<#$7KvIw(0`=B#(a& zjs@?PU$tML^CQ!+v4MMI%<(b zc|J#(gR_3sFEA3659O5aKHVs`mr0MZ5aSjT`WknK$BdpI?WEW=u}e(0R&LM`Nc(D; zQi=Al68ptL4?VnZBb(7&JT>8)pTe0W1wd-ayBeg5oR3 zClC?8mdb2~URFJ#Re#`F5RgnVk2g#o52z(V$;Z00VvCZJ+D;+0DpVWxk&w1sd&5;h z_j1D)x#383VBHx@xeRG&zOvy#_C50oH}*YHr+(U-*l${clkF=8NVgS!9bA9QIYjK# zg+H3tVYQ#wPl`=5DvBCrfXY{#igs}QhsSRiD_Yh z+my!du+~Zs4Hx0f1G{vF3+ia=-!Kqb7>nn(jFIqg1o;S1I^TH2l-Pe@8cLFx5QSf+ zbC`hr47FBF`ae#x&%F(f3f!^=v89I+$z$mzf(!aKK2I|IIbPy%L)s`nqKX+ktwz!r=Q_Zu_-zhYYpoPZ>i==$!r#SL zC_)hS#oHhODyVr{gyt84|tg|#AMj|$WZwIlh zavd=w<@pZJg1)-2^_(O19>OmXH;{FDfkkIHug-k6)sy+fd+!m*2$YDpyy zHW?}OsIsmVI@T2HHiEl})0ahb@^|vgL-+17Y^bmr(Xm}1eI!l~G^C@IuzDN?rB;V+ zA>DakP;A2uJxgIc`q*(W$`F~Z7kW$BT_7b1V`4q1M*hG}w--PI?OYcrdh_h9kr$5e zoAr7MK0JR9c`j)%Mr;)ma!|n%X|ymAU zku9hQ&_>+^J)sub-RVd3!dygz{)%UkZzN*+5&fgBe$ToN2FjN(-oZtz39jWG5E%&~ zk6Yeyyx(!IMx|(Bf?YhHs00R)srt-B}L}v&-?Z@sEd1r z?{Pt)jjgTE8H!_YLOmW@VbkXmt?q{+q^&o9m-D6%JbE&yB$#$YDh3F>wwM&^G1^3P zuXlg@RO6qmik0Y{zR$IloEB9Df}$jYcpDn~Hy z@jFHS@RA4GI=)Tdl}pjy;JLN-kYOs5MW!{AlzwL#G1qi0$mfo-@IxEJ&RviB9g$94cE`tCN)+h3weTJ<|ydcYgl6ZGr_*| z_qqHZ(2K<14GMhFR^t*MxK@~|v3?nReIu*9uC~}ttYWquM(k-U#KH9C61C=|ddP}( z>_(C0hkCFpDrfS50-c*o7uwUZK+POd!xU_Pn`vXV{vv*#52>!eXiBm=a-$6?AF6Cx zqPk0NJw> z0O$ehsi1>{ICx>8POqzhVpU#?rb8C!Qqm zG53d>^N;#E1XP|u%^8;~Goj)3G;lT#zJBndyW(pbE)C2{eU`OLL(E7I)8ifK`|n@b zk)^mN3)xg&E`vWj!G&J-@D%EkqvF#DT7klE7RU6GaG5GLCmwTx~b8H?7V!jXER@I&=fgXTvJpy6vh zD8fl#*d@Oy>^#0kZ3q6O?mf(#5_7opCDb>p>oJXkV;w4(<#ENsscKU274U=^6bA{x=o!H55g;GmF~`35^feuSU!b$c z?j}%{C_RaGFr788<#S1(u)+rYPFu$p+Yq8}6(8Y_HnRJ6{pU*~7&F{ZGy;$SXszxlvbO4v92yU%u^-*=GS85Dd` ztI0-4k$FG?+!YhpGP9=1n3)=V9M|P9t|2T!RK7I8Rak3XBUNd({82?+Y2IFDq8Kr^ z4O#T=Q>9UhspUdJ_2iI@m)1RGxMY=VPfClzq$@v7wxitTff1_K2r95M8JXCo5){vyBu6j_^rP zZ%U?;V_6i9x;6A=D`cvBZhouX8$3N4XQOdS6(V1#eU;rCBNpdrj=?h znJLuaEb$5PsMV+LYDviWp`eM0rW716p!l#brf6&h;_8vG=ew7GCoV}mNo!h@(TviS zNJ*tEmfvpBwp-WN7|$P!Za%wxLgH^781}oE-tAtjkCNsz?o()g*Q0PPV0el(a-P+9MMpvy z2G0TFiWyBZ5BoSl86&&pZb8}{4>7sog!nn=Dp{B^Fj40d|KdzSaN;e0%!ck17bl~BU+a;Sliw0p=Ywr9 zPK;vA`h4f%yhZWaf=iC-C%~=B1Kqu8>WhWRzgA4uoTlFRN;;y(k;fysvdkAqB}Qeg zOR|qZvQp6+$i~on%u@$ou~Z=3=U;A07=S_Z804?OC(h5pnw@L z@xF?G0o(g7rx0c)tv@S;orjSf!qxRa!L~9>&sq`AwnJ|xI-%)~4|eg2LVdrP&=?fC z)_fmXrF8_WJDJa}1r?*dy2@0zXkUqZ$}48kT}-EkT1xTiMVC$1SE>Ag%xc~>T`3L9 zGuFZH7rO^=HPwQzS5RopF>nd7NipOnEW(+8VyOoj6YkMZy|Q~Z^D<~sU|JYoM{A}0TWXMLcSRcRfB+v_a)nl}O zE$IEtG=9?m^}@i#3~KQ+P!^gW8)h$nT_f|ldYH=C$7^L?vy+%QB)4d}l*w>p9|BVN@3n=}lJLmo0p&^-0~6 zj-RR!ap6@scGy-yc{O!day76`74xuvFRl*hhcLQPGw%};8G`^*D-Yr!o@SL=-U-*f z_aiqmI`KsIx!EKu%LoUYO3_1Fw$s z51zu+k;fl+`l7C=N2q*nX5U3t^}-T~RcP`W3?h1yc)Z|GDRjbv1h_WEFGAFpnIhvo~?r(?JO+?fcu zI`4%)e|8Y*P;-D9jYELJ98C4>c-H;U9&YrpFsrCB?0)_y+U$#_>8q=-Q*WFX3~1uK z%I|*ED>(Qrm_C#Jri55+h1~8a4Wft!HtZYau+ffTO#}L{1iq+Bjd$odFJIMo@0 zr<$-B-5NG^_6@`SFkXCr8kUFsQukd2aaXdtfV1}Jx>Ot#1;Meb#*Y{8TE~uajH1J7 zv8{5bN3!5QgbQh*U8#1>Xpcz+(d5&~yvlu@T1q^{5cqMKYpD{G3&~>-(+`ijhG#8# z2{9kI)qw5?`puNwr-6MbfwfHlE1QIxmmhaz>c=hn^wyI z0LYUpX?{5FJOC3^EH7M1seXs8sbUWw%CNVAc}X6PmHnf!JMJMYW)BPQq<8Dq2Yc-% z@QX|9VWYDBECeB*zb?(5Pe-HTc9b3E%I=Xw4t{r=pwi$52|?*`;8@`bwE!xIe7|mb zjO$OAgnN-11#!85cfzqdZEC9x6`TobVha@6Zfy?YCZ9MXJ+TCtX}9!2E}Zv`v^U@|G`RwbF$46HR$WtusBBx#3)SPo zJp7v{#Nc!Y7(vZ_?@2s6@`trZEcV=FvFy+Cm>Lufs@@4DpUk`p~T%%h4tTgLmCrf;o~IDLp`W>qB(C#$@q1@RtM$&P-^ z5X0UGH|9mq1GTd;JKaGjw-9hFwu;+H?@qsu?=`cp0(7=bok$)kck$x_#uNACZ0XMz zPsKe|Pqj6FbusDjJg7P&ePIgh%0$}b_tT|j zRyRFzyW%9LBr*KT!@(&Lnx&yP57P~mOb-dO>|Fxz{i@-*YY>I9f1i1n>2 zdLc9xL~MRf3EO=a>>;ifN6m#q=MPBfE7AUqJmVyPOslM@b+RF~W!mL7a0Z{q8A`u{ z2%s!UlbLDjaB2nO%5%XY9!s`Mmaj^F^PHpd3x~tLxty+h*tS9uC^AWT#yoL;>8h>hGp*xAn#D;nl^_E*-&cFzzO`XCfivGg)=AWG4UNF!m z)&KE-um#eq1H;qc)h=boEv-tw+M6?9y(l~ihX%#U^NU+sa!8PkSU`MRq}S;-G$dBL zJZOmT-KBwCLVL<_hj!ohYfKUElDF|z7OPJWBlXH_hK&dM+fZad&V$ZM81@}sczhv` z-gM#sO-#v^N^2)gfwv`YnNJuf@|k&cuZKF>0wV~aU3b6 z%KtK1wAZ`X`875|wy1)K`0<5oPCXYr``O1I4gJ=ybAA{dc{k$71wvc)#bQAFd?mDh zov!#UG%C@3gYURmaUXFm?A6-z>=B#xmwsVQs&p83il_kg-DA^DnmreQvzSXBW{x#$ z)6PV9SF#%^jgc*6Dd{`hS|zX1)q?#+Y=cn9isy1mLP@4%@k8;?>;t|~I0X0RJinmB z$(*Eq>9NgTnD^K1@{Jey61>KhI4NO&boh``PsSS^qpDk$a~XW0Yjx8J!^VrdMkUWZ zKQdFHE*DQ%)*SuDSJ19rQ=v{`6hE140NuCGPTPLoQ)my8Jbd$Nn7_+S&}spWbL3e3 z;)-OP`lLlha2u*X^=2pJbaC1=iV=qN@n&E~{jF1rEXBRrfi|~@8G2dh6unA+A+|GS zogh=^w}yG!H5by@L=?Wl&r;g{_2`!PRh?KN`>g3^4gZH(Tp2SiD5EVl{Ygl*z#*N` zaWk`!H5$(sfi%}B({aEhhRCJ)`h6v(bKn&xR{u!%$<2;I_Bv;>@7#vPYD4MwC4RGw z_U#B_`Fk`Zp5|3P`wL^u7vAK5tfJdHXkE?LL~m5j4ymP{A};(d=uj77;Zk_oXhgNqJV zLL3#z6Sn!G);+_=`{V~R?$142nMC@*emZND7-WkCA|orZe3)wjF@{usWyHn`g1Nyc zjMdvlk-l^zl=oYG5|_eBfq|2sI78Iu)bh{@whte3y-f!xBkFA61oedtE#sdoSsb`Y+fDh zoy$}6Ft)f%sl{$eM}D+%pI0mw-MGJhdHu5X!DwrsY;$;`j62|eo#w!U+-1zNhnAL# zD{s|kDmNp&JA|w%;zG=D+c++Aw5YQeZ;+Rz%PHZ2wI4^7{M!(1KJn6uc?_`xp+e|n z=C&zk(UqjxlVItxu(>PSCz2{nTu$f5msG!sOx3H3FHxZ+?gb5&fVF-sWyAK*Wnz&B zuzA%DX}(I^hg0i+v_l8Sx=VJeoB9B@r50niR_31HWW!%62|^dsmG>q-BU!-Lco*e6 z_E`fJrZ_MMp0Cd+*HNnE^I>FOF*B^U4ey1aS+=H{;;%ioSd~TFpGBT-g9<0$eHsUV z(*{N(#7L+!uj6iM#lfc%VSx_Sd&TrTXupaSj^bXb#9q~Zq>~TFbs!)0s*z`~sk7cQ zD+WsAjU27@FcO~qzBDLzc(ka~I^w;gQu?mzKvMalZ$DynBZx zJ@tGqlE6g?DT2ZND+yTU?726tvZp>AA7b8fep8_i7HM4&Kf~BqFAKEwkcr{93pj5F zs5={GM|2W@x-5&_lTsy~NAeZ9TT-$x(WsX=MV|sErrWY6FIcYYNhQ2csIL2f;DWux z8$h`2rn zb_>!4(6v7McniwWH@5O+zIxQ4h!J4F4vR*~sdh*u6mV4$@GCFlNk<@$o|>p%ClB9Z zQIm9SyoQ+pnAmm)TQ#Yb7dPV`O_2PD`X#czrPhW!)+9yzZqdfsy;%X)bTsL4Lla(n zZHG>Od(`d&)~5E*dMYup20l^_Fb8z^tG(4)E=J~_dJ`(KkWoyL)+yED^ZD)(Ar2x7 zX%MqPe`R?)Keev95p^Aav2JfOwk0O;82jYP5iRmuK7vvCI&rqeORR`4xJzj~IEP}} zo|x9{->JQ+Bq~&^!5H8wT3|wlC@@&PSPlVrXA= z>i>p25h-#}zRgj7i?t)3IQ3{Bb;}fK+H@ z^*&a5($YT+&jBj2s>QG?3=boe-BCFCnati1uDc3cB zn#UYk(sC7AodsomE3|-(`_K}#Z$lTh-GvBk4o&1MP{I_@mLEx7_qgy5INL!Et-wwZ z1*Q2gcb%E|pq8uap_G?!!-_2hwM*xZnN>7Ec%dxQxjCG;2I9W#+$897XpU!SNR=~m zKcB*oHFYk~g#Z3X{OnoNCFA72noP2Pe%VSiZKi~Q*_agnBFZyViA=3l*G_r3PmIQ$ z_TZgM!R-uUM>OXgUJst|u-f1Mjq zy+|HH`K8*QFVKGcarR65s*)QaaVH@$j$iS`vn27;2~34YDByS9T!tEUj-PLTN0@Ef z3A&w2_&Y?Kda9<%$nga#WaZxT@;>Ix0VyRYO13Z7yHcS^d%W?ed2O%Zs(>B70h`??!{vflYUTonER^7~9ccZrg{QN#y9X~8F-5tZ<&c-JL=7nlj>1BclpRNm|A#Nc z|KH&Mi3a-SKwTRWN%_>3N|>G`oacs23b)? zOhrhSa>E8|3t3S{Ohrg5Lrqgmmyopw8n*((1||Z3lzZRDounB1(h*rMC%Z7iU`8rq zEg9MOZOmL4W?>8wO|pwnvPMY4iL6=5+7Ys4&l*bEvK-2;?y2*f(|OMQ=kxq^zwYb$ z=X!sa&+l`6zds2vV>5Xaj_d;Jkx5i}m;y{4&_I8H2_5fDR?Y~1fVfgcaTWe zz+wo185xTQsR0TAia-ENzjgQkrl1c<@dI%R5Eu-=;jvV}1$4ucAd24%T_CxV0kuEE zIA5{{YB2(!t0DUf30ar2w@@=K6NI+5dTj)Ef!2m`5AEX8-qJI#A{yTpW zLIqH?_>1VsLe=O#zeTaW6beY9e!tiB?fuEFc>2CU z&>zG?Mu*AR(_tPdRS|_W-Q3<^nPhx~((=Q)8p12nPAe?EIn6UjseWY+imY>ghjV-2 zA8{owUo2L{xvESHRH1^}0?ib|N4VSA`DAj^&5o{Om+T!6wP&?H=OkuE&y9-CdR5D< zj-C8G<=4PeVz>ZPyDzYO<8a~Y;e!pD=e6$XQPx!4$@j?+hpcCYNucHYR6^(OM}2ZG zc{Hz`-#+~_@0U@5=ac=O6YOb!$e1iN<51d}eHN&Uzw1ml1a{X`bkzEeOzj|x_ykj; z#OPobqt3=oO5r_1lbf5$Qn$Np(Jd|Ju`g{gm4f?zgsVOE%beN&excBpkiBFnN2t$D-1 z(irnZKWCJNdWQ>^jz&O#7E%k2a5pCqO(K2drW-lSW}T}(<6CDYgIunHml4;BuUtm9+P-AH$ng2IbNjL3Q*!>vd7olF z+bQO02hut(bQ38mwsM*Rx-*@7oWNOuZiG>@g<}O<;sI@1>r>s8@F#^Mg*)P!iMP_s zKSvt6Tp?*(&)m>|u+^J4F(XX2|1L4&bvSuo&$fAr7M$_ZqW2i+Bqu|4A<~)$pO84> zi|l;TSa~0vpec&KvM4 zE)H-jb&u13FsqusEtrIhnBOcIKO3v=*mZ-u^g;TI4P{7+4@`VDEPnL)D3?yI{QC81 zlK~@Jl;}YJC6_O?_C|8K2W4(Sd+g6wZ|BapZ|GOoF}{4eAQ_^f;0dAY(X=Bjd140h|Sc_(rez-(5Xm^oVwO4 zB5Vm)QuF1cd=K*Yg1HR$DRzYtO>K-j(_i^!jFg~fFcNL*RpX2$KRcc&G)`mtT|e84 zp``xFCl@w9A%NramL<%xPG`PimXi8f>~X@!AU{z}_QRIp6}r-@Oi@VPulH%$7X$?P z)1_d4*4Kptzp~hz>>N~(G^q|A2ueQIZjmXfpU&`DhQ-Lt+4A@D)ho|x$`_RpJuQgw z>{SUavCQeWmo#;1-$QjpyEjI*RI7bPt6;i!>LhQ?-7Lw|krIt?mMOf2md*8c=85S- zPW!0+oSl$gw|;kE;8?kIA6YgiXzV-!cWv%}(fas@puY3Ct*c3kl6hin84#LzF*Wht zfK9*o)@7}s)CT@9H7gf)+4vtibjR1ktucw0Cs&#l4g9vlav{@G?!0-v!X3jh1CXUZ z^JpAU_&|0@ye{=l2(;?aN*t%AVNQ3`anXxX6JlE1qYkIjxw4hAnH=j$iRwl9;JjCV z+AZF#Pq!Oa*9pDdA*lf!8bj(9CuiBt=tQf*?6gY@En}6>vb=suCHjW2@sE-wr*&+3 z_-21OR*VqkJMU`{P0GBNes`L}$vnxDx4zn^=OE@9bYs|6b-ibUQOrVk<}n14+Kkw^ve3y@`5Hyj%!2eyEFJVikB|$_%-7iBw5!=tx_n58Aa`%N}v~l zNH8a<)J?DS%XjbVoLqHKW4+gTaRYIGfjwO$D$+=w zrJBuYNYxy@lsI#{;dqDBeo3PYXi$ebdZdZdJi?gF`qG$zBhM1*I=iq2vIVZ-yQJ)T zFq7|$95zqvTWQL_Lhyu;?i71>hObDJJfKPiTg-zeEUbgv&FvJ=m5DqJfS_WkDwGg2 zzdoLk32BgC3XGM2Mpzqv#pI(S6Bw^kFTcOpQ+!eG6RJESS*S&SU*IMBtIFl8(?rUo z0=q{z3m$%-gf-dY6tR0uNva-_ye{;tkIR3Fc}SvURsG9j8UnDs3K08nvD|F>6~2@Nk6SE86{$ z=%$_7ww1_md2N<8hQ@%Ka8x`8xNfDAhL2OL_X2g z>D0tiaf@)l=DQI^C(yPX_)y;W+bBRyKZJJ=4;J0-$sf*t7Q#u3p1q6Z-0EF^A!m7w zxNUQK7-ogMj~(_oou@ipkS%CzI0~mlnH;WFT;}HYELmUV2x+rT-6=SiuSAe|UTOMv zSc7)kLko8tN;~&UrKB07RlGm^WP;8IUHN1|0_`+k(w=O~dopBZl=QVoY_7*Fb;8f#8o!hwA>NN2fh7qik+truaZ>%9yRT9^gu@?y4|UWOUYv*De&4?!deiG|BWl|v zn&B(Z6smVfEiy;Nnk3N6>g(xMcJZ~J`&Ik$9$cofGUVLd7Vfxi9Uk7>Kii?U#F;pP z(-`d>HgRlRs^yadZR;?OqI*W7P~P@KfxO_JU3pvsarNmaJA}Erhe@;J9>)#8*wnVG zt517>?jWr??}xv*aqTVXC}Jzxzp%H-sq)G=Cbp4zNo(J?yztUZ(`ZaQKWwddqCLKz zWfQ367$AEA~AD;Olo^%b8rWho4Y+gYzj4sQCm;by;oJL|3BQ6XLL?Jz{P-{8$!d#SN($Yq*k zoLdEh$!NHBDomX^XL|30RW0uDNNTJ@i}Pmnm5vxfR6WaeyxG~e=lQ+f9T%^;pG$nZ zN33KdllWVOkQ~-?ea|YbCSRtMwvX{qvz~d1;pdxPc1G>qV4ZV6SL;=)TPl;c6UG03 zNPI(7pIT;&qFB7gGzEotz=M}D)G@;cvUVLpBO*E2(WTm)ZT66uDYNJ{-nHCPX*TVt zAue3-hhb7&?}=AkgSF20owaPd8e^(IS>vjoC3F=8${X8Dq>?(roR@|!+5FVsAZ3+g z^eVj_Oa{AdEdlxZw7=tSIH(JC~RmAc!;fQq58pC0JHpf0an$R5Zh^?_*% z^RV$TvdWpM)?lZzNXto(fjMeKXfjT6$k&PJly0m6f8yO&91HzFK9K(k{6`322_S|- zB@;0ePsqOjJ=nUUmuA%lg$7PSS42-lm+{pGY6eb1S42-lw_(->_X2-aHMHJJzi0ku zJO44JY67-(*7UIdchcXJ|G}Jp7`i!*V0VBX2O9?mCv+n8`#--7{%b8MTcDj8*cN&o zc=-S(j*ccCsL+D}C0>9RCje|~266|0+@ZegY_@jJ(7ynnTk-*5Zs&;lyPMqH0Cwmw z#Kh)z{GTCS0K2V=jp={y23y$v3G)Hi&Fml$6URRxegL~Y$Poy#b^a3*0I-|bK%-73 z(0%!LR1m=K;9>{u#Xq+Ef5JQ*&;%xqAX^B?{I4kIzoP#&nG2fP>30ez>pwxLzdgjo z=}&+Mzz(#tu`&4*f!1T`VQ&fgQwtB@Kg7Xye++q`wKzdcoGgF;M4--gwxEAAFI3jq z&F&9FHJvRTL4VDF%9w*){|X5FmVwy)S(z7_)Cpt*{?8~MG&Ka|^rs3wXl{^$3-rkmO)bpB1yOO4&s(FAA>a{iCAAP99d zg@*sLHGSX;F zLZu*oA=K(mv7DSxE8D*iYGwBqLapro!rzDJFN9h-{)JF0r@s(t<@^^ytz7;>sFmwq z2(@zigU}ZiyZc`Vwet82|JBm}Yy~G5&+q>KCB((|uMRtdA!h&F>;L`om5{J=_hRJ( zuyR4~H(r0}#la^i@ZmqFY1x9IkB;11=wr>nA;A65%NOY42)%Zk|M9MaUhDtE=3wYM z00eRe0Z|ub?SR5TR;l$Nr9M*m<7J3Uuo4yxamuxwQ*CI0^YO5dqVEb0Oe-}ycjXzl zm^+9&6mAbBP7$r%Y9rDo{tj_zGfz9N#yc6FO(cIhXBkQ!+xAq~fd^PE_0t!Jys#>h zD?_D8?+bdfPWujWRTtU9Ih2O+g#K{D zPD()dF&5}!6^h)*y!6Yi)#c?~KJur;CnK0!;ETurE{W{E=05}-N_5$?E55|g%A^J+{Wyu z%m@cgZm}BKt6*XBrBi0#>1z3m zy$ZWeR1!;ji_-N9)(<$Y>_|apuO1@f0kdHf_PUahc$eENRZMAOk-}!C7@JN^N}p|z zXBK>T`VBr4?YA`}rM%L|QETm}UX5lf5(-f8i9_~j_= z_!*Na6_7~ZOyUKp!XV&&^UVLKW9{7JR%3)U)v1tsaQE2W%ejxc+GYW>EkHPmFewny z_}OK-+j@jEyc$F6ZsP;SOWT0Y=VV?6XFfXJXkS7;*m!m=X5UA!o+f{7;wgWMS*Kb= z4XlME({SooCws&JOp^8z1rZ{OjukcR`GKm2Gv0Da3rOR~pH!^L6k~;p-7j{_0-4H{ zTuy0Bev;bIK3Y-_(L98_ek{`|6|<$(O8qG2Ualy<=V16{cJS_$$CQw{%L)khbT#iW z(qs1W{WgM`Hr|$ECs@jW!3 zl4&TX(rHKz&iYGAE$%)lo`}`aIJ}^EoHEA2D9Q6bUN-_y=A{#s&y;_6^}curNZuiw zmp7UH_BwnZoLKWRf9P8Unxg(o1rBb*bb;oe?DT`%@vluri~P(!1Jxyq^?u}I_vbve zwTo`ev9u}9^rhUpvU&0f-VTW{3Z}mD9;-1!oQ?uNh0HUy<`B}j9N|PF9{2I-2s5pb zh6aI0{K-vk4T@B8bj5%3vOu?C)+QLNU_>FvFyjWzQgCT;=nMNyRJIoc;IY!v%ZjrV z>$%b(m9#w#`Zb~`#lz)8HoMnRm4qfVY)hR-(;`>gIlZI= zf^bXjH>JbAA3K%4XU)G-l!zewzalque>}5%kObxx?unBOulO9T1ibosa&6E21spZ_ zqXoTuNXuQ(j{22^=Q7-7*4jG`EbSDzLFC4 z9|Y@q`C}XEC7n7@1%&ywUuScfrv~U6OxJXjU7&#wYOR06m(v&#tC5S`VsxzastHil zQ0(*oD^akMhTHBBH~h@+k`2m&>&333z9KXld9s)vdaJu!lf$mYihrASQhR)GX?U{t zvb@V9_|nADDu(*#yjF%6kfb(j`F%YWfUSxXl53RDe&MiLS)+K+mBN3D7SP(3-Ep90 zmtZmw137=|gy}{`wyc`12+OuUgZsP- z?}1Dg&#JSm)x&$UiqGGJf=kymBD$P zC)S^A7nCDVa~5*+U1l2up6PT8wKRPLztRG_QZ|2NZrpR`hIO?1%8TyuWB-0kY4Y(mO0WY-;hKQ}y1|g!ZylZ;H&!E*FuE29*X@WuZUw|;QoZES z+KzuR&J1AYU4|bUq=#yZSN0ip`w+##iwf^E1AjLVIv*-BH#j3!}>nUa&=Pph6< z@|9y;yl`e&-Lgj^*KjPw$eNPCwv$d{c(HjftBwgZ&hEBt`+mzp$}wWK_c>w#A{{X+ z`w=q_54SCdEG>QTyT1c9_N{6Lzn08aKw}#tr z!TD6gPonR7wTa;S_3chGRnEyYj1x*ncyS9#I!cpIGg5^3L}qO^{rI1zsXq>@?Y^|= zsi{Y5PxBX3bl+|>>sOc4i)dXCo)@r_R^T^e?JNjA4w+w-j&m@OqBwxfrK^8n)|V=7 zb~?T@DS6G*!J>H~yW~a7IKfU8K$q-pM^ z%yLYA?1m~NDP{JWJ)&?sW|>HdQC1!Qk41g;kZSC}l~C8&q1`=@Ikm!T(2F@OH&pMO zGGBIuI32q*%(@`L$5TZ?8brDW>+J~p!dh18(lgwT)zz<*m-l%|Wc7a;M~6iQ5KnC1 zMDdT*W5s0r({7;n{cI zuAsZh8rF_6_cgKyCL@0bUEFPL^fkGX9W+3z81@~)T_8L6ghXQqqcz8 z2zrU;MB(giFOuQP)O1Z$jP4iJnU=FcsYj{b*W=|aR_L3&t1t6?ZBowJDYV>A;7v~w zhTjUgK5juMGa@Akn<_O^XDfb#MU+*7ANF1{F#BUbH_qiCV~l?jd_G$g-UiPIpQbGi z!3UoFN_W?+TF`uCs(_RI^0pox?t56G?I`E^h><#yuFb@*;;WDcVh!@M{I9-6y{qeFh=F?GAO zzR+{z&yM8W`TT;L?Rf0KPRh&I3SMzXxmr}S?-zRXM2uTsP+ETL=Af~InO9+KRXmAT z{MLA35#X7VNier1WfQ84J1dl3$q@`&=yVgw zuOO5K52{?(U#1UH_+@x211)fjtBLXFFJCzy9xf$WkXBdB#EIbMuMU)nyc6CoR(vns zfxbd8PXzBYCLZ(^VX3V7+aYXN35n1^pH(Nmq|UnHem}vK4!TS`43LF4S6dqbQoSi? z4hX6wJzIYl6I8;0?d7npa_lnb^1D0`n#oOByJsFvHi1vJ;9e(eYt1iko8O5{UA~l| zm!3wsp^5ILLmbXJ416F$+Qd&&TiSd%wkIGhv~d*OE;GD$7_IzeHF@6VTwW08e1x^u z3r5OM{6%7us{V;FR^xM^_;M)OnyfCd4AHu*G;x1ouG|^Ev7JnxC_mx@8Tz6t)jsFF zH{aPxXaCxHd9?0b$l@-m(rJ~a1)RD0i*F!x4qbO(^9u2f8(x+no+Z3c@NMR5S?Y|v zpWC{#MO|lr1!wGI->NEdsNgPdZ)*Ja!x;7+hY8``qED*lod?M^WKiXuYd4>B#e9n|#56IBeN^IF4geEO`4PPk=R?A2cx3=rRZ z{f-+3_ft|GCLwmuT0ec&FL5c_U`F6vGcG z*I9JThbZSVlFhw+j!T10R7^Z$0{`{TT^92Ujzt+J#Oo}jcVda5ys}E`frCT|N_UYq z6kDt7SE8te6XA16x=(xB3P<;tDL4<3!^d)I4KnHvX}Um5fnVVK=khy&{SXm6FZI;D zp^tff%K8zPX`MoCc=+|bv^2qI*N%VL@<&5u2oIsM^aQF51u>4EnIu4MhSOQBc|=sK zq8yFu^257vKKs4fmwm5Bof<8Vvr&2I;9W66mGokMZ(e0t2tE_k=wvOUp3w%YPurGx zGyIGhn%2J3=ph1`#hH&PDUq3FrHa`^3NM{{yzF-@gaJ`0S412nTC$>H!#aNx>m<0N z0q)p-3MeBKuHL&`ZogzhA)rJtRgrBJyt4N!Uu2XH!QIQKhU;^x9p<@^g`YadV>ft6 zr9`bd;A?DX#dd8BYl&DyV4-|Bz4Oy^vJ&0cm`8}LVYPfVC<8Y4ZXDfdv@BVpBBn! zHn~X|*h->|l>NbEnCq*dD^323mSP$|Hn^=hM|UTBH)(Ba^V~ysMofQS#mh;ff+LD1 zH{SY5^|N5MO80y*aqpZM*%v0S(P_%dvc%)a$R)M{ZH~ZP-%z_X;Oa=_b7AaGNjYIp zlN7K5-@V^@6ulLT-4h$f)v}ON`=DSios{=MZP2ZJquWd<#@v5{t3&*IQ99yQFwv@W z5HS|{1jhmGHFCw^@Irq9T)>K7!WO~)Jr94QHjZx!d7a9d?mUI;W0jijh=xr7%j%jl z@XdZ{;%?06OccZdpO{jqCpwWy?;+tx@JTp+*lUc(r^?j4w2nI@hux`onmiPAS@oVC zbCbi4P(!ykyo)V?%7Mrhm)G5TKnG21%wF!Y=6J#}*sF^tLIi(<L~aV}7<@y_XSK!mTA zO@*gjb8`z-4)4&4dVW>Sr>+ivy%%-rxROD5m5O0oom6I8R$99D1JY*e-C^w}fE+_0 z-s3xGzp9S$g3NzrZM0(f66?BzUfZz zy`ye(-uXz4ZIQne;7W-W5{3~GLAPg$-J2(8Z7K_FPN08euJ4tgJ4nxmS1(r)H~gY< zTZMbLD!%0&*P?XY2w>}$QH_2V;QHx_FM3^0k01$;Le<#8R;g(5HKP#|{G=pac9~rn zdNQjB@;k*89?q%tbKu)K!(#?KKR$4=ZSr=3=Xo^dUIV&A@Br$)=kLq3eh|48pEd9-1iEnT*0PV_7=1 zc(LVF@>WF+XR$ZwEb}V>PfWL?%|w5>e5@t>7mAegoYa(;X^MFHerqo~69DG5AS%xR zak77Grxc91yw4>{531`gzo0C^A}>3)@U?b^(`W@bVlpA=tP+~YA0*wlE~7Gsc&PU! zY4a>4TC>wg8JQ1dijKo)y^YO>7A;Cwrj_K;Uj+wTO%c2By(!Vp6c(B{KU)Vo#%FY4 z3E!iqLPCqXGuu?oFVW1JFBU^3B zS%xK~p^pZb((hg@Zc@Q%i6!ekDX;B}>X(m>|AJHpetigkG*MQ>Udog`^Uj(|Kz1KQ zyLoA^9&tz1v3^N}Tv)l0aK%?Gz2V7@6(?%SVwF?q%o6OG?@j1CeENpWVqAvJ=RAL0 zNCc)R8^6zZf<#J{2G73vQ|z^a{}^?3kH2qs1bOrYW$=+i}Ge_*Na81Y;xx$}znm=@ODWeB73#62S)i{zsb(+E- z^3NYJ$)?^goKUD4jBKZVMs!EzrN+SOAX-SZuX}*yGcq%AN!fpaB$t*^Li&F#I`ER+ zQn<-J#Cd`oV4g~2S&T>^QH}MJRM@Z6XteViaY!%|m)YJ1#Qj_5NkA2nn~SMV_m3j0 zPsSW2x5$*yXp&Xu8$=7RG9o{_dskM@xKh$ccDDGJbh?tSmAM|yzu+3{2baFA)o_j* z#Tb5X|NRKzAtLdzn94~uZDoI(9BnX|<*H@-=^fbX>=eaEuKCkB>vs(bLod++$Kfvx zGuA#pDVcR8%wt%79f1PF_Y+^zq5{T`X;f}5vAu>hC>&2v_V8zDt0?7*UM0?4;t1pv zo>=+n(}=X3IkLA}&t&8D0FMVd)DSU=vW4N9kl{&e_K@-~E_Sc+ul#>$#p#IWzrfY@ zWyb`G?@Mayn52&#VbqSMb9+@1EzEh73yUEa?c;b3L_UxN0dde36%LhIT7qSmR7lc@ z1wT=e*OFO>1)#7V^Z>U&NWWBXpe$DYh;6oSm}*f|z+)bI#$qmC(DDmkw6u02K=vvQv~dFjyg9-FIOb}*kD?Lx{0vLFCbu8>gc?K z*1?NK(?iraTvte*o#n{@%W1NS@u4?KS+A*xJIlo?)6-W7Y=(>Kv0oA!%p|ET5X$zk z@yFONDgoHxwB3MD;-HRPEs@oH2BdO!X<|dcC8My-cbq46>>E%!SzqK|vgF?I%nR0bz)&2|QV9 zJZsou_>q&?zJ^l08;3UK_I@z?8k4-;=IDOduxu=KIS=@Z~1UJ#7^G3(eHh zp#?YoXPAL`iVBx_Q>E#DlgT(A2uH`z(%kMIZcD7_Q~b?OCXY8*V9}kyxl@X1!sTVw zF?3twiD>@m>cKGw{=D2q0yKgSiVarMh0j3~B+8^yTP0sPv@m{}QIA9i$GFL~G~?@N z9NCN%E3eWRrw%jhVQNrdI6hQy_6b1xf~S0Cevm|jrE_LAd_;MFm$$GU>PY4k{`&G0 z?OQE=hoxUp4wIoSF1+*JRHZKwzY!@roYi^=6x^^B-%>W8?s{WPipr$L7fy(=5v?P2 z71JKhe$s9Xgg5=5QEz5Sf#oQiP@e34ynqL&&`I5#V{mVs;0wqoZ?mNmZT6rOSbbY= zldh1K03QM3+OI`_Y1~{|yjw0u6D_#B-6U)>+oPFb(odH9W=q`XNLzF8h56$k%+ZJQ zzN`rnD~r=DQlik@`C=NYP{&Z{Hm*R%3$nGk@s=8 zNTeI@$kWemhV}Fy^M})IJ5GeEiszorzxKE0tu@*Z0vj=Z!bj(`i%^j=J9_H(9SY5k zsG`Ekd}%I0L&IDwy)ePqBaMUt#hgb0mYtGo%XH6*2}j8nw0oSnZ&3~p#=rP@@~`B< zq&q8__39)saW?UD@Fu;zE6ByamgKe<&ak4BjipHJ!<1Bir=iN@uA+fW4#y@)jbPH{dBr z0Q`dfW-1!{aHowjZ>i2VXEyn?n+4xUbPrCVW1Tm5L z#bGw3z?J1yJ&n~{S>4PkaH+Xo|B7<+h@VD(f*W3VdDtQ-VMLx;{Jx~ju7YP=pT2{f ze)LX*anq=ust}g>Taw$_uUzMtp?pYN@)jH)&L<;uB6AY7w7* zt7G9DU!72tyw4ldw|(~J#|HD0WL4L&J^Vacqr%G5$Fw`H1*t>~^>R^7pIJ3?37B;K z7RrT^r|d!d=m}dE!^t;kdxbPAJ4)<1Z=7n)?YJDj%$&rUTAj{F@T1_U`gre2>5I zy5M>13n_$OGGF?t`V8qOvks@{)6t=W`w~mK4C2P)7!lKdQbQ4(n!=lO>E~TO#|;1| zCJ-ozoJs1#HPjlaRB5`BNqB)r2#-QQ3>L5Hw5d-BX4Vl`VY#AX7?b9zk>)~~a zS0ldV9mWjk)!CZo`l)h;#XPyG2ghC?L^>4Ahtyrb1nX1p(^0#@sxSr4L7;QR@S?f^o8S`l4w`C&+SujfskVx8cxHH z3N*oRAI%u!Yo;K0+clS!IT=EiOpVTH)j{p%UR3fUjl1#v~OfDrR97pZArRf92-wy z4Sj?$eSAC7S8ZD0t0sMaaA;$w;VY2uQEsEq>n!L@snLZ5`vnW!VTciFvq5>f3)#z~A7B}maQV&liAg~<%l z19)Fq!%5p+WtG-ZcIRTuT;zBY?N~Nbgd@{R*PvoI0N?FL^b6{Lkuu`i)tPY!Ux$}x zCW&c`$pQSD4N9C6TICPV)LYPpqDE}zzs+k<=3B+GyoGP3oY7Pier->jo*&=20TcPy zdg74l(^kX~pH{1$`BT$%*`h$Ew^=_p_fkKyK~9qm2KTEd`OlPDl*-+gD_Jns-w4so z(zIDnryPn?PwH=f5#3{wzpLE9_i_PA*hm!hwG0O8-8=O3ClizU9HLa-uY5a`vVyX2vB5x5H-5PR#)0o+aXp*doadNKcw1`(-!0;l0VUR=NS2dG!*al ztaP{_RNbvq?YJ+E%@N5KnpWu}#iehkac(gd4{BowvllCW0P;qeS{3{vSMZgjwp zWZHZ$S~)_f{Wx3NbReEzzIyMI>bi1)U?khy)0#!=?W ze1r8cU2#4!vOzk+r+p%t!Aeg>TR@k{1N)S+zZHpw#< zY-X|-jQmmpB`MW8H7U8Ws!?Ry`?yMB#63c9g}3+oB3cQRmcMhH2y@ z_(fk9vx7!jQO)B#!7rTDc>)?yj2rV;Uy&~FciyqMP4;S{Kd|t)h@Ts5JH?+HI1F7|v{G=A>=( z;|~2jgV&)r>l>*bGg56-Dn$<|IX&(6)}WOe0oF0hPQveXkrOk;R!3o}5T_CJCjq=` zfGCmJ?O;M;TrQoB(#5S#k=L{BsAcs}!HqhcoTr7t8m6X|4s?EkIu0jIiA6>)4>(MJ zR6?>Y?O+_-iB_W4>*)C*emNw=NSr;_aA}sJNF+jg{JwG>KX1hTct1^3PuepDgk-{} zp|d)*E9{1;*27PMeQ_xgU)GEnIP=>>Z|nW@G6(aO&OqUmA9B8b)G3&|OIw#aZpa2W(^L1yHTD~-08ILcrmuU~ z?7ErAoYK1nydRI(8Rp`VyHFo?Tf})W2b|xunqJy>OhqlUj)NDFSTwOvHvKfXo^|agC@2t0j6(h<9IEUF7 z+t5)p5Vi|fpNEN?O}|;uOk$OP(O{DfTBCiFTE2Y*&x1WV!_&Ofei7ul)8zkb?Ba>#yKwhwZ{5c#2>Flvrt?#ppk~xED)`bqxzvRq2oKurvv>{asdZELZ|LR zN8O{Vg@QLFIi#<3pL=4k3Y=02FGvLno;bcbo?97{(y6iWsR`~C2@fcLIHd6@1oU$m z7JQ&mNW*vc9PS#OSxs!*@uD9gMHGd3_nkl5bcdV+&T!cYuEU^LrS_#`c0Jx>n}UCu~(I$QFXzQm{W!zUxeNDJ4RomC^N`ywnFI)(y9Gsrijs}mV zws-v@{v*}Rf_ z-`1S;rQ6Yq)J;=sp+6+4HHV`i;hc8UVD|bw(uJ1L*sbLo#jh+hdv}hDR|4BV(*V8p z7Mb`Tw5%&NLA_#;2YD{jBR8=r-ipcs1T5PZc%=$6+h6zMJ#X(Pc&&&wQNuTEsc?Md~!H12y8B<#b{f)jif6Fn87({p1YO#k7>N3iJlXO z3sIgAFVugaA8Mz z$q0}++Z=XF>5%h(#aW9w=+7-i<{BS8A`2wB?s0&W`>VKOG2*5q^`9U*Tslc}fg}OL ziFCo5ugv_~)*9j2p93SLpO97Z*(=JA2)aCRMdgi#-DyPhEZa|3aX9w;Y~Nx>Zu)$l zz~qTx2zV8hE^@#%;BA`{nx7=&tFW(K;)od{h1{;?<4mD{0=k6R#Y#ht{?+=4?vd&$ zmhhKnqNWAJi?R}QMy&pT#fhLoek+DX&AA~&&Ks3Xw>1As-~@YQu}S8|247P{g8)-C z8_42?0S<;{el5q&*|E#deA4Hh?+i+BuOJWt)#Nu7vq-7_`7>FJt=F_Dn8eH8??YAg z?JqW!>ar4lqqBxhx|_KJPRb*H+>XVg^hXx;r5C5?Ue+vf)&Q-_FwAU1I!8_Yn-X4a56Hzg;?NiFCh>n4v9^=s-D7EX;+Fqcn2YQXy0#Q{PE(s+Y-nR;8InUX}I5z0kT9{ z@oTz{w$WX*NABYt^YLQX3RU9n;~r0X>Qyy1wIbWz65_3hKRl;w6UE!5H|v%6VZyBd z+Uo;JKaSlN#wctsA*K@<)`S4DHLV6`j5Kq9(RYX`KBzWD33P^zX9%8_vZT|!4|Xzh z4!||9aPr3GstOlR2;x9+i=W54fDHx50>kKX$3&WaIdW&V!EjV?3`ppEq&%VAQD&_H4CXNI8EPQ&cCX= z#lpvi)m0me{yO@U0920X=9?Ec<}ex&Tz1`_LM|uv(?OKjUY2j8FVup`!Ovp&gjMqR zSeLauj9VYnR%Z3CGUOQ3&r8SL*6W2f248mpLm`W43j+Fp@zP$&2N76nF^7}OFC71e zAJqS!;QxsPKnTdh(b>+%#L*h{e*q=J(1e#X1_y-(P*YVzMp>7?1_x>eP*YVzMp?Hn z2L}iR2V`Y5q$G72w>}I9nKcA{ggxVz+d&6~21!U%MnzedW+MnzdGLrqdS zmw_P&7ne*22OPHzLkBJc0W^~l`Y3mW-M!XcWRy4GxUWGPFBBzxM#Nr`I2Ae5~;bkA}-f8P7o{oMEW z&-*;z=l6V{&-eRNmbW{mVM>BLK`caLXrK@%1Hc^bpa-B3NSLy+IUOW0C=h?mg1`U` z0F=Hy3NQ^M185|G(lyZ1H$bCc%78iK$D&in-V8v+Tyo)=b2QUDn2Z1C6421$n6e0ug1j!T{OmjVtCCv*0^uE%h zK)>HhK_ESV9|}<6hg1dl5lMd#jmiQ@pchQj2IBVu`APm8p}sZ822!au1mAUg{*3-s zCHPXPtbf%ZU%x;GNC)r`38d4$mAZgmqv1gkCD8ZVswIO!r4UVNWGV?jPPH{eB}27ntpNF#k4f{&UdL1qgIHfd%8g3qH~T&Y}PcjRZ0Qkjc-h ziJ(CY{uO{fk}$vvqQlmgsjUxaLNsul+`u|WK$8&+{fSY0>`e#%qCf+hUQi(YPeO}N zP=fxV&;~RE`0@Y1I(&Z&27$B<=I@g-GlQ6CHPCv12AbaZXB4(degX0@P~{to_wSs$7>!a-`tnLgRA5D>4D+8bW{VyRg%d{;64mYvVzu z$8$n%%ecQLB%)tG%6C8zJFbe3o{} z9Vm?2S@hru(pj1^#;jt`Dyqzu$4u`s9+SxZ;ebEKvbcYF$x(*_Q|YT=u(zMSze&tw z^e#rq=o5}(Z5u=x5g%xpp4>6*Lt} z%ucz2PV9)0b-BM4+uyPhsqJZbjWoF|!s!;a)9t>llmB2}YSRL{wf?}=5@ztoVx#cU zak!_CZFGNI{yT$XN>T<)T}@<4^23PXZI+{YT^*$&b+0c(z48gcC++wtWA;EJhZA|D z=s9;z&dxldN<@+)(UZ@WK(yqTR5u=ujL2_O)sDN4IaOWD>aS_UrmI&f9m?E~Q|NS) zrka3_Znu3HmFiVFQjRyiw?xZtqwQXEwU&`?f-`?kCv;YZd&itSd$_<}Xljh`n(|{$ z^ocvZ*mfsHTuP8PI#71u8LjnDW0}jKp~JkkcxnDij87j*Aj(X;%CmG^Nui-9_qjgH zy_~vs$=SfJcyuNsK2Cp@e41Spx$i`dRk-M!?O|o-1Z3GKana#YL!#BAIv+PWypVB0 zw?}`RRZ{3r)fUgP{}I~qtR(#DN2r;d=wYnO{LQ~O_~6Xqu)@JJGkGbzo?os*ZGEr- z#pWO>OW_hd51^;ZE2JX*RePsA>z<01YR8z)MBzR`wcfe{x9U%xiiC)2yh-;{Eu3<2 zT^CLb*JZnQlKGS)a)ybXle^LIlHrWg?SFqlHclV53Vwkq)wFt**;K-_AXB&8Yd@41 z{hrIt&cBDNUL#?g+ofzTOkkZ}nMN!ov}CYY(!v9y7fceg_NE|qotA!+pPk>2O zcRZB)9bCr`(Z0QV@ty6hnJdDu52tK!h9ISn3)aB}eQj_roLF|&2K(r|W6UVmF<>KGFc zd@0{J72Tq{V&naJhmEb$!peWGp|_;>CbK)PX7!GwE-W!w^$w4BDp>WlIhpeU!HJ^d z73XPJWlJ3sovz9IDD(<`ZrF9+DUT?Ze0CdtI^s#fVMGj1#yc*ntj956I!$NNx-4~i z@Bry?dr$ZwMrel|`RP+BkJjc#FL^ zbCg~CYj*uRkh$zRJMMQ_DM=utpHl;52XW)PPY!o#DG(a<~%@E%J(_ zF00&(TCS)>&J5iC(2hIEZ9&1|4ATePHX99})Iw_K#kQQ!B`JSwxx${~7mw^d(2Pjy z9a^?oeMw~r4+K;=VmD`XzsU-{az04i>;BTgB&=k<>X-%jMH#Aaw@|gAttujbJ(abr z@Uhv{NF_W?;Hv7(&b@7KB+mquJy$x>czd#J&RJ^zgl@u}RQOFJ-lrR4im5Tx6NWg5 zSk+zW<{K4a;h%q;S5SaVP&`|^QjxNydEarj6La`^k%03t(suN7j!x<0$*o`HTJh$} zXmy0q(WV`E*~C+mZ~LS7*5yWt!4`3BtvyOCjnYInJN%73c;3*RTgCCjCGoz%gBeE; z&xN}jYnORwe3v`FYiqZ2wJ~pyXto3;o?5vZ6`K@0Y{r4~P|7fKMvvIL?gdx{KPzYe<=4R!CgWL-w z2?G2;07sZD*c$-$M*8Ie!dwu@M*zr?1OV(@+;IN7OaVavkAn}~0Sx;q{&z?a1mHn9 zxPifc|3rk4MY6w12*4xvn}h*8^1n$0z@zY+L;*aC|Bx_JMg2D+Ri6GPq>9ErB*F{e z(f&<*03O}n#1G)n`%MDK5&uKT_2RMmO~~#x{}6~5DQf!%B2Nka1CdSa{y=0G`#%ub z#^Db{p2YDFM7DMQ1CcX;{DDXt(BB9`+JOCkfk+!Je<0EZ{0~I-b^8O6eck^+?Z0Cslmw>_EU>lsdX%`#uP^Yx|@UlR~g3)&v94K=34e^?P zH60VJxG!fDP$0!$)m=H3tBtS9GfD8bFt!-HZfM-2Tl{r~ln#SkuLgujHsTqf9D8#`kCRT-0fFZWv)tgUEH&+!|Jkq25^{nyfut~|RBr74sMyAB6 zuWQu)wb_Bdkj2Tcfjlp+jmOP&ZZ9kBO~EQ9yAK>oU+&YrlVW%~>=8aaf)7snL;x?RA3u?>heW0>koUQe<>(DOXM00exbRCJ4O zSB~v(e1FjM5T#MnLf6Y2=VoRhx`u)uM5sq^rcgMbZZc#LQqOSYk%9Br{ny2MYg z3rGYnyrO3Kf;S{$$tOJyAreP_r;hv*%?LL3cEC}vyCRg4WNuC)<4|ps>wK{>Zrnfl znsL;~n>*ebME(-DAOp3g_vB;=MP3{ap3xD|y6!M~|3k;IWmHn7`pCG#de%PH8roNI zs!PU|(UdB6`8;}zuJPl94kHf5Mpv)N?Av--w1G=%qo_DukbOjV>f|=a}ZKYXv&T*gR#{1A#(M|K(=|DKh0OcPbmjQ zGEA^+234Zt+E2C2Y`p$BO0YXfb*Drst<;jsjP-zICRdH}rO-|J)8$pBVeF_`3Fqh+z#C8z2C@v#^Xlsv=t9nlM1l@5u zmAxZcj+Q;PgKh9hOjf#_X!$2+rzlzy%hA_w2Ok5TSZxfy5skU9CY5E`lZgk2kznd1 zMPFP)eNM$jzaN$i<)awWA1tAjyCff4Shb>=GK#vrqP zc3qN(<3TjFcex#Zdm8aAz9xZ9%kQ;$3`@koXeWOc_hLEDf&ftd6YKfv(+eQYj_P1w zb48-lf+nQ$13AI3h#MC#Qef!7=>?%!8rdZa?-n^B$n0C{E!}4oZ<%$kxX%yg&vrdb zQhRRZhK1zbioQC9Z(zORc%x`ngI(0-#WZkf?til1?%7*^m-}AASX?SiAiVIYP0tN^ zn*7}4B|0t&yFh?6n5qoB#rxHam^HwSjsujA-G8pvV4kwk*Gvx=u;Fo7=XKDN`M1K+8ML2?9?8bAF&CZ4Mmw28v!+|XUhIp3>DE^<>3f6 z@Y#UGiuTX9wTFJv8CK&2o3eyom6M0I|VS)XRBNT*f)6Tg!kpzY~#}luxHr zgum1`$#xgvwfy4~MkDybE0Q7@OwR62?-?_0edShv@Sq?89(eD*HQ~6P6W!8)kAEU8 zm=CwGhCIhs@cE3%`*Fs0p2fPuwy_ysoNuPUF;3T96TN_S{?KvCSux*IT+u9(v(DCz ziG>f&@6g|6eUFIkcMZ0ydwrEpjt#})Ked&m-dLh=J_t`R%B<6hbILTH_S5Q>F0d=V zH&c&)Ed53sYEYat5QwpvXSH_p%~-<#BlrNhPuiq2HJzwOjs4mOzV}@`;~CFaz9If56rTn!>e2!VXNv29RA7RW1HB?F zYZuZwryaNLgkL-GWZl$jUh`lwqF|SsJW-FLm{$HG<@sYdztlM{(*|-VZ;7^ z;`PSH%AmJC(W71nEsauGevV~LtO_aQ98858Co_d`Mq zU-_GRIN6f`npB2+W=7Jm&(%g=Qy#b7K^iH{r+A3hggKLXpIKc6nJrH$;@|r1PRwW# zRomQ-JWeLxsA=biW{En*8lHdbixT;NINoAIYZpx5t}{|eS>e{1!r8D}JnEJ|N~Tyw zaQmi@C(S-jfKu;`x|Y4BtP#J2tZP*d zOI23|>9ah(wxm0TOS4^r#fh4KhNePu0@(Hk1`K!>lXW03%)WYe$+cMy`6$~YNwHw>(OtS+4q%B~rOzbM5n5pfIrKa(Lk-w3`ejlPXnJ7=>{J$W8|YLX zCgeJqWjQ89uUM4op%wGN7DsR0!u|Eun^q@!`w4Zr6mJpE{wPV<@^#jKX2g!dkY~vj ztd7bzzhp6Pb^$L}q@R623!Ho4Bp_%?CBot8^@ z`eo(wS$i!xN2QMw?76sq3F0z9B7^2$>7p+^9ZIMSz15MSgX2AHe~a@Rn|lt!JcGe_ zO0NV9be)b}Rm(xPIm_F)qhAUC@j=|SN^z}MXpGsKinCpG(}2Z@YhgRZ^?o?fMEIcV zIQJ+^56`$=4rfasSH;v9Luco;*q3Y$l=&}ha;;`YkBK922v7$aNRWmXmBy_7qXwtVs9s%Fiq`b;1a`Hq#;&5`(GqEZ zn1D4qW4g||c|}5h{;S<#b>=};eK?^+y=S9!?6>TX=%}>BsPrliAInHC+d8$LVE@GE z1AJiVJO~2%3LEt{SLQwHdPEO5ITj_~+=z>LcO|fLBUOF#Ay*G8%T7jO)!#9U35uTF z$hMnwn7-RAvwf(=tYpHNXKV>2)#-RBO#j}|aJJLThhQ~-W!3%aeG>e`1Iq82{y1?O zAb+w-#;nE9Z+c}b*C1mr!MAPSiFFG%xSo8qdZ#?Q5Oh_8tIK%5Y^Z5CrGQU)Ir!zs ztM~^U&$UdM;=GCA&)7Sa9##D!Ay*dJF>n2=*GxwDpw5tIb1jtYwwMv9XV2x@y^XZI zTW_Wd)JYG27ntQc0#wsdPeh$ZkXg?qe*1tH7d-((By0ZHkfKo+3pk!8mtc;4Vf-<- zb*4QLXu6;tpl%p3f;R2A?W9gj#`m(sJH)vk|Iq+?xZd22x?gIGErPdau$WTU1}3}; zP{q8pf2Z5{Rh=&8@+T;RrpEY8yY@V4?A)d2*9$hX%2av`K(T~m;Mf+E4@rOmK}x_c zs^CL^->|s1ZzoE#Q>5RNX5Bq#nBl1fomZ1ZaZ%J`St{KU3(zta!davXE2_PE-A3Sy z$KTXB6y>F(BtLzP(tF1$w&y(<&x^gCX}E|M2f8HJ@_HMR$IWbZJ>501^NxX!U}rFV zHi#oT*DT8@vVf)fGet_)ZGZ>>qiJEwSY_t0+FDJv%pnX;|} zub+sNCdbC^vm(dz8--hgLd|z^Bb9f~!jLZ0*!uJO zYf0#Ydhun9dm91dJ+PMldB^VVn`R+H33+@GVQj+ZkAA8q>S+X>%yvd599Nt6Jt5Os zr@oKz@RgM&QL3}hEo$6JP9OzP`bUPP|DaHa$;sx(bWDDzyqstY6C>FB1s8DhqzUXak3Nr)QMoz| zRBiP;htHq|Gbu?z*y2fgG)~5UXE^$3d~318`@W~t(2N%0n8jk~JOk4o(b5bQ5XJ@O zo1yjTam936ZMzzm=_wK&E&R-RyCoHlsQN6E7dUORw-%4r#o}55h`KvH-0KMpZX&m( z8`?AalDcKVSN6ysmoY|F98;lc`CUhhLx8ZKoP49YXC!t7H~Luc3qs9*fRtLB+GErP z^mUFgZt8Ft)sAZAG^^zpXNYh_2|Zy;J?j3!3LN_z%qbE#Wsm6rK38Tr)glX1020G~fio&*Eff>lS5K(%(N1P}qd-5B zhF!9|Q`a2}!Bpv$)Hb8f_NQO$JzEBYjWhEEV$Ri9oWl%+BIb%rYdyD3-~=q1`ubhW znd-7k6%oFKp~5#>W0;iYu(sT=N>h3A_wuS3UnQj$boeD_1c}anK;7x>Bg<2^vB|^B znOK^9-%q5{Res?n)tgE`6HFqa0huD+MTo3uvxaLT`ViI~m2+pdc}=m*;;cWIsb|{B z!F-!E7Cs+IOgQhLP=5l>{Ul{vFSh(@x<%{r2cWTV$k8%*_<@X6SNoi`T!OlHOzES; ze8rD=A*fZ91qp3`z@-XT91J09{E#NjZRI(U`)YF>!`{g#sE8v?6}I_#Av16Oi8COz zl$DYptSY2M_Er1IwL*?dJ@`)W@R4SE%4^)Yw|7_^-|{zzW;}tE8?N-E@gM7PyTFeY z8f9Qfw^`-Ul=vZ(@E_dFww(MM-vVA=yRXx~eKZtfDu{-EPfNWe6TbDdKkc}g_OdgT z^i6yQ*U@ZKly(Q5HMYJmk~o~`k{17*p1lBPL?;rDa8KBOKP@U8)Tv3y3h6LE3Np!oc=H9F z^Q$i;C2inU-AFM#dk4alc%)*C<6x9tzt$SO^yWRe3q4`?Y)0j@R0{Se070eNTe=?Q zbH{q_>tY&@A*3wtwC~MPllHtbja`}{KtuO#DCA7Sf5NcbDNyiuZfJQPr$b802t%&b z66acf1zo`{=P3S^I@>f?|1cBt2eY#n2i8e?ZrG)h3bc-M-y8PqD92V<&5u@a!sj0})BI@fCd%5{t>5t#6|7xuNrRcmfwZ*? zZ?2{W8+2T{23PRIisOM*(^&D`IT@=@w`0|RD^plyXSs51dylvAf(SI_IV|y0gV<9e z0b?rAa#0P?JDgmDDe80WuZ)w)9^GRC3f4|ll~|U=2}_Z2W%1KlKvKqax`UHiCy9Ji z29deM<7YtfOsQk?UnpZ?1)5?8VdP?;e(i)0}^!k_^CRr_^j!tPw;vb*Lv*k{bJ95)aWTT?wTJuxa3CheKb?6G>LVkVkm7*-<){5 zFNa2f%BUfNDU_R(FH(vw7RH}$jAp2oiw6a(fiUc4V=EHA7cpqB@0CR8Dq|6##PMGf zxhUmcC@MWnp;Y*BTH<*Y)Wtb9JaP51CpMX_kM{lb`mZk@A)~Q)()cvYRT1icACb-McyXO+?!_BHjDkIJl0lSBo!G1nM;?XEP8ODnsU z!L^yG8Z89%C44BFvc?FyCW5olLYbUv%1E|(KocAz{<>Rr^wt*r9*{Nh3SOZQUOcw@ zAYgmn&x5GOCVygWxO3fq;!OMDGgNwf$(J*>o$)&76j5L5Ld384CPvJRUGpE4)lpZj zzA)O9S_>*Hur97Ej5XQ&l0iozvEKNlA5E35-=#glot-=tn|0=u()mLkzeF@?M;orA z)xi-}LNmGB1}QaHSID<@y*}X(JWcY-&zSYiO63L4WR3O|DZem(mlD5@=cvKVzCr^$ z*EjAW+IuiEofJz$)|eP*cpbVQk6+<9R<<0kS9Lv3h{*z5m&xW*L++F3=bS05RUC}u z=}zgT{X~rgW@`lAOn%|N5#zEHdqn8m7ig${IarqWV551>u;IAyM*h8}@8(6u$W>uu zf5^ls%wvs8m&y`0dcPYStgmzdFi=bQ*j=6=aWn*9Qst|M;} zFV&C`LYBezIOBHTsIr}L7yI>j$5&G$q8!H%3$)+O>XI?e>Qr|pf{J2M`Swq1222fH zKOt)LV9VsmsOD zc_zbNTBBlxHqMgHp3T%YyVyT4Z?;4M=xKdSHuQ#n5JDDapN>AIT6~Uv?LW$yt+#6w z8-<%B`cf1!)w?&ObjjmH&FeX&y?(R+j^!!27gUr_96xVc-#SLTd-xon+}E>5j0oAJ zUn_Lzt7tKJ5@H}=&0v`piMz?nuctzRHU$Z0@k(&w{eS%6{I9_O2mu=i*vbvz0=05; z#`!PO1@n%VpKb?*22@B@R7+BqDsKmA3sgu|R7+ATLrqdRmw_P&7q_)<2T%x;5c(;9 z?NWPG6jdC~Lx3*H@xaoeysilcaA9`_c3A<Vd>ZqGX6DWJ#6?i5wA;IOe}5DN>D2(014I$v z;}hVc5V(O5Ce^@_Y7GZGLOe|*1qjkYmc%g{L}8o;YIzMDqd**klaMn=KoCWNSknm| zh=n@Hrb9v^Kv6)DI0urUnxq9%lc5M&#Q??lHla^_4y8j@$D0Bkyh%^MTO=5Nnlbz6cvqWP4cFYUV$+!Ng19I7;UPagDi+*2*}bCsY~IwwnX)fN73IXzhJXWKBFKRv`mdpU}Arb^Zc_KfbuU1UBD~!e&f~3{JNA3qO zmc%RwdzNSc5d0A}!pr4pLq_#=Q%#t2jlD=;+`MPxvV(duLt!&4wfK^12r zJj3KKW8)1qfv>(cIGD-E^!7!7x4(Q2Kv9_=P$(3}zY`N^GEEO7LV+BAL6AA>-o$|-o{l9fte!J}G+8rce zjFXowo~h>c3{~_sY-$#zbl6AYfm?0aL(@NwE=n4yw5@F@*j>Hs(`>FjoU~pp9$%j`jS=r2v-7@>F8H%pG zmOYc+6d8JMUUV5gnzgNU9)9P1_o4`;NEcq<-l2NiDtQEZ&@Wh_O21%c^c{|t){0-7 zvVZP6<@rb7!^XV^L$g-co**gRp0w~}^u}8i+7WxM^lnU#*xELK`=^}auIb0aMv;;f zSL4&HrO3*?Yn5%jIWt=wdwaL6-udCEuszaAn=NPRM9vqB6sfb65t*U=Ue}%KM~btJ zHFifgoJpHr8h`qf=esT7eq51VLdxm6<;gO!jidHFwKBqKPuB0NfODVYo99>e zZa!9i&Hh+@S&YShdiI8NhDD38%W+Y$F`95Q^A|gIG$r0`tlV@WSI|i{mv+|t=w+T} z%RS89Hu>5#W}|LRX|;vT#oV?c(foU6&Q0_l5$1Q*q1?0I`Xi0NYxIh1w|Sng<-Lvv zaYhU0#>SQd11O$->+(QA`%rnu6OWQ&Ri2=xv2kl`TYXz;;4#_RO5wTQ{e4kegn5Nd z!Sy1ix`G9X4>xMQi!Yy^kocA5{q@W0^MYEVg~NrK?%;htQ)Z4QxF=JZhN^0vu4s-N zamZO^cJ55=rk*RWYh7213m)TDb>Fzy9I%SIpmV=#TR4&$j+c*o2Nr)9eP?;tC&u|X zPmfS`cTU@^u-7CxBd<*^ zo@VxIc{&kwXr62Q=iVNJVG?CsNs{dsS7#R%$IkOh>u9}gIdpimVfl@UM;`Uw(u1-c z?=*kyGH9`UrMl&2?5aY=F=5Q^Nq(BHh1gc1^=R|^Gb+%7c^Q@Q2a?;NxjfTf)&&;tPU z04Xyv*x9>)ZUKNg;{!0WcY^(AU0GQHjG)UlMz;Ud|Et6fV6=0!HGlqRsm<-)%bWm4 z2cVM)(9Y$(#sy$BvISM0jX-_)ca>i@sm{6A$jCeQ>%PCz>wpxIwl=Krq# zufZ&!nVtWc!rA)0$Od3EvA4A~dar<7Ej%48fbX85_4>!t(*9kD4W#aDW8`e{UIB@J z0Xo^g7eLPTcEEo#JAX*l#r^#{J4n;T!U_1-2vFzDEZzPpfb5(>tM+dOrP^8kJtx;c z`Zo6OuI!*xXP~X+e^xm_SwKfu&>Qma3hO@|1v>w8AiNhr&o2Bc{%_(0J*dPxfubef3A6}O?*x*NekV}6>^p(d<=zRDE`R?{pmc?I0;Mbdo47#f zO78?pSAHi@y2?9&(pBFH)N8eO0=cNa6DVEtoj~bY?*vN!{BHuCsf@->Mkdxkm;cz4 z+@NG*Q29Ui3^(h)NB`$uP1AgDn0+kfP;Z2vW293qelSe<4W9<}U=)1`1 z@fU)uJpV$F)t7(a|9w`Ah}e60(Sz=%X8|3C9H3MVZhsD+{~V`oX9;@!q{TqT0uvJ# z(|?`ZCazAPW6R}VpGeSW;D6Z667<~w0zH5xu)h}UO?ZQ?(walde8mf=%b}=>pe)@} zcqUP{2H@B>I<}p3Y}>YNEj>;-Gmg%s~{NRl+j1~T`v$T#E1?cu=qq2fZnUvdK)chFg`qR$TpBX z8(tfRdBRXCytUkLJ%|z+>b&hn+>p3^+79X%M-X!-dVn~KkJXx+#gq(+v>+F>l?n`N zK^bYJy7>3|bMxxYIb5+@G=KBq{9;Gfz&rD5Vrll7O1FXU9OI<{4?|}CGErsQ23f5R z5pj?70kY4RE?w2tn-n9T@y{`H_UHb5PA zK$30$sgAA-h&HyHn@X#JwFkX|R6VVMctRZ5FdO)rxtgY}`5RA!aD$xCPf!X8(?i+M_D6U!z2X&60&`^1NehfcVl!!cyV>x@2P{xF*v*E2VDWr*UjEi*NV{2qR`aJwTqmT=b?Of=8mv18P+;^jeZpcJ!YA{SXn(*H2`OLG_V}QsiGv&{| zTei_^%_mAC99)lDQdQ5&%C$R!vk%ici26Bq%8UG#K|@1xThj!;*_*f+PXEWTh?m-p zP`}og-{`iYnirG6k5i&cik4nrZ)<-lb8pNz(DaH%6$U^`S3pS1nQ=%=IV2rEXxv~G zw1zQH7+w@#jd1Wx)0AwJEDyRMv*>v5lF^fc5<0%xFEjr_AYMa#+jull0n2YD8&R0h zX}??+cZpk)$THzK)%|su*qW|mvl!_uK)OHH=D+5!;1%azeRxg2!0$E)3h_DFbIucT zU6^g0?G0e~%Ix-DW1em!+G5ap4(-o360BYRC4z4Y!f1u{`4&9E;eV2XVQ)3_CXeiiCye;o1lAAe;M%R*olbN zcL3(_a8-^Qs%&BPiUwDRt>!Lv$r`y@DzChq3Yo3RSX=nl@LHYE_et1h!Z1nzt4xFm zf|n@4LA4zF<0>+34JkhsOJWhtT}ym-6zQ}moVG27W9GGIco&a}Kfn1*jLCM@3miN* z`QpIRToKVxCBXPnH_SSHUqIU>P18v*U>W|$BQj6-u@RLOo<&>@ez3wjPj-%O7f zJn@I~7)kLF;#9%6l?g=rJXQY6mCLC61=phvXpE)^5E+Fe{EbGj74W&Q=+_O)Rqid~ z0Vq26_|WAvJP%1*-hegtK68T>T#w_lBNQ5wIH7e7P&Q|Y`$><13clErSl4SM>DOkt8o4}(DFGCRz?Lu~XP&)s(`>KDbfDBSt4QrqoHJ+~9_krGlyeS7 zD9cLwBA6*}IKP4yi4H7KhfICUhLF*H_)8e-44Eg6jkOl|NS8(xHP7LO6^Uj#sn7$% zwrT60dnej@A<3Cdw@M!yLMJ2tPkOvSMYmy-^O7EWCE~OB#vp8j-;KYObXg8}k?u_j zv8(BXM#cYPNKQkkyM&q4XHDVcS)q7BNMVw?^ovOGADq#{dFv|i^l-T5uYvsx z`hU*wupF98V;L6s(_WS>cY%BB=1ibizH$Yc%x&5DxCC1T34XJL%H#YjMu6)X(dI0l zG%uBqAmHorE+IsI=?Ub|!g4>lZJ+WhxECEPHPvw_)jy9z##%*mB?czvGhKd|uo45@ zll|-RAw1Dx*YLTwl8u(J(^ubnW!qRA zlyyyt1gfgTk#R+|x3X6y<*K`{tST`xS2lJOs)EQvVPTk6IC5{OZ=;e?r&b#889Qhf zTc1_PiYu$R;@+VNbvt}b`C{u^-Nh)vfS`;(-MtT_%&M)9y#wla=viSSuQxXl-CE-& zi21g4{xSjV@ew8Y767U!ZMPkP0yenp#+D{$L*A}*+d^*U7w>Cx&Y-u6_VSyf2Wnn) zb`-gpbe-<(j7S`5)Gr&~IJ6Hm^osBp$BuH4{dEw=!o|%$` zpsw3VW9bpAtN>`CixKxdRoKH}2k@B^xqZHpgf1-uQ1go(G@-YQ{U!32@sJg?&f4CY~SqM;b)=N=7jUzjw@dThETsvL2& zb2rWE3+QRZoN5T(EirCVjlRVnR!~| ztkde4V3ZtbyP51D`5d}l9`qM?(oA*@7B4>_0~)Fy`%wlSXtlCOK4Gv`D!VozY+If8 z&;~0bX>}9`1F1AyX=c}5cxq_jl==6N6{&4Q$Lr`}$7$#Z@^vJjO6ygGW$46UWJ@Dx z^)-c+u`~Z^&NZRf?$XAF6kek#LMG^Wm^-*_7Jbq4s1mg5&+v;mQwL5X>~~!%lM|DP z0@RNuFb0XP`QR7!m4x)f$&+BsJ)*rB3XR8?N-C>Z-Z}eKe$R__aqm;+%y9GI!|n0; zN*@;~)qbdxvC_I067b!5G2vcAH4M2SS8<8x!pj7TLd9d)EW%wC<)BV#f}Qadj7^X< z%tkPP-|LAH4ZCUpL4(!@Loc&|R!h5y1I$-q^@h4nDM#WVOISeZmEL?C1I`G`8}GFV zK zA9;2)3+vg18AnWMIX5Y$humjJ-TON6%dTg|%AqNS6Kz4>9$*7k^NW$@rSqu-RPu$E&to-lOiD~o(7KnVR69o|nM#n!-ziinkb8uiC1NnNJ`jkO zWlvJU^GTGIdnm5BsQM3F1`m1c00+5zHS4RVR{KyYF4b`d;|jhS+yokxt=bB|3*K6y zQNBJZIttYIDCI{itI3_^M)ZeDPuB3dSY<`ULTy(=82zs}QcsZ%wgdij1h6R6mRXEY zMfDL(rYNnF0AYYpx|NUM?VB)0Nl#KT6<1U zNV|QWnACbF%MUScg=Xaro$;z}D`bFtmVm@d8_jT99viad9HXM9RQ0E+ZXha~lGb8F z_;oz>BkOiP{0ow!rVUE$H?AzYf6lA{(egnaA+%dZy)LDF zv}Slwm9C^dzfV<)cJY)dOlq#I&Q87zT*(psY2{dI9AubZ?b{R|V`$5YwTa-|;#)g< zyQQ+EnBmunb96jHL3OjDGnSI=lOD-J1KqUF07dT#pWLXCVUZH&aW z7VbfTfpWFx(vm|oa}YEdJF-QIPQb|~5c{sLN&n87`Py6r`r@QO+oO&B%e8DC8#!@* zXdIR!zc=0G%}f5`2-t7y32n__bW}-h*yZ4%@YbYgmDhU80%QX;LcR06Sl;^@KF3cO zN+d@j*uOha*feBn%w?pQx%scs*#4f^p}n-y>U`*$QA?ahb681z2ar-$k#_Qmk`w#ItQ7q%xw=8|B?!5#y_$80>E?2*d2OKA@L;?CkJtT zge`^Fi$5y7WXhY1p)upBZiUksvL<*>W4P0fhyGIjg0VB+-ULYvi^lL27wKZZw54tJs^2-I-2e3g*>PFj4c{FYl921A&sXu9 zFjSZr3yrVFs%U4j@7?VW9u>{h*V}lhpVq;rZkZB80f-?>i2WWMmyWnQA>0x6MG8g# zDE6GKCQIC|j_=~$j>O)J2;%&sD65C;X`iLoXd`Or@oW9ppP$yvR}AY#8S7I#{_;zX zOER}M*Rd#m?*XIb@dB5^`|0U)h<*0zGX-C>esB>35~OO!kY~8+UT8Tiq3cd(#{y|f zvCP8c$^eJ+a9&FC&TI;r5qPEteM8-eN|mVGnI)%cYd5GI0?dCNq(y!5Fj`RIEb%=9 z3{?C=wN=ofonh`IMa~6-OY;F2pD_9AKqAHs=EZ(81^q!8lJiV!CH{7Q;fJt(0z4y3 z3rY*mX4NLQ;^u?7`?zx|b#=96R2b-8U+3{yRsc)=8O=v-bzJUk4s(L&c_uZ-_aeKc zce7vP$H}9bLq?fylV*CUORz&57yCB<^Sbb0g3s|JXABM#T zH~?{9=KNyjG{0ClEmb;p)|tJ46HDDPR5E^!UV}i!w`nFTOJPhT$|kX0_|&v^cJp?Y z##MA!wF~5sF=j=N%~`zPi=zNspe~Pz8fuA`#q$jd!tpNLDVe%I9i5iFd{PDR;d}x< zifmY4Gx1rU%&k0L4Tr|rN>%GS(R?d**W;nVVnHuFzcw$mP03PG2OTY*2pdmX-9IG#07Lii~zQ{ zy-;Fh&fyb0qXV+&ux{%43!=pFjGwat3oi6w>!tYe2D{@4-4ah7)Say8$e2ObMwY9e z!!bogS5W4wl|{-{L%6Lp1!`QIF^)izu*$BNY?VIX&;j&TaiB=PZr=$IQ^q~gR6}Td z7(Mm^VBY_=;a7*Qt$ zBjx&8VS)>unBw5E75S)@RH-Teuhfsa5Q(`cpJZgy9T+CGr4Fs!!U{E+l(|}&ujaA< zKg?FK)#aP3w+26=+K_D>3SBs}#n#+RLMas#B7yr*#q!W}Ppw|`Mh>(Npk4>z8hiy$ zV>p%xVdsuc72j2nV~U?hF#yma*-8}xc(Ldg53z9w#4Ar3;6?aS#7tJ-b!d=XlhXR+ z)WxGls-yqTbBeioPPOTyA?7qUwObbUhiZP!i)#P#Gk$t(qjIzRrwdC~3%83}%lq%n z-FERyaAtM=PHf0VfT}~E@psn9A#`x;myx&Q^1$kb2S32dTP;{vJ|3`i=W33dWZ+?! z5dv|QWv{Is$*v^K1}r5vpX{Bg#ENRE7de8`s?oZ#W(vV7&A-H3$F1Nucr%E2OieFJ zS)F2ReKwoyj)k_W-2=muNGbSB78hWE$_R@h^W?fv%F2lCg%YlpTDh~W*SdNg`=E(! z(bM;9;B-fC&wQ=iUJo!r)wrVVfi^4Yw~5*9hdW(PGn{<$zb;;y-|aBi7#qBeu40k?BzV z=Yk3M80XR6-aG!zPYiV#=bwBJEz%0kdCFZH&ad+bMg$+}aR(Ig6$rv=>EjU@BRo#c zpl0_oRSVF@EMxBGX=lj=e$g+AnMlKeTR;!d8$KY=hFhygN=59pxMk!;MO_MPjh(Dp z_>I`uc1yH0_I<>f>>;jd(UsbghkdS$`!aX@)@oQU&L8;>i5HH&U&oU zSaVFns??>kuzVmnOhs2%b>-7v*TO#cP6o=X2fz9!>K_+Ck=x0~waW0#Uh0QbF>^df z8a;Ow^afb@-%1!`c9oX{g^f#|xF0-3stl&@vZ;J?p5)!wUS+y`cG`*W#4*_c$kV1WLG)@MI4D5lj2JLECu4B1*wbX&U3 z`dH4nW|D2?bHrP75h2~axY14a`r%u3`Jp0uwp#zi*BTSVLJsmkEkc)WhoXHuHJ{6b z*<+Z}uohbjH?F;V6@TGPo-s z$%(W{b@m1*fsWjlVIX*dH14cv$10MNeaccQD8s}`3Ow&-zOm-6yH{A?Oj<;DCL9a< zuVVU`CiM+rF>;5VuHv2fVw#7jwFydv=^szf@JdQ%75&<1V{xfrE2{6P4tpG#M`bVq zjxy!$dgDK1VmCz2&+VL)qgLRB()hXncU7r=)^1aST7da?YFI4%z5h9E7U7N%PD;DC zom|Th``^YqsJR)w?N+wKFo#v=F=b^Vq8O^6e-mi|j{1v6 zWZ@gU0-ogERL8nx zKHa6LkL3Xi-e7`8cmc^xN{@H|E_5a1cJgjY@aV47I$Y;EI-p!HscjNdgu7_Km_tnn zSfZu&^B5c3_0u2`kTNIh6+<8c_Z1z$Nb^b8Ezv0sRSngKud;(+JF$XSocMhg<;VGL z;mJ2lRAy^^iEEv|m`PPr41yyZQ-}Khsa@Yzx%}Os6K|=Qa~5gxSanJOY&tB662hoy zYu;a5Z~luRZAo_qn;P?Cq*=I_f79aFH(M;mj$XLis-BI+^&v*Z5L|r=0pBWz>eNMB z=V|FA1uw+YHp$x*ac~*clw=L##=zDsQC^w?=1=FB`i_y{yWBp4pzaq^j>!Dxv6q~Z zlK-75q3-3t99RF>wOF+g;B`Wa`j%^fL;NWr>stWWE#o24$uWo|7*#CBw(_-{rw)`` zzbyR6VG~{JavBS`|EV;BLlzN!(J#XiVKirh)K&4iO&>cSL-w#$MuEj+=IO`ofNbX>M@JpZunuo84BPSMPbFuzbCSO) z3FpZXl^Fn@D)x`cj7UR^ii1U$w{%h_)iN9`mT2u#q&Xf_MA2B!DedY~Vy5x}aS|(S zQEV!i#zATWK=!sXKe4g=>L5moamsIS*=1OFeGGXp+B+BjcbVIf6c@wbLfYG_E^O0U ze59rvhV~X=GpX}RVPXdBpB@8q7@zZ~#*1+QFIy)y3E94_UAJ!_TFvUGQ#K~QFI#dz z9^kbZZV|-%U^m98U_lKBNs71qbHYJwp3@6T&hoVy0OlI@I@gLfics{*t*PL2xIJ~2 zx7ft9mlc}ji5bvmgC{4coQ2zFM|D|=oZ2leDb24DQ$Hc@#qoK=EJ`O-xk zSjh-SNeuU?!lN49su@}y8#26Kd!Sn{55BQsnM%L?O`fp|h-Cw_$n3`dfNADNaDX{d z|GQwZ$=txq8{$@df$<0Wi1L(f3IX3>R z*ZW>(8S-FTjs%(8A1{*Omu9+fHcA?Z)(3 z2G}~I_g07&OZoWED;Bz0s@8ckq$vg|DS?a!T0C<1a=u>M&v6m4EJJ3^s=tO*xFoWrO!~an5 zNt7Kt>8yM-o7Id`e(Wb<+H8Pi|<88aWj<=gsIUKSCd>0$^3{ zSF;Wp;(`M_GS;HHwTw$<8?xs1?X*W7!O97w$;bm~;Y_>`V7e5~T41`P%A_*_s`xM6 z?%Y9%irq+?rVfiXz&2Jolb?li!5hk#)q2)pI5%A^Y4%g!-O7$ubXX}1yn%6es42GQ zv_#Pu7PPUh>fRT|X`jXvw2k2`02bL<8hM1r&_|`&C_m3cD78Olw=#x!&Wfgf47W(O z40U_~ke-_?7~*q0QViv^`_KZ zS^`H0xM@**e|%i6QZ_6Czfm~QP~{64p(g~fGw*r=?Jxtc%l3Td{Hs-IF>8#isuJ=Q zzphNYjI220ymRsdvI?J(^96dIC?vPvmajTcYah91VmgcNRr|`*m z6vx~owBl~iRJ>Y8;xj8w0FWAKNj5B-#a<0#smlP0#MbZRI1UU*0b8l!?=?Pq)SDZ~ z%DJ{3pKt&8zgF5b*y|6S5;AE2j#3sT?p#t-{md#j6J`geq3_gHr{EN^5C{}iBhHF^L_4WYY1_y91>yd4=2=CzU;PUw@?2IbHm!q7gZKSB>ik&mz*Nrd4#58(iMqy(fKLwEk9yB6Yws1yuf=!T@V zF#jS3hD6JWTDW#NmQ9f7AV3I{QJDse`Lt#!G`~+AxP#X+Z)3<}>Zemm$n2XLDgQ~C zjn3oyP;}+8=_1pZ0~i+R_oM!`hiiZ~w$kC3q+8&2q&m3b$rDN%j&`)4>LUXCQdw9b zndLq`FT6Jl;0{`Jtgtim^dcf1iciK3eUTuXs|%q z*Zlpk_JUirLsTHmW&<$~BSfeYI_;r@a!vY>?{CC>-z2Y~1wca666IvmT*)W+4oOkM zX%d?<>kW>S)b`-f(DK-;P@D|1b>#z1)Xy_~NyHMKl^p0d%UF7V{Vm%-xEZcFHNgj=cSSGV~1ryv617dEkw zd9TEHeuNpMBmh6pVnpj12Ri+R86*cO*QKrc4IP7)`v??&{q-l>c1tLnXs!Me7mo^+ zALc25d4OhM9#5V~!@&@(8D6D|nY3=8UR38P0L#uL1vF0D9Zw=eB_*GXze{SZ3;X>v zvCbL8D&*8cV0{+i`@(PyzN?zd-14YlWyX6@|N6I03=j{AsxUM$o4q4>H7>8PdYyIz zrC23>@(PVY`h~zfbvkVDa=0=HM?OJ1*lifJxY<-frLvJK4rh%jd7mYVdpM<+I3jO! zPc+_2svq%3n{hYL4VwWJP<&?qO*_?G?s@cp$R@vM;G-r|%l^W28Bz1DsjtPndXu@?$Uy8_{w`B+ddRuIdaKJ>=E1f44?1 zA6PY7whp3l(U{q@%!W7|XV`E52Ka0)2OMXyv% zNg4t2VH!dSe@9|Wl)Cju6@O8X(`_#51?CYZ+g`?doCouS{ zrScECQJG3?31k9r`@iAM$9t2QmP?cQKr}J3)bX*W42)V$kILe1!_oNwn8niVXN^*_ z*r6kAJo6ygDs9b@PiR0DTKT39;Kzc-890_RP~euomD~*26>oB(ENMebKXu000yJGK zJn;6Cr%qa;b(OfN3EE|y;*yM67O!xb(7%wJ*0G!p=d^Q_@1iUm5c z`pPKE+pQR&-j$tL*U(--rH~ut_G{5t1W|8lfNfNkDs@Y;%`t25LrTn{jMrZsS~&NYEa((~P@)3oH+PHzKkCOs!J5Gk}(f?N3W> z%nx)6OSM(*8*~<^a_o$yV_o?3UViY^?&X4X$w}Wt`?DNW`4{LeR81q(Hx$0U6O4gOh7$9V0UDJRW_-nn! zW!PsdI#eOlW{T{`Tdt&rQL<>n$o13qNd*f*6ib%Ll5t6~Nl#=;{D$ByR;PjXss=bf%>WU9_`=F{*k;|H^H)Hj1V4^u6+YWR6+9-W+Kv z##ruVffAKUF{vWwod7o^{4(%J=sZh`UYr8`;_W5X(|e;`D(by1+v~E=O~cvVHZn+! z%W$$UF2#3C?bpxO0fIrs64x6gGsnPOWy36f6Kr*0V1nltb~$+x+F48fmUp4ulS<(V zm>pm~TOuP~H52%8NRMfvSn_vwR5<5WL)rwX3*&RGG=gDi=lQq z_LLwy4v2pUKVZ}9LsHl$(^7E5R3d?N&G}i>{iHFwMrq2-BSI`H*mtAfzkh!cO=Np? z^J=RUqW2h^&lcJyI~OjKRn(XU@1ZH7`Z7(rdQk9s z3*wBR3_!*Qyx19>#uly!`7+j+Qs1BLY?R%Jrx;b5?zR!+h22pf&dkYE_mi3r#3>c^B5KZ5c+qVNhp@p z!$hI{oeHO}(K6Oa(8g#nK%!9~}N{P2jX2w$m*!&HO zAwbe-b7Id9DF=mU0wb7zj%=&rF4lX1Wstx5+v7q*MAUyVVXy$PKl{jU9*d@Hn-)*C zas<$xp(e_;RUqL~E~rWAzz5kSjBhL6I1Fu?%;pLy(5)#>dEl3t z?7Glv_{MRFFf89|9>rl*tauBJyZHAO1VrZPp#3j$=N9c3dJdc+9d9bcn<|}sJP|AU z4#qn(@(yfN@Mp&xymabf%Lm3drq{yM_Mhu1gH{#m_M$6l;jz`01CL$d5H()dd%)*M zTgR$=5q8_;G=Ea}JV7CJ)NmAha_}u4tRd@i=Zo?56X*oOX;g6*w9uoGdS``+9>JxPAB|rgi2}*!m z#SQ6T5z?$lRC5+G^C=<-Q!cS*Csp}4R}BYfCNnS?dI^tnTUimVPIH?4 z8}f(aW`)H|&6QPZko;+SA~9jWgbAG90Vvf8DkXqoG7>zr03W%U zX>|&yIyAhLzTdf6t6-pb7H}_Ya(bou{-i7SYNndB6Z@>K*~L5qOka49?#fECaD(-V z<59~WemrE=UE@XVfD>cI($my=Cw?K$_Bv~ttDEe!)_wwN8(E-!7Fk`M+TlWz-o~1Y zY@((AjD!WPTFX-$kq3u!N*&t_Vo83imq*8$4!SS{N=y+(SHD}h2Qa}ucy^SmPwrJd zg&JH0xN-qrm(TnYwD*2`bhDd97#^d`B zLtg|YVi35db|%g)Y4OdF{t$A4;;Nd8X&5b#w*RZ!B3dAC!5}eGQkGLv^4kJBA^C~H zp5qAJ(uTKwVw*xzQd&}?Y4Y1YZvKDkw!Uph4lszs?Do`D;t%Udo_bzhb-|W= zmp-rySx=WBO^ejGLoXbwISp)CK9r-3VBfI8cvr37xjVVmf8TWe;=J+BefG?|@BU-f z4ItbEB^k<3CRMTF3u6G*^UJ*tGV#IRb@qV6#btmYrp_iUC84GS_EA^o`v(Yzh4jJf z$CM79L7~6_>mIXU3kef1_wPv)W{`m6Nsb>>3L`9dIu z{5*4u_nX9+(F^+(5s7txfwLG6{{??~@~A~p2u%^F7a$A7QW_cRuXL$(08#%_IHe+kZFu`jg%oF^D<&iUg2THHYm`bha0-VV`^Zs?5VXK1|x$k6sT>oT)*G0n#9m{^7o>0}>Yz z)OaQ^d~iFw|CN^Shz1U=x`SD0`-U{>xSb$Gmgd^1K2v$VG_u#=W8$-b@orK~N{b9Z%SVU{l1zvpx`cDhlWoFXTk><=47Rmq=98JvkqfT2!`;McpzUyz8fk9q8;Sq=={VC>s~SCttwdSF1-h$Sla1igO$8gC+#Wj*Zxz z6PZVCa*q!OujX7FcPT8d1B>&!8T~fhatf(=rpLbtU$}$LZC9=9+?H*Y=NUBCcq5m@ zJ4ovz{YJo8I)Tk4ljJKh^igQbv8jE(&)TkSf^};s8dnA4*-y@4YNFa=Jt3s?U2u); zR^S1f^-Wetx9FYY{5<=rI!XuWhH49~HM*m-)GmhH(Rk>vt5#~g<@z1nqbsMWn=Nh2 zvx*mlwiinj$FFrUQ!+sa*;#(KOo@hRU0UZQz8;(9vgf#Y_jdjRV++S@`!7+NT_(`nHpv0ZJ)=GO?*?w}m}*FGjf_rOuwSFDy=FUJ z1W{~B&lY4h3`xukpY3R7>8ghP7g)vP@L0uK0Ob#v>E7XNhYd?((NW8=?5q=cG@0$a z-r8xxDkUcdJ*D_mB+iQ3vPVgAPi2;-657CNDbLL%%N~G3%GQpP$SJ5@el=NoMm2!f z-wX^9V=E}i{=LJU4nxZdo$Ww2h~Roj+ZE9X{uWMxJ&pqfzSRy(*HF~VM#r=KE^)5@ zbKWDTG>Zc!SX0Es+nfK@Q`O5()x05Sq=Dm4PEeVtJJaeB!@7@~qM@CTQH>z%1ZoNU zaYtM3GJQ(+xd0LlzUfisALXnn zEA5fBp7z>=oXmbWp3Yixxn{daN@`v?3^Ajff_a_*<{OJ|vGiQUmU_15!$x7=%~&{% zuJ=iubMhoL71yqwD$jk=QDX88yVO}Zt-;-%f%`XMUxEo!i7?KFZHd_*_?$3VEbTw< zVNtjI1AssvRe@lo1gYRiqYvo5UPGl3mOa&JzLK=Kf-c4QwI@b+HjSTw_%V-<#d?9@D+-D1hp>z zSmlrPuXC5@S-&05=dGrbE@yBSVijWjz}S!g){#gBCTd7OLu$52AeHEUU?BVuMzoU3 zUs$k5K#vb10d-6W`AYs{hqd840O|v1e}Wng784yFShM$OY@%`hIs|6YSVclOs;seQ{5RDD&hD&_` zC`87)G6Cz^CDjn| zKE8C6`kRS@XEL?ftcjau?OYwLoW>Z^MAe0y1eX1^#4lc5)%BQ@MlFW4rj3sJg0`;}~pkf?l+lb3{9xPtW-$S>4z>uTt-e;|%&MP?l8= z*}swxRH;{0-W%&?09zK0^UaPKgvKY(RJJ)mF`S$u%x5eja0cSWnZ^1q+%5#E(^Q;eWD8|`ZXWcca7-S|K;91n*(N+GK@1*?m{&MJ>b0aG+_71WeG-hIKJRL)|ZG_kSY?7lm?dJc?KU zhGbjK!6Z8~DPfV66q8YvMM_RGtgeySbjON%&d++30ngc#Pxz5n39#pbapAP*v}S}9 zIcTbvL7@RDw-W1|!@W-CuRLYAT1RGrMig*sFUn1GC(ZXQ@8g5lKDZ)#Zm-L)<9gWV zNwBS^MGbiMe9cFVvrSdcn>1l%&0dJl$?Ra6FAJqB6G6<~&7Uuht&S^u)L{k3GdDl- zf^!uiK&bUx05~gihrc1+j@MtL%$}>A{cr2?`iR*`@F|^jBg43`pK=*yZuzaFj1szw z>aRpZ?q?diGVNFHRTWZmIQOo)OMQ_|{%(!dYSZr+3c7l>o3$@xQ(=m?&oqq-0=X`h z)!UiuT@)JQ<}IcB@uY|B8$bIGkejA$jVz=!_JCZ~1--xER!H~2nO$?s2gfUHZt^T? zC6-aswuO=r0P~4Xz)NeY`MDsAD2b|yXr=MgLYcw* zxBidXk_I4xVr$#0g@OYI=)PrzB3%LaW&vyy7g^fe)Lk22+*kZP|E~&Y;dwh_`jexA zqN1{tlbpWm|9mRhG$~vOWfwbM$%h}90iJ7XTXheCyn;h&0Nu&}K3)+Ur3W&`jAtSV zYKbASfC6eJ%>_cm^?m{1Q=CSyWPZ>*5sZNd-MgEB6aCv6n*ad+7y`f`k5@awTR>vm z6u5K5-_~^BG*hELNI-K#;{qVA9u#X@$96ipW=A;Rr8Pf*{pZgdIiMm?g`I&*-a&8! z7nasGPegyS<_7jJjwTjhEWEzHwx%FzC)Os8uc|-%qU*nVq?^8#Q#YUax8G{S+!D(hANI(#04s=%Zp3|{5@a6=1(*VN5O_>KiaCu0caRjy zw;v(^o$^U8|895(S|#(x3OG;vS*7@61vn-6QN%}0aR=!1_M(o7KII6|JAISW$47nq z459snJ}i8ZBg8EE5dtXte&&yU=6C)x`X&b;#QceNar^<;vFA68zXt*7(nR09N8_>% z?&b(Qh2CstKo{FTUG?(&zi)G>8?T^Rqql z;y<(Rm|wG#auQ>sBRTjkm8Y5lWpNqUE>sSUES_ICc&gRYSJ}~Iq5_bq5fP}(Bi|7? zT-+T33kS0J4d-%+wtf~&O+4N(n59}C?vME3roPRa@uX%=55*l6(W@s`ffHonhO zh+y9%8V&~Y;mvr{S~(lW08bo_8uzCc<5XS}<{jT)nj#XXro^94PHISCc z=DJ$$)?)WBamp*%rqw@}yH1PNMJ3SZ;GY>MpyC~OD#R1sjD7rppGz)wFB$m#0~O?{ z5Gv3J)kP+s<>8q|>NtZK-EVkoMAt*-!F_3>j5e@W6Ctu;sBgx602{fFD*EF27mgn8 zvqe(%V%wNM=?&ZuS_D^#^|oc%brt7||JT-a2jtv+aYTA(s8o`uXejaQp{1?TE~~9l z8Knr#hct*J$rDm&kdcxQLK2~=LBnX#Fd~J7-}%N%-TQmrKkoY;_kQoa=bn4c=ZyPE zs^UL-+k~3T*vS*^&QHtfnpI$PZt+ImxwZLI=(9UqKR$bOPd}5LP+rm$K0VZK{JFU7 ztk0?5&K14n+S&s}inAZ5Dbo`rx$6b}+qD|KlXds6>z$AxcaUP+Hk&KI!Zy=3@wQ2x z>EwlmRg+_H*)g}D7Sp&_r|`jHx5V<8wUTGoe+n18fp-i>i zdE(!qgDbCIn5-Ntxwg^CMQLWd>eS!_L4&1xUY4kDf9QC9^Vb(lC;O#Fdjh6uZa83f zF7$fi?9GO2rjV0fpU$|_8sj0)t+uIAT45_R^?AA%^Wg`VuQo2u-+lB15*mAS8DBT= zFaI{hkaEMlc;>EIMOQXVk04+E6v>j&E}o*(`6Z21c`0e#kDX6fN#)+XI8CLo*UU9R zX{m-l_tYT8!ED*`RZ3HDotb#y<&~3Bn`_%+4&Lnh^~{qdfAj2-l&VF&pL<`uO5|42 zOHw**%Y*lBo++ClQh&;5Xr0q?JM9Ujf?tkIC@}vfeD`R2;Ezj1{VI>`tDb+;^3=ML z;B_?qK=QNil(}YyXE)A!Ms_j1!u1+on*Y_ZwmRTYQH)#QRKbs@slse#U3zlj_!p(` z8dSwMdkxy_bk`nCDI-09o2)KPo_jSw)|<=GKDkz6cibhptA6jkecPWFqdDrIXaM{_m|JeN3qWvYNxnYdGtdiy{+`^;`J_p?FnN49DGw6m6G-%)qi z62moZ$k;aX6Q`waU;nE!uS7OJOv)o$3Qn>1>;4u#Lvge6i{P@JFV8}rUVi1ebbN$_ zTaOU)US55{q9?-FnyPYF9o%eIC2F4%zI&EcQGe3e^1??`##?O?eVV*et*6*8X_|{! zO=jYRK<#ekA!C7k-!AjVN&U2{U-V?Vnx8=RfX6~^K%$zD*3O9&_1vx3-fhl;t_p*WndK*~|BSdZ2tTO!$U#T*97RHOz`v4@-js zO$vH$6!;B!IB7poS}eEx`c(J&*#&`;RrNlMc}t%$qIx}d4_(ska9%;Ey+gj zP(#Yn90`A}*Od6D6YjLC6#wW}PL6U?OYh{~YC5FWlT(?oJgRTn9f|kf&+FtmC-;`F zQS(yf^D(r{D(sIU56pdkvZcaLZqe4Ps=5swPUrV-bF(-Ukgaz>F`CZ7F?{0ZS05dCKGog4zMwAjmHADBv(>F4%bL8or3&-5wX5`oYL=~yn)F=H z@HNlO!Z0ML%s@sV=$$cBoqoNvH(uFhq9;%BP34=(HL>!0~~ySvH; z?XHs@W{+I-M141Z)i&8M-Q>tT$27BmH==3vg5Pg`s<>mF=h518xcF|5e&&tgR*^D` znm}^XwUDU;@xMNEm5cWbEf`v%64tZjUD6je^KQZCC(q^>%8CrJR_1gK^bf(0a5{NU?x#YuLb-RYx%9Y_WYf3nvkvhOe@ra=km+|k zW$Sd8+>Pth-harF{J51X5i0N4yKA$1z#J$%*w%IFOwvg8+gj;D^)jb&>uqOAhrdNhO|WZ;EVOUcyxK_@2O*-h;QJgZ7lV z^vv^EvS07bZXtiA%^D8yA{IDua5C){IZJe7?CODQTBxZopK&z56}V1C6cv zY`lvN^Zk;m=J+VgI=CpTYAC@y{EFI($<{TBftOY-TxMK5h5va$l(bCxo!~%^wI7-; zr@fK5dqIw!be!#NS*JON+m>}BzDhIYSI*qjN8TP=)^b1j*g1aw(o&@ApG6zw&J*jY z^PG5Aa?eDA;mxMQ2Ybi0Ebg_;+2l1m`(5ddqYu0sF76gOwru^`6;Rr5{gS2l znoG)Ag2LN+Rxb*gd|cq=y*YR2D+}h_t*Pr?cV9~RVcMaH2aDeA-G6TDhdsyaxZ(>B z9NZDLy3(sEU`{1{^4r<>KHRAhkFJ{-^uCiTnPOldyZfqr_3=gW)INtB*Lok!^C}R_ zIj~L4Mp}2`tic64T;wMkKRT}&Wb!ym=a900RI%0juj%FKGKYY+n=`AGgo!BZKzRT~yXi zDCa9zWrwZQO}iU;+oqo1E~{|=^g)MN>M3iF3T;d}{cwZ(A*b@{H5@zJ=#sP9*XH{L zG!LvXuXPk^q#Vsw9qc-Gb;)^}lhRWEnxwDGR2dQ(kAn0Sq9`3fvf}TOe9Qa=CJk#R z$9`9;7F`!)eZu(J@d=GLD!u3TyvZ`iJNI_sIjd_7Zkb;DyEWPumpb*D7R!1UMh511 zUFx-6SDbNi-uIawpA^VyJ~UHr?DUGe!8bKw@Thf^j}Eo^sOF*pS?l}?k%GQ;3L;Uw z2b)C&ug9l^4}>&L6RMKT$}P25YV=;)nG(FZSS8TNJvg9xsYuiMlFO-6bg#dsZF}6o z??J1m5{@q9ezyH8A)n_s`04KP@zYls)47*3rxw zpFd3au5R@BVql`|IkLS9ZJTH6QWAH@)hQ8+-B@$h@y?ysyCyq$Q-w7pE8xLlR?!91 zY0?SzDppSY62r>QkvS~6?pW&nkPkWXeUejc(px)LH17(T$5pTLvv{DBDB`u$%c^v? zh1d$c3+nlnz9I|Nd|wE=4Dgc{PI$c6B*yi|IML%9C%w7E?{m{cFS}#7?d#x60~Jf7 zD~UskONB%t2T!qUT?4Ii%%zs9r5M~3jj=ybroDOV@w@gLrH{R4r)^G|?D1GnJua8e z^B(P9(bS+b&F)jIBe@YOZiVwD^NcDCMe`GODfn;(kL(UvcY0}zO_XoBd6HPc#oP2B z7WD6nWwfShWV!tFnS}Y=p&x0C z%LBDvKN^kSx2OJ0xXW&G%md>i{xyMzTz++}VA^R`)wdN`>V6d5$#6KG)3N?oMCSGd z%8`EKWvVuu_*~OcvUZKH-%Q$5jn1cA1YSg1>aC2FhyYrK@|_ZR+~4QN6&SBt@BP zJa6X>yV(m&L)we$gO0`e_38y)KilqdW&Daj)j~f79sLOr^PkY4rqNbaE#xS-aE@Qh z7EO=P=Ln{7R?lE$#}0H#H;SBcFunV_%iYVXG_ znkB*Et^4LUyY;IsczeibwSIjwi!2b{)U%!2<@6xc(o{pB&Oc?_On$u@->-SU9tnIi zopf0>Oii5~r24F4()1ILn~qxSHh7nBR~KX9S>jaLX1mI9PVi-})VeE+BbR+R^|-)Z z^zEaBj!@Ez_D_uyMa~P_-&WlkdSl!xk=ubwoQgUspA2oR{lz)uCb;E+^QHN!N2eT+ zk=AaR$-Q*nXr?AFN7vVIllZ5`O3A(D=gt?3*U?;8+NhsU(wrV{VSHvrdQr+<8M*7L zUDFch%%``_81|`<{4i~T^YdA<4!-xL>}_W}v3TfooY|)@{k~_`xo^IlWzjjl={3zp zVFm$hGB4H^3)u%crj^J)iat`CRrB<*hRaU<6q{sGTW(8H!Rpt`ESwMD@{Dj^@@1E8 z_@2#lx0dojTG6#F6Rd*dcgLM~>MfQio0f2R1O1~Q|5g2kH=EBN*jBSV^`t{bMA*x2 z-7|A6mMY9%=4w6TCR0bl+J9KAG*$dwcg9>z9~FV~SA_yQbi>()?wo(%aIW{Jtx40P zvnFhr@Fg{$J$7+7#>9!4Im~O@rmg5H^7FZc`Y&^x75SxS4Rv0ML~q&>+zGv6!uhhJ=+g^H4uJo-r*HgFLz1)1J%1OAn z%XxY0*VPk66)t})oMwA1GQ&_g@ypX~HC}Yl?A)bU_bwN2h~6V+?esM+UAm6jK1Een zigY=5U#W1zdA{iIcG`#LyCenpg=htZmU{n%Pn(8rKdK^p>7wa-P{nlqei6%TSV%eQTrn{d6wrqQV2lD*Z%v)hZ^KgaPzQl$sX z6&$4RS6G^*9*Ph88T_@kLGxf+HEC{&>3+V4#uGX#etey)er2JdXHShT$$;}Vw>4gVKO4r zy>Cx*kJsEOaXL;De>vcsy0P-->4pggi{n$pykbtB-tuaG(q1LK4g0cowq7k$j9m43 z-x6oRqi3F_cq)0mNh}hu3;Fzv%RX9{ z+*KDZ`{3E8Rle2ml-(uIz>6h6r^&oJSl_VQ!aJ0CHkCU=`;${bj+n@eZN99Xmn|#-R^HSZ%$-> zI{)&Un4(-lDd}Uu>i4CFO@`h2%wIb;CcUCqYn)hl^S~xm-zzcWvX`oBf4FA-p=R$? zbB6_QPd@T_ZMZ>{yQDHBCz|)fW{OF4@=oKlv)3BuFPf#5cYRaB#iLf?_d?66=c;n0 zC)Q?(YV>Cdiad>4-t1}j_I|dEnrp#>yD^D1!7V#9gN9!p(7)4pdbLbV@|JH>(udBD zKhNhoPxJaEdzmi#z*Qx#F}X@}gOejl{F7rzA4se|pL9F21>#e0F^7i`gF&Z|9vm@ZiWk zd)c;6@b}%Od4$;Jgf>=jpJ)i4TSj$WtLzmpm~vfFdpIrS&>EA*a{Zs*jtNdV+Ef*D zMCV%U;yGfk$?@vf50kF(?$<=`>g|_xtqDd3kUd?8N7rEZm2>!Y|IgReksliQ%KdRu5vjHlE(0pQ&;G^$Z<{fhN`d6(Li!u2+8Q*l$+Ue|eAv0n?bk!_U*5XBHbPKb{e*_0V)s z`h1N{%&by7bNTTC*=xexpPf17v^1c1bD-)C0o@mBzRiv|>VKLgC+`>UvybwVEK#@I z`mV`x)^@EQQ+nJ&j+FT*DeG2@*WF{ntn%A+MSEl6rLZZ*+{kM9`;6XrtiIb-c_(jm zs`;6VmiOA9{EFOi{mAZjr}lo|V!vi`;rj0zsvj9yE;}T??!=_er}%RNn$^E^L;^Rt zowX6yJdu0YDg0~CJ#B`lk62|xiNQxLt=FX@%i^}Tu8yhg*0>oJs(e0JqFYjjwd=S zKJD>qKQ6g;f+q0nnhpKwjZE58k=jKzL)RR6t^SP3?SfzTMkF0A_r0VI9Eb5~%o zk5TcIHP72GQk!#h%!V@j57mUrq?Oz>UZ}5I=z8Sz22Qy1S-Cp&;87v)F0jdexR>E`Pi_U z+@m-zZ>oL2qj?wn-Ery%df%CIRll4y=?nON$Wrp6kZs;zd%{7%{>J7EcH8zF_cu+7 zv0>>SR$1Bk=-gvvgAJVIZ(1gdk_T@rYCk*Ee1nIp_E@`E5~NT22}(wWY!{>91rJ^J38qw?!htJJOb*M3=l|5fFrb$gERclv+a;eMw7fJX8aUV-qh%d)rL zNgcm5C;nayIZ^P;lEI{;pSR-D4(XPEs^T`V9AwEQKTbz$7RK0E9^T!jxRA^d`cG4HqIrg&m}UI=FI_QnR-cz<-<)fgo2l-*VD`$WX1RFzGvlh39M=^wn3NnDJ)dh6 z`8jxt^qJ!aAO5&xQ1s+L(9e}SET}6MA9ye3HobYyr^6oKh8|fgczG-^`_W6kFVD`= z^u#}MeGhJ0o79{CN=;JX#Z6b|=$0+CgELZFJ1Z6}ySx8PPfMDP!R;R*x0hLEtSfiD zldo{yIq{boa(4f`2}+0XQQySw{hWa}O|aX#-8J{h0zT%u7>(Wdyp#T2VE;mq_? zS;uXhW*$Fr&^maAJ6-;QqTz89*Zf27MOwx)=bGkw6c6p07R!Ac_3NRh5NF7oLCdG) z+}PCI`YUL)xZD_kIS}V#iay1QkaOtrl%`;A_RrGMnH@73pET;6>Di_R2jW-!8 zP}Jc~$fQIMdM&-tmdK5@SjbQR;_53Hxhe2*lXLRCZXLE^klp;+0uJrsmrm*Vw@=3Q zvdWU?e#hS_sfQqFKyN;3CPyZ*CP zw1^$tZX7B)$715++huENcg*INXiM#Ea_HQbxr($gtdFsxe8@iV(*gtmd=LBu%|EyfbXR$%kS+IojnHhCTJeYJV9hoow^_EPNy z?QeU6`PzR&yu0 zT?z9o*L4r<%QLBZk;|9ZNBOmXYN{74i91U&ZpO1FMqkWrX;FjHWAOkjV1me3a{DO1}*6>bl31Ihn59a+b4mSbEgo_?zPO(^z70K)F(o)Im8qY5-zP4G_zeV`#?(UxWtkX6d zZ*sfq%TtziRfxSmdGGDpXovMb9bfkcG+wjToiICP>RQuJ&qS^Udg~;I9Nn=(@hBgE zsH{y)s1aNGn34GT{aGzcj`IeXSS@bCb_a{y>CpGr$BLJma?+T=cjj;{_xl*YP6Z#6tVtSy1{ABu&+Pj_KASnP zYu{XhuEf;&7ZpzD) zZZH$JkhYs%u_%1-=;zkQyH!?^^$wN1?s{UGn5oZ~%Jx%rUD-V~I_PGxfl~hTmG!y3 zSyco3NRPVrm>*>AKR7qKZFr01w2%i)pBAjBxYTcvdqkhRE~8U4z`Je2oio{Su{W0( zU3{=ee+A|AgqKol!awePV!ADIeYA&e-u{TIsb8<$4RGyk6I$Bsvas!Z$hy6DlDThX z1dl5W|kXml+ zx~Vb0_Whk6%eH}%C)|#Qde?LBKJdwCdcTHwqe-}-B>ZQMtTRRX-l9w?0rey50wtL; z2ircDKm9KFz-*Q%ops`5{l@u|j&$uhp285ByUET}TKF_4dAn2H*;Ch_tSLRVIzv>k z;j?7=o_V`?iU}dQ;);5wPrde@ zft`IbcDb*7c8FizJh!p_#n!XW?=P?JYe;r5>^pmQZ_ovW-Phmmp~Wq#+44A+`|{JS zRTMff+kK4Bg=5eSn$5;MsrH70!WIQn9>puJ^Nv<_b$ z-yxSEzCz2Xc-fQGQ%gUroVeG3>#bQR+h;th|J8X%y?`a=?E=Fo7Q1RX<< zDq$$pe~Z5Bf2S|=ke!Kcm*xFcmXlxCwCwpIW&H8=`R#ojCbkv#eV)A$o-Lq5A6#50 z^&~7xxj1}$k)3VPaK&?xu-e%*0@Q3yaIr=RG_% z!Md`vBjm=b?=`9m_j-I^oVUb3jj6lVgR42yV*ZcfMEzB1+43Rv;Vz6a>aqz+2c@qU z-hVpz=(Emw*H3qB-t$4=!SGDCF2^bLZ{^(;-6+g`OZ4p%156j!$OaXk_7YahTK2R; zOfopl@?|L3oliME!}H3`+qt!oFU6Y9u-bkpsq*|As_W-J9?H8k?dpqnwD#*W_4jCO zcHu^a3EH~(->=_)bdL4*B|4R>ovP9=eWC1Fd-cWeu0ZQ{ISGyC<(=ZIE?DjPpj@+s zb4!n5I;lDBWTwGy4pW_U_Yf&`0^LPido3AfOyI9Cw;z_xpg@Hw#qzPZ()_NJ$qL*8Q2BnlM#qH(m z?!CFs;7qLLJ0ZjuDPWK%dTDxv@n@FS%1gATQ9i?ylBai`;L1!tb$9rvVq@W)=}{AJ z?dd*yX;({{u-?^vi}PwJHs;z60=vW2a#KPs-+I~Pwnjtu?8|88xgssis+~VQ&ClJj z`)qr#=EiX$hrK=aj!!$XkE?t!zPfxt|9bg}*@N=&sh8VMFSs@C2z{R=@5pPXoL8;d zP2S)3UM?=TzVRwg)1Zm_cD9>@jicYI4Re(yDb<*7Jk!rocbnXmc02ER=%r*arPH^z zeRVC_sIcEioW0j#$Aa&wD#g>HKkaKhyuqHl?%4^cc}u@Pbe0Lv{;9Dz??uAxIVpYD znhJ}SKJ1fj-F;2PE%RKG(xyJ9-I_FwMIj}MlLb%g)K&`IYqzQ^IXX6WB6sdm*=dGT z_*VIqj(4U+CW&il%SRHftqE_>q7 zMU5^m^f{-Qg3E4G*QOer>B%*%nsY8!#^?Uiyy+JCk&ol|Ms{vzB#Q3v%b#aGU{~Ex zD`9c|R|hwsg@SUd`_%i-0HQE!Ft>jcid(X{mkRCgPemWw@8q}D4NuDGxHDsj0MMZw2| zAyvQOeKWsl>mCJ(m!;1p*bI94IIJuUShMzipPb4&QHphKO?fD@$Mf{QlNP(0eB}lv zZ0t=bJ}~5``f3Jy^=hHKun*5l>`%S=#9bU1)e@e0bIlyNNv`)S8bo|gv~~`<^<^|w zU2}Z!(kpXC%)Pa3Z%+y8&<2-SCv)W*TKSfG&v0@1vN6wk)42kpBTMRDA7WK#m-_Tb zF619)8006Xwq2+|sJ3zS@88XiXDU!nW$+ zJMV#xdf#q}rRdTpZ(lf1(P&w(SZRMGC{(&h-{ye4>D~_3(a23YoH*TG$C|nfit+?E z)O5v!DT%n9saA6oc2#`jxQ)?auCiu-tgF((_=iubGd=h3w@$Y&T^DN{b-8X%U<{Sr z_v?U0Ksonh>+ujbapvh$dlXai()?X2_jl*}>_ znWt^HrG6PHdF;LTj;7;y=KW7IvAMAmEgv0;cA0!zr0IuSdMGE#^~2Dsir9N6c?-^Y z=a)FE&$HcI8SRi);(oyUu<)1NU#H7_`f}*-Q9hca^rJ6~?0W~Uzo3U}^Riwp5g(GN`MtIkiwKyew;r!ScFKpa)3k}O>_-ryTEIBn!bD_9FM|t!IgCgB|l`<*f z~5@PvP{dZ+m}6V{)zBi=evFfQu7NtrT|T=W|zmRIzti?aIFQ zrF49U>4N#MWK&9av*hlHdE7X7WU-)k-_YaBZ;x#BYftguPe@NOOj=(@lX&+%C3y1w zjkY2BiM6xkHZOFvE05?2=4tuw&Y$qJ`o%h{8f7Kn$N)`+ZqNNu4^&N=%e#BaDw|Jf zIZtbSq!ui6?>X;4TUsea#E?7A>Y0U?|7UWNsH6RSm%$as=59IoAwO6)^3+BhV=pVz4Rmy0lP=RLLSM%xK3(aJ zee5@%$g8UiyC0fP$bA{fdcZqtotXCaTk1X`3rF{t$$MvXi*ESbJSmzyz-|Asq9sgg z0-52l)T`1jI8o$4`BLrttJ-wWC)4B4yWM=zdRL@F_&g`QH7EP%x%&OW!o>xwCl+(o z@*4KoCosP|SvX(0<#y+3-_xfS3$7?K_lp@-sPl*O-qg&veqpL!u?|5(D_cc;o@t!dynp|q6i8@UEi9rj0NI8e3rAGA%^O`84X&3>|U zeN)uf`XQ@-s;}FkAK%@+?k(HjIex&f@mRvn;Qak&D{8iRb^R==oLSKmLJ52RS;|{C zt3=C!aV%-Bv4g!+lDSSdbB1)m^j42sJU?4zgWd(9p;g_r-VdMLR+n=A;-mMZ=;^bK zEw=9`2!@v_d?$+0c$ z%i7H7_a#osH^09Av-I48teM-M<{Wb#H(6k*_IPEtkQ+IxytX#hzDhpS<(s0lhZ|V> zqC5EUA@e9)3$J{n)UzJR(~IMD7AVnY4=h*YpXhRFaqP`%i8sFvEJh_nRFEmp3Z+x{*M(x%j*h|Cl{uuJyweX|1L1G?=W&0^SfHV@>x(_T||Uaoz%NJPzKhqF!p>B`8h z(;}}NaCSWWmYeAqUVQS_J1vDK&${H`+|3Cp`vgpvh;R*)0$r19-Et4k3Dt`7lr!SH z`JU6SSlBA5f96y69!9fKOr8h7Y}KoH|GvrA?{7G-k5azdrnu;n*4eD9UA)VnnzkW^pWjT*Lab|C`M$^jfW{11K z7G2>CWfJd-d2#ImJ0`6%TfSP4C)Oe0{rf+!aytZumQ7-lISeK*oI>*FGO2VqGVza` zuiO-(WiUA`4jG}bsdOgRkIkl2kbVpbi-wM6vYBiu(vMA}Q%A<~e5oW8RXUxGbf9zC z6b3?LP^fHPE0tuS%3!05p|e;_bYeP(PUWqlk(A)?K4l``qS06s8bYHp*&O=Svow%0+S`;dkh4PX@1(qW_rqBr9Ts_PtZRF#!ILO|K$xt3q=xhqg zUkZasM`Z~PlR-hh$D~tPh(J?7bbrgqDE?Ad6yWgK!=|v<6jWp=Y$}6|evm^3ZX#nj z3>p<#43$h~F-MCV%ul9*9HR$Ig_A`&14|~eM-L{(QfU;*=mQ&}F*tOD=TtaB268jV za9me+Xpwk)Ts8yQ5J(GyihhI6U@$QnhlUCkl|kWP{TNK_CQ+GW=I_%QbqZ9t3~Y1~ zCX2yAxJzY`DcB}i#Mz8J1uBaPhltSFG}f3KLP$9k?jfoSsT>OHw?g}O4GHO_Ay4vkEwa9BKFJ<=XEP?cj>Km!$n?uZ5-rcikb`Xr_CWS)sWY4HRGl@~>{pzw-` zmPX_KAX+Me$1#8wx(5<1na;~IAaVGZ90u##lPi#eXSup;LGlh?Yj>brUU>##1zbmcrn<5iOa)J8lB2 zrURQA2?CqWlQ4xAR&7nROd2naXc-LNL!za#c$3Vag$3&pEi5>UXek_Cq1nj11H}K5 zDLl$D_%4k@<853vLeE|XbQYD@Nwf?$Pu`qpDLf~lg>@bwTHtOy(NgI=Q445co#sRf zeAr_F--9c}DWetA`Cm2Yz~LXYqci?VL6;No6R#^4u8zz@OCaAnXTw|^NpZ_#4=Vu3oG>> zX+Z{vmI^XJv=oqmmC(Xk-B!ZR*&HhGI6;83ZzNh4$iOOSfv46)%K#Z5S~#SKL<@&B zX*INPNV-G|hZIJ%WG1h0HGGf-iya^cY!;8=04=O@BhkVKvxyc?ztdr4-SUp`zi_pj zh!)Q1$Vf}!)e|jTEm0?E$yA=X6MUBm!nB7Vz(Pxj78W|*8CoWVr$w}|&>*6P!!ICO z5YmrC3ky|ufff$mhiE~XGF{+<#D!{g8Ci&|E6`bN-l~z7!b@@`r4rv`xRKVV(HMxz zXAlSkrBMi-%e(7F%Hxtz-Am_CsANPtG01c{*Re{8&H>hj46i}*IKgJkE4^z{b zWIAnx8|+alr2|$&SIPtfiJC<=*hL1f&4V;=B4AT8FUy0Z#6=+oU_3GkTY(1lVC?dM zMkS*xfc=g^V8oK?V6Tv|fCb^7k?)E81^EQ7f@{AzoTR|J=1G#|!s#Kqf;pKe+d-h{ zWCRHUjm*Nva;PZeBC`lHj_ixY0UQZ5UgKKQoP}g2mHB@@1rs2{07Ziko316zv!jBE zM*oz`L5~A)Hw9xp69g7HHt0tO8|l23q#yyz#F@?$wIhj3!yf@o9y=|AO#{W5ph=oO zANdnNz<`3s_NLMqOt3~qo+O3&a2Vi6jQw+12zU<&jh7cq5(h4^7$~3t8k3Dc2MWMS zHiZ}K4`=+DIAaPM)dplHc!J1s81`wjz^ z#vAe^&E>MGG!%J}*#wM6dBdTho)z??VV?%rhzT+qWE9OFEcm;q@d%Da?n1 znP8K=2>8N8G8JrA^m@{~C3J96u#bSQMrm~LzcCs}1n=-Vk^Er3VSm?8W4o>FSSt(?&c*sf!7nnolZG{oM#T!WyUNlrE zK@SFcIl)N=K_B~dP#_G{Hv=D&jb1P42bPA{w1K2B6TSm>d+c}MwAiqfM;l3sYBUNm z2@K$%@fGm&Sb&#KZ6qmcBHw~PqN5T5G;E!qu>gX(d6C2k+?VA`nx{i2Q&3h>;2(@e zr=T_n0t2wWFkcdfC+SNPQ`j*apA{#pZ;|g?*3BMr{>*kGS&6 z#MSzHw5JLO(z;V?_dJEYzI`+K6cJI&?|Xcy~6DlqG4XqY9gaTe}hV#RU_F>Nkk1 zK>!e$2`nNDl{p#>ARyY0%=r!MMzI#gqHZw-hEl;s@RkISgt?%j(Q|{)Aa*4QS0C#K zdIehpi;e123Jq`)dhKa!@<`YWkljBypn-PBJ_rJc7&HnXI|^^SH)-x9_)qW_TEXV? zJ_e8!?Wh=tgxyk5RR))Vj4EyjW?@lMpg~ZHcPaoB(fa_HmEP9*4E&zAwyu~44Y9ZW) zo-+|1feUlRo20-6rvx-PB2RQS_#+67L4|doG|*It83Y@SnM#ODr$r}rScYg0V_^!CrL`ewHv!g5Il%8v~DMf ztD#8+4xbHj)BA;jTb%)FuE>2`~k91HeUOV%7w1KE5;nFBroilYvMCWGygU2ec8F zL}|2w0U{lBD1pX=pxqj<7baV;xUq5?#LoVn4s-<99bF8#)W}_hn+Q=1URxMRQHP3}Z^C?`{tRS# zuxkV~8d%#b_z3T17)eYT61%AGgFZ|MMXiQV1D6UI5zSLl2`30WTnhLZh?<76plFee zQ6Sch7++A9Y%GyN*aEs75ds*+DGGRjD5nT3h;Rx%$iPmX!UVOCE(W9&QPhOZ|A!p8 zzf;^)3gmkw^IdpOGilg0UJrUo zxR`)niuc0`TR`)&&=4;{8wOr91A4-M6vsjmnGtcPU_cGrR0{lEKP2(_ zsCxysV@ybX{&1a#uGS4^u~fYM@=3C^{Bi9G2jkjRVOSk|L9kV z5Fol?@C7l2$^!2ny~+SE&_oDC*#Lizh`#eb?n8_N^?j)<1`Ul~0F91We-_+5RBM8I z244<1$Hg!hL~DN6g^hw+P$7p#YoO@mcX9?!;x8@$2M*J9kXuD-tzaz3@yH%WNg@l% zkx7No)oe6-e-cQQHM&Lk#SgAl{1RiJ%_{9;}$dMYpp9ThGjmHp2}`cni9-~<(5K-mBs(FH;R zlZyCt@Ii=zpdF!t4^f=(L9C>fMh1(AO#=K!ZWxRmm4f->*AatJd{2W^3>x+T8V$wh zz&;p_EE>k5HW@$%+?sc zjs`I+GzkK<-{I6zdLC$4S_x=ij>j$rXlyii0W{7iAo1I;Ba|(+V=8zj;<`qjB&Z6WRjpM}um9j0TQ6 zMgzYZTOo~s8XdTE_|<{^q81SP;mQY84{Bc^X#oEci-G*(Yp`(*P1`ek& z#>x!v9|mK!V?e_a6hMQJJ~{~-sy8qimM(-UeJqj>G&+NeDIfOlZ0TrqONGiu>^i|4 z8l1+#j}z+Tuznnf{{3F`U;IadcpDnU1RCTE(A{yM{0yT(aTG>lW48$q2%6` zCkSXDHpmAd_<+K1=*Pf>3zRbIa6mr{)&ULRD1!fJV9YVLLIEJgRvOfKplpQ;`yX5X z>J%E}UeVhGiIu;Q{4f3kUk(Exgq_6(L**7ug9tmyEeIW=nw8L8$kA~jIEia1D4oHs z9U;P~l7hEZ81MwGj2{W)0@1M$umpE+#QywK!BDM%=5yig2Y_vKci_TcI|c-YT1BE1 z5+6INI^aM22cq?V24l%;2%1B^E3z0!{h?V9a0)4CoQ9}qK{HnnS0-x4un&UEj?w-J z&3`@vpmdDYpz|)CIbV6#-!N7lPF+}Mh)(;AzFd9^j|D%nZKaEbu0_H%& za&|z&Eg&5V(XmAnPX+!>0(*o6EId`ijuY%3rmKO1y5KMvj6U{3$W381+yX+?J_cwY zU;aCu2K)cV{{Mq$0P10i2I6>_ss;vPsv4?&FjWn)!ZE6v;6IV$K~ttc1MGwD4iF7a zW8i`b@in|35{Mp|mjfAabP_feW&wX0d*B8%cq)jE#l0U0a*gu$egj!L=>FehN8vxn zVBmKf^amzf@GKM)B>dF?Tttvpbb(M6F@^^GCvGfe%OQw?#`0-Q$TOl>kpbd?E`|a2 z0Ocx-#S9zBIh~6QhF~o=2_!jCqXRk*4NB4A0VEayf$R!q${~mPA6)x~{~&FGNh?&) zqge@X&T#pF+lfWF7{EaE=pgEhq^pLoDk|X0HLB2L%08q{V)E5{1_HqBi`7e z7>)s82%8vQt)N#C%Ci5@SRjsF3_L!E=pi~7Ql;3c;f`QoILMY^z5v9zvA`pQHqo2{ z%uB?ZN9@l(6%48xvj-sjXl|26Jj6oT%7n+dI1NsI?2cjVDDU_`{0A{cOa+6Vg9Rd> z_6&0>!5U!E6R655>4B zY=TS~nj)kT5f!{2?j1v|BW9f71^Va;$C2}g4`SJVpy7TYByusQ5^_#BTcMy5(J6#t z#b6kl%*FCD5V8bM0%bpBAkkw25yXTQ^dKf5kWj_2Bh=RZwt%qzzwBobg;&VL@G=Lp z2gEZsbTRNm5mU?X+6>dIz?d;(3I0PV8}>*FXb=rX7ek|A8jb}(4CNLJR1LO3@aa*z z!J@Ibm|g~qiRJ;JZh(TRWdJn)XDn6?0%IBfXDs|+%E;zM;Xeo=VcHBX4u=2Wu^*Zz zhP~mwKjgo#D+@drrF`IO1OH(#_D%3{eSl9!m_fO1~^MgU{5oj^xy zBVh6|8kES7k(i%)fH9^1oOP09njIz^WBMSP2fIAB5Lxn18_2)q@Ha z;t?FWbnx#m&;~DRFpC1E9GDBlhMxyQw+&?sSo#xQ$`bzdh;{j^UkPZ9Ee5<-R8_G7 zgkW2Q1&);*UQ$is#-$EXF4oIk{Lp&%1oH5?R9gLgGpKdhn%z6Y;&(SDHTz%~cb4J?BU zC>obw4)&w~B5~O7n-S4DOhvLeSh*BDQ~dMZ754v^{qU3@bJ(E*0;`n(1%$y_a3V1{ z3z=BV!+_sY7;_Z~{uA#lF%v~_3%yQI*n|05@RAopAyC+ZZv+ZZ&?6zVD+Xu5^~HBW zlnx;03BDnA#UQ?q=>^DSVS0fBzW(pcjY1b3{4p-n`9f_RHZh1RN`n*?#x3x(&>I2w zW~_e={0FZH`%w-eD}f0T$R&oSI8cL)q7dSzN-!E^0P+tCr-mkV`1#yUwn}NC;jldQ`lhePIxWM}uy z4?=_%ofv)-0^^cg#Jh0zLpltf1S`RSvEWXl8)5$b z*^n`u{_i0>?EfG8AqJ00KZLkZ&H(O35jRv@U@ifmAvUJcVeA-HP4J(v+PG2ylaJkF zC;`A02tiFuxF8mVkA+t}C}%hv2m&EQxSrTx2;*WCLp?BxQ=#Mmt9B-STnf{oP!Ie^ z2aLjhppVd84h+UhZ~^@`y?2&ST56?BBs1#Duhz8m^Mcnj?ZWp&sQLq*LW+CTgU zBs9(dZ$wAcYyGi+z-Y{1K?EL6%|RIu_S<9tMbTGlfd7aJL3GvNE1}U(D2l;KZ$Q_= z%VK255G2JE7d&vnZ~#0krBRR|B)m3-mvzVo0c)TZ5DK}mTred2;pyAhu?#Q~sELMO zIQSP${SW^^a2zWVfgcRUe&~VFgQ!V`xEthSkclBWi%kq}4tllWZ`@^I77J)t6&%nY zeTYl~014G_5R`|@J+>dv;N=od18pk?`tP3#hK}%a!m_tXmQz5ZPEk}ev^1LXKP~T4 AG5`Po delta 102166 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 Hx)c5fenXjS diff --git a/org.texi b/org.texi index 11ecfb9b5..f0d88307d 100644 --- a/org.texi +++ b/org.texi @@ -3,7 +3,7 @@ @setfilename ../info/org @settitle Org Mode Manual -@set VERSION 4.58 +@set VERSION 4.59 @set DATE December 2006 @dircategory Emacs @@ -626,14 +626,13 @@ command (@pxref{Agenda commands}). @kindex C-c C-x b @item C-c C-x b Show the current subtree in an indirect buffer@footnote{The indirect -buffer (@pxref{Indirect Buffers,Indirect Buffers,Indirect Buffers,emacs,GNU -Emacs Manual}) will contain the entire buffer, but will -be narrowed to the current tree. Editing the indirect buffer will also -change the original buffer, but without affecting visibility in that -buffer .}, in a separate, dedicated frame. With positive numerical -prefix N, go up to level N before selecting the subtree. With negative -prefix -N, go up N levels. With @kbd{C-u} prefix, don't use the -dedicated frame, but another, new frame. +buffer (@pxref{Indirect Buffers,Indirect Buffers,Indirect +Buffers,emacs,GNU Emacs Manual}) will contain the entire buffer, but +will be narrowed to the current tree. Editing the indirect buffer will +also change the original buffer, but without affecting visibility in +that buffer.}. With numerical prefix ARG, go up to this level and then +take that tree. If ARG is negative, go up that many levels. With +@kbd{C-u} prefix, do not remove the previously used indirect buffer. @end table When Emacs first visits an Org-mode file, the global state is set to @@ -3713,11 +3712,10 @@ agenda buffers can be set with the variable @kindex b @item b -Display the entire subtree of the current item in an indirect buffer, in -a separate, dedicated frame. With positive numerical prefix N, go up to -level N before selecting the subtree. With negative prefix -N, go up N -levels. With @kbd{C-u} prefix, don't use the dedicated frame, but -another, new frame. +Display the entire subtree of the current item in an indirect buffer. +With numerical prefix ARG, go up to this level and then take that tree. +If ARG is negative, go up that many levels. With @kbd{C-u} prefix, do +not remove the previously used indirect buffer. @kindex l @item l diff --git a/orgcard.pdf b/orgcard.pdf index 70f4bf243e9de913a93aa7596af6f186cdc1d26a..9b519f51f20c586bfa0bbe331a5466b4b57fbabd 100644 GIT binary patch delta 31899 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 X2RB#3=8-r&5Xb^gNhz)%0sp@Mv^u6( delta 31657 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